diff --git a/packages/techdocs-common/src/stages/publish/awsS3.test.ts b/packages/techdocs-common/src/stages/publish/awsS3.test.ts
index ada3bf1b8f..d4fb0d2dc2 100644
--- a/packages/techdocs-common/src/stages/publish/awsS3.test.ts
+++ b/packages/techdocs-common/src/stages/publish/awsS3.test.ts
@@ -287,10 +287,11 @@ describe('AwsS3Publish', () => {
mockFs({
[entityRootDir]: {
html: {
- 'file.html': '',
+ 'unsafe.html': '',
},
img: {
'with spaces.png': 'found it',
+ 'unsafe.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('');
- expect(response.header).toMatchObject({
+ expect(htmlResponse.text).toEqual('');
+ 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('');
+ expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts
index 9ddf640abf..0bf720ae31 100644
--- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts
+++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts
@@ -344,10 +344,11 @@ describe('publishing with valid credentials', () => {
mockFs({
[entityRootDir]: {
html: {
- 'file.html': '',
+ 'unsafe.html': '',
},
img: {
'with spaces.png': 'found it',
+ 'unsafe.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('');
- expect(response.header).toMatchObject({
+ expect(htmlResponse.text).toEqual('');
+ 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('');
+ expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
diff --git a/packages/techdocs-common/src/stages/publish/googleStorage.test.ts b/packages/techdocs-common/src/stages/publish/googleStorage.test.ts
index 23110ebcf4..4db1d83c7e 100644
--- a/packages/techdocs-common/src/stages/publish/googleStorage.test.ts
+++ b/packages/techdocs-common/src/stages/publish/googleStorage.test.ts
@@ -286,10 +286,11 @@ describe('GoogleGCSPublish', () => {
mockFs({
[entityRootDir]: {
html: {
- 'file.html': '',
+ 'unsafe.html': '',
},
img: {
'with spaces.png': 'found it',
+ 'unsafe.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('');
- expect(response.header).toMatchObject({
+ expect(htmlResponse.text).toEqual('');
+ 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('');
+ expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
diff --git a/packages/techdocs-common/src/stages/publish/helpers.test.ts b/packages/techdocs-common/src/stages/publish/helpers.test.ts
index ae872be016..d5af98e7e1 100644
--- a/packages/techdocs-common/src/stages/publish/helpers.test.ts
+++ b/packages/techdocs-common/src/stages/publish/helpers.test.ts
@@ -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'],
];
diff --git a/packages/techdocs-common/src/stages/publish/helpers.ts b/packages/techdocs-common/src/stages/publish/helpers.ts
index 63568699c7..3ca424055f 100644
--- a/packages/techdocs-common/src/stages/publish/helpers.ts
+++ b/packages/techdocs-common/src/stages/publish/helpers.ts
@@ -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';
}
diff --git a/packages/techdocs-common/src/stages/publish/local.test.ts b/packages/techdocs-common/src/stages/publish/local.test.ts
index a5dca5f057..114401f1db 100644
--- a/packages/techdocs-common/src/stages/publish/local.test.ts
+++ b/packages/techdocs-common/src/stages/publish/local.test.ts
@@ -84,7 +84,8 @@ describe('local publisher', () => {
mockFs.restore();
mockFs({
[resolvedDir]: {
- 'some-file.html': 'found it',
+ 'unsafe.html': '',
+ 'unsafe.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('');
+ expect(htmlResponse.header).toMatchObject({
+ 'content-type': 'text/plain; charset=utf-8',
+ });
+
+ const svgResponse = await request(app).get(`/unsafe.svg`);
+ expect(svgResponse.text).toEqual('');
+ expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts
index 35e5586ee4..dfac7fb0f7 100644
--- a/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts
+++ b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts
@@ -292,9 +292,10 @@ describe('OpenStackSwiftPublish', () => {
mockFs({
[entityRootDir]: {
html: {
- 'file.html': '',
+ 'unsafe.html': '',
},
img: {
+ 'unsafe.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('');
- expect(response.header).toMatchObject({
+ expect(htmlResponse.text).toEqual('');
+ 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('');
+ expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});