/**
* 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('
'))
.toBe('<img src=x onerror=alert(1)>')
})
it('keeps a multi-parameter URL intact', () => {
// Double-escaped, this became & 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&b=2&c=3"')
expect(html).not.toContain('&')
})
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('')
})
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('map')
})
it('still renders bold, italic and code', () => {
expect(renderInline('**b**')).toBe('b')
expect(renderInline('*i*')).toBe('i')
expect(renderInline('`C:\\**temp**`'))
.toBe('C:\\**temp**')
})
})