CVE-2021-23639 (TypeScript, published 2021-12-10T20:15Z)
Description: value: The package md-to-pdf before 5.0.0 are vulnerable to Remote Code Execution (RCE) due to utilizing the library gray-matter to parse front matter content, without disabling the JS engine.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/simonhaenisch/md-to-pdf
Files changed:
readme.md
src/cli.ts
src/lib/config.ts
src/lib/help.ts
src/lib/md-to-pdf.ts
src/test/api.spec.ts
Commit message: fix: disable JS engine for front-matter by default to prevent RCE

BREAKING CHANGE: If you previously used JS in front-matter, you'll now have to set `--gray-matter-options 'null'` (or `gray_matter_options: undefined`) to overwrite the new default options that disable the JS engine.

Fixes #99.


Vulnerable code:
## Security Consideration
	const { content: md, data: frontMatterConfig } = grayMatter(mdFileContent);
	config = {
		...config,
		...(frontMatterConfig as Config),
		pdf_options: { ...config.pdf_options, ...frontMatterConfig.pdf_options },
	};
	const doc = await getDocument({ data: pdf.content }).promise;
	const page = await doc.getPage(1);
	const text = (await page.getTextContent()).items.map(({ str }) => str).join('');
	t.true(text.startsWith('Formulas with MathJax'));
	t.true(text.includes('a≠0'));

Fixed code:
  --gray-matter-options .... Set custom options for gray-matter
| `--gray-matter-options` | `null`                                                                |
The JS engine for front-matter is disabled by default for security reasons. You can enable it by overwriting the default gray-matter options (`--gray-matter-options null`, or `gray_matter_options: undefined` in the API).

## Security Considerations

### Local file server
### Don't trust markdown content you don't control

If you intend to use this tool to convert user-provided markdown content, please be aware that - as always - you should sanitize it before processing it with `md-to-pdf`.

	'--gray-matter-options': String,
import { GrayMatterOption } from 'gray-matter';
	gray_matter_options: {
		engines: {
			js: () =>
				new Error(
					'The JS engine for front-matter is disabled by default for security reasons. You can enable it by configuring graymatter_options.',
				),
		},
	},
	/**
	 * Options for gray-matter (front-matter parser).
	 *
	 * @see https://github.com/jonschlinkert/gray-matter#options
	 */
	gray_matter_options: GrayMatterOption<string, any>;

		--gray-matter-options ${chalk.dim('....')} Set custom options for gray-matter
	const { content: md, data: frontMatterConfig } = grayMatter(
		mdFileContent,
		args['--gray-matter-options'] ? JSON.parse(args['--gray-matter-options']) : config.gray_matter_options,
	);
	if (frontMatterConfig instanceof Error) {
		console.warn('Warning: the front-matter was ignored because it could not be parsed:\n', frontMatterConfig);
	} else {
		config = {
			...config,
			...(frontMatterConfig as Config),
			pdf_options: { ...config.pdf_options, ...frontMatterConfig.pdf_options },
		};
	}
const getPdfTextContent = async (content: Buffer) => {
	const doc = await getDocument({ data: content }).promise;
	const page = await doc.getPage(1);
	const textContent = (await page.getTextContent()).items.map(({ str }) => str).join('');

	return textContent;
};

	const textContent = await getPdfTextContent(pdf.content);

	t.true(textContent.startsWith('Formulas with MathJax'));
	t.true(textContent.includes('a≠0'));
});

test('the JS engine is disabled by default', async (t) => {
	const css = '`body::before { display: block; content: "${"i am injected"}"}`'; // eslint-disable-line no-template-curly-in-string

	const pdf = await mdToPdf({ content: `---js\n{ css: ${css} }\n---` });

	const textContent = await getPdfTextContent(pdf.content);

	t.is(textContent, '');
});

test('the JS engine for front-matter can be enabled', async (t) => {
	const css = '`body::before { display: block; content: "${"i am injected"}"}`'; // eslint-disable-line no-template-curly-in-string

	const pdf = await mdToPdf({ content: `---js\n{ css: ${css} }\n---` }, { gray_matter_options: undefined });

	const textContent = await getPdfTextContent(pdf.content);
	t.is(textContent, 'i am injected');