Skip to content

Commit 2d00ea0

Browse files
gatsbybottyhopp
andauthored
fix(gatsby-plugin-mdx): Do not leak frontmatter into page (#35859) (#35913)
Co-authored-by: Ty Hopp <tyhopp@users.noreply.github.com>
1 parent 4997d63 commit 2d00ea0

15 files changed

+260
-71
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
const page = {
2+
js: `/frontmatter-js`,
3+
javascript: `/frontmatter-javascript`,
4+
yaml: `/frontmatter-yaml`,
5+
json: `/frontmatter-json`,
6+
graphqlQuery: `/frontmatter-graphql-query`,
7+
}
8+
9+
// Attribute selector for element we assert against in pages
10+
const selector = `[data-cy="frontmatter"]`
11+
12+
// Strings used for frontmatter titles
13+
const titles = {
14+
notParsed: `I should not be parsed`,
15+
parsed: `I am parsed`,
16+
}
17+
18+
// Frontmatter that should not be rendered
19+
const otherKey = `Some other key`
20+
21+
describe(`webpack loader`, () => {
22+
describe(`---yaml frontmatter`, () => {
23+
beforeEach(() => {
24+
cy.visit(page.yaml).waitForRouteChange()
25+
})
26+
27+
it(`should parse`, () => {
28+
cy.get(selector).invoke(`text`).should(`eq`, titles.parsed)
29+
})
30+
31+
it(`should not leak into the page`, () => {
32+
cy.contains(otherKey).should(`not.exist`)
33+
})
34+
})
35+
36+
describe(`---json frontmatter`, () => {
37+
beforeEach(() => {
38+
cy.visit(page.json).waitForRouteChange()
39+
})
40+
41+
it(`should parse`, () => {
42+
cy.get(selector).invoke(`text`).should(`eq`, titles.parsed)
43+
})
44+
45+
it(`should not leak into the page`, () => {
46+
cy.contains(otherKey).should(`not.exist`)
47+
})
48+
})
49+
50+
describe(`---js frontmatter`, () => {
51+
beforeEach(() => {
52+
cy.visit(page.js).waitForRouteChange()
53+
})
54+
55+
it(`should parse`, () => {
56+
cy.get(selector).invoke(`text`).should(`eq`, `disabled`)
57+
})
58+
59+
it(`should not leak into the page`, () => {
60+
cy.contains(otherKey).should(`not.exist`)
61+
})
62+
})
63+
64+
describe(`---javascript frontmatter`, () => {
65+
beforeEach(() => {
66+
cy.visit(page.javascript).waitForRouteChange()
67+
})
68+
69+
it(`should parse`, () => {
70+
cy.get(selector).invoke(`text`).should(`eq`, `disabled`)
71+
})
72+
73+
it(`should not leak into the page`, () => {
74+
cy.contains(otherKey).should(`not.exist`)
75+
})
76+
})
77+
})
78+
79+
describe(`data layer`, () => {
80+
it(`---js or ---javascript frontmatter should not parse by default`, () => {
81+
cy.visit(page.graphqlQuery).waitForRouteChange()
82+
cy.contains(titles.notParsed).should(`not.exist`)
83+
})
84+
})
85+
86+
it(`---js and ---javascript frontmatter should not allow remote code execution`, () => {
87+
cy.readFile(`cypress/fixtures/file-to-attempt-rce-on.txt`).should(
88+
`eq`,
89+
`Nothing here, do not remove`
90+
)
91+
})

e2e-tests/mdx-less-babel/cypress/integration/js-frontmatter.js

Lines changed: 0 additions & 29 deletions
This file was deleted.

e2e-tests/mdx/src/pages/javascript-frontmatter.mdx renamed to e2e-tests/mdx-less-babel/src/pages/frontmatter-javascript.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
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)
44
console.trace()
55
return {
6-
title: `I should not be parsed`
6+
title: `I should not be parsed`,
7+
otherKey: `Some other key`
78
}
89
})()
910

1011
---
1112

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

14-
<span data-cy="js-frontmatter">
15+
<span data-cy="frontmatter">
1516
{props.pageContext.frontmatter?.title || `disabled`}
1617
</span>

e2e-tests/mdx-less-babel/src/pages/js-frontmatter.mdx renamed to e2e-tests/mdx-less-babel/src/pages/frontmatter-js.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
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)
44
console.trace()
55
return {
6-
title: `I should not be parsed`
6+
title: `I should not be parsed`,
7+
otherKey: `Some other key`
78
}
89
})()
910

1011
---
1112

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

