fix(agent-core-v2): redact non-ASCII, UNC, and forward-slash paths in telemetry - #2419
fix(agent-core-v2): redact non-ASCII, UNC, and forward-slash paths in telemetry#2419LHMQ878 wants to merge 3 commits into
Conversation
… telemetry `cleanTelemetryString` built both path patterns from `\w`, which is ASCII-only, so a match ended at the first non-ASCII byte and the rest of the path — user name included — went out in the POSTed event. A home directory named `李明`, `иван`, or `josé` leaked, as did `O'Brien`, since `'` is not in `\w` either. Two more spellings were missed outright: UNC paths have no drive letter so nothing matched them at all, and `C:/a/b` only matched the POSIX pattern, which left the drive letter behind as `C:<REDACTED: ...>`. Replace both patterns with one alternation whose segment is defined by exclusion — anything that is not a separator, whitespace, or a character no filesystem permits — covering every script without enumerating any. Merging the branches into a single pass also stops a later pattern from re-scanning an earlier one's replacement. Two smaller fixes in the same function, both visible in the new tests: - Normalize separators before looking for the `node_modules/` marker, so the diagnostic tail the module docstring promises to keep survives on Windows too. - Require a separator after a space-containing segment, so `C:\Program Files\a.txt could not be read` no longer swallows the message. Adds `test/app/telemetry/privacy.test.ts`; the module had no coverage. Closes MoonshotAI#2418
🦋 Changeset detectedLatest commit: 2c6936d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a04e94c4b3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| [ | ||
| // Drive-letter, either separator, optional `\\?\` / `\\.\` long-path prefix: | ||
| // C:\a\b, C:/a/b, \\?\C:\a\b | ||
| String.raw`(?:\\\\[?.]\\)?[A-Za-z]:[\\/]${INTERIOR}*${SEGMENT}?`, |
There was a problem hiding this comment.
Redact final path segments that contain spaces
For Windows drive-letter paths whose filename contains a space, this branch stops the match at the first word because the final component is ${SEGMENT} rather than the space-tolerant segment; for example C:\Users\alice\secret file.txt is cleaned to <REDACTED: user-file-path> file.txt, so the filename tail still leaves the process in telemetry. The previous Windows pattern redacted that whole path, and the v2 telemetry rule relies on CloudAppender redacting absolute paths as the safety net.
AGENTS.md reference: packages/agent-core-v2/AGENTS.md:L40-L42
Useful? React with 👍 / 👎.
| /** | ||
| * One path segment, defined by exclusion: anything that is not a separator, |
There was a problem hiding this comment.
Keep implementation comments out of v2 modules
This added block comment is the first of several local comments explaining regex internals, but the agent-core-v2 guide requires comments to live only in the top-of-file /** */ header and not beside statements. Please move any necessary rationale into the module header or tests, and remove the inline implementation narration so this file stays consistent with the package convention.
AGENTS.md reference: packages/agent-core-v2/AGENTS.md:L11-L15
Useful? React with 👍 / 👎.
…s intact The path alternation ended its final segment at the first space, so a filename with a space in it kept everything after that space on the wire: `C:\Users\alice\secret file.txt` cleaned to `<REDACTED: user-file-path> file.txt`. The filename is exactly the part worth redacting, so this is the leak the pattern exists to prevent. The final segment now accepts interior spaces, but only when what follows them still ends in a file extension. Trailing prose does not, so `C:\proj\a.txt could not be read` backtracks to the filename rather than swallowing the sentence — the boundary the previous design got by stopping at the first space is now expressed as a condition instead. Fixes a second over-redaction found while validating that boundary: with a space-tolerant final segment, `read 1/2 then 3/4 then 5/6` matched as one path, because `2 then 3` reads as a single spaced segment. The POSIX branch now refuses to start immediately after an alphanumeric. Requiring two interior segments would also have blocked the fraction, but it would have stopped redacting `/tmp/foo.txt` and `/etc/passwd`, which the pattern this branch replaced did redact. Comments move into the top-of-file block per the agent-core-v2 comment convention; the regex rationale is unchanged, only relocated.
|
Both findings addressed in P1 — confirmed for the drive-letter branch. I ran the old and new patterns side by side rather than reasoning about them: One correction to the finding: Fix. The final segment now accepts interior spaces, but only when what follows still ends in a file extension: const FINAL_SEGMENT = String.raw`(?:${SEGMENT_WITH_SPACES}\.[A-Za-z][A-Za-z0-9]{0,9}(?![^\s"'<>])|${SEGMENT})`;Prose after a path has no extension at the end, so the alternation backtracks to the bare segment. The boundary the old design got for free by stopping at the first space is now an explicit condition, and A second over-redaction, mine, not previously reported. With a space-tolerant final segment, String.raw`(?<![A-Za-z0-9])(?:\/${SEGMENT_WITH_SPACES}(?=\/))+\/${FINAL_SEGMENT}\/?`,Worth recording why not P2 — correct, and it applied to more than the one block. Every per-constant Verification. 23 tests pass in Full package suite is 88 failed / 3203 passed, against a 87/3201 baseline on a clean tree at the same commit. The one-test delta is |
`agent-core-v2` is private, but it is a workspace dependency of the published `@moonshot-ai/kimi-code`, so the fix reaches users and needs a version bump.
Closes #2418
Problem
cleanTelemetryStringis the last step before an event leaves the process —CloudAppender.trackcleans properties, buffers theEnrichedCloudEvent, andCloudTransportPOSTs it (cloudAppender.ts:115). Both of its path patterns were built from\w:\wis[A-Za-z0-9_], and neither pattern carries theuflag, so a match ends at the first non-ASCII byte and everything after it — user name included — goes out verbatim. On a Chinese-locale Windows install,C:\Users\<CJK name>is the ordinary case, not an edge case.Measured on
main:main/home/李明/proj/secret.txt/home/李明<REDACTED: user-file-path><REDACTED: user-file-path>C:\Users\李明\proj\secret.txt<REDACTED: user-file-path>李明\proj\secret.txt<REDACTED: user-file-path>/home/иван/proj/secret.txt/home/иван<REDACTED: user-file-path><REDACTED: user-file-path>/home/josé/proj/secret.txt<REDACTED: user-file-path>é<REDACTED: user-file-path><REDACTED: user-file-path>C:\Users\josé\proj\secret.txt<REDACTED: user-file-path>é\proj\secret.txt<REDACTED: user-file-path>C:\Users\O'Brien\proj\secret.txt<REDACTED: user-file-path>'Brien\proj\secret.txt<REDACTED: user-file-path>\\fileserver\home\alice.chen\proj\secret.txt<REDACTED: user-file-path>C:/Users/alice.chen/proj/secret.txtC:<REDACTED: user-file-path><REDACTED: user-file-path>node_modulestailC:\Users\alice.chen\repo\node_modules\pkg\index.js<REDACTED: user-file-path>node_modules/pkg/index.jsC:\Program Files\a.txt could not be read<REDACTED: user-file-path><REDACTED: user-file-path> could not be readThree root causes for the leaks:
\wis ASCII-only; UNC and\\?\forms have no drive letter soWINDOWS_PATHnever matched them; andC:/a/bneeds a backslash to matchWINDOWS_PATH, so only the tail was redacted.The last two rows are behaviour bugs rather than leaks. The
node_modules/marker is spelled with a forward slash and the Windows branch never looked for it, so the diagnostic tail the module docstring promises to keep was thrown away on Windows. AndWINDOWS_PATHallowed a bare space inside a segment without requiring a separator after it, so trailing message text was swallowed into the placeholder.None of this was covered —
privacy.tshad no test file.What changed
Replaced both patterns with one alternation. The segment is defined by exclusion — anything that is not a separator, whitespace, or a character no filesystem permits in a name:
That covers every script without enumerating any, and it also covers
O'Brien, which an enumerated class would have to remember. Three branches handle drive-letter (either separator, optional\\?\prefix), UNC, and POSIX; merging them into one pass is deliberate, because running them as separatereplacecalls let a later pattern re-scan an earlier one's replacement — that is what producednode_modules<REDACTED: user-file-path>in an intermediate version of this fix.Interior segments may contain spaces (
Program Files,alice chen) but only where a separator follows, which is what keeps a match from running into surrounding prose. The final segment uses the space-free form and stops at the first space.Two notes on the regex source:
(?:…)so a quantifier applies to the whole fragment. A bare[^…]+followed by?reads as a lazy+?, not an optional segment; that cost a debugging round here and the comment says so.redactPathnormalizes\to/before searching fornode_modules/, which is what fixes the Windows tail.Verification
vitest run test/app/telemetry/privacy.test.ts— 20 pass.privacy.tsreverted tomainand the new tests kept — 12 fail / 8 pass. Every claim in the table above is a failing assertion onmain.vitest run test/app/telemetry/— 56 pass across 6 files, unchanged.tsc -p tsconfig.json --noEmit— the only errors are two pre-existingTS2307s for@moonshot-ai/tree-sitter-bashinbashParserService.ts, present onmainand unrelated.oxlinton both touched files — 0 warnings, 0 errors.agent-core-v2suite: the set of failing test files is byte-identical before and after (64 files, all pre-existing on this Windows checkout). Test counts move only by the 20 added here.git ls-files --eolreportsi/lffor both files, per the repo's.gitattributes.Tests added
test/app/telemetry/privacy.test.ts, 20 cases in three groups:\\?\long form, forward-slash drive-letter, drive root.node_modulestails on both platforms, non-paths (3/4,/tmp,moonshotai/kimi-k2-instruct, plain prose, empty string), the labeled patterns, a path following an earlier placeholder, multiple paths in one value, and repeated calls.The leak cases assert the private identifier is absent, not merely that a placeholder is present — a partial redaction still contains a placeholder.
Scope
privacy.tsand its new test only.agent-core(v1) has no counterpart to this module; I did not touch it.