From 246e942a3946cbb9cfd1871ddcafb3c660c92eaa Mon Sep 17 00:00:00 2001 From: Chris Zuber Date: Wed, 7 Jan 2026 08:17:29 -0800 Subject: [PATCH] Refactor to use @aegisjsproject/escape for HTML escaping Replaced local HTML escaping logic in dom.js with imports from @aegisjsproject/escape for improved consistency and maintainability. Updated dependencies in package.json and package-lock.json. Added addImageSrc to CSP config in http.config.js and updated Rollup external modules. --- CHANGELOG.md | 3 +++ dom.js | 18 +++--------------- http.config.js | 4 +++- package-lock.json | 42 ++++++++++++++++++++++++++++++++++-------- package.json | 2 ++ rollup.config.js | 1 + 6 files changed, 46 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a83e4df..2357776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- Use `@aegisjsproject/escape` for HTML escaping + ## [v0.2.30] - 2026-01-05 ### Added diff --git a/dom.js b/dom.js index fa00154..7f1e08d 100644 --- a/dom.js +++ b/dom.js @@ -1,17 +1,6 @@ import { attachListeners } from '@aegisjsproject/callback-registry/events.js'; +import { escapeHTML as escape, escapeAttrName, ATTR_NAME_UNSAFE_PATTERN, HTML_REPLACEMENTS } from '@aegisjsproject/escape/html.js'; export const HTML_UNSAFE_PATTERN = /[<>"']|&(?![a-zA-Z\d]{2,5};|#\d{1,3};)/g; -/*eslint no-control-regex: "off"*/ -export const ATTR_NAME_UNSAFE_PATTERN = /[\u0000-\u001f\u007f-\u009f\s"'\\/=><&]/g; -export const HTML_REPLACEMENTS = Object.freeze({ - '&': '&', - '"': '"', - '\'': ''', - '<': '<', - '>': '>', -}); - -export const escape = str => (str?.toString?.() ?? '') - .replaceAll(HTML_UNSAFE_PATTERN, char => HTML_REPLACEMENTS[char]); /** * @@ -23,9 +12,6 @@ export const escapeAttrVal = str => { return escape(str); }; -export const escapeAttrName = str => (str?.toString?.() ?? '') - .replace(ATTR_NAME_UNSAFE_PATTERN, char => '_' + char.charCodeAt(0).toString(16).padStart(4, '0') + '_'); - export function createAttribute(name, value = '', namespace) { const attr = typeof namespace === 'string' ? document.createAttributeNS(namespace, name) @@ -124,3 +110,5 @@ export function replace(target, ...items) { attachListeners(target instanceof ShadowRoot ? target.host : target); } } + +export { escapeAttrName, escape, ATTR_NAME_UNSAFE_PATTERN, HTML_REPLACEMENTS }; diff --git a/http.config.js b/http.config.js index cf85a6f..323bdd8 100644 --- a/http.config.js +++ b/http.config.js @@ -1,5 +1,7 @@ -import { useDefaultCSP, addScriptSrc, addConnectSrc, addTrustedTypePolicy, lockCSP } from '@aegisjsproject/http-utils/csp.js'; +import { useDefaultCSP, addScriptSrc, addImageSrc, addConnectSrc, addTrustedTypePolicy, lockCSP } from '@aegisjsproject/http-utils/csp.js'; + addScriptSrc('https://unpkg.com/@shgysk8zer0/', 'https://unpkg.com/@aegisjsproject/'); +addImageSrc('blob:'); addConnectSrc('https://icanhazdadjoke.com/'); addTrustedTypePolicy('aegis-router#html', 'aegis-sanitizer#html', 'default'); lockCSP(); diff --git a/package-lock.json b/package-lock.json index 9bd423a..bbb47d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "license": "MIT", "dependencies": { "@aegisjsproject/callback-registry": "^1.0.3", + "@aegisjsproject/escape": "^1.0.4", "@aegisjsproject/parsers": "^0.1.4", "@aegisjsproject/router": "^1.1.3", "@aegisjsproject/sanitizer": "^0.2.4", @@ -34,6 +35,7 @@ "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-terser": "^0.4.4", "@shgysk8zer0/eslint-config": "^1.0.4", + "@shgysk8zer0/importmap": "^1.7.4", "@shgysk8zer0/polyfills": "^0.5.3", "eslint": "^9.0.0", "rollup": "^4.9.6" @@ -194,6 +196,25 @@ "node": ">=24.10.0" } }, + "node_modules/@aegisjsproject/escape": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@aegisjsproject/escape/-/escape-1.0.4.tgz", + "integrity": "sha512-xC5F2gVrqsJABEhrQQM1NHVfqLF92McuYDZLVdHBkJz5lo2jaHoPYVIK+x4T7irx07wG/l72ZYlS2/Lf4RvDKw==", + "funding": [ + { + "type": "librepay", + "url": "https://liberapay.com/shgysk8zer0" + }, + { + "type": "github", + "url": "https://github.com/sponsors/shgysk8zer0" + } + ], + "license": "MIT", + "engines": { + "node": ">=24.10.0" + } + }, "node_modules/@aegisjsproject/http-utils": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@aegisjsproject/http-utils/-/http-utils-1.0.4.tgz", @@ -1119,14 +1140,14 @@ } }, "node_modules/@shgysk8zer0/importmap": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@shgysk8zer0/importmap/-/importmap-1.5.1.tgz", - "integrity": "sha512-sxHyY2THBsN3i/TZOXYqkMuUErESvg9KlY+LirX0tuD+JG6Cfq+AU3EJ2vNcwnXc5DdpGEoQ41u+OkdXxq2i7Q==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@shgysk8zer0/importmap/-/importmap-1.7.4.tgz", + "integrity": "sha512-7gh7VdCU2OradMRs5Y+/zjqkDAjV/wMUpYDZv560hUACDGfHh9dx2IsEyHfqd4sumisNa1oy2QvyAN3quO43fw==", "dev": true, "license": "MIT", "dependencies": { "@shgysk8zer0/npm-utils": "^1.1.3", - "@shgysk8zer0/polyfills": "^0.5.1", + "@shgysk8zer0/polyfills": "^0.5.6", "commander": "^14.0.2" }, "bin": { @@ -2334,6 +2355,11 @@ "@shgysk8zer0/importmap": "^1.5.1" } }, + "@aegisjsproject/escape": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@aegisjsproject/escape/-/escape-1.0.4.tgz", + "integrity": "sha512-xC5F2gVrqsJABEhrQQM1NHVfqLF92McuYDZLVdHBkJz5lo2jaHoPYVIK+x4T7irx07wG/l72ZYlS2/Lf4RvDKw==" + }, "@aegisjsproject/http-utils": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@aegisjsproject/http-utils/-/http-utils-1.0.4.tgz", @@ -2814,13 +2840,13 @@ } }, "@shgysk8zer0/importmap": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@shgysk8zer0/importmap/-/importmap-1.5.1.tgz", - "integrity": "sha512-sxHyY2THBsN3i/TZOXYqkMuUErESvg9KlY+LirX0tuD+JG6Cfq+AU3EJ2vNcwnXc5DdpGEoQ41u+OkdXxq2i7Q==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@shgysk8zer0/importmap/-/importmap-1.7.4.tgz", + "integrity": "sha512-7gh7VdCU2OradMRs5Y+/zjqkDAjV/wMUpYDZv560hUACDGfHh9dx2IsEyHfqd4sumisNa1oy2QvyAN3quO43fw==", "dev": true, "requires": { "@shgysk8zer0/npm-utils": "^1.1.3", - "@shgysk8zer0/polyfills": "^0.5.1", + "@shgysk8zer0/polyfills": "^0.5.6", "commander": "^14.0.2" }, "dependencies": { diff --git a/package.json b/package.json index 2c2981f..3680213 100644 --- a/package.json +++ b/package.json @@ -98,12 +98,14 @@ "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-terser": "^0.4.4", "@shgysk8zer0/eslint-config": "^1.0.4", + "@shgysk8zer0/importmap": "^1.7.4", "@shgysk8zer0/polyfills": "^0.5.3", "eslint": "^9.0.0", "rollup": "^4.9.6" }, "dependencies": { "@aegisjsproject/callback-registry": "^1.0.3", + "@aegisjsproject/escape": "^1.0.4", "@aegisjsproject/parsers": "^0.1.4", "@aegisjsproject/router": "^1.1.3", "@aegisjsproject/sanitizer": "^0.2.4", diff --git a/rollup.config.js b/rollup.config.js index 8c9b28b..f1e4b25 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -26,6 +26,7 @@ export default [{ '@aegisjsproject/component/component.js', '@aegisjsproject/callback-registry/callbackRegistry.js', '@aegisjsproject/callback-registry/events.js', + '@aegisjsproject/escape/html.js', ] }, { input: 'core.js',