Skip to content

feat(rules): ShaBumpPropagation — detection for estate reusable SHA bumps (#418)#419

Merged
hyperpolymath merged 2 commits into
mainfrom
feat/sha-bump-propagation-rule-418
Jun 2, 2026
Merged

feat(rules): ShaBumpPropagation — detection for estate reusable SHA bumps (#418)#419
hyperpolymath merged 2 commits into
mainfrom
feat/sha-bump-propagation-rule-418

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Detection-half of the three-system propagation architecture filed in #418:

hypatia (this PR)  →  gitbot-fleet (actuation)  →  .git-private-farm (propagation)
  • lib/rules/sha_bump_propagation.ex — pure-data rule Hypatia.Rules.ShaBumpPropagation.check/1 consuming a PR-merge event, returning one finding per matched estate reusable workflow / composite action. Strategy is :review (flag-only — actuation pre-filters by title keyword, hypatia never does).
  • Registry: 10 standards reusables (governance-reusable.yml, rust-ci-reusable.yml, etc.) + a2ml-validate-action. Easy to extend.
  • Accepts both atom-keyed and binary-keyed payloads (webhook-friendly).
  • test/rules/sha_bump_propagation_test.exs — sensitivity + specificity oracle suite (pr_title carried verbatim, malformed-event safety, multi-workflow PR fans out).

Why this shape

  • No keyword-filter here. Per feedback_pr_sweep_title_keyword_exclusion + feedback_no_automated_licence_edits, hypatia emits + the actuator rejects. The pr_title field is carried verbatim to the actuator so it has the full string to test.
  • Module not bolted onto cicd_rules.ex. That file is already 1506 LoC and event-driven detection is a different shape from path/content scanning. Separate module keeps the contract clean.
  • No estate-wide consumer enumeration in this PR. estimated_consumers is a hint field set by the caller — actuation (gitbot-fleet#248) owns the enumeration, since it's the one fanning out.

Verification

Local mix can't run tests in this checkout (elixir 1.14 vs phoenix 1.15+ ABI break). A standalone runner exercised all 27 assertions before commit:

ok    rule name
ok    severity
ok    strategy
... (24 more) ...
All standalone assertions passed.

CI runs the ExUnit module via the project's normal pipeline; expect the same outcome.

Test plan

  • Sensitivity: known reusable workflow change emits finding
  • Sensitivity: multiple matched workflows in one PR emit multiple findings
  • Sensitivity: composite-action repos via action.yml
  • Sensitivity: pr_title carried verbatim (so actuator can keyword-filter)
  • Specificity: docs-only PR does NOT fire
  • Specificity: non-registered workflow does NOT fire
  • Specificity: non-reusable-repo PR does NOT fire
  • Specificity: malformed SHA returns []
  • Specificity: missing required field returns []
  • Input shape: string-keyed (webhook-style) accepted
  • Registry hygiene: every entry well-formed
  • CI: ExUnit suite passes under project's mix env

Related

Closes #418

🤖 Generated with Claude Code

…SHA bumps (#418)

Detection-half of the three-system propagation architecture:
  hypatia (this commit) -> gitbot-fleet -> .git-private-farm

Pure-data rule consuming a PR-merge event, returning a finding per
matched estate reusable workflow / composite action. Strategy
`:review` (flag-only); title-keyword exclusion stays in actuation
per feedback_pr_sweep_title_keyword_exclusion.

- lib/rules/sha_bump_propagation.ex — module + known_reusables registry
  (10 standards reusables + a2ml-validate-action composite). Accepts
  both atom- and binary-keyed event maps (webhook-friendly).
- test/rules/sha_bump_propagation_test.exs — sensitivity + specificity
  oracle suite; pr_title carried verbatim; malformed-event safety.

Local mix can't run tests (elixir 1.14 vs phoenix 1.15+); a standalone
runner exercised every assertion before commit. CI will run the
ExUnit module via the project's normal pipeline.

Origin trace: .git-private-farm#66 (propagation primitive) +
standards#341 + neurophone#107 (both closed redundant).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

🔍 Hypatia Security Scan

Findings: 146 issues detected

Severity Count
🔴 Critical 0
🟠 High 0
🟡 Medium 146
View findings
[
  {
    "reason": "Action urin 21 JRE\n        uses: actions/setup-java@be666c2fcd27 needs attention",
    "type": "unpinned_action",
    "file": "verify-proofs.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in clusterfuzzlite.yml",
    "type": "missing_timeout_minutes",
    "file": "clusterfuzzlite.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
Copy link
Copy Markdown
Owner Author

Status check (2026-06-02): PR has 10 failing checks across governance, AsciiDoc build, dogfooding, and CodeQL.

Failing categories:

  • governance / Validate Hypatia baseline
  • governance / Language / package anti-pattern policy
  • governance / Security policy checks
  • governance / Workflow security linter
  • Build AsciiDoc (twice)
  • analyze (javascript-typescript, none) (CodeQL)
  • Dogfooding compliance summary

Not blocking on you — these look like persistent estate-wide gates the broader sweep is working through. Flagging for visibility; not merging via admin-merge while real (non-skipped) checks are red.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

🔍 Hypatia Security Scan

Findings: 146 issues detected

Severity Count
🔴 Critical 0
🟠 High 0
🟡 Medium 146
View findings
[
  {
    "reason": "Action urin 21 JRE\n        uses: actions/setup-java@be666c2fcd27 needs attention",
    "type": "unpinned_action",
    "file": "verify-proofs.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in clusterfuzzlite.yml",
    "type": "missing_timeout_minutes",
    "file": "clusterfuzzlite.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath hyperpolymath merged commit 09fb0d6 into main Jun 2, 2026
38 checks passed
@hyperpolymath hyperpolymath deleted the feat/sha-bump-propagation-rule-418 branch June 2, 2026 18:53
hyperpolymath added a commit to hyperpolymath/standards that referenced this pull request Jun 2, 2026
… SARIF + gitleaks unblock) (#352)

## Summary

Two reusable workflows consume third-party actions that need GitHub-API
permissions the reusables didn't grant. Reusable workflow permission
blocks OVERRIDE the caller's, so adding perms at every consumer wrapper
is a no-op — **the fix must live here at source**.

This unblocks SARIF upload + gitleaks PR scan across **every estate
consumer** of these reusables.

## Reproducing failures (before this PR)

From `.git-private-farm#69` runs (verified Jun 02 18:34 UTC):

### hypatia SARIF upload
```
hypatia.sarif written: 48 result(s).
Run github/codeql-action/upload-sarif@...
  sarif_file: hypatia.sarif
Validating hypatia.sarif
Adding fingerprints to SARIF file.
##[error]Resource not accessible by integration - GET /actions/runs/...
```

The scan + SARIF generation **succeed**. The upload-sarif step then
calls `GET /repos/{owner}/{repo}/actions/runs/{run_id}` to attach the
blob and fails because `actions: read` is not granted.

### gitleaks ScanPullRequest
```
[hyperpolymath] is an individual user. No license key is required.
gitleaks version: 8.24.3
Gitleaks restored from cache
RequestError [HttpError]: Resource not accessible by integration
    at ... ScanPullRequest ...
```

The gitleaks binary scan **succeeds**. The post-scan `ScanPullRequest`
step calls additional API endpoints (PR-files, workflow-run metadata, PR
comment post) and fails because `pull-requests: write` + `actions: read`
are not granted.

## Changes

### `hypatia-scan-reusable.yml` — workflow-level

```yaml
permissions:
  contents: read
  security-events: write
  pull-requests: write
+ actions: read    # NEW — for upload-sarif's GET /actions/runs/{id}
```

Single-job workflow, so workflow-level is the right scope.

### `secret-scanner-reusable.yml` — gitleaks job-level only

The other jobs (`trufflehog`, `rust-secrets`, `shell-secrets`) only need
`contents: read`. Adding wider perms at workflow level would over-grant.
Gitleaks gets a job-level block instead:

```yaml
gitleaks:
+ permissions:
+   contents: read
+   pull-requests: write   # NEW — for PR comment post
+   actions: read          # NEW — for PR-files / workflow-run API calls
  steps: ...
```

## Why this is safe

Both new grants are **read-only** (`actions: read`) or **scoped to PRs
only** (`pull-requests: write`). They match GitHub's documented
permission requirements for these specific third-party actions:

- `codeql-action/upload-sarif` docs explicitly require `actions: read`
when calling from a wrapper.
- `gitleaks-action` v2 docs require `pull-requests: write` for PR
commenting + `actions: read` for the new PR-files API.

No new write capability beyond PR-comment posting, which already had to
be granted somewhere (incorrectly assumed it'd inherit from the caller).

## Blast radius

- `hypatia-scan-reusable.yml`: ~estate-wide consumer of Hypatia security
scan.
- `secret-scanner-reusable.yml`: 281 estate deployments per the
reusable's own header comment.

All consumers will see the fix on their next run via SHA-bump
propagation (which is exactly the cascade the 3-system propagation track
was designed for — `.git-private-farm#66` + `gitbot-fleet#249` +
`hypatia#419`).

## Test plan

- [ ] After merge: rerun `.git-private-farm#69` checks; both SARIF +
gitleaks transition to GREEN.
- [ ] Propagation cascade fires (or manual SHA bump if propagation not
yet live), and other estate consumers green up too.

## Related

- hyperpolymath/.git-private-farm#69 — local fixes that hit this
upstream wall
- hyperpolymath/.git-private-farm#66, #68 — 3-system propagation cascade
(this PR's merge SHA will be the first real propagation candidate)
- hyperpolymath/hypatia#419 — detection rule
- hyperpolymath/gitbot-fleet#249 — actuation script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit to hyperpolymath/gitbot-fleet that referenced this pull request Jun 2, 2026
… (#249)

## Summary

Actuation-half of the three-system propagation architecture (#248):

```
hypatia (detection)  →  gitbot-fleet (THIS)  →  .git-private-farm (propagation)
```

- \`scripts/propagate-sha-bump.sh\` consumes a hypatia finding with rule
\`reusable_workflow_sha_bump_needs_propagation\`, pre-filters the
upstream PR title (HARD), enumerates consumers via \`gh search code\`,
drops forks, and fires a \`propagate-sha-bump\` \`repository_dispatch\`
event into \`.git-private-farm\`.
- \`scripts/fix-script-registry.json\` wires the recipe → script;
\`dispatch-runner.sh\` routes via the existing \`by_recipe\` map.
- \`tests/propagate-sha-bump-smoke.sh\` exercises the refusal + DRY_RUN
paths (10/10 passing locally).

## Safety properties

Canonical title-keyword gate lives HERE (per
\`feedback_pr_sweep_title_keyword_exclusion\` +
\`feedback_no_automated_licence_edits\`). The receiver workflow
re-checks belt-and-braces.

- \`pr_title\` matching
\`license|SPDX|PMPL|MPL|AGPL|GPL|Apache|copyright|attribution|relicens|secret|vulnerab|CVE-\`
→ exit 0 (REFUSED, routed to manual review — not an error path).
- \`title_suffix\` synthesised from **safe** metadata (workflow slug +
short SHA), NOT from \`pr_title\`. So even if a forbidden keyword
somehow slipped past the pre-filter, the dispatched PR title would not
carry it.
- \`old_sha == new_sha\` rejected.
- \`source_repo\` constrained to \`hyperpolymath/*\`.
- \`source_workflow\` constrained to \`.github/workflows/*.{yml,yaml}\`
or \`action.{yml,yaml}\`.
- Forks dropped via \`gh repo view --json isFork\`.
- \`DRY_RUN=true\` prints the payload instead of dispatching.

## Coupling with the other two PRs

- **Depends on**: hyperpolymath/hypatia#419 — the finding shape this
script consumes.
- **Triggers**: hyperpolymath/.git-private-farm#68 — the receiver
workflow + scripts (#66).

Each can land independently; once all three land, the chain end-to-ends.

## Test plan

- [x] Wrong rule name → exit 1
- [x] Malformed SHA → exit 1
- [x] \`old_sha == new_sha\` → exit 1
- [x] Non-estate \`source_repo\` → exit 1
- [x] License keyword in \`pr_title\` → exit 0 (REFUSED message printed)
- [x] DRY_RUN with valid finding → exit 0, payload composed
- [x] \`branch_name\` slug correct
(\`ci/bump-<workflow-slug>-<short-sha>\`)
- [x] \`title_suffix\` synthesised from safe metadata only
- [ ] End-to-end (post-merge): hypatia emits → dispatch-runner picks up
→ this script dispatches → farm receiver runs → consumer PRs land
- [ ] Smoke test as a CI job (follow-up — leaving as local script for
first landing)

## Related

- #248 — this PR closes
- hyperpolymath/hypatia#419 — detection rule (PR open)
- hyperpolymath/.git-private-farm#66 — propagation scripts (PR open)
- hyperpolymath/.git-private-farm#68 — receiver workflow (PR open)

Closes #248

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: hyperpolymath <hyperpolymath@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rule: detect estate-wide reusable workflow SHA bumps needing propagation

1 participant