| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- import { DefaultAllowlist, sanitizeHtml } from '../../../src/util/sanitizer.js'
- describe('Sanitizer', () => {
- describe('sanitizeHtml', () => {
- it('should return the same on empty string', () => {
- const empty = ''
- const result = sanitizeHtml(empty, DefaultAllowlist, null)
- expect(result).toEqual(empty)
- })
- it('should retain tags with valid URLs', () => {
- const validUrls = [
- '',
- 'http://abc',
- 'HTTP://abc',
- 'https://abc',
- 'HTTPS://abc',
- 'ftp://abc',
- 'FTP://abc',
- 'mailto:me@example.com',
- 'MAILTO:me@example.com',
- 'tel:123-123-1234',
- 'TEL:123-123-1234',
- 'sip:me@example.com',
- 'SIP:me@example.com',
- '#anchor',
- '/page1.md',
- 'http://JavaScript/my.js',
- 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/', // Truncated.
- 'data:video/webm;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
- 'data:audio/opus;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
- 'unknown-scheme:abc'
- ]
- for (const url of validUrls) {
- const template = [
- '<div>',
- ` <a href="${url}">Click me</a>`,
- ' <span>Some content</span>',
- '</div>'
- ].join('')
- const result = sanitizeHtml(template, DefaultAllowlist, null)
- expect(result).toContain(`href="${url}"`)
- }
- })
- it('should sanitize template by removing tags with XSS', () => {
- const invalidUrls = [
- // eslint-disable-next-line no-script-url
- 'javascript:alert(7)',
- // eslint-disable-next-line no-script-url
- 'javascript:evil()',
- // eslint-disable-next-line no-script-url
- 'JavaScript:abc',
- ' javascript:abc',
- ' \n Java\n Script:abc',
- 'javascript:',
- 'javascript:',
- 'j avascript:',
- 'javascript:',
- 'javascript:',
- 'jav	ascript:alert();',
- 'jav\u0000ascript:alert();'
- ]
- for (const url of invalidUrls) {
- const template = [
- '<div>',
- ` <a href="${url}">Click me</a>`,
- ' <span>Some content</span>',
- '</div>'
- ].join('')
- const result = sanitizeHtml(template, DefaultAllowlist, null)
- expect(result).not.toContain(`href="${url}"`)
- }
- })
- it('should sanitize template and work with multiple regex', () => {
- const template = [
- '<div>',
- ' <a href="javascript:alert(7)" aria-label="This is a link" data-foo="bar">Click me</a>',
- ' <span>Some content</span>',
- '</div>'
- ].join('')
- const myDefaultAllowList = DefaultAllowlist
- // With the default allow list
- let result = sanitizeHtml(template, myDefaultAllowList, null)
- // `data-foo` won't be present
- expect(result).not.toContain('data-foo="bar"')
- // Add the following regex too
- myDefaultAllowList['*'].push(/^data-foo/)
- result = sanitizeHtml(template, myDefaultAllowList, null)
- expect(result).not.toContain('href="javascript:alert(7)') // This is in the default list
- expect(result).toContain('aria-label="This is a link"') // This is in the default list
- expect(result).toContain('data-foo="bar"') // We explicitly allow this
- })
- it('should allow aria attributes and safe attributes', () => {
- const template = [
- '<div aria-pressed="true">',
- ' <span class="test">Some content</span>',
- '</div>'
- ].join('')
- const result = sanitizeHtml(template, DefaultAllowlist, null)
- expect(result).toContain('aria-pressed')
- expect(result).toContain('class="test"')
- })
- it('should remove tags not in allowlist', () => {
- const template = [
- '<div>',
- ' <script>alert(7)</script>',
- '</div>'
- ].join('')
- const result = sanitizeHtml(template, DefaultAllowlist, null)
- expect(result).not.toContain('<script>')
- })
- it('should not use native api to sanitize if a custom function passed', () => {
- const template = [
- '<div>',
- ' <span>Some content</span>',
- '</div>'
- ].join('')
- function mySanitize(htmlUnsafe) {
- return htmlUnsafe
- }
- const spy = spyOn(DOMParser.prototype, 'parseFromString')
- const result = sanitizeHtml(template, DefaultAllowlist, mySanitize)
- expect(result).toEqual(template)
- expect(spy).not.toHaveBeenCalled()
- })
- it('should allow multiple sanitation passes of the same template', () => {
- const template = '<img src="test.jpg">'
- const firstResult = sanitizeHtml(template, DefaultAllowlist, null)
- const secondResult = sanitizeHtml(template, DefaultAllowlist, null)
- expect(firstResult).toContain('src')
- expect(secondResult).toContain('src')
- })
- })
- })
|