A collection of deliberately-vulnerable GitHub Actions workflows used to
demonstrate the Shoulder CLI github-actions-*
detection rules.
Each .yml in .github/workflows/ is a self-contained example that
triggers one primary rule. The matching .md next to it explains
the issue, links to the relevant research, and shows the fixed version.
A live, web-rendered view of what shoulder trust produces on
this repo. Useful for skimming the catch list, sharing a link in
review, or seeing the findings without installing the CLI locally.
The "malicious" run commands and exfil URLs in these workflows are inert — base64 strings decode to
echos, webhook URLs point atexample.com. Every workflow usesworkflow_dispatchor another non-auto trigger to avoid accidentally running.
# Scan everything
shoulder trust
# Scan one rule's example
shoulder trust .github/workflows/shell-injection.yml24 demos, one workflow per rule. Shoulder catches every one of them
on the current dev-4c679890 build.
State introduced by a less-trusted step flows into a privileged operation. Each row has a distinct sink so the remediation advice is specific.
| # | Rule | Severity | Workflow |
|---|---|---|---|
| 21 | ai-agent-untrusted-workspace | CRITICAL | ai-agent-untrusted-workspace.yml |
| 22 | local-action-from-untrusted-ref | HIGH | local-action-from-untrusted-ref.yml |
| 23 | untrusted-build-scripts-in-release | HIGH | untrusted-build-scripts-in-release.yml |
| 24 | workflow-run-untrusted-artifact | HIGH | workflow-run-untrusted-artifact.yml |
The headline demo. The v2 branch is a single PR off main that
adds one new AI-generated-looking workflow,
.github/workflows/ai-pr-reviewer.yml. Run the diff:
shoulder trust diff main..v2Shoulder reports 9 new risks on the one changed file — 2
critical (unsafe-checkout, ai-agent-untrusted-input), 2 high
(shell-injection), 5 medium. Nothing is reported on files the PR
doesn't touch.
The five highest-impact rules also have secure-baseline branches so you can demo what a "good PR that quietly turns bad" looks like for a single rule:
secure-<rule>— same asmain, but with only that one workflow fixed. The "safe" starting point.regression-<rule>— branched fromsecure-<rule>and reintroduces the vulnerability in a single commit.
Diff one against the other — no checkout required:
shoulder trust diff secure-shell-injection..regression-shell-injection| Rule | Diff command |
|---|---|
| shell-injection | shoulder trust diff secure-shell-injection..regression-shell-injection |
| unsafe-checkout | shoulder trust diff secure-unsafe-checkout..regression-unsafe-checkout |
| unpinned-action | shoulder trust diff secure-unpinned-action..regression-unpinned-action |
| excessive-permissions | shoulder trust diff secure-excessive-permissions..regression-excessive-permissions |
| secrets-exposure | shoulder trust diff secure-secrets-exposure..regression-secrets-exposure |
Scored against this repo's 24 demos (full point for catching the intended rule, half for a generic/weaker match, zero for a miss):
| Tool | Score | Notes |
|---|---|---|
| Shoulder | 24.0 / 24 | Only tool catching every demo. Unique on the AI-agent rule family, CVE/advisory enrichment, container-image pinning, curl | bash detection, EOL-runtime warnings, and workflow_run taint-source modelling. |
| Zizmor | 11.5 / 24 | Strong on line precision (every ${{ }} flagged), good coarse dangerous-triggers net. No AI-agent rules, no CVE enrichment, no malware-pattern detection. |
| Poutine | 8.0 / 24 | Smallest ruleset but the deepest taint-flow modelling of the OSS tools. Its untrusted_checkout_exec rule emits named sinks (npm, setup-node, ./.github/actions/build) with sink-specific remediation. No AI-agent or workflow_run coverage. |
| Semgrep OSS | 3.5 / 24 | Two GHA rules ship by default. Not really competing on this surface — Semgrep's focus is app-code SAST. |
Each tool has a different sweet spot. Shoulder leads on coverage breadth and AI-era patterns; Poutine has the closest architectural match on cross-boundary trust; Zizmor is the best free per-line linter for the common patterns it does cover.