Skip to content

fix(security): require browser origin for folder import when desktop gate inactive (#5480)#6073

Open
CVE-Hunter-Leo wants to merge 1 commit into
nexu-io:mainfrom
CVE-Hunter-Leo:fix/folder-import-auth-gate-5480
Open

fix(security): require browser origin for folder import when desktop gate inactive (#5480)#6073
CVE-Hunter-Leo wants to merge 1 commit into
nexu-io:mainfrom
CVE-Hunter-Leo:fix/folder-import-auth-gate-5480

Conversation

@CVE-Hunter-Leo

@CVE-Hunter-Leo CVE-Hunter-Leo commented Jul 25, 2026

Copy link
Copy Markdown

Summary

POST /api/import/folder and POST /api/projects/:id/working-dir previously accepted arbitrary baseDir from any non-browser local process when the desktop auth gate was inactive. This allowed unauthenticated directory binding (CWE-284/CWE-22).

Before: non-browser clients could bind arbitrary directories without auth
After: when desktop gate inactive, routes require browser Origin header

Desktop mode requests are unaffected - they continue through HMAC import token verification.

Changes

  • apps/daemon/src/import-export-routes.ts: add folderImportAuthorizationReason() shared helper, enforce in both routes
  • apps/daemon/tests/folder-import-auth-gate.test.ts: 4 integration test cases

Surface area

  • Routes: POST /api/import/folder and POST /api/projects/:id/working-dir
  • Function: folderImportAuthorizationReason() in apps/daemon/src/import-export-routes.ts
  • Impact: Non-browser local processes (curl, scripts, etc.) can no longer bind arbitrary directories when the desktop auth gate is inactive
  • Desktop mode: Unaffected — HMAC import token verification continues as before
  • Browser UI: Unaffected — requests with a valid Origin header pass the gate
  • Non-affected: All other import/export routes (ZIP import, project export, screenshot export, etc.)

Validation

  • Ran pnpm --filter @open-design/daemon test -- folder-import-auth-gate — all 4 integration test cases pass
  • Integration tests use supertest + express to verify:
    • POST /api/import/folder without Origin → 403 ✓
    • POST /api/import/folder with Origin: http://localhost:3000 → NOT 403 ✓
    • POST /api/projects/:id/working-dir without Origin → 403 ✓
    • POST /api/projects/:id/working-dir with Origin → NOT 403 ✓
  • Manual verification: curl to /api/import/folder without Origin header returns 403; browser request with Origin passes gate

Closes #5480

…gate inactive (nexu-io#5480)

POST /api/import/folder and POST /api/projects/:id/working-dir previously
accepted arbitrary baseDir from any non-browser local process when the
desktop auth gate was inactive (non-desktop mode). This allowed
unauthenticated directory binding — a local process could import any
folder as a project and read/write its contents via the project file API.

Fix: when isDesktopAuthGateActive() returns false, require a browser
Origin header on these routes. The server.ts origin middleware already
validates browser origins, so a non-empty Origin proves the request came
from the legitimate web UI. Desktop mode requests are unaffected — they
continue through the existing HMAC import token verification.

Adds a shared folderImportAuthorizationReason() helper that both routes
use, and regression tests covering rejection of non-browser requests and
acceptance of browser-origin requests.

Closes nexu-io#5480
@lefarcen

Copy link
Copy Markdown
Contributor

Thanks @CVE-Hunter-Leo — centralizing the auth gate for both folder import paths is the right direction for this security fix. I've requested review from @nettee.

One quick PR-body ask before pool review gets deeper: could you add the missing Surface area and Validation details? For this one, None may be the right Surface area choice if you view it as backend-only, and Validation should note what you ran for the new auth-gate coverage.

Also flagging this for manual QA before merge because it changes a live import path. Nothing needed from you right now — we'll queue QA once the PR is merge-ready.

@lefarcen
lefarcen requested a review from nettee July 25, 2026 10:54
@lefarcen lefarcen added size/M PR changes 100-300 lines risk/high High risk: apps/desktop, daemon, auth, migration, workflows, package deps type/bugfix Bug fix needs-validation Runtime change detected; needs human or /explore agent validation. labels Jul 25, 2026

@nettee nettee 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.

I found one blocking issue in the new folder-import auth gate; details are inline.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

function folderImportAuthorizationReason(req: import('express').Request): string | null {
if (isDesktopAuthGateActive()) return null;
const origin = req.headers.origin;
if (typeof origin !== 'string' || origin.length === 0) {

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.

folderImportAuthorizationReason() is treating the presence of an Origin header as proof that the request came from the web UI, but the daemon's own origin model does not give you that guarantee. server.ts explicitly allows non-browser clients when Origin is missing, and isAllowedBrowserOrigin() only checks that a supplied origin string looks like a same-host loopback/LAN origin. A raw local client can therefore send Origin: http://127.0.0.1:<port> (or another allowed loopback/LAN origin) and still reach these routes, so the unauthenticated directory-binding hole remains open even though the new no-Origin case is blocked. Please switch this gate to a credential that a local script cannot forge, such as keeping the existing desktop import-token/HMAC flow for these privileged routes or another explicit daemon-issued secret, and extend the regression coverage to prove that a non-browser request with a forged allowed Origin is rejected.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

@lefarcen

Copy link
Copy Markdown
Contributor

Hey @CVE-Hunter-Leo@nettee has the current blocker on this head covered: the new Origin-based gate is still forgeable by a raw local client, so the auth hole is not fully closed yet. Please address that review thread first, and when you update the PR body, also add the missing Surface area and Bug fix verification details so the next pass has the full context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-validation Runtime change detected; needs human or /explore agent validation. risk/high High risk: apps/desktop, daemon, auth, migration, workflows, package deps size/M PR changes 100-300 lines type/bugfix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug][Vulnerability]: Arbitrary Directory Binding via Unauthenticated Folder Import (Non-Desktop Gate) in Open Design

3 participants