feat(security): add response security headers + make CI security gates real #19

Closed
admin wants to merge 2 commits from ci/security-headers-and-scanning into main
2 Commits
Author SHA1 Message Date
admin d6adad7caf fix(ci): use actions/upload-artifact@v3, not v4, on this Gitea runner
Security Scan (SIAX Cloud) / gitleaks (push) Successful in 9s
Security Scan (SIAX Cloud) / license-inventory (push) Successful in 27s
CI (SIAX Cloud) / security (push) Successful in 16s
Security Scan (SIAX Cloud) / trivy (push) Failing after 36s
CI (SIAX Cloud) / quality (push) Successful in 2m8s
CI (SIAX Cloud) / security (pull_request) Successful in 21s
Security Scan (SIAX Cloud) / gitleaks (pull_request) Successful in 8s
Security Scan (SIAX Cloud) / trivy (pull_request) Failing after 18s
Security Scan (SIAX Cloud) / license-inventory (pull_request) Successful in 23s
CI (SIAX Cloud) / quality (pull_request) Successful in 2m21s
Verified empirically by pushing and inspecting the real run
(git.siax.io/sax3l/0pt/actions/runs/4788): v4 (pinned SHA) failed with
conclusion=failure on both upload steps. continue-on-error already kept
this from breaking the build, but the artifacts silently weren't being
saved. v3.2.2 is the known-compatible version for self-hosted Gitea's
artifact backend; re-verify after push.
2026-08-11 13:50:39 +02:00
admin 8d2ff738c3 feat(security): add response security headers + make CI security gates real
BACKLOG P0-13: next.config.mjs now sets HSTS, X-Frame-Options,
X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and a
same-origin CSP on every route. Previously the only security header came
from Vercel's platform default (HSTS) -- nothing was set by the app itself,
so it would have silently disappeared on cutover to Coolify/Traefik.
Verified at runtime (curl -I against a production build), not just that the
build compiles.

BACKLOG P0-16: Trivy in security.yml/nightly.yml now actually fails the
build on HIGH/CRITICAL findings (`--exit-code 1`) instead of the previous
`--exit-code 0` (purely advisory, never blocked a merge). Verified locally
against this repo's real dependency tree -- 19 HIGH findings currently exist
in pnpm-lock.yaml (next.js, postcss, sharp, nanoid), several in next.js
itself relating to middleware/auth bypass. See PR description: this is
expected to turn the security.yml `trivy` job red until those are bumped in
a follow-up -- that's the fix working as intended, not a regression, but it
needs a human decision on timing given the sibling auth-gate PR (#11).

`services/scrapers/` stays explicitly excluded from the scan
(--skip-dirs) -- that tree is a dated, deliberate keep-decision under
active review in a separate thread (BACKLOG P3-3) and is out of scope here.

BACKLOG P0-19: dedicated gitleaks job added to security.yml, full git
history (--log-opts=--all), pinned + checksum-verified binary. Verified
locally against this repo's actual history first (81 commits, no leaks) so
this blocking gate doesn't open red on day one.

BACKLOG P1-14: SBOM (CycloneDX) + license inventory (`pnpm licenses list`)
generated as informational, non-blocking build artifacts. actions/checkout,
actions/setup-node, actions/cache, actions/upload-artifact all pinned to
commit SHA (was floating @v4 tags) across every workflow file, Trivy/gitleaks
pinned to specific released versions instead of floating install scripts.
cosign image-signing explicitly NOT added -- there is no container-registry
publish step in this repo's CI to attach a signature to yet (Coolify builds
directly from git); flagging as a real follow-up once that pipeline exists.

BACKLOG P1-15: explicit `permissions: contents: read` + `concurrency` added
to every workflow that lacked them (only .github/workflows/ci.yml already
had concurrency). Pinned pnpm-store cache added to ci.yml's quality job.
Root CODEOWNERS added (`* @sax3l`) so Gitea has someone to assign once
required-approvals is turned on (separate repo-settings change, not a file
change). The "doctor step" part of P1-15 is added in the follow-up repo-
hygiene PR, alongside the `siax:doctor` script it invokes.
2026-08-11 13:50:38 +02:00