From a9a71005642e4d75e736f478bed35e84434e5ba0 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Thu, 16 Jul 2026 09:06:07 -0700 Subject: [PATCH] Surface rulesets/secrets read failures as ERROR, not empty (Copilot #326) Both endpoints return empty collections for the legitimate none-case, so a 404 there means the call failed and must become the repo's ERROR finding instead of cascading into false missing-ruleset/missing-secret DEFECTs. Co-Authored-By: Claude Opus 4.8 (1M context) --- spec/audit.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/audit.py b/spec/audit.py index 4cf5e583..be05929a 100644 --- a/spec/audit.py +++ b/spec/audit.py @@ -103,7 +103,9 @@ def audit_repo(entry, spec): # --- Rulesets --- dev_payload = "repo-config/operational/develop.json" if model == "operational" else "repo-config/develop.json" expect_rulesets = {"develop": load(dev_payload), "main": load("repo-config/main.json")} - live_list = gh(f"repos/{slug}/rulesets?per_page=100", ok404=True) or [] + # No ok404: a repo with no rulesets returns an empty list, so a 404 means the call failed + # (access/rename) and must surface as ERROR, not read as "no rulesets". + live_list = gh(f"repos/{slug}/rulesets?per_page=100") or [] live_names = [r["name"] for r in live_list] for name, payload in expect_rulesets.items(): ids = [r["id"] for r in live_list if r["name"] == name] @@ -121,8 +123,10 @@ def audit_repo(entry, spec): # --- Secrets (names only) --- secrets = spec["secrets"] stores = {} + # No ok404: an empty store returns {"secrets": []}, so a 404/403 (permissions, rename) must + # surface as ERROR rather than cascade into false missing-secret DEFECTs. for store, path in [("actions", f"repos/{slug}/actions/secrets?per_page=100"), ("dependabot", f"repos/{slug}/dependabot/secrets?per_page=100")]: - data = gh(path, ok404=True) + data = gh(path) stores[store] = {s["name"] for s in (data or {}).get("secrets", [])} mechanisms = [secrets["targetMechanisms"].get(p.get("target")) for p in entry.get("publish", [])] mechanisms += [secrets.get("typeMechanisms", {}).get(t) for t in types]