Skip to content

Commit a75e866

Browse files
committed
fix: harden frontmatter fence sanitization
1 parent 95d0aca commit a75e866

2 files changed

Lines changed: 14 additions & 2 deletions

File tree

src/lib/ingest-sanitize.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ describe("sanitizeIngestedFileContent", () => {
3535
expect(sanitizeIngestedFileContent(input)).toBe("---\ntype: x\n---\n\n# Body")
3636
})
3737

38+
it("strips a frontmatter fence after leading blank lines with a case-insensitive label", () => {
39+
const input = "\n \n```YAML\n---\ntype: x\n---\n```\n# Body"
40+
expect(sanitizeIngestedFileContent(input)).toBe("---\ntype: x\n---\n# Body")
41+
})
42+
43+
it("strips a CRLF fence around empty frontmatter", () => {
44+
const input = "```yaml\r\n---\r\n---\r\n```\r\n\r\n# Body"
45+
expect(sanitizeIngestedFileContent(input)).toBe("---\r\n---\r\n\r\n# Body")
46+
})
47+
3848
it("does NOT strip a non-fence-wrapped document containing a fenced code block in the body", () => {
3949
const input =
4050
"---\ntype: x\n---\n\n# Heading\n\n```js\nconsole.log('hi')\n```\n\nmore body"

src/lib/ingest-sanitize.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ export function sanitizeIngestedFileContent(content: string): string {
9090

9191
/** Top-level fence wrapper. Removes the open + matching close fence lines. */
9292
function stripOuterCodeFence(content: string): string {
93-
const open = content.match(/^[ \t]*```(?:yaml|md|markdown)?[ \t]*\r?\n/)
93+
const open = content.match(
94+
/^(?:\uFEFF)?(?:[ \t]*\r?\n)*[ \t]*```(?:yaml|md|markdown)?[ \t]*\r?\n/i,
95+
)
9496
if (!open) return content
9597
const afterOpen = content.slice(open[0].length)
9698

@@ -103,7 +105,7 @@ function stripOuterCodeFence(content: string): string {
103105
// continue with an unfenced Markdown body. Only strip this shape when
104106
// the fenced section is exactly a complete `---` frontmatter block.
105107
const frontmatterOnly = afterOpen.match(
106-
/^(---[ \t]*\r?\n[\s\S]*?\r?\n---[ \t]*\r?\n)[ \t]*```[ \t]*(?:\r?\n|$)/,
108+
/^(---[ \t]*\r?\n[\s\S]*?^---[ \t]*\r?\n)[ \t]*```[ \t]*(?:\r?\n|$)/m,
107109
)
108110
if (!frontmatterOnly) return content
109111
return frontmatterOnly[1] + afterOpen.slice(frontmatterOnly[0].length)

0 commit comments

Comments
 (0)