Fuzzing

The fuzz/ package contains seven Termivar-owned semantic targets plus five bounded upstream-parser targets. html_form_controls, expression_semantics, and declarative_policy_wire exercise extraction and declarative-policy contracts; decision_loop_authority exercises adaptive planner authority, and oast_correlation exercises the transport-free correlation state machine, and native_oast_provider exercises the self-hosted provider's bounded input contracts without network I/O. native_oast_adapter exercises narrowing provider-permit, sequencing, accounting, event-reduction, and redaction contracts without network I/O. The parser targets are dependency-level signals and must not be reported as Termivar decision-runtime coverage.

Setup

cargo install cargo-fuzz --version 0.13.2 --locked
cargo fuzz list
cargo fuzz run html_form_controls
cargo fuzz run expression_semantics
cargo fuzz run declarative_policy_wire
cargo fuzz run decision_loop_authority
cargo fuzz run oast_correlation
cargo fuzz run native_oast_provider
cargo fuzz run native_oast_adapter

Every pull request replays the committed semantic corpora, runs the deterministic structured OAST regression matrix, and compiles all Termivar-owned targets. The Scheduled Fuzzing workflow runs all twelve targets in bounded weekly campaigns and when fuzz harnesses change on main. Every target uploads its libFuzzer log and a structured campaign summary for 90 days; failures also retain crash artifacts. This provides regression pressure, not proof of parser or decision-runtime safety.

Run fuzzing on a dedicated machine or bounded CI job. Start with a small, non-sensitive seed corpus. Crashes must preserve the minimized input and exact commit SHA.

Targets

Target Ownership Contract
html_form_controls Termivar Bounded HTML sample to exact, names-only, sorted and deduplicated form-control observations
expression_semantics Termivar Exact TextList truth table, truthful contributing evidence IDs, deterministic evaluation, and bounded expression round trips
declarative_policy_wire Termivar One-field semantic corruption rejects or reconstructs an exactly equivalent historical policy
decision_loop_authority Termivar Adaptive scheduling cannot bypass registration, host suppression, requirements, risk, budget, prerequisites, executor identity, or claim policy; errors are atomic
oast_correlation Termivar Host-minted token reuse, exact case/correlation binding, protocol grants, expiry/cancellation, bounded poll permits, opaque event-key replay suppression, and atomic batch failure semantics
native_oast_provider Termivar Exact public-origin, loopback-bind, strict session-request, opaque-identity, callback-path identity, and cursor parsing; checked resource limits; administrator-secret validation/redaction; raw-free state transitions; and fixed provider regressions without network I/O
native_oast_adapter Termivar Exact provider/target-origin separation, narrowing permit limits, fixed operation sequencing, parent-budget accounting, raw-free HTTP event reduction, malformed poll rejection, and receipt/secret redaction without network I/O
http_parser Upstream httparse request parser survival
json_parser Upstream serde_json value parser survival
yaml_parser Upstream-only dependency serde_yaml value parser survival
xml_parser Upstream-only dependency quick-xml event reader survival
text_parser Upstream URL and UTF-8 parser survival

html_form_controls compiles the same private extractor source used by HttpEvidenceExecutor; no production API is made public for fuzzability. Its structured oracle computes expected names independently from the parser output. It asserts exact HTML name preservation (including whitespace), independent coverage of the input/select/textarea branches, HTML element namespaces, unqualified name-attribute selection, integration-point behavior, and exclusion of empty names and non-control/decoy content. Raw malformed inputs must classify the 64-KiB parse boundary exactly, remain deterministic, and return sorted/deduplicated non-empty observations. The target also protects the names-only privacy boundary and accepts at most 64 KiB of raw input.

expression_semantics builds bounded typed evidence through public Termivar APIs. Its independent oracle requires complete, case-sensitive TextList element equality; scalar text, padded names, case changes, and substring-only values do not contribute. Repeated evaluation and accepted expression serialization must be identical, and a nested expression is limited to 32 levels and 64 nodes in the harness.

declarative_policy_wire starts from valid selectors, calibrations, reasoning rules, verification rules/cases, adaptation rules, and directives. It deletes, misspells, nulls, conflicts, or corrupts one semantics-bearing field. The result must reject unless it is a documented historical representation that reconstructs exactly the original policy and canonicalizes on the next write. The expression and declarative-policy targets cap inputs at 16 KiB and semantic strings at 256 bytes.

decision_loop_authority converts at most 16 KiB of input into one of fourteen small, network-free DecisionLoop scenarios. It runs every scenario twice and compares normalized session, Experience, command, and hypothesis semantics. An unregistered, suppressed, ineligible, over-risk, over-budget, or dependent scheduled action cannot dispatch; missing host context fails closed for both schedule and retry directives. Active continuation is reauthorized against dynamic host suppression, and authorization observes a conclusive outcome's prospective hypothesis state. Every returned authorization error must leave the session, Experience store, and motivation hypothesis exactly unchanged. An eligible independent action must carry its registered executor and its own Motivation or KnowledgeOnly transition policy. A replay cannot continue an outstanding action after that action is absent from the planner registry. It also rejects a legacy/default replay that would broaden a currently registered KnowledgeOnly action into hypothesis-transition authority.

oast_correlation converts a structured prefix of an input of at most 4 KiB into one of twelve pure, provider-neutral scenarios. The input independently selects the token and event-key bytes, monotonic schedule, expiry slack, authority limits, and poll budget. An expectation table defined independently from the implementation requires the exact error variant, registration and poll counters, active or terminal state and time, accepted/unique/duplicate counts, atomic failure behavior, and reduced receipt semantics for each scenario. A second run must reproduce that same expected observation. The scenarios cover token reuse, wrong case/correlation routing, protocol grants, expiry, cancellation, spent poll permits, duplicate keys, conflicting protocol families for one event key, and unique-event limits. The harness performs no network, provider, clock, random, reporting, or scanner-runtime work.

