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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanitize-html",
3
- "version": "1.19.0",
3
+ "version": "1.19.1",
4
4
  "description": "Clean up user-submitted HTML, preserving whitelisted elements and whitelisted attributes on a per-element basis",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
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
- return s.replace(/&(?![a-zA-Z0-9#]{1,7};)/g, '&') // Match ampersands not part of existing HTML entity
419
+ if (options.parser.decodeEntities) {
420
+ s = s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\>/g, '&gt;');
421
+ if (quote) {
422
+ s = s.replace(/\"/g, '&quot;');
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, '&amp;') // Match ampersands not part of existing HTML entity
420
431
  .replace(/</g, '&lt;')
421
- .replace(/\>/g, '&gt;')
422
- .replace(/\"/g, '&quot;');
432
+ .replace(/\>/g, '&gt;');
433
+ if (quote) {
434
+ s = s.replace(/\"/g, '&quot;');
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&quot;to&lt;be&gt;filtered</a>');
144
+ }}), '<a href="http://somelink">some_text_need"to&lt;be&gt;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
- '&lt;img src=&quot;javascript:evil&quot;/&gt;'
425
+ '&lt;img src="javascript:evil"/&gt;'
426
426
  );
427
427
  assert.equal(
428
428
  sanitizeHtml('<<a href="javascript:evil"/>a href="javascript:evil"/>'
429
429
  ),
430
- '&lt;<a>a href=&quot;javascript:evil&quot;/&gt;</a>'
430
+ '&lt;<a>a href="javascript:evil"/&gt;</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
- }), '&quot;normal text&quot;<script>"this is code"</script>'
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
- }), '&quot;normal text&quot;<style>body { background-image: url("image.test"); }</style>'
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
- }), '&quot;normal text&quot;'
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('"< & >"'), '&quot;&lt; &amp; &gt;&quot;');
788
+ it('Should encode &, <, > and where necessary, "', function() {
789
+ assert.equal(sanitizeHtml('"< & >" <span class="&#34;test&#34;">cool</span>', {
790
+ allowedTags: [ 'span' ],
791
+ allowedAttributes: {
792
+ span: [ 'class' ]
793
+ }
794
+ }), '"&lt; &amp; &gt;" <span class="&quot;test&quot;">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="&lt;0&amp;0;0.2&amp;" />');
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 &amp; & that &reg; &#x0000A; &#10; &plusmn; OK?';
793
801
  var expectedResult = 'This &amp; &amp; that &reg; &#x0000A; &#10; &plusmn; 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="&lt;0&amp;0;0.2&amp;" />');
821
+ // });
801
822
  });