More explicit tests, and tidier helper.
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user