Repository navigation
Fuzz Policy and Parser Security
Audience: Engine developers, security reviewers, and CI maintainers Thread Context: SEC-120 — stable-v1 untrusted file and package parsers Platform-Backend Scope: Cross-platform policy tooling; Linux CI is the blocking execution lane
SEC-120 remains open and release-blocking. The repository has a blocking structural policy gate, but it does not yet have a production fuzz target, seed corpus, sanitizer fuzz smoke, scheduled campaign, coverage result, or crash-free-duration result.
The deterministic snapshot in docs/sec120-fuzz-policy-check.json is validated by CI.
For the recorded source-tree state it reports 105 explicitly inventoried parsers, all
blocked, 151 detected candidates deferred with an owner and expiry, and 1979
source files scanned across 17 first-party roots. Those counts are not fuzz coverage.
passed in that snapshot is computed from the closure blockers, so it reads false
while any blocker remains.
Two gates, deliberately separate:
| Gate | Command | Blocks | Today |
|---|---|---|---|
| Structural | check_fuzz_policy.py --ci |
merges (Required CI Gate) | passes |
| Closure | check_fuzz_policy.py --ci --require-closure |
releases (release.yml) |
fails — by design |
Network packet/protocol fuzzing belongs to NET-100 behind G12. AngelScript and
visual-script fuzzing belongs to ENG-200 behind G11, including .as script-file
ingestion under SparkEngine/Source/Engine/Scripting. Their subtrees are named as
ticketed, owned, expiring exclusions rather than silently omitted.
The policy tooling lives under the case-sensitive Git path tools/fuzz-policy/.
The fuzz-policy Linux job runs its standalone CMake project, and Required CI Gate
depends on that job. The root CMake project exposes check-fuzz-policy and the
FuzzPolicy/FuzzPolicyAdversarial CTest tests behind
SPARK_ENABLE_FUZZ_POLICY_CHECKS (default ${BUILD_TESTS}), so an engine-only
configure never requires Python.
The gate proves that:
- policy JSON is strict UTF-8 with bounded size/depth/collections, unique keys, integer-only numeric fields, exact schemas, and known enum values;
- every declared path is repository-relative and resolves through regular, non-reparse, non-symlink components; source/target/seed files cannot be hard links, case aliases, or cross-volume paths, and the opened handle is re-checked against the root on Windows, Linux, and macOS (any other platform fails closed);
- the 17 declared first-party roots were scanned deterministically within file, directory, byte, depth, directory-entry, and shared wall-time limits, with unreadable entries fatal and the entry cap applied before a directory listing is materialized;
- the declared scope cannot shrink: every
Spark*/GameModulessource tree present on disk must be a declared root, and the extension set must be the full supported set; - every detected candidate is either owned by an inventory record or deferred with a
reason, the
sec-120-parser-triageowner, the SEC-120 ticket, and an expiry; - exclusions are reviewed waivers, not free text. An exclusion needs an approved ticket from a hard-coded allowlist, that ticket's approved owner, an expiry inside 365 days, and the exact count of candidates it hides. Whole-scan-root, overlapping, case-aliased, expired, stale (hiding nothing), and newly-absorbing exclusions are all rejected. A single-file exclusion additionally pins a SHA-256 of the reviewed content;
- a parser marked
fuzzedbinds a real build: anadd_executablethat declares the target and compiles the declared harness, a listfile reachable from the rootCMakeLists.txtviaadd_subdirectory,-fsanitize=fuzzerinstrumentation, anadd_testthat invokes that target and passes the corpus directory plus exact-max_len/-timeout/-rss_limit_mbvalues, and aset_tests_propertieswith a matchingTIMEOUTand afuzzlabel. CMake and C++ are tokenized, so a commented-out or string-literal declaration satisfies nothing; - the harness defines
LLVMFuzzerTestOneInput(const uint8_t*, size_t)with a body, includes a header, defines and usesSPARK_FUZZ_MAX_DEPTHandSPARK_FUZZ_MAX_INPUT_BYTESat the budgeted values, compares its input size against the input bound, and calls an entry symbol that exists in that parser's own inventoried sources; - corpus seeds are opened and read, not merely
stat-ed, and a corpus content digest pinslast_verifiedto exact seed bytes; - the narrative ledger
docs/sec120-fuzz-policy-evidence.jsoncannot claim closure, or a non-blocking status, while blockers remain.
The gate does not prove that the regex scanner finds every possible parser, or that
declared limits hold at runtime. The snapshot reports detector_blind_spot_count — 27
inventoried files that no detector pattern matches, found by human review — precisely so
that limitation is a number rather than an assumption.
# structural gate (blocks merges)
python3 tools/fuzz-policy/check_fuzz_policy.py --source-root . --ci
# closure gate (blocks releases; expected to fail until real targets land)
python3 tools/fuzz-policy/check_fuzz_policy.py --source-root . --ci --require-closure
# regenerate the committed snapshot after changing any policy input
python3 tools/fuzz-policy/check_fuzz_policy.py --source-root . --emit-json \
> docs/sec120-fuzz-policy-check.json
python3 -m unittest discover -s Tests/fuzz-policy -p "test_*.py" -v
bash tools/check-fuzz-policy.sh # also runs via tools/validate-all.sh
cmake -S tools/fuzz-policy -B build/fuzz-policy
cmake --build build/fuzz-policy --target check-fuzz-policy
ctest --test-dir build/fuzz-policy --output-on-failure --no-tests=error -C Release-C Release is required by multi-config generators (Visual Studio) and ignored by
single-config ones; without it CTest reports Not Run on Windows. All validation
failures return nonzero, including JSON emission mode. The CI mode also checks the
workflow/CMake wiring and requires the committed evidence snapshot to equal the
freshly computed report.
- Add its production implementation files to
tools/fuzz-policy/parser-inventory.jsonusing canonical repository-relative paths. - Mark it
blockedwith a substantive SEC-120 reason, orfuzzedwith a harness, CMake listfile, target, CTest selector, corpus id, andentry_symbol. - Remove any corresponding entry from
deferred_candidates. - For a fuzzed parser, add exactly one entry to
corpus-manifest.json. The seed tree lives underTests/fuzz-corpora/, must be non-empty, fresh, confined, link-free, within every declared limit, and pinned bycontent_digest. - Bind the exact input, timeout, memory, depth, and smoke limits in the harness and the CMake registration, then run the commands above under ASan/UBSan.
- Persist each minimized crash input as a regression and regenerate the snapshot.
Do not change a parser to fuzzed based on an upstream library campaign or a unit test
that bypasses the production entry point. Adding an exclusion ticket requires editing
APPROVED_EXCLUSION_TICKETS in tools/fuzz-policy/parser_inventory.py — that code
change is the review record.
- classify the 151-file deferred backlog before it expires on 2027-02-24;
- implement production-entry-point fuzz targets for the inventoried parsers, starting
with the highest-risk binary readers (
neural-weights-nnw,terrain-sparkterrain,daemon-asset-cache-blob,editor-level-streaming-world,startup-splash-bmp,fps-terrain-heightmap-bmp,asset-media-windows); - commit bounded seed corpora under
Tests/fuzz-corpora/and minimized regressions; - add blocking ASan/UBSan smoke and scheduled campaigns with retained coverage and
crash-free-duration evidence, and wire the
-L fuzzsmoke run into the CI job (the gate already requires it as soon as any parser is markedfuzzed); - independently review that each harness reaches production parsing code and that allocation, depth, path, integer, and time bounds are enforced by that code;
- extend the detector so the 27 known blind spots shrink.
Source of truth: tools/fuzz-policy/, cmake/SparkFuzzPolicy.cmake, the blocking
fuzz-policy job in .github/workflows/build.yml, and the closure step in
.github/workflows/release.yml. Status checked 2026-08-28 against base commit
006c2ed32f751d3c363e76c3595c78a95069c4bc; rerun the CI command for current counts.
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers