Skip to content

actions: opt-in --resolve flag fills in the SHA each mutable @<ref> points at#57

Merged
bokuweb merged 1 commit into
mainfrom
claude/actions-resolve-sha
May 11, 2026
Merged

actions: opt-in --resolve flag fills in the SHA each mutable @<ref> points at#57
bokuweb merged 1 commit into
mainfrom
claude/actions-resolve-sha

Conversation

@bokuweb

@bokuweb bokuweb commented May 11, 2026

Copy link
Copy Markdown
Owner

Summary

Closes the deferred follow-up from #47 (actions audit).

sakimori actions audit was offline-only: it flagged foo/bar@v1
as a mutable ref but left the user to manually look up what to pin
to. New --resolve flag (off by default) hits the GitHub REST API
and surfaces the resolved SHA on each finding so the user can
copy-paste the fix straight from the report.

sakimori actions audit --resolve .github/workflows/*.yml
.github/workflows/ci.yml  (0 ok, 7 warn, 4 error)
  ERROR  build/checkout: mutable tag `v4` on third-party action `foo/bar` …
         → resolved: b4ffde65f46336ab88eb53be808477a3936bae11

Architecture

  • actions::Resolver trait keeps the offline path the default and
    lets tests substitute a deterministic fake.
  • actions::GithubResolver is the concrete impl: GET /repos/{o}/{r}/commits/{ref}
    via ureq, reads GITHUB_TOKEN to raise the rate limit to
    5000/hour.
  • actions::resolve_all(&mut findings, &dyn Resolver) walks
    findings, caches per (owner, repo, ref) so a workflow that uses
    actions/checkout@v4 ten times only hits the API once, and
    populates Finding::resolved_sha (or Finding::resolve_error
    when the lookup fails — one rate-limited action shouldn't kill
    the whole audit).

Schema additions

Finding gains two Option<String> fields, both
skip_serializing_if = "Option::is_none" so off-by-default
consumers (no --resolve) see no JSON-shape change:

  • resolved_sha: 40-char hex when the lookup succeeded.
  • resolve_error: human-readable reason when it failed
    (HTTP 403, HTTP 404, network timeout, …).

Test plan

  • cargo fmt --all -- --check

  • cargo clippy --workspace --all-targets -- -D warnings

  • cargo test --workspace — 5 new unit tests in actions::tests:

    • resolve_all_fills_sha_for_mutable_refs_and_skips_pinned_ones
    • resolve_all_caches_repeated_lookups
    • resolve_all_records_error_per_finding_without_aborting
    • finding_owner_repo_extraction_handles_subpath_and_local
    • url_encode_ref_handles_slashes_and_pluses

    142 total in sakimori-core, all green.

  • sakimori actions audit --help shows the new flag

  • Build succeeds on the (no-network) macOS dev box —
    GithubResolver::new doesn't make any calls until
    resolve_all is invoked, so it's safe to construct.

🤖 Generated with Claude Code

…> points at

Closes the deferred follow-up from #47 (`actions audit`).

`sakimori actions audit` was offline-only: it flagged
`foo/bar@v1` as a mutable ref but left the user to manually look up
what to pin to. New `--resolve` flag (off by default) hits the
GitHub REST API and surfaces the resolved SHA on each finding so
the user can copy-paste the fix straight from the report.

Architecture:

- `actions::Resolver` trait keeps the offline path the default and
  lets tests substitute a deterministic fake.
- `actions::GithubResolver` is the concrete impl
  (`GET /repos/{owner}/{repo}/commits/{ref}` via ureq, reads
  `GITHUB_TOKEN` to raise the rate limit to 5000/hour).
- `actions::resolve_all(&mut findings, &dyn Resolver)` walks
  findings, caches per `(owner, repo, ref)` so a workflow that uses
  `actions/checkout@v4` ten times only hits the API once, and
  populates `Finding::resolved_sha` (or `Finding::resolve_error`
  when the lookup fails — one rate-limited action shouldn't kill
  the whole audit).

Output:

- **text**: indented `→ resolved: <sha>` line under the finding;
  `→ resolve failed: <reason>` on lookup error.
- **JSON**: new `resolved_sha` / `resolve_error` fields on
  `Finding` (both `skip_serializing_if = "Option::is_none"` so
  off-by-default consumers see no schema change).

5 new unit tests in `actions::tests` — caching, error isolation,
owner/repo extraction edge cases, percent-encoding for
`feature/x` style refs.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Signed-off-by: bokuweb <[email protected]>
@github-actions

Copy link
Copy Markdown

sakimori report

metric count
observed 249
denied 4
lost 0

events by kind

kind count
exec 6
open 64
connect 2

denied samples (first 10)

kind detail
exec /usr/bin/whoami (/usr/bin/whoami)
open /etc/shadow
connect 1.1.1.1:443
connect 8.8.8.8:443
📊 Open the full HTML report locally
rm -rf /tmp/sakimori-25654234782 && gh run download 25654234782 -R bokuweb/sakimori -n sakimori-report -D /tmp/sakimori-25654234782 && (open /tmp/sakimori-25654234782/sakimori-report.html 2>/dev/null || xdg-open /tmp/sakimori-25654234782/sakimori-report.html 2>/dev/null || echo "open file:///tmp/sakimori-25654234782/sakimori-report.html")

Requires the gh CLI. The command downloads the workflow artifact and opens the self-contained HTML report in your browser.

@bokuweb bokuweb merged commit 2c880eb into main May 11, 2026
10 checks passed
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.

1 participant