SPB Git

spb/autoscholar

Public
15.2 KB · 40 lines markdown
1# Security — AutoScholar.ai23Status: living document, updated per phase. Everything listed under _Enforced_ is implemented in code and covered by tests named in docs/TEST_REPORT.md; items under _Limitations_ are honest gaps.45## Threat model summary67| #   | Threat (from the build spec §11)                                                              | Enforcement                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Evidence                                                                                                                                |8| --- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |9| 1   | Credential attacks on the single admin account, incl. `admin123` after rotation               | Argon2id (m=64 MiB, t=3, p=4); bootstrap password used only while `admin_users` is empty, flagged `must_change_password`; rotation revokes all sessions and rotates the token; old hashes kept in `password_changes` and rejected; per-IP + per-user rate limits (20/15 min, 10/15 min) and exponential lockout from 5 failures (1 min → 1 h); bootstrap password reuse refused by policy                                                                                                                                                                                                                                                                                                                           | integration test "bootstraps admin123 once, forces rotation, rejects reuse", "locks out after repeated failures"; e2e admin login tests |10| 2   | Broken access control to admin/internal APIs, logs, unpublished papers                        | Every `/api/admin/*` handler calls `requireAdmin()` (server-side session lookup, idle/absolute expiry, CSRF on mutations, same-origin check, mutation rate limit); `/api/internal/*` rejects requests carrying tunnel headers (`X-Forwarded-*`), enforces the private CIDR allow-list and the worker bearer token (SHA-256 stored, `asw_` prefix for redaction); public queries only read `papers`/`paper_versions` projections, never run/worker tables; private artifacts only through the admin route                                                                                                                                                                                                            | e2e: every admin route returns 401 signed-out; integration: worker auth/disable/quarantine                                              |11| 3   | Malicious Markdown / XSS in uploaded `CLAUDE.md` and metadata                                 | Upload accepts UTF-8 `.md`/`.txt` ≤ 256 KiB, rejects control chars, path separators, platform-targeting patterns; original bytes + SHA-256 stored; preview rendered server-side with `marked` + `sanitize-html` strict allowlist; CSP (`default-src 'self'`, no inline scripts except Next runtime)                                                                                                                                                                                                                                                                                                                                                                                                                 | unit tests `validateInstruction`; e2e XSS preview test                                                                                  |12| 4   | Prompt injection from web pages, PDFs, datasets, repositories                                 | Platform policy lives in the SDK options (hooks, `disallowedTools`, sandbox), not in prompts; fetched content cannot add tools or permissions; `PreToolUse` hook denies LAN/loopback/IP-literal/cluster-name URLs for WebFetch and MCP tools (DNS-resolved SSRF guard); reviewer session is write-restricted to `review/`                                                                                                                                                                                                                                                                                                                                                                                           | selftest (`autoscholar-worker selftest`), Phase 5 malicious-instruction run                                                             |13| 5   | Agent access to cluster admin, SSH keys, password stores, other workspaces, production source | Worker runs as dedicated non-admin hidden user `asworker` under a LaunchDaemon; workspaces `/var/autoscholar/runs/<run-id>` (0750 root dir); SDK sandbox (Seatbelt): writes limited to the workspace, `denyRead` on `/Users`, `/etc/autoscholar`, keychains, sudoers; hook denies `sudo/ssh/launchctl/pfctl/dscl/pm2/brew/...`, paths under `/Users`, `~/.ssh`, other run directories; `disallowedTools` scoped `Bash(sudo *)` etc. apply in every mode; env of the SDK subprocess is replaced (only PATH, isolated HOME/CLAUDE_CONFIG_DIR, API key); no SSH keys on the worker user; platform source lives in `/var/autoscholar/bundle` owned by asworker but not writable from the sandbox (outside `allowWrite`) | selftest + Phase 5 denial log                                                                                                           |14| 6   | SSRF, path traversal, symlink escape, sandbox escape, shell injection, unsafe archives        | Storage keys normalised and root-checked (`assertSafeKey`); artifact descriptor rejects `..`/absolute paths; hook resolves real paths (symlink-safe) for Write/Edit; Seatbelt profile for the platform-side reproduction step; no shell interpolation of uploaded text anywhere (uploaded Markdown only ever becomes a file on disk)                                                                                                                                                                                                                                                                                                                                                                                | storage unit tests; policy hook                                                                                                         |15| 7   | Overly broad worker access to storage/DB                                                      | Workers never see the DB or the artifact store: only the HTTP protocol with lease-scoped fencing tokens; uploads are server-hashed and stored under server-generated keys                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | integration fencing tests                                                                                                               |16| 8   | Queue duplication, stale lease completion, cancellation races, unauthorized publish decisions | Idempotency key unique index; `SELECT … FOR UPDATE SKIP LOCKED` leasing; monotonic fencing token per run checked on every worker call; lease expiry → retry with checkpoint; publish decision computed by `services/qa` on the server from artifacts, never from model text; publication idempotent by `paper_versions.run_id` unique index and sequence allocation inside the transaction                                                                                                                                                                                                                                                                                                                          | integration: idempotent launch (3 concurrent), fencing, crash/expiry, cancel race, publication concurrency                              |17| 9   | Secret exfiltration via logs, outputs, PDFs, citations, telemetry                             | `redact()` applied to all log lines and all run events before persistence; QA hard gate `secrets.none` scans text artifacts for key patterns; `input/`, `provenance/events.jsonl`, `.env`, key files are never promoted to public; SDK subprocess env contains no worker token; selftest asserts worker.env never appears in output                                                                                                                                                                                                                                                                                                                                                                                 | integration: events contain no `sk-ant-`; gate unit coverage                                                                            |18| 10  | Resource exhaustion, uncontrolled bills, infinite retries, oversized artifacts                | Manifest caps `maxBudgetUsd` (≤ 2000), `maxRuntimeMinutes`, `maxAttempts` (≤ 5) enforced by the scheduler and the runner (per-stage `maxBudgetUsd`/`maxTurns`/wall-clock abort); scheduler cancels runs over the runtime cap; artifact ≤ 512 MiB (ingest) / 256 MiB (runner), instruction ≤ 256 KiB, JSON bodies bounded; attempts stop at `maxAttempts`                                                                                                                                                                                                                                                                                                                                                            | integration: runtime cap, attempt limit                                                                                                 |19| 11  | Accidental public release of private/PII/copyrighted material                                 | Promotion allow-list (`PUBLIC_PROMOTION_RULES`) + deny-list; `synthetic` artifacts can never be published; retraction and artifact withdrawal (HTTP 410) keep tombstones; literature connectors store metadata + links, not full texts                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | integration publication test checks `input/CLAUDE.md` and `code/.env` absent from the public bundle                                     |20| 12  | Supply chain, TLS, backups                                                                    | pnpm lockfile, pinned ranges; `pnpm audit` in CI; TLS by Caddy on the MacLustr gateway; HSTS in production; nightly backups (docs/OPERATIONS.md)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | Phase 8                                                                                                                                 |2122## Execution isolation on macOS workers (what is and is not a boundary)2324- **Is a boundary:** separate POSIX user (`asworker`, non-admin, hidden), Seatbelt sandbox for every Bash/tool command (write scope = workspace, read denials on sensitive paths, hostname-allowlisted egress proxy, no local binding), pf rules per uid blocking RFC1918/CGNAT/link-local egress except the control plane, platform hooks (deny beats everything), replaced process environment.25- **Is not a boundary by itself:** the working directory. We do not claim it is. The Seatbelt profile and the user separation are the boundaries; the hook is defense in depth.26- **Not available today:** Linux containers/VMs (no Docker daemon running on any node). If stronger isolation is required (e.g., untrusted binary workloads), block dispatch until a container host exists; the worker refuses to run when the sandbox is unavailable (`failIfUnavailable: true`, `sandboxAvailable=false` ⇒ never leased).2728## Session and transport2930- Cookie `as_session`: HttpOnly, Secure (production/https), SameSite=Strict, 12 h absolute, 2 h idle; opaque random token, SHA-256 stored; CSRF = HMAC(session secret, token hash) echoed in `x-csrf-token`.31- Admin and public traffic: HTTPS via Caddy (automatic certificates) on the MacLustr gateway → WireGuard → node. Internal worker traffic: LAN only, bearer token; not reachable through the gateway (header check) and limited to `INTERNAL_API_ALLOWED_CIDRS`.32- Security headers: CSP, X-Frame-Options SAMEORIGIN, nosniff, Referrer-Policy, Permissions-Policy, HSTS (prod).3334## Known limitations / follow-ups3536- No mTLS between worker and control plane (no PKI); tokens are revocable and scoped. Rotation endpoint: re-enroll with the enrollment secret.37- The SDK sandbox proxy is hostname-based and does not inspect TLS; allowlisted hosts are trusted at the hostname level.38- MFA (TOTP/passkey) is scaffolded in the schema (`totp_secret`) but not enabled in v1.39- The clientIp used for limits relies on `X-Forwarded-For` only when `TRUST_PROXY=1` (behind Caddy); direct LAN requests carry no IP (Next.js does not expose the socket), so per-IP limits apply per deployment edge.40
Files · main
Menu

Theme