CVE-2021-39199 (JavaScript, published 2021-09-07T19:15Z)
Description: value: remark-html is an open source nodejs library which compiles Markdown to HTML. In affected versions the documentation of remark-html has mentioned that it was safe by default. In practice the default was never safe and had to be opted into. That is, user input was not sanitized. This means arbitrary HTML can be passed through leading to potential XSS attacks. The problem has been patched in 13.0.2 and 14.0.1: `remark-html` is now safe by default, and the implementation matches the documentation. On older affected versions, pass `sanitize: true` if you cannot update.
Severity: MEDIUM (CVSS v3 base score: 6.1)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/remarkjs/remark-html
Files changed:
index.js
test/index.js
Commit message: Fix to sanitize by default

The docs have always said `remark-html` is safe by default.
It wasn’t and this patches that.

If you do want to be unsafe, use `remark-html` with `sanitize: false`:

```diff
  -.use(remarkHtml)
  +.use(remarkHtml, {sanitize: false})
```


Vulnerable code:
export default function remarkHtml(options = {}) {
  const handlers = options.handlers || {}
  const schema =
    options.sanitize && typeof options.sanitize === 'object'
      ? options.sanitize
      : undefined
    const hast = toHast(node, {allowDangerousHtml: !options.sanitize, handlers})
    const cleanHast = options.sanitize ? sanitize(hast, schema) : hast
      Object.assign({}, options, {allowDangerousHtml: !options.sanitize})
  let processor = remark().use(remarkHtml)
    processor.stringify({type: 'alpha'}),
    processor.stringify({
    processor.stringify({
  processor = remark().use(remarkHtml, {
    processor.processSync('paragraph text').toString(),
  processor = remark()
    .use(remarkHtml)
    processor.processSync('![hello](example.jpg "overwritten")').toString(),
  processor = remark()
    .use(remarkHtml)
    processor.processSync('**Bold!**').toString(),
  processor = remark()
    .use(remarkHtml)
    processor.processSync('`var`').toString(),
  processor = remark()
    processor.processSync('`var`').toString(),
  processor = remark()
    .use(remarkHtml)
    processor.processSync('```js\nvar\n```\n').toString(),
    '<h2>Hello <span>world</span></h2>\n',
    'should be `sanitation: false` by default'
    '<h2>Hello <span>world</span></h2>\n',
      .use(remarkHtml)
      .use(remarkHtml)

Fixed code:
export default function remarkHtml(settings) {
  const options = {...(settings || {})}
  /** @type {boolean|undefined} */
  let clean

  if (typeof options.sanitize === 'boolean') {
    clean = options.sanitize
    options.sanitize = undefined
  }

  if (typeof clean !== 'boolean') {
    clean = true
  }
    const hast = toHast(node, {
      allowDangerousHtml: !clean,
      handlers: options.handlers
    })
    const cleanHast = clean ? sanitize(hast, options.sanitize) : hast
      Object.assign({}, options, {allowDangerousHtml: !clean})
  let processorDangerous = remark().use(remarkHtml, {sanitize: false})
    processorDangerous.stringify({type: 'alpha'}),
    processorDangerous.stringify({
    processorDangerous.stringify({
  processorDangerous = remark().use(remarkHtml, {
    sanitize: false,
    processorDangerous.processSync('paragraph text').toString(),
  processorDangerous = remark()
    .use(remarkHtml, {sanitize: false})
    processorDangerous
      .processSync('![hello](example.jpg "overwritten")')
      .toString(),
  processorDangerous = remark()
    .use(remarkHtml, {sanitize: false})
    processorDangerous.processSync('**Bold!**').toString(),
  processorDangerous = remark()
    .use(remarkHtml, {sanitize: false})
    processorDangerous.processSync('`var`').toString(),
  processorDangerous = remark()
    processorDangerous.processSync('`var`').toString(),
  processorDangerous = remark()
    .use(remarkHtml, {sanitize: false})
    processorDangerous.processSync('```js\nvar\n```\n').toString(),
    '<h2>Hello world</h2>\n',
    'should be `sanitation: true` by default'
    '<h2>Hello world</h2>\n',
      .use(remarkHtml, {sanitize: false})
      .use(remarkHtml, {sanitize: false})