Skip to content

test(fuzz): coverage-guided fuzzing for untrusted-input surfaces - #37

Closed
seonghobae wants to merge 4 commits into
mainfrom
feat/add-fuzzing
Closed

seonghobae wants to merge 4 commits into
mainfrom
feat/add-fuzzing

Conversation

@seonghobae

Copy link
Copy Markdown
Contributor

Summary

Adds coverage-guided fuzzing (cargo-fuzz / libFuzzer) plus a stable proptest mirror for the gateway's attacker-controlled and untrusted-config surfaces. Targets were selected with CodeGraph (codegraph explore), which surfaced the request scorer, the persisted-state deserializer, the admin-token config parser, and the DNSBL zone generator as the reachable, no-covering-test entry points for arbitrary input.

Fuzz targets (fuzz/)

Target Surface Invariants
fuzz_score_request waf_ids_core::score_request no panic; reason non-empty; deterministic scoring
fuzz_appdata_json serde_json::from_str::<AppData> (state file) no panic; serde round-trip stable
fuzz_parse_admin_tokens waf_ids_ai_soc::parse_admin_tokens (ADMIN_TOKENS) no panic; no empty token key/actor
fuzz_dnsbl_zone export_dnsbl_zone / validate_dnsbl no panic; TXT payloads fully escaped (no zone break-out)

Each target ships a seed corpus of real attack payloads (SQLi, XSS, traversal, cmd-injection, SSRF, JNDI, percent-encoded) and malformed/edge-case inputs.

Keeping primary CI green

  • The fuzz/ crate is an isolated cargo workspace, so cargo test --workspace at the repo root never builds it (no nightly needed for normal CI).
  • crates/waf-ids-core/tests/fuzz_invariants.rs and tests/fuzz_invariants.rs re-assert the same invariants via proptest on stable, so they run in the existing test job. cargo fmt --check, cargo test --locked --workspace, and cargo clippy --locked --workspace --all-targets -- -D warnings all pass.

CI job

.github/workflows/fuzz.yml runs each target on a bounded budget — 60s/target on PRs, 300s/target nightly (cron) + manual dispatch — uploading crash artifacts on failure, so fuzzing cost stays predictable.

Licensing

All added dependencies are permissive (MIT OR Apache-2.0): cargo-fuzz, libfuzzer-sys, arbitrary, proptest. No GPL/AGPL. No runtime secrets. Fuzzing survey paper added under docs/papers/; usage in docs/fuzzing.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RTAMs4bpSZS77Xe3RQjv9P

Add cargo-fuzz (libFuzzer) harnesses plus a stable proptest mirror for the
gateway's attacker-controlled and untrusted-config surfaces, identified with
CodeGraph as the reachable, untested entry points for arbitrary input:

- fuzz_score_request      -> waf_ids_core::score_request (request scorer:
  percent-decode + signature/threat/DNSBL/anomaly matching)
- fuzz_appdata_json       -> serde_json::from_str::<AppData> (persisted state file)
- fuzz_parse_admin_tokens -> waf_ids_ai_soc::parse_admin_tokens (ADMIN_TOKENS config)
- fuzz_dnsbl_zone         -> export_dnsbl_zone / validate_dnsbl (zone-file escaping)

Each target asserts no panic on arbitrary input plus surface-specific
invariants (non-empty reason, deterministic scoring, serde round-trip, no empty
token/actor, fully-escaped TXT payloads), and ships a seed corpus of real
attack payloads and edge cases.

The fuzz crate is an isolated workspace so `cargo test --workspace` at the root
is unaffected. crates/waf-ids-core/tests/fuzz_invariants.rs and
tests/fuzz_invariants.rs re-assert the same invariants via proptest on stable,
so they stay green in primary CI without a nightly toolchain.

CI: .github/workflows/fuzz.yml runs each target for a bounded budget (60s/target
on PRs, 300s nightly) to keep cost predictable. All added deps are permissive
(cargo-fuzz, libfuzzer-sys, arbitrary, proptest: MIT OR Apache-2.0). Docs in
docs/fuzzing.md; fuzzing survey paper in docs/papers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RTAMs4bpSZS77Xe3RQjv9P
Comment thread .github/workflows/fuzz.yml Outdated
Comment thread .github/workflows/fuzz.yml Outdated
Comment thread .github/workflows/fuzz.yml Outdated
Comment thread .github/workflows/fuzz.yml Outdated
Seongho Bae and others added 2 commits July 8, 2026 23:00
Fixes the fuzz_dnsbl_zone crash and restores the coverage-evidence gate to
100% line/function coverage for PR #37.

Real bug (fuzz crash): export_dnsbl_zone rendered the untrusted `origin`
and each entry `code` into the zone file verbatim. A newline plus double
quotes in `origin` (or a non-IP `code`) broke out of the generated zone and
produced TXT records with unescaped quotes, violating the fuzz invariant.

- Sanitize the zone origin to DNS-safe characters (LDH, `_`, `.`); empty
  input falls back to the reserved `.invalid` TLD.
- Require each DNSBL response `code` to parse as an IP literal, re-rendering
  the canonical form and skipping entries that do not.
- Escape control characters (including raw newlines) in TXT payloads as BIND
  decimal escapes so a crafted reason/source cannot inject extra zone lines.
- Add regression tests reproducing the crash shape and the escaping paths.

Coverage-evidence (workspace 100% line/function gate): move the env-parsing
helpers into the tested library, add `run_from_env` (bound + served via an
ephemeral listener under an immediate shutdown in-process), reduce main.rs to
a thin shim covered end-to-end by tests/binary.rs (graceful SIGTERM shutdown),
and add unit tests for every configuration branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RTAMs4bpSZS77Xe3RQjv9P
…case

Adds the exact libFuzzer crash input (newline + quotes in `origin`) to the
fuzz_dnsbl_zone corpus so CI replays it and proves the zone-injection fix
holds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RTAMs4bpSZS77Xe3RQjv9P
@seonghobae
seonghobae enabled auto-merge (squash) July 9, 2026 01:21
@opencode-agent
opencode-agent Bot disabled auto-merge July 11, 2026 11:57
@seonghobae

Copy link
Copy Markdown
Contributor Author

최신 main에 이 PR의 fuzzing 기능이 검증된 통합 구현으로 이미 반영되어 중복·충돌 PR을 정리합니다.

  • main HEAD: 3f0f6e0
  • Enterprise WAF/SOC feature set (consolidated) #35 통합 구현은 score_request, DNSBL zone, admin token, AppData JSON 네 fuzz target과 corpus, fuzz invariant tests를 보존합니다.
  • docs/papers/fuzzing-art-science-engineering-survey-arxiv-1812.00140.pdf 원문도 Git LFS 없이 main에 보존되어 있습니다.
  • cargo test --workspace --all-targets: root 59 + binary 1 + root invariant 1 + core 8 + core invariant 3, 모두 통과
  • cargo check --manifest-path fuzz/Cargo.toml: 통과
  • 최신 #43은 push-triggered Scorecard 분석만 복원했고 fuzz 구현에는 변경이 없습니다.
  • Pinned-Dependencies 리뷰 4건은 모두 resolved/outdated이며 current open code-scanning은 패키지/CVE가 아닌 Scorecard 저장소 설정 신호입니다. 이는 #39의 중앙 required workflow 전환 및 조직 거버넌스에서 추적합니다.
  • 유일한 OpenCode 실패는 구형 중앙 모델 풀 자동화 실패입니다.

오래된 충돌 브랜치를 재병합하지 않고 단일 main 구현을 유지합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants