diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index aa7b7ef68..04fb6b3a8 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -36,7 +36,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT
- name: Create the release
if: steps.changelog.outputs.changelog_content != ''
- uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
+ uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
name: ${{ github.ref_name }}
body: '${{ steps.changelog.outputs.changelog_content }}'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7abcd6f8..aee09ae08 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
This project adheres to [Semantic Versioning](https://semver.org/).
+## 8.5.10
+
+- Fixed XSS via unescaped `` in non-bundler cases (by @TharVid).
+
## 8.5.9
- Speed up source map encoding paring in case of the error.
diff --git a/lib/lazy-result.js b/lib/lazy-result.js
index 1ea52b87a..085ff228a 100644
--- a/lib/lazy-result.js
+++ b/lib/lazy-result.js
@@ -281,6 +281,13 @@ class LazyResult {
}
}
this.hasListener = Object.keys(this.listeners).length > 0
+ this.hasKeyListeners = false
+ for (let key in this.listeners) {
+ if (key.includes('-')) {
+ this.hasKeyListeners = true
+ break
+ }
+ }
}
async runAsync() {
@@ -517,19 +524,51 @@ class LazyResult {
walkSync(node) {
node[isClean] = true
- let events = getEvents(node)
- for (let event of events) {
- if (event === CHILDREN) {
- if (node.nodes) {
- node.each(child => {
- if (!child[isClean]) this.walkSync(child)
- })
- }
- } else {
- let visitors = this.listeners[event]
- if (visitors) {
- if (this.visitSync(visitors, node.toProxy())) return
- }
+ let type = TYPE_TO_CLASS_NAME[node.type]
+ let listeners = this.listeners
+ let proxy
+ let visitors
+ let key
+
+ if (this.hasKeyListeners) {
+ if (node.type === 'decl') {
+ key = node.prop.toLowerCase()
+ } else if (node.type === 'atrule') {
+ key = node.name.toLowerCase()
+ }
+ }
+
+ visitors = listeners[type]
+ if (visitors) {
+ proxy = node.toProxy()
+ if (this.visitSync(visitors, proxy)) return
+ }
+
+ if (key) {
+ visitors = listeners[type + '-' + key]
+ if (visitors) {
+ if (!proxy) proxy = node.toProxy()
+ if (this.visitSync(visitors, proxy)) return
+ }
+ }
+
+ if (node.nodes) {
+ node.each(child => {
+ if (!child[isClean]) this.walkSync(child)
+ })
+ }
+
+ visitors = listeners[type + 'Exit']
+ if (visitors) {
+ if (!proxy) proxy = node.toProxy()
+ if (this.visitSync(visitors, proxy)) return
+ }
+
+ if (key) {
+ visitors = listeners[type + 'Exit-' + key]
+ if (visitors) {
+ if (!proxy) proxy = node.toProxy()
+ this.visitSync(visitors, proxy)
}
}
}
diff --git a/lib/processor.js b/lib/processor.js
index 2afe2ef0a..5eda6c410 100644
--- a/lib/processor.js
+++ b/lib/processor.js
@@ -7,7 +7,7 @@ let Root = require('./root')
class Processor {
constructor(plugins = []) {
- this.version = '8.5.9'
+ this.version = '8.5.10'
this.plugins = this.normalize(plugins)
}
diff --git a/lib/stringifier.js b/lib/stringifier.js
index e07ad12e7..012fa622d 100644
--- a/lib/stringifier.js
+++ b/lib/stringifier.js
@@ -1,5 +1,17 @@
'use strict'
+// Escapes sequences that could break out of an HTML {
+ let root = new Root()
+ root.append(new Rule({ selector: '' }))
+ root.append(new AtRule({ name: 'media', params: '