Every CVESearch/Get API key

CVE-2026-59960

Argos JavaScript: CI Branch Name OS Command Injection in @argos-ci/core

7.5 HighFix available

Argos JavaScript provides official Argos SDKs for JavaScript. Prior to Argos core package version 6.2.1, attacker-controlled CI branch or ref values from GITHUB_HEAD_REF or ARGOS_BRANCH can flow through config.branch and getMergeBaseCommitSha() when hasRemoteContentAccess is false. The gitFetch() and gitMergeBase() functions in packages/core/src/ci-environment/git.ts interpolate these values into execSync() command strings executed by /bin/sh -c, so shell metacharacters in a pull-request branch name can execute arbitrary commands with the Argos upload process privileges on the CI runner. Successful exploitation can expose CI secrets, alter build artifacts, or compromise the runner. This issue is fixed in Argos core package version 6.2.1.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

0.64%Up 0.2 points since Sep 15, 2026
90%50%10%1%

What each source reports

CVE List Score used7.5

Argos JavaScript: CI Branch Name OS Command Injection in @argos-ci/core

Argos JavaScript provides official Argos SDKs for JavaScript. Prior to Argos core package version 6.2.1, attacker-controlled CI branch or ref values from GITHUB_HEAD_REF or ARGOS_BRANCH can flow through config.branch and getMergeBaseCommitSha() when hasRemoteContentAccess is false. The gitFetch() and gitMergeBase() functions in packages/core/src/ci-environment/git.ts interpolate these values into execSync() command strings executed by /bin/sh -c, so shell metacharacters in a pull-request branch name can execute arbitrary commands with the Argos upload process privileges on the CI runner. Successful exploitation can expose CI secrets, alter build artifacts, or compromise the runner. This issue is fixed in Argos core package version 6.2.1.

Record
CVE-2026-59960
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02
EUVDEUVD-2026-776417.5

Argos JavaScript provides official Argos SDKs for JavaScript. Prior to Argos core package version 6.2.1, attacker-controlled CI branch or ref values from GITHUB_HEAD_REF or ARGOS_BRANCH can flow through config.branch and getMergeBaseCommitSha() when hasRemoteContentAccess is false. The gitFetch() and gitMergeBase() functions in packages/core/src/ci-environment/git.ts interpolate these values into execSync() command strings executed by /bin/sh -c, so shell metacharacters in a pull-request branch name can execute arbitrary commands with the Argos upload process privileges on the CI runner. Successful exploitation can expose CI secrets, alter build artifacts, or compromise the runner. This issue is fixed in Argos core package version 6.2.1.

Record
EUVD-2026-77641
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-4x45-gxvp-62837.5

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

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

Summary

@argos-ci/core@6.2.0 passes attacker-controlled CI branch/ref strings directly into an execSync() template literal in packages/core/src/ci-environment/git.ts:89. When a CI project has hasRemoteContentAccess: false, the Argos upload flow calls getMergeBaseCommitSha(), which invokes gitFetch() with the unsanitized branch name. Because execSync() passes the command string to /bin/sh -c, shell metacharacters such as $() command substitution are evaluated before git runs, enabling an attacker who can influence the branch name (e.g., via a pull request) to execute arbitrary OS commands on the CI runner. CVSS Base Score: 7.5 (High).

Details

The vulnerable sink is in packages/core/src/ci-environment/git.ts:87-90:

function gitFetch(input: { ref: string; depth: number; target: string }) {
  execSync(
    `git fetch --force --update-head-ok --depth ${input.depth} origin ${input.ref}:${input.target}`,
  );
}

execSync() with a template-literal string invokes /bin/sh -c "<command>". The shell expands $(), backticks, ;, and other metacharacters before spawning git, so any special characters present in input.ref or input.target are interpreted as shell instructions.

A secondary sink exists at packages/core/src/ci-environment/git.ts:67:

execSync(`git merge-base ${input.head} ${input.base}`)

