Skip to content

[Security] Fail closed before exposing privileged frontend APIs#246

Open
fettpl wants to merge 5 commits into
sybil-solutions:mainfrom
fettpl:p1/issue-221-frontend-auth
Open

[Security] Fail closed before exposing privileged frontend APIs#246
fettpl wants to merge 5 commits into
sybil-solutions:mainfrom
fettpl:p1/issue-221-frontend-auth

Conversation

@fettpl

@fettpl fettpl commented Jul 17, 2026

Copy link
Copy Markdown

Summary

Fails closed before a production web deployment can expose Local Studio's privileged terminal, filesystem, connector, browser, and coding-agent surfaces. Production now requires an operator token or an explicit unauthenticated acknowledgement, while desktop bypass is tied to explicit desktop identity and loopback binding.

Closes #221

Dependency

Depends on #244, whose release-gating repair keeps the current repository quality gate green. The access-control implementation is independently reviewable on top of that prerequisite.

Root cause and threat model

Frontend access is equivalent to shell and filesystem access as the Local Studio host user. The previous production posture admitted requests when no frontend token was configured, and LOCAL_STUDIO_DATA_DIR incorrectly acted as a desktop identity signal. Query-token exchange also placed the shared secret in URLs, where browser history, reverse proxies, access logs, and telemetry could retain it.

Token-gating only browser navigation was insufficient because the standalone agent runtime calls selected frontend routes and both processes can spawn descendants. Operator and callback credentials therefore also had to be contained across the runtime proxy, MCP processes, terminal commands, Git commands, logs, errors, and child environments.

Solution design

  • Add one fail-closed access-posture model shared by middleware, Node route guards, standalone startup, and tests.
  • Require LOCAL_STUDIO_FRONTEND_TOKEN or explicit LOCAL_STUDIO_FRONTEND_ALLOW_UNAUTHENTICATED=true in production web mode.
  • Restrict desktop bypass to LOCAL_STUDIO_DESKTOP=1 with an explicit loopback HOSTNAME; LOCAL_STUDIO_DATA_DIR has no authorization effect.
  • Replace query-token exchange with a bounded form POST that compares the token server-side and sets only an HttpOnly, SameSite cookie.
  • Reject credential-like query parameters before logging, except exact lowercase api_key on /api/proxy/** where the proxy consumes it, removes it from the forwarded URL, and converts it to the controller authorization header.
  • Create a process-local callback credential accepted only by the exact plan, canvas, connector-call, and browser callback operations required by the runtime.
  • Scrub reserved frontend credentials from runtime descendants, terminal and Git children, and MCP process environments.
  • Restrict runtime proxy headers so cookies, operator/callback tokens, authorization, origin, referer, and arbitrary headers cannot cross the boundary.
  • Make standalone production own its matching loopback runtime bundle and reject unsafe or unverifiable reuse.
  • Update remote deployment to synchronize the shared runtime modules and retire the legacy standalone runtime service.

Acceptance criteria

  • Unsafe production configuration exits before spawning either server.
  • Development, desktop, authenticated production, and explicit unauthenticated production postures remain supported.
  • Query tokens are rejected and cannot create a frontend session.
  • Valid bounded POST exchange sets only the HttpOnly cookie; invalid, oversized, or unsupported bodies fail closed.
  • Node route guards enforce the same posture independently of middleware.
  • Callback credentials are accepted only by exact required routes and methods.
  • Runtime, MCP, terminal, and Git descendants cannot inherit reserved frontend credentials.
  • Runtime proxy headers and errors do not disclose or forward frontend credentials.
  • Controller api_key query compatibility is limited to the proxy boundary and is consumed before forwarding or logging.
  • Remote deployment transfers runtime ownership to the frontend without generating or persisting operator secrets.

Validation

  • Reviewed and published exact head: bf577aa9a55f3ff42464f91a99d76c173fdd70a5.
  • npm --prefix frontend run test:access-control: 25 tests, 0 failures.
  • npm --prefix frontend run check:quality: passed, including the access-control suite and production build.
  • npm run check: passed, including contracts, structure, frontend, and controller gates.
  • npm --prefix services/agent-runtime run build: passed.
  • Normal pre-push hook: passed again while publishing the exact reviewed head.
  • Independent P0/P1 review of the exact head: approved with no findings.
  • npm --prefix frontend run desktop:dist: passed.
  • Canonical /Applications/Local Studio.app reinstall: strict deep code-signature verification passed and bundle ID is org.local.studio.desktop.
  • The existing persisted desktop profile reproduced the separately tracked [Reliability] Make launch acceptance and cleanup atomic #226 agent-runtime startup timeout. The same installed bundle with fresh isolated desktop state started its packaged runtime and frontend, and GET /api/desktop-health returned HTTP 200 with {"ok":true}.
  • A Developer ID identity was unavailable locally, so the verification bundle was ad-hoc signed with repository entitlements; no release asset was signed or published.

Current main does not define npm run test:integration or a generic npm --prefix frontend run test script, so those unavailable commands are not represented as passing.

UI changes

Adds an access form composed from existing UI-kit primitives. No new design-system primitive is introduced.

Risks and rollout

  • This is intentionally breaking for unsafe production web deployments: operators must configure a token or explicitly acknowledge unauthenticated host access.
  • The deployment script never generates, prints, copies, or silently persists an operator secret; remote frontend/.env.local remains operator-managed.
  • Token-gated standalone mode refuses a pre-existing runtime because it cannot prove that process received the ephemeral callback credential.
  • Merge [CI/CD] Gate every release on the exact commit that passed CI #244 first, then refresh this branch onto main if GitHub does not remove the stacked prerequisite automatically.
  • Fork-originated GitHub Actions require upstream approval before jobs are created.

@fettpl
fettpl force-pushed the p1/issue-221-frontend-auth branch from b2f1116 to 64383a1 Compare July 17, 2026 22:59
@fettpl

fettpl commented Jul 17, 2026

Copy link
Copy Markdown
Author

@0xSero This ready/non-draft fork PR now points to independently reviewed exact head bf577aa. Its 25-test access-control suite, full repository check, intact pre-push quality/build hook, production desktop package, strict installed signature, and isolated live desktop-health validation passed. Current-head workflows are waiting at GitHub’s fork approval gate: CI run 29732075157 and Security run 29732075003. Please approve these runs when convenient.

@fettpl
fettpl marked this pull request as ready for review July 18, 2026 04:59
@fettpl
fettpl requested a review from 0xSero as a code owner July 18, 2026 04:59
@fettpl
fettpl force-pushed the p1/issue-221-frontend-auth branch from 9b854b5 to bf577aa Compare July 20, 2026 09:36
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.

[Security] Fail closed before exposing privileged frontend APIs

1 participant