Skip to content

Commit f8605d6

Browse files
authored
[fix] harden attribute escaping during ssr (sveltejs#7530)
1 parent 9635a2e commit f8605d6

File tree

3 files changed

+23
-10
lines changed

3 files changed

+23
-10
lines changed

src/runtime/internal/ssr.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,20 @@ export function escape(value: unknown, is_attr = false) {
8585
let escaped = '';
8686
let last = 0;
8787

88-
while (pattern.test(str)) {
89-
const i = pattern.lastIndex - 1;
90-
const ch = str[i];
91-
escaped += str.substring(last, i) + (ch === '&' ? '&' : (ch === '"' ? '"' : '<'));
92-
last = i + 1;
93-
}
88+
while (pattern.test(str)) {
89+
const i = pattern.lastIndex - 1;
90+
const ch = str[i];
91+
escaped += str.substring(last, i) + (ch === '&' ? '&' : (ch === '"' ? '"' : '<'));
92+
last = i + 1;
93+
}
9494

9595
return escaped + str.substring(last);
9696
}
9797

9898
export function escape_attribute_value(value) {
99-
return typeof value === 'string' ? escape(value, true) : value;
99+
// keep booleans, null, and undefined for the sake of `spread`
100+
const should_escape = typeof value === 'string' || (value && typeof value === 'object');
101+
return should_escape ? escape(value, true) : value;
100102
}
101103

102104
export function escape_object(obj) {
@@ -192,7 +194,7 @@ export function create_ssr_component(fn) {
192194

193195
export function add_attribute(name, value, boolean) {
194196
if (value == null || (boolean && !value)) return '';
195-
const assignment = (boolean && value === true) ? '' : `="${escape_attribute_value(value.toString())}"`;
197+
const assignment = (boolean && value === true) ? '' : `="${escape(value, true)}"`;
196198
return ` ${name}${assignment}`;
197199
}
198200

test/server-side-rendering/samples/attribute-escaped-quotes-spread/_expected.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,10 @@
22
bar="'></div><script>alert(42)</script>"
33
foo="&quot;></div><script>alert(42)</script>"
44
qux="&amp;&amp;&amp;"
5-
></div>
5+
quux="&quot;><script>alert(42)</script>"
6+
></div>
7+
8+
<div
9+
foo="foo"
10+
unsafe="&quot;><script>alert(42)</script>"
11+
></div>
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
<script>
2+
const safe = { foo: 'foo' };
3+
const unsafe = { toString: () => '"><script>alert(42)<\/script>' };
4+
25
export let props = {
36
foo: '"></div><script>alert(42)</' + 'script>',
47
bar: "'></div><script>alert(42)</" + 'script>',
58
['"></div><script>alert(42)</' + 'script>']: 'baz',
69
qux: '&&&',
10+
quux: unsafe
711
};
812
</script>
913

10-
<div {...props}></div>
14+
<div {...props}></div>
15+
<div {...safe} {unsafe}></div>

0 commit comments

Comments
 (0)