Skip to content

Commit

Permalink
fix(gatsby-plugin-mdx): Do not leak frontmatter into page (#35859)
Browse files Browse the repository at this point in the history
  • Loading branch information
tyhopp authored Jun 9, 2022
1 parent faaca37 commit 5c7e2a6
Show file tree
Hide file tree
Showing 15 changed files with 260 additions and 71 deletions.
91 changes: 91 additions & 0 deletions e2e-tests/mdx-less-babel/cypress/integration/frontmatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const page = {
js: `/frontmatter-js`,
javascript: `/frontmatter-javascript`,
yaml: `/frontmatter-yaml`,
json: `/frontmatter-json`,
graphqlQuery: `/frontmatter-graphql-query`,
}

// Attribute selector for element we assert against in pages
const selector = `[data-cy="frontmatter"]`

// Strings used for frontmatter titles
const titles = {
notParsed: `I should not be parsed`,
parsed: `I am parsed`,
}

// Frontmatter that should not be rendered
const otherKey = `Some other key`

describe(`webpack loader`, () => {
describe(`---yaml frontmatter`, () => {
beforeEach(() => {
cy.visit(page.yaml).waitForRouteChange()
})

it(`should parse`, () => {
cy.get(selector).invoke(`text`).should(`eq`, titles.parsed)
})

it(`should not leak into the page`, () => {
cy.contains(otherKey).should(`not.exist`)
})
})

describe(`---json frontmatter`, () => {
beforeEach(() => {
cy.visit(page.json).waitForRouteChange()
})

it(`should parse`, () => {
cy.get(selector).invoke(`text`).should(`eq`, titles.parsed)
})

it(`should not leak into the page`, () => {
cy.contains(otherKey).should(`not.exist`)
})
})

describe(`---js frontmatter`, () => {
beforeEach(() => {
cy.visit(page.js).waitForRouteChange()
})

it(`should parse`, () => {
cy.get(selector).invoke(`text`).should(`eq`, `disabled`)
})

it(`should not leak into the page`, () => {
cy.contains(otherKey).should(`not.exist`)
})
})

describe(`---javascript frontmatter`, () => {
beforeEach(() => {
cy.visit(page.javascript).waitForRouteChange()
})

it(`should parse`, () => {
cy.get(selector).invoke(`text`).should(`eq`, `disabled`)
})

it(`should not leak into the page`, () => {
cy.contains(otherKey).should(`not.exist`)
})
})
})

describe(`data layer`, () => {
it(`---js or ---javascript frontmatter should not parse by default`, () => {
cy.visit(page.graphqlQuery).waitForRouteChange()
cy.contains(titles.notParsed).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`
)
})
29 changes: 0 additions & 29 deletions e2e-tests/mdx-less-babel/cypress/integration/js-frontmatter.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
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`
title: `I should not be parsed`,
otherKey: `Some other key`
}
})()

---

<h1>JS frontmatter engine is disabled by default</h1>

<span data-cy="js-frontmatter">
<span data-cy="frontmatter">
{props.pageContext.frontmatter?.title || `disabled`}
</span>
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
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`
title: `I should not be parsed`,
otherKey: `Some other key`
}
})()

---

<h1>JS frontmatter engine is disabled by default</h1>

<span data-cy="js-frontmatter">
<span data-cy="frontmatter">
{props.pageContext.frontmatter?.title || `disabled`}
</span>
8 changes: 8 additions & 0 deletions e2e-tests/mdx-less-babel/src/pages/frontmatter-json.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---json
{ "title": "I am parsed", "otherKey": "Some other key" }

---

<h1>A page with JSON frontmatter</h1>

<span data-cy="frontmatter">{props.pageContext.frontmatter?.title}</span>
9 changes: 9 additions & 0 deletions e2e-tests/mdx-less-babel/src/pages/frontmatter-yaml.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---yaml
title: I am parsed
otherKey: Some other key

---

<h1>A page with YAML frontmatter</h1>

<span data-cy="frontmatter">{props.pageContext.frontmatter?.title}</span>
91 changes: 91 additions & 0 deletions e2e-tests/mdx/cypress/integration/frontmatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const page = {
js: `/frontmatter-js`,
javascript: `/frontmatter-javascript`,
yaml: `/frontmatter-yaml`,
json: `/frontmatter-json`,
graphqlQuery: `/frontmatter-graphql-query`,
}

// Attribute selector for element we assert against in pages
const selector = `[data-cy="frontmatter"]`

// Strings used for frontmatter titles
const titles = {
notParsed: `I should not be parsed`,
parsed: `I am parsed`,
}

// Frontmatter that should not be rendered
const otherKey = `Some other key`

describe(`webpack loader`, () => {
describe(`---yaml frontmatter`, () => {
beforeEach(() => {
cy.visit(page.yaml).waitForRouteChange()
})

it(`should parse`, () => {
cy.get(selector).invoke(`text`).should(`eq`, titles.parsed)
})

it(`should not leak into the page`, () => {
cy.contains(otherKey).should(`not.exist`)
})
})

describe(`---json frontmatter`, () => {
beforeEach(() => {
cy.visit(page.json).waitForRouteChange()
})

it(`should parse`, () => {
cy.get(selector).invoke(`text`).should(`eq`, titles.parsed)
})

it(`should not leak into the page`, () => {
cy.contains(otherKey).should(`not.exist`)
})
})

describe(`---js frontmatter`, () => {
beforeEach(() => {
cy.visit(page.js).waitForRouteChange()
})

it(`should parse`, () => {
cy.get(selector).invoke(`text`).should(`eq`, `disabled`)
})

it(`should not leak into the page`, () => {
cy.contains(otherKey).should(`not.exist`)
})
})

describe(`---javascript frontmatter`, () => {
beforeEach(() => {
cy.visit(page.javascript).waitForRouteChange()
})

it(`should parse`, () => {
cy.get(selector).invoke(`text`).should(`eq`, `disabled`)
})

it(`should not leak into the page`, () => {
cy.contains(otherKey).should(`not.exist`)
})
})
})

describe(`data layer`, () => {
it(`---js or ---javascript frontmatter should not parse by default`, () => {
cy.visit(page.graphqlQuery).waitForRouteChange()
cy.contains(titles.notParsed).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`
)
})
29 changes: 0 additions & 29 deletions e2e-tests/mdx/cypress/integration/js-frontmatter.js

