sanitize-html 1.19.0 → 1.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +170 -0
- package/README.md +4 -158
- package/dist/index.js +20 -5
- package/dist/sanitize-html.js +20 -5
- package/dist/sanitize-html.min.js +12 -12
- package/package.json +1 -1
- package/src/index.js +20 -6
- package/test/test.js +30 -9
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -315,7 +315,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
315
315
|
}
|
|
316
316
|
result += ' ' + a;
|
|
317
317
|
if (value.length) {
|
|
318
|
-
result += '="' + escapeHtml(value) + '"';
|
|
318
|
+
result += '="' + escapeHtml(value, true) + '"';
|
|
319
319
|
}
|
|
320
320
|
} else {
|
|
321
321
|
delete frame.attribs[a];
|
|
@@ -351,7 +351,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
351
351
|
// which have their own collection of XSS vectors.
|
|
352
352
|
result += text;
|
|
353
353
|
} else {
|
|
354
|
-
var escaped = escapeHtml(text);
|
|
354
|
+
var escaped = escapeHtml(text, false);
|
|
355
355
|
if (options.textFilter) {
|
|
356
356
|
result += options.textFilter(escaped);
|
|
357
357
|
} else {
|
|
@@ -412,14 +412,28 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
412
412
|
|
|
413
413
|
return result;
|
|
414
414
|
|
|
415
|
-
function escapeHtml(s) {
|
|
415
|
+
function escapeHtml(s, quote) {
|
|
416
416
|
if (typeof(s) !== 'string') {
|
|
417
417
|
s = s + '';
|
|
418
418
|
}
|
|
419
|
-
|
|
419
|
+
if (options.parser.decodeEntities) {
|
|
420
|
+
s = s.replace(/&/g, '&').replace(/</g, '<').replace(/\>/g, '>');
|
|
421
|
+
if (quote) {
|
|
422
|
+
s = s.replace(/\"/g, '"');
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// TODO: this is inadequate because it will pass `&0;`. This approach
|
|
426
|
+
// will not work, each & must be considered with regard to whether it
|
|
427
|
+
// is followed by a 100% syntactically valid entity or not, and escaped
|
|
428
|
+
// if it is not. If this bothers you, don't set parser.decodeEntities
|
|
429
|
+
// to false. (The default is true.)
|
|
430
|
+
s = s.replace(/&(?![a-zA-Z0-9#]{1,20};)/g, '&') // Match ampersands not part of existing HTML entity
|
|
420
431
|
.replace(/</g, '<')
|
|
421
|
-
.replace(/\>/g, '>')
|
|
422
|
-
|
|
432
|
+
.replace(/\>/g, '>');
|
|
433
|
+
if (quote) {
|
|
434
|
+
s = s.replace(/\"/g, '"');
|
|
435
|
+
}
|
|
436
|
+
return s;
|
|
423
437
|
}
|
|
424
438
|
|
|
425
439
|
function naughtyHref(name, href) {
|
package/test/test.js
CHANGED
|
@@ -141,7 +141,7 @@ describe('sanitizeHtml', function() {
|
|
|
141
141
|
}
|
|
142
142
|
}}, textFilter: function (text) {
|
|
143
143
|
return text.replace(/\s/g, '_');
|
|
144
|
-
}}), '<a href="http://somelink">some_text_need
|
|
144
|
+
}}), '<a href="http://somelink">some_text_need"to<be>filtered</a>');
|
|
145
145
|
});
|
|
146
146
|
|
|
147
147
|
it('should add new text when not initially set and replace attributes when they are changed by transforming function', function () {
|
|
@@ -422,12 +422,12 @@ describe('sanitizeHtml', function() {
|
|
|
422
422
|
assert.equal(
|
|
423
423
|
sanitizeHtml('<<img src="javascript:evil"/>img src="javascript:evil"/>'
|
|
424
424
|
),
|
|
425
|
-
'<img src
|
|
425
|
+
'<img src="javascript:evil"/>'
|
|
426
426
|
);
|
|
427
427
|
assert.equal(
|
|
428
428
|
sanitizeHtml('<<a href="javascript:evil"/>a href="javascript:evil"/>'
|
|
429
429
|
),
|
|
430
|
-
'<<a>a href
|
|
430
|
+
'<<a>a href="javascript:evil"/></a>'
|
|
431
431
|
);
|
|
432
432
|
});
|
|
433
433
|
it('should allow attributes to be specified as globs', function() {
|
|
@@ -456,12 +456,12 @@ describe('sanitizeHtml', function() {
|
|
|
456
456
|
assert.equal(
|
|
457
457
|
sanitizeHtml('<div>"normal text"</div><script>"this is code"</script>', {
|
|
458
458
|
allowedTags: [ 'script' ]
|
|
459
|
-
}), '
|
|
459
|
+
}), '"normal text"<script>"this is code"</script>'
|
|
460
460
|
);
|
|
461
461
|
assert.equal(
|
|
462
462
|
sanitizeHtml('<div>"normal text"</div><style>body { background-image: url("image.test"); }</style>', {
|
|
463
463
|
allowedTags: [ 'style' ]
|
|
464
|
-
}), '
|
|
464
|
+
}), '"normal text"<style>body { background-image: url("image.test"); }</style>'
|
|
465
465
|
);
|
|
466
466
|
});
|
|
467
467
|
it('should not unescape escapes found inside script tags', function() {
|
|
@@ -480,7 +480,7 @@ describe('sanitizeHtml', function() {
|
|
|
480
480
|
textFilter: function(text) {
|
|
481
481
|
return text.replace(' this should be removed', '');
|
|
482
482
|
}
|
|
483
|
-
}), '
|
|
483
|
+
}), '"normal text"'
|
|
484
484
|
);
|
|
485
485
|
});
|
|
486
486
|
it('should respect htmlparser2 options when passed in', function() {
|
|
@@ -785,10 +785,18 @@ describe('sanitizeHtml', function() {
|
|
|
785
785
|
allowedSchemes: sanitizeHtml.defaults.allowedSchemes.concat([ 'tel' ]),
|
|
786
786
|
}), '<q cite=\"http://www.google.com\">HTTP</q><q cite=\"https://www.google.com\">HTTPS</q><q cite=\"mailto://www.google.com\">MAILTO</q><q cite=\"tel://www.google.com\">TEL</q><q cite=\"ftp://www.google.com\">FTP</q><q>DATA</q><q>LDAP</q><q>ACROBAT</q><q>VBSCRIPT</q><q>FILE</q><q>RLOGIN</q><q>WEBCAL</q><q>JAVASCRIPT</q><q>MMS</q>');
|
|
787
787
|
});
|
|
788
|
-
it('Should encode &, <, > and "', function() {
|
|
789
|
-
assert.equal(sanitizeHtml('"< & >"
|
|
788
|
+
it('Should encode &, <, > and where necessary, "', function() {
|
|
789
|
+
assert.equal(sanitizeHtml('"< & >" <span class=""test"">cool</span>', {
|
|
790
|
+
allowedTags: [ 'span' ],
|
|
791
|
+
allowedAttributes: {
|
|
792
|
+
span: [ 'class' ]
|
|
793
|
+
}
|
|
794
|
+
}), '"< & >" <span class=""test"">cool</span>');
|
|
795
|
+
});
|
|
796
|
+
it('Should not pass through &0; unescaped if decodeEntities is true (the default)', function() {
|
|
797
|
+
assert.equal(sanitizeHtml('<img src="<0&0;0.2&" />', {allowedTags: ['img']}), '<img src="<0&0;0.2&" />');
|
|
790
798
|
});
|
|
791
|
-
it('Should not double encode ampersands on HTML entities', function() {
|
|
799
|
+
it('Should not double encode ampersands on HTML entities if decodeEntities is false (TODO more tests, this is too loose to rely upon)', function() {
|
|
792
800
|
var textIn = 'This & & that ® 
 ± OK?';
|
|
793
801
|
var expectedResult = 'This & & that ® 
 ± OK?';
|
|
794
802
|
var sanitizeHtmlOptions = {
|
|
@@ -798,4 +806,17 @@ describe('sanitizeHtml', function() {
|
|
|
798
806
|
};
|
|
799
807
|
assert.equal(sanitizeHtml(textIn, sanitizeHtmlOptions), expectedResult);
|
|
800
808
|
});
|
|
809
|
+
// TODO: make this test and similar tests for entities that are not
|
|
810
|
+
// strictly valid pass, at which point decodeEntities: false is safe
|
|
811
|
+
// to use.
|
|
812
|
+
//
|
|
813
|
+
// it('Should not pass through &0; (a bogus entity) unescaped if decodeEntities is false', function() {
|
|
814
|
+
// assert.equal(sanitizeHtml(
|
|
815
|
+
// '<img src="<0&0;0.2&" />', {
|
|
816
|
+
// allowedTags: ['img'],
|
|
817
|
+
// parser: {
|
|
818
|
+
// decodeEntities: false
|
|
819
|
+
// }
|
|
820
|
+
// }), '<img src="<0&0;0.2&" />');
|
|
821
|
+
// });
|
|
801
822
|
});
|