-
Notifications
You must be signed in to change notification settings - Fork 257
Description
Daily security red team scan completed for 2026-02-20. Two medium-priority security findings were identified in actions/setup/sh and actions/setup/js using the pattern-analysis technique (daily incremental scan, 437 files analyzed).
Overview
The scan identified no backdoors, secret exfiltration, or malicious obfuscation. All hardcoded credential patterns were confirmed as test fixtures. The two flagged issues are legitimate supply chain and shell injection risk patterns that should be remediated for defense-in-depth.
Finding 1: Supply Chain Risk — Unverified External Installer Execution
Severity: Medium
Location: actions/setup/sh/install_copilot_cli.sh:43,76
The script downloads a shell installer from https://raw.githubusercontent.com/github/copilot-cli/main/install.sh and executes it directly as root (sudo bash) with no SHA256 checksum verification. If the upstream URL is ever compromised or the CDN is intercepted, arbitrary code would run with root privileges on the runner.
Contrast: install_awf_binary.sh correctly downloads a checksums.txt and verifies SHA256 before execution — the same pattern should be applied here.
Forensics:
- Commit:
cbe1a90 - Author: Copilot
- Date: 2026-02-20 06:42:22 -0800
- PR: Add
base-branchsupport toassign-to-agentfor cross-repo PR creation (Addbase-branchsupport toassign-to-agentfor cross-repo PR creation #17133)
Relevant code (lines 40–76)
if curl -fsSL "$INSTALLER_URL" -o "$INSTALLER_TEMP" 2>&1; then
echo "Successfully downloaded installer"
return 0
fi
...
sudo VERSION="$VERSION" bash "$INSTALLER_TEMP"
# OR
sudo bash "$INSTALLER_TEMP"Finding 2: Shell Injection Risk — exec() with Template String Branch Name
Severity: Low–Medium
Location: actions/setup/js/upload_assets.cjs:98–99,114,174
exec.exec() is called with template string arguments that interpolate normalizedBranchName directly into shell commands:
await exec.exec(`git rev-parse --verify origin/\$\{normalizedBranchName}`);
await exec.exec(`git checkout -B \$\{normalizedBranchName} origin/\$\{normalizedBranchName}`);
await exec.exec(`git checkout --orphan \$\{normalizedBranchName}`);
await exec.exec(`git push origin \$\{normalizedBranchName}`);While normalizeBranchName() sanitizes via allowlist regex (/[^a-zA-Z0-9\-_/.]+/g), passing a single string to exec.exec() invokes the shell interpreter. The @actions/exec package supports an argument array form that bypasses shell parsing entirely and is safer.
Forensics:
- Commit:
cbe1a90 - Author: Copilot
- Date: 2026-02-20 06:42:22 -0800
- PR: Add
base-branchsupport toassign-to-agentfor cross-repo PR creation (Addbase-branchsupport toassign-to-agentfor cross-repo PR creation #17133)
Remediation Tasks
-
Task 1 (
install_copilot_cli.sh): Add SHA256 checksum verification before executing the downloaded installer, following the pattern ininstall_awf_binary.sh. Pin to a specific commit SHA or release tag rather thanmainto eliminate TOCTOU risk.- If the upstream Copilot CLI installer does not publish checksums, consider vendoring the installer or pinning to a release artifact.
@pelikhanplease review and confirm whether checksum verification is feasible for this installer.
-
Task 2 (
upload_assets.cjs): Convertexec.exec()calls that use template strings to the array argument form:// Before (shell-interpreted): await exec.exec(`git checkout -B \$\{normalizedBranchName} origin/\$\{normalizedBranchName}`); // After (no shell, safer): await exec.exec("git", ["checkout", "-B", normalizedBranchName, `origin/\$\{normalizedBranchName}`]);
Apply this pattern to all four call sites (lines 98, 99, 114, 174).
Confirmed Clean
View items confirmed non-malicious
| Pattern | Files | Assessment |
|---|---|---|
eval() usage |
render_template.test.cjs, interpolate_prompt.test.cjs, update_activation_comment.test.cjs |
Test harness only — standard Vitest pattern for loading CJS scripts |
| Hardcoded credentials | redact_secrets.test.cjs (4 instances) |
Official placeholder values (AWS AKIA example, Google AIzaSy example) used to test redaction logic |
Buffer.from(..., 'base64') |
Multiple files | Legitimate GitHub Contents API response decoding |
String.fromCharCode |
sanitize_content_core.cjs:663 |
Unicode full-width → half-width normalization, not obfuscation |
rm -rf |
clean_git_credentials_test.sh, install_awf_binary.sh |
Scoped temp directories only |
fs.unlinkSync |
safe_inputs_bootstrap.cjs:70, safe_outputs_bootstrap.cjs:62, mcp_handler_shell.cjs:77,115 |
Deleting own temp/config files with fully-qualified os.tmpdir() or hardcoded config paths |
| Suspicious keywords | clean_git_credentials.sh, substitute_placeholders.test.cjs, create_pull_request.test.cjs |
Appear in comments explaining prevention, or in test strings for sanitization validation |
| Suspicious network domains | All files | No .ru, .cn, .tk, .xyz or other suspicious TLDs found |
http.request to localhost |
safe_inputs_mcp_server_http.test.cjs |
Integration test against local port 4100 only |
Scan Metadata
| Field | Value |
|---|---|
| Scan date | 2026-02-20 |
| Scan mode | Daily Incremental |
| Technique | Pattern Analysis |
| Files analyzed | 437 |
| True positives | 2 |
| False positives | 25 (dismissed after review) |
| Run | §22228465574 |
| Cache | /tmp/gh-aw/cache-memory/security-red-team |
References:
Generated by Daily Security Red Team Agent