Skip to content

fix: extract 7 unsafe expressions, 30 secrets, and 23 inputs to env vars, pin 19 unpinned actions#9143

Open
dagecko wants to merge 1 commit intoZ3Prover:masterfrom
dagecko:runner-guard/fix-ci-security
Open

fix: extract 7 unsafe expressions, 30 secrets, and 23 inputs to env vars, pin 19 unpinned actions#9143
dagecko wants to merge 1 commit intoZ3Prover:masterfrom
dagecko:runner-guard/fix-ci-security

Conversation

@dagecko
Copy link
Copy Markdown

@dagecko dagecko commented Mar 27, 2026

Security: Harden GitHub Actions workflows

Hey, my name is Chris (dagecko on GitHub). I'm a security researcher and I built Runner Guard, a CI/CD security scanner that detects GitHub Actions vulnerabilities. Over the last three weeks, I've been scanning public repositories for the same vulnerability classes that were exploited in the tj-actions/changed-files supply chain attack. Your repo came up in that scan as affected, and I'm working to get fixes out to maintainers.

This PR fixes what I could automatically, and flags anything else that needs a manual look. If you have any questions or concerns, just drop a comment here and I'll respond.

To connect our RGS rule IDs to vulnerabilities, you can see the full listing here: https://github.com/Vigilant-LLC/runner-guard

Fixes applied

Rule Severity File Description
RGS-002 critical memory-safety.yml Extracted unsafe expressions from run blocks to env vars
RGS-002 critical nightly.yml Extracted unsafe expressions from run blocks to env vars
RGS-002 critical nuget-build.yml Extracted unsafe expressions from run blocks to env vars
RGS-002 critical release.yml Extracted unsafe expressions from run blocks to env vars
RGS-007 medium 9 workflow files Pinned 19 unpinned actions to commit SHA
RGS-008 high 15 .lock.yml workflows Extracted 30 secrets from run blocks to env vars
RGS-014 high nightly-validation.yml, nuget-build.yml Extracted 23 workflow_dispatch inputs to env vars

Additional findings (manual review recommended)

Rule Severity File Description
RGS-004 high memory-safety-report.lock.yml 67 comment-triggered workflows without author authorization check
RGS-006 high ocaml.yaml Curl-pipe-bash remote code execution
RGS-018 high copilot-setup-steps.yml Suspicious payload execution pattern

Why these matter

RGS-002 (Expression injection): Attacker-controllable expressions like github.ref_name are interpolated directly into shell scripts before execution. A crafted tag or branch name can inject arbitrary commands into your CI, including the release and nightly pipelines.

RGS-008 (Secrets in run blocks): Secrets baked into script source code before execution. Moving them to env mappings keeps them out of the script source.

RGS-007 (Unpinned actions): Mutable tags can be force-pushed to malicious code. Same attack vector as tj-actions/changed-files.

I have detailed attack scenarios and blast radius analysis for each of these findings. I didn't include them here to avoid putting that information in a public PR. If you'd like to see the full breakdown, drop a comment or reach out and I'm happy to share privately.

How to verify

Review the diff, each change is mechanical and preserves workflow behavior:

  • Expression extraction: Moves ${{ }} expressions from run: blocks into env: mappings, preventing shell injection
  • SHA pinning: Pins third-party actions to immutable commit SHAs (original version tag preserved as comment)

Found by Runner Guard | Built by Vigilant | Research | Twitter

If this PR is not welcome, just close it and I won't send another.

@NikolajBjorner
Copy link
Copy Markdown
Contributor

We are using aw to compile from .md to lock.yml files.
From what I can see, you have updated only the lock.yml flies.
https://github.github.com/gh-aw/
The AW compiler generates the lock.yml files.
@pelikhan

@NikolajBjorner
Copy link
Copy Markdown
Contributor

There is another thing i don't get with this security hardening. It looks like hardening involves adding SHA "pinning".
If this is the way to go, then it should really be caught at authoring time.

…v vars

Removed lock.yml changes — those are generated by the AW compiler.
Applied fixes only to manually-maintained workflow files.
@dagecko dagecko force-pushed the runner-guard/fix-ci-security branch from 471eb86 to 398e269 Compare March 28, 2026 23:56
@dagecko
Copy link
Copy Markdown
Author

dagecko commented Mar 29, 2026

Thanks for the context on the AW compiler. I wasn't aware the lock.yml files were generated. I dug into the gh-aw project and the good news is the compiler does pin actions to SHAs, which is solid. I did find some other things in the compiler's generated output that affect downstream repos like yours that I think you may be interested in. Feel free to DM me on Twitter or LinkedIn and I can send it your way.

I've updated this PR to remove all the lock.yml changes and applied fixes only to the workflow files you maintain directly. All 49 fixes are mechanical. SHA pinning and expression extraction to env mappings. Shell-aware syntax is applied where needed ($env:VAR for PowerShell steps, %VAR% for cmd steps).

I took another look at your project and found some additional hardening opportunities. Here's what I just added to the updated PR:

  • 1 critical: github.ref_name injection in memory-safety.yml
  • 19 high: workflow_dispatch input injection in nightly-validation.yml (release_tag parameter, 19 instances across bash/pwsh steps)
  • 18 medium: Unpinned third-party actions across ci.yml, docs.yml, ocaml.yaml, wasm.yml, and others

On your point about catching this at authoring time, totally agree. That's exactly why I built Runner Guard — it's a GitHub Action you can add with a couple lines of YAML to catch these issues on every push/PR before they land.

I also posted on Twitter about a lot of these hardening techniques if you want to read through the research, and if you like it wouldn't mind a share.

- Chris (dagecko)

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.

2 participants