Files
hyperframes/packages/core/src/telemetryRedaction.test.ts
T
Miguel Ángel 71f716a91b fix(core): telemetry redaction covers folders with spaces in any script (#6734)
* fix(core): telemetry redaction covers folders with spaces in any script

* style(core): shorten the redaction folder note

* fix(core): spaced folders without a root redact only before a file name

* style(core): drop the redaction note that described the leak as intended

* style(core): two fewer comment lines in the redactor

* fix(core): bound the spaced-folder match so long lines stay linear
2026-10-29 10:19:12 +00:00

201 lines
8.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { redactKnownPaths, redactTelemetryString } from "./telemetryRedaction.js";
describe("redactTelemetryString", () => {
it("redacts macOS, Linux, Windows, file URLs, and URL query strings", () => {
expect(
redactTelemetryString(
[
"/Users/alice/project/video.mp4",
"/home/ubuntu/project/video.mp4",
"/workspace/app/video.mp4",
"C:\\Users\\Alice\\project\\video.mp4",
"file:///tmp/render/video.mp4",
"https://example.com/video.mp4?token=secret",
].join(" "),
),
).toBe("[path] [path] [path] [path] [file-url] https://example.com/video.mp4?…");
});
it("redacts a signing parameter cut off from its URL", () => {
expect(
redactTelemetryString(
"z-Signature=supersecret&X-Amz-Credential=AKID x-goog-signature=abc -t 1",
),
).toBe("z-Signature=…&X-Amz-Credential=… x-goog-signature=… -t 1");
});
// The redactor used to enumerate roots (/Users, /home, /opt, /tmp, …). Any
// root outside that list reached telemetry verbatim, which is most of them.
it.each([
"/data/media/interview.mov",
"/mnt2/nfs/share/take3.wav",
"/srv2/renders/2026/final.mp4",
"/nix/store/abc123/asset.png",
])("redacts the non-allowlisted absolute root in %s", (path) => {
const out = redactTelemetryString(`ffprobe failed reading ${path}`);
expect(out).not.toContain("/");
expect(out).toContain("[path]");
});
it("redacts relative paths, including a dash-prefixed one", () => {
expect(redactTelemetryString("could not open ./assets/-weird-name.mp3")).toBe(
"could not open [path]",
);
expect(redactTelemetryString("could not open ../-out.wav")).toBe("could not open [path]");
expect(redactTelemetryString("could not open .\\tmp\\-x.aac")).toBe("could not open [path]");
});
it("redacts a bare basename — a caller may pass one instead of a path", () => {
expect(redactTelemetryString("Invalid data found in my-client-cut.mp4")).toBe(
"Invalid data found in [file]",
);
});
// Over-redaction is cheap; these are ordinary in ffprobe stderr and turning
// them into [path] would make a diagnostic string useless.
it.each(["N/A", "24/1", "Stream #0:0", "moov atom not found", "48000/1001"])(
"leaves %s alone",
(text) => {
expect(redactTelemetryString(`ffprobe: ${text}`)).toBe(`ffprobe: ${text}`);
},
);
// A `?` is illegal in a Windows filename, so this is not a query string —
// the whole token is path, and must not survive by hiding behind a `?`.
it("consumes the rest of the token once a path is established", () => {
expect(redactTelemetryString("Navigation failed for C:\\Users\\A\\v.mov?not-a-query")).toBe(
"Navigation failed for [path]",
);
});
it("truncates after redacting, so a long path cannot survive by being cut", () => {
const out = redactTelemetryString(`/data/${"x".repeat(500)}/a.mp4`, 40);
expect(out).not.toContain("xxx");
});
// Named explicitly in review: a relative path with NO `./` prefix was
// missed by both the absolute rule (needs a leading slash) and the `./`
// rule (needs the dot), so it reached telemetry completely unredacted.
it.each([
"customer/acme-secret/video.mp4",
"assets/bgm.mp3",
"projects/client-name/cut/final.mov",
"a\\b\\c.wav",
])("redacts the bare relative path %s", (path) => {
const out = redactTelemetryString(`Invalid data found when processing ${path}`);
expect(out).toBe("Invalid data found when processing [path]");
});
it("redacts a dash-prefixed bare basename", () => {
expect(redactTelemetryString("could not open -customer-secret-intro.mp4")).toBe(
"could not open [file]",
);
});
});
// The segment classes were ASCII `\w`, so a non-Latin path went out verbatim.
// This redactor also feeds CLI telemetry and producer observation messages,
// where no known-path list is supplied to compensate.
describe("non-Latin paths", () => {
it.each([
"/数据/客户/秘密视频.mp4",
"/данные/клиент/видео.mp4",
"/data/客户/secret.mp4",
"/Users/alice/проект/видео.mp4",
])("redacts the absolute path %s", (path) => {
const out = redactTelemetryString(`ffprobe failed reading ${path}`);
expect(out).toBe("ffprobe failed reading [path]");
});
it("redacts a non-Latin bare relative path without stranding the first segment", () => {
// The lookbehind used to be `\w`-based, so a match could start mid-token
// when the preceding character was non-ASCII: this redacted to `客户[path]`.
expect(redactTelemetryString("could not open 客户/秘密/视频.mp4")).toBe(
"could not open [path]",
);
});
it.each(["./资产/背景.mp3", "../输出/final.wav"])("redacts the relative path %s", (path) => {
expect(redactTelemetryString(`could not open ${path}`)).toBe("could not open [path]");
});
it("redacts a non-Latin bare basename", () => {
expect(redactTelemetryString("Invalid data found in 秘密视频.mp4")).toBe(
"Invalid data found in [file]",
);
});
});
describe("paths with spaces", () => {
it.each([
"/Users/kim/내 드라이브/영상 프로젝트/clip.mp4",
"/Users/ivan/Мои документы/Проект Видео/видео.mp4",
"/Users/tanaka/マイ\u3000ドライブ/動画/clip.mp4",
"/Users/alex/Library/CloudStorage/GoogleDrive-alex@example.com/My Drive/Claude Projects/edit.mp4",
"/Users/alex/Dropbox (Personal)/Client Work/edit.mov",
"C:\\Users\\Kim\\OneDrive - Contoso\\Client Videos\\clip.mp4",
"\\\\fileserver\\Team Share\\clips\\clip.mp4",
])("redacts every folder of %s", (path) => {
expect(redactTelemetryString(`ffprobe failed reading ${path}`)).toBe(
"ffprobe failed reading [path]",
);
});
it("keeps the words after the path", () => {
expect(redactTelemetryString("could not read /Users/kim/My Drive/clip.mp4 was not found")).toBe(
"could not read [path] was not found",
);
});
it.each([
" Duration: N/A, start: 0.000000, bitrate: N/A",
"Stream #0:0: Video: h264, 24/1 fps, 30000/1001 tbr",
"r_frame_rate=24/1 avg_frame_rate=30000/1001",
"input/output error then read/write failed",
])("never joins ordinary ffprobe words into a path: %s", (line) => {
expect(redactTelemetryString(line)).toBe(line);
});
it("redacts spaced folders of a path that lost its root", () => {
const out = redactTelemetryString("could not open My Drive/Claude Projects/edit.mp4");
expect(out).not.toMatch(/Drive|Claude|Projects|edit/);
});
});
describe("redactKnownPaths", () => {
// Shape matching has holes by construction. A caller that built the argv
// knows the exact path, so it can name it instead of hoping a regex does.
it("redacts an exact path a regex would not recognise as one", () => {
const weird = "acme_secret_project";
expect(redactKnownPaths(`ffprobe: ${weird}: Invalid data`, [weird])).toBe(
"ffprobe: [path]: Invalid data",
);
});
it("redacts the basename too — ffprobe often reports only that", () => {
const out = redactKnownPaths("moov atom not found in secret-cut.mp4", [
"/data/x/secret-cut.mp4",
]);
expect(out).toContain("[path]");
expect(out).not.toContain("secret-cut");
});
it("leaves the message alone when no path was supplied", () => {
expect(redactKnownPaths("moov atom not found", [])).toBe("moov atom not found");
});
// Guards against a one/two-character basename turning every occurrence of
// that letter into [path].
it("ignores paths too short to be distinctive", () => {
expect(redactKnownPaths("a stream at a rate", ["a"])).toBe("a stream at a rate");
});
// This sits on an error path: throwing here turns a reported failure into an
// unhandled rejection, which is what a non-Error rejection's `undefined`
// message caused.
it.each([undefined, null, 42, {}])("returns a string for the non-string input %s", (value) => {
expect(() => redactKnownPaths(value as unknown as string, ["/tmp/x.mp4"])).not.toThrow();
});
});