More explicit tests, and tidier helper.

Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Eric Peterson
2021-05-31 17:51:33 +02:00
parent 348c46896f
commit 33f6e98685
3 changed files with 28 additions and 15 deletions
@@ -21,11 +21,17 @@ import { getFileTreeRecursively, getHeadersForFileExtension } from './helpers';
describe('getHeadersForFileExtension', () => {
const correctMapOfExtensions = [
['.html', 'text/plain; charset=utf-8'],
['.htm', 'text/plain; charset=utf-8'],
['.HTML', 'text/plain; charset=utf-8'],
['.dhtml', 'text/plain; charset=utf-8'],
['.xhtml', 'text/plain; charset=utf-8'],
['.xml', 'text/plain; charset=utf-8'],
['.css', 'text/css; charset=utf-8'],
['.png', 'image/png'],
['.jpg', 'image/jpeg'],
['.jpeg', 'image/jpeg'],
['.svg', 'text/plain; charset=utf-8'],
['.SVG', 'text/plain; charset=utf-8'],
['.json', 'application/json; charset=utf-8'],
['.this-in-not-an-extension', 'text/plain; charset=utf-8'],
];
@@ -16,6 +16,22 @@
import mime from 'mime-types';
import recursiveReadDir from 'recursive-readdir';
/**
* Helper to get the expected content-type for a given file extension. Also
* takes XSS mitigation into account.
*/
const getContentTypeForExtension = (ext: string): string => {
const defaultContentType = 'text/plain; charset=utf-8';
// Prevent sanitization bypass by preventing browsers from directly rendering
// the contents of untrusted files.
if (ext.match(/htm|xml|svg/i)) {
return defaultContentType;
}
return mime.contentType(ext) || defaultContentType;
};
export type responseHeadersType = {
'Content-Type': string;
};
@@ -28,18 +44,9 @@ export type responseHeadersType = {
export const getHeadersForFileExtension = (
fileExtension: string,
): responseHeadersType => {
const headerType = {
'Content-Type':
mime.contentType(fileExtension) || 'text/plain; charset=utf-8',
return {
'Content-Type': getContentTypeForExtension(fileExtension),
};
// Prevent sanitization bypass by preventing browers from directly rendering
// the contents of untrusted content.
if (headerType['Content-Type'].match(/html|xml/)) {
headerType['Content-Type'] = 'text/plain; charset=utf-8';
}
return headerType;
};
/**
@@ -137,10 +137,10 @@ export class LocalPublish implements PublisherBase {
// Handle content-type header the same as all other publishers.
setHeaders: (res, filePath) => {
const fileExtension = path.extname(filePath);
const { 'Content-Type': header } = getHeadersForFileExtension(
fileExtension,
);
res.setHeader('Content-Type', header);
const headers = getHeadersForFileExtension(fileExtension);
for (const [header, value] of Object.entries(headers)) {
res.setHeader(header, value);
}
},
});
}