Skip to content

ci: add poutine supply-chain scan for GitHub Actions workflows - #505

Draft
reitblatt wants to merge 3 commits into
roostorg:mainfrom
reitblatt:ci/poutine-pipeline-scan
Draft

reitblatt wants to merge 3 commits into
roostorg:mainfrom
reitblatt:ci/poutine-pipeline-scan

Conversation

@reitblatt

@reitblatt reitblatt commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Description

Adds poutine (BoostSecurity) as a CI supply-chain scanner for our GitHub Actions pipelines, wired to run in CI and gated so it stays green in steady state.

poutine complements zizmor rather than duplicating it: it reasons about pipeline-level supply-chain risk (untrusted checkout execution, injection from external contributor input, workflows exposing all secrets, self-hosted runners reachable from PRs, unpinnable components, build components with known advisories, confused-deputy auto-merge) and can additionally scan stale branches and whole orgs from the API.

What's here

  • boostsecurityio/poutine-action — upstream's official action does the scanning. It is invoked twice because it emits one format per run: pretty for the job summary, sarif for code scanning.
  • Pinned to main@badd750a, not the latest tag. The newest tag (v1.1.4, Apr 2026) still builds from poutine:1.1.4; main@badd750a builds from poutine:1.1.6@sha256:722a8e09…. Pinning @main as upstream's README shows would leave us on a mutable ref, which is exactly what poutine and zizmor both warn about, so this pins the commit and the comment records why. Bump when upstream tags the 1.1.6 base.
  • A gate step, because the action always exits 0 — it redirects poutine's output to a file and never uses --fail-on-violation. The step fails the job only on warning/error SARIF levels, so notes stay informational.
  • .github/workflows/poutine.yml — mirrors the conventions in zizmor.yml: top-level permissions: {}, per-job contents: read + security-events: write, persist-credentials: false, concurrency group, all actions commit-SHA pinned. Triggers on workflow-path pushes/PRs, a weekly cron (new poutine rules/advisories can flag workflows nobody has touched), and workflow_dispatch. SARIF is uploaded to code scanning on push/schedule; on PRs (which may come from forks that cannot upload SARIF) findings land in the job summary instead. SARIF is always kept as a build artifact.
  • .poutine.yml — nothing suppressed today, disableVersionCheck: true, plus a documented example for permanently accepting a specific action if we ever want that.
  • .gitignore — ignore the generated poutine.sarif / poutine.txt.

Current scan results for this repo

Zero findings at warning or error. Every gating rule passes: untrusted_checkout_exec, injection, pr_runs_on_self_hosted, job_all_secrets, default_permissions_on_risky_events, debug_enabled, if_always_true, confused_deputy_auto_merge, unpinnable_action, unverified_script_exec, known_vulnerability_in_build_component, known_vulnerability_in_build_platform.

The only output is 6 note-level github_action_from_unverified_creator_used findings — EmbarkStudios/cargo-deny-action, astral-sh/setup-uv (×3), dtolnay/rust-toolchain, zizmorcore/zizmor-action — all of which are already commit-SHA pinned. These are reported to code scanning but deliberately do not fail CI, so merging this does not require suppressions or an allowlist to maintain. analyze_repo_stale_branches is also clean.

That means this lands green and only speaks up on a real regression.

Testing

  • Ran poutine 1.1.6 (the version the pinned action builds from) against this branch locally: zero warning/error findings; the gate's jq filter returns 0.
  • Confirmed the gate fires: lowering the threshold to note selects the 6 note findings.
  • Confirmed .poutine.yml is picked up: poutine only auto-discovers it from its working directory, and the action runs with $GITHUB_WORKSPACE as its working directory.
  • Scanned this branch's own new workflow: adds no findings (poutine does not flag boostsecurityio/poutine-action under github_action_from_unverified_creator_used, i.e. its vendor is treated as a verified creator).
  • Cross-checked analyze_local against analyze_repo (API) and analyze_repo_stale_branches; same results.
  • YAML validated; repo pre-commit hooks pass.

Checklist

  • Tests pass locally
  • uv run ruff check . passes (no unused imports or other lint errors) — no Python touched; pre-commit ruff hooks ran clean
  • uv tool run fawltydeps --check-unused --pyenv .venv passes (no unused dependencies) — no dependency changes
  • Updated CHANGELOG.md with my changes, if notable (refer to Keep a Changelog conventions)

Happy to add a CHANGELOG.md entry under Unreleased if you consider a CI-only security scanner notable — say the word and I'll push it with the PR link.

poutine (https://github.com/boostsecurityio/poutine) is a CI pipeline
scanner: it looks for supply-chain weaknesses in workflows themselves
(untrusted checkout execution, injection from external contributor
input, secrets exposure, self-hosted runners on PRs, unpinnable
components, known-vulnerable build components).

- poutine-scan.sh runs the pinned poutine image against the repo
  read-only with no network access, emits a pretty report plus SARIF,
  and gates on POUTINE_FAIL_LEVEL (default: warning).
- .github/workflows/poutine.yml runs it on workflow changes, weekly for
  rule/advisory drift, and on demand; SARIF goes to code scanning on
  push, and to the job summary on pull requests (forks cannot upload).
- .poutine.yml suppresses nothing today; note-level findings are
  reported without blocking merges.

Current state of the repo: zero warning/error findings. The six
note-level 'action from unverified creator' findings are all already
commit-SHA pinned.
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Replaces poutine-scan.sh with boostsecurityio/poutine-action, per
upstream's recommended integration.

- Pinned to main@badd750a rather than the latest tag (v1.1.4): that tag
  still ships poutine 1.1.4, while this commit ships 1.1.6, the same
  image digest the previous script pinned.
- The action always exits 0, so a small gate step reads the SARIF and
  fails the job on warning/error findings only; note-level findings are
  still reported without blocking merges.
- The action runs twice (pretty + sarif) because it emits one format per
  invocation: pretty goes to the job summary, SARIF to code scanning.

No change in results: still zero warning/error findings.
Carries over the three points raised on coop#721:

- Pin the scanner binary, not just the action: the pinned action commit
  builds FROM poutine:1.1.6@sha256:722a8e09..., so the version cannot
  drift under a fixed action SHA (this is why it is pinned to
  main@badd750a and not @main as upstream's README suggests).
- Upload SARIF only on push, annotate on pull requests: PR runs now emit
  inline file/line annotations from the SARIF instead of relying on the
  job summary alone.
- No standing suppressions to maintain: .poutine.yml is gone. Its only
  content was disableVersionCheck, now set via the
  POUTINE_DISABLE_VERSION_CHECK env var on the scan steps, so there is
  no config file that could quietly grow exceptions.

Also adds the CHANGELOG entry under 'CI & infrastructure'.
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