Code Injection via Input
Code Injection via Input
Description
Workflows that accept workflow_dispatch inputs and interpolate them directly into shell commands give untrusted users a remote code execution primitive: an attacker can supply foo; curl attacker and the runner executes it with the workflow’s privileges. GitHub explicitly warns that user input must be validated before use in commands or file paths. 1
Vulnerable Instance
- Workflow exposes a free-form
workflow_dispatchinput (typestring). - A step calls the input inside
run: |without quoting or validation. - Runner inherits write/scoped permissions, so malicious commands can exfiltrate secrets or push commits.
name: Manual Deploy
on:
workflow_dispatch:
inputs:
target_env:
description: "Environment name"
required: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run deploy script
run: ./scripts/deploy.sh ${{ inputs.target_env }}An attacker can trigger this workflow with target_env: "prod && cat $GITHUB_TOKEN" and arbitrary commands will run.
Mitigation Strategies
- Validate inputs early
Use bash regex/allowlists orcasestatements to restrict the accepted characters or values. - Prefer enumerated choices
Convert string inputs totype: choicewhere practical so GitHub enforces the set of allowed values. - Quote and sanitize
Always quote input references (e.g.,"${{ inputs.name }}") and strip unsafe characters before use. - Separate logic from user input
Avoid passing inputs toeval, command substitution, or scripts that construct shell pipelines. - Limit workflow permissions
Set minimalpermissionsso even if injection occurs, token scope minimizes damage. - Audit dispatch triggers
Periodically reviewworkflow_dispatchworkflows for unsaferunsteps or missing validation blocks.
Secure Version
name: Manual Deploy (Safe)
on:
workflow_dispatch:
inputs:
target_env:
- description: "Environment name"
+ type: choice
+ options: [staging, production]
required: true
jobs:
deploy:
+ permissions:
+ contents: read
+ deployments: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+ - name: Validate environment
+ run: |
+ case "${{ inputs.target_env }}" in
+ staging|production) ;;
+ *) echo "Invalid env"; exit 1 ;;
+ esac
- name: Deploy
- run: ./scripts/deploy.sh ${{ inputs.target_env }}
+ run: ./scripts/deploy.sh "${{ inputs.target_env }}"
Impact
| Dimension | Severity | Notes |
|---|---|---|
| Likelihood | Manual workflows often expose unvalidated text inputs for convenience. | |
| Risk | Injected commands inherit the workflow token, enabling repo takeovers or secret theft. | |
| Blast radius | Any environment reachable by the deploy script (infra, registries, production) is exposed. |
References
- GitHub Docs, “Security hardening for GitHub Actions,” https://docs.github.com/actions/security-guides/security-hardening-for-github-actions 1
- GitHub Docs, “Events that trigger workflows: workflow_dispatch,” https://docs.github.com/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
GitHub Docs, “Security hardening for GitHub Actions,” https://docs.github.com/actions/security-guides/security-hardening-for-github-actions ↩︎ ↩︎
Last updated on