test(security-review): same-repo path smoke test#1319
Conversation
Package TarballHow to installgh release download pr-1319-tarball --repo aws/agentcore-cli --pattern "*.tgz" --dir /tmp/pr-tarball
npm install -g /tmp/pr-tarball/aws-agentcore-0.14.1.tgz |
agentcore-cli-automation
left a comment
There was a problem hiding this comment.
This PR is explicitly marked DO NOT MERGE in the description — it's a smoke test for the security-review workflow. Flagging the two deliberate findings as inline comments per the test's intent, and requesting changes so this isn't accidentally merged.
Please close (don't merge) once the workflow trigger path has been verified, and remove scripts/__sec_review_smoketest.mjs.
|
|
||
| // FINDING 1 — Hardcoded credential pattern. | ||
| const HARDCODED_AWS_ACCESS_KEY_ID = 'AKIAIOSFODNN7EXAMPLE'; | ||
| const HARDCODED_AWS_SECRET_ACCESS_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'; |
There was a problem hiding this comment.
HIGH — Hardcoded AWS credentials.
Lines 8–9 embed an AWS access key ID and secret access key directly in source. Even though these are AWS's documented example values (AKIAIOSFODNN7EXAMPLE / wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY) and not real credentials, committing literals that match the AWS credential format:
- Trips secret scanners (GitHub secret scanning, gitleaks, trufflehog) and pollutes their signal-to-noise.
- Sets a bad pattern that can be copy-pasted into real code.
- Is exactly the shape the
/security-reviewskill is intended to flag.
This file should be removed before close. If a smoke test fixture is needed long-term, fetch credentials from process.env or a test-only secret manager rather than literals.
| const url = new URL(req.url, 'http://localhost'); | ||
| const target = url.searchParams.get('host') ?? 'localhost'; | ||
|
|
||
| exec(`ping -c 1 ${target}`, (err, stdout, stderr) => { |
There was a problem hiding this comment.
HIGH — Command injection via exec() with unvalidated user input.
target is read from the host query parameter on line 21 and interpolated directly into a shell command on line 24:
exec(`ping -c 1 ${target}`, ...)A request like ?host=localhost;cat%20/etc/passwd (or `…`, $(…), &&, |, etc.) executes arbitrary shell commands as the server process. This is a textbook OS command injection (CWE-78).
Fixes (pick one):
- Use
execFile('ping', ['-c', '1', target], …)so arguments are passed without a shell. - Strictly validate
targetagainst an allowlist / hostname regex (e.g./^[a-zA-Z0-9.-]+$/) and reject anything else before invokingexec. - Drop the shell-out entirely and use a library/socket-based reachability check.
Again — this file is the smoke-test fixture and should be deleted after the workflow is verified rather than fixed in place.
| const HARDCODED_AWS_ACCESS_KEY_ID = 'AKIAIOSFODNN7EXAMPLE'; | ||
| const HARDCODED_AWS_SECRET_ACCESS_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'; |
There was a problem hiding this comment.
HIGH — Hardcoded AWS credentials in source code
These constants embed an AWS access key ID and secret access key directly in the repository:
const HARDCODED_AWS_ACCESS_KEY_ID = 'AKIAIOSFODNN7EXAMPLE';
const HARDCODED_AWS_SECRET_ACCESS_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY';Even though these are AWS's documented "EXAMPLE" placeholder values (so no real account is exposed here), the pattern is dangerous: committing access-key-shaped strings to a repo trains contributors to hardcode credentials and bypasses secret-scanning heuristics that distinguish example vs. real keys mostly by regex. If a real AKIA… key followed the same pattern, anyone with read access to the repo (including via forks, GitHub mirrors, or git clone of stale checkouts) could exfiltrate it, and rotation requires revoking the IAM credential rather than just removing the file.
Remediation: Load credentials from the default AWS SDK credential provider chain (environment variables, shared config/credentials files, IAM role, SSO) — never inline them. For test fixtures that genuinely need a key-shaped string, use an obviously fake value (e.g., "FAKE-ACCESS-KEY") that won't match AKIA[0-9A-Z]{16} and add a .secretlintrc/gitleaks allowlist entry if the scanner flags it. Also ensure CI runs secretlint/gitleaks on every PR so a real key in this shape would block merge.
| const url = new URL(req.url, 'http://localhost'); | ||
| const target = url.searchParams.get('host') ?? 'localhost'; | ||
|
|
||
| exec(`ping -c 1 ${target}`, (err, stdout, stderr) => { |
There was a problem hiding this comment.
HIGH — Command injection via exec() with attacker-controlled input
target is read directly from the host query parameter and interpolated into a shell command string passed to child_process.exec:
const target = url.searchParams.get('host') ?? 'localhost';
exec(`ping -c 1 ${target}`, (err, stdout, stderr) => { ... });exec invokes /bin/sh -c <string>, so any shell metacharacter in target is interpreted by the shell. A request like GET /?host=localhost;cat%20/etc/passwd runs an arbitrary follow-on command in the server process; $(…), backticks, &&, ||, |, redirection, and newlines are all available to the attacker. Because the stdout/stderr of the spawned shell is written back into the HTTP response, this is also a trivial data-exfiltration channel — the attacker reads the output of whatever command they injected.
Remediation: Use execFile (or spawn) with the binary and an argv array so arguments are passed to execve directly without a shell:
import { execFile } from 'node:child_process';
execFile('ping', ['-c', '1', target], (err, stdout, stderr) => { ... });Additionally, validate target against an allow-list pattern before use (e.g., RFC 1123 hostname regex or net.isIP), reject anything else with 400, and consider rate-limiting / authentication on this endpoint since it lets unauthenticated callers trigger outbound network traffic from the server (SSRF-adjacent).
|
Claude Security Review: posted 2 inline findings on this PR. (run) |
| const url = new URL(req.url, 'http://localhost'); | ||
| const target = url.searchParams.get('host') ?? 'localhost'; | ||
|
|
||
| exec(`ping -c 1 ${target}`, (err, stdout, stderr) => { |
Coverage Report
|
|
Closing — same-repo auto-trigger path verified end-to-end. Workflow ran (num_turns: 11, $0.48), bundled /security-review skill posted 2 inline findings at correct lines, summary comment posted. |
Test PR for the security review workflow — DO NOT MERGE.
Verifies the auto-trigger path on a same-repo PR: workflow should fire on
pull_request_target: openedand the bundled/security-reviewskill should post inline review comments for the two deliberate findings inscripts/__sec_review_smoketest.mjs.Will close once verified.