native_oast_provider accepts at most 4 KiB and routes a bounded prefix through the exact production public-origin, loopback-bind, provider-limit, administrator-token, session-request, opaque-identity, and cursor constructors. Callback-shaped inputs and complete Authorization values are reduced through the production fixed-route and redacted Bearer parsers. Repeated parsing must classify identically; successful origins remain canonical HTTPS DNS origins, successful binds remain loopback, limits remain inside every hard ceiling, and secret Debug/error text cannot contain supplied token material. Its fixed regressions keep IP-literal, localhost, userinfo, non-root path, query, fragment, wildcard, public-bind, and unspecified-bind rejection in every campaign. A bounded in-memory state model also registers, allocates, records and deduplicates GET/HEAD observations, polls, and cleans up while comparing only raw-free result shape; provider-minted random identities are never used as an oracle. The harness does not start the HTTP provider, read secret sources, or perform network I/O.

Semantic corpus and reproduction

Reviewed seeds under fuzz/corpus/html_form_controls/ include the minimized whitespace-normalization reproducer, exact and substring-only convention names, real/empty/duplicate/Unicode controls, quote-state spoofing, raw-text/comment decoys, values, character references, malformed/truncated markup, a long name, modest nesting, SVG/MathML control lookalikes, genuine HTML integration-point descendants, foreign-content re-entry, and distinct identities for every supported control tag. Generated hash-named corpus files remain ignored until they are deliberately reviewed and promoted.

fuzz/corpus/expression_semantics/ contains exact _token, substring-only, padded, case-sensitive _METHOD, scalar/list mismatch, duplicates, Unicode, depth-boundary, valid nested, malformed nested-expression, empty all/any, and blank text-matcher examples. fuzz/corpus/declarative_policy_wire/ contains one reviewed seed for every corruption scenario: matcher, aggregation, reasoning condition, verification scope/case guard, adaptation condition, and pipeline directive loss or typo. fuzz/corpus/decision_loop_authority/ contains one reviewed seed for each authority boundary: unregistered and host-suppressed scheduling, unmet requirements, risk and budget rejection, prerequisite rejection, eligible Motivation and KnowledgeOnly dispatch, context-free schedule/retry, prospective motivation rejection, active host suppression, and a legacy/default replay that references an action no longer registered with the planner or broadens a KnowledgeOnly case into hypothesis-transition authority. The harness replay also constructs 96 deterministic, owned 80-byte OAST seeds: all twelve scenarios crossed with eight boundary patterns. Every seed populates the complete token, event key, timing, and bound-selector prefix consumed by the OAST model rather than relying on fallback bytes. It separately constructs 96 deterministic, owned 96-byte native-provider seeds by crossing twelve scenario selectors with eight boundary patterns. They exercise all provider-input oracle classes without containing real credentials or public provider addresses. Sixteen additional exact route and Bearer seeds pin canonical and adversarial production-parser cases. The adapter harness constructs 96 deterministic, owned bounded seeds by crossing twelve permit, sequencing, reduction, and accounting scenarios with eight boundary patterns. The seeds contain only reserved .invalid origins, loopback values, and synthetic credentials; they perform no network I/O and retain no callback URL or provider response body. Generated hash-named files in every corpus remain ignored until reviewed.

Replay all committed seeds without libFuzzer:

cargo test --manifest-path fuzz/harness/Cargo.toml --locked

Replay or minimize one retained finding:

cd fuzz
cargo fuzz run html_form_controls artifacts/html_form_controls/<artifact>
cargo fuzz tmin html_form_controls artifacts/html_form_controls/<artifact>
cargo fuzz run expression_semantics artifacts/expression_semantics/<artifact>
cargo fuzz tmin declarative_policy_wire artifacts/declarative_policy_wire/<artifact>
cargo fuzz run decision_loop_authority artifacts/decision_loop_authority/<artifact>
cargo fuzz tmin decision_loop_authority artifacts/decision_loop_authority/<artifact>
cargo fuzz run oast_correlation artifacts/oast_correlation/<artifact>
cargo fuzz tmin oast_correlation artifacts/oast_correlation/<artifact>
cargo fuzz run native_oast_provider artifacts/native_oast_provider/<artifact>
cargo fuzz tmin native_oast_provider artifacts/native_oast_provider/<artifact>
cargo fuzz run native_oast_adapter artifacts/native_oast_adapter/<artifact>
cargo fuzz tmin native_oast_adapter artifacts/native_oast_adapter/<artifact>

Scheduled campaigns use an explicit 60-second budget and 1024-MiB RSS limit with a recorded deterministic seed. HTML and upstream targets use at most 64 KiB and five seconds per input; semantic policy targets use at most 16 KiB and two seconds. A timeout, semantic assertion failure, panic, excessive allocation, or sanitizer finding requires the same minimize-classify-regress-fix triage as a normal test failure.

Fuzz harnesses should have no network, filesystem, or ambient-clock dependencies. The native-provider state oracle deliberately calls the exact production OS-CSPRNG identity path rather than introducing a deterministic identity mint; it normalizes every generated identity away and asserts only transition classes and bounded counts. No random identity bytes participate in an expected value. A parser rejection is not a crash; panics, hangs, excessive allocation, and sanitizer findings require triage.

Published reports

Reports committed under docs/reports/fuzzing/ record the exact commit, bounded runtime, target outcomes, corpus counts, and workflow provenance. A report is published only after all target jobs complete; absence of a crash in a bounded run is not a safety claim.