Skip to content

feat: add security fuzz tests and harden input sinks#20

Merged
scottbrown merged 1 commit into
mainfrom
scott.brown/add-security-fuzz-tests
Jul 8, 2026
Merged

feat: add security fuzz tests and harden input sinks#20
scottbrown merged 1 commit into
mainfrom
scott.brown/add-security-fuzz-tests

Conversation

@scottbrown

Copy link
Copy Markdown
Owner

Summary

Adds native Go fuzz tests that harden gitgrab against path traversal and command/argument injection at its untrusted-input boundaries, split into a lightweight CI tier and a heavyweight ad-hoc tier.

While building the tests I found the type-safety validators (IsValid()) were defined but never called — untrusted GitHub API data and CLI flags flowed straight into filepath.Join and exec.Command("git", "clone", ...). Fuzz tests that only asserted current behavior wouldn't harden anything, so this PR closes the holes first, then fuzzes the invariants.

Hardening (gitgrab.go)

  • Name validators → strict allowlists matching GitHub's real rules; reject ./.., path separators, leading/trailing -, and control/whitespace characters.
  • URL validators → reject leading - (git option injection) and embedded whitespace/control chars (command splitting).
  • New pure choke-point helpers, all fuzzable and wired into CloneRepo/FetchAllRepos:
    • resolveRepoPath — validates the repo name and confirms the joined path stays directly within the target dir.
    • buildCloneURL — validates every untrusted component before constructing/returning a clone URL.
    • parseRepos — strict JSON decode of API responses (rejects trailing garbage).
  • git clone -- URL PATH separator so a --prefixed value can't be interpreted as a flag.

Fuzz targets (fuzz_test.go)

Six targets asserting security invariants (not fixed outputs), each with a malicious seed corpus (traversal, command substitution, ext::sh, --upload-pack=, NUL bytes, oversized inputs):

FuzzParseCloneMethod, FuzzRepositoryNameValidation, FuzzOrganizationNameValidation, FuzzResolveRepoPath, FuzzBuildCloneURL, FuzzParseRepos.

Two tiers

  • Lightweight / CItask fuzz-ci (short burst per target, default FUZZTIME=15s), wired into a new fuzz job in .github/workflows/ci.yml. Seed corpora also run under task test on every push/PR.
  • Heavyweight / ad hoctask fuzz (default 5m/target, e.g. task fuzz FUZZTIME=30m).

Both tiers auto-discover every Fuzz* target (grep in the internal fuzz-run task), since go test -fuzz fuzzes one target per invocation.

Testing

  • go build, go vet, task test all pass.
  • Heavyweight fuzz run: millions of executions per target, zero crashers, no crash corpus persisted.
  • Existing unit tests unchanged and passing.

🤖 Generated with Claude Code

Add native Go fuzz tests covering the untrusted-input boundaries (clone
method flag, repository/organization names and URLs, path resolution,
clone-URL construction, and API JSON decoding) to guard against path
traversal and command/argument injection.

The type-safety validators (IsValid) existed but were never called, so
untrusted GitHub API data flowed straight into filepath.Join and
exec.Command. Harden those sinks first so the fuzz tests assert real
security invariants:

- Tighten name validators to strict allowlists (reject "."/"..", path
  separators, leading/trailing '-', control/whitespace chars).
- Strengthen URL validators to reject leading '-' and embedded
  whitespace/control chars.
- Add pure choke-point helpers resolveRepoPath, buildCloneURL, and
  parseRepos, wired into CloneRepo/FetchAllRepos.
- Use `git clone --` separator to prevent argument injection.

Fuzzing is split into two tiers:
- Lightweight (CI): `task fuzz-ci` runs a short burst per target and is
  wired into a new fuzz job in ci.yml; seed corpora also run under
  `task test`.
- Heavyweight (ad hoc): `task fuzz` runs each target for a long,
  configurable duration (FUZZTIME, default 5m).

Both tiers auto-discover every Fuzz* target since `go test -fuzz` fuzzes
one target per invocation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@scottbrown scottbrown merged commit a3c1c09 into main Jul 8, 2026
6 checks passed
@scottbrown scottbrown deleted the scott.brown/add-security-fuzz-tests branch July 8, 2026 19:06
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.

1 participant