Skip to content

Commit 52e3326

Browse files
authored
Merge pull request #2 from Rich-Harris/gh-1
guard against XSS
2 parents 5a5f92d + 171b1d2 commit 52e3326

File tree

2 files changed

+26
-12
lines changed

2 files changed

+26
-12
lines changed

src/index.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';
22
const reserved = /^(?:do|if|in|for|int|let|new|try|var|byte|case|char|else|enum|goto|long|this|void|with|await|break|catch|class|const|final|float|short|super|throw|while|yield|delete|double|export|import|native|return|switch|throws|typeof|boolean|default|extends|finally|package|private|abstract|continue|debugger|function|volatile|interface|protected|transient|implements|instanceof|synchronized)$/;
3-
4-
function getName(num: number) {
5-
let name = '';
6-
7-
do {
8-
name = chars[num % chars.length] + name;
9-
num = ~~(num / chars.length) - 1;
10-
} while (num >= 0);
11-
12-
return reserved.test(name) ? `${name}_` : name;
13-
}
3+
const unsafe = /[<>\/\u2028\u2029]/g;
4+
const escaped: Record<string, string> = { '<': '\\u003C', '>' : '\\u003E', '/': '\\u002F', '\u2028': '\\u2028', '\u2029': '\\u2029' };
145

156
export default function devalue(value: any) {
167
const repeated = new Map();
@@ -173,12 +164,27 @@ export default function devalue(value: any) {
173164
}
174165
}
175166

167+
function getName(num: number) {
168+
let name = '';
169+
170+
do {
171+
name = chars[num % chars.length] + name;
172+
num = ~~(num / chars.length) - 1;
173+
} while (num >= 0);
174+
175+
return reserved.test(name) ? `${name}_` : name;
176+
}
177+
176178
function isPrimitive(thing: any) {
177179
return Object(thing) !== thing;
178180
}
179181

182+
function escape(char: string) {
183+
return escaped[char];
184+
}
185+
180186
function stringifyPrimitive(thing: any) {
181-
if (typeof thing === 'string') return JSON.stringify(thing);
187+
if (typeof thing === 'string') return JSON.stringify(thing).replace(unsafe, escape);
182188
if (thing === void 0) return 'void 0';
183189
if (thing === 0 && 1 / thing < 0) return '-0';
184190
return String(thing);

test/test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ describe('devalue', () => {
6666
test('String (repetition)', [str, str], `(function(a){return [a,a]}("a string"))`);
6767
});
6868

69+
describe('XSS', () => {
70+
test(
71+
'Dangerous string',
72+
`</script><script src='https://evil.com/script.js'>alert('pwned')</script><script>`,
73+
`"\\u003C\\u002Fscript\\u003E\\u003Cscript src='https:\\u002F\\u002Fevil.com\\u002Fscript.js'\\u003Ealert('pwned')\\u003C\\u002Fscript\\u003E\\u003Cscript\\u003E"`
74+
);
75+
});
76+
6977
describe('misc', () => {
7078
test('Object without prototype', Object.create(null), 'Object.create(null)');
7179

0 commit comments

Comments
 (0)