diff --git a/.hypatia-baseline.json b/.hypatia-baseline.json index 3cd5de9b..fcd874c2 100644 --- a/.hypatia-baseline.json +++ b/.hypatia-baseline.json @@ -1726,20 +1726,14 @@ }, { "severity": "high", - "rule_module": "workflow_audit", + "rule_module": [ + "workflow_audit", + "workflow_hardening" + ], "type": "invalid_actions_lock", "file_pattern": "**actions.lock", - "note": "TRIAGED (round-2 fix-forward 2026-09-22): Hypatia workflow_audit fails the lock closed with transitive_dependencies_missing -- asana/push-signed-commits@d615 (immutable pin, ref v1.3) declares transitive actions/setup-python@v2, which floats upstream and can never be a lock key. gh-actions-lock v0.1.6 verify says valid:true on the same file; Hypatia is stricter than the authoritative tool. Exposure is upstream float only. Exit: Asana pins setup-python, we replace the action, or Hypatia downgrades float-transitives. #947 deleted the metadata to silence the scanner; round 2 restored it verbatim and acked honestly.", - "expires_at": "2026-12-22", - "tracking_issue": "hyperpolymath/standards#951" - }, - { - "severity": "high", - "rule_module": "workflow_hardening", - "type": "invalid_actions_lock", - "file": ".github/workflows/actions.lock", - "note": "TRIAGED (round-2 fix-forward 2026-09-22): second emission path of the same asana float-transitive as the workflow_audit ack (see #951). Hypatia emits invalid_actions_lock TWICE: workflow_audit/parse_actions_lock (file actions.lock) and WH004 standalone (rule_module workflow_hardening, full path). Both verified by running the real scanner locally. Same exit criteria as #951.", + "note": "TRIAGED (round-2 fix-forward 2026-09-22; collapsed 2026-09-22 per #966): Hypatia emits invalid_actions_lock from TWO rule modules for ONE defect -- workflow_audit/parse_actions_lock (file `actions.lock`) and WH004 standalone (workflow_hardening, full path `.github/workflows/actions.lock`). Both verified by running the real scanner locally. The defect: asana/push-signed-commits@d615 (immutable pin, ref v1.3) declares transitive actions/setup-python@v2, which floats upstream and can never be a lock key. gh-actions-lock v0.1.6 verify says valid:true on the same file; Hypatia is stricter than the authoritative tool. Exposure is upstream float only. Exit: Asana pins setup-python, we replace the action, or Hypatia downgrades float-transitives. #947 deleted the metadata to silence the scanner; round 2 restored it verbatim and acked honestly. Tracking moved from #951 to #963 on 2026-09-22: #951 was closed COMPLETED with zero comments while the defect is demonstrably live (actions.lock:210 still declares the floating transitive under the asana pin, and setup-python is never a top-level lock key), because #963 superseded it. An acknowledgement expiring 2026-12-22 against a CLOSED issue has nobody owning its exit criteria -- the ack keeps working and the cure stops being anyone job. This was two entries until list-valued rule_module landed (#971) -- two entries meant two expiry dates for one decision, which is how a half-expired acknowledgement silently reopens a gate. DELIBERATE SCOPE CHANGE, recorded because it is a widening and not a refactor: the workflow_hardening half previously matched the exact path `.github/workflows/actions.lock`, and one entry carries one matcher, so it now matches `**actions.lock` -- the breadth the workflow_audit half already had at the same severity and type. Measured consequence: a workflow_hardening invalid_actions_lock at e.g. `vendor/actions.lock` was KEPT before and is SUPPRESSED now. Accepted because both halves are the same defect with the same exit criteria; if a second actions.lock is ever vendored, narrow this entry rather than trusting it.", "expires_at": "2026-12-22", - "tracking_issue": "hyperpolymath/standards#951" + "tracking_issue": "hyperpolymath/standards#963" } ] diff --git a/scripts/tests/apply-baseline-test.sh b/scripts/tests/apply-baseline-test.sh index a5098968..1dc10e83 100755 --- a/scripts/tests/apply-baseline-test.sh +++ b/scripts/tests/apply-baseline-test.sh @@ -258,6 +258,58 @@ else pass=$((pass + 1)) fi +# --- The SHIPPED ledger, not a fixture ------------------------------------- +# #966: hypatia emits invalid_actions_lock from BOTH workflow_audit and +# workflow_hardening for one defect. It was acked twice, which meant two +# expiry dates for one decision — the shape in which a half-expired ack +# silently reopens a gate. #971 made rule_module list-valued; this collapses +# the pair into one entry. These assertions run against the real +# .hypatia-baseline.json so that splitting it back, or narrowing the matcher, +# reds this suite instead of quietly un-suppressing a live finding. +SHIPPED="$SCRIPT_DIR/../../.hypatia-baseline.json" +if [ ! -f "$SHIPPED" ]; then + echo "FAIL: shipped baseline not found at $SHIPPED" + fail=$((fail + 1)) +else + # Exactly one entry, and it must name BOTH modules. + got=$(jq -r '[.[] | select(.type == "invalid_actions_lock")] as $e + | "\($e | length),\($e[0].rule_module | if type == "array" then length else 1 end)"' "$SHIPPED") + if [ "$got" = "1,2" ]; then + echo "PASS: shipped ledger holds ONE invalid_actions_lock ack naming TWO modules" + pass=$((pass + 1)) + else + echo "FAIL: shipped invalid_actions_lock acks: expected 1 entry / 2 modules, got $got" + fail=$((fail + 1)) + fi + + # ⚠ The two emissions carry DIFFERENT file values — `actions.lock` from + # workflow_audit and the full path from WH004 — so the surviving entry must + # match by file_pattern. An exact `file` key would suppress only one of + # them and the other would red main. Both, or the collapse is unsound. + cat > "$WORK/findings-shipped-pair.json" <<'EOF' +[{"severity":"high","rule_module":"workflow_audit","type":"invalid_actions_lock","file":"actions.lock"}, + {"severity":"high","rule_module":"workflow_hardening","type":"invalid_actions_lock","file":".github/workflows/actions.lock"}] +EOF + assert_status "shipped ledger suppresses BOTH emission paths" \ + "$WORK/findings-shipped-pair.json" "$SHIPPED" "2,0" + + # Over-match control: the collapse must not have turned the ack into a + # blanket amnesty for anything hypatia says about actions.lock. + cat > "$WORK/findings-shipped-other.json" <<'EOF' +[{"severity":"high","rule_module":"workflow_audit","type":"shell_download","file":"actions.lock"}, + {"severity":"high","rule_module":"workflow_lint","type":"invalid_actions_lock","file":"actions.lock"}] +EOF + got=$(bash "$APPLY" "$WORK/findings-shipped-other.json" "$SHIPPED" advisory \ + | jq -r '"\(.findings_suppressed | length),\(.findings_kept | length)"') + if [ "$got" = "0,2" ]; then + echo "PASS: shipped ledger does NOT suppress other types or modules" + pass=$((pass + 1)) + else + echo "FAIL: shipped ledger over-matches: expected 0,2 got $got" + fail=$((fail + 1)) + fi +fi + assert_invalid_option "invalid mode" bypass high assert_invalid_option "invalid threshold" blocking nonsense