From 05b66bd68b173d30921c9f0565b3dc2379039fcd Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 9 Mar 2022 17:02:25 -0600 Subject: [PATCH] Escape HTML by default (#2747) * feat: escape HTML by default * feat(test): add escaping test --- .changeset/sour-socks-enjoy.md | 5 +++++ packages/astro/package.json | 4 ++-- packages/astro/src/runtime/server/escape.ts | 19 +------------------ packages/astro/src/runtime/server/index.ts | 4 ++-- packages/astro/test/astro-expr.test.js | 9 +++++++++ .../astro-expr/src/pages/escape.astro | 9 +++++++++ pnpm-lock.yaml | 11 +++++++---- 7 files changed, 35 insertions(+), 26 deletions(-) create mode 100644 .changeset/sour-socks-enjoy.md create mode 100644 packages/astro/test/fixtures/astro-expr/src/pages/escape.astro diff --git a/.changeset/sour-socks-enjoy.md b/.changeset/sour-socks-enjoy.md new file mode 100644 index 000000000..aa9b56485 --- /dev/null +++ b/.changeset/sour-socks-enjoy.md @@ -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. diff --git a/packages/astro/package.json b/packages/astro/package.json index 69818591e..d3dea5a39 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -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", diff --git a/packages/astro/src/runtime/server/escape.ts b/packages/astro/src/runtime/server/escape.ts index a0482fdf2..6c6eb4ff6 100644 --- a/packages/astro/src/runtime/server/escape.ts +++ b/packages/astro/src/runtime/server/escape.ts @@ -1,23 +1,6 @@ const entities = { '"': 'quot', '&': 'amp', "'": 'apos', '<': 'lt', '>': 'gt' } as const; -const warned = new Set(); -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 diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts index 19affdb0b..d2a022805 100644 --- a/packages/astro/src/runtime/server/index.ts +++ b/packages/astro/src/runtime/server/index.ts @@ -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 { // 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. } diff --git a/packages/astro/test/astro-expr.test.js b/packages/astro/test/astro-expr.test.js index e08323f86..e996062b5 100644 --- a/packages/astro/test/astro-expr.test.js +++ b/packages/astro/test/astro-expr.test.js @@ -99,4 +99,13 @@ describe('Expressions', () => { // test 9: Expected {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('<script>console.log("pwnd")</script>') + expect($('#trusted')).to.have.lengthOf(1); + }); }); diff --git a/packages/astro/test/fixtures/astro-expr/src/pages/escape.astro b/packages/astro/test/fixtures/astro-expr/src/pages/escape.astro new file mode 100644 index 000000000..8c16d7fd2 --- /dev/null +++ b/packages/astro/test/fixtures/astro-expr/src/pages/escape.astro @@ -0,0 +1,9 @@ + + + My site + + + {''} + console.log("yay!")'} /> + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f66275bda..b1f75c17c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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==}