Commit Graph
1 Commits
Author SHA1 Message Date
Simon AxelssonandClaude Opus 5 8ebc90ffa3 fix(security,repo-honesty): real security headers + a doctor that checks truth, not presence
masterplan-lock / check (pull_request) Successful in 4s
masterplan-lock / check (push) Successful in 7s
CI (SIAX Cloud) / security (pull_request) Successful in 17s
CI (SIAX Cloud) / security (push) Successful in 18s
CI (SIAX Cloud) / quality (push) Successful in 39s
CI (SIAX Cloud) / integration (pull_request) Successful in 41s
CI (SIAX Cloud) / integration (push) Successful in 44s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 1m37s
Security Scan (SIAX Cloud) / security (push) Successful in 1m35s
CI (SIAX Cloud) / quality (pull_request) Successful in 3m4s
Two defects of the same kind: the repo asserted things about itself that were
not true, and nothing in it could tell.

A. CHANGELOG.md claimed CSP/HSTS/X-Frame-Options since 2026-08. `main`
   contained none of it -- a repo-wide grep for
   `content-security-policy|strict-transport-security|x-frame-options` matched
   exactly one file, CHANGELOG.md itself. The only header on 0pt.app was
   Vercel's own HSTS default, which does not survive the move to
   Coolify/Traefik that siax.config.json and .gitea/workflows/cd.yml are
   already pointed at.

   - next.config.mjs: `headers()` on `/(.*)` with CSP (frame-ancestors 'none',
     base-uri 'self', object-src 'none', form-action 'self'), HSTS,
     X-Frame-Options DENY, X-Content-Type-Options, Referrer-Policy,
     Permissions-Policy; plus poweredByHeader: false. Same idiom as
     sax3l/siax-io's next.config.mjs.
   - Carve-outs re-derived against THIS tree, not inherited: the stale
     `ci/security-headers-and-scanning` branch (8d2ff73, 142 commits behind)
     carried a `form-action https://checkout.stripe.com` allowance justified
     by a redirect that is actually `window.location.href`, which form-action
     does not govern -- dropped rather than kept as decorative permission.
     frame-src is derived from NOCODB_BASE_URL/METABASE_SITE_URL, the same env
     the components/embed.tsx iframe is configured from, so wiring that
     component up cannot be silently blanked by this header (the failure
     sax3l/siax-io recorded as SIO-013). 'unsafe-eval' is dev-only.
   - tests/security/security-headers.test.ts: blocking regression gate inside
     `pnpm run test`, i.e. the required "CI (SIAX Cloud) / quality" context.
     Asserts against expectations written out in the test, not imported from
     the config. Proven to fail: removing X-Frame-Options -> `pnpm run test`
     exits 1; weakening frame-ancestors to 'self' -> exits 1.

B. scripts/siax-doctor.mjs validated that siax.repo.json fields were PRESENT,
   never what they said. Measured on this tree: operationalState "running"
   (false) and "down" (true) both gave 40/40, exit 0, byte-identical output.

   - Value checks (enums, git.siax.io const, fleet node pattern, bare-hostname
     form), cross-file consistency (declared prod hostnames must appear in
     this repo's own siax.config.json / contracts/openapi.yaml, and vice
     versa), and GOV-06 substantiation: operationalState must be backed by a
     dated probe record in evidence/operational-state.json that agrees with
     it. Liveness is not knowable offline; whether a liveness CLAIM is backed
     is. Offline, deterministic, no network -- CI can never go red for want of
     an internet connection.
   - `--live` (opt-in) probes the declared hosts for real and checks the
     headers on the wire; `--write-evidence` refreshes the record. Not wired
     into CI on purpose.
   - tests/repo/siax-doctor.test.ts runs the doctor inside `pnpm run test` and
     asserts the negative cases against fixture trees (SIAX_DOCTOR_ROOT), so
     the "cannot fail" defect cannot come back.

C. CHANGELOG.md is now true. The header claim stands; the four claims it was
   bundled with (Trivy --exit-code 1, gitleaks, SHA-pinned actions, SBOM +
   license artifacts) are still false on main and are listed as not
   implemented instead of left asserted. All four live in .gitea/workflows/**,
   a protected path on this repo, so they need an authorised workflow change.

Nothing under .gitea/workflows/** or .gitignore is touched.

Verified: pnpm run typecheck exit 0; pnpm run test exit 0 (74 files / 965
tests passed, up from 72/945); pnpm run build exit 0 and the built
.next/routes-manifest.json carries all six headers; node scripts/siax-doctor.mjs
55/55 exit 0; --live 57/57 exit 0. eslint on the new files exit 0 (repo lint is
advisory and its 17 pre-existing problems are unchanged).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 02:43:32 +02:00