Skip to content

Security: always-on

Every *-security component — go-security, rust-security, tofu-security, svelte-security, skill-security — carries no changes input. Every scanner in every one of them runs on every merge request, no matter which files that MR touches. This was a deliberate policy decision made alongside change-detection, not an oversight — the whole security category is exempt from it.

The reasoning

Change-detection exists to skip work that provably can't have been affected by an MR's diff — no point running go-test on a documentation-only change. Security scanning doesn't fit that logic, for a reason specific to what it's checking:

A dependency's vulnerability status isn't a function of the diff — it's a function of time. A CVE can be published against a library your project already depends on, on a day when nobody touched a single line of code. govulncheck, cargo-deny, osv-scanner, and the rest exist precisely to catch that: every MR re-checks the current dependency tree against the current advisory database, regardless of what changed. If security scanning were change-detected, a docs-only MR merged the day after a CVE drops would sail through unscanned — exactly the MR you'd want the scanner to catch.

There's also no fallback to rely on: the schedule-never guard (every component except renovate-self skips scheduled pipelines) means there is no nightly re-scan picking up what an MR-time filter might have skipped. Change-detected security would mean some CVEs are never caught until the next change to a matching file — which could be a long time.

The accepted cost: an expensive scanner like govulncheck runs even on an MR that only touches a README. That's a deliberate trade — maximum posture over marginal runner-time savings, for a category where the savings would come at the cost of a real security blind spot.

Secrets, specifically

Secret scanning (gitleaks, present in every *-security component) has the same always-on property for an even more direct reason — a secret committed anywhere in the diff needs to be caught in that MR, not deferred by a change filter that happened not to match the file it landed in. Every gitleaks job is additionally scoped to the MR's own commit range ($CI_MERGE_REQUEST_DIFF_BASE_SHA..$CI_COMMIT_SHA) rather than scanning full history on every run — a shared runner's cached refs from other branches can otherwise trip a false positive, an unrelated problem that this scoping avoids without weakening the always-on guarantee. See 2026-06-08-gitleaks-scan-scoping-v0.10.3.md.

What this doesn't mean

Always-on doesn't mean unconfigurable. Every scanner in every *-security component is its own job, so a consumer can opt one out individually (<job>: { rules: [{ when: never }] }) if it genuinely doesn't apply — but the default, out of the box, is every scanner, every MR, no filter.

See also