Repository navigation
fix(ms365-app): require caller authentication on the chat-tool route - #16143
optimus-fulcria wants to merge 1 commit into
Conversation
`POST /tools/{tool_name}` read `uid` from the request body and dispatched to
handlers that act on that user's stored Microsoft Graph token — `read_email`,
`send_email`, `send_chat_message`, `list_recent_files`, `read_file_text` and
`upload_text_file` among 17 tools — with no caller authentication at all.
Anyone who could reach the deployment and name a uid could read and send that
user's mail.
Adds `ms365_tools_auth.require_ms365_tools_auth`, mirroring the guards already
merged for `omi-github-app` and `omi-shipbob-app`: a shared secret presented as
`Authorization: Bearer <secret>` or an `ms365_tools_token` query parameter,
compared with `hmac.compare_digest` against `MS365_TOOLS_SECRET`. Fails closed —
503 while the secret is unset, 401 for a missing, wrong or non-Bearer one.
The hermetic suite covers the guard's decision table and asserts the chat-tool
route is still wired to it, so the dependency cannot be dropped from the
decorator unnoticed. The two shared stub harnesses that import this app's
`main.py` gain `fastapi.Depends`, which their decorator stubs then ignore.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Disclosure: this PR overlaps #14456 ("fix(plugins): require auth on /tools/* routes acting on uid-keyed credentials in 7 apps"), which was opened on 2026-09-18 — five days before this one — and already includes Two things here are not in #14456, and they're the only reason I'm not closing this unilaterally:
Happy either way, maintainers' call: I can close this in favour of #14456 and open the two deltas above as a small follow-up against it once it lands, or rebase this down to just those deltas now. I'd suggest the former — #14456 is the better-scoped unit for the class, and its 503-until-provisioned rollout note applies to |
|
Keeping this open on 796b93c. Microsoft 365 /tools/{tool_name} on main still has no auth dependency. This head adds require_ms365_tools_auth. Only cubic ran. Not approving this SHA. — Astar, on behalf of @aryanorastar |
|
Verified on head 796b93c in an isolated, network-disabled container — this is a solid fix for a real gap, and the guard mirrors the merged pattern rather than inventing a new one. File-by-file:
Two calls for a human maintainer before merge:
🤖 Automated review on behalf of the maintainers. by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with |
kodjima33
left a comment
There was a problem hiding this comment.
Verified: missing caller-auth on /tools/{tool_name} confirmed on current main (_auth_guard only checks the named uid has a token, not that the caller may act). Fix mirrors existing merged guards (omi-github-app, omi-shipbob-app) and adds 11 tests. Holding merge for manual sign-off since this touches auth/secret-handling code and the repo's own automation flagged it security-review + needs-maintainer-review.
|
Keeping this open. Not approving — Astar, on behalf of @aryanorastar |
aryanorastar
left a comment
There was a problem hiding this comment.
Approved 796b93c6ce27f085eef9ce5f3c686b6115168970. plugins/omi-ms365-app/main.py on main posts /tools/{tool_name} with no caller dependency. This head requires require_ms365_tools_auth and Hygiene succeeded.
|
Verdict: correct, well-scoped security fix, consistent with the guards already merged for omi-github-app and omi-shipbob-app; nothing blocking from my side. Verified on 796b93c:
Security/blast radius: this is the most sensitive of the plugin tool routes (mail, calendar, Teams, OneDrive), and the fix fails closed — the right default. Deployment coordination is the one operational item: until USER IMPACT: MS365 chat tools (mail, calendar, Teams, files) stop responding on any deployment until the operator sets Both maintainer approvals on this head already stand; from a review standpoint this is ready for a merge decision, with the rollout note above. by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with |
|
Verified: correct, well-scoped security fix that mirrors the merged github/shipbob guard pattern; from automation's side nothing blocks beyond the human merge decision. What I checked, file by file:
Security: closes a real gap — Non-blocking nit for the whole guard family (github/shipbob/composio too): Auth-surface sign-off stays with the human maintainers (two approvals already on this head); the merge call is theirs. by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with |
|
Hey @optimus-fulcria 👋 Thank you so much for taking the time to contribute to Omi! We truly appreciate you putting in the effort to submit this pull request. After careful review, we've decided not to merge this particular PR. Please don't take this personally — we genuinely try to merge as many contributions as possible, but sometimes we have to make tough calls based on:
Before your next PR, please skim:
If this was declined for direction or taste, maintainers should cite an invariant ID or open a proposed one — ask if that citation is missing. Your contribution is still valuable to us, and we'd love to see you contribute again in the future! If you'd like feedback on how to improve this PR or want to discuss alternative approaches, please don't hesitate to reach out. Thank you for being part of the Omi community! |
Summary
POST /tools/{tool_name}inplugins/omi-ms365-app/main.pytookuidfrom the request body and dispatched to a handler that acts on that user's stored Microsoft Graph token, with no caller authentication on the route:_auth_guardonly checks that a Microsoft token exists for the named uid, so naming a connected user is enough. The 17 registered tools includeread_email,search_emails,send_email,list_recent_chats,send_chat_message,list_recent_files,read_file_textandupload_text_file, andconfig.GRAPH_SCOPESrequestsMail.Read,Mail.Send,Mail.ReadWrite,Calendars.ReadWrite,Chat.ReadWriteandFiles.ReadWrite.All. Any client that can reach the deployment and name a uid could read a user's mailbox and send mail as them.Same defect class as the guards already merged for
omi-github-app(github_tools_auth.py) andomi-shipbob-app(shipbob_tools_auth.py), and as the earlier zapier / paid-mentor / webhook fixes; ms365 was missed by all of them./tools/*in this app is the most sensitive instance left: it is mail, not inventory.Fix
New
plugins/omi-ms365-app/ms365_tools_auth.pywithrequire_ms365_tools_auth, mirroring the merged guards rather than inventing a second shape:MS365_TOOLS_SECRET, presented asAuthorization: Bearer <secret>or anms365_tools_tokenquery parameter.hmac.compare_digest(timing-safe); a whitespace-only secret counts as unconfigured.dependencies=[Depends(require_ms365_tools_auth)]on the single chat-tool route. Caller authentication only —uidstays where it is, in the body.Docs:
MS365_TOOLS_SECRETadded to the README's required-environment table, the endpoint table, thecurlexample, and.env.example, plus a short section stating that the same value has to reach the Omi backend or every tool call is refused.plugins/omi-ms365-app/test_tool_dispatch.pyandplugins/test_oauth_error_params_escaped.pyimport thismain.pyagainstfastapistubs; both stubs gain aDependsattribute, which their decorator stubs then ignore.Verification
python3 plugins/omi-ms365-app/test_ms365_tools_auth.py— 11 tests. Nine cover the guard's decision table (unconfigured→503, blank secret→503, no token→401, wrong bearer→401, wrong query token→401,Basicscheme→401, correct bearer→ok,bearerlowercase→ok, correct query token→ok); two load the realmain.pythrough a recording stub and assert the chat-tool route — and only it — carriesrequire_ms365_tools_auth, so the dependency cannot be dropped from the decorator unnoticed.fastapiblocked from import, matching this app's stdlib-only test lane.test_tool_dispatch.py(7),test_input_coercion.py(8),test_services_hardening.py,test_calendar_pagination.py,plugins/test_oauth_error_params_escaped.py(3),plugins/test_uid_reflection_remaining_apps.py(9).make preflight— 21 checks pass, including the newms365-tools-auth-testsentry registered in.github/checks-manifest.yaml(both lanes).Failure-Class: none
🤖 Generated with Claude Code