Skip to content

Commit 313d6ae

Browse files
fix(whatsapp): strip control characters from outbound document fileName (#77114)
Merged via squash. Prepared head SHA: 5417a8e Co-authored-by: masatohoshino <246810661+masatohoshino@users.noreply.github.com> Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com> Reviewed-by: @mcaxtr
1 parent 8d21ac3 commit 313d6ae

3 files changed

Lines changed: 57 additions & 1 deletion

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { describe, expect, it } from "vitest";
2+
import { resolveWhatsAppDocumentFileName } from "./document-filename.js";
3+
4+
describe("resolveWhatsAppDocumentFileName", () => {
5+
it("strips CRLF injection sequences from fileName", () => {
6+
expect(
7+
resolveWhatsAppDocumentFileName({
8+
fileName: "evil.pdf\r\nX-Injected: bad",
9+
mimetype: "application/pdf",
10+
}),
11+
).toBe("evil.pdfX-Injected: bad");
12+
});
13+
14+
it("strips C0 control characters and DEL from fileName", () => {
15+
expect(
16+
resolveWhatsAppDocumentFileName({
17+
fileName: "\x00evil\x1f\x7f.pdf",
18+
mimetype: "application/pdf",
19+
}),
20+
).toBe("evil.pdf");
21+
});
22+
23+
it("falls back to MIME-derived default when fileName collapses to empty after strip", () => {
24+
expect(
25+
resolveWhatsAppDocumentFileName({
26+
fileName: "\r\n\x00",
27+
mimetype: "application/pdf",
28+
}),
29+
).toBe("file.pdf");
30+
});
31+
32+
it("returns plain filename unchanged when no control characters present", () => {
33+
expect(
34+
resolveWhatsAppDocumentFileName({
35+
fileName: "document.pdf",
36+
mimetype: "application/pdf",
37+
}),
38+
).toBe("document.pdf");
39+
});
40+
41+
it("falls back to MIME-derived default when fileName is undefined", () => {
42+
expect(
43+
resolveWhatsAppDocumentFileName({
44+
mimetype: "application/pdf",
45+
}),
46+
).toBe("file.pdf");
47+
});
48+
49+
it("falls back to bare default when both fileName and mimetype are absent", () => {
50+
expect(resolveWhatsAppDocumentFileName({})).toBe("file");
51+
});
52+
});

extensions/whatsapp/src/document-filename.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@ export function resolveWhatsAppDocumentFileName(params: {
1313
fileName?: string;
1414
mimetype?: string;
1515
}): string {
16-
return params.fileName?.trim() || resolveWhatsAppDefaultDocumentFileName(params.mimetype);
16+
const fallbackName = resolveWhatsAppDefaultDocumentFileName(params.mimetype);
17+
// eslint-disable-next-line no-control-regex
18+
const stripped = params.fileName?.replace(/[\x00-\x1f\x7f]/g, "").trim();
19+
return stripped || fallbackName;
1720
}

test/scripts/lint-suppressions.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ describe("production lint suppressions", () => {
127127
"extensions/slack/src/monitor/provider-support.ts|typescript/no-unnecessary-type-parameters|1",
128128
"extensions/telegram/src/telegram-ingress-worker.runtime.ts|unicorn/require-post-message-target-origin|1",
129129
"extensions/telegram/src/telegram-ingress-worker.ts|unicorn/require-post-message-target-origin|1",
130+
"extensions/whatsapp/src/document-filename.ts|no-control-regex|1",
130131
"scripts/e2e/mcp-channels-harness.ts|unicorn/prefer-add-event-listener|1",
131132
"scripts/lib/extension-package-boundary.ts|typescript/no-unnecessary-type-parameters|1",
132133
"scripts/lib/plugin-npm-release.ts|typescript/no-unnecessary-type-parameters|1",

0 commit comments

Comments
 (0)