Skip to content
This repository has been archived by the owner on Feb 28, 2022. It is now read-only.

Commit

Permalink
fix(html pipe): Sanitize generated markdown to avoid XSS attacks
Browse files Browse the repository at this point in the history
Properly escape the initial markdown and custom matchers to avoid potential XSS attacks via JS
injection, and also avoid DOM clubbering

fix #253
  • Loading branch information
ramboz committed May 28, 2019
1 parent 756229a commit 8c55d0d
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 3 deletions.
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/utils/mdast-to-vdom.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const unified = require('unified');
const parse = require('rehype-parse');
const { JSDOM } = require('jsdom');
const HeadingHandler = require('./heading-handler');
const sanitize = require('./sanitize-hast');
const image = require('./image-handler');
const embed = require('./embed-handler');
const link = require('./link-handler');
Expand Down
46 changes: 46 additions & 0 deletions src/utils/sanitize-hast.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
const _ = require('lodash');
const sanitize = require('hast-util-sanitize');
const GITHUB_SCHEMA = require('hast-util-sanitize/lib/github');

// Define helix specific customizations on top of the default GitHub schema
const HELIX_SCHEMA_CUSTOMIZATIONS = {
tagNames: ['esi:include'],
attributes: {
'esi:include': ['src'],
code: ['className'],
img: ['sizes', 'srcset'],
},
};

/**
* A customizer function for lodash's mergeWith method that concats arrays.
* @param {*} obj The object to merge into
* @param {*} src The object to merge from
* @returns {Array<Object>} the concatenanted array or undefined if the object is not an array
*/
function arrayMergeCustomizer(obj, src) {
if (_.isArray(obj)) {
return obj.concat(src);
}
return undefined;
}

// Define the sanitization schema for helix
const HELIX_SCHEMA = _.mergeWith(
GITHUB_SCHEMA,
HELIX_SCHEMA_CUSTOMIZATIONS,
arrayMergeCustomizer,
);

module.exports = hast => sanitize(hast, HELIX_SCHEMA);
24 changes: 24 additions & 0 deletions test/testHTMLFromMarkdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,28 @@ describe('Testing Markdown conversion', () => {
</table>
`);
});

it('XSS escape href attribute on links', async () => {
await assertMd(`
[Foo](javascript://%0Dalert('XSS!'))
[Bar](javascript:alert('XSS!'))
`, `
<p>
<a>Foo</a>
<a>Bar</a>
</p>
`);
});

it('XSS escape href in images', async () => {
await assertMd(`
![Foo](javascript://%0Dalert('XSS!'))
![Bar](javascript:alert('XSS!'))
`, `
<p>
<img alt="Foo">
<img alt="Bar">
</p>
`);
});
});
6 changes: 3 additions & 3 deletions test/testMdastToVDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ describe('Test MDAST to VDOM Transformation', () => {
it('Programmatic Matcher Function', () => {
const mdast = fs.readJSONSync(path.resolve(__dirname, 'fixtures', 'simple.json'));
const transformer = new VDOM(mdast, action.secrets);
transformer.match(({ type }) => type === 'heading', () => '<h1>All Headings are the same to me</h1>');
transformer.match(({ type }) => type === 'heading', () => '<h1 id="h1">All Headings are the same to me</h1>');
assertTransformerYieldsDocument(
transformer,
'<h1>All Headings are the same to me</h1>',
'<h1 id="user-content-h1">All Headings are the same to me</h1>',
);
});

Expand All @@ -108,7 +108,7 @@ describe('Test MDAST to VDOM Transformation', () => {
assertTransformerYieldsDocument(
transformer, `
<div>
<a name="h1"></a>
<a name="user-content-h1"></a>
<h1>All Headings are the same to me</h1>
</div>`,
);
Expand Down

0 comments on commit 8c55d0d

Please sign in to comment.