From 417e4e8e364bca9ca10ed33f37e3875b198d16dd Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Thu, 2 Jun 2022 20:40:41 +0200 Subject: [PATCH] fix(gatsby-plugin-mdx): don't allow JS frontmatter by default (#35830) Co-authored-by: Ty Hopp (cherry picked from commit b3690fbb70cc4470f6f17595f59d83146a443ffb) --- e2e-tests/mdx-less-babel/.gitignore | 1 + .../fixtures/file-to-attempt-rce-on.txt | 1 + .../cypress/integration/js-frontmatter.js | 29 ++++++++++++++ e2e-tests/mdx-less-babel/gatsby-node.js | 12 ++++++ e2e-tests/mdx-less-babel/package.json | 15 ++++---- .../src/pages/javascript-frontmatter.mdx | 16 ++++++++ .../src/pages/js-frontmatter.mdx | 16 ++++++++ .../src/pages/mdx-query-js-frontmatter.js | 30 +++++++++++++++ .../javascript-frontmatter.mdx | 16 ++++++++ .../frontmatter-engine/js-frontmatter.mdx | 16 ++++++++ e2e-tests/mdx/.gitignore | 1 + .../fixtures/file-to-attempt-rce-on.txt | 1 + .../mdx/cypress/integration/js-frontmatter.js | 29 ++++++++++++++ e2e-tests/mdx/gatsby-config.js | 2 +- e2e-tests/mdx/gatsby-node.js | 13 +++++++ e2e-tests/mdx/package.json | 1 + .../mdx/src/pages/javascript-frontmatter.mdx | 16 ++++++++ e2e-tests/mdx/src/pages/js-frontmatter.mdx | 16 ++++++++ .../mdx/src/pages/mdx-query-js-frontmatter.js | 30 +++++++++++++++ .../javascript-frontmatter.mdx | 16 ++++++++ .../frontmatter-engine/js-frontmatter.mdx | 16 ++++++++ packages/gatsby-plugin-mdx/README.md | 5 +++ .../__tests__/gatsby-node.js | 3 ++ packages/gatsby-plugin-mdx/gatsby-node.js | 38 +++++++++++++++++++ .../gatsby/on-create-node.js | 1 + .../gatsby-plugin-mdx/loaders/mdx-loader.js | 12 ++++-- .../utils/create-mdx-node.js | 4 +- packages/gatsby-plugin-mdx/utils/gen-mdx.js | 9 +++-- packages/gatsby-plugin-mdx/utils/mdx.js | 7 ++-- 29 files changed, 351 insertions(+), 21 deletions(-) create mode 100644 e2e-tests/mdx-less-babel/cypress/fixtures/file-to-attempt-rce-on.txt create mode 100644 e2e-tests/mdx-less-babel/cypress/integration/js-frontmatter.js create mode 100644 e2e-tests/mdx-less-babel/gatsby-node.js create mode 100644 e2e-tests/mdx-less-babel/src/pages/javascript-frontmatter.mdx create mode 100644 e2e-tests/mdx-less-babel/src/pages/js-frontmatter.mdx create mode 100644 e2e-tests/mdx-less-babel/src/pages/mdx-query-js-frontmatter.js create mode 100644 e2e-tests/mdx-less-babel/src/posts/frontmatter-engine/javascript-frontmatter.mdx create mode 100644 e2e-tests/mdx-less-babel/src/posts/frontmatter-engine/js-frontmatter.mdx create mode 100644 e2e-tests/mdx/cypress/fixtures/file-to-attempt-rce-on.txt create mode 100644 e2e-tests/mdx/cypress/integration/js-frontmatter.js create mode 100644 e2e-tests/mdx/src/pages/javascript-frontmatter.mdx create mode 100644 e2e-tests/mdx/src/pages/js-frontmatter.mdx create mode 100644 e2e-tests/mdx/src/pages/mdx-query-js-frontmatter.js create mode 100644 e2e-tests/mdx/src/posts/frontmatter-engine/javascript-frontmatter.mdx create mode 100644 e2e-tests/mdx/src/posts/frontmatter-engine/js-frontmatter.mdx diff --git a/e2e-tests/mdx-less-babel/.gitignore b/e2e-tests/mdx-less-babel/.gitignore index 615f726febf63..a6c24c20b52b1 100644 --- a/e2e-tests/mdx-less-babel/.gitignore +++ b/e2e-tests/mdx-less-babel/.gitignore @@ -9,3 +9,4 @@ yarn-error.log # Cypress output cypress/videos/ +cypress/screenshots/ diff --git a/e2e-tests/mdx-less-babel/cypress/fixtures/file-to-attempt-rce-on.txt b/e2e-tests/mdx-less-babel/cypress/fixtures/file-to-attempt-rce-on.txt new file mode 100644 index 0000000000000..036fdbe69718b --- /dev/null +++ b/e2e-tests/mdx-less-babel/cypress/fixtures/file-to-attempt-rce-on.txt @@ -0,0 +1 @@ +Nothing here, do not remove \ No newline at end of file diff --git a/e2e-tests/mdx-less-babel/cypress/integration/js-frontmatter.js b/e2e-tests/mdx-less-babel/cypress/integration/js-frontmatter.js new file mode 100644 index 0000000000000..ac26fcb0aba45 --- /dev/null +++ b/e2e-tests/mdx-less-babel/cypress/integration/js-frontmatter.js @@ -0,0 +1,29 @@ +describe(`webpack loader`, () => { + it(`---js frontmatter should not parse by default`, () => { + cy.visit(`/js-frontmatter`).waitForRouteChange() + + // Check frontmatter not parsed in page context + cy.get(`[data-cy="js-frontmatter"]`).invoke(`text`).should(`eq`, `disabled`) + }) + + it(`---javascript frontmatter should not parse by default`, () => { + cy.visit(`/javascript-frontmatter`).waitForRouteChange() + + // Check frontmatter not parsed in page context + cy.get(`[data-cy="js-frontmatter"]`).invoke(`text`).should(`eq`, `disabled`) + }) +}) + +describe(`data layer`, () => { + it(`---js or ---javascript frontmatter should not parse by default`, () => { + cy.visit(`/mdx-query-js-frontmatter/`).waitForRouteChange() + cy.contains(`I should not be parsed`).should("not.exist") + }) +}) + +it(`---js and ---javascript frontmatter should not allow remote code execution`, () => { + cy.readFile(`cypress/fixtures/file-to-attempt-rce-on.txt`).should( + `eq`, + `Nothing here, do not remove` + ) +}) diff --git a/e2e-tests/mdx-less-babel/gatsby-node.js b/e2e-tests/mdx-less-babel/gatsby-node.js new file mode 100644 index 0000000000000..5e4c76ccc2535 --- /dev/null +++ b/e2e-tests/mdx-less-babel/gatsby-node.js @@ -0,0 +1,12 @@ +exports.createSchemaCustomization = ({ actions }) => { + const { createTypes } = actions + + createTypes(` + type Mdx implements Node { + frontmatter: Frontmatter + } + type Frontmatter { + title: String + } + `) +} diff --git a/e2e-tests/mdx-less-babel/package.json b/e2e-tests/mdx-less-babel/package.json index 7df2170e3f8fe..6560a2e290bd6 100644 --- a/e2e-tests/mdx-less-babel/package.json +++ b/e2e-tests/mdx-less-babel/package.json @@ -6,9 +6,9 @@ "@mdx-js/mdx": "^1.6.6", "@mdx-js/react": "^1.6.6", "cypress": "^3.1.0", - "gatsby": "^2.0.118", - "gatsby-plugin-mdx": "^1.2.19", - "gatsby-source-filesystem": "^2.3.14", + "gatsby": "^3.0.0", + "gatsby-plugin-mdx": "^2.0.0", + "gatsby-source-filesystem": "^3.0.0", "react": "^16.9.0", "react-dom": "^16.9.0", "theme-ui": "^0.3.1" @@ -18,10 +18,11 @@ ], "license": "MIT", "scripts": { - "build": "gatsby build", - "develop": "gatsby develop", + "clean": "gatsby clean", + "build": "cross-env CYPRESS_SUPPORT=y gatsby build", + "develop": "cross-env CYPRESS_SUPPORT=y gatsby develop", "format": "prettier --write '**/*.js'", - "test": "cross-env CYPRESS_SUPPORT=y npm run build && npm run start-server-and-test", + "test": "npm run build && npm run start-server-and-test", "start-server-and-test": "start-server-and-test serve http://localhost:9000 cy:run", "serve": "gatsby serve", "cy:open": "cypress open", @@ -34,4 +35,4 @@ "prettier": "2.0.4", "start-server-and-test": "^1.7.1" } -} \ No newline at end of file +} diff --git a/e2e-tests/mdx-less-babel/src/pages/javascript-frontmatter.mdx b/e2e-tests/mdx-less-babel/src/pages/javascript-frontmatter.mdx new file mode 100644 index 0000000000000..5210c21d086d4 --- /dev/null +++ b/e2e-tests/mdx-less-babel/src/pages/javascript-frontmatter.mdx @@ -0,0 +1,16 @@ +---javascript +(() => { +require(`fs`).writeFileSync(`${process.cwd()}/cypress/fixtures/file-to-attempt-rce-on.txt`, (new Error('Helpful stack trace if this does execute. It should not execute.')).stack) +console.trace() +return { +title: `I should not be parsed` +} +})() + +--- + +

JS frontmatter engine is disabled by default

+ + + {props.pageContext.frontmatter?.title || `disabled`} + diff --git a/e2e-tests/mdx-less-babel/src/pages/js-frontmatter.mdx b/e2e-tests/mdx-less-babel/src/pages/js-frontmatter.mdx new file mode 100644 index 0000000000000..fcb411cdccfda --- /dev/null +++ b/e2e-tests/mdx-less-babel/src/pages/js-frontmatter.mdx @@ -0,0 +1,16 @@ +---js +(() => { +require(`fs`).writeFileSync(`${process.cwd()}/cypress/fixtures/file-to-attempt-rce-on.txt`, (new Error('Helpful stack trace if this does execute. It should not execute.')).stack) +console.trace() +return { +title: `I should not be parsed` +} +})() + +--- + +

JS frontmatter engine is disabled by default

+ + + {props.pageContext.frontmatter?.title || `disabled`} + diff --git a/e2e-tests/mdx-less-babel/src/pages/mdx-query-js-frontmatter.js b/e2e-tests/mdx-less-babel/src/pages/mdx-query-js-frontmatter.js new file mode 100644 index 0000000000000..c94d169a7699e --- /dev/null +++ b/e2e-tests/mdx-less-babel/src/pages/mdx-query-js-frontmatter.js @@ -0,0 +1,30 @@ +import React from "react" +import { graphql } from "gatsby" + +export default function PageRunningGraphqlResolversOnJSFrontmatterTestInputs({ + data, +}) { + return
{JSON.stringify(data.allMdx.nodes, null, 2)}
+} + +export const query = graphql` + { + allMdx(filter: { slug: { glob: "frontmatter-engine/*" } }) { + nodes { + frontmatter { + title + } + body + excerpt + tableOfContents + timeToRead + wordCount { + paragraphs + sentences + words + } + mdxAST + } + } + } +` diff --git a/e2e-tests/mdx-less-babel/src/posts/frontmatter-engine/javascript-frontmatter.mdx b/e2e-tests/mdx-less-babel/src/posts/frontmatter-engine/javascript-frontmatter.mdx new file mode 100644 index 0000000000000..76f5bd494d3a4 --- /dev/null +++ b/e2e-tests/mdx-less-babel/src/posts/frontmatter-engine/javascript-frontmatter.mdx @@ -0,0 +1,16 @@ +---javascript +(() => { +require(`fs`).writeFileSync(`${process.cwd()}/cypress/fixtures/file-to-attempt-rce-on.txt`, (new Error('Helpful stack trace if this does execute. It should not execute.')).stack) +console.trace() +return { +title: `I should not be parsed` +} +})() + +--- + +

JS frontmatter engine is disabled by default w

+ + + {props.pageContext.frontmatter?.title || `disabled`} + diff --git a/e2e-tests/mdx-less-babel/src/posts/frontmatter-engine/js-frontmatter.mdx b/e2e-tests/mdx-less-babel/src/posts/frontmatter-engine/js-frontmatter.mdx new file mode 100644 index 0000000000000..fcb411cdccfda --- /dev/null +++ b/e2e-tests/mdx-less-babel/src/posts/frontmatter-engine/js-frontmatter.mdx @@ -0,0 +1,16 @@ +---js +(() => { +require(`fs`).writeFileSync(`${process.cwd()}/cypress/fixtures/file-to-attempt-rce-on.txt`, (new Error('Helpful stack trace if this does execute. It should not execute.')).stack) +console.trace() +return { +title: `I should not be parsed` +} +})() + +--- + +

JS frontmatter engine is disabled by default

+ + + {props.pageContext.frontmatter?.title || `disabled`} + diff --git a/e2e-tests/mdx/.gitignore b/e2e-tests/mdx/.gitignore index 615f726febf63..1bdfa17d9c898 100644 --- a/e2e-tests/mdx/.gitignore +++ b/e2e-tests/mdx/.gitignore @@ -9,3 +9,4 @@ yarn-error.log # Cypress output cypress/videos/ +cypress/screenshots/ \ No newline at end of file diff --git a/e2e-tests/mdx/cypress/fixtures/file-to-attempt-rce-on.txt b/e2e-tests/mdx/cypress/fixtures/file-to-attempt-rce-on.txt new file mode 100644 index 0000000000000..036fdbe69718b --- /dev/null +++ b/e2e-tests/mdx/cypress/fixtures/file-to-attempt-rce-on.txt @@ -0,0 +1 @@ +Nothing here, do not remove \ No newline at end of file diff --git a/e2e-tests/mdx/cypress/integration/js-frontmatter.js b/e2e-tests/mdx/cypress/integration/js-frontmatter.js new file mode 100644 index 0000000000000..ac26fcb0aba45 --- /dev/null +++ b/e2e-tests/mdx/cypress/integration/js-frontmatter.js @@ -0,0 +1,29 @@ +describe(`webpack loader`, () => { + it(`---js frontmatter should not parse by default`, () => { + cy.visit(`/js-frontmatter`).waitForRouteChange() + + // Check frontmatter not parsed in page context + cy.get(`[data-cy="js-frontmatter"]`).invoke(`text`).should(`eq`, `disabled`) + }) + + it(`---javascript frontmatter should not parse by default`, () => { + cy.visit(`/javascript-frontmatter`).waitForRouteChange() + + // Check frontmatter not parsed in page context + cy.get(`[data-cy="js-frontmatter"]`).invoke(`text`).should(`eq`, `disabled`) + }) +}) + +describe(`data layer`, () => { + it(`---js or ---javascript frontmatter should not parse by default`, () => { + cy.visit(`/mdx-query-js-frontmatter/`).waitForRouteChange() + cy.contains(`I should not be parsed`).should("not.exist") + }) +}) + +it(`---js and ---javascript frontmatter should not allow remote code execution`, () => { + cy.readFile(`cypress/fixtures/file-to-attempt-rce-on.txt`).should( + `eq`, + `Nothing here, do not remove` + ) +}) diff --git a/e2e-tests/mdx/gatsby-config.js b/e2e-tests/mdx/gatsby-config.js index e63a750799933..f072f361ea177 100644 --- a/e2e-tests/mdx/gatsby-config.js +++ b/e2e-tests/mdx/gatsby-config.js @@ -37,7 +37,7 @@ module.exports = { * See #26914 for more info. */ function remarkRequireFilePathPlugin() { - return function transformer(tree, file) { + return function transformer(_, file) { if (!file.dirname) { throw new Error("No directory name for this markdown file!") } diff --git a/e2e-tests/mdx/gatsby-node.js b/e2e-tests/mdx/gatsby-node.js index eceaaff1a0d20..3c94f90b31a46 100644 --- a/e2e-tests/mdx/gatsby-node.js +++ b/e2e-tests/mdx/gatsby-node.js @@ -16,3 +16,16 @@ exports.onPostBuild = async ({ graphql }) => { { spaces: 2 } ) } + +exports.createSchemaCustomization = ({ actions }) => { + const { createTypes } = actions + + createTypes(` + type Mdx implements Node { + frontmatter: Frontmatter + } + type Frontmatter { + title: String + } + `) +} diff --git a/e2e-tests/mdx/package.json b/e2e-tests/mdx/package.json index 5e103c197b723..60d3dc2b36e9c 100644 --- a/e2e-tests/mdx/package.json +++ b/e2e-tests/mdx/package.json @@ -19,6 +19,7 @@ ], "license": "MIT", "scripts": { + "clean": "gatsby clean", "build": "cross-env CYPRESS_SUPPORT=y gatsby build", "develop": "cross-env CYPRESS_SUPPORT=y gatsby develop", "format": "prettier --write '**/*.js'", diff --git a/e2e-tests/mdx/src/pages/javascript-frontmatter.mdx b/e2e-tests/mdx/src/pages/javascript-frontmatter.mdx new file mode 100644 index 0000000000000..5210c21d086d4 --- /dev/null +++ b/e2e-tests/mdx/src/pages/javascript-frontmatter.mdx @@ -0,0 +1,16 @@ +---javascript +(() => { +require(`fs`).writeFileSync(`${process.cwd()}/cypress/fixtures/file-to-attempt-rce-on.txt`, (new Error('Helpful stack trace if this does execute. It should not execute.')).stack) +console.trace() +return { +title: `I should not be parsed` +} +})() + +--- + +

JS frontmatter engine is disabled by default

+ + + {props.pageContext.frontmatter?.title || `disabled`} + diff --git a/e2e-tests/mdx/src/pages/js-frontmatter.mdx b/e2e-tests/mdx/src/pages/js-frontmatter.mdx new file mode 100644 index 0000000000000..fcb411cdccfda --- /dev/null +++ b/e2e-tests/mdx/src/pages/js-frontmatter.mdx @@ -0,0 +1,16 @@ +---js +(() => { +require(`fs`).writeFileSync(`${process.cwd()}/cypress/fixtures/file-to-attempt-rce-on.txt`, (new Error('Helpful stack trace if this does execute. It should not execute.')).stack) +console.trace() +return { +title: `I should not be parsed` +} +})() + +--- + +

JS frontmatter engine is disabled by default

+ + + {props.pageContext.frontmatter?.title || `disabled`} + diff --git a/e2e-tests/mdx/src/pages/mdx-query-js-frontmatter.js b/e2e-tests/mdx/src/pages/mdx-query-js-frontmatter.js new file mode 100644 index 0000000000000..c94d169a7699e --- /dev/null +++ b/e2e-tests/mdx/src/pages/mdx-query-js-frontmatter.js @@ -0,0 +1,30 @@ +import React from "react" +import { graphql } from "gatsby" + +export default function PageRunningGraphqlResolversOnJSFrontmatterTestInputs({ + data, +}) { + return
{JSON.stringify(data.allMdx.nodes, null, 2)}
+} + +export const query = graphql` + { + allMdx(filter: { slug: { glob: "frontmatter-engine/*" } }) { + nodes { + frontmatter { + title + } + body + excerpt + tableOfContents + timeToRead + wordCount { + paragraphs + sentences + words + } + mdxAST + } + } + } +` diff --git a/e2e-tests/mdx/src/posts/frontmatter-engine/javascript-frontmatter.mdx b/e2e-tests/mdx/src/posts/frontmatter-engine/javascript-frontmatter.mdx new file mode 100644 index 0000000000000..76f5bd494d3a4 --- /dev/null +++ b/e2e-tests/mdx/src/posts/frontmatter-engine/javascript-frontmatter.mdx @@ -0,0 +1,16 @@ +---javascript +(() => { +require(`fs`).writeFileSync(`${process.cwd()}/cypress/fixtures/file-to-attempt-rce-on.txt`, (new Error('Helpful stack trace if this does execute. It should not execute.')).stack) +console.trace() +return { +title: `I should not be parsed` +} +})() + +--- + +

JS frontmatter engine is disabled by default w

+ + + {props.pageContext.frontmatter?.title || `disabled`} + diff --git a/e2e-tests/mdx/src/posts/frontmatter-engine/js-frontmatter.mdx b/e2e-tests/mdx/src/posts/frontmatter-engine/js-frontmatter.mdx new file mode 100644 index 0000000000000..fcb411cdccfda --- /dev/null +++ b/e2e-tests/mdx/src/posts/frontmatter-engine/js-frontmatter.mdx @@ -0,0 +1,16 @@ +---js +(() => { +require(`fs`).writeFileSync(`${process.cwd()}/cypress/fixtures/file-to-attempt-rce-on.txt`, (new Error('Helpful stack trace if this does execute. It should not execute.')).stack) +console.trace() +return { +title: `I should not be parsed` +} +})() + +--- + +

JS frontmatter engine is disabled by default

+ + + {props.pageContext.frontmatter?.title || `disabled`} + diff --git a/packages/gatsby-plugin-mdx/README.md b/packages/gatsby-plugin-mdx/README.md index fd889fe17ca38..09c8be75125df 100644 --- a/packages/gatsby-plugin-mdx/README.md +++ b/packages/gatsby-plugin-mdx/README.md @@ -129,6 +129,7 @@ scope, and more. | [`mediaTypes`](#media-types) | `["text/markdown", "text/x-markdown"]` | Determine which media types are processed by MDX | | [`shouldBlockNodeFromTransformation`](#shouldblocknodefromtransformation) | `(node) => false` | Disable MDX transformation for nodes where this function returns true | | [`commonmark`](#commonmark) | `false` | Use CommonMark | +| [`JSFrontmatterEngine`](#jsfrontmatterengine) | `false` | Add support for JavaScript frontmatter engine | #### Extensions @@ -472,6 +473,10 @@ module.exports = { MDX will be parsed using CommonMark. +#### JSFrontmatterEngine + +Adds support for JavaScript frontmatter engine. Use with caution - see https://github.com/gatsbyjs/gatsby/security/advisories/GHSA-mj46-r4gr-5x83 + ### Components MDX and `gatsby-plugin-mdx` use components for different things like rendering diff --git a/packages/gatsby-plugin-mdx/__tests__/gatsby-node.js b/packages/gatsby-plugin-mdx/__tests__/gatsby-node.js index 0c7bacc5ae648..ee5d6b1f04984 100644 --- a/packages/gatsby-plugin-mdx/__tests__/gatsby-node.js +++ b/packages/gatsby-plugin-mdx/__tests__/gatsby-node.js @@ -17,6 +17,7 @@ describe(`pluginOptionsSchema`, () => { `"mediaTypes[1]" must be a string`, `"shouldBlockNodeFromTransformation" must have an arity lesser or equal to 1`, `"root" must be a string`, + `"JSFrontmatterEngine" must be a boolean`, ] const { errors } = await testPluginOptionsSchema(pluginOptionsSchema, { @@ -29,6 +30,7 @@ describe(`pluginOptionsSchema`, () => { mediaTypes: [1, 2], shouldBlockNodeFromTransformation: (wrong, number) => null, root: 1, + JSFrontmatterEngine: `this should be a boolean`, }) expect(errors).toEqual(expectedErrors) @@ -63,6 +65,7 @@ describe(`pluginOptionsSchema`, () => { mediaTypes: [`text/markdown`, `text/x-markdown`, `custom-media/type`], shouldBlockNodeFromTransformation: node => Boolean(node), root: `james-holden`, + JSFrontmatterEngine: true, }) expect(isValid).toBe(true) diff --git a/packages/gatsby-plugin-mdx/gatsby-node.js b/packages/gatsby-plugin-mdx/gatsby-node.js index a5e0f5bb92389..5532c8181d76a 100644 --- a/packages/gatsby-plugin-mdx/gatsby-node.js +++ b/packages/gatsby-plugin-mdx/gatsby-node.js @@ -68,6 +68,9 @@ exports.onPostBootstrap = ({ cache }, pluginOptions) => { } } +// Dedupe warning +let warnedAboutJSFrontmatterEngine = false + exports.pluginOptionsSchema = function ({ Joi }) { return Joi.object({ extensions: Joi.array() @@ -127,5 +130,40 @@ exports.pluginOptionsSchema = function ({ Joi }) { .description( `[deprecated] This is a legacy option that used to define root directory of the project. It was needed to generate a cache directory location. It currently has no effect.` ), + JSFrontmatterEngine: Joi.boolean() + .default(false) + .description(`Enable JavaScript frontmatter parsing`), + }).custom(value => { + const { JSFrontmatterEngine, engines = {} } = value || {} + + if (JSFrontmatterEngine) { + // show this warning only once in main process + if (!process.env.GATSBY_WORKER_ID) { + console.warn( + `JS frontmatter engine is enabled in gatsby-plugin-mdx (via JSFrontmatterEngine: true in plugin options). This can cause a security risk, see https://github.com/gatsbyjs/gatsby/security/advisories/GHSA-mj46-r4gr-5x83. If you are not relying on this feature we strongly suggest disabling it via the "JSFrontmatterEngine: false" plugin option. If you rely on this feature make sure to properly secure or sanitize your content source.` + ) + } + return value + } + + const js = () => { + if (!warnedAboutJSFrontmatterEngine) { + console.warn( + `You have frontmatter declared with "---js" or "---javascript" that is not parsed by default to mitigate a security risk (see https://github.com/gatsbyjs/gatsby/security/advisories/GHSA-mj46-r4gr-5x83). If you require this feature it can be enabled by setting "JSFrontmatterEngine: true" in the plugin options of gatsby-plugin-mdx.` + ) + warnedAboutJSFrontmatterEngine = true + } + // we still have to return a frontmatter, se we just stub it with empty object + return {} + } + + return { + ...value, + engines: { + ...engines, + js, + javascript: js, + }, + } }) } diff --git a/packages/gatsby-plugin-mdx/gatsby/on-create-node.js b/packages/gatsby-plugin-mdx/gatsby/on-create-node.js index 1768c91ddb6af..0f7488e8c5686 100644 --- a/packages/gatsby-plugin-mdx/gatsby/on-create-node.js +++ b/packages/gatsby-plugin-mdx/gatsby/on-create-node.js @@ -74,6 +74,7 @@ async function onCreateNodeExtraBabel( id: createNodeId(`${node.id} >>> Mdx`), node, content, + options, }) createNode(mdxNode) diff --git a/packages/gatsby-plugin-mdx/loaders/mdx-loader.js b/packages/gatsby-plugin-mdx/loaders/mdx-loader.js index a602ca833c5a4..2dfd727b72507 100644 --- a/packages/gatsby-plugin-mdx/loaders/mdx-loader.js +++ b/packages/gatsby-plugin-mdx/loaders/mdx-loader.js @@ -83,7 +83,7 @@ const hasDefaultExport = (str, options) => { methods.splice(methods.indexOf(`paragraph`), 0, `esSyntax`) } - const { content } = grayMatter(str) + const { content } = grayMatter(str, options) unified() .use(toMDAST, options) .use(esSyntax) @@ -111,7 +111,7 @@ module.exports = async function mdxLoader(content) { const resourceQuery = this.resourceQuery || `` if (isolateMDXComponent && !resourceQuery.includes(`type=component`)) { - const { data } = grayMatter(content) + const { data } = grayMatter(content, pluginOptions) const requestPath = slash( `/${path.relative(this.rootContext, this.resourcePath)}?type=component` @@ -163,6 +163,7 @@ export const _frontmatter = ${JSON.stringify(data)};` id: `fakeNodeIdMDXFileABugIfYouSeeThis`, node: fileNode, content, + options, }) } } catch (e) { @@ -180,9 +181,12 @@ export const _frontmatter = ${JSON.stringify(data)};` let code = content // after running mdx, the code *always* has a default export, so this // check needs to happen first. - if (!hasDefaultExport(content, DEFAULT_OPTIONS) && !!defaultLayout) { + if (!hasDefaultExport(content, options) && !!defaultLayout) { debug(`inserting default layout`, defaultLayout) - const { content: contentWithoutFrontmatter, matter } = grayMatter(content) + const { content: contentWithoutFrontmatter, matter } = grayMatter( + content, + options + ) code = `${matter ? matter : ``} diff --git a/packages/gatsby-plugin-mdx/utils/create-mdx-node.js b/packages/gatsby-plugin-mdx/utils/create-mdx-node.js index e7cf7d7fa4eb9..f848b0b8d424e 100644 --- a/packages/gatsby-plugin-mdx/utils/create-mdx-node.js +++ b/packages/gatsby-plugin-mdx/utils/create-mdx-node.js @@ -5,10 +5,10 @@ const extractExports = require(`../utils/extract-exports`) const { findImportsExports } = require(`../utils/gen-mdx`) -async function createMdxNodeExtraBabel({ id, node, content }) { +async function createMdxNodeExtraBabel({ id, node, content, options }) { let code try { - code = await mdx(content) + code = await mdx(content, { filepath: node.absolutePath, ...options }) } catch (e) { // add the path of the file to simplify debugging error messages e.message += `${node.absolutePath}: ${e.message}` diff --git a/packages/gatsby-plugin-mdx/utils/gen-mdx.js b/packages/gatsby-plugin-mdx/utils/gen-mdx.js index 97a115fdca4bf..e558745979b6e 100644 --- a/packages/gatsby-plugin-mdx/utils/gen-mdx.js +++ b/packages/gatsby-plugin-mdx/utils/gen-mdx.js @@ -89,7 +89,10 @@ async function genMDX( // pull classic style frontmatter off the raw MDX body debug(`processing classic frontmatter`) - const { data, content: frontMatterCodeResult } = grayMatter(node.rawBody) + const { data, content: frontMatterCodeResult } = grayMatter( + node.rawBody, + options + ) const content = isolateMDXComponent ? frontMatterCodeResult @@ -210,7 +213,7 @@ async function findImports({ pathPrefix, ...helpers }) { - const { content } = grayMatter(node.rawBody) + const { content } = grayMatter(node.rawBody, options) const gatsbyRemarkPluginsAsremarkPlugins = await getSourcePluginsAsRemarkPlugins({ @@ -289,7 +292,7 @@ async function findImportsExports({ pathPrefix, ...helpers }) { - const { data: frontmatter, content } = grayMatter(rawInput) + const { data: frontmatter, content } = grayMatter(rawInput, options) const gatsbyRemarkPluginsAsRemarkPlugins = await getSourcePluginsAsRemarkPlugins({ diff --git a/packages/gatsby-plugin-mdx/utils/mdx.js b/packages/gatsby-plugin-mdx/utils/mdx.js index 624861d05aef6..21fe4d93c0984 100644 --- a/packages/gatsby-plugin-mdx/utils/mdx.js +++ b/packages/gatsby-plugin-mdx/utils/mdx.js @@ -8,10 +8,9 @@ const grayMatter = require(`gray-matter`) * @param {Object} options options for mdx library * @return {String} JSX source */ -module.exports = async function mdxToJsx(source, options) { - const { data, content } = grayMatter(source) - - const code = await mdx(content, options || {}) +module.exports = async function mdxToJsx(source, options = {}) { + const { data, content } = grayMatter(source, options) + const code = await mdx(content, options) return `${code}