Complete data flow (source → sink):

  1. packages/core/src/ci-environment/services/github-actions.ts:104 — reads env.GITHUB_HEAD_REF without validation (source).
  2. packages/core/src/ci-environment/services/github-actions.ts:165 — returns the branch from the CI context.
  3. packages/core/src/ci-environment/services/github-actions.ts:330 — stores the value as branch.
  4. packages/core/src/config.ts:119-123 — loads ciEnv?.branch into config.branch; only format: String is applied, no sanitization.
  5. packages/core/src/upload.ts:285 — calls getMergeBaseCommitSha({ base, head: config.branch }) when the API returns hasRemoteContentAccess: false.
  6. packages/core/src/ci-environment/git.ts:123 — passes attacker-controlled value as ref to gitFetch().
  7. packages/core/src/ci-environment/git.ts:89 — sink: execSync( git fetch ... origin ${input.ref}:${input.target} ).

There is no allowlist, regex, or shell-escaping applied to the branch string at any point in the chain.

Recommended remediation — replace template-literal execSync calls with execFileSync using argument arrays, which bypass the shell entirely:

-import { execSync } from "node:child_process";
+import { execFileSync, execSync } from "node:child_process";

 function gitFetch(input: { ref: string; depth: number; target: string }) {
-  execSync(
-    `git fetch --force --update-head-ok --depth ${input.depth} origin ${input.ref}:${input.target}`,
-  );
+  execFileSync("git", [
+    "fetch", "--force", "--update-head-ok",
+    "--depth", String(input.depth),
+    "origin", `${input.ref}:${input.target}`,
+  ]);
 }

 function gitMergeBase(input: { base: string; head: string }) {
-  return execSync(`git merge-base ${input.head} ${input.base}`).toString().trim();
+  return execFileSync("git", ["merge-base", input.head, input.base], { encoding: "utf8" }).trim();
 }

PoC

Prerequisites:

  • Docker installed on the test machine.
  • Internet access to pull node:22 and install @argos-ci/cli@5.0.5 from npm.

Step 1 — Build the Docker image:

docker build -t argos-vuln-001 \
  -f /path/to/vuln-001/Dockerfile \
  /path/to/reports/npmAI_634_argos-ci__argos-javascript/

The Dockerfile:

  • Uses node:22 as the base.
  • Creates a local bare git repository at /remote.git and a working repository at /git-workspace with that bare repo as origin, so git fetch has a reachable remote.
  • Installs @argos-ci/cli@5.1.0 (which depends on @argos-ci/core@6.2.0) globally from the…
Record
GHSA-4x45-gxvp-6283
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02
NVD7.5

Argos JavaScript provides official Argos SDKs for JavaScript. Prior to Argos core package version 6.2.1, attacker-controlled CI branch or ref values from GITHUB_HEAD_REF or ARGOS_BRANCH can flow through config.branch and getMergeBaseCommitSha() when hasRemoteContentAccess is false. The gitFetch() and gitMergeBase() functions in packages/core/src/ci-environment/git.ts interpolate these values into execSync() command strings executed by /bin/sh -c, so shell metacharacters in a pull-request branch name can execute arbitrary commands with the Argos upload process privileges on the CI runner. Successful exploitation can expose CI secrets, alter build artifacts, or compromise the runner. This issue is fixed in Argos core package version 6.2.1.

Record
CVE-2026-59960
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02
OSV7.5

Argos JavaScript: CI Branch Name OS Command Injection in @argos-ci/core

Argos JavaScript provides official Argos SDKs for JavaScript. Prior to Argos core package version 6.2.1, attacker-controlled CI branch or ref values from GITHUB_HEAD_REF or ARGOS_BRANCH can flow through config.branch and getMergeBaseCommitSha() when hasRemoteContentAccess is false. The gitFetch() and gitMergeBase() functions in packages/core/src/ci-environment/git.ts interpolate these values into execSync() command strings executed by /bin/sh -c, so shell metacharacters in a pull-request branch name can execute arbitrary commands with the Argos upload process privileges on the CI runner. Successful exploitation can expose CI secrets, alter build artifacts, or compromise the runner. This issue is fixed in Argos core package version 6.2.1.

Record
CVE-2026-59960
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02
OSVGHSA-4x45-gxvp-62837.5

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

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

Summary

