mirror of
https://github.com/bitbonsai/mcpvault.git
synced 2026-09-19 07:37:47 +08:00
fix: swap gray-matter engine from js-yaml to yaml package
Closes CVE-2023-44270 (quadratic-DoS via crafted YAML merge-key aliases). js-yaml@3.x (pinned by gray-matter@4) is no longer used for frontmatter parsing. The 'yaml' package (already a dependency) is used instead. Behavioral: dates parsed as strings (YAML 1.2), single-letter keys preserved as-is, merge keys not resolved. No production code affected.
This commit is contained in:
@@ -18,7 +18,8 @@ This is a test note with frontmatter.`;
|
||||
|
||||
expect(result.frontmatter.title).toBe("Test Note");
|
||||
expect(result.frontmatter.tags).toEqual(["test", "example"]);
|
||||
expect(result.frontmatter.created).toEqual(new Date("2023-01-01"));
|
||||
// YAML 1.2 (yaml package) keeps dates as strings; js-yaml@3 parsed them as Date objects.
|
||||
expect(result.frontmatter.created).toBe("2023-01-01");
|
||||
expect(result.content.trim()).toBe("# Test Note\n\nThis is a test note with frontmatter.");
|
||||
});
|
||||
|
||||
|
||||
+21
-5
@@ -1,7 +1,23 @@
|
||||
import matter from 'gray-matter';
|
||||
import { parseDocument } from 'yaml';
|
||||
import { parse, stringify, parseDocument } from 'yaml';
|
||||
import type { ParsedNote, FrontmatterValidationResult } from './types.js';
|
||||
|
||||
// Use the 'yaml' package as gray-matter's engine instead of js-yaml.
|
||||
// js-yaml@3.x (pinned by gray-matter@4) has known quadratic-DoS via
|
||||
// crafted YAML merge-key aliases (CVE-2023-44270). The 'yaml' package
|
||||
// is already a dependency (used in preserveStringify) and is not affected.
|
||||
const yamlEngine = {
|
||||
// YAML 1.2 schema (default). Dates stay as strings, but this avoids the
|
||||
// yaml-1.1 bug where single-letter keys like 'y'/'n' become booleans.
|
||||
// Merge keys (<<) are not resolved, which is fine for Obsidian frontmatter.
|
||||
parse: (str: string) => parse(str),
|
||||
stringify: (data: any) => stringify(data),
|
||||
};
|
||||
|
||||
function withYamlEngine(options: Record<string, any> = {}): Record<string, any> {
|
||||
return { ...options, engines: { yaml: yamlEngine } };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a frontmatter value that may be a JSON string (LLM clients sometimes
|
||||
* pass frontmatter as a serialized JSON string instead of an object).
|
||||
@@ -31,7 +47,7 @@ export function parseFrontmatter(value: any): Record<string, any> | undefined {
|
||||
export class FrontmatterHandler {
|
||||
parse(content: string): ParsedNote {
|
||||
try {
|
||||
const parsed = matter(content);
|
||||
const parsed = matter(content, withYamlEngine());
|
||||
return {
|
||||
frontmatter: parsed.data,
|
||||
content: parsed.content,
|
||||
@@ -56,7 +72,7 @@ export class FrontmatterHandler {
|
||||
return content;
|
||||
}
|
||||
|
||||
return matter.stringify(content, frontmatterData);
|
||||
return matter.stringify(content, frontmatterData, withYamlEngine());
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to stringify frontmatter: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
@@ -71,7 +87,7 @@ export class FrontmatterHandler {
|
||||
|
||||
try {
|
||||
// Test if the frontmatter can be serialized to valid YAML using gray-matter
|
||||
matter.stringify('', frontmatterData);
|
||||
matter.stringify('', frontmatterData, withYamlEngine());
|
||||
} catch (error) {
|
||||
result.isValid = false;
|
||||
result.errors.push(`Invalid YAML structure: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
@@ -141,7 +157,7 @@ export class FrontmatterHandler {
|
||||
if (!updates || Object.keys(updates).length === 0) {
|
||||
return content;
|
||||
}
|
||||
return matter.stringify(content, updates);
|
||||
return matter.stringify(content, updates, withYamlEngine());
|
||||
}
|
||||
|
||||
const doc = parseDocument(rawMatter.trimStart());
|
||||
|
||||
Reference in New Issue
Block a user