Skip to content

fix(core): prevent OS command injection via CI branch names (GHSA-4x45-gxvp-6283)#319

Merged
gregberge merged 1 commit into
mainfrom
fix/ci-branch-command-injection
Jun 21, 2026
Merged

fix(core): prevent OS command injection via CI branch names (GHSA-4x45-gxvp-6283)#319
gregberge merged 1 commit into
mainfrom
fix/ci-branch-command-injection

Conversation

@gregberge

Copy link
Copy Markdown
Member

Summary

Fixes GHSA-4x45-gxvp-6283 — OS command injection (CWE-78, CVSS 7.5) in @argos-ci/core.

Attacker-controlled CI branch/ref strings flowed unsanitized into execSync() template literals in packages/core/src/ci-environment/git.ts. Because execSync runs the command through /bin/sh -c, shell metacharacters such as $(), backticks, and ; were evaluated before git ran. An attacker who could influence a branch name (e.g. by opening a PR, or via GITHUB_HEAD_REF / ARGOS_BRANCH) could execute arbitrary commands on the CI runner whenever a project had hasRemoteContentAccess: false.

Fix

Replaced every shell-interpolating execSync that handles user data with execFileSync("git", [...args]), which spawns git directly with an argument array and never invokes a shell. This covers all four injectable sinks (the advisory named two):

  • gitFetch — the primary sink
  • gitMergeBase — the secondary sink
  • listShas — interpolated a ref/path
  • listParentCommits — interpolated input.sha

Static, no-input calls (rev-parse, config --get) were left as-is since they carry no injectable data.

Test

Added a regression test in git.test.ts mirroring the PoC: it sets up a temp git repo with a real origin, calls getMergeBaseCommitSha with a branch name containing $(touch <marker>), and asserts the marker file is not created.

Verified the test is meaningful — it fails against the original vulnerable code (the marker gets created) and passes with the fix. tsc and prettier are clean.

🤖 Generated with Claude Code

…5-gxvp-6283)

Attacker-controlled CI branch/ref strings were interpolated into execSync()
template literals in the git CI-environment helpers. Since execSync runs the
command through /bin/sh -c, shell metacharacters such as $() were evaluated
before git ran, letting anyone who could influence a branch name (e.g. via a
PR) execute arbitrary commands on the CI runner.

Replace the shell-interpolating execSync calls with execFileSync using
argument arrays, which spawn git directly and never invoke a shell. Covers
all four injectable sinks: gitFetch, gitMergeBase, listShas and
listParentCommits.

Adds a regression test that feeds a $() payload through
getMergeBaseCommitSha and asserts the injected command does not run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
argos-js-sdk-reference Ready Ready Preview, Comment Jun 21, 2026 8:21am

Request Review

@gregberge gregberge requested a review from jsfez June 21, 2026 08:21
@gregberge gregberge merged commit 8355f3a into main Jun 21, 2026
68 checks passed
@gregberge gregberge deleted the fix/ci-branch-command-injection branch June 21, 2026 08:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant