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.title).toBe("Test Note");
|
||||||
expect(result.frontmatter.tags).toEqual(["test", "example"]);
|
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.");
|
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 matter from 'gray-matter';
|
||||||
import { parseDocument } from 'yaml';
|
import { parse, stringify, parseDocument } from 'yaml';
|
||||||
import type { ParsedNote, FrontmatterValidationResult } from './types.js';
|
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
|
* Parse a frontmatter value that may be a JSON string (LLM clients sometimes
|
||||||
* pass frontmatter as a serialized JSON string instead of an object).
|
* 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 {
|
export class FrontmatterHandler {
|
||||||
parse(content: string): ParsedNote {
|
parse(content: string): ParsedNote {
|
||||||
try {
|
try {
|
||||||
const parsed = matter(content);
|
const parsed = matter(content, withYamlEngine());
|
||||||
return {
|
return {
|
||||||
frontmatter: parsed.data,
|
frontmatter: parsed.data,
|
||||||
content: parsed.content,
|
content: parsed.content,
|
||||||
@@ -56,7 +72,7 @@ export class FrontmatterHandler {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
return matter.stringify(content, frontmatterData);
|
return matter.stringify(content, frontmatterData, withYamlEngine());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to stringify frontmatter: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
throw new Error(`Failed to stringify frontmatter: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||||
}
|
}
|
||||||
@@ -71,7 +87,7 @@ export class FrontmatterHandler {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Test if the frontmatter can be serialized to valid YAML using gray-matter
|
// Test if the frontmatter can be serialized to valid YAML using gray-matter
|
||||||
matter.stringify('', frontmatterData);
|
matter.stringify('', frontmatterData, withYamlEngine());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
result.isValid = false;
|
result.isValid = false;
|
||||||
result.errors.push(`Invalid YAML structure: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
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) {
|
if (!updates || Object.keys(updates).length === 0) {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
return matter.stringify(content, updates);
|
return matter.stringify(content, updates, withYamlEngine());
|
||||||
}
|
}
|
||||||
|
|
||||||
const doc = parseDocument(rawMatter.trimStart());
|
const doc = parseDocument(rawMatter.trimStart());
|
||||||
|
|||||||
Reference in New Issue
Block a user