with one click
security-review
How to review PRs for security — credentials, injection, workflow permissions, supply chain, git operation safety
Menu
How to review PRs for security — credentials, injection, workflow permissions, supply chain, git operation safety
| name | security-review |
| description | How to review PRs for security — credentials, injection, workflow permissions, supply chain, git operation safety |
| domain | security |
| confidence | medium |
| source | extracted from copilot-instructions.md patterns and GitHub Actions security best practices |
Every PR that touches authentication, credentials, environment variables, file system operations, child process execution, user input handling, GitHub API calls, or workflow files must be reviewed for security. This skill provides a systematic checklist for catching security issues before they reach production.
Use this skill when a PR includes any of:
.github/workflows/ fileschild_process.exec, child_process.spawn)gh CLI wrappers)No secrets in source code — ever.
Review checklist:
GITHUB_TOKEN, NPM_TOKEN, or similar values.env files committed (verify .gitignore covers them)process.env.X or ${{ secrets.X }} in workflowsWatch for disguised secrets:
https://user:token@host)Personal data (especially email addresses) must never be written to committed files.
Squad-specific rule: git config user.email is explicitly banned from being written to any file that gets committed. This prevents PII leakage into the repository.
Review checklist:
git config user.email output captured and stored in sourceReview checklist:
undefined)process.env.X reference is added, is the variable documented?Path traversal prevention:
path.join(baseDir, userInput) without validation)path.resolve and verify the result starts with the expected base directory).., absolute paths when relative are expected, or null bytesArbitrary file write prevention:
Shell injection prevention:
child_process.exec() (exec uses a shell and is vulnerable to injection)child_process.execFile() or child_process.spawn() with argument arrays (no shell interpretation)exec must be used, validate/sanitize all interpolated valuesexec(\git commit -m "${userMessage}"`)` is injectableReview checklist for gh CLI wrappers:
gh-cli.ts module wraps GitHub CLI calls. Verify that arguments are passed as array elements, not interpolated into a shell stringgh-cli.ts or adds new gh commands, check for injection vectorspull_request_target safety:
pull_request_target + checking out PR code = critical security risk (the workflow runs with write token but executes untrusted code from the fork)pull_request_target + NO code checkout = safe (write token used only with trusted workflow code)pull_request_target, verify it does NOT checkout ${{ github.event.pull_request.head.ref }} or ${{ github.event.pull_request.head.sha }}Token scope minimization:
permissions: blockswrite permissionspermissions: write-all or omits permissions (defaults to broad), flag itRecursion safety:
GITHUB_TOKEN-generated events do NOT trigger new workflow runs (GitHub prevents infinite loops)GITHUB_TOKEN, it CAN trigger downstream workflows — verify this is intentionalSecrets in workflows:
${{ secrets.X }} in workflow run: steps where the value could leak to logs::add-mask:: to mask secret values in workflow outputThese rules are from the project's mandatory Git Safety guidelines:
Staging safety:
git add . or git add -A — these stage unintended deletions from incomplete working treesgit commit -a — same riskgit add path/to/specific/file.ts with explicit file pathsPush safety:
git push --force or --force-with-lease to shared branches (dev, main)dev or main — must use a PRsquad/{issue-number}-{slug}If a PR adds git operations (e.g., in a script, workflow, or agent action), verify every git add, git commit, and git push command follows these rules.
Typos in export names cause silent runtime failures — the export resolves to undefined instead of throwing.
Review checklist:
FSStorageProvidr vs. FSStorageProvider)export { X as Y } where X doesn't exist — TypeScript may not catch this in all configurationsNew npm dependencies should be audited:
npm audit)node:fs, node:path, node:crypto over third-party equivalents)Squad-specific: Protected bootstrap files must use ONLY node:* built-in modules. New dependencies in these files are a critical finding.
When the gh CLI is used in contexts where multiple GitHub accounts might be active:
gh auth status is validated before operationsExample 1: Critical — Shell injection in git command
PR adds:
exec(`git commit -m "${commitMessage}"`)
Finding: [critical] Shell injection — commitMessage is user-controlled.
A message containing "; rm -rf /" would execute arbitrary commands.
Fix: Use execFile('git', ['commit', '-m', commitMessage]) instead.
Example 2: Critical — pull_request_target with checkout
PR adds workflow:
on: pull_request_target
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
- run: npm test
Finding: [critical] pull_request_target + PR head checkout = untrusted
code execution with write token. An attacker can modify package.json
scripts in their fork to exfiltrate GITHUB_TOKEN.
Fix: Use 'pull_request' trigger instead, or do not checkout PR code.
Example 3: High — Email written to committed file
PR adds to init.ts:
const email = execSync('git config user.email').toString().trim();
fs.writeFileSync('.squad/config.json', JSON.stringify({ author: email }));
Finding: [high] Personal data (email) written to committed file.
This violates the PII-in-source rule. Squad config files are committed
to the repo and shared across contributors.
Fix: Read email at runtime only, never persist to committed files.
Example 4: Medium — Overly broad workflow permissions
PR adds workflow with no permissions block (inherits default write-all):
on: push
jobs:
deploy:
runs-on: ubuntu-latest
Finding: [medium] No explicit permissions block. Workflow inherits
default permissions which may be overly broad.
Fix: Add 'permissions:' block with minimum required scopes.
Example 5: Medium — New dependency when built-in exists
PR adds to package.json:
"dependencies": { "mkdirp": "^3.0.0" }
Finding: [medium] mkdirp is unnecessary — Node.js built-in
fs.mkdirSync(path, { recursive: true }) provides the same functionality.
Fix: Use node:fs built-in instead of adding a new dependency.
Example 6: Low — Missing environment variable validation
PR uses process.env.SQUAD_API_KEY without checking if it's defined:
const apiKey = process.env.SQUAD_API_KEY;
fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
Finding: [low] No validation that SQUAD_API_KEY is defined.
If undefined, the Bearer token will be "Bearer undefined" which
may produce confusing auth errors.
Fix: Check for undefined and throw a descriptive error early.
Structure your security review with severity levels:
## Security Review
**Verdict:** APPROVE | APPROVE WITH NOTES | REQUEST CHANGES | REJECT
### Findings
1. [severity: critical] — description
- File(s): path/to/file.ts:L42
- Risk: what could go wrong
- Fix: specific remediation
2. [severity: high] — ...
3. [severity: medium] — ...
4. [severity: low] — ...
### Severity Guide
- **Critical:** Exploitable vulnerability, credential exposure, or code execution risk. Must fix before merge.
- **High:** Security design flaw that could lead to exploitation. Must fix before merge.
- **Medium:** Security best practice violation. Should fix before merge.
- **Low:** Minor hardening opportunity. Can fix in a follow-up PR.
### Summary
Brief overview of security posture and required actions.
exec() and user-controlled input ("it's just a commit message")pull_request_target trigger changes ("the workflow looks fine otherwise")git add . in scripts as acceptable ("it works on my machine")permissions: blocks ("defaults are probably fine")node_modules additions in bootstrap files ("it's a small package"){what this skill teaches agents}
{what this skill teaches agents}
{what this skill teaches agents}
{what this skill teaches agents}
Categorized catalog of common Squad operations. Coordinator reads this file and presents it as an interactive menu when the user asks for available commands or help.
End-to-end validation of coordinator and agent template changes