14-
<span data-cy="js-frontmatter">
15+
<span data-cy="frontmatter">
1516
{props.pageContext.frontmatter?.title || `disabled`}
1617
</span>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---json
2+
{ "title": "I am parsed", "otherKey": "Some other key" }
3+
4+
---
5+
6+
<h1>A page with JSON frontmatter</h1>
7+
8+
<span data-cy="frontmatter">{props.pageContext.frontmatter?.title}</span>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---yaml
2+
title: I am parsed
3+
otherKey: Some other key
4+
5+
---
6+
7+
<h1>A page with YAML frontmatter</h1>
8+
9+
<span data-cy="frontmatter">{props.pageContext.frontmatter?.title}</span>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
const page = {
2+
js: `/frontmatter-js`,
3+
javascript: `/frontmatter-javascript`,
4+
yaml: `/frontmatter-yaml`,
5+
json: `/frontmatter-json`,
6+
graphqlQuery: `/frontmatter-graphql-query`,
7+
}
8+
9+
// Attribute selector for element we assert against in pages
10+
const selector = `[data-cy="frontmatter"]`
11+
12+
// Strings used for frontmatter titles
13+
const titles = {
14+
notParsed: `I should not be parsed`,
15+
parsed: `I am parsed`,
16+
}
17+
18+
// Frontmatter that should not be rendered
19+
const otherKey = `Some other key`
20+
21+
describe(`webpack loader`, () => {
22+
describe(`---yaml frontmatter`, () => {
23+
beforeEach(() => {
24+
cy.visit(page.yaml).waitForRouteChange()
25+
})
26+
27+
it(`should parse`, () => {
28+
cy.get(selector).invoke(`text`).should(`eq`, titles.parsed)
29+
})
30+
31+
it(`should not leak into the page`, () => {
32+
cy.contains(otherKey).should(`not.exist`)
33+
})
34+
})
35+
36+
describe(`---json frontmatter`, () => {
37+
beforeEach(() => {
38+
cy.visit(page.json).waitForRouteChange()
39+
})
40+
41+
it(`should parse`, () => {
42+
cy.get(selector).invoke(`text`).should(`eq`, titles.parsed)
43+
})
44+
45+
it(`should not leak into the page`, () => {
46+
cy.contains(otherKey).should(`not.exist`)
47+
})
48+
})
49+
50+
describe(`---js frontmatter`, () => {
51+
beforeEach(() => {
52+
cy.visit(page.js).waitForRouteChange()
53+
})
54+
55+
it(`should parse`, () => {
56+
cy.get(selector).invoke(`text`).should(`eq`, `disabled`)
57+
})
58+
59+
it(`should not leak into the page`, () => {
60+
cy.contains(otherKey).should(`not.exist`)
61+
})
62+
})
63+
64+
describe(`---javascript frontmatter`, () => {
65+
beforeEach(() => {
66+
cy.visit(page.javascript).waitForRouteChange()
67+
})
68+
69+
it(`should parse`, () => {
70+
cy.get(selector).invoke(`text`).should(`eq`, `disabled`)
71+
})
72+
73+
it(`should not leak into the page`, () => {
74+
cy.contains(otherKey).should(`not.exist`)
75+
})
76+
})
77+
})
78+
79+
describe(`data layer`, () => {
80+
it(`---js or ---javascript frontmatter should not parse by default`, () => {
81+
cy.visit(page.graphqlQuery).waitForRouteChange()
82+
cy.contains(titles.notParsed).should(`not.exist`)
83+
})
84+
})
85+
86+
it(`---js and ---javascript frontmatter should not allow remote code execution`, () => {
87+
cy.readFile(`cypress/fixtures/file-to-attempt-rce-on.txt`).should(
88+
`eq`,
89+
`Nothing here, do not remove`
90+
)
91+
})

e2e-tests/mdx/cypress/integration/js-frontmatter.js

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from "react"
2+
import { graphql } from "gatsby"
3+
4+
export default function PageRunningGraphqlResolversOnJSFrontmatterTestInputs({
5+
data,
6+
}) {
7+
return <pre>{JSON.stringify(data.allMdx.nodes, null, 2)}</pre>
8+
}
9+
10+
export const query = graphql`
11+
{
12+
allMdx(filter: { slug: { glob: "frontmatter-engine/*" } }) {
13+
nodes {
14+
frontmatter {
15+
title
16+
}
17+
body
18+
excerpt
19+
tableOfContents
20+
timeToRead
21+
wordCount {
22+
paragraphs
23+
sentences
24+
words
25+
}
26+
mdxAST
27+
}
28+
}
29+
}
30+
`

e2e-tests/mdx-less-babel/src/pages/javascript-frontmatter.mdx renamed to e2e-tests/mdx/src/pages/frontmatter-javascript.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
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)
44
console.trace()
55
return {
6-
title: `I should not be parsed`
6+
title: `I should not be parsed`,
7+
otherKey: `Some other key`
78
}
89
})()
910

1011
---
1112

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

14-
<span data-cy="js-frontmatter">
15+
<span data-cy="frontmatter">
1516
{props.pageContext.frontmatter?.title || `disabled`}
1617
</span>

e2e-tests/mdx/src/pages/js-frontmatter.mdx renamed to e2e-tests/mdx/src/pages/frontmatter-js.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
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)
44
console.trace()
55
return {
6-
title: `I should not be parsed`
6+
title: `I should not be parsed`,
7+
otherKey: `Some other key`
78
}
89
})()
910

1011
---
1112

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

14-
<span data-cy="js-frontmatter">
15+
<span data-cy="frontmatter">
1516
{props.pageContext.frontmatter?.title || `disabled`}
1617
</span>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---json
2+
{ "title": "I am parsed", "otherKey": "Some other key" }
3+
4+
---
5+
6+
<h1>A page with JSON frontmatter</h1>
7+
8+
<span data-cy="frontmatter">{props.pageContext.frontmatter?.title}</span>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---yaml
2+
title: I am parsed
3+
otherKey: Some other key
4+
5+
---
6+
7+
<h1>A page with YAML frontmatter</h1>
8+
9+
<span data-cy="frontmatter">{props.pageContext.frontmatter?.title}</span>

packages/gatsby-plugin-mdx/loaders/mdx-loader.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,9 @@ export const _frontmatter = ${JSON.stringify(data)};`
183183
// check needs to happen first.
184184
if (!hasDefaultExport(content, options) && !!defaultLayout) {
185185
debug(`inserting default layout`, defaultLayout)
186-
const { content: contentWithoutFrontmatter, matter } = grayMatter(
187-
content,
188-
options
189-
)
186+
const { content: contentWithoutFrontmatter } = grayMatter(content, options)
190187

191-
code = `${matter ? matter : ``}
188+
code = `
192189
193190
import DefaultLayout from "${slash(defaultLayout)}"
194191

0 commit comments

Comments
 (0)