Follow-up to EXP2-SEC-04 (wire game IDs). The wire validator rejected
separators, control characters and the `.`/`..` pseudo-components and
otherwise relied on the catalog lookup that follows every request. That
lookup is real, but it leaves a class of IDs on the wire that the catalog
itself would never publish: Windows device names (`CON`, `nul.txt`,
`com1`), trailing dots or spaces, and the Windows-reserved characters
`< > : " | ? *`. Rejecting them at the protocol boundary means a
filesystem-backed handler can never see one, whichever consumer is added
next.
`validate_game_id` now also calls
`lanspread_db::content_manifest::validate_portable_component`, the same
function the catalog uses to admit game IDs and that `path_validation.rs`
already reuses. Reusing it rather than copying the device-name table
(the parallel branch grew three private copies) guarantees the wire rule
can neither over-match nor drift: an ID the catalog accepts always
encodes. The existing `InvalidPathComponent` error variant is reused so
callers and logs are unchanged.
Tests add the newly rejected forms and a positive list of catalog-valid
IDs that must keep encoding: embedded dots and spaces (`game..v1
(final)`), `console.txt`, `com10` and a non-ASCII name. The fixture
catalogs and the peer test suite, which encode many request IDs, pass
unchanged.
Test plan:
- `cargo test -p lanspread-proto`: 24 passed.
- `cargo test -p lanspread-peer -p lanspread-peer-cli`: 491 + 15 + 21
passed.
- `cargo clippy -p lanspread-proto --all-targets -- -D warnings`: clean.
Claude-Session: https://claude.ai/code/session_01QRkCv4a4GqkajyamxmbSuA