fix(whatsapp): strip control characters from outbound document fileName (#77114)

Merged via squash.

Prepared head SHA: 5417a8ee2c
Co-authored-by: masatohoshino <246810661+masatohoshino@users.noreply.github.com>
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Reviewed-by: @mcaxtr
This commit is contained in:
Masato Hoshino
2026-05-28 01:17:52 -03:00
committed by GitHub
parent 8d21ac3f6e
commit 313d6ae1b3
3 changed files with 57 additions and 1 deletions
@@ -0,0 +1,52 @@
import { describe, expect, it } from "vitest";
import { resolveWhatsAppDocumentFileName } from "./document-filename.js";
describe("resolveWhatsAppDocumentFileName", () => {
it("strips CRLF injection sequences from fileName", () => {
expect(
resolveWhatsAppDocumentFileName({
fileName: "evil.pdf\r\nX-Injected: bad",
mimetype: "application/pdf",
}),
).toBe("evil.pdfX-Injected: bad");
});
it("strips C0 control characters and DEL from fileName", () => {
expect(
resolveWhatsAppDocumentFileName({
fileName: "\x00evil\x1f\x7f.pdf",
mimetype: "application/pdf",
}),
).toBe("evil.pdf");
});
it("falls back to MIME-derived default when fileName collapses to empty after strip", () => {
expect(
resolveWhatsAppDocumentFileName({
fileName: "\r\n\x00",
mimetype: "application/pdf",
}),
).toBe("file.pdf");
});
it("returns plain filename unchanged when no control characters present", () => {
expect(
resolveWhatsAppDocumentFileName({
fileName: "document.pdf",
mimetype: "application/pdf",
}),
).toBe("document.pdf");
});
it("falls back to MIME-derived default when fileName is undefined", () => {
expect(
resolveWhatsAppDocumentFileName({
mimetype: "application/pdf",
}),
).toBe("file.pdf");
});
it("falls back to bare default when both fileName and mimetype are absent", () => {
expect(resolveWhatsAppDocumentFileName({})).toBe("file");
});
});
+4 -1
View File
@@ -13,5 +13,8 @@ export function resolveWhatsAppDocumentFileName(params: {
fileName?: string;
mimetype?: string;
}): string {
return params.fileName?.trim() || resolveWhatsAppDefaultDocumentFileName(params.mimetype);
const fallbackName = resolveWhatsAppDefaultDocumentFileName(params.mimetype);
// eslint-disable-next-line no-control-regex
const stripped = params.fileName?.replace(/[\x00-\x1f\x7f]/g, "").trim();
return stripped || fallbackName;
}
+1
View File
@@ -127,6 +127,7 @@ describe("production lint suppressions", () => {
"extensions/slack/src/monitor/provider-support.ts|typescript/no-unnecessary-type-parameters|1",
"extensions/telegram/src/telegram-ingress-worker.runtime.ts|unicorn/require-post-message-target-origin|1",
"extensions/telegram/src/telegram-ingress-worker.ts|unicorn/require-post-message-target-origin|1",
"extensions/whatsapp/src/document-filename.ts|no-control-regex|1",
"scripts/e2e/mcp-channels-harness.ts|unicorn/prefer-add-event-listener|1",
"scripts/lib/extension-package-boundary.ts|typescript/no-unnecessary-type-parameters|1",
"scripts/lib/plugin-npm-release.ts|typescript/no-unnecessary-type-parameters|1",