# @argos-ci/core: CI Branch Name OS Command Injection

Unsanitized CI branch name flows into execSync git fetch in @argos-ci/core, letting a crafted branch name run shell commands on the CI runner.

- Payout: Research
- Severity: high
- Program: @argos-ci/core
- Found by: EQSTLab, yym8538
- Disclosed: 2026-09-10
- CVE: CVE-2026-59960
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-4x45-gxvp-6283
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-4x45-gxvp-6283
- Tags: area:oss domain:oss impact:internal-access impact:rce kind:research platform:ghsa sev:high tech:github-actions tech:nodejs technique:source-review vuln:command-injection vuln:rce

**Target:** npm CLI/SDK git helper (execSync git fetch)
**Root cause:** gitFetch() and gitMergeBase() interpolate attacker-controlled ref/branch strings into execSync template literals, which run via /bin/sh -c, so shell metacharacters like $() are evaluated before git executes.
**Steps:**
1. Set up a git repo with an origin remote and a mock Argos API returning `{"hasRemoteContentAccess": false}` for GET /v2/project
2. Set `ARGOS_BRANCH='main$(touch${IFS}/tmp/argos-ci-cve-poc)'`
3. Run `argos upload empty --files '*.png'` with ARGOS_API_BASE_URL pointing at the mock
4. Upload flow calls getMergeBaseCommitSha -> gitFetch -> execSync, shell expands $() and creates the marker file
**How found:** Source review of the upload flow traced config.branch from GITHUB_HEAD_REF through config.ts into getMergeBaseCommitSha and gitFetch, spotting the template-literal execSync sink with no sanitization.
**Exploitation:** Attacker influences the branch/ref name (e.g. PR branch name or GITHUB_HEAD_REF/ARGOS_BRANCH env) in a CI pipeline using Argos with hasRemoteContentAccess:false; the value reaches execSync in gitFetch and shell command substitution executes arbitrary commands with the upload process's privileges, enabling CI secret exfiltration and runner compromise.
**Escalation:** Highest risk in pull_request_target or privileged workflows where fork-supplied branch names are processed alongside repository secrets.
**Insight:** When auditing CI SDKs, trace env-derived ref/branch strings into child_process calls; execSync template literals are shell sinks even when the interpolated value 'looks like' a git ref, and ${IFS} defeats naive space filters.

