docs: remove em dashes from repo prose and forbid them going forward
- Rewrote em dash prose across the whole tree outside docs/reviews/ (the historical reviews stay verbatim, matching the alktunnels and alktty precedent): src doc comments and inline comments (587), docs/architecture (319), docs/research + sdd_process (484), tests (361), tasks (899), fuzz (28), README/CHANGELOG/Cargo.toml (77), AGENTS.md (85), and the .opencode/agents prompts (109) - Fenced content follows the precedent: comments, ASCII-diagram label separators, and template placeholders rewritten; wire data, JSON/BAST documents, byte diagrams, and transcripts survive verbatim; assert, expect, panic, error-display, and format message literals rewritten as prose; no code, fixture, or test data change - src/rfc1928/LICENSE: the two dashes in alksocks' own provenance header rewritten; the MIT legal text untouched - AGENTS.md adds Project Convention 18: no em dashes in prose; varied restructure, no spaced-hyphen substitutes, fenced prose rule, en dash numeric ranges exempt, docs/reviews/ exempt from the greps - tasks/*.md frontmatter: the name values rewritten by the sweep are now quoted (a raw colon inside an unquoted YAML scalar parsed as null); taskgraph validate reports 37 tasks, no issues Verification: cargo test; cargo clippy --all-targets -D warnings; cargo fmt --check; cargo doc --no-deps; fuzz corpus replay (cargo test --manifest-path fuzz/shared/Cargo.toml); wasm check and clippy for wasm32-unknown-unknown with -D warnings; em dash grep over the repo returns matches only in docs/reviews/ and the bast.md BAST JSON data fence
This commit is contained in:
1 parent
9ca8c52601
commit
e196ed43e3
115 files changed
+3593
-3531
No files matched your search
+6
-6
@@ -5,19 +5,19 @@ libFuzzer targets for alksocks' wire surfaces (see
|
||||
|
||||
## Layout
|
||||
|
||||
- `fuzz_targets/` — one binary per target; thin `fuzz_target!` wrappers.
|
||||
- `shared/` — the invariant logic, as a plain library so normal
|
||||
- `fuzz_targets/`: one binary per target; thin `fuzz_target!` wrappers.
|
||||
- `shared/`: the invariant logic, as a plain library so normal
|
||||
`cargo test` (stable toolchain) can replay the committed corpora
|
||||
through the same invariants (`fuzz/shared/src/*.rs` `#[cfg(test)]`
|
||||
modules; quinn's CI pattern). The fuzz binaries are nightly-only;
|
||||
the shared crate is stable-clean.
|
||||
- `corpus/<target>/` — committed hand-made seeds (regenerate with
|
||||
- `corpus/<target>/`: committed hand-made seeds (regenerate with
|
||||
`python3 fuzz/gen_fuzz_seeds.py` from the repo root). Grown corpora
|
||||
and artifacts are gitignored.
|
||||
- `run-detached.sh` — mandatory runner for agent sessions: wraps
|
||||
- `run-detached.sh`: mandatory runner for agent sessions; wraps
|
||||
`cargo fuzz run` in `setsid` + `nohup` + log redirection so an OOM
|
||||
in a target can never take down the agent host (research doc §5).
|
||||
- `json.dict` — JSON token dictionary for the params target.
|
||||
- `json.dict`: JSON token dictionary for the params target.
|
||||
|
||||
## Targets
|
||||
|
||||
@@ -31,7 +31,7 @@ libFuzzer targets for alksocks' wire surfaces (see
|
||||
|
||||
`cargo fuzz build` and `cargo fuzz run` must be executed with `fuzz/`
|
||||
(or deeper) as the working directory so rustup selects the pinned
|
||||
nightly toolchain — the detached runner handles that itself.
|
||||
nightly toolchain; the detached runner handles that itself.
|
||||
|
||||
```sh
|
||||
# build (nightly, pinned by rust-toolchain.toml inside fuzz/; run from fuzz/)
|
||||
|
||||
@@ -81,7 +81,7 @@ def datagram_codec_seeds():
|
||||
|
||||
# The joint-bound max-domain shape: a legal RFC payload that would
|
||||
# overflow the u16 (65507 to a 255-char domain = 65766). The frame
|
||||
# itself can never carry that length field — encode the closest
|
||||
# itself can never carry that length field, so encode the closest
|
||||
# wire-legal shape (0xFFFF declared with the max-domain body) for
|
||||
# the decode walk; the encode-side 65766 rejection is pinned in
|
||||
# the shared invariants.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `Arbitrary` support for `serde_json::Value` — a bounded-depth
|
||||
//! `Arbitrary` support for `serde_json::Value`: a bounded-depth
|
||||
//! recursive generator shared by the semantic targets. Depth and size
|
||||
//! bounds keep exec/s high (serde_json's own 128-depth recursion limit
|
||||
//! is the decode-side guard; this generator explores structure).
|
||||
|
||||
@@ -35,7 +35,7 @@ fn probe_addr() -> TargetAddr {
|
||||
pub fn fuzz_datagram_codec(data: &[u8]) {
|
||||
// The infallible reassembly direction: any chunk sequence feeds
|
||||
// cleanly. Drive the reader with the whole input and with awkward
|
||||
// 3-byte slices — splitting transparency must hold for both.
|
||||
// 3-byte slices; splitting transparency must hold for both.
|
||||
let mut whole = DatagramReader::new();
|
||||
let whole_dgs = whole.feed(data);
|
||||
assert_retained_bound(&whole, data.len());
|
||||
@@ -117,7 +117,7 @@ pub fn fuzz_datagram_codec(data: &[u8]) {
|
||||
|
||||
// Re-frame round-trip: for input that is a concatenation of
|
||||
// complete frames, parsing then re-framing reproduces the frames
|
||||
// byte-for-byte — except a `len == 0` frame, which is well-framed
|
||||
// byte-for-byte, except for a `len == 0` frame, which is well-framed
|
||||
// but decodes to `Err(EmptyFrame)` (protocol garbage; it has no
|
||||
// addr to re-frame). The frame count holds over every frame; the
|
||||
// byte-for-byte re-frame holds over the decodable ones.
|
||||
@@ -136,7 +136,7 @@ pub fn fuzz_datagram_codec(data: &[u8]) {
|
||||
}
|
||||
Err(WireError::EmptyFrame) => {
|
||||
// The frame-walk must agree this is a `len == 0`
|
||||
// frame — the only zero-inner-byte shape.
|
||||
// frame: the only zero-inner-byte shape.
|
||||
let declared = u16::from_be_bytes([frame_bytes[0], frame_bytes[1]]) as usize;
|
||||
assert_eq!(declared, 0);
|
||||
assert_eq!(frame_bytes.len(), 2);
|
||||
@@ -147,7 +147,7 @@ pub fn fuzz_datagram_codec(data: &[u8]) {
|
||||
// A well-framed frame can decode to the address
|
||||
// garbage shapes: the declared length carried a
|
||||
// too-short, non-UTF-8, or unknown-ATYP addr
|
||||
// region. The decode refused the addr — the only
|
||||
// region. The decode refused the addr, so the only
|
||||
// re-frame possible is the raw frame itself, which
|
||||
// the count/prefix walks above already pin.
|
||||
}
|
||||
@@ -159,7 +159,7 @@ pub fn fuzz_datagram_codec(data: &[u8]) {
|
||||
}
|
||||
|
||||
// Frame direction: the joint bound exactly at the edge, with the
|
||||
// sizes echoed — including the 65766 max-domain shape (a legal RFC
|
||||
// sizes echoed, including the 65766 max-domain shape (a legal RFC
|
||||
// payload that would overflow the u16 length field).
|
||||
let max_payload = MAX_INNER_LEN - addr_encoding_len(&probe_addr());
|
||||
let framed = frame(&probe_addr(), &vec![0u8; max_payload]).expect("exact limit is legal");
|
||||
@@ -214,7 +214,7 @@ pub fn fuzz_datagram_codec(data: &[u8]) {
|
||||
// Mid-frame observability: after the final feed, the reader is
|
||||
// mid-frame exactly when the input ends inside a frame (a partial
|
||||
// frame beyond the 2-byte header; a dangling 1-byte header
|
||||
// fragment is NOT mid-frame — the header is not yet consumed).
|
||||
// fragment is NOT mid-frame: the header is not yet consumed).
|
||||
let expected_mid = ends_inside_frame(data, well_framed_len);
|
||||
assert_eq!(
|
||||
whole.is_mid_frame(),
|
||||
@@ -236,7 +236,7 @@ fn result_shapes(dgs: &[Result<(TargetAddr, bytes::Bytes), WireError>]) -> Vec<S
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// A cheap digest stand-in: length + first/last bytes — enough to pin
|
||||
/// A cheap digest stand-in: length + first/last bytes, enough to pin
|
||||
/// equality across the split feeds.
|
||||
fn digest(payload: &[u8]) -> u64 {
|
||||
let mut h = payload.len() as u64;
|
||||
@@ -251,7 +251,7 @@ fn digest(payload: &[u8]) -> u64 {
|
||||
|
||||
/// Walk the framing exactly as the decoder does: complete frames from
|
||||
/// the start; the walk stops at the first frame that overruns the
|
||||
/// input. Returns the frames and the byte length they occupy — the
|
||||
/// input. Returns the frames and the byte length they occupy: the
|
||||
/// remainder (if any) is a partial/trailing region.
|
||||
fn split_frames(data: &[u8]) -> (Vec<&[u8]>, usize) {
|
||||
let mut frames = Vec::new();
|
||||
@@ -269,12 +269,12 @@ fn split_frames(data: &[u8]) -> (Vec<&[u8]>, usize) {
|
||||
}
|
||||
|
||||
/// Count complete, well-formed-address frames whose payload region is
|
||||
/// empty — the only source an empty-payload datagram may come from
|
||||
/// empty: the only source an empty-payload datagram may come from
|
||||
/// (the F-2 walk, alktunnels' count_empty_frames shape adapted to the
|
||||
/// `(addr, payload)` inner structure: a frame's empty payload needs
|
||||
/// `declared == addr_len`, not `declared == 0`). Domain-form frames
|
||||
/// must also carry a valid-UTF-8 domain: the codec validates UTF-8
|
||||
/// before the payload split — a non-UTF-8 domain with an empty payload
|
||||
/// before the payload split, so a non-UTF-8 domain with an empty payload
|
||||
/// region surfaces as `DomainNotUtf8`, never as an empty datagram.
|
||||
fn count_empty_payload_frames(data: &[u8]) -> usize {
|
||||
let mut count = 0usize;
|
||||
@@ -315,7 +315,7 @@ fn ends_inside_frame(data: &[u8], well_framed_len: usize) -> bool {
|
||||
// Mid-frame iff the well-framed prefix leaves a *parsed but
|
||||
// incomplete* frame: a remainder ≥ 2 bytes means the length header
|
||||
// was consumed and the frame is in flight (a remainder of 1 byte
|
||||
// is a buffered header fragment — not yet a frame in flight).
|
||||
// is a buffered header fragment, not yet a frame in flight).
|
||||
let remainder = data.len() - well_framed_len;
|
||||
remainder >= 2
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@ pub fn fuzz_params_parse(data: &[u8]) {
|
||||
}
|
||||
|
||||
fn parse_shapes(value: &Value) {
|
||||
// The trivial params: the canonical form is `{}` — it parses and
|
||||
// The trivial params: the canonical form is `{}`; it parses and
|
||||
// round-trips to the canonical form. Unknown fields reject loudly
|
||||
// (the serde pin and the schema's additionalProperties agree —
|
||||
// (the serde pin and the schema's additionalProperties agree;
|
||||
// ADR-001). NOTE the serde reality the harness must model: a
|
||||
// fieldless struct deserializes from ANY empty JSON composite
|
||||
// (the seq form `[]` included) — the wire's `type: object` input
|
||||
// (the seq form `[]` included), but the wire's `type: object` input
|
||||
// schema (the registry's gate) is what keeps the non-object forms
|
||||
// from ever reaching the type; the serde pin is the second gate.
|
||||
let parses = serde_json::from_value::<Socks5Params>(value.clone()).is_ok();
|
||||
@@ -54,7 +54,7 @@ fn parse_shapes(value: &Value) {
|
||||
assert_ne!(
|
||||
value,
|
||||
&serde_json::json!({}),
|
||||
"the empty object must parse — anything else must reject"
|
||||
"the empty object must parse; anything else must reject"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ fn parse_shapes(value: &Value) {
|
||||
|
||||
// The reason surface is a pass-through extraction, not a validated
|
||||
// table: Some(details.reason) iff the code is channel:open_failed
|
||||
// and the reason is a string — any string surfaced as-is (the
|
||||
// and the reason is a string; any string is surfaced as-is (the
|
||||
// alktunnels harness-model finding #2, the modeled contract).
|
||||
let code = value
|
||||
.get("code")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Invariants for the forked RFC 1928 core (`src/rfc1928/`, ADR-013 —
|
||||
//! Invariants for the forked RFC 1928 core (`src/rfc1928/`; ADR-013:
|
||||
//! fully owned code): no-panic on any byte sequence across the whole
|
||||
//! handshake state machine, every malformed shape is a typed error
|
||||
//! (the F-1 class — wire bytes must never reach `unreachable!()`),
|
||||
//! (the F-1 class: wire bytes must never reach `unreachable!()`),
|
||||
//! round-trips for well-formed messages, and the full-domain
|
||||
//! reply-code table. Both protocol directions are covered: the server
|
||||
//! typestate (the producer drives it against consumer bytes) and the
|
||||
@@ -80,7 +80,7 @@ async fn fuzz_read_address(data: &[u8]) {
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
// Typed failure — fine; the ATYP gate maps unknown
|
||||
// Typed failure: fine; the ATYP gate maps unknown
|
||||
// types to IncorrectAddressType (the F-1 class).
|
||||
if let AddrError::IncorrectAddressType = err {
|
||||
assert!(
|
||||
@@ -191,7 +191,7 @@ async fn fuzz_udp_header(data: &[u8]) {
|
||||
|
||||
/// `ReplyError::from_u8` over the full u8 domain: total, no panic,
|
||||
/// unassigned codes map to the GeneralFailure catch-all (the F-1
|
||||
/// pin — the direct probe of the class the fork already fixed).
|
||||
/// pin, the direct probe of the class the fork already fixed).
|
||||
fn fuzz_reply_table(data: &[u8]) {
|
||||
for &code in data {
|
||||
let reply = ReplyError::from_u8(code);
|
||||
|
||||
Reference in new issue
Block a user