[security] Code bridge pages inherit portal-origin authority without Code origin isolation
Summary
When CRABBOX_CODE_ORIGIN_TEMPLATE is unset or invalid, Crabbox keeps Code bridge traffic on the coordinator origin under /portal/leases/{lease}/code/.... The proxied Code response is lease-controlled HTML/JavaScript, but it is returned with a CSP that permits scripts and same-origin connections. Because portal sessions use a crabbox_session cookie scoped to Path=/, JavaScript running in that Code page can make same-origin requests to other /portal routes as the viewer.
This crosses the supported coordinator authentication and ownership boundary described in SECURITY.md: the issue does not require Crabbox to isolate hostile arbitrary tenants, but it does let lower-trust lease-controlled browser content act with the authenticated portal identity of a user or admin who opens that lease's Code bridge during supported sharing or inspection. The maintained operations guide documents per-lease Code origins as optional defense-in-depth for exactly this cooperative sharing/admin-inspection surface, but the default same-origin compatibility path leaves the ambient-authority path reachable.
Affected Components
- Checked commit:
5001cd1179a42c174da65c8e8a87bf16218aef95
- Component: coordinator portal / Code bridge
- Affected files and lines:
worker/src/fleet.ts:4881-4882 routes /portal/leases/{id}/code/... into the Code proxy.
worker/src/fleet.ts:7029-7105 proxies Code traffic on the portal origin unless a configured isolated origin exists.
worker/src/code-origin.ts:7-35 returns no isolated origin when CRABBOX_CODE_ORIGIN_TEMPLATE is missing, malformed, non-HTTPS, or lacks exactly one {lease} placeholder.
worker/src/fleet.ts:13254-13301 replaces upstream CSP with a VS Code-compatible policy that allows script execution and same-origin connect-src.
worker/src/coordinator-entry.ts:86-106 authenticates /portal... requests by converting the portal cookie into an internal bearer request.
worker/src/coordinator-entry.ts:191-201 reads crabbox_session from the request cookie and sets Authorization: Bearer ... for portal requests.
worker/src/oauth.ts:660-668 mints the portal cookie with Path=/.
Attack Path
Attacker-controlled source:
Lease-controlled Code bridge HTTP responses, such as code-server HTML, extension-provided web content, or other script-capable content returned through the Code bridge for a lease visible to the victim.
Relevant control:
codeOriginForLease() returns undefined when CRABBOX_CODE_ORIGIN_TEMPLATE is absent or invalid. In that state, codePortalProxy() does not redirect the browser to a separate per-lease origin; it continues serving Code responses from the coordinator's /portal/leases/{lease}/code/... path.
Sink:
The victim browser executes the lease-controlled Code page as coordinator-origin JavaScript. Same-origin requests to /portal include the Path=/ crabbox_session cookie, and prepareCoordinatorRequest() converts that cookie into authenticated portal authority.
Preconditions:
- The coordinator has Code bridge support enabled for a lease and does not have a valid per-lease
CRABBOX_CODE_ORIGIN_TEMPLATE.
- The victim can access the attacker's Code-capable lease through normal portal visibility, sharing, or admin inspection.
- The victim opens the lease's Code bridge page while authenticated to the portal.
Steps:
- The victim authenticates to the coordinator portal and receives a
crabbox_session cookie scoped to Path=/.
- The victim opens
/portal/leases/{attacker-lease}/code/....
- Because no isolated Code origin is available,
codePortalProxy() returns the Code bridge response directly under the coordinator origin.
- The response CSP allows scripts and same-origin network requests, so lease-controlled JavaScript can call portal routes such as
/portal, /portal/admin for admin viewers, /portal/runs/{run-id}/logs, /portal/leases/{victim-lease}/share?format=json, or POST actions such as /portal/leases/{victim-lease}/share and /portal/leases/{victim-lease}/release when the viewer has manage access.
- The browser automatically attaches
crabbox_session; Crabbox converts that cookie into authenticated portal context and returns or performs the action as the viewer.
Important scope limit:
The portal cookie is not a general /v1 API bearer token in browser JavaScript. prepareCoordinatorRequest() only converts crabbox_session for /portal... requests, and the portal documentation states that /v1/... API tokens are separate and not echoed to the browser. This report is therefore scoped to portal reads and portal actions reachable with the viewer's portal session, not direct arbitrary /v1 API calls.
Impact
A Code-capable lease can become a same-origin script execution surface for the coordinator portal. If another user opens that lease's Code bridge, the lease-controlled page can read portal pages and JSON endpoints visible to that user, including lease metadata, share state, run details, retained logs, and admin portal pages for admin viewers. It can also submit same-origin portal POST actions that the viewer is authorized to perform, such as changing share state or stopping/deleting leases the viewer can manage.
This is reportable under the maintainer-authored scope because the coordinator's authentication, ownership, and sharing controls are explicit product boundaries for the supported trusted-team model. The finding is not based on mutually hostile tenants sharing a broker as an isolation boundary; it is based on Crabbox serving lease-controlled Code HTML/JavaScript on the same browser origin as authenticated coordinator portal controls in a documented sharing/admin-inspection workflow.
Severity Assessment
CVSS Assessment
| Metric |
v3.1 |
v4.0 |
| Score |
9.0 / 10.0 |
9.3 / 10.0 |
| Severity |
Critical |
Critical |
| Vector |
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H |
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H |
| Calculator |
CVSS v3.1 Calculator |
CVSS v4.0 Calculator |
The attack is network-reachable for an authenticated lower-privilege user or lease controller and requires user interaction from the victim. Once the victim opens the bridged Code page, execution moves from lease-controlled content into coordinator-origin portal authority, allowing high confidentiality, integrity, and availability impact over the portal-visible resources and actions reachable by the victim.
Recommended Remediation
Fail closed for browser Code bridge traffic unless it is isolated from the coordinator portal origin. Practical options:
- Require a valid
CRABBOX_CODE_ORIGIN_TEMPLATE before serving script-capable Code bridge responses to browsers.
- If compatibility requires retaining the same entry URL, keep
/portal/leases/{id}/code/... as a launcher that mints a short-lived viewer ticket and redirects to a per-lease origin before any Code HTML/JavaScript is served.
- For deployments that intentionally keep same-origin Code for local compatibility, render only a non-executable interstitial on the coordinator origin and require explicit operator acknowledgement before opening the Code service in an isolated origin or separate host.
- Add regression tests proving that missing or invalid
CRABBOX_CODE_ORIGIN_TEMPLATE cannot silently fall back to coordinator-origin script execution for shared/admin-visible Code pages.
Do not rely on filtering crabbox_session before forwarding requests to the Code backend as the primary fix. That protects the backend from directly receiving the portal cookie, but it does not stop browser-executed same-origin JavaScript from using the cookie against /portal routes.
Validation
Validation rubric:
Evidence:
SECURITY.md:22-25 states that coordinator authentication, ownership, and sharing controls remain product boundaries in the supported trusted-team model. SECURITY.md:38-42 lists authentication bypass, cross-owner access contrary to authorization, and trusted credential exposure to a lower-trust destination as in-scope examples.
docs/security.md:43-48 says per-lease Code origins keep lease-controlled Code HTML/JavaScript off the coordinator origin and off other lease origins for cooperative sharing and admin inspection. The same text classifies this as defense-in-depth and not hostile multitenant isolation; the accepted issue is scoped to the coordinator portal authority boundary, not hostile tenant isolation.
docs/features/portal.md:58-77 documents the intended isolated-origin flow and explicitly says missing or invalid configuration keeps existing same-origin Code behavior for compatibility.
docs/features/portal.md:81-103 documents that portal pages use crabbox_session, that the Worker converts it internally to bearer auth for portal pages, and that /v1 API tokens are separate and not echoed to the browser.
worker/src/fleet.ts:4881-4882 routes /portal/leases/{id}/code/... to codePortalProxy().
worker/src/code-origin.ts:11-34 returns undefined for missing or invalid templates.
worker/src/fleet.ts:7077-7080 redirects to an isolated Code origin only when codeOriginForLease() returns one; otherwise the request proceeds to codeProxyHTTP() at worker/src/fleet.ts:7105.
worker/src/fleet.ts:7411-7417 returns the bridged Code HTTP response with codeResponseHeaders().
worker/src/fleet.ts:13254-13301 allows script execution and same-origin connections on proxied Code responses.
worker/src/coordinator-entry.ts:86-106 authenticates /portal requests, and worker/src/coordinator-entry.ts:191-201 converts the crabbox_session cookie to Authorization: Bearer ... for those portal requests.
worker/src/oauth.ts:660-668 sets crabbox_session with Path=/, making it available to same-origin /portal/leases/{id}/code/... and other /portal... requests.
worker/src/fleet.ts:5729-5780 demonstrates a same-origin portal JSON/read-write target for lease sharing when the viewer can manage that lease, and worker/src/fleet.ts:5293-5328 demonstrates a portal POST action that releases/deletes manageable leases.
worker/test/fleet.test.ts:13498-13524 confirms proxied Code HTML receives a VS Code-compatible CSP and cannot set crabbox_session; worker/test/fleet.test.ts:13542-13552 confirms crabbox_session is not forwarded to the Code backend. Those are useful controls, but they do not prevent browser-side same-origin requests to /portal routes.
- Local CVSS validation with the
cvss Python package returned (9.0, 9.0, 9.1) for CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H and (9.3,) for CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H.
Suggested regression coverage:
- Add a Worker test that configures a Code-capable lease with no
CRABBOX_CODE_ORIGIN_TEMPLATE and asserts browser Code HTML is not served as executable coordinator-origin content.
- Add a Worker test that invalid templates do not silently preserve executable same-origin Code responses for shared/admin-visible portal paths.
- Keep existing positive coverage for valid templates, short-lived isolated viewer sessions, path-scoped Code cookies, and backend cookie filtering.
[security] Code bridge pages inherit portal-origin authority without Code origin isolation
Summary
When
CRABBOX_CODE_ORIGIN_TEMPLATEis unset or invalid, Crabbox keeps Code bridge traffic on the coordinator origin under/portal/leases/{lease}/code/.... The proxied Code response is lease-controlled HTML/JavaScript, but it is returned with a CSP that permits scripts and same-origin connections. Because portal sessions use acrabbox_sessioncookie scoped toPath=/, JavaScript running in that Code page can make same-origin requests to other/portalroutes as the viewer.This crosses the supported coordinator authentication and ownership boundary described in
SECURITY.md: the issue does not require Crabbox to isolate hostile arbitrary tenants, but it does let lower-trust lease-controlled browser content act with the authenticated portal identity of a user or admin who opens that lease's Code bridge during supported sharing or inspection. The maintained operations guide documents per-lease Code origins as optional defense-in-depth for exactly this cooperative sharing/admin-inspection surface, but the default same-origin compatibility path leaves the ambient-authority path reachable.Affected Components
5001cd1179a42c174da65c8e8a87bf16218aef95worker/src/fleet.ts:4881-4882routes/portal/leases/{id}/code/...into the Code proxy.worker/src/fleet.ts:7029-7105proxies Code traffic on the portal origin unless a configured isolated origin exists.worker/src/code-origin.ts:7-35returns no isolated origin whenCRABBOX_CODE_ORIGIN_TEMPLATEis missing, malformed, non-HTTPS, or lacks exactly one{lease}placeholder.worker/src/fleet.ts:13254-13301replaces upstream CSP with a VS Code-compatible policy that allows script execution and same-originconnect-src.worker/src/coordinator-entry.ts:86-106authenticates/portal...requests by converting the portal cookie into an internal bearer request.worker/src/coordinator-entry.ts:191-201readscrabbox_sessionfrom the request cookie and setsAuthorization: Bearer ...for portal requests.worker/src/oauth.ts:660-668mints the portal cookie withPath=/.Attack Path
Attacker-controlled source:
Lease-controlled Code bridge HTTP responses, such as code-server HTML, extension-provided web content, or other script-capable content returned through the Code bridge for a lease visible to the victim.
Relevant control:
codeOriginForLease()returnsundefinedwhenCRABBOX_CODE_ORIGIN_TEMPLATEis absent or invalid. In that state,codePortalProxy()does not redirect the browser to a separate per-lease origin; it continues serving Code responses from the coordinator's/portal/leases/{lease}/code/...path.Sink:
The victim browser executes the lease-controlled Code page as coordinator-origin JavaScript. Same-origin requests to
/portalinclude thePath=/crabbox_sessioncookie, andprepareCoordinatorRequest()converts that cookie into authenticated portal authority.Preconditions:
CRABBOX_CODE_ORIGIN_TEMPLATE.Steps:
crabbox_sessioncookie scoped toPath=/./portal/leases/{attacker-lease}/code/....codePortalProxy()returns the Code bridge response directly under the coordinator origin./portal,/portal/adminfor admin viewers,/portal/runs/{run-id}/logs,/portal/leases/{victim-lease}/share?format=json, or POST actions such as/portal/leases/{victim-lease}/shareand/portal/leases/{victim-lease}/releasewhen the viewer has manage access.crabbox_session; Crabbox converts that cookie into authenticated portal context and returns or performs the action as the viewer.Important scope limit:
The portal cookie is not a general
/v1API bearer token in browser JavaScript.prepareCoordinatorRequest()only convertscrabbox_sessionfor/portal...requests, and the portal documentation states that/v1/...API tokens are separate and not echoed to the browser. This report is therefore scoped to portal reads and portal actions reachable with the viewer's portal session, not direct arbitrary/v1API calls.Impact
A Code-capable lease can become a same-origin script execution surface for the coordinator portal. If another user opens that lease's Code bridge, the lease-controlled page can read portal pages and JSON endpoints visible to that user, including lease metadata, share state, run details, retained logs, and admin portal pages for admin viewers. It can also submit same-origin portal POST actions that the viewer is authorized to perform, such as changing share state or stopping/deleting leases the viewer can manage.
This is reportable under the maintainer-authored scope because the coordinator's authentication, ownership, and sharing controls are explicit product boundaries for the supported trusted-team model. The finding is not based on mutually hostile tenants sharing a broker as an isolation boundary; it is based on Crabbox serving lease-controlled Code HTML/JavaScript on the same browser origin as authenticated coordinator portal controls in a documented sharing/admin-inspection workflow.
Severity Assessment
CVSS Assessment
The attack is network-reachable for an authenticated lower-privilege user or lease controller and requires user interaction from the victim. Once the victim opens the bridged Code page, execution moves from lease-controlled content into coordinator-origin portal authority, allowing high confidentiality, integrity, and availability impact over the portal-visible resources and actions reachable by the victim.
Recommended Remediation
Fail closed for browser Code bridge traffic unless it is isolated from the coordinator portal origin. Practical options:
CRABBOX_CODE_ORIGIN_TEMPLATEbefore serving script-capable Code bridge responses to browsers./portal/leases/{id}/code/...as a launcher that mints a short-lived viewer ticket and redirects to a per-lease origin before any Code HTML/JavaScript is served.CRABBOX_CODE_ORIGIN_TEMPLATEcannot silently fall back to coordinator-origin script execution for shared/admin-visible Code pages.Do not rely on filtering
crabbox_sessionbefore forwarding requests to the Code backend as the primary fix. That protects the backend from directly receiving the portal cookie, but it does not stop browser-executed same-origin JavaScript from using the cookie against/portalroutes.Validation
Validation rubric:
CRABBOX_CODE_ORIGIN_TEMPLATEproduces same-origin fallback instead of fail-closed behavior./portalroutes with the viewer'scrabbox_session.SECURITY.mdtreats as product boundaries./v1API calls are not authenticated by the portal cookie, and the backend does not receivecrabbox_sessionthrough proxied Code requests.Evidence:
SECURITY.md:22-25states that coordinator authentication, ownership, and sharing controls remain product boundaries in the supported trusted-team model.SECURITY.md:38-42lists authentication bypass, cross-owner access contrary to authorization, and trusted credential exposure to a lower-trust destination as in-scope examples.docs/security.md:43-48says per-lease Code origins keep lease-controlled Code HTML/JavaScript off the coordinator origin and off other lease origins for cooperative sharing and admin inspection. The same text classifies this as defense-in-depth and not hostile multitenant isolation; the accepted issue is scoped to the coordinator portal authority boundary, not hostile tenant isolation.docs/features/portal.md:58-77documents the intended isolated-origin flow and explicitly says missing or invalid configuration keeps existing same-origin Code behavior for compatibility.docs/features/portal.md:81-103documents that portal pages usecrabbox_session, that the Worker converts it internally to bearer auth for portal pages, and that/v1API tokens are separate and not echoed to the browser.worker/src/fleet.ts:4881-4882routes/portal/leases/{id}/code/...tocodePortalProxy().worker/src/code-origin.ts:11-34returnsundefinedfor missing or invalid templates.worker/src/fleet.ts:7077-7080redirects to an isolated Code origin only whencodeOriginForLease()returns one; otherwise the request proceeds tocodeProxyHTTP()atworker/src/fleet.ts:7105.worker/src/fleet.ts:7411-7417returns the bridged Code HTTP response withcodeResponseHeaders().worker/src/fleet.ts:13254-13301allows script execution and same-origin connections on proxied Code responses.worker/src/coordinator-entry.ts:86-106authenticates/portalrequests, andworker/src/coordinator-entry.ts:191-201converts thecrabbox_sessioncookie toAuthorization: Bearer ...for those portal requests.worker/src/oauth.ts:660-668setscrabbox_sessionwithPath=/, making it available to same-origin/portal/leases/{id}/code/...and other/portal...requests.worker/src/fleet.ts:5729-5780demonstrates a same-origin portal JSON/read-write target for lease sharing when the viewer can manage that lease, andworker/src/fleet.ts:5293-5328demonstrates a portal POST action that releases/deletes manageable leases.worker/test/fleet.test.ts:13498-13524confirms proxied Code HTML receives a VS Code-compatible CSP and cannot setcrabbox_session;worker/test/fleet.test.ts:13542-13552confirmscrabbox_sessionis not forwarded to the Code backend. Those are useful controls, but they do not prevent browser-side same-origin requests to/portalroutes.cvssPython package returned(9.0, 9.0, 9.1)forCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:Hand(9.3,)forCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H.Suggested regression coverage:
CRABBOX_CODE_ORIGIN_TEMPLATEand asserts browser Code HTML is not served as executable coordinator-origin content.