diff --git a/src/plugins/files/server/routes/common.test.ts b/src/plugins/files/server/routes/common.test.ts index 3b10adb5226f9..21b24a7c018d1 100644 --- a/src/plugins/files/server/routes/common.test.ts +++ b/src/plugins/files/server/routes/common.test.ts @@ -10,48 +10,40 @@ import type { File } from '../file'; import { getDownloadHeadersForFile } from './common'; describe('getDownloadHeadersForFile', () => { - function expectHeaders({ - contentDisposition, - contentType, - }: { - contentDisposition: string; - contentType: string; - }) { + function expectHeaders({ contentType }: { contentType: string }) { return { 'content-type': contentType, - 'content-disposition': `attachment; filename="${contentDisposition}"`, 'cache-control': 'max-age=31536000, immutable', - 'x-content-type-options': 'nosniff', }; } const file = { data: { name: 'test', mimeType: undefined } } as unknown as File; test('no mime type and name from file object', () => { expect(getDownloadHeadersForFile({ file, fileName: undefined })).toEqual( - expectHeaders({ contentType: 'application/octet-stream', contentDisposition: 'test' }) + expectHeaders({ contentType: 'application/octet-stream' }) ); }); test('no mime type and name (without ext)', () => { expect(getDownloadHeadersForFile({ file, fileName: 'myfile' })).toEqual( - expectHeaders({ contentType: 'application/octet-stream', contentDisposition: 'myfile' }) + expectHeaders({ contentType: 'application/octet-stream' }) ); }); test('no mime type and name (with ext)', () => { expect(getDownloadHeadersForFile({ file, fileName: 'myfile.png' })).toEqual( - expectHeaders({ contentType: 'image/png', contentDisposition: 'myfile.png' }) + expectHeaders({ contentType: 'image/png' }) ); }); test('mime type and no name', () => { const fileWithMime = { data: { ...file.data, mimeType: 'application/pdf' } } as File; expect(getDownloadHeadersForFile({ file: fileWithMime, fileName: undefined })).toEqual( - expectHeaders({ contentType: 'application/pdf', contentDisposition: 'test' }) + expectHeaders({ contentType: 'application/pdf' }) ); }); test('mime type and name', () => { const fileWithMime = { data: { ...file.data, mimeType: 'application/pdf' } } as File; expect(getDownloadHeadersForFile({ file: fileWithMime, fileName: 'a cool file.pdf' })).toEqual( - expectHeaders({ contentType: 'application/pdf', contentDisposition: 'a cool file.pdf' }) + expectHeaders({ contentType: 'application/pdf' }) ); }); }); diff --git a/src/plugins/files/server/routes/common.ts b/src/plugins/files/server/routes/common.ts index aba84ee6487ec..77d743452a1e4 100644 --- a/src/plugins/files/server/routes/common.ts +++ b/src/plugins/files/server/routes/common.ts @@ -19,11 +19,7 @@ export function getDownloadHeadersForFile({ file, fileName }: Args): ResponseHea return { 'content-type': (fileName && mime.getType(fileName)) ?? file.data.mimeType ?? 'application/octet-stream', - // Note, this name can be overridden by the client if set via a "download" attribute on the HTML tag. - 'content-disposition': `attachment; filename="${fileName || getDownloadedFileName(file)}"`, 'cache-control': 'max-age=31536000, immutable', - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options - 'x-content-type-options': 'nosniff', }; } diff --git a/src/plugins/files/server/routes/file_kind/download.ts b/src/plugins/files/server/routes/file_kind/download.ts index 842bf5744956a..94afb51751071 100644 --- a/src/plugins/files/server/routes/file_kind/download.ts +++ b/src/plugins/files/server/routes/file_kind/download.ts @@ -12,7 +12,7 @@ import type { FilesClient } from '../../../common/files_client'; import type { FileKind } from '../../../common/types'; import { fileNameWithExt } from '../common_schemas'; import { fileErrors } from '../../file'; -import { getDownloadHeadersForFile } from '../common'; +import { getDownloadHeadersForFile, getDownloadedFileName } from '../common'; import { getById } from './helpers'; import type { CreateHandler, FileKindRouter } from './types'; import { CreateRouteDefinition, FILES_API_ROUTES } from '../api_routes'; @@ -39,8 +39,9 @@ export const handler: CreateHandler = async ({ files, fileKind }, req, if (error) return error; try { const body: Response = await file.downloadContent(); - return res.ok({ + return res.file({ body, + filename: fileName ?? getDownloadedFileName(file), headers: getDownloadHeadersForFile({ file, fileName }), }); } catch (e) { diff --git a/src/plugins/files/server/routes/file_kind/integration_tests/file_kind_http.test.ts b/src/plugins/files/server/routes/file_kind/integration_tests/file_kind_http.test.ts index 6fe0986e42df9..23ddcb704b65d 100644 --- a/src/plugins/files/server/routes/file_kind/integration_tests/file_kind_http.test.ts +++ b/src/plugins/files/server/routes/file_kind/integration_tests/file_kind_http.test.ts @@ -72,7 +72,7 @@ describe('File kind HTTP API', () => { .expect(200); expect(header['content-type']).toEqual('image/png'); - expect(header['content-disposition']).toEqual('attachment; filename="test.png"'); + expect(header['content-disposition']).toEqual('attachment; filename=test.png'); expect(buffer.toString('utf8')).toEqual('what have you'); }); diff --git a/src/plugins/files/server/routes/integration_tests/routes.test.ts b/src/plugins/files/server/routes/integration_tests/routes.test.ts index 5852d017d923f..ecb14ece09ad8 100644 --- a/src/plugins/files/server/routes/integration_tests/routes.test.ts +++ b/src/plugins/files/server/routes/integration_tests/routes.test.ts @@ -331,7 +331,7 @@ describe('File HTTP API', () => { .expect(200); expect(header['content-type']).toEqual('application/pdf'); - expect(header['content-disposition']).toEqual('attachment; filename="myfilename.pdf"'); + expect(header['content-disposition']).toEqual('attachment; filename=myfilename.pdf'); expect(buffer.toString('utf8')).toEqual('test'); }); }); diff --git a/src/plugins/files/server/routes/public_facing/download.ts b/src/plugins/files/server/routes/public_facing/download.ts index 6eadde2aaa383..0cf25e323b08d 100644 --- a/src/plugins/files/server/routes/public_facing/download.ts +++ b/src/plugins/files/server/routes/public_facing/download.ts @@ -17,7 +17,7 @@ import { } from '../../file_share_service/errors'; import type { FilesRouter } from '../types'; import { CreateRouteDefinition, FILES_API_ROUTES } from '../api_routes'; -import { getDownloadHeadersForFile } from '../common'; +import { getDownloadHeadersForFile, getDownloadedFileName } from '../common'; import { fileNameWithExt } from '../common_schemas'; import { CreateHandler } from '../types'; @@ -44,8 +44,9 @@ const handler: CreateHandler = async ({ files }, req, res) => { try { const file = await fileService.asInternalUser().getByToken(token); const body: Readable = await file.downloadContent(); - return res.ok({ + return res.file({ body, + filename: fileName ?? getDownloadedFileName(file), headers: getDownloadHeadersForFile({ file, fileName }), }); } catch (e) { diff --git a/x-pack/test/api_integration/apis/cases/files.ts b/x-pack/test/api_integration/apis/cases/files.ts index 236408390c2f0..0c0effbfafd9a 100644 --- a/x-pack/test/api_integration/apis/cases/files.ts +++ b/x-pack/test/api_integration/apis/cases/files.ts @@ -310,7 +310,7 @@ export default ({ getService }: FtrProviderContext): void => { }); expect(header['content-type']).to.eql('image/png'); - expect(header['content-disposition']).to.eql('attachment; filename="test.png"'); + expect(header['content-disposition']).to.eql('attachment; filename=test.png'); expect(buffer.toString('utf8')).to.eql('abc'); });