Remediate the real findings the now-functional osv-scanner and zizmor gates
surfaced on release/v3.8.26.
Deps (osv-scanner, 2 HIGH -> 0):
- form-data 4.0.5 -> ^4.0.6 (GHSA-hmw2-7cc7-3qxx, transitive via axios)
- vite 8.0.5 -> ^8.0.16 (GHSA-fx2h-pf6j-xcff HIGH + GHSA-v6wh-96g9-6wx3
MODERATE; dev-only via vitest/@vitejs/plugin-react/fumadocs-mdx)
Applied via package.json overrides of existing deps (no new allowlist entry
needed). vulnCount 13 -> 10; build:cli + vitest MCP suite (16 files/187 tests)
green post-bump.
Workflows (zizmor, 195 -> 187):
- env-harden 7 template-injection findings by moving each ${{ ... }} into env:
and referencing "$VAR" in the script (GitHub-documented mitigation):
ci.yml i18n; electron-release.yml validate/build/release steps.
- allowlist 1 dangerous-triggers FP: deploy-vps.yml on:workflow_run is guarded
on conclusion=='success' and deploys via SSH without checking out untrusted
code. Added .zizmor.yml rules.dangerous-triggers.ignore with justification.
Tighten baselines to the improved state (direction: down): vulnCount 13 -> 10,
zizmorFindings 195 -> 187. secretFindings (3) and bundleSize (5601) unchanged.
Three CI security gates in the quality-extended job never produced a value;
diagnose + fix each, then freeze the real measured numbers as advisory ratchet
baselines (dedicatedGate => SKIP in the blocking quality-gate ratchet).
FIX 1 — .zizmor.yml: migrate the config from the pre-1.0 'ignores: []' schema to
the 'rules: {}' schema. zizmor 1.25.2 rejected the old field ('unknown field
`ignores`, expected `rules`') and performed NO audit. Now check:workflows emits
zizmorFindings=195.
FIX 2 — scripts/check/check-secrets.mjs: the gate ran 'gitleaks detect --no-git
--source .', which walks the WHOLE tree including a real node_modules/ (90k+ files
under npm ci) and times out (ETIMEDOUT) — gitleaks has no traversal-exclude flag
(.gitleaks.toml paths filter findings AFTER reading). Scope the scan to the source
dirs (src/open-sse/bin/electron/scripts), one 'gitleaks dir <dir>' invocation each
('gitleaks dir' takes a single path; multiple args fall back to scanning the CWD).
Also fix .gitleaks.toml: it lacked [extend].useDefault=true, so the custom config
REPLACED the default ruleset with zero rules and detected nothing — the gate always
reported 0 regardless of real secrets. Now: ~10s (was 120s timeout), secretFindings=3
(generic-api-key false positives in beta-header strings / column names).
FIX 3 — .github/workflows/ci.yml: the scanner install resolved release URLs via
unauthenticated api.github.com (60 req/hr/IP; returns empty when throttled -> silent
no-op install -> every gate self-skips). Switch gitleaks + osv-scanner to 'gh release
download' (preinstalled + GITHUB_TOKEN-authed, 5000 req/hr); add GH_TOKEN to the step
env. actionlint/zizmor install paths unchanged.
MEASURE + FREEZE (advisory, dedicatedGate:true, direction down) in
config/quality/quality-baseline.json: secretFindings=3, zizmorFindings=195,
vulnCount=13 (LOW=4/MOD=7/HIGH=2), bundleSize=5601. Seeded from a local run with the
real binaries on PATH (2026-06-15). They stay advisory (SKIP in the blocking ratchet;
quality-extended is continue-on-error) until a green CI run confirms the fixed tooling
produces values; the flip to blocking is a follow-up PR. continue-on-error untouched.
Validated locally: zizmor --config parses; check:secrets <60s + real count;
check:workflows/check:vuln-ratchet emit real numbers; ci.yml actionlint-clean; baseline
JSON valid; 103 build-scanner unit tests + 19 check-secrets + 18 quality-ratchet pass;
the 4 keys SKIP in the ratchet. FIX 3 logic is sound but CI-only (cannot run gh release
download against the runner locally).
FIX 1 — security scanner install (gitleaks/osv/actionlint/zizmor):
the step ran under bash -e and aborted before the $GITHUB_PATH export when
'go install ...gitleaks/v8@latest' failed/produced a non-working binary, so no
scanner landed on PATH and every check self-skipped. Rewritten to set +e,
install from official release downloads, ALWAYS export $HOME/.local/bin, and
print diagnostics. Validated only on the next CI run (binaries install in CI).
FIX 2 — size-limit bundleSize: add @size-limit/file devDependency (the correct
preset for the plain FILE-size .size-limit.json) + allowlist entry. check:bundle-size
now measures the 4 bin entries via the preset (bundleSize=5601) instead of erroring
on the missing preset / falling back to fs.statSync.
FIX 3 — codeqlAlerts promoted to BLOCKING: check-codeql-ratchet.mjs now reads
metrics.codeqlAlerts.value, exits 1 ONLY on a real regression (measured > baseline),
and exits 0 (graceful skip) on any measurement failure (gh absent / no auth /
no repo / API error) so missing infra never blocks. Baseline seeded codeqlAlerts=0
(repo has 0 open alerts). Blocking step wired in the quality-gate job with
security-events:read; the duplicate advisory step removed from quality-extended.
New TDD: evaluateCodeqlRatchet (5 cases).
Move the committed quality-gate state files out of the repo root into
config/quality/ and the v3.8.24 documentation audit into docs/ops/, then
re-point every gate script, test and .gitignore entry at the new paths.
Refresh docs/architecture/REPOSITORY_MAP.md (stale since v3.8.2) to match
the current layout.
Moved -> config/quality/:
quality-baseline.json, complexity-baseline.json, duplication-baseline.json,
file-size-baseline.json, test-discovery-baseline.json,
dependency-allowlist.json, .license-allowlist.json
(generated quality-metrics.json now written here too; still gitignored)
Moved -> docs/ops/:
DOCUMENTATION_AUDIT_REPORT.md (+ meta.json entry + fabricated-docs skip)
Path updates: check-{complexity,duplication,file-size,test-discovery,deps,
licenses,dead-code,cognitive-complexity,type-coverage}.mjs, check-quality-
ratchet.mjs, collect-metrics.mjs, check-tracked-artifacts.mjs (+ its test and
check-deps test). Also gitignore /logs/ (was untracked-not-ignored).
Tracked root files: 56 -> 48. Tool configs left in root on purpose: most are
auto-discovered there, and the tsconfig variants have location-relative
files:[] arrays that would need 46 path rewrites for a 2-file gain.