diff --git a/CHANGELOG.md b/CHANGELOG.md index 3764146be38..b8ac03d08c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Media: treat legacy Word/OLE attachments with `application/msword` or `application/x-cfb` MIME as binary so printable-looking `.doc` files are not embedded into prompts as text. Fixes #54176; carries forward #54380. Thanks @andyliu. - Config: accept documented `browser.tabCleanup` keys in strict root config validation, so configured tab cleanup no longer fails before runtime reads it. Fixes #74577. Thanks @lonexreb and @ezdlp. - Cron: validate disabled job schedule edits before persisting updates, so invalid cron changes no longer partially mutate stored jobs. Fixes #74459. Thanks @yfge. - Channels/status: keep Telegram, Slack, and Google Chat read-only allowlist/default-target accessors on config-only paths, so status and channel summaries do not resolve SecretRef-backed runtime credentials. Thanks @eusine. diff --git a/src/media-understanding/apply.test.ts b/src/media-understanding/apply.test.ts index 4be1088b146..da28bca8978 100644 --- a/src/media-understanding/apply.test.ts +++ b/src/media-understanding/apply.test.ts @@ -1617,6 +1617,36 @@ describe("applyMediaUnderstanding", () => { expectFileNotApplied({ ctx, result, body: "" }); }); + it.each([ + { fileName: "legacy.doc", mediaType: "application/msword" }, + { fileName: "compound-file.doc", mediaType: "application/x-cfb" }, + ])( + "skips legacy Word/OLE MIME $mediaType even when explicitly allowed and bytes look printable", + async ({ fileName, mediaType }) => { + const printableOlePayload = Buffer.from( + "Root Entry WordDocument 1Table Data Microsoft Office legacy text preview", + "utf8", + ); + const filePath = await createTempMediaFile({ + fileName, + content: printableOlePayload, + }); + + const { ctx, result } = await applyWithDisabledMedia({ + body: "", + mediaPath: filePath, + mediaType, + cfg: createMediaDisabledConfigWithAllowedMimes([ + "text/plain", + "application/msword", + "application/x-cfb", + ]), + }); + + expectFileNotApplied({ ctx, result, body: "" }); + }, + ); + it("keeps vendor +json attachments eligible for text extraction", async () => { const filePath = await createTempMediaFile({ fileName: "payload.bin", diff --git a/src/media-understanding/apply.ts b/src/media-understanding/apply.ts index 06acf6397e7..6b0511c2bbe 100644 --- a/src/media-understanding/apply.ts +++ b/src/media-understanding/apply.ts @@ -362,7 +362,9 @@ function isBinaryMediaMime(mime?: string): boolean { mime === "application/gzip" || mime === "application/x-gzip" || mime === "application/x-rar-compressed" || - mime === "application/x-7z-compressed" + mime === "application/x-7z-compressed" || + mime === "application/msword" || + mime === "application/x-cfb" ) { return true; }