Commit b858d71
authored
security: add ClusterFuzzLite fuzzing for the SQL-safety kernel (#300)
* security: add ClusterFuzzLite fuzzing for the SQL-safety kernel
Closes Scorecard Fuzzing alert #55. Scorecard's Fuzzing check does not
recognize Python source patterns at all (only Go/Haskell/JS-TS/Erlang);
the only path to real credit for a Python project is OSS-Fuzz or
ClusterFuzzLite deployment. Confirmed via the alert's own rule.help text
before building this, per the standard OSS-Fuzz Python integration
(https://google.github.io/oss-fuzz/getting-started/new-project-guide/python-lang/).
Harness (.clusterfuzzlite/sql_safety_fuzzer.py) targets
SafeSqlDriver._validate — the pglast parse + AST-walker in
mcpg.sql.safety — feeding it arbitrary bytes via Atheris and treating
anything but the documented ValueError as a bug. This is the project's
actual security-critical surface (see CLAUDE.md's "SQL-safety kernel"
section); the existing tests/unit/test_sql_kernel_fuzz.py property tests
cover known-shape adversarial inputs, this harness covers unknown-shape
ones.
Two workflows: cflite_pr.yml (5-minute fuzz run on PRs touching
src/mcpg/sql/** or .clusterfuzzlite/**, address+undefined sanitizers)
and cflite_batch.yml (1-hour daily batch run to build corpus depth over
time).
Also adds .gitattributes rules forcing LF for *.sh and .clusterfuzzlite/**
— these scripts run inside a Linux container and a CRLF-mangled shebang
would break the interpreter on a Windows checkout.
Verified: Atheris 3.1.0 ships wheels for cp312/cp313/cp314, matching
this project's requires-python >=3.12 — no version-compatibility gap on
the fuzzing library itself. Full build/run validated by this PR's own
cflite_pr.yml workflow run (Docker-in-Docker local validation wasn't
available in this session — see PR checks for the real signal).
Entire-Checkpoint: 9d5df8697fd3
* fix: ignore-requires-python in ClusterFuzzLite build (base image is 3.11)
PR #300's own CI run caught this: base-builder-python ships Python
3.11.13, below mcpg's declared floor (requires-python >=3.12, set for
the wider ~254-tool codebase). pip3 install . failed outright on the
version gate before ever reaching the actual code.
The fuzz harness only imports mcpg.sql.safety + its direct deps
(mcpg.sql.allowlist, mcpg.sql.driver) — verified none of the three use
any 3.12-only syntax, so --ignore-requires-python is safe here. Scoped
to the fuzzing container only; pyproject.toml's real floor is untouched.
Entire-Checkpoint: 3b7a9a8d9b30
* fix: keep-unaffected-fuzz-targets on ClusterFuzzLite PR fuzzing
PR #300's own CI run caught another real gap: without this,
build_fuzzers tries to prune "unaffected" targets by diffing against a
coverage baseline artifact (cifuzz-coverage-latest) that can't exist yet
on a brand-new integration — hard-fails with "No fuzz targets found" on
literally the first fuzzing PR ever. There's exactly one fuzz target in
this repo, so the optimization has no upside; keep it unconditionally.
Entire-Checkpoint: c84ca2b91fd2
* fix: restore LLVMFuzzerTestOneInput detection marker in fuzz wrapper
Third real gap PR #300's own CI caught: build succeeded (pyinstaller
packaged the binary fine, keep-unaffected-fuzz-targets sidestepped the
coverage-diff issue), but OSS-Fuzz's bad-build-check still reported "No
fuzz targets found". Root cause: the previous commit's execution-wrapper
heredoc dropped the "# LLVMFuzzerTestOneInput for fuzzer detection."
comment line from the OSS-Fuzz reference example — that line isn't
decorative, it's a literal string the detection step greps wrapper
scripts for to recognize them as fuzz targets. Restored verbatim.
Entire-Checkpoint: 0d81d061fe9a
* fix: scope fuzzer discovery to \$SRC root (Gemini review on #300)
gemini-code-assist correctly flagged: find "\$SRC" -name '*_fuzzer.py'
searches recursively, matching both the Dockerfile's intentional
COPY .clusterfuzzlite/sql_safety_fuzzer.py \$SRC/ and the incidental
copy that COPY . \$SRC/mcpg (the whole repo) also pulls in at
\$SRC/mcpg/.clusterfuzzlite/sql_safety_fuzzer.py. Confirmed in the prior
CI run's own log: two full pyinstaller passes for the same fuzzer,
silently overwriting the \$OUT wrapper the second time. -maxdepth 1
scopes discovery to the intentional copy only.
Entire-Checkpoint: 2bcc94e8e1a81 parent 2be9954 commit b858d71
7 files changed
Lines changed: 184 additions & 0 deletions
File tree
- .clusterfuzzlite
- .github/workflows
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
0 commit comments