Files
home-network-k3s-impl/conformance/tests/l0-static/vuln-triage-bot-workflow-guards/check.sh
T
jakman85andClaude Opus 5.5 b85d1e3ed7 feat(vuln): vuln-scanner platform manifests, Flux tier, alerts, L0/L2/L3 tests
Hand-authored platform/vuln-scanner (non-gating Flux tier, digest-pinned Trivy 0.76.1 and
python 3.14.9-alpine, read-only RBAC, per-pod oauth2-proxy, internal-only cve.<domain>),
five alerts with promtool tests, R-VULN-01..09 registry entries, and vuln-* L0 checks with
mutation self-tests. src/ and the bot workflow are integrated separately.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 09:42:52 -06:00

103 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================================
# L0 static check (offline, no cluster) — vuln-triage-bot-workflow-guards
# Serves: R-VULN-05 (runs on a schedule, fails loudly), R-VULN-07 (bot conventions, docs/17).
#
# GIVEN .gitea/workflows/cve-triage.yaml,
# THEN (1) it runs on `15 7 * * *` and has a workflow_dispatch `dry_run` input;
# (2) concurrency is set (the workflow never runs against itself);
# (3) a step fails the job HARD when RENOVATE_TOKEN is empty (exit non-zero, not skip);
# AND (4) the evaluator is run on mutated copies and must flag each (non-vacuity).
# A missing workflow file is a FAIL: this is whole-repo scoped and has no skip path.
# =============================================================================
set -euo pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "${DIR}/../../../.." && pwd)"
exec python3 - "$ROOT" <<'PY'
import copy, re, sys
from pathlib import Path
import yaml
ROOT = Path(sys.argv[1])
NAME = "vuln-triage-bot-workflow-guards"
FAILURES, CHECKS = [], 0
WF = ROOT / ".gitea" / "workflows" / "cve-triage.yaml"
def check(cond, desc):
global CHECKS
CHECKS += 1
if not cond:
FAILURES.append(desc)
if not WF.is_file():
print(f"CHECK FAIL ({NAME}): .gitea/workflows/cve-triage.yaml is absent (the BOT's deliverable); "
f"whether it fails on a missing credential cannot be asserted without it", file=sys.stderr)
raise SystemExit(1)
doc = yaml.safe_load(WF.read_text())
def evaluate(doc):
bad = []
triggers = doc.get(True) if True in doc else doc.get("on") # YAML reads `on:` as boolean True
if not isinstance(triggers, dict):
return ["no triggers parsed"]
if "15 7 * * *" not in [s.get("cron") for s in triggers.get("schedule") or [] if isinstance(s, dict)]:
bad.append("no schedule with cron 15 7 * * *")
inputs = ((triggers.get("workflow_dispatch") or {}).get("inputs") or {})
if "dry_run" not in inputs:
bad.append("workflow_dispatch has no dry_run input")
conc = doc.get("concurrency")
jobs = doc.get("jobs") or {}
if not conc and not any(isinstance(j, dict) and j.get("concurrency") for j in jobs.values()):
bad.append("no concurrency set (the bot must not run against itself)")
hard_fail = False
for j in jobs.values():
for step in (j.get("steps") or []) if isinstance(j, dict) else []:
run = str(step.get("run", ""))
if ("RENOVATE_TOKEN" in run and re.search(r"(-z|!\s*-n)[^\n]*RENOVATE_TOKEN|RENOVATE_TOKEN[^\n]*(-z)", run)
and re.search(r"exit\s+[1-9]", run)):
hard_fail = True
if not hard_fail:
bad.append("no step fails hard (exit non-zero) when RENOVATE_TOKEN is empty")
return bad
for v in evaluate(doc):
check(False, f"(1-3) {v}")
check(isinstance(doc.get("jobs"), dict) and doc["jobs"], "(1) the workflow declares jobs")
# (4) non-vacuity
def mutate(fn):
m = copy.deepcopy(doc)
fn(m)
return m
def trig(m):
return m.get(True) if True in m else m.get("on")
def no_cron(m): trig(m).pop("schedule", None)
def no_dry(m): trig(m)["workflow_dispatch"] = {}
def no_conc(m):
m.pop("concurrency", None)
for j in m.get("jobs", {}).values():
if isinstance(j, dict):
j.pop("concurrency", None)
def no_guard(m):
for j in m.get("jobs", {}).values():
for step in j.get("steps") or []:
if "RENOVATE_TOKEN" in str(step.get("run", "")):
step["run"] = 'echo "::warning::RENOVATE_TOKEN unset, skipping"'
for name, fn in (("cron removed", no_cron), ("dry_run input removed", no_dry),
("concurrency removed", no_conc), ("credential check softened", no_guard)):
check(bool(evaluate(mutate(fn))), f"(4) evaluator missed mutant: {name}")
if FAILURES:
print(f"CHECK FAIL ({NAME}): {len(FAILURES)} of {CHECKS} assertions failed", file=sys.stderr)
for f in FAILURES:
print(f" - {f}", file=sys.stderr)
raise SystemExit(1)
print(f"PASS ({NAME}): {CHECKS} assertions")
PY