Account for SVG/XML files too.

Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Eric Peterson
2021-05-28 17:40:52 +02:00
parent 58ba10677a
commit 104d2d44ee
7 changed files with 73 additions and 29 deletions
@@ -287,10 +287,11 @@ describe('AwsS3Publish', () => {
mockFs({
[entityRootDir]: {
html: {
'file.html': '<html></html>',
'unsafe.html': '<html></html>',
},
img: {
'with spaces.png': 'found it',
'unsafe.svg': '<svg></svg>',
},
'some folder': {
'also with spaces.js': 'found it too',
@@ -328,11 +329,19 @@ describe('AwsS3Publish', () => {
metadata: { namespace, name },
} = entity;
const response = await request(app).get(
`/${namespace}/${kind}/${name}/html/file.html`,
const htmlResponse = await request(app).get(
`/${namespace}/${kind}/${name}/html/unsafe.html`,
);
expect(response.text).toEqual('<html></html>');
expect(response.header).toMatchObject({
expect(htmlResponse.text).toEqual('<html></html>');
expect(htmlResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
const svgResponse = await request(app).get(
`/${namespace}/${kind}/${name}/img/unsafe.svg`,
);
expect(svgResponse.text).toEqual('<svg></svg>');
expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
@@ -344,10 +344,11 @@ describe('publishing with valid credentials', () => {
mockFs({
[entityRootDir]: {
html: {
'file.html': '<html></html>',
'unsafe.html': '<html></html>',
},
img: {
'with spaces.png': 'found it',
'unsafe.svg': '<svg></svg>',
},
'some folder': {
'also with spaces.js': 'found it too',
@@ -385,11 +386,19 @@ describe('publishing with valid credentials', () => {
metadata: { namespace, name },
} = entity;
const response = await request(app).get(
`/${namespace}/${kind}/${name}/html/file.html`,
const htmlResponse = await request(app).get(
`/${namespace}/${kind}/${name}/html/unsafe.html`,
);
expect(response.text).toEqual('<html></html>');
expect(response.header).toMatchObject({
expect(htmlResponse.text).toEqual('<html></html>');
expect(htmlResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
const svgResponse = await request(app).get(
`/${namespace}/${kind}/${name}/img/unsafe.svg`,
);
expect(svgResponse.text).toEqual('<svg></svg>');
expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
@@ -286,10 +286,11 @@ describe('GoogleGCSPublish', () => {
mockFs({
[entityRootDir]: {
html: {
'file.html': '<html></html>',
'unsafe.html': '<html></html>',
},
img: {
'with spaces.png': 'found it',
'unsafe.svg': '<svg></svg>',
},
'some folder': {
'also with spaces.js': 'found it too',
@@ -318,6 +319,7 @@ describe('GoogleGCSPublish', () => {
const jsResponse = await request(app).get(
`/${namespace}/${kind}/${name}/some%20folder/also%20with%20spaces.js`,
);
expect(jsResponse.text).toEqual('found it too');
});
it('should pass text/plain content-type for html', async () => {
@@ -326,11 +328,19 @@ describe('GoogleGCSPublish', () => {
metadata: { namespace, name },
} = entity;
const response = await request(app).get(
`/${namespace}/${kind}/${name}/html/file.html`,
const htmlResponse = await request(app).get(
`/${namespace}/${kind}/${name}/html/unsafe.html`,
);
expect(response.text).toEqual('<html></html>');
expect(response.header).toMatchObject({
expect(htmlResponse.text).toEqual('<html></html>');
expect(htmlResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
const svgResponse = await request(app).get(
`/${namespace}/${kind}/${name}/img/unsafe.svg`,
);
expect(svgResponse.text).toEqual('<svg></svg>');
expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
@@ -25,7 +25,7 @@ describe('getHeadersForFileExtension', () => {
['.png', 'image/png'],
['.jpg', 'image/jpeg'],
['.jpeg', 'image/jpeg'],
['.svg', 'image/svg+xml'],
['.svg', 'text/plain; charset=utf-8'],
['.json', 'application/json; charset=utf-8'],
['.this-in-not-an-extension', 'text/plain; charset=utf-8'],
];
@@ -34,8 +34,8 @@ export const getHeadersForFileExtension = (
};
// Prevent sanitization bypass by preventing browers from directly rendering
// the contents of HTML files kept in storage.
if (headerType['Content-Type'].match(/html/)) {
// the contents of untrusted content.
if (headerType['Content-Type'].match(/html|xml/)) {
headerType['Content-Type'] = 'text/plain; charset=utf-8';
}
@@ -84,7 +84,8 @@ describe('local publisher', () => {
mockFs.restore();
mockFs({
[resolvedDir]: {
'some-file.html': 'found it',
'unsafe.html': '<html></html>',
'unsafe.svg': '<svg></svg>',
},
});
});
@@ -93,10 +94,16 @@ describe('local publisher', () => {
mockFs.restore();
});
it('should pass text/plain content-type for html', async () => {
const response = await request(app).get(`/some-file.html`);
expect(response.text).toEqual('found it');
expect(response.header).toMatchObject({
it('should pass text/plain content-type for unsafe types', async () => {
const htmlResponse = await request(app).get(`/unsafe.html`);
expect(htmlResponse.text).toEqual('<html></html>');
expect(htmlResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
const svgResponse = await request(app).get(`/unsafe.svg`);
expect(svgResponse.text).toEqual('<svg></svg>');
expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
@@ -292,9 +292,10 @@ describe('OpenStackSwiftPublish', () => {
mockFs({
[entityRootDir]: {
html: {
'file.html': '<html></html>',
'unsafe.html': '<html></html>',
},
img: {
'unsafe.svg': '<svg></svg>',
'with spaces.png': 'found it',
},
'some folder': {
@@ -327,17 +328,25 @@ describe('OpenStackSwiftPublish', () => {
expect(jsResponse.text).toEqual('found it too');
});
it('should pass text/plain content-type for html', async () => {
it('should pass text/plain content-type for unsafe types', async () => {
const {
kind,
metadata: { namespace, name },
} = entity;
const response = await request(app).get(
`/${namespace}/${kind}/${name}/html/file.html`,
const htmlResponse = await request(app).get(
`/${namespace}/${kind}/${name}/html/unsafe.html`,
);
expect(response.text).toEqual('<html></html>');
expect(response.header).toMatchObject({
expect(htmlResponse.text).toEqual('<html></html>');
expect(htmlResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
const svgResponse = await request(app).get(
`/${namespace}/${kind}/${name}/img/unsafe.svg`,
);
expect(svgResponse.text).toEqual('<svg></svg>');
expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});