package jobspec import ( "strings" "testing" ) // FuzzParseMarkdownRoundTrip is the REQ-067 fuzz harness for R-015 // byte-exact body preservation. It generates random frontmatter + body // combinations, runs ParseMarkdown, and asserts that the parsed Body // equals the original body byte-for-byte whenever parsing succeeds. // When parsing fails (bad frontmatter), the iteration passes — the // parser is allowed to reject malformed input. // // The seed corpus (added via f.Add) covers adversarial fixtures: CRLF // body, BOM prefix, no frontmatter, only-closing-separator, body with // `---` inside a code fence, trailing whitespace, empty body. The seed // corpus runs as regular tests under `go test` (CI); random input runs // only under `go test -fuzz=FuzzParseMarkdownRoundTrip` in a dedicated // process. func FuzzParseMarkdownRoundTrip(f *testing.F) { // Seed 1: valid frontmatter + simple body. f.Add([]byte("---\nkind: Job\nname: seed1\n---\n# body\n")) // Seed 2: CRLF body. f.Add([]byte("---\r\nkind: Job\r\nname: seed2\r\n---\r\n# body\r\nCRLF\r\n")) // Seed 3: BOM prefix. f.Add([]byte("\uFEFF---\nkind: Job\nname: seed3\n---\nbody\n")) // Seed 4: no frontmatter (just body) — should fail to parse. f.Add([]byte("# just a body\nno frontmatter\n")) // Seed 5: frontmatter with only the closing `---` (no opening). f.Add([]byte("body\n---\nmore body\n")) // Seed 6: body containing `---` in a code fence. f.Add([]byte("---\nkind: Job\nname: seed6\n---\n```bash\necho '---'\n```\n")) // Seed 7: body with trailing whitespace. f.Add([]byte("---\nkind: Job\nname: seed7\n---\nbody with trailing spaces \n")) // Seed 8: empty body. f.Add([]byte("---\nkind: Job\nname: seed8\n---\n")) // Seed 9: empty frontmatter (should fail). f.Add([]byte("---\n---\nbody\n")) // Seed 10: body with no trailing newline. f.Add([]byte("---\nkind: Job\nname: seed10\n---\nno trailing newline")) f.Fuzz(func(t *testing.T, data []byte) { // Reconstruct the body from the input so we can assert // byte-exact round-trip. We do this by re-splitting the // frontmatter using the same logic the parser uses, but only // to extract the expected body. If the input has no valid // frontmatter delimiter pair, ParseMarkdown will return an // error and we pass the iteration. expectedBody := extractExpectedBody(string(data)) spec, err := ParseMarkdown(data) if err != nil { // Parser rejected the input — acceptable for a fuzz // iteration (the input may be malformed). Pass. return } // R-015: body must be byte-exact. if spec.Body != expectedBody { t.Errorf("R-015 body round-trip mismatch:\n got = %q\nwant = %q", spec.Body, expectedBody) } }) } // extractExpectedBody returns the body portion of a Markdown jobspec // input using the same delimiter-splitting logic as splitFrontmatter, // so the fuzz harness can assert byte-exact preservation independently // of the parser's internal extraction. If the input has no valid // frontmatter, the result is "" (and ParseMarkdown will error). func extractExpectedBody(content string) string { stripped := content if strings.HasPrefix(stripped, "\uFEFF") { stripped = stripped[len("\uFEFF"):] } trimmed := strings.TrimLeft(stripped, "\r\n\t ") if !strings.HasPrefix(trimmed, "---") { return "" } rest := trimmed[3:] if len(rest) > 0 && rest[0] != '\n' && rest[0] != '\r' { return "" } rest = strings.TrimLeft(rest, "\r\n") idx := findClosingDelimiter(rest) if idx < 0 { return "" } afterClose := rest[idx:] newlineIdx := strings.IndexAny(afterClose, "\r\n") if newlineIdx < 0 { return "" } bodyStart := newlineIdx if strings.HasPrefix(afterClose[bodyStart:], "\r\n") { bodyStart += 2 } else { bodyStart += 1 } return afterClose[bodyStart:] }