Files
vercel__vercel-plugin/tests/fuzz-yaml.test.ts

522 lines
17 KiB
TypeScript

import { describe, test, expect } from "bun:test";
import {
extractFrontmatter,
parseSkillFrontmatter,
} from "../hooks/skill-map-frontmatter.mjs";
/**
* Property / fuzz tests for the inline YAML parser (parseSimpleYaml via
* parseSkillFrontmatter) and frontmatter extraction.
* Goal: no crashes or unhandled exceptions from any input.
*/
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Parse YAML frontmatter string; returns result or Error. */
function safeParse(yaml: string): ReturnType<typeof parseSkillFrontmatter> | Error {
try {
return parseSkillFrontmatter(yaml);
} catch (err) {
return err as Error;
}
}
/** Must either return a valid result or throw with a message. */
function expectNoUnhandled(yaml: string) {
const result = safeParse(yaml);
if (result instanceof Error) {
expect(result.message).toBeTruthy();
return;
}
expect(result).toHaveProperty("name");
expect(result).toHaveProperty("description");
expect(result).toHaveProperty("metadata");
}
// ---------------------------------------------------------------------------
// extractFrontmatter edge cases
// ---------------------------------------------------------------------------
describe("fuzz: extractFrontmatter", () => {
test("empty string returns empty yaml and body", () => {
const { yaml, body } = extractFrontmatter("");
expect(yaml).toBe("");
expect(body).toBe("");
});
test("no frontmatter delimiters returns everything as body", () => {
const input = "Just some markdown\nwith no frontmatter";
const { yaml, body } = extractFrontmatter(input);
expect(yaml).toBe("");
expect(body).toBe(input);
});
test("BOM is stripped", () => {
const input = "\uFEFF---\nname: test\n---\nbody";
const { yaml, body } = extractFrontmatter(input);
expect(yaml).toBe("name: test");
expect(body).toBe("body");
});
test("Windows line endings (CRLF)", () => {
const input = "---\r\nname: test\r\n---\r\nbody";
const { yaml, body } = extractFrontmatter(input);
expect(yaml).toBe("name: test");
expect(body).toBe("body");
});
test("frontmatter with only dashes (empty YAML)", () => {
const input = "---\n\n---\nbody";
const { yaml, body } = extractFrontmatter(input);
expect(yaml).toBe("");
expect(body).toBe("body");
});
test("multiple --- blocks returns first match", () => {
const input = "---\nfirst: 1\n---\nmiddle\n---\nsecond: 2\n---\nend";
const { yaml } = extractFrontmatter(input);
expect(yaml).toBe("first: 1");
});
test("--- not at start is not frontmatter", () => {
const input = "some text\n---\nname: test\n---\nbody";
const { yaml, body } = extractFrontmatter(input);
expect(yaml).toBe("");
expect(body).toBe(input);
});
});
// ---------------------------------------------------------------------------
// parseSkillFrontmatter: empty / missing input
// ---------------------------------------------------------------------------
describe("fuzz: parseSkillFrontmatter empty inputs", () => {
test("empty string", () => {
const result = parseSkillFrontmatter("");
expect(result).toEqual({ name: "", description: "", summary: "", metadata: {}, validate: [], chainTo: [] });
});
test("whitespace only", () => {
const result = parseSkillFrontmatter(" \n \n ");
expect(result).toEqual({ name: "", description: "", summary: "", metadata: {}, validate: [], chainTo: [] });
});
test("null/undefined don't crash", () => {
expect(parseSkillFrontmatter(null as any)).toEqual({ name: "", description: "", summary: "", metadata: {}, validate: [], chainTo: [] });
expect(parseSkillFrontmatter(undefined as any)).toEqual({ name: "", description: "", summary: "", metadata: {}, validate: [], chainTo: [] });
});
});
// ---------------------------------------------------------------------------
// Bare values (per CLAUDE.md: bare null/true/false are strings, not JS types)
// ---------------------------------------------------------------------------
describe("fuzz: bare value handling", () => {
test("bare null is string 'null'", () => {
const result = safeParse("name: null");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("null");
}
});
test("bare true is string 'true'", () => {
const result = safeParse("name: true");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("true");
}
});
test("bare false is string 'false'", () => {
const result = safeParse("name: false");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("false");
}
});
test("numeric values coerced to empty string for name field", () => {
// parseSkillFrontmatter coerces non-string values to "" for name
const result = safeParse("name: 42");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("");
}
});
});
// ---------------------------------------------------------------------------
// Tab indentation
// ---------------------------------------------------------------------------
describe("fuzz: tab indentation", () => {
test("tab at start of line throws", () => {
const yaml = "name: test\n\tdescription: bad";
const result = safeParse(yaml);
expect(result).toBeInstanceOf(Error);
expect((result as Error).message).toContain("tab");
});
test("tab in value is fine (not indentation)", () => {
const result = safeParse("name: hello\tworld");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("hello\tworld");
}
});
});
// ---------------------------------------------------------------------------
// Unclosed brackets (per CLAUDE.md: treated as scalar strings)
// ---------------------------------------------------------------------------
describe("fuzz: unclosed brackets", () => {
test("unclosed [ in value is scalar string", () => {
const result = safeParse("name: [unclosed");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("[unclosed");
}
});
test("unclosed [ with content", () => {
const result = safeParse("name: [a, b, c");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("[a, b, c");
}
});
});
// ---------------------------------------------------------------------------
// Quoted strings
// ---------------------------------------------------------------------------
describe("fuzz: quoted strings", () => {
test("single-quoted string", () => {
const result = safeParse("name: 'hello world'");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("hello world");
}
});
test("double-quoted string", () => {
const result = safeParse('name: "hello world"');
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("hello world");
}
});
test("unterminated single quote throws", () => {
const result = safeParse("name: 'hello");
expect(result).toBeInstanceOf(Error);
expect((result as Error).message).toContain("unterminated");
});
test("unterminated double quote throws", () => {
const result = safeParse('name: "hello');
expect(result).toBeInstanceOf(Error);
expect((result as Error).message).toContain("unterminated");
});
test("empty quoted strings", () => {
const r1 = safeParse("name: ''");
expect(r1).not.toBeInstanceOf(Error);
if (!(r1 instanceof Error)) expect(r1.name).toBe("");
const r2 = safeParse('name: ""');
expect(r2).not.toBeInstanceOf(Error);
if (!(r2 instanceof Error)) expect(r2.name).toBe("");
});
test("quotes inside different quote type", () => {
const result = safeParse(`name: "it's a test"`);
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("it's a test");
}
});
});
// ---------------------------------------------------------------------------
// Inline arrays
// ---------------------------------------------------------------------------
describe("fuzz: inline arrays", () => {
test("empty array", () => {
const result = safeParse("metadata:\n pathPatterns: []");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.metadata).toEqual({ pathPatterns: [] });
}
});
test("array with trailing comma throws (empty entry)", () => {
const result = safeParse("metadata:\n pathPatterns: [a, b, ]");
expect(result).toBeInstanceOf(Error);
});
test("array with leading comma throws (empty entry)", () => {
const result = safeParse("metadata:\n pathPatterns: [, a, b]");
expect(result).toBeInstanceOf(Error);
});
test("nested inline arrays are split by top-level commas", () => {
// The inline parser doesn't recursively parse nested brackets within
// the top-level array — brackets inside items are treated as scalars
const result = safeParse("metadata:\n data: [[1, 2], [3, 4]]");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.metadata).toEqual({ data: ["[1", "2]", "[3", "4]"] });
}
});
test("array with quoted commas", () => {
const result = safeParse(`metadata:\n pathPatterns: ["a,b", "c,d"]`);
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.metadata).toEqual({ pathPatterns: ["a,b", "c,d"] });
}
});
});
// ---------------------------------------------------------------------------
// Block arrays (YAML list syntax)
// ---------------------------------------------------------------------------
describe("fuzz: block arrays", () => {
test("standard block array", () => {
const yaml = "metadata:\n pathPatterns:\n - '*.ts'\n - '*.tsx'";
const result = safeParse(yaml);
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.metadata).toEqual({ pathPatterns: ["*.ts", "*.tsx"] });
}
});
test("block array with empty dash items", () => {
const yaml = "metadata:\n pathPatterns:\n -\n -";
const result = safeParse(yaml);
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.metadata).toEqual({ pathPatterns: ["", ""] });
}
});
});
// ---------------------------------------------------------------------------
// Comments
// ---------------------------------------------------------------------------
describe("fuzz: comments", () => {
test("comment-only document is empty", () => {
const result = safeParse("# just a comment\n# another one");
expect(result).toEqual({ name: "", description: "", summary: "", metadata: {}, validate: [], chainTo: [] });
});
test("inline comments are NOT stripped (becomes part of value)", () => {
// The inline YAML parser doesn't strip inline comments
const result = safeParse("name: hello # world");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("hello # world");
}
});
});
// ---------------------------------------------------------------------------
// Indentation edge cases
// ---------------------------------------------------------------------------
describe("fuzz: indentation", () => {
test("inconsistent indentation throws", () => {
const yaml = "metadata:\n priority: 5\n pathPatterns: []";
const result = safeParse(yaml);
expect(result).toBeInstanceOf(Error);
});
test("deeply nested structure", () => {
const yaml = [
"metadata:",
" pathPatterns:",
" - '*.ts'",
" priority: 10",
].join("\n");
const result = safeParse(yaml);
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.metadata).toEqual({ pathPatterns: ["*.ts"], priority: 10 });
}
});
test("leading spaces on top-level key throws", () => {
const result = safeParse(" name: test");
expect(result).toBeInstanceOf(Error);
});
});
// ---------------------------------------------------------------------------
// Empty document / whitespace variations
// ---------------------------------------------------------------------------
describe("fuzz: empty and whitespace documents", () => {
test("newline only", () => {
expectNoUnhandled("\n");
});
test("multiple newlines", () => {
expectNoUnhandled("\n\n\n\n");
});
test("spaces only", () => {
expectNoUnhandled(" ");
});
test("mixed whitespace no content", () => {
expectNoUnhandled(" \n \n ");
});
});
// ---------------------------------------------------------------------------
// Colons in values
// ---------------------------------------------------------------------------
describe("fuzz: colons in values", () => {
test("colon in value (URL)", () => {
const result = safeParse("name: https://example.com");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("https://example.com");
}
});
test("colon at end of key with empty value", () => {
const result = safeParse("name:");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("");
}
});
});
// ---------------------------------------------------------------------------
// Unicode in YAML
// ---------------------------------------------------------------------------
describe("fuzz: unicode in YAML", () => {
test("unicode key name", () => {
expectNoUnhandled("名前: テスト");
});
test("emoji in values", () => {
expectNoUnhandled("name: 🚀 rocket");
});
test("RTL characters", () => {
expectNoUnhandled("name: مرحبا");
});
});
// ---------------------------------------------------------------------------
// Stress: random YAML-like strings
// ---------------------------------------------------------------------------
describe("fuzz: random YAML-like strings", () => {
const chars = "abcABC012: \n-[]'\"#,\t{}|>!&*?@`%";
function randomYaml(len: number, seed: number): string {
let result = "";
let s = seed;
for (let i = 0; i < len; i++) {
s = (s * 1103515245 + 12345) & 0x7fffffff;
result += chars[s % chars.length];
}
return result;
}
test("200 random YAML strings don't cause unhandled exceptions", () => {
for (let i = 0; i < 200; i++) {
const len = (i % 80) + 1;
const yaml = randomYaml(len, i * 31337);
try {
parseSkillFrontmatter(yaml);
} catch (err) {
// Must be a descriptive error, not a raw crash
expect(err).toBeInstanceOf(Error);
expect((err as Error).message).toBeTruthy();
}
}
});
test("random strings through extractFrontmatter + parse pipeline", () => {
for (let i = 0; i < 100; i++) {
const len = (i % 120) + 10;
const raw = `---\n${randomYaml(len, i * 7919)}\n---\nbody`;
try {
const { yaml } = extractFrontmatter(raw);
if (yaml) parseSkillFrontmatter(yaml);
} catch (err) {
expect(err).toBeInstanceOf(Error);
expect((err as Error).message).toBeTruthy();
}
}
});
});
// ---------------------------------------------------------------------------
// Realistic full frontmatter round-trips
// ---------------------------------------------------------------------------
describe("fuzz: realistic frontmatter round-trips", () => {
test("full skill frontmatter parses correctly", () => {
const yaml = [
"name: Next.js",
"description: Framework for React",
"metadata:",
" priority: 10",
" pathPatterns:",
" - 'next.config.*'",
" - 'app/**/*.tsx'",
" - 'pages/**/*.tsx'",
" bashPatterns:",
" - 'npx next'",
" - 'next dev'",
].join("\n");
const result = safeParse(yaml);
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("Next.js");
expect(result.description).toBe("Framework for React");
expect(result.metadata).toEqual({
priority: 10,
pathPatterns: ["next.config.*", "app/**/*.tsx", "pages/**/*.tsx"],
bashPatterns: ["npx next", "next dev"],
});
}
});
test("frontmatter with only name", () => {
const result = safeParse("name: minimal");
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.name).toBe("minimal");
expect(result.metadata).toEqual({});
}
});
test("metadata is not object (array) falls back to empty", () => {
const yaml = "name: test\nmetadata:\n - item1\n - item2";
const result = safeParse(yaml);
expect(result).not.toBeInstanceOf(Error);
if (!(result instanceof Error)) {
expect(result.metadata).toEqual({});
}
});
});