Skip to content

feat(demo): opt-in public sandbox scans + BomLens SBOM upload (CAX11-friendly) - #519

Merged
haksungjang merged 1 commit into
mainfrom
feat/demo-sandbox-scan
Jul 25, 2026
Merged

feat(demo): opt-in public sandbox scans + BomLens SBOM upload (CAX11-friendly)#519
haksungjang merged 1 commit into
mainfrom
feat/demo-sandbox-scan

Conversation

@haksungjang

Copy link
Copy Markdown
Contributor

Lets a read-only public demo offer a bounded live scan / SBOM ingest without surrendering the read-only safety boundary — cheaply enough to run on a ~4 GB box (Hetzner CAX11, ~$4/mo), so the demo doesn't need the $18/mo CAX31 the runbook originally assumed.

The architecture

Heavy scanning happens on the visitor's own machine (BomLens, local-first); the demo runs only tiny scans + cheap SBOM ingest. Small inputs (≤10 MiB) scan live in the demo; larger projects are scanned in BomLens and the resulting CycloneDX SBOM is uploaded — ingest is light (trivy sbom + conformance), so even large projects cost the demo box almost nothing. This mirrors the real product story (BomLens scans, TRUSCA governs).

Carve-out (off by default — both flags required)

  • DEMO_ALLOW_SANDBOX_SCANS gates a middleware allow-list extension opening exactly POST /v1/projects/{id}/scans and .../sbom-ingest, matched on the normalized path (traversal-safe). Flag off ⇒ demo is 100% locked as before.
  • Service-layer guards confine writes to the seeded "Demo Sandbox" project (403 otherwise) and to kind==source (container / sbom kinds → 422, so no unguarded trivy image <ref> pull / SSRF). Ordering: 404 → 403 → 422.
  • Boot-time validate_demo_sandbox_limits() fails fast if the carve-out is on but safe ceilings aren't applied — a deploy that forgot the overlay refuses to start wide-open.
  • /health surfaces demo_sandbox_scans (= read_only AND allow) for the SPA.

Deployment

  • docker-compose.demo.yml: worker mem_limit 2g + memswap_limit 2g + --concurrency 1 (a runaway scan is OOM-killed inside its container, not the host), small-box sizing, the demo scan safety rails (10 MiB source, 10 MiB / 3000-component SBOM, scancode off, 5/min), no extra port exposure.
  • Runbook (EN/KO): CAX11 (~$4) path, demo scan-mode section, mandatory demo-reset timer (daily wipe + reseed of the shared sandbox).

Frontend

  • SPA re-enables scan + SBOM upload only on the Demo Sandbox project when demo_sandbox_scans is true; ingest dialog, a ≤10 MiB / "use BomLens for larger" notice, and a shared-public-sandbox warning. EN/KO.

Security (Producer-Reviewer, two rounds)

First review found container-scan SSRF (H), non-sandbox write reach (H), and limits decoupled from the flag (M). All fixed; the closing review confirms 6/6 findings closed, no regressions, carve-out safe to enable (bypass paths — webhooks, rematch — verified blocked). Remaining items are Info-level.

Testing

  • Backend: 4155 unit green, mypy 549 clean, ruff clean; integration guards (sandbox source 202, container 422, non-sandbox 403, carve-out-off regression) on real Postgres.
  • Frontend (Node 20): typecheck + 1470 unit green.
  • docker-compose -f docker-compose.yml -f docker-compose.demo.yml config merge-validated.

Ships disabled — enabling requires the demo overlay + both flags + reset timer (the boot check enforces the bounds).

…friendly)

Lets a read-only public demo offer a bounded live scan / SBOM ingest
without surrendering the read-only safety boundary, cheaply enough to run
on a ~4 GB box (Hetzner CAX11, ~$4/mo).

Carve-out (off by default; both flags required):
- DEMO_ALLOW_SANDBOX_SCANS gates a middleware allow-list extension that
  opens exactly POST /v1/projects/{id}/scans and .../sbom-ingest, matched
  on the normalized path (traversal-safe). Flag off = 100% locked as before.
- Service-layer guards confine writes to the seeded "Demo Sandbox" project
  (403 otherwise) and to kind==source (container/sbom kinds 422 — no
  unguarded trivy-image ref pull / SSRF). Order: 404 -> 403 -> 422.
- Boot-time validate_demo_sandbox_limits() fails fast if the carve-out is
  on but the safe ceilings aren't applied (source 10 MiB, concurrency 1,
  scancode off, SBOM 10 MiB / 3000 components) — so a deploy that forgot
  the overlay refuses to start wide-open.
- /health surfaces demo_sandbox_scans (read_only AND allow) for the SPA.

Deployment:
- docker-compose.demo.yml overlay: worker mem_limit 2g + memswap_limit 2g
  (+ --concurrency 1), small-box sizing, the demo scan safety rails, no
  extra port exposure. Runbook (EN/KO) gains the CAX11 (~$4) path, a demo
  scan-mode section, and mandatory demo-reset timer (daily, shared sandbox
  is wiped + reseeded).

Frontend:
- SPA re-enables scan + SBOM-upload only on the Demo Sandbox project when
  demo_sandbox_scans is true; SbomIngestDialog, a ≤10 MiB / "use BomLens
  for larger" notice, and a shared-public-sandbox warning. EN/KO.

Security: Producer-Reviewer, two rounds. Initial review found container-scan
SSRF, non-sandbox write reach, and unbound limits; all fixed and confirmed
closed in the re-review, no regressions.

Backend 4155 unit green, mypy 549, ruff; FE typecheck + 1470 unit green;
compose overlay merge-validated.
@haksungjang
haksungjang merged commit 87b5ba1 into main Jul 25, 2026
20 checks passed
@haksungjang
haksungjang deleted the feat/demo-sandbox-scan branch July 25, 2026 06:57
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.

1 participant