Skip to content

Add fuzz tests for frontmatter YAML parser#3851

Merged
pelikhan merged 9 commits into
mainfrom
copilot/add-fuzz-tests-frontmatter-parser
Nov 13, 2025
Merged

Add fuzz tests for frontmatter YAML parser#3851
pelikhan merged 9 commits into
mainfrom
copilot/add-fuzz-tests-frontmatter-parser

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Nov 13, 2025

Plan: Add Fuzz Tests for Frontmatter YAML Parser

Objective

Implement native Go fuzz testing for the frontmatter YAML parser to discover edge cases and potential security vulnerabilities in user input handling.

Checklist

  • Create pkg/parser/frontmatter_fuzz_test.go with comprehensive seed corpus
  • Add valid YAML frontmatter samples from existing tests
  • Add invalid/malicious YAML samples (deeply nested, very long strings, special characters)
  • Add edge cases (empty input, only delimiters, malformed YAML)
  • Verify parser never panics or hangs on arbitrary input
  • Ensure proper error returns for invalid input
  • Test with go test -fuzz=FuzzParseFrontmatter -fuzztime=10s
  • Document any discovered edge cases in comments
  • Run full test suite to ensure no regressions
  • Add make fuzz target to run all fuzz tests
  • Merge main branch
  • Integrate with CI fuzz job
  • Address code review feedback

Implementation Summary

Created pkg/parser/frontmatter_fuzz_test.go with 45 seed test cases covering:

  • Valid YAML frontmatter (simple, complex, nested structures)
  • Invalid YAML (missing colons, unclosed brackets, malformed quotes, duplicate keys)
  • Edge cases (empty frontmatter, very long strings/keys, deeply nested, Unicode)
  • Special YAML features (anchors, aliases, merge keys, multiline strings)
  • Malformed delimiters and control characters

Fixed bug in pkg/parser/frontmatter.go:

  • yaml.Unmarshal sets map to nil for empty YAML
  • Added check to initialize empty map when nil to prevent nil pointer issues

Added make fuzz target:

  • Runs all fuzz tests for 30 seconds each
  • Tests both pkg/parser (FuzzParseFrontmatter) and pkg/workflow (FuzzExpressionParser)
  • Uses specific test names to avoid "matches more than one fuzz test" error
  • Documented in Makefile help

Integrated with CI:

  • Updated .github/workflows/ci.yml fuzz job to run both fuzz tests
  • FuzzParseFrontmatter runs for 10s in CI
  • FuzzExpressionParser continues to run for 10s in CI
  • Both tests run in the CI pipeline fuzz job

Code review fixes:

  • Added strconv import for proper integer-to-string conversion
  • Replaced string(rune(i%10+'0')) with strconv.Itoa(i%10) for clarity and correctness
  • Fixed Makefile fuzz target to use specific test names instead of wildcard

Merged main branch:

  • Successfully merged latest changes from main
  • All tests pass after merge
  • Fuzz tests continue to work correctly

Test Results:

  • Fuzz test runs successfully for 10 seconds
  • Executed 74,446 test cases
  • Discovered 55 interesting cases (edge cases that expand coverage)
  • All unit tests pass
  • Parser handles all fuzzer-generated inputs without panic
  • make fuzz executes both parser and workflow fuzz tests
  • CI fuzz job now runs both fuzz tests
  • All linting passes

Files Modified

  • Created: pkg/parser/frontmatter_fuzz_test.go (comprehensive fuzz test)
  • Modified: pkg/parser/frontmatter.go (fix nil map initialization)
  • Modified: Makefile (add fuzz target with specific test names)
  • Modified: .github/workflows/ci.yml (integrate new fuzz test into CI)
  • Merged: Latest changes from main branch
Original prompt

This section details on the original issue you should resolve

<issue_title>[task] Add fuzz tests for frontmatter YAML parser</issue_title>
<issue_description>## Objective
Implement native Go fuzz testing for the frontmatter YAML parser to discover edge cases and potential security vulnerabilities in user input handling.

Context

