Skip to content

feat(tests): add fuzz harnesses, threat model & hash scoping (roadmap 11.5) - #45

Merged
danielPoloWork merged 1 commit into
mainfrom
feat/security-fuzzing-hash-scoping
Jul 5, 2026
Merged

feat(tests): add fuzz harnesses, threat model & hash scoping (roadmap 11.5)#45
danielPoloWork merged 1 commit into
mainfrom
feat/security-fuzzing-hash-scoping

Conversation

@danielPoloWork

Copy link
Copy Markdown
Owner

Summary

Closes the review's security gap: a threat model, libFuzzer harnesses for the three
untrusted-input components, and an explicit non-cryptographic scope for the hash functions
(with extended NIST vectors). The one Milestone-11 item with a code component.

Motivation

Spec-review acceptance criterion: "A security section covers untrusted-input parsing and
hash-function scoping, with enumerated fuzzing targets and SHA-256 test-vector validation."
Roadmap item 11.5. Decision locked with the maintainer: SHA-256 is non-cryptographic.

Changes

  • Fuzzing (src/fuzz/cpp/it/d4np/util/): one libFuzzer harness each for JsonParser,
    CliParser, BinaryDeserializer, gated by EGL_UTIL_BUILD_FUZZERS, with a fuzz preset
    (Clang/libFuzzer + ASan/UBSan) and a CI fuzz smoke job (bounded -max_total_time).
    • Dual-mode: a real LLVMFuzzerTestOneInput, plus a standalone replay main
      (fuzz_standalone.hpp) when not built with libFuzzer — so the whole matrix (incl. MSVC)
      compiles them, they stay in the clang-tidy compile DB, and a crashing input replays locally.
    • Asserted invariant: never crash, never UB, never throw on input (ADR-0022/0021/0024/0029).
  • Threat model (docs/security/threat-model.md): trust boundary, per-component attack surface
    • mitigations, fuzzing strategy, hash scope. Linked from SECURITY.md and spec §6.
  • Hash scoping (hash.hpp): states the non-cryptographic scope (integrity digest, not
    constant-time, not for passwords/MACs/signatures) + a @warning on sha256() — correcting the
    prior "the cryptographic digest" wording. Extended NIST FIPS 180-4 vectors: the 56-byte
    two-block message (static_assert) and the one-million-'a' message (runtime).
  • ADR-0031 records the fuzzing strategy and hash-scoping decision.

Design Patterns

  • None — a security/testing decision, not a design pattern.

Verification

  • Local MSVC build: util_tests 221/221 green incl. the new SHA-256 vectors (compile-time
    static_assert + runtime); all three standalone harnesses build and replay clean on
    sample JSON / argv / binary input.
  • clang-format and clang-tidy clean on the new sources (tidy: the libFuzzer uint8_t* ABI
    is bridged cast-free — no reinterpret_cast, no raw pointer arithmetic, no NOLINT).
  • python tools/consistency_lint.py passes.
  • libFuzzer mode is CI-only — no Clang on the dev box, so the coverage-guided run and the
    new fuzz CI job are unverified locally; the harness logic is exercised via standalone replay.
  • CI matrix — note repo Actions minutes are exhausted; any red is billing at startup.

Documentation Impact

  • README.md — n/a (milestone row already 🚧)
  • ROADMAP.md checkbox flipped (11.5)
  • ADR added (0031) + indexed
  • Spec updated (§6) + SECURITY.md + new docs/security/
  • CHANGELOG.md updated (Security + Changed)
  • PR metadata — milestone "M11 — Specification & Assurance Hardening"; type label feat

… 11.5)

Close the review's security gap for the untrusted-input components.

Fuzzing: one libFuzzer harness each for JsonParser, CliParser, and
BinaryDeserializer under src/fuzz/, gated by EGL_UTIL_BUILD_FUZZERS with a
`fuzz` preset (Clang/libFuzzer, ASan+UBSan) and a CI smoke job. Each is
dual-mode: a real LLVMFuzzerTestOneInput, plus a standalone replay main
(fuzz_standalone.hpp) when not built with libFuzzer — so the whole matrix
(incl. MSVC) compiles them, they stay in the clang-tidy compile database,
and a crashing corpus entry replays locally. The libFuzzer uint8_t* ABI
is bridged to string_view/span cast-free (no reinterpret_cast, no raw
pointer arithmetic) to stay tidy-clean without NOLINT. The asserted
invariant: never crash, never UB, never throw on input.

Threat model: docs/security/threat-model.md documents the trust boundary,
each component's attack surface + mitigations, and the fuzzing strategy;
linked from SECURITY.md and spec §6.

Hash scoping: hash.hpp now states the NON-cryptographic scope explicitly
(SHA-256 is an integrity digest, not constant-time, not for
passwords/MACs/signatures) — correcting the prior "the cryptographic
digest" wording — with a @warning on sha256(). Extended NIST FIPS 180-4
vectors: the 56-byte two-block message (static_assert) and the
one-million-'a' message (runtime).

Local verify (MSVC): util_tests 221/221 green incl. the new SHA-256
vectors; standalone harnesses build and replay clean; clang-format and
clang-tidy clean on the new sources; consistency_lint passing. libFuzzer
mode itself is CI-only (no Clang on the dev box).

ADR-0031
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@danielPoloWork danielPoloWork added the feat A new feature label Jul 5, 2026
@danielPoloWork danielPoloWork self-assigned this Jul 5, 2026
@danielPoloWork
danielPoloWork merged commit 0bc0712 into main Jul 5, 2026
0 of 20 checks passed
@danielPoloWork
danielPoloWork deleted the feat/security-fuzzing-hash-scoping branch July 5, 2026 18:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant