yaml.github-actions.security.github-script-injection.github-script-injection

Author
unknown
Download Count*
License
Using variable interpolation ${{...}}
with github
context data in a actions/github-script
's script:
step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github
context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env:
to store the data and use the environment variable in the run:
script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".
Run Locally
Run in CI
Defintion
rules:
- id: github-script-injection
languages:
- yaml
message: "Using variable interpolation `${{...}}` with `github` context data in
a `actions/github-script`'s `script:` step could allow an attacker to
inject their own code into the runner. This would allow them to steal
secrets and code. `github` context data can have arbitrary user input and
should be treated as untrusted. Instead, use an intermediate environment
variable with `env:` to store the data and use the environment variable in
the `run:` script. Be sure to use double-quotes the environment variable,
like this: \"$ENVVAR\"."
metadata:
category: security
cwe:
- "CWE-94: Improper Control of Generation of Code ('Code Injection')"
owasp:
- A03:2021 - Injection
references:
- https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#understanding-the-risk-of-script-injections
- https://securitylab.github.com/research/github-actions-untrusted-input/
- https://github.com/actions/github-script
technology:
- github-actions
cwe2022-top25: true
subcategory:
- vuln
likelihood: HIGH
impact: HIGH
confidence: HIGH
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
patterns:
- pattern-inside: "steps: [...]"
- pattern-inside: |
uses: $ACTION
...
- pattern-inside: |
with:
...
script: ...
...
- pattern: "script: $SHELL"
- metavariable-regex:
metavariable: $ACTION
regex: actions/github-script@.*
- metavariable-pattern:
language: generic
metavariable: $SHELL
patterns:
- pattern-either:
- pattern: ${{ github.event.issue.title }}
- pattern: ${{ github.event.issue.body }}
- pattern: ${{ github.event.pull_request.title }}
- pattern: ${{ github.event.pull_request.body }}
- pattern: ${{ github.event.comment.body }}
- pattern: ${{ github.event.review.body }}
- pattern: ${{ github.event.review_comment.body }}
- pattern: ${{ github.event.pages. ... .page_name}}
- pattern: ${{ github.event.head_commit.message }}
- pattern: ${{ github.event.head_commit.author.email }}
- pattern: ${{ github.event.head_commit.author.name }}
- pattern: ${{ github.event.commits ... .author.email }}
- pattern: ${{ github.event.commits ... .author.name }}
- pattern: ${{ github.event.pull_request.head.ref }}
- pattern: ${{ github.event.pull_request.head.label }}
- pattern: ${{ github.event.pull_request.head.repo.default_branch }}
- pattern: ${{ github.head_ref }}
- pattern: ${{ github.event.inputs ... }}
severity: ERROR
Examples
github-script-injection.test.yaml
name: test-script-run
on:
- push
jobs:
script-run:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Run script 1
uses: actions/github-script@v6
if: steps.report-diff.outputs.passed == 'true'
with:
# ruleid: github-script-injection
script: |
const fs = require('fs');
const body = fs.readFileSync('/tmp/file.txt', {encoding: 'utf8'});
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '${{ github.event.pull_request.title }}' + body
})
return true;
- name: Run script 2
uses: actions/github-script@latest
with:
# ruleid: github-script-injection
script: |
const fs = require('fs');
const body = fs.readFileSync('/tmp/${{ github.event.issue.title }}.txt', {encoding: 'utf8'});
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Thanks for reporting!'
})
return true;
- name: Ok script 1
uses: not-github/custom-action@latest
with:
# ok: github-script-injection
script: |
return ${{ github.event.issue.title }};
- name: Ok script 2
uses: actions/github-script@latest
with:
# ok: github-script-injection
script: |
console.log('${{ github.event.workflow_run.artifacts_url }}');
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Thanks for reporting!'
})
return true;
Short Link: https://sg.run/g1G0