mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
2 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
369d136d19
|
Add Docker Compose deployment workflow (#65)
* Add Docker Compose deployment workflow * Address Docker deployment review feedback Harden publishing inputs and temporary credential handling, and tighten Docker runtime defaults requested by the PR review. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix Docker publish build in CI mode Set CI=true during the image build so pnpm prune can run non-interactively inside Docker. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix Docker runtime dependency layout Use pnpm deploy for the daemon package so the runtime image includes production dependencies where Node resolves them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Use legacy pnpm deploy in Docker build Allow pnpm v10 deploy to package the daemon workspace without requiring injected workspace packages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Align Docker runtime with Node 24 Use Node 24 for both build and runtime stages and update image verification for the workspace daemon dependency layout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove legacy OD_HOST Docker binding fallback Use OD_BIND_HOST as the single daemon bind-host setting for Docker deployment and origin validation. * Update Docker image verifier for daemon dist runtime Check the packaged daemon dist entrypoint and allow npm from the Node 24 runtime image while still rejecting build-only tools. * Allow private LAN browser origins for daemon * Share daemon origin validation helpers Move browser origin validation into a shared daemon module so tests exercise the production logic and cover the remaining private LAN edge cases. * Harden Docker Compose port exposure Bind the Compose deployment to localhost by default and pass the published port through to the daemon origin checks so host-port overrides remain same-origin. * Keep deployment hosts out of local-only no-origin checks Require an actual matching Origin before configured deployment origins can satisfy local-only daemon guards, preventing no-Origin remote clients from bypassing those checks. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: mrcfps <mrc@powerformer.com> Co-authored-by: lefarcen <935902669@qq.com> |
||
|
|
339a57c65c
|
fix(security): bind daemon to localhost by default, add origin validation (#365)
* fix(security): bind daemon to localhost by default, add origin validation middleware The daemon's Express server previously defaulted to binding 0.0.0.0, exposing all 30+ API endpoints to the local network with zero authentication. This included endpoints that spawn CLI agents, write project files, and manage API keys. Changes: - Default bind address changed from 0.0.0.0 to 127.0.0.1 in both server.ts and cli.ts. Users who need network access can still set OD_BIND_HOST=0.0.0.0 explicitly. - Added origin validation middleware on /api/* routes: requests with an Origin header are only allowed from localhost/127.0.0.1/[::1] on the daemon's own port. Non-browser clients (no Origin header) are unaffected. - 7 unit tests for the origin validation logic. - Existing tests: 327/327 passing. * fix: address PR review — fail-closed, OD_WEB_PORT, HTTPS, Origin: null Address feedback from @lefarcen, @mrcfps, and Codex review: P1: Fail-closed when resolvedPort is not yet set (returns 403 instead of passing through). P2: Include OD_WEB_PORT in allowed origins for split-port proxy setups (web port ≠ daemon port). Add HTTPS variants. P3: Exempt Origin: null for sandboxed iframe preview fetches (/api/projects/:id/raw/*) so artifact previews keep working. P4: Update help text from 0.0.0.0 to 127.0.0.1. Test coverage expanded to 13 tests including: - OD_WEB_PORT split-port proxy scenario - Origin: null iframe preview - HTTPS origin variants - Fail-closed before port resolution - Cross-origin rejection with OD_WEB_PORT set All 333 tests passing, TypeScript clean. * fix: scope Origin: null bypass to raw-file previews only, support non-loopback bind host Two issues found in second review round: 1. Origin: null was a global bypass for ALL /api routes, allowing sandboxed iframes to reach state-changing endpoints (project create/delete, agent runs). Now only GET requests to /projects/:id/raw/* pass through with Origin: null — the original intent for iframe file previews. POST/DELETE with Origin: null are rejected. 2. Allowed origins only included loopback addresses. When daemon binds to a non-loopback address (--host for Tailscale, LAN, or 0.0.0.0), browser requests from that address would get 403. Now the bound host is included in allowed origins alongside loopback, keeping the documented network-access escape hatch working for browser clients. Added negative tests for Origin: null on POST/DELETE/non-raw GET, and positive tests for non-loopback bind host scenarios. All 339 tests passing. * fix: centralize origin policy, add spritesheet to Origin: null allowlist Addresses @mrcfps's third review round: 1. isLocalSameOrigin() now uses the same policy as the global origin middleware: HTTPS + HTTP, OD_WEB_PORT, and the explicit bind host (OD_BIND_HOST). Previously it only accepted HTTP on loopback hosts, so requests from https://127.0.0.1 or a Tailscale address could pass the global guard but 403 on per-route checks. 2. /api/codex-pets/:id/spritesheet is a read-only route that sets Access-Control-Allow-Origin: null for canvas drawing by sandboxed iframes. Added to the Origin: null allowlist so the middleware doesn't block it before the route handler runs. 3. buildAllowedOrigins() extracted as a closure so both the middleware and isLocalSameOrigin() share identical logic. 340/340 tests passing. * test: remove dead fail-closed test with unused 5th argument The 'fails closed when port is 0' test called request() with an extra argument the helper ignores, then never asserted on res. Real coverage lives in the dedicated 'fail-closed before port resolution' describe block. |