@argos-ci/core@6.2.0 passes attacker-controlled CI branch/ref strings directly into an execSync() template literal in packages/core/src/ci-environment/git.ts:89. When a CI project has hasRemoteContentAccess: false, the Argos upload flow calls getMergeBaseCommitSha(), which invokes gitFetch() with the unsanitized branch name. Because execSync() passes the command string to /bin/sh -c, shell metacharacters such as $() command substitution are evaluated before git runs, enabling an attacker who can influence the branch name (e.g., via a pull request) to execute arbitrary OS commands on the CI runner. CVSS Base Score: 7.5 (High).

Details

The vulnerable sink is in packages/core/src/ci-environment/git.ts:87-90:

function gitFetch(input: { ref: string; depth: number; target: string }) {
  execSync(
    `git fetch --force --update-head-ok --depth ${input.depth} origin ${input.ref}:${input.target}`,
  );
}

execSync() with a template-literal string invokes /bin/sh -c "<command>". The shell expands $(), backticks, ;, and other metacharacters before spawning git, so any special characters present in input.ref or input.target are interpreted as shell instructions.

A secondary sink exists at packages/core/src/ci-environment/git.ts:67:

execSync(`git merge-base ${input.head} ${input.base}`)

Complete data flow (source → sink):

  1. packages/core/src/ci-environment/services/github-actions.ts:104 — reads env.GITHUB_HEAD_REF without validation (source).
  2. packages/core/src/ci-environment/services/github-actions.ts:165 — returns the branch from the CI context.
  3. packages/core/src/ci-environment/services/github-actions.ts:330 — stores the value as branch.
  4. packages/core/src/config.ts:119-123 — loads ciEnv?.branch into config.branch; only format: String is applied, no sanitization.
  5. packages/core/src/upload.ts:285 — calls getMergeBaseCommitSha({ base, head: config.branch }) when the API returns hasRemoteContentAccess: false.
  6. packages/core/src/ci-environment/git.ts:123 — passes attacker-controlled value as ref to gitFetch().
  7. packages/core/src/ci-environment/git.ts:89 — sink: execSync( git fetch ... origin ${input.ref}:${input.target} ).

There is no allowlist, regex, or shell-escaping applied to the branch string at any point in the chain.

Recommended remediation — replace template-literal execSync calls with execFileSync using argument arrays, which bypass the shell entirely:

-import { execSync } from "node:child_process";
+import { execFileSync, execSync } from "node:child_process";

 function gitFetch(input: { ref: string; depth: number; target: string }) {
-  execSync(
-    `git fetch --force --update-head-ok --depth ${input.depth} origin ${input.ref}:${input.target}`,
-  );
+  execFileSync("git", [
+    "fetch", "--force", "--update-head-ok",
+    "--depth", String(input.depth),
+    "origin", `${input.ref}:${input.target}`,
+  ]);
 }

 function gitMergeBase(input: { base: string; head: string }) {
-  return execSync(`git merge-base ${input.head} ${input.base}`).toString().trim();
+  return execFileSync("git", ["merge-base", input.head, input.base], { encoding: "utf8" }).trim();
 }

PoC

Prerequisites:

  • Docker installed on the test machine.
  • Internet access to pull node:22 and install @argos-ci/cli@5.0.5 from npm.

Step 1 — Build the Docker image:

docker build -t argos-vuln-001 \
  -f /path/to/vuln-001/Dockerfile \
  /path/to/reports/npmAI_634_argos-ci__argos-javascript/

The Dockerfile:

  • Uses node:22 as the base.
  • Creates a local bare git repository at /remote.git and a working repository at /git-workspace with that bare repo as origin, so git fetch has a reachable remote.
  • Installs @argos-ci/cli@5.1.0 (which depends on @argos-ci/core@6.2.0) globally from the…
Record
GHSA-4x45-gxvp-6283
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
@argos-ci/core
npm
through 6.2.0
6.2.1
@argos-ci/core
npm
before 6.2.1
6.2.1

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-59960 is a high vulnerability (CVSS 7.5) in Argos-ci Argos-javascript, published September 10, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.64%, the 51st percentile. Fixed in @argos-ci/core 6.2.1.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open