- 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
202 lines
10 KiB
Markdown
202 lines
10 KiB
Markdown
---
|
|
id: implementation/fix-rfc1928-posture
|
|
name: "Fix: rfc1928 keep-or-cut pass + honest refusal vocabulary (review-002 N-1, N-4)"
|
|
status: completed
|
|
depends_on: []
|
|
scope: narrow
|
|
risk: medium
|
|
impact: component
|
|
level: implementation
|
|
---
|
|
|
|
## Description
|
|
|
|
The review-001 keep-or-cut triggers have fired (the producer layer
|
|
landed without consuming the anticipated surfaces), and the
|
|
in-stream refusal vocabulary mislabels policy refusals as bugs.
|
|
One posture pass over both
|
|
(`docs/reviews/002-protocol-core-implementation-review.md`):
|
|
|
|
1. **N-1 (minor): the fork's consumerless machinery.** The
|
|
producer drives `negotiate_auth` with `&[NoAuthentication]` only;
|
|
the seams carry their own error vocabularies; `pump_bidi`
|
|
replaced `transfer`. Per the review's disposition:
|
|
|
|
**Cut** (the producer already replaced them; ADR-013's
|
|
no-speculative-surface posture wins):
|
|
- `StandardAuthentication` + `allow_no_auth` +
|
|
`auth_method_enums!`'s generated enum
|
|
(`src/rfc1928/server.rs:448-482`; the macro itself can stay or
|
|
go with it, as nothing else uses it)
|
|
- `skip_auth_this_is_not_rfc_compliant`
|
|
(`src/rfc1928/server.rs:185-187`; the consumer typestate's
|
|
`skip_auth` config covers the in-tree need)
|
|
- `CheckResult`'s `Option`/`Result` impls
|
|
(`src/rfc1928/server.rs:162-172`; keep `bool`, and re-add with a
|
|
typed-rejection design if a door ever wants it)
|
|
|
|
**Keep with note** (the hand-port-map posture, ADR-013):
|
|
- `ConnectError` (constructed nowhere, but the hand-port map's
|
|
companion; its reply-code mapping is what a future port
|
|
reuses, which the module doc already says so)
|
|
- `transfer`/`wait_on_tcp` (differential-suite-consumed; their
|
|
semantics stay pinned)
|
|
- `SocksServerError::Bug` (now consumed by the producer's
|
|
refusal sites; see N-4 for its display)
|
|
|
|
The coverage blocks resolve automatically (the accepted-uncovered
|
|
posture was exactly these blocks). Update the module docs where
|
|
they name the cut machinery (e.g. `util/stream.rs`'s
|
|
`ConnectError` note, the `auth_method_enums!` doc's "generate a
|
|
similar enum" line) and the differential suite's comment that
|
|
references `StandardAuthentication`'s method ordering
|
|
(`tests/rfc1928-differential.rs:103`; reword to the RFC's
|
|
ordering rationale, not the cut type).
|
|
|
|
2. **N-4 (observation): the task-log refusal vocabulary mislabels
|
|
policy refusals.** Five in-stream refusal sites return errors the
|
|
wire never sees (the wire got its RFC-faithful reply already;
|
|
these are log/observability signals only), but the vocabulary
|
|
lies: the BIND command gate returns
|
|
`SocksServerError::AuthenticationRejected` (an ACL refusal is not
|
|
"authentication rejected"; `src/producer.rs:230-233`), and four
|
|
seam refusals return `Bug("... refused in-stream")` (a policy
|
|
refusal is not a bug, but the display literally says `BUG: ...` at
|
|
`src/producer.rs:262-264`, `302-304`, `483-485`, `541-543`)..
|
|
An operator grepping `BUG:` chases ghosts that are
|
|
working-as-designed refusals.
|
|
|
|
Fix per the review's sketch: add one variant
|
|
(`SocksServerError::Refused(&'static str)`-shaped) for the
|
|
in-stream seam refusals and the command gate; use it at the five
|
|
refusal sites. The wire bytes are untouched (these variants never
|
|
reach the wire, since the RFC reply went out first). Alternatively
|
|
keep `Bug` and rename its display, but the dedicated variant is
|
|
the honest shape; pick one and record it. Mind the fork's
|
|
provenance posture (convention 17): this is an owned-code
|
|
extension, so extend the module doc's carried-changes note if the
|
|
variant list grows.
|
|
|
|
Tests: the five refusal sites' existing tests assert the mapped
|
|
reply codes (unchanged, since the wire is untouched); update any test
|
|
asserting the *session-error* variant at those sites (the harness's
|
|
`Bug` at `tests/harness.rs:387` is its own shape; leave it unless it
|
|
wants the new variant); one test pinning the new variant's
|
|
display/shape if the vocabulary is table-tested elsewhere.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [x] `StandardAuthentication`, `allow_no_auth`, the generated
|
|
enum, `skip_auth_this_is_not_rfc_compliant`, and
|
|
`CheckResult`'s `Option`/`Result` impls are cut; `bool` stays
|
|
- [x] `ConnectError`, `transfer`/`wait_on_tcp`, `Bug` kept, each
|
|
with its keep-note current (module docs name the posture)
|
|
- [x] The differential suite stays green (the cut machinery was not
|
|
load-bearing for it; the `:103` comment reworded)
|
|
- [x] The five in-stream refusal sites carry the honest vocabulary
|
|
(one variant or the renamed display, with the recorded choice);
|
|
the wire bytes are untouched (the reply-code assertions stay
|
|
green)
|
|
- [x] The fork's provenance notice is extended, not replaced, if the
|
|
carried-changes note names the new variant (convention 17)
|
|
- [x] `cargo test` green; clippy/fmt clean; both wasm checks clean;
|
|
`cargo doc --no-deps` clean (module docs changed)
|
|
|
|
## References
|
|
|
|
- docs/reviews/002-protocol-core-implementation-review.md §N-1, §N-4
|
|
- docs/reviews/001-rfc1928-full-review.md (the keep-or-cut triggers
|
|
this closes out; the C-1 misleading-display note)
|
|
- docs/architecture/decisions/013-targeted-fork.md (the hand-port
|
|
map vs no-speculative-surface distinction)
|
|
- docs/architecture/decisions/006-access-control.md (the gate:
|
|
policy refusals are not authentication failures)
|
|
- src/rfc1928/LICENSE (the provenance notice; extend, never
|
|
replace)
|
|
|
|
## Notes
|
|
|
|
- **N-4 shape chosen: the dedicated variant**, namely
|
|
`SocksServerError::Refused(&'static str)` with display `"Refused:
|
|
{0}"`, not the `Bug` display rename. The review's sketch favored
|
|
the dedicated variant ("the honest shape"), and the vocabulary
|
|
split is real: `Bug` stays upstream-verbatim (`BUG: {0}`, its
|
|
"should be unreachable" contract intact) while policy refusals
|
|
get their own honest label. The five sites now read
|
|
`Refused("bind command gate: not allowed")`,
|
|
`Refused("dial refused in-stream")`,
|
|
`Refused("egress refused in-stream")`,
|
|
`Refused("bind factory refused in-stream")`,
|
|
`Refused("bind accept refused in-stream")`, same
|
|
`&'static str` payload shape as `Bug`, zero wire impact (the RFC
|
|
reply went out before each `Err`).
|
|
- **The harness's `Bug` site was migrated too** (the task said
|
|
"leave unless it wants the new variant"): `tests/harness.rs`'s
|
|
minimal CONNECT producer returned `Bug("dial refused in-stream")`,
|
|
the same working-as-designed refusal shape, so it takes the new
|
|
variant for vocabulary consistency across the log surface. No
|
|
test asserts that harness error's variant, so nothing else
|
|
moved.
|
|
- **The mapping-table test grew the new variant**:
|
|
`SocksServerError::Refused` rides the `GeneralFailure`
|
|
fall-through loop in `to_reply_error_mapping_table` (alongside
|
|
`Bug`), and a new test pins the display string and the
|
|
fall-through mapping (`refused_variant_display_is_honest_and_
|
|
wire_invisible`), covering the task's "one test pinning the new variant's
|
|
display/shape".
|
|
- **Provenance extended, not replaced** (convention 17): the
|
|
`server.rs` module doc gained a "Post-fork owned changes"
|
|
paragraph (the cuts, the keep-notes, the `Refused` addition);
|
|
`docs/architecture/rfc1928.md` gained the matching "owned-code
|
|
post-fork changes" list under the deliberate modifications and a
|
|
`last_updated` line. The bundled `src/rfc1928/LICENSE` notice
|
|
already carries the "modifications after the fork point" clause
|
|
generically, so no change was needed there.
|
|
- **Keep-notes made current**: `stream.rs`'s `ConnectError` note
|
|
records that the trigger fired and the disposition is **keep**
|
|
(was: "cut it then"); `transfer`/`wait_on_tcp`'s differential-
|
|
suite note lives in the `server.rs` module doc; `Bug`'s keep-note
|
|
is the same paragraph (upstream-verbatim, its display untouched).
|
|
- **The differential suite's `:103` comment** reworded to the
|
|
RFC-ordering rationale (the server picks by its own preference,
|
|
more-secure-first), and no cut-type reference remains anywhere.
|
|
- **The `AuthMethod` trait doc** was updated for the macro cut
|
|
(upstream's "use enums for sets" line → a custom selection is a
|
|
slice of method structs) and the module doc's extraction-time
|
|
adjustments line dropped the `#[macro_export]` note (the macro
|
|
itself is gone).
|
|
|
|
## Summary
|
|
|
|
Both items landed. **N-1 (the cut)**: `auth_method_enums!` (the
|
|
macro, nothing else used it), `StandardAuthentication` +
|
|
`StandardAuthenticationStarted` + `allow_no_auth`,
|
|
`skip_auth_this_is_not_rfc_compliant`, and `CheckResult`'s
|
|
`Option`/`Result` impls are gone from `src/rfc1928/server.rs`, since
|
|
they were the producer-replaced machinery per ADR-013's
|
|
no-speculative-surface posture. Kept with notes: `ConnectError`
|
|
(hand-port companion, `stream.rs` note updated to record the fired
|
|
trigger + keep disposition), `transfer`/`wait_on_tcp`
|
|
(differential-suite-consumed, semantics pinned there), `Bug`
|
|
(upstream-verbatim, display intact). **N-4 (the vocabulary)**: new
|
|
`SocksServerError::Refused(&'static str)` variant, used at the five
|
|
in-stream refusal sites (the BIND command gate + the four seam
|
|
refusals) plus the harness's minimal producer; `Bug`'s display is
|
|
untouched (a real bug should still read `BUG:`); the wire bytes are
|
|
byte-identical (every reply-code assertion stayed green unchanged).
|
|
Tests: the three gate-site session-error asserts moved from
|
|
`AuthenticationRejected` to `Refused(...)`, the four `Bug(...)`
|
|
asserts to `Refused(...)`, the mapping-table loop grew the variant,
|
|
and a new display/shape test pins both. Docs: the `server.rs`
|
|
module doc's owned-changes paragraph, the `rfc1928.md`
|
|
"owned-code post-fork changes" list + `last_updated`, the
|
|
`stream.rs` keep-note, the `AuthMethod` doc, and the differential
|
|
comment. The review-002 N-1 coverage blocks resolve (the cuts were
|
|
the accepted-uncovered surface).
|
|
|
|
Verified: `cargo test` (356 green across all suites incl.
|
|
`--all-features`), `cargo clippy --all-targets -- -D warnings`
|
|
clean, `cargo fmt --check` clean, `cargo doc --no-deps` clean,
|
|
`cargo check --target wasm32-unknown-unknown` +
|
|
`cargo clippy --target wasm32-unknown-unknown -- -D warnings`
|
|
clean. |