fuzz: land the name_validation target (§3.3, wave 7)

This commit is contained in:
glm-5.3-flash committed 2026-10-10 22:08:23 +00:00
1 parent f7c1bd79b0
commit 5043f7bcd8
52 files changed
+465 -6

No files matched your search

+1
View File
@@ -2,5 +2,6 @@ target
corpus/*
!corpus/payload_codec
!corpus/every_parse
!corpus/name_validation
artifacts
coverage
+7
View File
@@ -28,4 +28,11 @@ test = false
doc = false
bench = false
[[bin]]
name = "name_validation"
path = "fuzz_targets/name_validation.rs"
test = false
doc = false
bench = false
[workspace]
+1 -1
View File
@@ -29,7 +29,7 @@ libFuzzer targets for alkstore's parse/codec surfaces (see
|---|---|---|
| `payload_codec` | `encode_payload` / `payload_as::<T>` (`alkstore/src/payload.rs`) | no-panic on any byte sequence; every decode failure typed `Error::Codec`; deep nesting loud at serde_json's recursion limit; `Value` round-trip (serde_json canonicalization pinned); typed round-trip for a representative payload set |
| `every_parse` | the three `parse_every_interval` implementations (differential, through the engines' `#[doc(hidden)]` re-exports) + each engine's `skip_forward` boundary arithmetic | no-panic on any UTF-8-lossy string (each parser, each boundary path); typed rejection (`Error::InvalidSpec` on pg/mem, the substrate's error under its mapping on SQLite); three-way agreement on the Ok/Err partition and the `Ok` interval value (error identity not pinned — the SQLite `Result<i64, String>` vs `InvalidSpec` asymmetry is known); overflow posture: `skip_forward` over adversarial `(next_fire_at, now, interval)` triples rejects-or-saturates, never wraps or panics; the `n <= 0` and giant-`n` arms (leading zeros, digit overflow at exactly `i64::MAX/86400`, positive-zero rejection) |
| `name_validation` | `validate_name` / `validate_shared_name` / `validate_local_name` (`alkstore/src/validation.rs`) | filled by its target task |
| `name_validation` | `validate_name` / `validate_shared_name` / `validate_local_name` (`alkstore/src/validation.rs`) | no-panic on any byte sequence (lossy-decoded and `arbitrary`-structured strings); deterministic accept/reject (double-invocation-pinned); rejection only via the typed taxonomy — `Error::InvalidName` for empty/whitespace-only on both kinds, `Error::ReservedName` for the leading byte-exact `__alkstore_` prefix on the shared kind only (the local kind takes the non-empty rule only, so a leading reserved prefix is legal there); the reserved-prefix rule exact in every casing/boundary form (casing deviations, missing trailing underscore, embedded and whitespace-led forms accept); the rejection carries the supplied name verbatim (names are not trimmed); verdict-class length-independence across the probed neighborhoods (0↔1, 11↔12, 63↔64, 4095↔4096, 65536) — no length cap is pinned (ADR-008 §4), so no length is ever a rejection cause |
## Commands
+1
View File
@@ -0,0 +1 @@
a
+1
View File
@@ -0,0 +1 @@
aa
+1
View File
@@ -0,0 +1 @@
aaaa
+1
View File
@@ -0,0 +1 @@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+1
View File
@@ -0,0 +1 @@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+1
View File
@@ -0,0 +1 @@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+1
View File
@@ -0,0 +1 @@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+1
View File
@@ -0,0 +1 @@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+1
View File
@@ -0,0 +1 @@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Whitespace-only changes.
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+2
View File
@@ -0,0 +1,2 @@
+1
View File
@@ -0,0 +1 @@
 
+1
View File
@@ -0,0 +1 @@
 
+1
View File
@@ -0,0 +1 @@
__alkstore_
+1
View File
@@ -0,0 +1 @@
__alkstore_x
+1
View File
@@ -0,0 +1 @@
__alkstore_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+1
View File
@@ -0,0 +1 @@
__alkstore_listener_reconnected__
+1
View File
@@ -0,0 +1 @@
__ALKSTORE_x
+1
View File
@@ -0,0 +1 @@
__Alkstore_x
+1
View File
@@ -0,0 +1 @@
__alkStore_x
+1
View File
@@ -0,0 +1 @@
__alkstore
+1
View File
@@ -0,0 +1 @@
x__alkstore_y
+1
View File
@@ -0,0 +1 @@
__alkstore_x
+1
View File
@@ -0,0 +1 @@
__alkstore_
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
__alkstore_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
__alkstore_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
__alkstore_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
__alkstore_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+1
View File
@@ -0,0 +1 @@
é-😀-名前
+1
View File
@@ -0,0 +1 @@
é́
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
tab name
+1
View File
@@ -0,0 +1 @@

+1
View File
@@ -0,0 +1 @@
�(
+1
View File
@@ -0,0 +1 @@

+11
View File
@@ -0,0 +1,11 @@
//! The §3.3 target: core's name-validation invariants, driven through
//! the already-`pub` validation surface (`validate_name` over all
//! `NameKind`s, `validate_shared_name`, `validate_local_name`).
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| {
alkstore_fuzz_shared::name_validation::fuzz_name_validation(data);
});
+68
View File
@@ -245,9 +245,77 @@ def every_parse_seeds():
i += 1
def name_validation_seeds():
i = 0
# Plain-content names at the probed lengths: the empty↔one-char
# boundary, the pg-identifier (±1) and target-max_len (±1/±2)
# neighborhoods. No length cap is pinned; the seeds pin
# length-independence of the verdict class.
for length in [1, 2, 4, 62, 63, 64, 65, 4095, 4096]:
write_seed("name_validation", i, b"a" * length)
i += 1
# Empty and whitespace-only shapes (spaces, tab, newline, mixed,
# unicode whitespace) — InvalidName on both kinds.
for raw in [b"", b" ", b" ", b"\t", b"\n", b" \t\n ", b"\xc2\xa0", b"\xe2\x80\x83"]:
write_seed("name_validation", i, raw)
i += 1
# The reserved prefix: the boundary form (alone), suffixed, at a
# boundary length, and the one v1-reserved string.
for raw in [
b"__alkstore_",
b"__alkstore_x",
b"__alkstore_" + b"a" * 63,
b"__alkstore_listener_reconnected__",
]:
write_seed("name_validation", i, raw)
i += 1
# Prefix near-misses: casing deviations, the missing trailing
# underscore, embedded and whitespace-led forms — the pinned rule
# is byte-exact and leading-only, so these must accept.
for raw in [
b"__ALKSTORE_x",
b"__Alkstore_x",
b"__alkStore_x",
b"__alkstore",
b"x__alkstore_y",
b" __alkstore_x",
b"__alkstore_ ",
]:
write_seed("name_validation", i, raw)
i += 1
# Whitespace-only and reserved-prefixed at the pg-identifier
# neighborhood lengths (±1, ±2).
for length in [62, 63, 64, 65]:
write_seed("name_validation", i, b" " * length)
i += 1
write_seed("name_validation", i, b"__alkstore_" + b"a" * (length - len(b"__alkstore_")))
i += 1
# Unicode and control-char shapes, including invalid UTF-8 (lossy
# decode) and embedded NUL.
for raw in [
"é-😀-名前".encode(),
"\u00e9\u0301".encode(),
b"\x00\x01\x02",
b"a\x00b",
b"tab\tname",
b"\x7f",
b"\xc3\x28",
b"\xff\xfe",
]:
write_seed("name_validation", i, raw)
i += 1
def main():
payload_codec_seeds()
every_parse_seeds()
name_validation_seeds()
if __name__ == "__main__":
+1
View File
@@ -5,4 +5,5 @@
pub mod arbitrary_value;
pub mod every_parse;
pub mod name_validation;
pub mod payload_codec;
+272
View File
@@ -0,0 +1,272 @@
//! Invariant logic for the `name_validation` target (docs/research/
//! fuzzing.md §3.3, `alkstore/src/validation.rs`): the entry-point
//! name-validation surface over the whole string space — no-panic on
//! any byte sequence (lossy-decoded and `arbitrary`-structured alike),
//! deterministic accept/reject (double-invocation pinned), rejection
//! only via the typed `InvalidName`/`ReservedName` taxonomy
//! (ADR-008 §4/§5), the reserved-prefix rule byte-exact and
//! leading-only with the shared/local kind split (the local kind takes
//! the non-empty rule only — a leading `__alkstore_` is legal there),
//! the error carrying the supplied name verbatim (names are not
//! trimmed), and length-independence of the verdict class. No length
//! cap is pinned (ADR-008 §4 pins non-empty + reserved-prefix only), so
//! "exactness at the cap" is pinned as: nothing at any length flips a
//! verdict — the only exact boundaries in the rule are empty↔one-char,
//! whitespace-only↔content, and the byte-exact prefix itself.
use arbitrary::{Arbitrary, Unstructured};
use alkstore::{
validate_local_name, validate_name, validate_shared_name, Error, NameKind,
RESERVED_LISTENER_RECONNECTED, RESERVED_PREFIX,
};
/// The probed length neighborhoods: the empty↔one-char boundary
/// (0↔1), the reserved-prefix length (11↔12), the Postgres identifier
/// bytes (63↔64), the target's max_len (4095↔4096), and the
/// payload-cap scale (65536). None of these is a pinned cap — the ADR
/// pins no length limit — so the invariant is that the verdict class
/// never changes with length.
const LENGTH_PROBES: &[usize] = &[0, 1, 2, 3, 11, 12, 62, 63, 64, 65, 4095, 4096, 65_536];
/// The verdict of one validation call, narrowed to the taxonomy's typed
/// rejections: `Accept`, `InvalidName { name }`, or
/// `ReservedName { name }`. Any other outcome is a contract violation
/// and panics at construction — the typed-rejection invariant.
#[derive(Debug, Clone, PartialEq, Eq)]
enum Verdict {
Accept,
InvalidName(String),
ReservedName(String),
}
pub fn fuzz_name_validation(data: &[u8]) {
let lossy = String::from_utf8_lossy(data);
assert_verdict_invariants(&lossy);
assert_verdict_battery();
assert_structured(data);
assert_length_independence();
}
/// One string across the whole invariant set: deterministic (the
/// double-invocation pin), typed-only, all three entry points agreeing,
/// and the observed verdicts matching the locally recomputed rule.
fn assert_verdict_invariants(name: &str) {
let shared = shared_verdict(name);
assert_eq!(
shared,
shared_verdict(name),
"the shared-kind verdict must be deterministic for {name:?}"
);
let local = local_verdict(name);
assert_eq!(
local,
local_verdict(name),
"the local-kind verdict must be deterministic for {name:?}"
);
assert_eq!(
shared,
kind_verdict(NameKind::Shared, name),
"`validate_shared_name` must agree with `validate_name(Shared)` for {name:?}"
);
assert_eq!(
local,
kind_verdict(NameKind::Local, name),
"`validate_local_name` must agree with `validate_name(Local)` for {name:?}"
);
assert_classification(name, &shared, &local);
}
/// The rule of record recomputed locally and asserted against the
/// observed verdicts: empty or whitespace-only → `InvalidName` on both
/// kinds (names are not trimmed — the rejection never trims the name it
/// reports); otherwise the byte-exact leading reserved prefix rejects
/// `ReservedName` on the shared kind and is legal on the local kind;
/// anything else accepts on both kinds.
fn assert_classification(name: &str, shared: &Verdict, local: &Verdict) {
if name.is_empty() || name.trim().is_empty() {
assert_eq!(
shared,
&Verdict::InvalidName(name.to_owned()),
"empty/whitespace-only must reject `InvalidName` on the shared kind for {name:?}"
);
assert_eq!(
local,
&Verdict::InvalidName(name.to_owned()),
"empty/whitespace-only must reject `InvalidName` on the local kind for {name:?}"
);
return;
}
if name.starts_with(RESERVED_PREFIX) {
assert_eq!(
shared,
&Verdict::ReservedName(name.to_owned()),
"a leading reserved prefix must reject `ReservedName` on the shared kind for {name:?} — the error carries the name verbatim"
);
assert_eq!(
local,
&Verdict::Accept,
"the local kind takes the non-empty rule only — a leading reserved prefix is legal there: {name:?}"
);
return;
}
assert_eq!(
shared,
&Verdict::Accept,
"a non-empty, non-whitespace shared name without the exact reserved prefix must accept: {name:?}"
);
assert_eq!(
local,
&Verdict::Accept,
"a non-empty, non-whitespace local name must accept: {name:?}"
);
}
/// The casing/boundary battery for the reserved-prefix rule: the rule
/// is byte-exact and leading-only, so the near-miss forms (casing
/// deviations, the missing trailing underscore, embedded and
/// whitespace-led prefix occurrences) must accept on the shared kind,
/// while the byte-exact prefix (alone, suffixed, whitespace-suffixed —
/// anything not all-whitespace) must reject.
const BATTERY: &[&str] = &[
"",
" ",
"\t\n",
"\u{a0}",
"a",
"é-😀",
"a\u{0}b",
"__alkstore_",
"__alkstore_x",
RESERVED_LISTENER_RECONNECTED,
"__alkstore_ ",
"__alkstore_x_",
"__alkstore",
"__ALKSTORE_x",
"__Alkstore_x",
"__alkStore_x",
"__alkstore_X",
"x__alkstore_y",
" __alkstore_x",
];
fn assert_verdict_battery() {
for name in BATTERY {
assert_verdict_invariants(name);
}
}
/// The structured path: `arbitrary`-derived strings plus their
/// boundary-derivative forms (leading/trailing/embedded reserved
/// prefix, whitespace-wrapped, case-shifted) through the same
/// invariant set.
fn assert_structured(data: &[u8]) {
let mut u = Unstructured::new(data);
for _ in 0..2 {
let Ok(name) = String::arbitrary(&mut u) else {
return;
};
assert_verdict_invariants(&name);
for variant in derived_variants(&name) {
assert_verdict_invariants(&variant);
}
}
}
fn derived_variants(name: &str) -> Vec<String> {
let mut variants = vec![
format!("{}{}", RESERVED_PREFIX, name),
format!("{}{}", name, RESERVED_PREFIX),
format!("x{}{}y", RESERVED_PREFIX, name),
format!(" {}", name),
format!("{} ", name),
];
variants.push(name.to_uppercase());
variants
}
/// Verdict-class length-independence: the same content class (plain,
/// whitespace-only, reserved-prefixed, casing near-miss, embedded
/// prefix) holds its verdict at every probed length. No length cap is
/// pinned — a length anywhere is not a rejection cause.
fn assert_length_independence() {
for &len in LENGTH_PROBES {
let plain = "a".repeat(len);
assert_verdict_invariants(&plain);
let ws_only = " ".repeat(len);
assert_verdict_invariants(&ws_only);
let reserved = format!("{}{}", RESERVED_PREFIX, "a".repeat(len));
assert_verdict_invariants(&reserved);
let casing = format!("__ALKSTORE_{}", "a".repeat(len));
assert_verdict_invariants(&casing);
let embedded = format!("{}{}{}", "a".repeat(len), RESERVED_PREFIX, "b".repeat(len));
assert_verdict_invariants(&embedded);
}
}
fn shared_verdict(name: &str) -> Verdict {
match validate_shared_name(name) {
Ok(()) => Verdict::Accept,
Err(Error::InvalidName { name }) => Verdict::InvalidName(name),
Err(Error::ReservedName { name }) => Verdict::ReservedName(name),
Err(other) => panic!(
"shared-name rejection must be typed `InvalidName`/`ReservedName`, got {other:?} for {name:?}"
),
}
}
fn local_verdict(name: &str) -> Verdict {
match validate_local_name(name) {
Ok(()) => Verdict::Accept,
Err(Error::InvalidName { name }) => Verdict::InvalidName(name),
Err(Error::ReservedName { name }) => Verdict::ReservedName(name),
Err(other) => panic!(
"local-name rejection must be typed `InvalidName`/`ReservedName`, got {other:?} for {name:?}"
),
}
}
fn kind_verdict(kind: NameKind, name: &str) -> Verdict {
match validate_name(kind, name) {
Ok(()) => Verdict::Accept,
Err(Error::InvalidName { name }) => Verdict::InvalidName(name),
Err(Error::ReservedName { name }) => Verdict::ReservedName(name),
Err(other) => panic!(
"name rejection must be typed `InvalidName`/`ReservedName`, got {other:?} for {name:?}"
),
}
}
#[cfg(test)]
mod corpus_replay {
use super::*;
use std::path::PathBuf;
#[test]
fn committed_corpus_replays_through_the_invariants() {
let corpus = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../corpus/name_validation");
let mut count = 0usize;
for entry in std::fs::read_dir(&corpus).expect("corpus directory is committed") {
let path = entry.expect("corpus entry readable").path();
let data = std::fs::read(&path).expect("corpus entry readable");
fuzz_name_validation(&data);
count += 1;
}
assert!(
count >= 32,
"committed seed corpus is present, found {count}"
);
}
#[test]
fn invariants_hold_without_corpus() {
fuzz_name_validation(b"");
fuzz_name_validation(b"a");
fuzz_name_validation(b" ");
fuzz_name_validation(b"__alkstore_");
fuzz_name_validation(b"__alkstore_listener_reconnected__");
fuzz_name_validation(b"__alkstore_l\xffistener");
fuzz_name_validation(&[0u8; 16]);
fuzz_name_validation(&[b'a'; 65_537]);
}
}
+62 -5
View File
@@ -1,7 +1,7 @@
---
id: fuzz-target-name-validation
name: Fuzz target — `name_validation` (core name validation)
status: pending
status: completed
depends_on: [fuzz-scaffold]
scope: narrow
risk: trivial
@@ -58,14 +58,71 @@ guard-order discipline made continuous.
- docs/research/fuzzing.md §3.3 (target spec), §4, §7
- docs/architecture/core-contract.md §Naming / reserved namespace
- docs/architecture/decisions/014-naming-and-reserved-namespace.md §1
(the reserved-prefix rule the target demonstrates)
- 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.md`
reference 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
> To be filled by implementation agent
- **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.rs` checks 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 the `NameKind::Local` doc).
- **Corpus-replay floor set at 32** (44 seeds committed), following
the sibling targets' pattern (64 / 48).
## Summary
> To be filled on completion
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).