PATH Injection Rule (Medium) Overview #
This rule detects PATH injection vulnerabilities when untrusted input is written to $GITHUB_PATH within normal workflow contexts. While these workflows have limited permissions compared to privileged triggers, PATH injection can still lead to command hijacking and build process compromise.
Key Features: #
- Normal Trigger Detection: Identifies dangerous patterns in
pull_request,push,schedule, and other normal triggers - GITHUB_PATH Write Detection: Analyzes scripts that write to
$GITHUB_PATHfile - Auto-fix Support: Automatically validates paths using
realpathbefore writing to $GITHUB_PATH - Zero False Positives: Does not flag already-safe patterns with proper path validation
Security Impact #
Severity: Medium (6/10)
PATH injection in normal workflows represents a moderate vulnerability in GitHub Actions:
- Command Hijacking: Attackers can prepend malicious directories to PATH
- Build Process Compromise: Build tools can be replaced with malicious versions
- Limited Blast Radius: Normal triggers typically have read-only permissions
- Fork-Based Attacks: External contributors can exploit this via fork PRs
- Persistence Across Steps: PATH modifications persist throughout the job
This vulnerability is classified as CWE-426: Untrusted Search Path and CWE-427: Uncontrolled Search Path Element.
Normal Workflow Triggers #
The following triggers are considered normal (non-privileged):
pull_request: Runs with read-only permissions on fork PRspush: Triggered by pushes to the repositoryschedule: Runs on a cron scheduleworkflow_dispatch: Manually triggered workflowsrelease: Triggered by release events
Example Vulnerable Workflow #
Consider this workflow that writes user input to $GITHUB_PATH:
name: Build with Custom Tools
on:
pull_request: # Normal trigger with limited permissions
types: [opened, synchronize]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# MEDIUM VULNERABILITY: Untrusted path written to GITHUB_PATH
- name: Add PR tools to PATH
run: |
echo "${{ github.head_ref }}/tools" >> "$GITHUB_PATH"
- name: Build
run: |
make build # Could execute malicious 'make' from attacker's path
Attack Scenario #
How PATH Injection Can Be Exploited in Normal Workflows:
Attacker Forks Repository: Creates a fork and opens a PR
Malicious Branch Name: Uses a crafted branch name:
Branch: ../../../tmp/evilPATH Modified: The workflow adds the attacker-controlled path:
echo "../../../tmp/evil/tools" >> "$GITHUB_PATH"Commands Potentially Hijacked: If the attacker can place binaries:
- name: Build run: | make build # May execute attacker's 'make' if path resolvesLimited Impact: In normal workflows:
- Read-only GITHUB_TOKEN limits damage
- Cannot push to repository directly
- But can still affect build artifacts and logs
Why Medium Severity #
Normal workflows with PATH injection are less dangerous because:
- Read-Only Access: Fork PRs get read-only GITHUB_TOKEN
- No Secrets by Default: Secrets aren’t available in fork PR workflows
- Limited Repository Access: Cannot modify repository content
- Contained Blast Radius: Damage limited to the workflow run
However, risks remain:
- Build artifacts may be poisoned
- CI test results can be manipulated
- Workflow logs may leak information
- Self-hosted runners could be compromised
Safe Pattern (Using Path Validation with realpath) #
The recommended approach is to validate paths using realpath:
name: Build with Custom Tools (Safe)
on:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# SAFE: Validate path before adding to GITHUB_PATH
- name: Add tools to PATH safely
env:
HEAD_REF_PATH: ${{ github.head_ref }}
run: |
# realpath resolves the path and ensures it's absolute and canonical
echo "$(realpath "$HEAD_REF_PATH")/tools" >> "$GITHUB_PATH"
- name: Build
run: |
make build # Safe - PATH contains validated paths only
Auto-Fix Example #
sisakulint can automatically fix this vulnerability:
Before (Vulnerable):
- name: Add tools to PATH
run: |
echo "${{ github.head_ref }}/bin" >> "$GITHUB_PATH"
After Auto-Fix (Safe):
- name: Add tools to PATH
env:
HEAD_REF_PATH: ${{ github.head_ref }}
run: |
echo "$(realpath "$HEAD_REF_PATH")/bin" >> "$GITHUB_PATH"
Detection Details #
The rule detects:
Direct writes to $GITHUB_PATH using various formats:
>> $GITHUB_PATH>> "$GITHUB_PATH">> '${GITHUB_PATH}'>>$GITHUB_PATH
Untrusted input sources including:
github.head_refgithub.event.pull_request.head.refgithub.event.issue.title- And other user-controlled fields
Normal workflow triggers where the impact is medium
Comparison with Critical Severity #
| Aspect | Medium (Normal Triggers) | Critical (Privileged Triggers) |
|---|---|---|
| Permissions | Read-only | Write access |
| Secrets | Not available (forks) | Available |
| Repository Access | Cannot modify | Can modify |
| Impact | Limited | Severe |
| Example Triggers | pull_request, push | pull_request_target, workflow_run |
Related Rules #
- envpath-injection-critical: Detects the same pattern in privileged workflows
- envvar-injection-medium: Detects environment variable injection via $GITHUB_ENV
- code-injection-medium: Detects direct code injection in normal contexts
References #
- CodeQL: PATH Injection (Medium)
- GitHub Security: Script Injection
- OWASP: Uncontrolled Search Path Element
Testing #
To test this rule with example workflows:
# Detect vulnerable patterns
sisakulint script/actions/envpath-injection-medium.yaml
# Apply auto-fix
sisakulint -fix on script/actions/envpath-injection-medium.yaml
Configuration #
This rule is enabled by default. To disable it, use:
sisakulint -ignore envpath-injection-medium
Note: Disabling this rule is generally not recommended, as PATH injection can still cause issues even in normal workflows.