This file was deleted.

30 changes: 30 additions & 0 deletions e2e-tests/mdx/src/pages/frontmatter-graphql-query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react"
import { graphql } from "gatsby"

export default function PageRunningGraphqlResolversOnJSFrontmatterTestInputs({
data,
}) {
return <pre>{JSON.stringify(data.allMdx.nodes, null, 2)}</pre>
}

export const query = graphql`
{
allMdx(filter: { slug: { glob: "frontmatter-engine/*" } }) {
nodes {
frontmatter {
title
}
body
excerpt
tableOfContents
timeToRead
wordCount {
paragraphs
sentences
words
}
mdxAST
}
}
}
`
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
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`
title: `I should not be parsed`,
otherKey: `Some other key`
}
})()

---

<h1>JS frontmatter engine is disabled by default</h1>

<span data-cy="js-frontmatter">
<span data-cy="frontmatter">
{props.pageContext.frontmatter?.title || `disabled`}
</span>
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
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`
title: `I should not be parsed`,
otherKey: `Some other key`
}
})()

---

<h1>JS frontmatter engine is disabled by default</h1>

<span data-cy="js-frontmatter">
<span data-cy="frontmatter">
{props.pageContext.frontmatter?.title || `disabled`}
</span>
8 changes: 8 additions & 0 deletions e2e-tests/mdx/src/pages/frontmatter-json.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---json
{ "title": "I am parsed", "otherKey": "Some other key" }

---

<h1>A page with JSON frontmatter</h1>

<span data-cy="frontmatter">{props.pageContext.frontmatter?.title}</span>
9 changes: 9 additions & 0 deletions e2e-tests/mdx/src/pages/frontmatter-yaml.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---yaml
title: I am parsed
otherKey: Some other key

---

<h1>A page with YAML frontmatter</h1>

<span data-cy="frontmatter">{props.pageContext.frontmatter?.title}</span>
7 changes: 2 additions & 5 deletions packages/gatsby-plugin-mdx/loaders/mdx-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,9 @@ export const _frontmatter = ${JSON.stringify(data)};`
// check needs to happen first.
if (!hasDefaultExport(content, options) && !!defaultLayout) {
debug(`inserting default layout`, defaultLayout)
const { content: contentWithoutFrontmatter, matter } = grayMatter(
content,
options
)
const { content: contentWithoutFrontmatter } = grayMatter(content, options)

code = `${matter ? matter : ``}
code = `
import DefaultLayout from "${slash(defaultLayout)}"
Expand Down

0 comments on commit 5c7e2a6

Please sign in to comment.