Description
The script input to actions/github-script runs as a JavaScript function body. Directly inserting an external value such as an issue title through ${{ }} can break out of a string as the code is constructed and execute other code. Values used in file paths also need separate path restrictions.
Potential impact
- Injected code may read runner files or alter artifacts.
- Depending on the job’s token and permissions, it may misuse repository features such as issues and comments or expose secrets.
Remediation
- Read values as data from
context.payload, or pass them throughenvand readprocess.env. Do not interpolate them into code strings or evaluate them witheval. - Validate the allowed format and scope of file paths and API arguments. Passing a value as data does not make every subsequent use safe.
- Limit token and secret access, and keep untrusted code out of privileged jobs.
Examples
These examples explicitly grant the permissions needed to create a comment. The first example’s /tmp file read illustrates the risk and fails if that file has not been prepared. Pin actions to a reviewed version or commit.
Before
name: test-script-run
on:
issues:
types: [opened]
permissions:
contents: read
issues: write
jobs:
script-run:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run script
uses: actions/github-script@v7
with:
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;
The issue title is inserted directly into a JavaScript string and a file path. Both code injection and path manipulation need attention.
After
name: test-script-run
on:
issues:
types: [opened]
permissions:
contents: read
issues: write
jobs:
script-run:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run script
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Thanks for reporting!'
})
return true;
The unnecessary file read is removed and only a fixed comment is posted. If the title is needed, read and validate it as data rather than inserting it into code.