diff --git a/SECURITY_REVIEW_2026-09-12.md b/SECURITY_REVIEW_2026-09-12.md deleted file mode 100644 index 0998892..0000000 --- a/SECURITY_REVIEW_2026-09-12.md +++ /dev/null @@ -1,114 +0,0 @@ -# Security review — 2026-09-12 - -## Scope and threat boundary - -This pass reviewed the full Lanspread workspace after the Fable 5.1 remediation. -The product is a desktop launcher for LAN parties, not an Internet-facing -multi-tenant service. Remote LAN peers and their network traffic are untrusted. -The bundled catalog is trusted authority. A user-selected local game root and -explicit local install actions remain user authority. The three Windows launch -scripts intentionally run elevated. - -## Reassessment of previously ignored findings - -The earlier file dismissed several finite-resource findings too broadly. This -pass fixed anonymous pool monopolization, retry multiplication, oversized remote -state, heavy UI publication, selected-root ingestion, elevated-script trust -binding, and catalog-preflight traversal. The remaining accepted decisions and -their exact limits are recorded in `SECURITY_FIXES_FABLE5.1_IGNORED.md`. - -The main retained decisions are: - -- anonymous LAN membership, metadata browsing, and catalog-content downloads; -- Ordinary Install consuming the current root `.eti` set, including the S33 - local-mutation workflow; -- transactional pre-verification disk writes; -- no OS sandbox around `unrar`; -- local-user regex CPU cost and the fixed companion-window capability; and -- required elevation for catalog-authorized setup, game, and server scripts. - -These are proportionate for the application only because remote paths and -content remain catalog-bound, local mutations require an explicit local action, -and remote work now has finite global and per-origin budgets. - -## Findings fixed in this pass - -| Finding | Final boundary | -| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| mDNS advertised-address quota bypass | Candidate quotas use the observed response IP. One source gets 8 active-or-cooling candidates. | -| Unbounded mDNS dependency state | Cache records are capped at 1,024 globally and 128 per source; timers are deduplicated and capped at 4,096. | -| Anonymous request pool monopolization | One IP gets 8 handshake attempts per 3 seconds, 8/64 connections, 8/16 control tasks, 8/48 bulk tasks, and 1/2 Stream Install providers. | -| Renewable bulk-send deadlines | Raw chunks use one absolute ten-minute window; Stream Install uses a catalog-sized absolute window plus ten-minute inactivity through FIN. | -| Ordinary Sybil retry multiplication | At most 8 distinct identities at distinct IPs share one 20-minute retry window after the initial failure. | -| Stream Install drip and retry multiplication | At most 4 endpoint IPs share `10 min + 2 x catalog bytes / 1 MiB/s`; only written nonempty file bytes refresh inactivity. | -| Eager control-response allocation | An 8 MiB endpoint-IP budget and shared 32 MiB connector budget are acquired after manual four-byte length validation and held through decode. | -| Aggregate remote state | At most 16,384 remote library rows and 16,384 remote Call-to-Play events; rejected revisions retain watermarks. | -| Peer identity amplification | At most 8 authenticated identities may reside at one endpoint IP, within the global 64-peer limit. | -| Unbounded full-view publication | Remote-library and Call-to-Play views keep one queued and one replaceable pending snapshot; lifecycle events remain FIFO. | -| Call-to-Play projection cost | One author retains 128 creator roots; the UI projects 128 nominations and 256 messages and sorts messages once. | -| Selected-root ingestion | Index, traversal, depth, byte, elapsed-time, bounded `version.ini`, and 64 MiB aggregate settings-read limits fail without publishing partial state. | -| Mutable elevated Windows scripts | A fixed-role elevated worker reloads embedded catalog authority, verifies the locked script, uses System32 `cmd.exe`, and transfers locks into the child. | -| Catalog preflight root traversal | Package roots must be regular non-link directories before a bounded leaf read; diagnostics omit observed package contents. | -| Developer recipe shell injection | Just positional arguments and exported values are passed as quoted shell data. | -| Unused frontend shell authority | The main-window `shell:allow-open` capability was removed. | - -## Independent follow-up review - -A fresh source-to-sink review found and closed six additional gaps: response -semaphore head-of-line blocking, half-open handshake fairness, an unbounded -public readiness-sentinel read, aggregate settings-file reads, repeated monitor -backoff reset after a root switch, and an over-strict ten-minute provider egress -deadline. The corrected provider deadline is ten minutes plus one catalog copy -at 1 MiB/s. The handshake limiter drops excess Initials because issuing a Retry -token would bypass s2n's next connection-attempt callback. - -The reviewer read back the follow-up range and found no remaining concrete -source-level bypass or regression. - -## Dependency scan - -`just security-audit` refreshed RustSec and scanned 578 locked Rust packages, -then audited the Deno lockfile. It found no known vulnerabilities in either -graph. RustSec reported seven informational warnings: - -- six unmaintained build/transitive crates (`proc-macro-error` and five `unic-*` - crates); and -- `glib 0.18.5` unsoundness limited to `VariantStrIter` methods. - -The affected `glib` API has no call site in Lanspread or its locked dependants, -and the GTK 0.18 branch is constrained by the current Tauri Linux backend. The -warnings are dependency maintenance work rather than reachable findings. - -## Residual limits - -- Native Windows execution was unavailable. Linux-visible worker authority, - parsing, digest, role, and handoff tests pass, but the Windows lock-transfer - test has not run. -- The absent production scripts may invoke mutable secondary executables or - configuration as administrator. The production payload must be audited before - claiming transitive administrator-code provenance. -- Ordinary Install deliberately accepts locally changed or additional `.eti` - archives. Their expanded output and decompression cost are not catalog - authority; links are skipped and audited before promotion. -- Stream Install staging still uses ambient path calls. Exploitation requires a - concurrent same-machine writer able to mutate the selected root. -- Per-origin fairness cannot stop cooperating hosts or one host using several - usable IP addresses; finite global limits remain authoritative. - -## Validation - -- `just fmt`: passed. -- `just clippy`: passed with warnings denied. -- `just test`: passed, including 541 peer tests and all workspace targets. -- `just frontend-test`: 94 tests passed. -- `just build-fixture`: passed. -- `just peer-cli-build`: passed. -- `just peer-cli-tests`: all four catalog profiles passed; the container - scenarios could not start because this host denies Podman's `newuidmap` - capability, including outside the sandbox. -- `just mdns-vendor-test`: 106 tests passed with socket access. -- `just security-audit`: no known Rust or Deno vulnerabilities; seven accepted - RustSec informational warnings. - -Physical-LAN multicast behavior and native Windows execution remain runtime -validation boundaries. diff --git a/SECURITY_FIXES_FABLE5.1_IGNORED.md b/organize/security/SECURITY_ISSUES_ACCEPTED.md similarity index 100% rename from SECURITY_FIXES_FABLE5.1_IGNORED.md rename to organize/security/SECURITY_ISSUES_ACCEPTED.md