Skip to content

Commit c618ffd

Browse files
authored
HTML Parsing Fixes (#75)
* Updating deps to their latest versions * Adding test to ensure that html entities arent reencoded after csp is generated * Moving memory-fs to a defined as a dev dependency * Ensuring style tags wrapped in noscript tags also have a hash generated for them * Properly honoring xhtml mode when set on the html-webpack-plugin instance
1 parent f70d5b3 commit c618ffd

File tree

8 files changed

+451
-172
lines changed

8 files changed

+451
-172
lines changed

.eslintrc.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ module.exports = {
77
},
88
rules: {
99
'prettier/prettier': ['error', { singleQuote: true }],
10+
'import/no-extraneous-dependencies': [
11+
'error',
12+
{ devDependencies: ['test-utils/**/*', '**/*.jest.js'] },
13+
],
1014
},
1115
globals: {
1216
document: true,

package-lock.json

Lines changed: 306 additions & 166 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@
2929
"author": "Anuj Nair",
3030
"license": "MIT",
3131
"dependencies": {
32-
"cheerio": "^1.0.0-rc.3",
33-
"lodash": "^4.17.20",
34-
"memory-fs": "^0.5.0"
32+
"cheerio": "^1.0.0-rc.5",
33+
"lodash": "^4.17.20"
3534
},
3635
"peerDependencies": {
3736
"webpack": "^4 || ^5",
@@ -40,13 +39,14 @@
4039
"devDependencies": {
4140
"babel-jest": "^26.6.3",
4241
"codecov": "^3.8.1",
43-
"eslint": "^7.15.0",
42+
"eslint": "^7.16.0",
4443
"eslint-config-airbnb-base": "^14.2.1",
45-
"eslint-config-prettier": "^7.0.0",
44+
"eslint-config-prettier": "^7.1.0",
4645
"eslint-plugin-import": "^2.22.1",
4746
"eslint-plugin-prettier": "^3.3.0",
4847
"html-webpack-plugin": "^5.0.0-alpha.15",
4948
"jest": "^26.6.3",
49+
"memory-fs": "^0.5.0",
5050
"prettier": "^2.2.1",
5151
"webpack": "^5.10.1"
5252
}

plugin.jest.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,4 +939,95 @@ describe('CspHtmlWebpackPlugin', () => {
939939
});
940940
});
941941
});
942+
943+
describe('HTML parsing', () => {
944+
it("doesn't encode escaped HTML entities", (done) => {
945+
const config = createWebpackConfig([
946+
new HtmlWebpackPlugin({
947+
filename: path.join(WEBPACK_OUTPUT_DIR, 'index.html'),
948+
template: path.join(
949+
__dirname,
950+
'test-utils',
951+
'fixtures',
952+
'with-escaped-html.html'
953+
),
954+
}),
955+
new CspHtmlWebpackPlugin(),
956+
]);
957+
958+
webpackCompile(config, (_, selectors) => {
959+
const $ = selectors['index.html'];
960+
expect($('body').html().trim()).toEqual(
961+
'<h1>Escaped Content<h1>'
962+
);
963+
done();
964+
});
965+
});
966+
967+
it('generates a hash for style tags wrapped in noscript tags', (done) => {
968+
const config = createWebpackConfig([
969+
new HtmlWebpackPlugin({
970+
filename: path.join(WEBPACK_OUTPUT_DIR, 'index.html'),
971+
template: path.join(
972+
__dirname,
973+
'test-utils',
974+
'fixtures',
975+
'with-noscript-tags.html'
976+
),
977+
}),
978+
new CspHtmlWebpackPlugin(),
979+
]);
980+
981+
webpackCompile(config, (csps) => {
982+
const expected =
983+
"base-uri 'self';" +
984+
" object-src 'none';" +
985+
" script-src 'unsafe-inline' 'self' 'unsafe-eval' 'nonce-mockedbase64string-1';" +
986+
" style-src 'unsafe-inline' 'self' 'unsafe-eval' 'sha256-JUH8Xh1Os2tA1KU3Lfxn5uZXj2Q/a/i0UVMzpWO4uOU='";
987+
988+
expect(csps['index.html']).toEqual(expected);
989+
990+
done();
991+
});
992+
});
993+
994+
it('honors xhtml mode if set on the html-webpack-plugin instance', (done) => {
995+
const config = createWebpackConfig([
996+
new HtmlWebpackPlugin({
997+
filename: path.join(WEBPACK_OUTPUT_DIR, 'index.html'),
998+
template: path.join(
999+
__dirname,
1000+
'test-utils',
1001+
'fixtures',
1002+
'with-xhtml.html'
1003+
),
1004+
xhtml: true,
1005+
}),
1006+
new CspHtmlWebpackPlugin(),
1007+
]);
1008+
1009+
webpackCompile(config, (csps, selectors, fileSystem) => {
1010+
const xhtmlContents = fileSystem
1011+
.readFileSync(path.join(WEBPACK_OUTPUT_DIR, 'index.html'), 'utf8')
1012+
.toString();
1013+
1014+
// correct doctype
1015+
expect(xhtmlContents).toContain(
1016+
'<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
1017+
);
1018+
1019+
// self closing tag
1020+
expect(xhtmlContents).toContain(
1021+
'<meta name="author" content="Slack"/>'
1022+
);
1023+
1024+
// csp has been added in
1025+
expect(xhtmlContents).toContain(
1026+
`<meta http-equiv="Content-Security-Policy" content="base-uri 'self'; object-src 'none'; script-src 'unsafe-inline' 'self' 'unsafe-eval' 'nonce-mockedbase64string-1'; style-src 'unsafe-inline' 'self' 'unsafe-eval'"/>`
1027+
);
1028+
1029+
done();
1030+
});
1031+
});
1032+
});
9421033
});

