Repository navigation
feat(workflow_audit): WF028 — reusable call into a moved repository - #965
Conversation
hyperpolymath/standards moved to metadatastician/standards on 2026-10-25. GitHub keeps a redirect for git and the REST API, but does not follow it when it resolves a reusable workflow's `uses:`, so every caller still naming the old owner ends in startup_failure before any job runs (measured: hypatia governance runs 40424594888 / 40424740139). WF028 flags that call where WF027 finds a reusable call (a job's `uses:` key in block or flow style, continued below its key, or via a job alias), and names the re-pointed reference at the same revision. The moved-repo table is derived from StandardsRepo.former/0, so each further transfer into the org (standards D97) is a row, not a rule. Advisory, severity low, auto_fixable: false, like WF026/WF027 (AGENTIC must-not-promote-to-blocking); no fleet fixer rewrites the owner yet. cli.ex now carries the finding's `replacement` through to SARIF/fleet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J4QvDfvetbJ5z7uREtbJWw
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 3 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Governance, Secret Scanner and Hypatia Security Scan end in The failure happens before any job starts, and it is the same on Generated by Claude Code |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a3e6b3f502
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ases only Three Codex findings on #965, each with a test that fails on the old code: - A block-style `uses:` value ends at whitespace or its closing quote, not at `,` `}` `]` `#`: a branch or tag may hold them, and the replacement claims the same revision. Flow delimiters still end a flow entry. - A leading `uses:` no longer hides the flow siblings on its line (`uses: a@x }, b: { uses: a@y`): both are reported. - An anchored mapping counts as a reusable call only where a job is given it whole (`g: *shared`); `- *shared` under `steps:` is a step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J4QvDfvetbJ5z7uREtbJWw
|
Add Carrot credits or activate Agent usage billing to use Autofix |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d2cbf7c2c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…y each alias Two more Codex findings on #965, fixed at the root rather than per case: - The `uses:` value is now read as a YAML scalar: a quoted one runs to its closing quote whatever it holds (`"…@Release,one"` in a flow job), and only a plain one ends at whitespace (block) or the flow delimiters. - PinSites.node_alias_sites/1 now reports each alias' `kind` (:job or :step), decided where the alias stands, by the classification node_aliases/4 already made. WF028 keeps only :job, so `jobs: { a: *job, b: { steps: [*step] } }` no longer marks the step's anchored `uses:` live. The key is additive; existing callers match on subsets of the map. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J4QvDfvetbJ5z7uREtbJWw
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 10ab3da81f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # at whitespace in block style (a branch or tag may hold `,` `}` `]` | ||
| # `#`) and, inside a flow collection, at the delimiters too. | ||
| key = "[\"']?uses[\"']?\\s*:\\s*(?:[&!]\\S*\\s+)*" | ||
| quoted = "\"([^\"]*)\"|'([^']*)'" |
There was a problem hiding this comment.
Decode quoted YAML scalars before matching moved repositories
When a valid double-quoted uses: value contains a YAML escape, the regex compares the encoded source rather than the decoded scalar, so a call such as uses: "\x68yperpolymath/standards/.github/workflows/x.yml@main" resolves to the moved repository but produces no WF028 finding. Decode double-quoted YAML values before applying call_re so escaped owner, path, or ref characters cannot bypass the rule.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Leaving this as is. The path traced is evasion: someone would have to write \x68yperpolymath on purpose. WF028 isn't a security control; it warns a repository that its own call will hit startup_failure, and the failure itself is loud and visible on every run. Nobody has a reason to hide from it. Decoding YAML escapes would also mean writing a scalar decoder the codebase doesn't have, which costs more than this blind spot does.
Generated by Claude Code
| # makes a reusable call; `- *step` and `steps: [ *step ]` are steps. | ||
| # PinSites classifies each alias where it stands, not by its line. | ||
| aliased = | ||
| for %{key_line: n, kind: :job} <- Hypatia.Rules.PinSites.node_alias_sites(content), |
There was a problem hiding this comment.
Recognize aliases continued below job keys
When a job's alias value is placed on the following line, as in jobs:\n g:\n *job, valid YAML resolves g to the anchored mapping, but node_alias_sites/1 returns no job entry because it recognizes only the same-line g: *job form. If that external mapping calls the moved repository, aliased remains empty and WF028 omits the failing reusable call; include aliases used as continued job values when collecting live anchor lines.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Leaving this as is. g: with *job on the line below is valid YAML, but the classifier is PinSites.node_aliases/4, which WF027, PinIntegrity and the merge policy also rely on. Teaching it a new layout changes all of them, which is out of proportion for a rare form in an advisory rule. If it's worth doing, it's a PinSites change with its own tests across those callers, not something to ride along on WF028.
Generated by Claude Code
| case Map.fetch(continued, number) do | ||
| {:ok, {joined, _how}} -> joined | ||
| :error -> line |
There was a problem hiding this comment.
Read unindented continuations inside flow jobs
Inside a flow mapping YAML does not use indentation to determine the value, so jobs: {\ng: {\nuses:\nhyperpolymath/standards/.github/workflows/x.yml@main\n}\n} is a valid reusable call. wf_continued_uses/1 applies block-style indentation rules and returns no entry for this layout, leaving the empty uses: line unchanged here and causing WF028 to miss the call; resolve continued values using the active flow context rather than requiring deeper indentation.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Leaving this as is. The layout is valid YAML, but I traced who writes it: nobody does. Estate workflows are block-style, with a few KYAML (flow) jobs that keep the value on its key's line or indent it. The resolver is wf_continued_uses/1, which WF027 shares, so fixing it here would widen a shared helper for a layout with no known caller. WF028 is advisory, so the cost of a miss is one missed hint. If this ever needs fixing, it belongs in the shared resolver for WF027 and WF028 together, not in this PR.
Generated by Claude Code
…tension A workflow file name may hold `@` (`build@nightly.yml@release`), so the reusable's path now ends at `.yml`/`.yaml` rather than at the first `@`; `reusable` and `current_sha` are reported as GitHub reads them (Codex, #965). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J4QvDfvetbJ5z7uREtbJWw
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e7db2dc499
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| key = "[\"']?uses[\"']?\\s*:\\s*(?:[&!]\\S*\\s+)*" | ||
| quoted = "\"([^\"]*)\"|'([^']*)'" | ||
| block_value = "(?:" <> quoted <> "|([^\\s\"']+))" | ||
| flow_value = "(?:" <> quoted <> "|([^\\s\"'#,}\\]]+))" |
There was a problem hiding this comment.
Preserve hashes inside unquoted flow references
In an unquoted flow scalar, # starts a YAML comment only when preceded by whitespace, and Git permits it in branch/tag names. For jobs: { g: { uses: hyperpolymath/standards/.github/workflows/x.yml@release#one } }, this character class truncates the parsed ref to release, so WF028 emits incorrect current_sha and replacement metadata instead of preserving release#one; allow # in the plain value while continuing to stop at whitespace.
Useful? React with 👍 / 👎.
| inline_re | ||
| |> Regex.scan(line, return: :index) | ||
| |> Enum.flat_map(fn [{at, _} | groups] -> | ||
| if wf027_inline_job?(lines, flow, line, number, at + 1) or live?, |
There was a problem hiding this comment.
Track live anchored uses sites rather than whole lines
When multiple anchored mappings are defined on one physical line, aliased contains only their shared key_line, so a job alias makes live? true for every inline uses: match on that line. For example, if &job contains a current-repository call and &step contains the moved-repository call on the same line, with *job used as a job and *step used only under steps:, this branch reports the step-level call even though WF028 excludes actions; retain the specific anchored site offset or identity instead of marking the entire line live.
Useful? React with 👍 / 👎.
Why
hyperpolymath/standardsmoved tometadatastician/standardson 2026-10-25. GitHub keeps a redirect for git and the REST API, but it does not follow that redirect when it resolves a reusable workflow'suses:. Every caller still naming the old owner therefore ends instartup_failure("workflow was not found") before any job runs. This was measured on hypatia's governance runs 40424594888 and 40424740139. #961 proposed a rule for this so that a regression is caught by a rule, not found by people. This PR is that rule.What
check_reusable_call_to_moved_repository/1, wired intoaudit/3) flags a reusable-workflow call by a moved repository's former name. It finds the call where WF027 does: a job'suses:key in block or flow style, a value continued below its key, or a call through a job alias. A path in arun:script, anenv:value, a comment or a step'suses:does not fire. The finding names the re-pointed reference at the same revision, since a move keeps history.StandardsRepo.former/0. Each further transfer into the org (standards ruling D97) is then one row, not a new rule.lowandauto_fixable: false. AGENTIC'smust-not-promote-to-blockingrule applies, and no fleet fixer rewrites the owner yet.cli.exnow carries the finding'sreplacementkey through to SARIF and the fleet; until now it was dropped by theMap.takewhitelist.[workflow-pin-detection]andOperations.mdlist WF028.Validation
test/rules/workflow_audit_pins_test.exs: 104 tests, 0 failures. That includes 9 new WF028 tests and a self-check that hypatia's own.github/workflowsproduce no WF028 findings.workflow_audit_chapel,workflow_audit_cron_driftandstandards_repotests pass too.true, the "step / run-script" test goes red.mix format --check-formattedpasses on the touched files.repo.hex.pm, so the project's deps (and the fullmix test, includingcli_test.exs) could not be fetched. CI is the first full-suite run.Follow-ups outside this repo
auto_fixable: true), andfix-stale-reusable-pin.sh/stale-reusable-pins.tsvstill key on the old path.rsr-oracle.ymlstill treatshyperpolymath/standardsas the canon (separate change).Generated by Claude Code