Skip to content

Commit f5e0941

Browse files
feat(cicd_rules): :known_fake_action_sha rule — block partial-prefix-corruption fakes (#397)
## Summary Adds a new `Hypatia.Rules.CicdRules` entry that flags GitHub Action pins where the SHA is fabricated. Complement to the existing `:unpinned_action` rule. ## Problem Estate audit on 2026-05-30 found **67 fake action SHA pairs across ~50 repos** (11% fabrication rate across 372 unique pins). Verified via `gh api repos/<org>/<action>/commits/<sha>` returning 422. **Universal pattern: partial-prefix corruption.** The first 8-20 hex chars match a real release's SHA; the suffix is fabricated. Examples: | Fake | Real | Real version | |---|---|---| | `7ab2955eb728f5440978d`**7b4f723a50dea1f3608** | `7ab2955eb728f5440978d`**5824358023be3a2802d** | setup-zig v2.2.0 | | `49933ea5288caeca8642`**195f2b846b8bbe245a93** | `49933ea5288caeca8642`**d1e84afbd3f7d6820020** | setup-node v4.4.0 | | `909cc5acb0`**135c37a79510dd77767e217930de55** | `909cc5acb0`**fdd60627fb858598759246509fa755** | setup-deno v2.0.2 | Almost certainly a single AI-hallucination event that propagated across the estate via copy-customise of templates. The fakes slip past visual review because of the matching prefix. ## What this rule does Adds `:known_fake_action_sha` to `@blocked_patterns` enumerating the 25 known fakes from the audit. Caught at scan time; blocks new code from re-introducing them. Reason field links to the substitution map in `project_estate_fake_action_sha_punch_list_2026_05_30` memory entry. `applies_to: [\"*.yml\", \"*.yaml\"]` to scope to workflow files. ## Tested inline (transcript) ``` ✓ expect=true got=true :: known fake setup-beam ✓ expect=true got=true :: known fake upload-artifact (partial-prefix) ✓ expect=false got=false :: REAL upload-artifact v4.6.2 (no false-positive) ✓ expect=false got=false :: REAL setup-node v4.4.0 (no false-positive) ✓ expect=true got=true :: fake setup-node (partial-prefix corruption) ✓ expect=true got=true :: fake codeql ✓ expect=false got=false :: REAL checkout v4 (no false-positive) ``` ## Out of scope For proactive detection of FUTURE fakes (beyond the static 25-entry list), a `mix hypatia.verify_action_shas` task is the right shape — needs network access to `gh api`, can't be a static-regex rule. Documented as a follow-up in the punch-list memory. ## Provenance Discovered while wiring `hyperpolymath/snifs#30` build-mode CI gate. The static-rule design intent was previously captured in `feedback_verify_action_sha_pins` memory: > Consider adding a complementary rule that VERIFIES SHA pins resolve upstream — would have caught all four fakes above. This PR ships the static-list version of that idea. ## Test plan - [ ] Rule compiles + registers (verified locally) - [ ] No false-positives on real action SHAs in current estate workflows - [ ] When scanning the 50 repos still carrying these fakes, the rule flags them - [ ] After round-2 sweep (admin-merge style, in flight as PID 607915) completes, the rule's positives should drop to zero estate-wide
1 parent 15af96a commit f5e0941

1 file changed

Lines changed: 29 additions & 0 deletions

File tree

lib/rules/cicd_rules.ex

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,35 @@ defmodule Hypatia.Rules.CicdRules do
549549
reason:
550550
"GitHub Actions and reusable workflows must be SHA-pinned (canonical detection: WH004 in workflow_hardening.ex)"
551551
},
552+
# COMPLEMENT to :unpinned_action — catches the OPPOSITE failure mode where
553+
# the pin is a SHA-shaped string but the SHA doesn't actually exist on the
554+
# upstream action repo (gh api repos/<org>/<action>/commits/<sha> -> 422).
555+
#
556+
# Discovered 2026-05-30 estate audit: ~67 fake SHA pairs across ~50 repos
557+
# (11% fabrication rate across 372 unique pins). All confirmed via
558+
# `gh api commits/<sha> -> 422`. Pattern: partial-prefix corruption — the
559+
# first 8-20 hex chars match a real release's SHA, the suffix is
560+
# fabricated, slipping past visual review. Almost certainly a single AI
561+
# hallucination event that got copy-pasted across the estate.
562+
#
563+
# Static-list approach: enumerates the 25 known fake SHAs from the
564+
# 2026-05-30 audit. Inert until uncommented, but blocks regression if
565+
# any of them propagates into NEW files. For proactive detection of
566+
# FUTURE fakes, see the design note in
567+
# `project_estate_fake_action_sha_punch_list_2026_05_30.md` — a `mix
568+
# hypatia.verify_action_shas` task is the right shape for that
569+
# (requires network access; can't be a static regex rule).
570+
#
571+
# Substitution map for replacement SHAs lives in the punch-list memory
572+
# entry above; each fake has a documented version-faithful real SHA.
573+
%{
574+
id: :known_fake_action_sha,
575+
pattern:
576+
~r/@(7ab2955eb728f5440978d7b4f723a50dea1f3608|5a67e1a1dd86cae5e5bef84e2da5060406a66c07|5fae568d37c3b73e0e4ca63d4e2c4e324a2b3497|dd344bc1cec854a9b55c2b857c28b688010e4fce|ea165f8d65b6db9a8b71b5c2d1a090c0daf9c8bb|65c79d7f54e76e4e3c7a8f34db0f4ac8b515c478|49933ea5288caeca8642195f2b846b8bbe245a93|1d0ff469b7ec7b3cb9d8673fde0c81c483a26c6c|8a8ef8526528d8a4ff3e2c90be08e25ef8efbd9b|37835157ba6190a612501a35f7959b8eb3009581|80e8293c5aaa80faf9b3e8fde13b80c04073d7ab|8a83df83c66e0b5c7ee6b3cd5636dd2d2c0c7c56|7ee2e0fdffec27d19ccbb8fb3dcf8a83b9d7f9e8|b2f9ef845756500b97acbdaf5c1dd4e9c1d15734|6624720a57d4c312633c7b953db2f2da5bcb4c3a|ea9e4e37992a54ee68a9571571f9a567d8f90f78|a4784f2dad6682d68cce8299ef20b1ca931bbdfb|ad397744b0d591a723ab90405b7247fac0e6b8db|e2bb9c9574c8527c28e4a102d0f2cb9e8efe9c0d|909cc5acb0135c37a79510dd77767e217930de55|9db7efd2d8d10d73081f64624f472263a3d12f20|61df2da039798545a278151ba52240239f604b71|5304e04ea2b1555a004402327ce0988b1f48fa1d|53eca37a5015e5c7075c3d9b4b08705001f35835|b5a3d9d107a26590ff7e9e6eddc6f0acff786aff)\b/,
577+
reason:
578+
"Known-fabricated action SHA pin — `gh api commits/<sha>` returns 422. Replace with the version-faithful real SHA per the substitution map in `project_estate_fake_action_sha_punch_list_2026_05_30.md`. These were caught in the 2026-05-30 estate audit (~67 fakes / 11% fabrication rate); the partial-prefix corruption pattern slips past visual review.",
579+
applies_to: ["*.yml", "*.yaml"]
580+
},
552581
%{
553582
id: :missing_permissions,
554583
pattern: ~r/^permissions:/m,

0 commit comments

Comments
 (0)