|
| 1 | +# Security Considerations for PR Checks Workflow |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The `.github/workflows/pr-checks.yml` workflow uses `pull_request_target` which runs in the context of the base repository with write permissions. This is necessary to post comments on PRs from forks, but requires careful handling of user-controlled data to prevent security vulnerabilities. |
| 6 | + |
| 7 | +## Security Mitigations Implemented |
| 8 | + |
| 9 | +### 1. Script Injection Prevention |
| 10 | + |
| 11 | +**Vulnerability**: User-controlled data (usernames, branch names, PR titles) could contain malicious content that gets executed if interpolated directly into template literals or strings. |
| 12 | + |
| 13 | +**Mitigation**: |
| 14 | +- **Username sanitization**: GitHub usernames can only contain alphanumeric characters and hyphens. We sanitize by removing any other characters: `pr.user.login.replace(/[^a-zA-Z0-9-]/g, '')` |
| 15 | +- **Console logging**: Changed from template literals to comma-separated arguments: `console.log('Key:', value)` instead of `console.log(\`Key: \${value}\`)` |
| 16 | +- **No code execution**: User data is only used in comment bodies (Markdown), not in executable contexts |
| 17 | + |
| 18 | +### 2. Minimal Permissions |
| 19 | + |
| 20 | +The workflow uses the principle of least privilege: |
| 21 | +```yaml |
| 22 | +permissions: |
| 23 | + pull-requests: write # Only for posting comments |
| 24 | + contents: read # Only for reading PR metadata |
| 25 | +``` |
| 26 | +
|
| 27 | +### 3. No Code Checkout from Forks |
| 28 | +
|
| 29 | +This workflow does NOT checkout code from the PR branch, avoiding the risk of executing malicious code from forks. It only: |
| 30 | +- Reads PR metadata via GitHub API |
| 31 | +- Posts comments to the PR |
| 32 | +
|
| 33 | +### 4. Limited Scope |
| 34 | +
|
| 35 | +The workflow only performs these actions: |
| 36 | +1. Detect if PR is from main branch (informational) |
| 37 | +2. Detect if PR is from organization account (critical) |
| 38 | +3. Post comments with guidance |
| 39 | +
|
| 40 | +No sensitive operations like deploying, publishing, or modifying code are performed. |
| 41 | +
|
| 42 | +## What Data is Safe to Use? |
| 43 | +
|
| 44 | +### Safe (GitHub-controlled): |
| 45 | +- `context.repo.owner` - Base repository owner |
| 46 | +- `context.repo.repo` - Base repository name |
| 47 | +- `pr.number` - PR number (numeric) |
| 48 | +- `pr.head.repo.owner.type` - Owner type (enum: "User" or "Organization") |
| 49 | + |
| 50 | +### Requires Sanitization (User-controlled): |
| 51 | +- `pr.user.login` - Username (sanitize to alphanumeric + hyphens) |
| 52 | +- `pr.head.ref` - Branch name (can contain special characters) |
| 53 | +- `pr.head.repo.full_name` - Repository name (can be renamed) |
| 54 | +- `pr.title` - PR title (arbitrary text) |
| 55 | +- `pr.body` - PR description (arbitrary Markdown) |
| 56 | + |
| 57 | +## Testing for Security Issues |
| 58 | + |
| 59 | +### Recommended Tools: |
| 60 | +1. **zizmor**: Security linting for GitHub Actions |
| 61 | + ```bash |
| 62 | + pip install zizmor |
| 63 | + zizmor .github/workflows/pr-checks.yml |
| 64 | + ``` |
| 65 | + |
| 66 | +2. **actionlint**: General linting for GitHub Actions |
| 67 | + ```bash |
| 68 | + actionlint .github/workflows/pr-checks.yml |
| 69 | + ``` |
| 70 | + |
| 71 | +3. **Manual review**: Check for any use of `${}` with user-controlled data |
| 72 | + |
| 73 | +### Test Cases: |
| 74 | +1. PR from user with unusual username (test sanitization) |
| 75 | +2. PR from branch with special characters in name |
| 76 | +3. PR with malicious content in title/description |
| 77 | + |
| 78 | +## References |
| 79 | + |
| 80 | +- [GitHub Actions Security Hardening](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) |
| 81 | +- [Preventing Script Injection](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) |
| 82 | +- [actions/github-script Security](https://github.com/actions/github-script#passing-inputs-to-the-script) |
| 83 | + |
| 84 | +## Monitoring |
| 85 | + |
| 86 | +Watch for: |
| 87 | +- Unexpected workflow failures |
| 88 | +- Comments with unusual formatting |
| 89 | +- GitHub security advisories related to Actions |
| 90 | + |
| 91 | +## Updates |
| 92 | + |
| 93 | +When modifying this workflow: |
| 94 | +1. ✅ Never use `${}` with user-controlled data in template literals |
| 95 | +2. ✅ Always sanitize usernames, branch names, and other user inputs |
| 96 | +3. ✅ Use console.log with comma-separated values, not template literals |
| 97 | +4. ✅ Test with edge cases (special characters, long inputs) |
| 98 | +5. ✅ Run security scanning tools before merging |
0 commit comments