Skip to content

Security hardening: tighten cross-origin policies#595

Merged
corinagum merged 3 commits into
mainfrom
cg/cors-tightening
May 27, 2026
Merged

Security hardening: tighten cross-origin policies#595
corinagum merged 3 commits into
mainfrom
cg/cors-tightening

Conversation

@corinagum
Copy link
Copy Markdown
Collaborator

Summary

Two narrow tightenings of cross-origin policy in the TS SDK.

Changes

1. ExpressAdapter.serveStatic — drop wildcard cors() middleware

Tab static content was served with a wildcard Access-Control-Allow-Origin header. Teams loads tab content inside an iframe, governed by CSP frame-ancestors, not by CORS. The wildcard served no documented use case and risked surprising any future endpoint mounted under the same path.

Callers that legitimately need cross-origin reads of the served assets can layer their own CORS middleware at the consuming route.

2. DevtoolsPlugin WebSocket — require same-origin upgrades

The DevTools WebSocket server previously accepted upgrades from any origin (the ws library's default when no verifyClient is provided). The DevTools UI is always loaded from the same origin as the bot server, so legitimate clients send an Origin header matching the request Host. A verifyClient callback now rejects upgrades whose Origin does not match the request Host; absent Origin is also rejected.

What this does not change

  • No public API change. No behavior change for in-app same-origin usage.
  • HttpPlugin (already marked deprecated in-code) still uses a global cors(); that path will be removed with the plugin.

Validation

  • Full monorepo npm run build, npm run test, npm run lint clean.
  • examples/tab sample: tab index, JS, and CSS still served with HTTP 200 and no Access-Control-Allow-Origin header. Cross-origin probe returns the asset with no Access-Control-Allow-* headers, so browsers would block cross-origin fetch as intended.
  • examples/echo with devtools enabled: same-origin WebSocket upgrade succeeds (101); cross-origin and missing-Origin probes return 401 from the ws library.

@corinagum corinagum force-pushed the cg/cors-tightening branch from 35ee931 to ca1cb60 Compare May 26, 2026 21:32
Comment thread packages/apps/src/http/express-adapter.ts
@corinagum corinagum marked this pull request as ready for review May 27, 2026 16:56
Copilot AI review requested due to automatic review settings May 27, 2026 16:56
@corinagum corinagum force-pushed the cg/cors-tightening branch from ca1cb60 to c6a2ab3 Compare May 27, 2026 16:56
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens cross-origin behavior in the SDK by removing permissive default CORS headers for served tab assets and by restricting DevTools WebSocket upgrades to same-origin requests.

Changes:

  • Removed wildcard cors() middleware from ExpressAdapter.serveStatic() so static assets no longer emit Access-Control-Allow-Origin: *.
  • Added a verifyClient gate to the DevTools WebSocketServer to reject upgrades whose Origin does not match the request host (and reject missing Origin).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/dev/src/plugin.ts Adds origin verification for DevTools WebSocket upgrades.
packages/apps/src/http/express-adapter.ts Drops default wildcard CORS headers when serving static tab assets and updates method documentation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/dev/src/plugin.ts
Comment thread packages/apps/src/http/express-adapter.ts
Comment thread packages/dev/src/plugin.ts
@corinagum corinagum force-pushed the cg/cors-tightening branch from c6a2ab3 to f672f4f Compare May 27, 2026 18:44
corinagum added 3 commits May 27, 2026 13:57
Locks in the absence of `Access-Control-Allow-Origin` (and friends)
on responses from `serveStatic()` so a future regression that
reintroduces wildcard CORS would fail this test.
@corinagum corinagum force-pushed the cg/cors-tightening branch from b5ddf6e to e24abda Compare May 27, 2026 20:57
@corinagum corinagum merged commit f799bc0 into main May 27, 2026
6 checks passed
@corinagum corinagum deleted the cg/cors-tightening branch May 27, 2026 22:07
corinagum added a commit that referenced this pull request May 27, 2026
## Summary

Brings `origin/main` into `release` for the **2.0.12** release. **No
carve-outs this time** — Quoted Replies is included.

`version.json`: `2.0.12-preview.{height}` → `2.0.12`.

Aligns with teams.py 2.0.12 (PR #442 already on PyPI / pending publish).

## What's in this release (delta from 2.0.11)

**Features**
- Quoted Replies + new quotes features — previously held back via the
2.0.11 carve-out (#576)
- SuggestedActionSubmitActivity for `suggestedAction/submit` invoke
(#591)
- Default targeted replies for targeted inbound messages (#592)
- Filter colliding keys before `Object.assign` in `ActivityContext`
(#596)
- Prompt Preview support (#536)
- A2A sample (#584)
- AI/MCPClient (#572)
- Allow passing custom HTTP client via `AppOptions`-equivalent
- Reactions API marked GA (#575), Rename `ReactionClient.remove()` →
`delete()` (#567)

**Security & fixes**
- Security hardening: tighten cross-origin policies (#595)
- Lock JsonWebToken trust-boundary contract (#586)
- `fix(deps)`: npm audit fix — clears all high-severity advisories
(#599)
- `fix(apps)`: log inbound activities at info, warn on missing
Authorization (#568)
- `fix`: App user-agent merging (#573)
- `fix`: Add null checks in `local-memory.ts` to prevent role crash
(#438)
- `fix`: `@microsoft/teams.client` import error in webpack 5 (#566)
- Switch to named imports without subpaths (#561)

**Deprecations / package changes**
- Deprecate `DevtoolsPlugin` in favor of Microsoft 365 Agents Playground
(#593)
- Remove in-repo Teams CLI package (#580) — moved to
`teams-sdk/packages/cli`
- Deprecate AI Libraries (#588)
- Correct imports + return types in misc. packages (#589)

**Dependency bumps**
- turbo 2.8.11 → 2.9.14 (#587)
- qs 6.15.0 → 6.15.2 (#594)
- hono 4.12.14 → 4.12.16 (#562)
- npm audit fix bundle (#599): axios 1.13.5 → 1.16.1, fast-uri 3.1.0 →
3.1.2, ws 8.19.0 → 8.21.0, @azure/msal-node hoisted 5.2.2, uuid 11.1.0 →
11.1.1, @azure/identity 4.13.0 → 4.13.1, express-rate-limit, ip-address,
brace-expansion, etc.

## Quoted Replies inclusion notes

- Teams client rendering is in-sync with the wire format as of
2026-05-06.
- APX QR rollout completed: Public 2026-04-10, GCCH/DoD/Gallatin
2026-04-14.
- No SDK-side carve-outs needed; this matches the teams.py 2.0.12 (PR
#442) decision.

## Branch structure note

`prep-release/2.0.12` is reset to `origin/main` + a one-line
`version.json` bump. Diff against `release` (this PR) shows the full
delta from 2.0.11. We deliberately did not use a 2-parent merge commit —
the auto-merge tried to interleave non-overlapping hunks from main's QR
additions with release's QR carve-outs in `activity.ts`, producing a
semantically broken file. Reset-to-main + version bump is the clean way
to express "release should equal main."

## Test plan

- [x] `npm install` clean (post-audit-fix lockfile)
- [x] `npm run build` — 33/33 targets clean
- [x] `npm test` — 14/14 task targets, 279+ tests pass
- [x] E2E echo bot smoke test against `cg-test-bot-py` via canary ABS —
done on `cg/audit-deps-ts` (now merged); covers axios + msal-node code
paths
- [ ] Pipeline Build + Test stages green on `release` after merge
- [ ] Publish pipeline run with **Public** → ESRP → npm
`@microsoft/teams.*@2.0.12`
- [ ] Smoke install: `npm view @microsoft/teams.apps@2.0.12` after
publish
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants