fix: Usage of ctx.attachment overrides explicit Content-Type (#9949)

This commit is contained in:
Tom Moor
2025-08-16 11:29:00 -04:00
committed by GitHub
parent 82994c7b7b
commit c83a6b4f41
6 changed files with 35 additions and 13 deletions
+2 -1
View File
@@ -70,6 +70,7 @@
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.2",
"@dotenvx/dotenvx": "^1.48.4",
"@emoji-mart/data": "^1.2.1",
"@fast-csv/parse": "^5.0.5",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
@@ -119,6 +120,7 @@
"class-validator": "^0.14.2",
"command-score": "^0.1.2",
"compressorjs": "^1.2.1",
"content-disposition": "^0.5.4",
"cookie": "^0.7.0",
"copy-to-clipboard": "^3.3.3",
"core-js": "^3.37.0",
@@ -127,7 +129,6 @@
"date-fns": "^3.6.0",
"dd-trace": "^5.62.0",
"diff": "^5.2.0",
"@dotenvx/dotenvx": "^1.48.4",
"email-providers": "^1.14.0",
"emoji-mart": "^5.6.0",
"emoji-regex": "^10.4.0",
+9 -5
View File
@@ -1,6 +1,7 @@
import JWT from "jsonwebtoken";
import Router from "koa-router";
import mime from "mime-types";
import contentDisposition from "content-disposition";
import env from "@server/env";
import {
AuthenticationError,
@@ -97,11 +98,14 @@ router.get(
ctx.set("Cache-Control", cacheHeader);
ctx.set("Content-Type", contentType);
ctx.set("Content-Security-Policy", "sandbox");
ctx.attachment(fileName, {
type: forceDownload
? "attachment"
: FileStorage.getContentDisposition(contentType),
});
ctx.set(
"Content-Disposition",
contentDisposition(fileName, {
type: forceDownload
? "attachment"
: FileStorage.getContentDisposition(contentType),
})
);
// Handle byte range requests
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
+3 -3
View File
@@ -9,9 +9,9 @@ import env from "@server/env";
*/
export default function createCSPMiddleware() {
// Construct scripts CSP based on options in use
const defaultSrc = ["'self'"];
const scriptSrc = ["'self'"];
const styleSrc = ["'self'", "'unsafe-inline'"];
const defaultSrc: string[] = ["'self'"];
const scriptSrc: string[] = [];
const styleSrc: string[] = ["'self'", "'unsafe-inline'"];
if (env.isCloudHosted) {
scriptSrc.push("www.googletagmanager.com");
+13 -2
View File
@@ -2,6 +2,7 @@ import path from "path";
import fractionalIndex from "fractional-index";
import fs from "fs-extra";
import invariant from "invariant";
import contentDisposition from "content-disposition";
import JSZip from "jszip";
import Router from "koa-router";
import escapeRegExp from "lodash/escapeRegExp";
@@ -775,7 +776,12 @@ router.post(
if (attachments.length === 0) {
ctx.set("Content-Type", contentType);
ctx.attachment(`${fileName}.${extension}`);
ctx.set(
"Content-Disposition",
contentDisposition(`${fileName}.${extension}`, {
type: "attachment",
})
);
ctx.body = content;
return;
}
@@ -818,7 +824,12 @@ router.post(
});
ctx.set("Content-Type", "application/zip");
ctx.attachment(`${fileName}.zip`);
ctx.set(
"Content-Disposition",
contentDisposition(`${fileName}.zip`, {
type: "attachment",
})
);
ctx.body = zip.generateNodeStream(ZipHelper.defaultStreamOptions);
}
);
+7 -1
View File
@@ -1,4 +1,5 @@
import Router from "koa-router";
import contentDisposition from "content-disposition";
import { Op } from "sequelize";
import { UserRole } from "@shared/types";
import { RevisionHelper } from "@shared/utils/RevisionHelper";
@@ -166,7 +167,12 @@ router.post(
if (accept?.includes("text/html")) {
const name = `${slugify(document.titleWithDefault)}-${revision.id}.html`;
ctx.set("Content-Type", "text/html");
ctx.attachment(name);
ctx.set(
"Content-Disposition",
contentDisposition(name, {
type: "attachment",
})
);
ctx.body = content;
return;
}
+1 -1
View File
@@ -6805,7 +6805,7 @@ config-chain@^1.1.13:
resolved "https://registry.yarnpkg.com/consolidated-events/-/consolidated-events-2.0.2.tgz#da8d8f8c2b232831413d9e190dc11669c79f4a91"
integrity "sha1-2o2PjCsjKDFBPZ4ZDcEWacefSpE= sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ=="
content-disposition@~0.5.4:
content-disposition@^0.5.4, content-disposition@~0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==