Escape HTML by default (#2747)

* feat: escape HTML by default

* feat(test): add escaping test
This commit is contained in:
Nate Moore 2022-03-09 17:02:25 -06:00 committed by GitHub
parent 658a92915d
commit 05b66bd68b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 35 additions and 26 deletions

View file

@ -0,0 +1,5 @@
---
'astro': minor
---
Escape HTML inside of expressions by default. Please see [our migration guide](https://docs.astro.build/en/migrate/#deprecated-unescaped-html) for more details.

View file

@ -58,7 +58,7 @@
"test:match": "mocha --timeout 20000 -g"
},
"dependencies": {
"@astrojs/compiler": "^0.12.0-next.8",
"@astrojs/compiler": "^0.12.0-next.9",
"@astrojs/language-server": "^0.8.6",
"@astrojs/markdown-remark": "^0.6.4",
"@astrojs/prism": "0.4.0",
@ -95,10 +95,10 @@
"resolve": "^1.20.0",
"rollup": "^2.64.0",
"semver": "^7.3.5",
"sirv": "^2.0.2",
"serialize-javascript": "^6.0.0",
"shiki": "^0.10.0",
"shorthash": "^0.0.2",
"sirv": "^2.0.2",
"slash": "^4.0.0",
"sourcemap-codec": "^1.4.8",
"srcset-parse": "^1.1.0",

View file

@ -1,23 +1,6 @@
const entities = { '"': 'quot', '&': 'amp', "'": 'apos', '<': 'lt', '>': 'gt' } as const;
const warned = new Set<string>();
export const escapeHTML = (string: any, { deprecated = false }: { deprecated?: boolean } = {}) => {
const escaped = string.replace(/["'&<>]/g, (char: keyof typeof entities) => '&' + entities[char] + ';');
if (!deprecated) return escaped;
if (warned.has(string) || !string.match(/[&<>]/g)) return string;
// eslint-disable-next-line no-console
console.warn(`Unescaped HTML content found inside expression!
The next minor version of Astro will automatically escape all
expression content. Please use the \`set:html\` directive.
Expression content:
${string}`);
warned.add(string);
// Return unescaped content for now. To be removed.
return string;
};
export const escapeHTML = (string: any) => string.replace(/["'&<>]/g, (char: keyof typeof entities) => '&' + entities[char] + ';');
/**
* RawString is a "blessed" version of String

View file

@ -8,7 +8,7 @@ import { escapeHTML, UnescapedString, unescapeHTML } from './escape.js';
export type { Metadata } from './metadata';
export { createMetadata } from './metadata.js';
export { escapeHTML, unescapeHTML } from './escape.js';
export { unescapeHTML } from './escape.js';
const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
const htmlBooleanAttributes =
@ -36,7 +36,7 @@ async function _render(child: any): Promise<any> {
// of wrapping it in a function and calling it.
return _render(child());
} else if (typeof child === 'string') {
return escapeHTML(child, { deprecated: true });
return escapeHTML(child);
} else if (!child && child !== 0) {
// do nothing, safe to ignore falsey values.
}

View file

@ -99,4 +99,13 @@ describe('Expressions', () => {
// test 9: Expected {undefined && <span id="undefined" />} not to render
expect($('#frag-undefined')).to.have.lengthOf(0);
});
it('Escapes HTML by default', async () => {
const html = await fixture.readFile('/escape/index.html');
const $ = cheerio.load(html);
expect($('body').children()).to.have.lengthOf(1);
expect($('body').text()).to.include('&lt;script&gt;console.log(&quot;pwnd&quot;)&lt;/script&gt;')
expect($('#trusted')).to.have.lengthOf(1);
});
});

View file

@ -0,0 +1,9 @@
<html lang="en">
<head>
<title>My site</title>
</head>
<body>
{'<script>console.log("pwnd")</script>'}
<Fragment set:html={'<script id="trusted">console.log("yay!")</script>'} />
</body>
</html>

View file

@ -340,7 +340,7 @@ importers:
packages/astro:
specifiers:
'@astrojs/compiler': ^0.12.0-next.8
'@astrojs/compiler': ^0.12.0-next.9
'@astrojs/language-server': ^0.8.6
'@astrojs/markdown-remark': ^0.6.4
'@astrojs/parser': ^0.22.1
@ -414,7 +414,7 @@ importers:
yargs-parser: ^21.0.0
zod: ^3.8.1
dependencies:
'@astrojs/compiler': 0.12.0-next.8
'@astrojs/compiler': 0.12.0-next.9
'@astrojs/language-server': 0.8.10
'@astrojs/markdown-remark': link:../markdown/remark
'@astrojs/prism': link:../astro-prism
@ -1328,10 +1328,12 @@ packages:
leven: 3.1.0
dev: true
/@astrojs/compiler/0.12.0-next.8:
resolution: {integrity: sha512-HeREaw5OR5J7zML+/LxhrqUr57571kyNXL4HD2pU929oevhx3PQ37PQ0FkD5N65X9YfO+gcoEO6whl76vtSZag==}
/@astrojs/compiler/0.12.0-next.9:
resolution: {integrity: sha512-XHvGrPBhr/LBYZT4TuXf8mK2+CYCClQoPln9FlF6gIx4yTsWpUwAi3mhhFOGJi4NB1Y1ZbcXS60IjMy/2adpLg==}
dependencies:
tsm: 2.2.1
typescript: 4.6.2
uvu: 0.5.3
dev: false
/@astrojs/language-server/0.8.10:
@ -8788,6 +8790,7 @@ packages:
/source-map/0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
requiresBuild: true
/source-map/0.7.3:
resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==}