From de1a36a0d660292f1009c8bc50de9979a03643d4 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Jun 2026 11:27:22 +0200 Subject: [PATCH] docs(devops): document two-token model, OSV alerts, and nightly-audit runbook in ci-gitea.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds §Renovate + Nightly Audit — Token Model covering: - Two-token model: RENOVATE_TOKEN vs NIGHTLY_AUDIT_TOKEN (issues-only), blast radius rationale, PAT rotation cadence (annual + on-compromise) - OSV vs platform alerts on Gitea — osvVulnerabilityAlerts is the load-bearing detector; Gitea exposes no vulnerability graph for vulnerabilityAlerts - Nightly vs PR gate divergence table (dev deps in/out) - Runbook: triage severity → pin/upgrade/override → close issue Refs #818. See ADR-041 for full rationale. Co-Authored-By: Claude Sonnet 4.6 --- docs/infrastructure/ci-gitea.md | 79 +++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/docs/infrastructure/ci-gitea.md b/docs/infrastructure/ci-gitea.md index 6d99f694..871eec78 100644 --- a/docs/infrastructure/ci-gitea.md +++ b/docs/infrastructure/ci-gitea.md @@ -462,3 +462,82 @@ jobs: name: e2e-results path: frontend/test-results/e2e/ ``` + +--- + +## Renovate + Nightly Audit — Token Model + +> See [ADR-041](../adr/041-renovate-runner-setup.md) for full rationale. + +### Two-token model + +This repo uses two separate tokens for automated dependency work, both manually +provisioned as Gitea secrets. There is **no auto-provided `GITEA_TOKEN`** on +self-hosted Gitea runners — it must be created manually (see §Context Variable Names +table above). + +| Secret | Scopes | Job | Reason | +|--------|--------|-----|--------| +| `RENOVATE_TOKEN` | `contents` + `pull_request` + `issues` | `renovate.yml` | Renovate needs to read/write files and open PRs | +| `NIGHTLY_AUDIT_TOKEN` | `issues` only | `nightly.yml` → `npm-audit` job | Only needs to file a tracking issue; an `issues`-only token cannot push branches or read contents — limits blast radius on leak | + +Both tokens belong to a single dedicated bot account. **Branch protection on `main` +must forbid the bot pushing directly**, because a `contents`-scoped token can push +to any unprotected branch. + +### PAT rotation cadence + +Rotate both tokens: +- **Annually** (calendar reminder) +- **Immediately** on suspected compromise (runner log leak, accidental `set -x`, etc.) + +Tokens are stored exclusively as Gitea secrets. Never commit them to `.env` files or +log them. The nightly audit step passes its token via `env:` at the step level, reads +it as `$NIGHTLY_AUDIT_TOKEN` in the shell, and never runs the API `curl` under +`set -x`. + +### OSV vs platform alerts on Gitea + +Renovate's `vulnerabilityAlerts` config key requires a *platform* vulnerability graph. +**Gitea does not expose one** — it has no equivalent to the GitHub Advisory Database +API. On this runner, `vulnerabilityAlerts` is a label carrier only: it attaches +`security` and `P1-high` labels to PRs that `osvVulnerabilityAlerts` already raised. + +`osvVulnerabilityAlerts: true` is the load-bearing detector. Renovate queries +[OSV.dev](https://osv.dev) directly, which works regardless of platform. The runner +host must be able to reach `osv.dev:443`. If egress is filtered and OSV.dev is +unreachable, the flag silently no-ops — verify egress when standing up the runner. + +### Nightly audit vs PR gate (divergence) + +| Gate | Command | Dev deps | When | +|------|---------|----------|------| +| PR gate (`ci.yml`) | `npm audit --audit-level=high --omit=dev` | ❌ excluded | Every PR | +| Nightly audit (`nightly.yml`) | `npm audit --audit-level=high` | ✅ included | Nightly + `workflow_dispatch` | + +The nightly job is **deliberately broader** — it catches dev-tooling advisories +(esbuild, Vite, vitest, etc.) that the PR gate ignores. A red nightly audit job does +**not** mean the PR gate is broken; the two signals are independent. + +### Runbook: nightly-opened tracking issue + +When the `npm-audit` job opens or updates the tracking issue +"Nightly npm audit: high-severity advisory": + +1. **Triage severity.** Check the advisory page (link in the issue body). Is it + exploitable in production? A dev-only dep (e.g. esbuild, prettier) has no + production attack surface — treat as low urgency. + +2. **Pin or upgrade.** If a non-breaking upgrade is available, update + `frontend/package.json` and regenerate the lockfile. Open a PR. + +3. **Override if justified.** If the advisory does not apply (dev-only dep, no + exploitable path), add an `npm audit` override in `package.json`: + ```json + "overrides": { "esbuild": ">=0.25.4" } + ``` + Document the rationale in the PR body. See #817 for the reference decision tree. + +4. **Close the tracking issue** once the advisory is resolved or overridden and the + nightly job runs clean (verify via the `✅ npm audit clean` heartbeat in the job + summary).