This is part of the testing quality improvement initiative (discussion #3811). The frontmatter parser handles user-provided YAML in workflow files, making it a security-critical component. Fuzz testing can discover malformed input handling issues that manual testing might miss.

Approach

  1. Create pkg/parser/frontmatter_fuzz_test.go
  2. Implement func FuzzParseFrontmatter(f *testing.F)
  3. Seed the fuzzer with:
    • Valid YAML frontmatter samples from existing tests
    • Invalid/malicious YAML (deeply nested structures, very long strings, special characters)
    • Edge cases (empty input, only delimiters, malformed YAML)
  4. Verify the parser never panics or hangs on arbitrary input
  5. Ensure proper error returns for invalid input

Files to Modify

  • Create: pkg/parser/frontmatter_fuzz_test.go
  • Reference: pkg/parser/frontmatter_test.go (for seed data)
  • Reference: pkg/parser/frontmatter.go (parser implementation)

Acceptance Criteria

  • Fuzz test function created with appropriate seed corpus
  • Test includes both valid and invalid YAML samples as seeds
  • Parser handles all fuzzer-generated inputs without panic
  • Errors are properly returned for invalid input
  • Test runs successfully with go test -fuzz=FuzzParseFrontmatter -fuzztime=10s
  • Any discovered edge cases are documented in comments

Related

Part of testing improvement task 1 from discussion #3811
Related to #3811

AI generated by Plan Command for discussion #3811</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits November 13, 2025 11:58
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot

- Add check in ExtractFrontmatterFromContent to ensure frontmatter map is never nil
- yaml.Unmarshal sets map to nil for empty YAML, now initialize empty map instead
- Fuzz test now passes with 10s run (74k+ executions, 55 interesting cases found)
- All unit tests pass

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 13, 2025

@copilot

Fixed the fuzz test implementation. The issue was that yaml.Unmarshal sets the map to nil when parsing empty YAML frontmatter. Added a check to always initialize an empty map when this occurs.

Results:

  • ✅ Fuzz test passes with 10s run (74k+ executions, 55 interesting cases discovered)
  • ✅ All unit tests pass
  • ✅ Parser handles all fuzzer-generated inputs without panic

Commit: 97b6c7a

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot make sure new fuzz test runs with "make fuzz"

- Added fuzz target to Makefile that runs fuzz tests for 30s in parser and workflow packages
- Updated help documentation to include fuzz command
- Tests FuzzParseFrontmatter in pkg/parser and FuzzExpressionParser in pkg/workflow

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 13, 2025

@copilot make sure new fuzz test runs with "make fuzz"

Added make fuzz target that runs all fuzz tests for 30 seconds. It executes:

  • FuzzParseFrontmatter in pkg/parser (frontmatter YAML parser)
  • FuzzExpressionParser in pkg/workflow (GitHub expression parser)

The target is documented in make help.

Commit: 56e39ae

Copilot AI changed the title [WIP] Add fuzz tests for frontmatter YAML parser Add fuzz tests for frontmatter YAML parser Nov 13, 2025
@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot merge main

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 13, 2025

@copilot merge main

Merged main branch successfully. All tests pass after merge, including the fuzz tests.

Merge commit: b38deb9

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot integrate new fuzz test with existing fuzz test suite and make sure it runs in ci.yml fuzz job

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 13, 2025

@copilot integrate new fuzz test with existing fuzz test suite and make sure it runs in ci.yml fuzz job

Integrated FuzzParseFrontmatter into CI workflow. Updated .github/workflows/ci.yml fuzz job to run both:

  • FuzzParseFrontmatter in pkg/parser (10s)
  • FuzzExpressionParser in pkg/workflow (10s)

Both fuzz tests are also available via make fuzz locally (runs for 30s each).

Commit: cb10108 (note: experiencing GitHub server error on push, commit is ready locally)

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot do it again and try to push

- Updated .github/workflows/ci.yml to run both fuzz tests
- FuzzParseFrontmatter now runs in CI alongside FuzzExpressionParser
- Each fuzz test runs for 10 seconds in CI

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 13, 2025

@copilot do it again and try to push

Successfully pushed! The CI integration is now in place. Updated .github/workflows/ci.yml to run both fuzz tests:

  • FuzzParseFrontmatter in pkg/parser (10s)
  • FuzzExpressionParser in pkg/workflow (10s)

Commit: c70dc0c

@pelikhan pelikhan marked this pull request as ready for review November 13, 2025 15:54
Copilot AI review requested due to automatic review settings November 13, 2025 15:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds comprehensive fuzz testing for the frontmatter YAML parser to discover edge cases and security vulnerabilities in user input handling. The implementation follows the existing pattern established by FuzzExpressionParser in the workflow package and integrates fuzz tests into both the Makefile and CI workflow.

Key Changes:

  • Created FuzzParseFrontmatter with 45 seed test cases covering valid YAML, invalid syntax, edge cases, Unicode, special characters, and malformed delimiters
  • Fixed a nil pointer bug in frontmatter.go where yaml.Unmarshal returns nil for empty YAML
  • Added make fuzz target to run all fuzz tests for 30 seconds each

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
pkg/parser/frontmatter_fuzz_test.go New fuzz test with comprehensive seed corpus (589 lines) covering valid/invalid YAML, edge cases, and helper validation functions
pkg/parser/frontmatter.go Bug fix: initialize empty map when yaml.Unmarshal returns nil for empty YAML frontmatter
Makefile Add fuzz target to run fuzz tests for 30 seconds and update help documentation
.github/workflows/ci.yml Integrate FuzzParseFrontmatter into CI fuzz job to run for 10 seconds alongside FuzzExpressionParser

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/parser/frontmatter_fuzz_test.go Outdated
// Deeply nested structure (overly deep - may hit limits)
deepNested := "---\nname: Test\ndata:\n"
for i := 0; i < 100; i++ {
deepNested += strings.Repeat(" ", i+1) + "level" + string(rune(i%10+'0')) + ":\n"
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conversion string(rune(i%10+'0')) is incorrect for generating numeric characters. When i%10 is between 0-9, adding '0' (which is 48) gives 48-57, but the issue is that the code tries to convert an integer to a rune to a string incorrectly.

This should use strconv.Itoa(i%10) or format the number properly:

deepNested += strings.Repeat("  ", i+1) + "level" + strconv.Itoa(i%10) + ":\n"

The current code happens to work because it's generating runes in the ASCII digit range, but it's semantically incorrect and confusing.

Copilot uses AI. Check for mistakes.
Comment thread pkg/parser/frontmatter_fuzz_test.go Outdated
// Very long array with many items
longArray := "---\nname: Test\nitems:\n"
for i := 0; i < 1000; i++ {
longArray += " - item" + string(rune(i%10+'0')) + "\n"
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as line 200: string(rune(i%10+'0')) is an incorrect conversion pattern. Should use strconv.Itoa(i%10) instead:

longArray += "  - item" + strconv.Itoa(i%10) + "\n"

This makes the intent clearer and is the proper way to convert integers to strings in Go.

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +6
import (
"strings"
"testing"
)
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing strconv import. The code at lines 200 and 441 should use strconv.Itoa() for integer-to-string conversion instead of the incorrect string(rune(i%10+'0')) pattern. Add:

import (
	"strconv"
	"strings"
	"testing"
)

Copilot uses AI. Check for mistakes.
Comment thread Makefile Outdated
Comment on lines +78 to +79
go test -fuzz=. -fuzztime=30s ./pkg/parser/...
go test -fuzz=. -fuzztime=30s ./pkg/workflow/...
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fuzz target uses -fuzz=. which attempts to match all fuzz tests in a package. However, Go's fuzzing engine can only execute one fuzz test at a time. When multiple fuzz tests match the pattern, it will error with "testing: will not fuzz, -fuzz matches more than one fuzz test".

The CI configuration correctly uses specific test names (-fuzz=FuzzParseFrontmatter and -fuzz=FuzzExpressionParser), but the Makefile target should follow the same pattern.

Recommended fix:

fuzz:
	@echo "Running fuzz tests for 30 seconds..."
	go test -fuzz=FuzzParseFrontmatter -fuzztime=30s ./pkg/parser/
	go test -fuzz=FuzzExpressionParser -fuzztime=30s ./pkg/workflow/

This ensures each fuzz test runs sequentially with its own 30-second time budget, matching the CI behavior.

Suggested change
go test -fuzz=. -fuzztime=30s ./pkg/parser/...
go test -fuzz=. -fuzztime=30s ./pkg/workflow/...
go test -fuzz=FuzzParseFrontmatter -fuzztime=30s ./pkg/parser/
go test -fuzz=FuzzExpressionParser -fuzztime=30s ./pkg/workflow/

Copilot uses AI. Check for mistakes.
@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot lint

- Add strconv import for proper integer-to-string conversion
- Replace string(rune(i%10+'0')) with strconv.Itoa(i%10) at lines 200 and 441
- Fix Makefile fuzz target to use specific test names instead of wildcard pattern
- Ensures each fuzz test runs sequentially with proper 30s time budget

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 13, 2025

@copilot lint

Linting complete. All code review issues have been addressed:

  1. ✅ Added strconv import
  2. ✅ Fixed integer-to-string conversion using strconv.Itoa() instead of string(rune(i%10+'0'))
  3. ✅ Fixed Makefile fuzz target to use specific test names (-fuzz=FuzzParseFrontmatter and -fuzz=FuzzExpressionParser) instead of wildcard pattern
  4. ✅ All linting passes
  5. ✅ All tests pass

Commit: c1bb1eb

@pelikhan pelikhan merged commit d5c6464 into main Nov 13, 2025
41 of 47 checks passed
@pelikhan pelikhan deleted the copilot/add-fuzz-tests-frontmatter-parser branch November 13, 2025 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[task] Add fuzz tests for frontmatter YAML parser

3 participants