Skip to content

ci: OSS-Fuzz build integration, run on CI via ClusterFuzzLite - #51

Merged
FMSMITH91 merged 3 commits into
mainfrom
ci/oss-fuzz-clusterfuzzlite
Aug 8, 2026
Merged

ci: OSS-Fuzz build integration, run on CI via ClusterFuzzLite#51
FMSMITH91 merged 3 commits into
mainfrom
ci/oss-fuzz-clusterfuzzlite

Conversation

@FMSMITH91

Copy link
Copy Markdown
Owner

Adds the OSS-Fuzz build contract for the five Atheris harnesses already in tests/fuzz/, and runs it on pull requests.

First, the honest bit about the upstream program

OSS-Fuzz's stated acceptance bar is that a project "must have a significant user base and/or be critical to the global IT infrastructure". A self-hosted game control panel will not clear that, so submitting to https://github.com/google/oss-fuzz would almost certainly be declined.

ClusterFuzzLite is the answer to exactly that problem — same base images, same build contract, same crash reporting and dedup, running on this repo's own CI with no enrolment. That's what this PR wires up.

And if the project ever does qualify, these three files are the submission: drop them into projects/linuxgsm-panel/ with a Dockerfile that clones instead of copies, plus a contact email.

What's here

.clusterfuzzlite/Dockerfile      base-builder-python + the pinned requirements
.clusterfuzzlite/build.sh        compiles every fuzz_*.py, zips its seed corpus
.clusterfuzzlite/project.yaml    language, engine, sanitizers
.github/workflows/cflite_pr.yml  code-change fuzzing on PRs -> SARIF

Two things the OSS-Fuzz Python guide doesn't cover for a repo shaped like this

  • The panel is an application, not a pip package. There's no setup.py, so the guide's pip3 install . doesn't apply. The repo root goes on PYTHONPATH instead — that's both how the harnesses' import ssh_manager resolves and how PyInstaller finds those modules to bundle.
  • The harnesses pre-load paramiko / eventlet.tpool / config via importlib.import_module("..."), deliberately, so instrument_imports() only instruments the parser under test. A string is invisible to PyInstaller's static analysis, so those are declared as --hidden-import. Without them the targets build clean and die on their first import — which is why bad-build-check is left on: that's precisely the failure mode here.

Also worth flagging: run_fuzzers defaults to language: c++ and does not inherit the build step's setting, so it's set explicitly.

Deliberate overlap with fuzz.yml

fuzz.yml is the broad sweep — every target, fixed 60s, plain Atheris. This runs in code-change mode: only what the diff touched, with real crash dedup, filing findings as SARIF in the Security tab next to the CodeQL and Bandit alerts. Dropping either is reasonable; keeping both is what this does. Say the word if you'd rather I retire fuzz.yml.

Not enabled

Batch fuzzing, corpus pruning and coverage all need a separate storage repo plus a PAT to keep a corpus between runs. Without one they'd start from the committed seeds every time and learn nothing. .clusterfuzzlite/README.md documents how to turn them on.

Verification

Done: shellcheck clean, both YAML files parse, every corpus directory resolves to a target, repo lint clean.

Not done: the Docker build itself — there's no Docker on this machine, so I could not run helper.py build_fuzzers / check_build. The README lists those commands, and this PR's own CI run is the first real execution. If the build is wrong, this PR is where it shows.

🤖 Generated with Claude Code

Adds the OSS-Fuzz build contract for the five Atheris harnesses already in
tests/fuzz/, and runs it on pull requests.

  .clusterfuzzlite/Dockerfile     base-builder-python + pinned requirements
  .clusterfuzzlite/build.sh       compiles every fuzz_*.py, zips its corpus
  .clusterfuzzlite/project.yaml   language, engine, sanitizers

On the upstream program itself: OSS-Fuzz's stated bar is that a project "must
have a significant user base and/or be critical to the global IT
infrastructure", which a self-hosted game control panel will not clear. That
is what ClusterFuzzLite is for — the same base images, the same build
contract, the same crash reporting, running on this repo's own CI with no
enrolment. If the project ever does qualify, these three files ARE the
submission: drop them in projects/linuxgsm-panel/ with a Dockerfile that
clones instead of copies.

Two things the OSS-Fuzz Python guide does not cover for a repo shaped like
this one:

- The panel is an application, not a pip package — there is no setup.py, so
  `pip3 install .` does not apply. The repo root goes on PYTHONPATH instead,
  which is both how the harnesses' imports resolve and how PyInstaller finds
  those modules to bundle.
- The harnesses pre-load paramiko / eventlet.tpool / config through
  importlib.import_module("..."), deliberately, so instrument_imports() only
  instruments the parser. A string is invisible to PyInstaller's static
  analysis, so those are named as --hidden-import; without them the targets
  build clean and die on first import. bad-build-check is left ON precisely
  because that is the failure mode here.

The workflow runs in code-change mode: it fuzzes what the diff touched, not
everything, and files a crash as SARIF in the Security tab next to the CodeQL
and Bandit alerts. run_fuzzers gets an explicit `language: python` — it
defaults to c++ and does not inherit the build step's setting.

This overlaps fuzz.yml on purpose: that is the broad every-target sweep, this
is the targeted one with real crash reporting. Dropping either is reasonable;
keeping both is what this does.

Batch fuzzing, corpus pruning and coverage are NOT enabled — all three need a
separate storage repo and a PAT to keep a corpus between runs. The README
says how to turn them on.

Verified: shellcheck clean, both YAML files parse, every corpus directory
resolves to a target. NOT verified locally — the Docker build itself; there
is no Docker on this machine. The README documents the helper.py commands to
check it, and CI is the first real run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@codacy-production

codacy-production Bot commented Aug 8, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

The first CI run failed, which is what it was there to do: bad_build_check
caught 3 of 5 targets dying at startup on

    ModuleNotFoundError: No module named 'eventlet.hubs.epolls'

eventlet.hubs picks its implementation with a string, at import time:

    builtin_hub_modules = tuple(importlib.import_module('eventlet.hubs.' + name)
                                for name in ('epolls', 'kqueue', 'poll', 'selects'))

so `--hidden-import eventlet.tpool` bundled tpool and none of the hubs it
transitively needs. Naming each hub by hand would work today and rot the next
time eventlet adds one, so this collects the whole package instead. Same for
paramiko (kex/cipher backends) and dns, which arrives transitively via
eventlet's greendns.

Kept the harnesses as they are rather than making their eventlet pre-load
optional: ssh_manager does tolerate eventlet being absent, but a fuzz target
whose import graph differs from production is testing something else.
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Codacy's gate is zero new issues and it caught the one RUN line where $SRC
was unquoted. The COPY lines are not shell, so they are unaffected.
@FMSMITH91
FMSMITH91 merged commit aaa2bd8 into main Aug 8, 2026
18 checks passed
@FMSMITH91
FMSMITH91 deleted the ci/oss-fuzz-clusterfuzzlite branch August 8, 2026 12:38
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.

2 participants