Files
shopdb-flask/plugins/guides/frontend/utils/inline.spec.js

51 lines
1.9 KiB
JavaScript

/**
* Inline rendering: the security model and the link bugs it hid.
*
* Escape-then-format is the whole defence, and it has one awkward consequence
* that produced real broken links: by the time a URL is captured it is already
* escaped, so it has to be decoded before it can be escaped again exactly once.
*/
import { describe, it, expect } from 'vitest'
import { renderInline } from './inline.js'
describe('renderInline', () => {
it('escapes markup an author pasted', () => {
expect(renderInline('<img src=x onerror=alert(1)>'))
.toBe('&lt;img src=x onerror=alert(1)&gt;')
})
it('keeps a multi-parameter URL intact', () => {
// Double-escaped, this became &amp;amp; and the link opened the wrong
// record. Every ServiceNow and SharePoint link carries several parameters.
const html = renderInline('[ticket](https://x.example/nav?a=1&b=2&c=3)')
expect(html).toContain('href="https://x.example/nav?a=1&amp;b=2&amp;c=3"')
expect(html).not.toContain('&amp;amp;')
})
it('does not let emphasis reach inside an href', () => {
const html = renderInline('[t](https://x.example/a*b*c)')
expect(html).toContain('href="https://x.example/a*b*c"')
expect(html).not.toContain('<em>')
})
it('refuses a protocol-relative URL dressed as an in-app path', () => {
// '//evil.example' starts with '/' but the browser reads it as off-site.
expect(renderInline('[evil](//evil.example/x)')).toBe('evil')
})
it('refuses javascript: and keeps the label', () => {
expect(renderInline('[bad](javascript:alert(1))')).toBe('bad')
})
it('allows an in-app link', () => {
expect(renderInline('[map](/map)')).toBe('<a href="/map">map</a>')
})
it('still renders bold, italic and code', () => {
expect(renderInline('**b**')).toBe('<strong>b</strong>')
expect(renderInline('*i*')).toBe('<em>i</em>')
expect(renderInline('`C:\\**temp**`'))
.toBe('<code>C:\\**temp**</code>')
})
})