Skip to content

Commit 8a2e118

Browse files
authored
Merge pull request #204 from g-k/add-escape-json
Add escape json
2 parents 6319080 + be57205 commit 8a2e118

File tree

4 files changed

+138
-0
lines changed

4 files changed

+138
-0
lines changed

API.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* [Escaping Characters](#escaping-characters "Escaping Characters")
2727
* [escapeHtml](#escapehtmlstring "escapeHtml")
2828
* [escapeHeaderAttribute](#escapeheaderattributeattribute "escapeHeaderAttribute")
29+
* [escapeJson](#escapejsonstring "escapeJson")
2930
* [escapeRegex](#escaperegexstring "escapeRegex")
3031
* [Errors](#errors "Errors")
3132
* [assert](#assertcondition-message "assert")
@@ -412,6 +413,15 @@ Escape attribute value for use in HTTP header
412413
var a = Hoek.escapeHeaderAttribute('I said "go w\\o me"'); //returns I said \"go w\\o me\"
413414
```
414415

416+
### escapeJson(string)
417+
418+
Unicode escapes the characters `<`, `>`, and `&` to prevent mime-sniffing older browsers mistaking JSON as HTML, and escapes line and paragraph separators for JSONP and script contexts.
419+
420+
```javascript
421+
422+
var lineSeparator = String.fromCharCode(0x2028);
423+
var a = Hoek.escapeJson('I said <script>confirm(&).' + lineSeparator); //returns I said \\u003cscript\\u003econfirm(\\u0026).\\u2028
424+
```
415425

416426
### escapeRegex(string)
417427

lib/escape.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,40 @@ exports.escapeHtml = function (input) {
5353
};
5454

5555

56+
exports.escapeJson = function (input) {
57+
58+
if (!input) {
59+
return '';
60+
}
61+
62+
const lessThan = 0x3C;
63+
const greaterThan = 0x3E;
64+
const andSymbol = 0x26;
65+
const lineSeperator = 0x2028;
66+
67+
// replace method
68+
let charCode;
69+
return input.replace(/[<>&\u2028\u2029]/g, (match) => {
70+
71+
charCode = match.charCodeAt(0);
72+
73+
if (charCode === lessThan) {
74+
return '\\u003c';
75+
}
76+
else if (charCode === greaterThan) {
77+
return '\\u003e';
78+
}
79+
else if (charCode === andSymbol) {
80+
return '\\u0026';
81+
}
82+
else if (charCode === lineSeperator) {
83+
return '\\u2028';
84+
}
85+
return '\\u2029';
86+
});
87+
};
88+
89+
5690
internals.escapeJavaScriptChar = function (charCode) {
5791

5892
if (charCode >= 256) {

lib/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,10 @@ exports.escapeJavaScript = function (string) {
843843
return Escape.escapeJavaScript(string);
844844
};
845845

846+
exports.escapeJson = function (string) {
847+
848+
return Escape.escapeJson(string);
849+
};
846850

847851
exports.nextTick = function (callback) {
848852

test/escaper.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,93 @@ describe('escapeHtml()', () => {
8888
done();
8989
});
9090
});
91+
92+
describe('escapeJson()', () => {
93+
94+
it('encodes < and > as unicode escaped equivalents', (done) => {
95+
96+
const encoded = Hoek.escapeJson('<script><>');
97+
expect(encoded).to.equal('\\u003cscript\\u003e\\u003c\\u003e');
98+
done();
99+
});
100+
101+
it('doesn\'t encode \0 as hex escaped equivalent', (done) => {
102+
103+
const encoded = Hoek.escapeJson('\0');
104+
expect(encoded).to.equal('\0');
105+
done();
106+
});
107+
108+
it('encodes & (ampersand) as unicode escaped equivalent', (done) => {
109+
110+
const encoded = Hoek.escapeJson('&&');
111+
expect(encoded).to.equal('\\u0026\\u0026');
112+
done();
113+
});
114+
115+
it('encodes line seperator as unicode escaped equivalent', (done) => {
116+
117+
const lineSeparator = String.fromCharCode(0x2028);
118+
const encoded = Hoek.escapeJson(lineSeparator);
119+
expect(encoded).to.equal('\\u2028');
120+
done();
121+
});
122+
123+
it('encodes paragraph seperator as unicode escaped equivalent', (done) => {
124+
125+
const paragraphSeparator = String.fromCharCode(0x2029);
126+
const encoded = Hoek.escapeJson(paragraphSeparator);
127+
expect(encoded).to.equal('\\u2029');
128+
done();
129+
});
130+
131+
it('doesn\'t encode U+13F0 Cherokee Letter Ye as unicode escaped equivalent', (done) => {
132+
133+
const encoded = Hoek.escapeJson('Ᏸ');
134+
expect(encoded).to.equal('Ᏸ');
135+
done();
136+
});
137+
138+
it('doesn\'t encode U+1F4A9 PILE OF POO as unicode escaped equivalent', (done) => {
139+
140+
const encoded = Hoek.escapeJson('💩');
141+
expect(encoded).to.equal('💩');
142+
done();
143+
});
144+
145+
it('doesn\'t encode U+1D306 TETRAGRAM FOR CENTRE as unicode escaped equivalent', (done) => {
146+
147+
const encoded = Hoek.escapeJson('𝌆');
148+
expect(encoded).to.equal('𝌆');
149+
done();
150+
});
151+
152+
it('doesn\'t encode \\ (backslash)', (done) => {
153+
154+
const encoded = Hoek.escapeJson('\\');
155+
expect(encoded).to.equal('\\');
156+
done();
157+
});
158+
159+
it('doesn\'t throw an exception when passed null', (done) => {
160+
161+
const encoded = Hoek.escapeJson(null);
162+
expect(encoded).to.equal('');
163+
done();
164+
});
165+
166+
it('doesn\'t encode {} characters', (done) => {
167+
168+
const encoded = Hoek.escapeJson('{}');
169+
expect(encoded).to.equal('{}');
170+
done();
171+
});
172+
173+
it('doesn\'t encode / (slash) character', (done) => {
174+
175+
const encoded = Hoek.escapeJson('<script>alert(1)</script>');
176+
expect(encoded).to.equal('\\u003cscript\\u003ealert(1)\\u003c/script\\u003e');
177+
done();
178+
});
179+
180+
});

0 commit comments

Comments
 (0)