fix: allow genuine HTML file downloads
Address Codex review feedback: only reject HTML responses when the file metadata indicates a non-HTML type. Genuine HTML files (with mimetype text/html or .html extension) are now allowed through. - Check file.mimetype and file.name extension before rejecting HTML - Add tests for legitimate HTML file downloads
This commit is contained in:
parent
da6f3e21a5
commit
b305640e69
@ -289,6 +289,76 @@ describe("resolveSlackMedia", () => {
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("allows genuine HTML file when mimetype indicates text/html", async () => {
|
||||
// Mock the store module
|
||||
vi.doMock("../../media/store.js", () => ({
|
||||
saveMediaBuffer: vi.fn().mockResolvedValue({
|
||||
path: "/tmp/test.html",
|
||||
contentType: "text/html",
|
||||
}),
|
||||
}));
|
||||
|
||||
const { resolveSlackMedia } = await import("./media.js");
|
||||
|
||||
// A genuine HTML file shared by a user
|
||||
const htmlContent = `<!DOCTYPE html><html><body>User's HTML page</body></html>`;
|
||||
const htmlResponse = new Response(htmlContent, {
|
||||
status: 200,
|
||||
headers: { "content-type": "text/html; charset=utf-8" },
|
||||
});
|
||||
mockFetch.mockResolvedValueOnce(htmlResponse);
|
||||
|
||||
const result = await resolveSlackMedia({
|
||||
files: [
|
||||
{
|
||||
url_private: "https://files.slack.com/page.html",
|
||||
name: "page.html",
|
||||
mimetype: "text/html",
|
||||
},
|
||||
],
|
||||
token: "xoxb-test-token",
|
||||
maxBytes: 10 * 1024 * 1024,
|
||||
});
|
||||
|
||||
// Should allow genuine HTML files
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.contentType).toBe("text/html");
|
||||
});
|
||||
|
||||
it("allows HTML file based on .html extension even without mimetype", async () => {
|
||||
// Mock the store module
|
||||
vi.doMock("../../media/store.js", () => ({
|
||||
saveMediaBuffer: vi.fn().mockResolvedValue({
|
||||
path: "/tmp/test.html",
|
||||
contentType: "text/html",
|
||||
}),
|
||||
}));
|
||||
|
||||
const { resolveSlackMedia } = await import("./media.js");
|
||||
|
||||
const htmlContent = `<!DOCTYPE html><html><body>User's HTML snippet</body></html>`;
|
||||
const htmlResponse = new Response(htmlContent, {
|
||||
status: 200,
|
||||
headers: { "content-type": "text/html" },
|
||||
});
|
||||
mockFetch.mockResolvedValueOnce(htmlResponse);
|
||||
|
||||
const result = await resolveSlackMedia({
|
||||
files: [
|
||||
{
|
||||
url_private: "https://files.slack.com/snippet.html",
|
||||
name: "snippet.html",
|
||||
// No mimetype set
|
||||
},
|
||||
],
|
||||
token: "xoxb-test-token",
|
||||
maxBytes: 10 * 1024 * 1024,
|
||||
});
|
||||
|
||||
// Should allow based on .html extension
|
||||
expect(result).not.toBeNull();
|
||||
});
|
||||
|
||||
it("falls through to next file when first returns HTML", async () => {
|
||||
// Mock the store module
|
||||
vi.doMock("../../media/store.js", () => ({
|
||||
|
||||
@ -82,8 +82,12 @@ export async function resolveSlackMedia(params: {
|
||||
|
||||
// Guard: reject if we received HTML instead of expected media.
|
||||
// This happens when Slack auth fails and returns a login page.
|
||||
// Skip this check if the file metadata indicates it's genuinely an HTML file.
|
||||
const detectedMime = fetched.contentType?.split(";")[0]?.trim().toLowerCase();
|
||||
if (detectedMime === "text/html" || looksLikeHtml(fetched.buffer)) {
|
||||
const expectedMime = file.mimetype?.split(";")[0]?.trim().toLowerCase();
|
||||
const isExpectedHtml =
|
||||
expectedMime === "text/html" || file.name?.toLowerCase().endsWith(".html");
|
||||
if (!isExpectedHtml && (detectedMime === "text/html" || looksLikeHtml(fetched.buffer))) {
|
||||
const fileId = file.name ?? file.id ?? "unknown";
|
||||
logWarn(
|
||||
`slack: received HTML instead of media for file ${fileId}; possible auth failure or expired URL`,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user