plugin.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ const defaultProcessFn = (builtPolicy, htmlPluginData, $) => {
4141
metaTag.attr('content', builtPolicy);
4242

4343
// eslint-disable-next-line no-param-reassign
44-
htmlPluginData.html = $.html();
44+
htmlPluginData.html = get(htmlPluginData, 'plugin.options.xhtml', false)
45+
? $.xml()
46+
: $.html();
4547
};
4648

4749
const defaultPolicy = {
@@ -312,6 +314,8 @@ class CspHtmlWebpackPlugin {
312314
processCsp(htmlPluginData, compileCb) {
313315
const $ = cheerio.load(htmlPluginData.html, {
314316
decodeEntities: false,
317+
_useHtmlParser2: true,
318+
xmlMode: get(htmlPluginData, 'plugin.options.xhtml', false),
315319
});
316320

317321
// if not enabled, remove the empty tag
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html lang="en-US">
3+
<head>
4+
<meta name="author" content="Slack" />
5+
<meta http-equiv="Content-Security-Policy" />
6+
<title>Slack CSP HTML Webpack Plugin Tests</title>
7+
</head>
8+
<body>
9+
&lt;h1&gt;Escaped Content&lt;h1&gt;
10+
</body>
11+
</html>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!DOCTYPE html>
2+
<html lang="en-US">
3+
<head>
4+
<meta name="author" content="Slack" />
5+
<meta http-equiv="Content-Security-Policy" />
6+
<title>Slack CSP HTML Webpack Plugin Tests</title>
7+
<noscript>
8+
<style>
9+
body {
10+
background-color: red;
11+
}
12+
</style>
13+
</noscript>
14+
</head>
15+
<body>
16+
Body
17+
</body>
18+
</html>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2+
<html lang="en-US">
3+
<head>
4+
<meta name="author" content="Slack" />
5+
<meta http-equiv="Content-Security-Policy" content="" />
6+
<title>Slack CSP HTML Webpack Plugin Tests</title>
7+
</head>
8+
<body>
9+
Body
10+
</body>
11+
</html>

0 commit comments

Comments
 (0)