docs(devops): document two-token model, OSV alerts, and nightly-audit runbook in ci-gitea.md
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 4m28s
CI / OCR Service Tests (pull_request) Successful in 24s
CI / Backend Unit Tests (pull_request) Successful in 5m12s
CI / fail2ban Regex (pull_request) Successful in 48s
CI / Semgrep Security Scan (pull_request) Successful in 22s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m9s
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 4m28s
CI / OCR Service Tests (pull_request) Successful in 24s
CI / Backend Unit Tests (pull_request) Successful in 5m12s
CI / fail2ban Regex (pull_request) Successful in 48s
CI / Semgrep Security Scan (pull_request) Successful in 22s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m9s
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user