docs(comments): sweep em dashes from source comments, test prose, and manifest

Same treatment for the prose the compiler carries: doc and line comments
across src/ and tests/, test assertion and error-message strings (prose
surfaces: the tunnel-map registration/establish errors, the key-source
decode payload, assertion messages), scripts/matrix.sh comment lines,
the fuzz sidecar's comments and README (corpus seeds untouched), and the
Cargo.toml comment headers plus the crates.io description. The AGENTS.md
no-em-dash rule (convention 14) now holds for the whole tracked tree.

Verification: repo-wide git grep is clean (single exempt hit: the
verification command's own search pattern), cargo test (18 suites, zero
failures, default and per-feature builds via scripts/matrix.sh), clippy
--all-targets -D warnings and clippy all-features, fmt --check, doc
--no-deps, and the fuzz corpus gate (fuzz/shared, 11 tests) all green.
This commit is contained in:
glm-5.3-flash committed 2026-11-04 20:28:05 +00:00
1 parent f4b5f896b4
commit 1e583ab55b
55 files changed
+1756 -1751

No files matched your search

+16 -16
View File
@@ -1,16 +1,16 @@
# alkssh fuzzing
libFuzzer targets for alkssh's ingress surfaces — the family standing
libFuzzer targets for alkssh's ingress surfaces: the family standing
rule (anything worth fuzzing gets fuzzed), landed per the plan's wave-5
charter and the alkgit convention: the JSON schema gate itself is
charter and the alkgit convention. The JSON schema gate itself is
alkcall's surface, family-fuzzed there; SSH framing, key exchange, and
the channel multiplexer are russh's wire (family ADR-003) — the
the channel multiplexer are russh's wire (family ADR-003); the
alkssh-owned post-framing ingress is what fuzzes here.
## 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 (`#[cfg(test)]` modules; quinn's CI
pattern). The fuzz binaries are nightly-only; the shared crate is
@@ -18,34 +18,34 @@ alkssh-owned post-framing ingress is what fuzzes here.
`#[doc(hidden)]` fuzz-visible exposures: the exec-grammar parse path
in `src/dispatch.rs`, the key-decode arm `decode_key_arm` in
`src/key_source.rs`, and the `terminal_params` conversion in
`src/tty_map.rs` — the recorded exposure decisions: the ingresses are
`src/tty_map.rs`; the recorded exposure decisions: the ingresses are
production-private, so the exposures are the production surfaces
themselves, never forks; the fingerprint ingress is fully public API)
and must never become a dependency of the root crate.
- `corpus/<target>/` — committed hand-made seeds (regenerate with
`python3 fuzz/gen_fuzz_seeds.py` from the repo root — must be a no-op
diff on the committed set). Only `seed-*` files are tracked; grown
- `corpus/<target>/`: committed hand-made seeds (regenerate with
`python3 fuzz/gen_fuzz_seeds.py` from the repo root; the result must
be a no-op diff on the committed set). Only `seed-*` files are tracked; grown
corpora (SHA-named units), artifacts, and coverage output are
gitignored and are **not** merged back into the committed seeds.
- `run-detached.sh` — mandatory runner for agent sessions: wraps
- `run-detached.sh`: the mandatory runner for agent sessions. It wraps
`cargo fuzz run` in `setsid` + `nohup` + log redirection so an OOM
in a target can never take down the agent host.
- `gen_fuzz_seeds.py` — deterministic seed generator (quiche pattern).
- `gen_fuzz_seeds.py`: deterministic seed generator (quiche pattern).
## Targets
| Target | Drives | Invariants |
|---|---|---|
| `exec_parse` | the production exec-grammar parse (`parse_exec`, `src/dispatch.rs` — the handler's ingress shape: byte string → `from_utf8` fallback `""` → parse; the pre-parse `max_exec_command_len` cap and the git row's `invalid-repo-id` gate are the root contract rows' territory, the parse is everything behind them) | no-panic on any byte string (any length behind the cap); the typed routing classes (git-parseable → the git row, non-git → the session pipe path, service-only → decline-with-reason) agree with a from-spec grammar oracle (the differential pair) and are stable per input; for `Git` routes the repo-id gate classification is deterministic |
| `fingerprint` | the crate's single fingerprint path ([`alkssh::fingerprint`] over `russh::keys::parse_public_key_base64` — ADR-004 decision 1) | no-panic on arbitrary key blobs (a non-blob is the parse's typed refusal); every accepted key's fingerprint is exactly the `SHA256:` + unpadded-standard-base64 contract shape (43-char body, canonical alphabet, decodes to a 32-byte digest, re-encodes canonically); determinism; the committed `ssh-keygen -lf` parity vectors still agree (the corpus-gate test row) |
| `key_decode` | the decrypt-at-use chain's decode arm (`decode_key_arm`, `src/key_source.rs` — the production `decrypt_and_decode` conversion exposed `#[doc(hidden)]`) + the underlying `decode_secret_key` passphrase arm (robustness drive — the production chain always passes `None`) | no-panic on arbitrary plaintext shapes (the vault's `String` shape, lossy from arbitrary bytes) and passphrase shapes; every failure on the production arm is exactly the typed `KeySourceError::Decode` class — never a catch-all, never a panic; the failure text never carries the plaintext; the `Zeroizing` posture rides the drive (the wrap is what the adapter seam applies); determinism |
| `terminal_params` | the `pty_request` args → `TerminalParamsWire` conversion (`terminal_params`, `src/tty_map.rs` — OQ-44's SSH modes → reserved-modes `{}` JSON) | no-panic on arbitrary term strings, u32 dimensions, and mode blobs (the modes are never parsed — reserved); the dimension clamp is exactly the recorded cap (`min(value, 65535)`, never a magnitude truncation); `term` preserved verbatim; the reserved-modes JSON is always exactly `{}`; the full serialized wire shape is deterministic per input |
| `exec_parse` | the production exec-grammar parse (`parse_exec`, `src/dispatch.rs`, the handler's ingress shape: byte string → `from_utf8` fallback `""` → parse; the pre-parse `max_exec_command_len` cap and the git row's `invalid-repo-id` gate are the root contract rows' territory, the parse is everything behind them) | no-panic on any byte string (any length behind the cap); the typed routing classes (git-parseable → the git row, non-git → the session pipe path, service-only → decline-with-reason) agree with a from-spec grammar oracle (the differential pair) and are stable per input; for `Git` routes the repo-id gate classification is deterministic |
| `fingerprint` | the crate's single fingerprint path ([`alkssh::fingerprint`] over `russh::keys::parse_public_key_base64`, ADR-004 decision 1) | no-panic on arbitrary key blobs (a non-blob is the parse's typed refusal); every accepted key's fingerprint is exactly the `SHA256:` + unpadded-standard-base64 contract shape (43-char body, canonical alphabet, decodes to a 32-byte digest, re-encodes canonically); determinism; the committed `ssh-keygen -lf` parity vectors still agree (the corpus-gate test row) |
| `key_decode` | the decrypt-at-use chain's decode arm (`decode_key_arm`, `src/key_source.rs`, the production `decrypt_and_decode` conversion exposed `#[doc(hidden)]`) + the underlying `decode_secret_key` passphrase arm (a robustness drive; the production chain always passes `None`) | no-panic on arbitrary plaintext shapes (the vault's `String` shape, lossy from arbitrary bytes) and passphrase shapes; every failure on the production arm is exactly the typed `KeySourceError::Decode` class, never a catch-all, never a panic; the failure text never carries the plaintext; the `Zeroizing` posture rides the drive (the wrap is what the adapter seam applies); determinism |
| `terminal_params` | the `pty_request` args → `TerminalParamsWire` conversion (`terminal_params`, `src/tty_map.rs`, OQ-44's SSH modes → reserved-modes `{}` JSON) | no-panic on arbitrary term strings, u32 dimensions, and mode blobs (the modes are never parsed; they are reserved); the dimension clamp is exactly the recorded cap (`min(value, 65535)`, never a magnitude truncation); `term` preserved verbatim; the reserved-modes JSON is always exactly `{}`; the full serialized wire shape is deterministic per input |
## Commands
`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/)
+2 -2
View File
@@ -1,7 +1,7 @@
#![no_main]
//! The exec-grammar target (`mapping.md` §Session-channel dispatch —
//! the git-parseable exec grammar + the dispatch parse path) — thin
//! The exec-grammar target (`mapping.md` §Session-channel dispatch,
//! the git-parseable exec grammar + the dispatch parse path): a thin
//! wrapper over the shared invariant battery.
use libfuzzer_sys::fuzz_target;
+2 -2
View File
@@ -1,7 +1,7 @@
#![no_main]
//! The fingerprint-format target (ADR-004 decision 1 — the `SHA256:` +
//! unpadded base64 path) — thin wrapper over the shared invariant
//! The fingerprint-format target (ADR-004 decision 1, the `SHA256:` +
//! unpadded base64 path): a thin wrapper over the shared invariant
//! battery.
use libfuzzer_sys::fuzz_target;
+2 -2
View File
@@ -1,7 +1,7 @@
#![no_main]
//! The key-decode target (ADR-005 decision 2 — the decrypt-at-use
//! chain's `decode_secret_key` seam) — thin wrapper over the shared
//! The key-decode target (ADR-005 decision 2, the decrypt-at-use
//! chain's `decode_secret_key` seam): a thin wrapper over the shared
//! invariant battery.
use libfuzzer_sys::fuzz_target;
+2 -2
View File
@@ -1,8 +1,8 @@
#![no_main]
//! The terminal-parameters target (`tty_map.rs` — the `pty_request`
//! The terminal-parameters target (`tty_map.rs`, the `pty_request`
//! args → `TerminalParamsWire` conversion, the SSH modes → reserved
//! modes JSON) — thin wrapper over the shared invariant battery.
//! modes JSON): a thin wrapper over the shared invariant battery.
use libfuzzer_sys::fuzz_target;
+1 -1
View File
@@ -1,7 +1,7 @@
//! The corpus-replay plumbing: the committed seeds replay through the
//! invariant batteries on the stable toolchain (`cargo test
//! --manifest-path fuzz/shared/Cargo.toml` runs this in
//! `#[cfg(test)]` modules across the ingress units — the quinn CI
//! `#[cfg(test)]` modules across the ingress units, the quinn CI
//! pattern), so the gate never needs a fuzzing session.
pub fn read_corpus(target: &str) -> Vec<Vec<u8>> {
+7 -7
View File
@@ -1,10 +1,10 @@
//! The exec-grammar ingress (`mapping.md` §Session-channel dispatch,
//! `dispatch.rs`): the production `parse_exec` path driven on arbitrary
//! command strings — no-panic, typed routing classes, and agreement
//! command strings: no-panic, typed routing classes, and agreement
//! with a from-spec re-derivation of the grammar. The production
//! ingress shape is the handler's: the per-length cap
//! (`Limits::max_exec_command_len`) is enforced *before* the parse
//! (decline first, alloc never — the cap's pre-parse placement is
//! (decline first, alloc never; the cap's pre-parse placement is
//! pinned by the root's contract rows in `tests/dispatch_core.rs`);
//! everything behind the cap is this battery: a byte string →
//! `std::str::from_utf8(..).unwrap_or("")` → the parse (invalid UTF-8
@@ -43,7 +43,7 @@ pub fn fuzz_exec_parse(data: &[u8]) {
/// The from-spec oracle: the grammar re-derived from the dispatch
/// module's documented rules, sharing no code with the
/// implementation — the differential pair (the repo_id target's
/// implementation, the differential pair (the repo_id target's
/// oracle pattern).
fn spec_parse(command: &str) -> ExecRoute {
let (token, argument) = match command.split_once(' ') {
@@ -64,7 +64,7 @@ fn spec_parse(command: &str) -> ExecRoute {
}
/// The serving-service arm of the oracle: a serving token must carry
/// a non-empty, path-shaped argument (SSH exec must carry the repo —
/// a non-empty, path-shaped argument (SSH exec must carry the repo;
/// service-only is the decline-with-reason class; anything else
/// unparseable is the pipe row).
fn spec_serving(service: GitServiceRoute, argument: Option<&str>) -> ExecRoute {
@@ -78,9 +78,9 @@ fn spec_serving(service: GitServiceRoute, argument: Option<&str>) -> ExecRoute {
}
}
/// The argument grammar re-derived: a path-shaped argument — fully
/// single-quote-wrapped (a non-empty inner ending in a quote) or
/// unquoted absolute — normalized by stripping the quotes, one
/// The argument grammar re-derived: a path-shaped argument, either
/// fully single-quote-wrapped (a non-empty inner ending in a quote) or
/// unquoted absolute, normalized by stripping the quotes, one
/// leading `/`, and one trailing `.git` suffix (an empty-after-strip
/// prefix keeps the suffix; the git row's validation declines the
/// empties, never a silent emptying).
+4 -4
View File
@@ -1,9 +1,9 @@
//! The fingerprint format ingress (ADR-004 decision 1,
//! `fingerprint.rs`): arbitrary key blobs through the crate's single
//! fingerprint path — the composition of russh's public OpenSSH blob
//! fingerprint path: the composition of russh's public OpenSSH blob
//! parse (`russh::keys::parse_public_key_base64`, the same re-export
//! the crate's surface rides) and the crate's public
//! [`alkssh::fingerprint`]. The ingress is fully public API — the
//! [`alkssh::fingerprint`]. The ingress is fully public API; the
//! recorded exposure decision needs no `#[doc(hidden)]` surface.
//!
//! Invariants: no-panic on any byte string (a non-blob is the parse's
@@ -11,7 +11,7 @@
//! accepted key's fingerprint is exactly the `SHA256:` + unpadded
//! standard base64 contract shape (one `:` separator, 43-char body,
//! canonical alphabet, 32-byte digest decoded exactly); determinism
//! (same key, same string — and the same string re-parses to the same
//! (same key, same string; the same string re-parses to the same
//! verdict). The committed `ssh-keygen -lf` parity vectors
//! (`tests/vectors/`) are replayed by the corpus-gate tests below.
@@ -98,7 +98,7 @@ fn decode_unpadded(body: &str) -> Option<Vec<u8>> {
Some(out)
}
/// A from-spec unpadded standard-base64 encoder — the re-encode half.
/// A from-spec unpadded standard-base64 encoder: the re-encode half.
fn encode_unpadded(bytes: &[u8]) -> String {
const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut out = Vec::new();
+10 -10
View File
@@ -1,30 +1,30 @@
//! The key-decode ingress (ADR-005 decision 2, `key_source.rs`): the
//! decrypt-at-use chain's one-time `decode_secret_key` conversion —
//! decrypt-at-use chain's one-time `decode_secret_key` conversion,
//! driven through the production arm itself
//! ([`alkssh::decode_key_arm`], the `#[doc(hidden)]` exposure that is
//! `decrypt_and_decode`'s exact decode stage, not a fork).
//!
//! Invariants: no-panic on arbitrary plaintext shapes (the production
//! plaintext is the vault's `Zeroizing<String>` — valid UTF-8 by
//! plaintext is the vault's `Zeroizing<String>`, valid UTF-8 by
//! return type; the battery shapes arbitrary bytes into that form via
//! the lossy conversion and drives it through the same zeroized
//! wrap the vault's `decrypt` return applies, alkssh 0.2.0 seam) and
//! arbitrary passphrase shapes (the
//! underlying decode's passphrase arm — the production chain always
//! underlying decode's passphrase arm; the production chain always
//! passes `None`; the `Some` arm is robustness drive only); every
//! failure is the typed [`KeySourceError::Decode`] class — never a
//! failure is the typed [`KeySourceError::Decode`] class, never a
//! catch-all, never another variant on this arm, never a panic; the
//! failure text never carries the plaintext (the assertable half of
//! the Zeroizing posture — the plaintext exists solely inside the
//! call, dropped zeroized; no retention, no static cache — and the
//! the Zeroizing posture: the plaintext exists solely inside the
//! call, dropped zeroized, with no retention and no static cache; the
//! typed error's only payload is the decode machinery's message
//! class, never the plaintext).
//!
//! The no-echo invariant's assertable form is *the bounded-message
//! pair* (reshaped in-task after the first campaign's finding): the
//! failure text is a bounded fixed message (≤ 256 bytes — the decode
//! failure text is a bounded fixed message (≤ 256 bytes; the decode
//! machinery's vocabulary, never a growable echo), and no 256-byte
//! contiguous plaintext run appears in it — which, given the bound,
//! contiguous plaintext run appears in it, which, given the bound,
//! cannot false-fire (a string shorter than 256 bytes cannot contain a
//! 256-byte run). A plaintext-`contains`-message check in the other
//! direction was unsound: an attacker-chosen plaintext can be a
@@ -70,7 +70,7 @@ fn fingerprint(key: &russh::keys::PrivateKey) -> String {
}
/// The typed-class invariant: `Ok` or exactly
/// [`KeySourceError::Decode`] — the decode arm's only failure shape —
/// [`KeySourceError::Decode`] (the decode arm's only failure shape),
/// and the Decode text is the bounded fixed-message pair (see the
/// module docs; the two assertions are jointly airtight).
fn typed_decode_class(outcome: &Result<russh::keys::PrivateKey, KeySourceError>, plaintext: &str) {
@@ -123,7 +123,7 @@ mod invariants {
}
/// A plaintext that is not OpenSSH-PEM key material is a
/// [`KeySourceError::Decode`] refusal (the inner-format contract —
/// [`KeySourceError::Decode`] refusal (the inner-format contract:
/// decision 1, never a fallback).
#[test]
fn non_key_plaintext_is_the_typed_decode_refusal() {
+5 -5
View File
@@ -1,22 +1,22 @@
//! The terminal-parameters ingress (`tty_map.rs`, OQ-44's reserved
//! modes): the `pty_request` args → `TerminalParamsWire` conversion
//! driven through the production [terminal_params] surface (the
//! `#[doc(hidden)]` exposure — `handler.rs`'s `pty_request` row
//! `#[doc(hidden)]` exposure: `handler.rs`'s `pty_request` row
//! constructs the stored params with exactly this function).
//!
//! The harness's framed feed (deterministic, seed-friendly):
//! `term_len` (u32 LE, bounded to the remaining input) + the term
//! bytes + four u32 LE dimensions (`cols`/`rows`/pixel width/height,
//! zero-defaulted when the input runs out) + an arbitrary trailing
//! mode blob. The mode blob is *never parsed* — the SSH ttymodes ride
//! the reserved `{}` JSON (OQ-44, backends ignore modes in v1) — so
//! mode blob. The mode blob is *never parsed*; the SSH ttymodes ride
//! the reserved `{}` JSON (OQ-44, backends ignore modes in v1), so
//! the battery's totality over arbitrary mode blobs is by
//! construction; the framing demonstrates it (the conversion's
//! outcome never varies with the blob).
//!
//! Invariants: no-panic on any term string (empty, unicode, control
//! characters) and any u32 dimensions; the dimension clamp is the
//! recorded cap (`min(value, 65535)` — a dimension beyond 65535 is
//! recorded cap (`min(value, 65535)`; a dimension beyond 65535 is
//! nonsense on both papers) computed exactly, never a magnitude
//! truncation; `term` is preserved verbatim; the reserved-modes JSON
//! is always exactly the empty object; the full wire shape is
@@ -89,7 +89,7 @@ pub fn fuzz_terminal_params(data: &[u8]) {
}
}
/// The cursor's u32 (zero-defaulted when the framed field is absent —
/// The cursor's u32 (zero-defaulted when the framed field is absent;
/// the feed is total over any input).
fn take_u32(cursor: &mut &[u8]) -> u32 {
if cursor.len() >= 4 {