Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ jobs:
- name: Run tests
run: task test

fuzz:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'

- name: Install Task
uses: arduino/setup-task@v3
with:
version: 3.x

- name: Run lightweight fuzz tests
run: task fuzz-ci

build:
runs-on: ubuntu-latest
steps:
Expand Down
25 changes: 25 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,24 @@ This is `gitgrab`, a CLI utility written in Go that clones all GitHub repositori
- Tests use `httptest.NewRecorder()` for mock HTTP responses
- Current test coverage: ~70%

### Security fuzz tests (`fuzz_test.go`)

Native Go fuzz targets cover the untrusted-input boundaries and assert security
invariants (path containment, no argument/command injection, safe URL/name
handling, robust JSON decoding). They run in two tiers:

- **Lightweight (CI)**: `task fuzz-ci` runs a short, time-bounded burst per
target (`FUZZTIME` default `15s`) and is wired into the `fuzz` job in
`.github/workflows/ci.yml`. The seed corpora also run as normal tests under
`task test`.
- **Heavyweight (ad hoc)**: `task fuzz` runs each target for a long duration
(`FUZZTIME` default `5m`, e.g. `task fuzz FUZZTIME=30m`) on a developer
machine.

Both tiers loop over every `Fuzz*` function automatically (targets are
discovered by grep in the `fuzz-run` internal task), since `go test -fuzz`
only fuzzes one target per invocation.

## Development Commands

**Build the application (preferred method):**
Expand Down Expand Up @@ -70,6 +88,13 @@ task coverage
go test . -run TestName
```

**Run fuzz tests:**
```bash
task fuzz-ci # Lightweight, time-bounded (CI tier)
task fuzz # Heavyweight ad hoc (defaults to 5m per target)
task fuzz FUZZTIME=30m # Override per-target duration
```

**Security and code quality checks:**
```bash
task check # Run all security scans
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@ task build
# The binary will be created at .build/gitgrab
```

## Security fuzz testing

GitGrab is fuzz-tested at its untrusted-input boundaries (the clone-method
flag, GitHub-supplied repository/organization names and URLs, path resolution,
clone-URL construction, and API JSON decoding) to guard against path traversal
and command/argument injection. The fuzz targets live in `fuzz_test.go` and
come in two tiers:

```bash
# Lightweight, time-bounded burst — runs in CI on every push/PR
task fuzz-ci

# Heavyweight ad hoc run on a developer machine (defaults to 5m per target)
task fuzz

# Override the per-target duration
task fuzz FUZZTIME=30m
```

The seed corpora also execute as ordinary unit tests during `task test`, so the
malicious inputs are checked on every test run even without a fuzzing pass.

## Requirements

- Go 1.24+
Expand Down
25 changes: 25 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,31 @@ tasks:
cmds:
- go test ./... -cover

fuzz-ci:
desc: "Lightweight, time-bounded fuzzing for CI (short burst per target)"
cmds:
- task: fuzz-run
vars: { FUZZTIME: "{{.FUZZTIME | default \"15s\"}}" }

fuzz:
desc: "Heavyweight ad hoc fuzzing (override duration with FUZZTIME, e.g. FUZZTIME=5m)"
cmds:
- task: fuzz-run
vars: { FUZZTIME: "{{.FUZZTIME | default \"5m\"}}" }

fuzz-run:
internal: true
desc: "Runs every Fuzz* target for FUZZTIME each (go fuzz allows one target per run)"
vars:
FUZZTIME: '{{.FUZZTIME | default "15s"}}'
TARGETS:
sh: grep -rhoE '^func (Fuzz[A-Za-z0-9_]+)' *.go | awk '{print $2}'
cmds:
- for: { var: TARGETS }
cmd: |
echo "==> Fuzzing {{.ITEM}} for {{.FUZZTIME}}"
go test -run '^$' -fuzz '^{{.ITEM}}$' -fuzztime={{.FUZZTIME}} .

check:
desc: "Run all security scans"
deps: [ sast, vet, vuln ]
Expand Down
Loading