GHSA-HWPP-H97W-2H3J
Vulnerability from github – Published: 2026-07-01 19:01 – Updated: 2026-07-01 19:01attach_packed_output can register arbitrary .json/.txt/.md/.xml files and bypass the MCP file-read safety check
Summary
Repomix'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.
This 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.
Affected target
- Repository:
yamadashy/repomix - Commit reviewed:
adf5a12f2211a7fabf24ee11a21734adccee5143 - Component: MCP server tools
- Package version reviewed:
repomix@1.14.0
Root cause
The 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.
Vulnerability chain
attach_packed_outputaccepts a direct file path, not only a directory.resolveOutputFilePath()allows any file whose extension matches.xml,.md,.txt, or.json.- The tool reads that file with
fs.readFile(outputFilePath, 'utf8'). - It extracts metrics, but malformed or non-Repomix JSON simply produces empty metrics rather than rejection.
formatPackToolResponse()registers the original file path under a generatedoutputId.read_repomix_outputresolves theoutputIdand returns the full file content.
Auth boundary violated
The respected boundary is the explicit local file-read guard: file_system_read_file blocks files that fail the secret scan.
The 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.
Source trace
src/mcp/tools/fileSystemReadFileTool.ts:72-83: direct file reads runrunSecretLint()and return an error if the scan finds sensitive content.src/mcp/tools/attachPackedOutputTool.ts:76-87: direct file input is accepted based only on extension.src/mcp/tools/attachPackedOutputTool.ts:111-120:.json,.txt,.md, and.xmlare supported formats.src/mcp/tools/attachPackedOutputTool.ts:228-240: JSON parse errors return empty metrics rather than rejecting the file as non-Repomix output.src/mcp/tools/attachPackedOutputTool.ts:272-308: the file is read and passed toformatPackToolResponse()without a secret check.src/mcp/tools/mcpToolRuntime.ts:77-82:formatPackToolResponse()registers the providedoutputFilePathand reads it.src/mcp/tools/readRepomixOutputTool.ts:56-74:read_repomix_outputreads the registered file path and returns its content.README.md:984-991: the tool is documented as providing secure access to existing packed outputs.
Reproduction
run.sh:
#!/usr/bin/env bash
set -euo pipefail
set +e
node run.js 2>&1 | tee transcript.txt
rc=${PIPESTATUS[0]}
set -e
printf '%s\n' "$rc" > exit-code.txt
exit "$rc"
The 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.
Observed transcript:
ATTACH_TOOL_SOURCE_READS_FILE=true
ATTACH_TOOL_HAS_SECRETLINT_CHECK=false
READ_FILE_TOOL_HAS_SECRETLINT_CHECK=true
RUNTIME_REGISTERS_OUTPUT_PATH=true
ATTACH_ACCEPTS_JSON_EXTENSION=true
ATTACHED_NON_REPOMIX_JSON_PATH=true
SENTINEL_READ_BACK=true
REPOMIX_ATTACH_PACKED_OUTPUT_ARBITRARY_JSON_READ_REPRODUCED=true
Impact
In 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.
This does not require shell execution or writing files. It requires MCP tool-call capability against a Repomix server running with local filesystem access.
Suggested fix
Apply the same safety boundary to all local-file read paths:
- Require
attach_packed_outputto validate that the target file is a genuine Repomix output before registration. - Reject malformed/non-Repomix JSON/XML/Markdown/plain files instead of registering them with empty metrics.
- Run the same secret check used by
file_system_read_filebefore registering or returning content. - Consider storing a content snapshot in the registry rather than registering arbitrary local paths for later reads.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.14.0"
},
"package": {
"ecosystem": "npm",
"name": "repomix"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.14.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-49988"
],
"database_specific": {
"cwe_ids": [
"CWE-200"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-01T19:01:41Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"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\u0027s MCP server exposes a normal `file_system_read_file` tool that reads absolute paths only after running the project\u0027s 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, \u0027utf8\u0027)`.\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\u003e\u00261 | tee transcript.txt\nrc=${PIPESTATUS[0]}\nset -e\nprintf \u0027%s\\n\u0027 \"$rc\" \u003e 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\u0027s 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.",
"id": "GHSA-hwpp-h97w-2h3j",
"modified": "2026-07-01T19:01:41Z",
"published": "2026-07-01T19:01:41Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/yamadashy/repomix/security/advisories/GHSA-hwpp-h97w-2h3j"
},
{
"type": "PACKAGE",
"url": "https://github.com/yamadashy/repomix"
}
],
"schema_version": "1.4.0",
"severity": [
{
"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",
"type": "CVSS_V4"
}
],
"summary": "repomix: attach_packed_output can bypass file-read secret scanning for supported local files"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.