6.3 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | |||
|---|---|---|---|---|---|---|---|---|---|---|---|
| fuzz-target-name-validation | Fuzz target — `name_validation` (core name validation) | completed |
|
narrow | trivial | component | implementation |
|
Description
The §3.3 target: name_validation — the cheapest target, driving core's
already-pub validation surface (alkstore/src/validation.rs:
validate_name over all NameKinds, validate_shared_name,
validate_local_name). No exposure needed.
fuzz_targets/name_validation.rs+shared/src/ name_validation.rs(invariants +#[cfg(test)]replay),[[bin]]entry. Input: arbitrary&[u8]→ strings (lossy + a structured path), lengths spanning the caps, allNameKinds.- Invariants (§3.3, verbatim): total (no panic on any input);
deterministic accept/reject (same input → same verdict, pinned by
double-invocation in the invariant fn); rejection only via typed
Error::InvalidName(orReservedNamewhere the reserved-prefix rule fires — both are the taxonomy's typed rejections; nothing else, no panic); the reserved-prefix rule (__alkstore_, ADR-014 §1's shared namespace) rejects in every casing/boundary form the fuzzer finds; length-boundary exactness at the cap (the ±1 neighborhood — a name at the cap is accepted, one past it rejected, per the pinned rule). - Seeds via
gen_fuzz_seeds.py'sname_validationfunction: cap-boundary lengths (±1, ±2), the reserved prefix in plain / suffixed / embedded forms, empty and whitespace-only strings, unicode and control-char shapes, the reserved listener-reconnected string. Corpus committed; gitignore negation. fuzz/README.mdtargets-table row.
Low expected yield (the doc says so) — the value is the entry-point domain guard demonstrated over the whole string space, the gate's guard-order discipline made continuous.
Acceptance Criteria
fuzz_targets/name_validation.rs+shared/src/ name_validation.rsexist;cargo fuzz buildbuilds the target- All §3.3 invariants implemented and exercised by both the fuzz path and the replay module
- Seed corpus committed, deterministic-generated, gitignore negation in place
cargo test --manifest-path fuzz/shared/Cargo.tomlgreen;sharedclippy/fmt gates green- Root workspace gates still green
References
- docs/research/fuzzing.md §3.3 (target spec), §4, §7
- docs/architecture/core-contract.md §Naming / reserved namespace
- docs/architecture/decisions/008-contract-v1-pinning.md §4/§5 — the
reserved-prefix rule and the typed-rejection taxonomy the target
demonstrates (the description's
014-naming-and-reserved-namespace.mdreference was stale: ADR-014 is the outbox ADR; the reserved namespace is ADR-008 §4, with the kinds list extended by ADR-014 outbox and ADR-009 schedule — corrected here) - alkstore/src/validation.rs (the surface)
Notes
- No length cap is pinned — the "length-boundary exactness at the
cap" invariant was rendered as verdict-class length-independence.
The description (and fuzzing.md §3.3) pinned a cap with a ±1
neighborhood ("a name at the cap is accepted, one past it rejected"),
but no cap exists anywhere in the normative stack: ADR-008 §4 pins
only non-empty (whitespace-only-rejected) + the reserved-prefix rule,
core-contract §Naming adds nothing about length, and
alkstore/src/validation.rschecks nothing about length. Per AGENTS.md the ADR wins, so the shipped invariant pins what is true: the verdict class is length-independent (no length is ever a rejection cause) across probed neighborhoods (0↔1 empty boundary, 11↔12 prefix length, 63↔64 pg-identifier bytes, 4095↔4096 target max_len, 65536 payload-cap scale). The only exact boundaries in the pinned rule — empty↔one-char, whitespace-only↔content, and the byte-exact prefix — are pinned exactly. - The "rejects in every casing/boundary form" invariant is pinned
with its exact-case direction. The rule is byte-exact and
leading-only (
starts_with(RESERVED_PREFIX)), so the battery pins the byte-exact prefix rejecting (alone, suffixed, whitespace-suffixed — anything not all-whitespace) and the near-miss forms accepting on the shared kind (casing deviations, missing trailing underscore, embedded and whitespace-led occurrences) plus the local kind taking the non-empty rule only (leading reserved prefix legal there, per ADR-008 §4's annotation and theNameKind::Localdoc). - Corpus-replay floor set at 32 (44 seeds committed), following the sibling targets' pattern (64 / 48).
Summary
Landed the §3.3 target: fuzz_targets/name_validation.rs (thin
fuzz_target! wrapper, like the sibling targets) +
shared/src/name_validation.rs (invariants + #[cfg(test)] corpus
replay), its [[bin]] entry, pub mod in shared's lib.rs, the
name_validation_seeds() generator (44 deterministic seeds committed:
cap-neighborhood lengths, empty/whitespace-only, the reserved prefix
plain/suffixed/at-length plus the listener-reconnected string, seven
near-miss forms, unicode/control/invalid-UTF-8 shapes), the
!corpus/name_validation gitignore negation, and the README
targets-table row. Invariants: total (no-panic over lossy and
arbitrary-structured strings plus derived boundary forms);
determinism (double invocation on both kinds); typed-only rejections
(narrowed to a Verdict enum that panics on anything but
Accept/InvalidName/ReservedName); wrapper equivalence
(validate_shared_name/validate_local_name vs validate_name on
each NameKind); classification asserted against a locally recomputed
rule of record (verbatim-name-carrying errors — names not trimmed);
the casing/boundary battery; and the length-independence battery
(see Notes).
Verified: cargo test --manifest-path fuzz/shared/Cargo.toml green
(6/6, 44-seed replay included); shared clippy -D warnings + fmt
--check green; cargo fuzz build green from fuzz/ (nightly pin);
root cargo build / cargo test (all suites pass) / clippy
-D warnings / fmt --check green; seed regeneration deterministic;
corpus files trackable (negation verified via git check-ignore).
Campaign runs are fuzz-campaigns-and-gate's concern (this task is
one of its three dependencies).