65 KiB
Comprehensive Security Audit Report: lanspread P2P Game Library Sharing
Target System: lanspread Peer-to-Peer Game Library Sharing Platform
Target Repository: /home/pfs/shm/ls
Date of Audit: 2026-08-28
Audit Scope: All workspace crates (crates/lanspread-*) and Tauri GUI client (crates/lanspread-tauri-deno-ts/)
Assessment Type: White-box Source Code Security Review, Threat Modeling, Architecture Analysis & Vulnerability Assessment
Table of Contents
- Executive Summary & Overall Security Posture
- Threat Model & Attack Surface Breakdown
- 2.1 Adversary Model & Trust Assumptions
- 2.2 LAN mDNS Discovery & Peer Spoofing
- 2.3 QUIC Transport, TLS 1.3 & Wire Protocol Framing
- 2.4 Download Path Traversal, Chunk Verification & Archive Extraction
- 2.5 Tauri IPC Boundaries, Webview Isolation & Frontend Security
- 2.6 Database & Persistence Integrity
- Audit Scope & Component Coverage
- Comprehensive Findings Matrix
- Detailed Vulnerability & Hardening Write-ups
- 5.1 P2P Networking, Discovery & Wire Protocol
- Finding NET-01: Unauthenticated Inbound QUIC Streams & Forged Change Hints Trigger Reflected State-Pull Flooding (High)
- Finding NET-02: Missing IP Address & Port Validation in Discovered mDNS Services Enables Handshake Redirection to Multicast / Broadcast Targets (Medium)
- Finding NET-03: Inbound Request Framing Uses Excessive 8 MiB Limit Allowing Stream Memory Exhaustion DoS (Medium)
- Finding NET-04: Global Capacity Cap in RecentCandidates Allows LAN Adversary to Deny Peer Discovery (Medium)
- Finding NET-05: Unbounded Collection Deserialization in Response::decode Prior to Semantic Bounds Validation (Low)
- Finding NET-06: Unauthenticated Game Chunk and Stream-Install Egress on QUIC Server (Informational)
- 5.2 File Transfer, Storage Safety, Chunk Verification & Archive Extraction
- Finding EXP2-SEC-01: Unsafe Archive Extraction in SidecarUnpacker (Missing Post-Unpack Manifest Validation & Symlink Redirection Risk) (High)
- Finding EXP2-SEC-02: Unbounded Archive Decompression (Zip/RAR Bomb) Leading to Host Disk Exhaustion Denial of Service (Medium)
- Finding EXP2-SEC-03: Incomplete Path Sanitization in path_validation.rs (Omission of Windows Device Names & Trailing Dots/Spaces) (Medium)
- Finding EXP2-SEC-04: Under-Constrained game_id Validation in lanspread-proto Wire Protocol Boundary (Medium)
- Finding EXP2-SEC-05: Direct Pre-Verification Chunk Writes to Filesystem (Low / Defense-in-Depth)
- Finding EXP2-SEC-06: Predictable Fallback State Directory in World-Writable Shared /tmp Location (Low)
- Finding EXP2-SEC-07: Ambient File Creation in stream_install.rs Bypassing Capability-Based ConfinedGameRoot (Low / Hardening)
- 5.3 Tauri Desktop Shell, IPC Boundaries, Webview Isolation & Frontend Security
- Finding SEC-IPC-01: Elevated Administrator Execution of Untrusted P2P Batch Scripts with Fragile Parameter Parsing (High)
- Finding SEC-IPC-02: Disabled Content Security Policy (csp: null) in Tauri Webview Configuration (High)
- Finding SEC-IPC-03: Unvalidated game_id Path Resolution & Leftover Debug Macro in get_game_thumbnail (Medium)
- Finding SEC-IPC-04: Unsandboxed External Archive Extraction (unrar) for Untrusted P2P Archives (Medium)
- Finding SEC-IPC-05: Broad Webview Window Creation Capabilities (allow-create-webview-window) (Low)
- Finding SEC-FE-01: Client-Side Regular Expression Denial of Service (ReDoS) in Log Viewers (Low)
- 5.4 Database & Data Persistence Integrity
- 5.1 P2P Networking, Discovery & Wire Protocol
- Prioritized Recommendations & Hardening Roadmap
- Codebase Health & Verification Results
1. Executive Summary & Overall Security Posture
lanspread is a peer-to-peer (P2P) desktop application designed for local area network (LAN) party environments, enabling nodes to discover neighboring peers via Multicast DNS (mDNS-SD), synchronize library metadata and real-time "Call to Play" events over QUIC/TLS 1.3, and download, unpack, and launch game packages directly across machines. The system is implemented as a Rust workspace with an asynchronous backend (tokio, s2n-quic, sqlx) and a desktop user interface built with Tauri (Vite, Deno, TypeScript, React).
Threat Environment
The operational context of lanspread is a local network environment (LAN party, shared gaming lounge, university dormitory, or open Wi-Fi). In this environment:
- Physical and link-layer network access is untrusted and unauthenticated.
- Any participant on the subnet can inject raw UDP packets (including spoofed mDNS multicast on UDP 5353 and arbitrary QUIC UDP frames).
- Any participant can establish direct QUIC connections to running peers, open bidirectional streams, and send arbitrary control or data payloads.
- Peers may run modified, hostile versions of
lanspreadcrafted to harvest metadata, exhaust node resources, disrupt network discovery, or deliver malicious archive content and scripts.
Major Architectural Strengths
- Capability-Based Directory Confinement (
cap_primitives): The core download engine enforces filesystem sandboxing usingConfinedGameRootand handle-relative file operations withFollowSymlinks::No, preventing path traversal during standard chunk transfers. - Cryptographic Manifest Authority (BLAKE3): Game file manifests (
CatalogContentManifest) enforce strict 4 MiB chunk-level and file-level BLAKE3 cryptographic digests. Corrupted or tampered chunks are detected during transfer. - Crash-Consistent Transactional File Management: The download manager uses temporary write handles, journaled file ownership (
DownloadOwnershipTransaction), and deferred sentinel writing (version.ini) to ensure incomplete downloads are never committed or published. - Parameterized Database Layer: SQLite interactions in
lanspread-dbandlanspread-compatutilizesqlxprepared queries with parameter binding, eliminating classic SQL injection vulnerabilities. - Single Wire Protocol Policy: By strictly disallowing legacy wire version fallbacks, backward-compatibility shims, and permissive serde escape hatches (
#[serde(other)]), the wire protocol surface remains compact and auditable.
Key Security Posture Risks
Despite these robust foundational designs, the security audit identified 20 distinct vulnerabilities and hardening deficiencies (4 High, 7 Medium, 8 Low, 1 Informational). The primary risks include:
- Elevated Remote Code Execution via Untrusted P2P Scripts: When launching games or starting dedicated servers on Windows,
lanspreadexecutes batch scripts (game_setup.cmd,game_start.cmd,server_start.cmd) downloaded from untrusted peers with elevated Administrator privileges (runasverb viacmd.exe). - Reflected State-Pull Amplification & Unauthenticated Inbound Streams: The QUIC server does not enforce Mutual TLS (mTLS) client authentication (
.with_no_client_auth()). Unauthenticated attackers can inject forgedChangeHintmessages impersonating other peers, triggering concurrent outbound QUIC connection floods directed at victim nodes. - Disabled Webview Content Security Policy:
tauri.conf.jsonexplicitly sets"csp": null, eliminating browser-level defenses against Cross-Site Scripting (XSS) and remote resource loading. - Unchecked External Archive Extraction (
SidecarUnpacker): Extraction of.eti(RAR) archives via the externalunrarsidecar lacks post-extraction manifest verification, symlink auditing, and disk volume capacity preflight checks.
+---------------------------------------------------------------------------------------------------+
| LANSPREAD ARCHITECTURE |
| |
| +---------------------------------------------------------------------------------------------+ |
| | FRONTEND (Vite / Deno / React) | |
| | - UI Components, CtpChat, Log Viewers (ReDoS risk: SEC-FE-01) | |
| +---------------------------------------------------------------------------------------------+ |
| | IPC (invoke) |
| v |
| +---------------------------------------------------------------------------------------------+ |
| | TAURI DESKTOP SHELL (src-tauri/) | |
| | - Disabled CSP ("csp": null, SEC-IPC-02) | |
| | - Elevated UAC batch execution (cmd.exe runas, SEC-IPC-01) | |
| | - Unvalidated thumbnail path resolver (SEC-IPC-03) | |
| | - Sidecar Unpacker spawning unrar (EXP2-SEC-01, EXP2-SEC-02, SEC-IPC-04) | |
| +---------------------------------------------------------------------------------------------+ |
| | | |
| v v |
| +---------------------------------------+ +-------------------------------------------+ |
| | DATABASE & COMPAT | | CORE P2P BACKEND (lanspread-peer) | |
| | - lanspread-db / lanspread-compat | | - QUIC Server (No mTLS: NET-01) | |
| | - Parameterized sqlx (SEC-DB-01) | | - mDNS Discovery (Candidate DoS: NET-04) | |
| | - Manifest parser & BLAKE3 digests | | - Download Engine (ConfinedGameRoot) | |
| +---------------------------------------+ | - Stream Install (Ambient FS: EXP2-SEC-07)| |
| +-------------------------------------------+ |
+---------------------------------------------------------------------------------------------------+
2. Threat Model & Attack Surface Breakdown
2.1 Adversary Model & Trust Assumptions
- LAN Adversary: An attacker with network access on the local subnet (IP layer and broadcast/multicast domain). The adversary can capture, forge, and inject UDP packets, establish QUIC connections, and execute modified protocol implementations.
- Untrusted Peer Content: Games, metadata, file chunks, and archive payloads hosted by remote peers must be treated as inherently untrusted. A malicious peer may attempt to deliver malicious code, craft directory traversal sequences, or exhaust victim resources.
- Compromised Webview: A potential threat where malicious HTML/JS injected via peer metadata or chat messages attempts to escape the webview sandbox and invoke native host commands via Tauri IPC.
2.2 LAN mDNS Discovery & Peer Spoofing
- Component:
crates/lanspread-mdns,crates/lanspread-peer/src/services/discovery.rs,crates/lanspread-peer/src/services/advertise.rs. - Attack Surface: Inbound multicast DNS responses received on UDP port 5353 (
_lanspread._udp.local.). - Threats:
- Peer Spoofing: An attacker can advertise arbitrary
peer_ids, IP addresses, and ports. - Discovery Denial-of-Service: Saturating candidate rate limiters (
RecentCandidates) to block legitimate peer discovery. - Target Redirection: Announcing multicast, broadcast, loopback, or internal infrastructure IP addresses to force victim nodes to send QUIC handshake packets to unauthorized destinations.
- Peer Spoofing: An attacker can advertise arbitrary
2.3 QUIC Transport, TLS 1.3 & Wire Protocol Framing
- Component:
crates/lanspread-peer/src/{tls.rs, quic_runtime.rs, network.rs, services/stream.rs},crates/lanspread-proto. - Attack Surface: Inbound UDP datagrams on peer QUIC listening port (e.g. 42424), TLS 1.3 handshake negotiation, bidirectional stream framing.
- Threats:
- Unauthenticated Stream Injection: Inbound connections have no client identity validation, allowing arbitrary anonymous nodes to send control commands.
- Amplification & Reflection: Forged state change hints causing the recipient to connect to third-party victim nodes.
- Stream Memory Exhaustion: Opening maximum permitted streams (64 globally) and pushing maximum frame lengths (8 MiB control frames) of buffered JSON data.
2.4 Download Path Traversal, Chunk Verification & Archive Extraction
- Component:
crates/lanspread-peer/src/{download/, stream_install.rs, install/transaction.rs, path_validation.rs},crates/lanspread-utils. - Attack Surface: Content manifests, game IDs, relative file paths, chunk streams, and downloaded
.eti(RAR) archives. - Threats:
- Path Traversal / Escape: Crafting relative paths with
../, UNC prefixes, drive letters, Windows device names (CON,PRN,AUX,NUL), or trailing dots/spaces to write outside the game directory. - Symlink Exploitation: Archives containing symbolic links or NTFS directory junctions pointing to sensitive system locations.
- Decompression Bombs: Highly compressed archives that decompress to hundreds of gigabytes, exhausting host disk space.
- Pre-Verification Chunk Write Corruption: Writing unverified chunks directly to live files before completing hash checks.
- Path Traversal / Escape: Crafting relative paths with
2.5 Tauri IPC Boundaries, Webview Isolation & Frontend Security
- Component:
crates/lanspread-tauri-deno-ts/src-tauri/,crates/lanspread-tauri-deno-ts/src/. - Attack Surface: 24 registered Tauri IPC commands, frontend DOM rendering, IPC parameter validation, webview capability configuration.
- Threats:
- Elevated Privilege Escalation: Invoking batch scripts via Windows
cmd.exewith Administrator (runas) UAC elevation. - Cross-Site Scripting (XSS): Execution of untrusted scripts in the webview context in the absence of a Content Security Policy (
"csp": null). - IPC Parameter Abuse: Passing path traversal strings into asset resolution commands (
get_game_thumbnail). - Client-Side ReDoS: Catastrophic backtracking in user-supplied regex filters in log windows.
- Elevated Privilege Escalation: Invoking batch scripts via Windows
2.6 Database & Persistence Integrity
- Component:
crates/lanspread-db,crates/lanspread-compat. - Attack Surface: SQLite database file (
game.db), content index (catalog-content-index-v1.jsonl), local state storage. - Threats:
- SQL Injection: Attempting to manipulate database queries via unsanitized strings (mitigated by sqlx parameter binding).
- Corrupted Database Execution: Exploiting SQLite features like trusted schema or custom functions in untrusted database files.
3. Audit Scope & Component Coverage
Every crate under crates/ and all frontend directories under crates/lanspread-tauri-deno-ts/ were thoroughly evaluated during this security audit:
| Component / Subsystem | Directory Path | Primary Responsibility | Audit Evaluation & Security Posture |
|---|---|---|---|
lanspread-peer |
crates/lanspread-peer/ |
Core P2P runtime: TLS, QUIC server/client, discovery, state sync, download manager, VFS, install transactions | Core Focus: Identified lack of mTLS (NET-01), mDNS IP validation gaps (NET-02), discovery candidate DoS (NET-04), archive extraction integrity (EXP2-SEC-01), and path sanitization omissions (EXP2-SEC-03). Download manager confinement (ConfinedGameRoot) is highly robust. |
lanspread-proto |
crates/lanspread-proto/ |
Wire protocol data structures, framing constants, JSON codecs | Evaluated: Identified symmetric 8 MiB request frame limit (NET-03), unbounded collection deserialization (NET-05), and under-constrained wire game_id validation (EXP2-SEC-04). Strict serialization golden tests pass. |
lanspread-mdns |
crates/lanspread-mdns/ |
Multicast DNS discovery wrapper (mdns-sd) |
Evaluated: Evaluated service announcement and packet reception. Found missing semantic validation on extracted socket addresses (NET-02). |
lanspread-db |
crates/lanspread-db/ |
SQLite schema, catalog content manifest models, BLAKE3 hash checks | Evaluated: Strong validation of path components, Windows device names, and NFC normalization. No SQL injection vulnerabilities found. |
lanspread-compat |
crates/lanspread-compat/ |
Catalog bundle loading, SQLite queries, ETI migration glue | Evaluated: SQL queries use strict parameter bindings. Identified missing defensive SQLite PRAGMA (trusted_schema = OFF) (SEC-DB-01). |
lanspread-utils |
crates/lanspread-utils/ |
Shared filesystem and hashing helpers | Evaluated: Audited for safe I/O operations and symlink protections. Conforms to security baseline. |
lanspread-peer-cli |
crates/lanspread-peer-cli/ |
Scripted JSONL peer test harness and external unpacker | Evaluated: Audited JSONL command parser and external unrar invocation. Identified unsandboxed archive extraction (SEC-IPC-04). |
lanspread-tauri-deno-ts (Backend) |
crates/lanspread-tauri-deno-ts/src-tauri/ |
Tauri shell, IPC command handlers, Windows script execution, sidecar unpacker | Core Focus: Identified elevated Administrator script execution (SEC-IPC-01), disabled CSP (SEC-IPC-02), unvalidated thumbnail resolution (SEC-IPC-03), and decompression bomb risks (EXP2-SEC-02). |
lanspread-tauri-deno-ts (Frontend) |
crates/lanspread-tauri-deno-ts/src/ |
React/Deno/TS GUI client, CtpChat, log viewers, state stores | Evaluated: Evaluated React DOM escaping and state hydration. Identified client-side ReDoS in log window regex filters (SEC-FE-01). |
4. Comprehensive Findings Matrix
The following table summarizes all 20 findings identified across the workspace, categorized by severity and vulnerability type:
| Finding ID | Severity | Type | Affected Component | Primary Location | Finding Title |
|---|---|---|---|---|---|
| NET-01 | High | Exploitable Vulnerability | lanspread-peer |
src/tls.rs:126-134src/services/state_sync.rs:362-389 |
Unauthenticated Inbound QUIC Streams & Forged Change Hints Trigger Reflected State-Pull Flooding |
| NET-02 | Medium | Exploitable Vulnerability | lanspread-peer / lanspread-mdns |
src/services/discovery.rs:393-412lanspread-mdns/src/lib.rs:273-290 |
Missing IP Address & Port Validation in Discovered mDNS Services Enables Handshake Redirection to Multicast / Broadcast Targets |
| NET-03 | Medium | Exploitable Vulnerability / DoS | lanspread-proto / lanspread-peer |
lanspread-proto/src/lib.rs:13src/services/stream.rs:37-41 |
Inbound Request Framing Uses Excessive 8 MiB Limit Allowing Stream Memory Exhaustion DoS |
| NET-04 | Medium | Exploitable Vulnerability / DoS | lanspread-peer |
src/services/discovery.rs:30-67, 300-309 |
Global Capacity Cap in RecentCandidates Allows LAN Adversary to Deny Peer Discovery |
| NET-05 | Low | Defense-in-Depth / Resource Safety | lanspread-proto |
src/lib.rs:718-745, 811-834 |
Unbounded Collection Deserialization in Response::decode Prior to Semantic Bounds Validation |
| NET-06 | Informational | Architectural Policy | lanspread-peer |
src/services/transfer.rs:258-341src/services/stream.rs:273-303 |
Unauthenticated Game Chunk and Stream-Install Egress on QUIC Server |
| EXP2-SEC-01 | High | Exploitable Vulnerability | lanspread-peer / src-tauri |
src/install/transaction.rs:461-468, 514-544src-tauri/src/lib.rs:3195-3238 |
Unsafe Archive Extraction in SidecarUnpacker (Missing Post-Unpack Manifest Validation & Symlink Redirection Risk) |
| EXP2-SEC-02 | Medium | Exploitable Vulnerability / DoS | src-tauri / lanspread-peer |
src-tauri/src/lib.rs:3166-3240src/install/transaction.rs:514-544 |
Unbounded Archive Decompression (Zip/RAR Bomb) Leading to Host Disk Exhaustion Denial of Service |
| EXP2-SEC-03 | Medium | Exploitable Vulnerability | lanspread-peer |
src/path_validation.rs:14-88 |
Incomplete Path Sanitization in path_validation.rs (Omission of Windows Device Names & Trailing Dots/Spaces) |
| EXP2-SEC-04 | Medium | Defense-in-Depth / Validation Gap | lanspread-proto |
src/lib.rs:769-780, 912-923 |
Under-Constrained game_id Validation in lanspread-proto Wire Protocol Boundary |
| EXP2-SEC-05 | Low | Defense-in-Depth / Crash Safety | lanspread-peer |
src/download/transport.rs:367-402 |
Direct Pre-Verification Chunk Writes to Filesystem |
| EXP2-SEC-06 | Low | Hardening / Multi-user Safety | lanspread-peer |
src/state_paths.rs:18-32 |
Predictable Fallback State Directory in World-Writable Shared /tmp Location |
| EXP2-SEC-07 | Low | Hardening / Architectural Parity | lanspread-peer |
src/stream_install.rs:1295-1316, 1895-1900 |
Ambient File Creation in stream_install.rs Bypassing Capability-Based ConfinedGameRoot |
| SEC-IPC-01 | High | Exploitable Vulnerability / RCE | lanspread-tauri-deno-ts (src-tauri) |
src-tauri/src/lib.rs:1474-1513, 1867-1959, 2008-2064 |
Elevated Administrator Execution of Untrusted P2P Batch Scripts with Fragile Parameter Parsing |
| SEC-IPC-02 | High | Defense-in-Depth / Isolation | lanspread-tauri-deno-ts (src-tauri) |
src-tauri/tauri.conf.json:20-22 |
Disabled Content Security Policy (csp: null) in Tauri Webview Configuration |
| SEC-IPC-03 | Medium | Exploitable Vulnerability | lanspread-tauri-deno-ts (src-tauri) |
src-tauri/src/lib.rs:1534-1552 |
Unvalidated game_id Path Resolution & Leftover Debug Macro in get_game_thumbnail |
| SEC-IPC-04 | Medium | Defense-in-Depth / Sandboxing | lanspread-peer-cli / src-tauri |
lanspread-peer-cli/src/lib.rs:344-370src-tauri/src/lib.rs:3195-3221 |
Unsandboxed External Archive Extraction (unrar) for Untrusted P2P Archives |
| SEC-IPC-05 | Low | Hardening / Least Privilege | lanspread-tauri-deno-ts (src-tauri) |
src-tauri/capabilities/default.json:11 |
Broad Webview Window Creation Capabilities (allow-create-webview-window) |
| SEC-FE-01 | Low | Exploitable Vulnerability / UI DoS | lanspread-tauri-deno-ts (frontend) |
src/MainLogsWindow.tsx:160-164src/UnpackLogsWindow.tsx:109-113 |
Client-Side Regular Expression Denial of Service (ReDoS) in Log Viewers |
| SEC-DB-01 | Low | Defense-in-Depth / Database | lanspread-compat |
src/catalog_bundle.rs:67-75src/eti.rs:29-45 |
Missing Defensive SQLite PRAGMAs (trusted_schema = OFF) on Catalog Database Pool |
5. Detailed Vulnerability & Hardening Write-ups
5.1 P2P Networking, Discovery & Wire Protocol
Finding NET-01: Unauthenticated Inbound QUIC Streams & Forged Change Hints Trigger Reflected State-Pull Flooding
- Severity Rating: High (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H)
- Category: Exploitable Vulnerability / Authentication & Amplification
- Affected Component:
crates/lanspread-peer(TLS & State Sync Subsystems) - File & Line References:
crates/lanspread-peer/src/tls.rs:126-134(server_provider)crates/lanspread-peer/src/services/server.rs:262-348(handle_peer_connection)crates/lanspread-peer/src/services/stream.rs:265-272(handle_peer_stream)crates/lanspread-peer/src/services/state_sync.rs:101-114, 362-389(hint_requires_pull,perform_refresh_for_peer)
Vulnerability Description & Root Cause Analysis
In tls.rs, the QUIC server TLS configuration explicitly disables client certificate authentication:
pub(crate) fn server_provider(identity: &PeerIdentity) -> Result<S2nRustlsServer, RustlsError> {
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
let mut config = ServerConfig::builder_with_provider(provider)
.with_protocol_versions(&[&rustls::version::TLS13])?
.with_no_client_auth()
.with_single_cert(vec![identity.certificate()], identity.private_key())?;
harden_server_config(&mut config);
Ok(S2nRustlsServer::from(config))
}
When inbound QUIC connections are established in server.rs, connecting clients are completely anonymous.
When an inbound bidirectional stream delivers Request::LibraryChanged(hint) or Request::CallToPlayChanged(hint), handle_peer_stream in stream.rs passes the ChangeHint structure directly to ctx.state_sync.schedule_hint(...) without verifying that the connection's sender owns hint.claimed_peer_id.
In state_sync.rs:
async fn hint_requires_pull(ctx: &NetworkServiceCtx, trigger: HintTrigger) -> bool {
let snapshot = ctx
.peer_game_db
.read()
.await
.revision_snapshot(&trigger.hint.claimed_peer_id);
hint_requires_pull_from_snapshot(ctx.peer_id, trigger, snapshot.as_ref())
}
If claimed_peer_id matches a known peer in peer_game_db and the hint specifies an incremented revision or new session ID, hint_requires_pull evaluates to true. This causes state_sync to immediately queue an outbound perform_refresh_for_peer, triggering a full outbound QUIC connection and Hello snapshot pull targeting the victim peer (claimed_peer_id).
Threat & Impact Analysis
- Reflected DoS / Network Amplification: An attacker on the LAN can establish unauthenticated QUIC streams to every peer on the subnet and send forged
ChangeHintmessages claimingPeer_Victimhas published revision999999. Every peer node on the network will simultaneously initiate an outbound QUIC connection toPeer_Victimand pull its fullHelloSnapshot, exhaustingPeer_Victim's network bandwidth, QUIC connection limits, and CPU. - Unauthenticated Information Harvesting: Any anonymous client can connect to any running peer and issue
Request::HelloorRequest::Pingto extract complete user display names, full shared game catalogs, and real-time Call-to-Play chat messages without presenting credentials.
Concrete Attack Scenario / Reproduction Steps
- An attacker on the LAN listens to mDNS broadcasts and identifies
Peer_Victim(PeerId_V, IP192.168.1.50:42424) and 30 other participating peers (Peer_1throughPeer_30). - The attacker opens a standard QUIC connection to
Peer_1(192.168.1.10:42424). - Over a bidirectional stream, the attacker sends:
{"LibraryChanged":{"claimed_peer_id":"<PeerId_V>","runtime_session_id":"00000000000000000000000000000000","revision":999999}} Peer_1receives the frame, observes thatPeerId_Vhas a new revision, and initiatesperform_refresh_for_peer(PeerId_V).- The attacker repeats steps 2–3 across
Peer_2throughPeer_30. - All 30 peers concurrently open QUIC connections to
Peer_Victimand request full library snapshots, saturatingPeer_Victim's connection permits and CPU.
Actionable Remediation Guidance
- Enforce Mutual TLS (mTLS):
- Configure
ServerConfigto require client certificates via a customClientCertVerifierthat validates the client's Ed25519 self-signed certificate and extracts their public key. - Configure
ClientConfigto present the localPeerIdentitycertificate during outbound handshakes.
- Configure
- Bind Inbound Commands to Verified Identity:
- Extract the authenticated client
PeerIdfrom the TLS connection metadata inhandle_peer_connection. - In
stream.rs, asserthint.claimed_peer_id == authenticated_peer_id. Reject and drop any hint where the claimed identity does not match the TLS identity.
- Extract the authenticated client
Finding NET-02: Missing IP Address & Port Validation in Discovered mDNS Services Enables Handshake Redirection to Multicast / Broadcast Targets
- Severity Rating: Medium (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:M)
- Category: Exploitable Vulnerability / Input Validation
- Affected Component:
crates/lanspread-peer(Discovery) /crates/lanspread-mdns - File & Line References:
crates/lanspread-peer/src/services/discovery.rs:393-412(validated_candidate_endpoint)crates/lanspread-mdns/src/lib.rs:273-290(MdnsBrowser::handle_service_resolved)
Vulnerability Description & Root Cause Analysis
In discovery.rs:
fn validated_candidate_endpoint(info: &MdnsPeerInfo) -> Option<PeerEndpoint> {
if info.proto_ver != Some(PROTOCOL_VERSION) {
return None;
}
let Some(peer_id) = info.peer_id else {
return None;
};
Some(PeerEndpoint::new(peer_id, info.addr))
}
Neither lanspread-mdns nor discovery.rs validates the socket address info.addr. The application does not check if info.addr.ip() is a multicast address (224.0.0.0/4, ff00::/8), a broadcast address (255.255.255.255), an unspecified address (0.0.0.0, ::), or if info.addr.port() == 0.
Threat & Impact Analysis
An attacker broadcasting forged mDNS records can specify destination IP addresses pointing to multicast (224.0.0.1), broadcast (255.255.255.255), or internal infrastructure ports. When DiscoveryWorker processes these candidates, it spawns run_protocol_negotiation which calls connector.connect(&endpoint). This causes s2n-quic to transmit UDP Initial handshake packets to multicast or broadcast addresses, polluting network segments and triggering unexpected socket errors.
Concrete Attack Scenario / PoC
- Attacker transmits an mDNS response for service
_lanspread._udp.local.with TXT recordproto_ver=8,peer_id=<valid_base32_id>, and host address224.0.0.251:42424. MdnsBrowserparses the record and sends anMdnsServiceevent withaddr = 224.0.0.251:42424.validated_candidate_endpointaccepts the endpoint.run_peer_discoverydispatches a QUIC connection attempt to224.0.0.251:42424.
Actionable Remediation Guidance
Implement an admissibility filter for discovered IP addresses and ports in discovery.rs:
fn is_admissible_peer_ip(ip: std::net::IpAddr) -> bool {
match ip {
std::net::IpAddr::V4(v4) => !v4.is_unspecified() && !v4.is_multicast() && !v4.is_broadcast(),
std::net::IpAddr::V6(v6) => !v6.is_unspecified() && !v6.is_multicast(),
}
}
fn validated_candidate_endpoint(info: &MdnsPeerInfo) -> Option<PeerEndpoint> {
if info.proto_ver != Some(PROTOCOL_VERSION) {
return None;
}
if info.addr.port() == 0 || !is_admissible_peer_ip(info.addr.ip()) {
log::debug!("Ignoring peer at {} with invalid IP/port", info.addr);
return None;
}
let Some(peer_id) = info.peer_id else {
return None;
};
Some(PeerEndpoint::new(peer_id, info.addr))
}
Finding NET-03: Inbound Request Framing Uses Excessive 8 MiB Limit Allowing Stream Memory Exhaustion DoS
- Severity Rating: Medium (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:M)
- Category: Exploitable Vulnerability / Denial of Service
- Affected Component:
crates/lanspread-proto,crates/lanspread-peer(Stream & Quic Runtime) - File & Line References:
crates/lanspread-proto/src/lib.rs:13(MAX_CONTROL_FRAME_BYTES)crates/lanspread-peer/src/quic_runtime.rs:59, 83(quic_server_limits)crates/lanspread-peer/src/services/stream.rs:37-41(control_codec)
Vulnerability Description & Root Cause Analysis
In lanspread-proto/src/lib.rs:
pub const MAX_CONTROL_FRAME_BYTES: usize = 8 * 1024 * 1024; // 8 MiB
In stream.rs:
fn control_codec() -> LengthDelimitedCodec {
LengthDelimitedCodec::builder()
.max_frame_length(MAX_CONTROL_FRAME_BYTES)
.new_codec()
}
All valid inbound Request variants (Ping, Hello, LibraryChanged, CallToPlayChanged, GetGameFileChunk, StreamInstall) encode to less than 1,024 bytes. However, handle_peer_stream configures FramedRead using MAX_CONTROL_FRAME_BYTES (8 MiB) for incoming request streams. With MAX_GLOBAL_CONTROL_STREAM_TASKS = 64, up to 64 concurrent streams can each buffer up to 8 MiB before serde_json parsing fails.
Threat & Impact Analysis
An attacker opening 64 concurrent streams and pushing 8 MiB payloads of formatted JSON whitespace or long strings forces the victim process to allocate and buffer 64 * 8 MiB = 512 MiB of raw payload in memory. On RAM-constrained gaming devices, this spike can induce out-of-memory (OOM) termination.
Actionable Remediation Guidance
Split MAX_CONTROL_FRAME_BYTES into asymmetric bounds for requests versus responses:
pub const MAX_REQUEST_FRAME_BYTES: usize = 64 * 1024; // 64 KiB
pub const MAX_RESPONSE_FRAME_BYTES: usize = 8 * 1024 * 1024; // 8 MiB (for HelloSnapshot)
Configure FramedRead in handle_peer_stream to use MAX_REQUEST_FRAME_BYTES.
Finding NET-04: Global Capacity Cap in RecentCandidates Allows LAN Adversary to Deny Peer Discovery
- Severity Rating: Medium (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:M)
- Category: Exploitable Vulnerability / Denial of Service
- Affected Component:
crates/lanspread-peer(Discovery) - File & Line References:
crates/lanspread-peer/src/services/discovery.rs:30-67, 300-309(RecentCandidates)
Vulnerability Description & Root Cause Analysis
In discovery.rs:
const MAX_ACTIVE_DISCOVERY_CANDIDATES: usize = 64;
impl RecentCandidates {
fn try_record(&mut self, candidate: PeerEndpoint, now: tokio::time::Instant) -> bool {
self.expire(now);
if self.entries.iter().any(|(endpoint, _)| {
endpoint.peer_id == candidate.peer_id || endpoint.addr == candidate.addr
}) || self.entries.len() >= MAX_ACTIVE_DISCOVERY_CANDIDATES
{
return false;
}
self.entries
.push_back((candidate, now + DISCOVERY_CANDIDATE_COOLDOWN));
true
}
}
If self.entries.len() >= 64, try_record returns false for all newly discovered candidate endpoints. In run_peer_discovery, !candidate_is_admissible(...) causes the discovery loop to log a warning and discard the new peer.
Threat & Impact Analysis
An attacker broadcasting 64 fake mDNS service announcements with distinct PeerIds and ports will saturate RecentCandidates within milliseconds. Once saturated, all legitimate new peers joining the LAN will be rejected and ignored for the 5-second cooldown window. Repeating this flood every 5 seconds creates a permanent discovery blackout.
Actionable Remediation Guidance
Implement FIFO/LRU eviction in RecentCandidates when capacity is reached instead of dropping new candidates:
if self.entries.len() >= MAX_ACTIVE_DISCOVERY_CANDIDATES {
self.entries.pop_front();
}
self.entries.push_back((candidate, now + DISCOVERY_CANDIDATE_COOLDOWN));
Finding NET-05: Unbounded Collection Deserialization in Response::decode Prior to Semantic Bounds Validation
- Severity Rating: Low (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L)
- Category: Defense-in-Depth / Resource Safety
- Affected Component:
crates/lanspread-proto - File & Line References:
crates/lanspread-proto/src/lib.rs:718-726, 739-745, 811-834
Vulnerability Description & Root Cause Analysis
Response::decode executes serde_json::from_slice::<Response>(bytes) before calling semantic validation methods (LibrarySnapshot::validate(), CallToPlayAuthorSnapshot::validate()). A malicious peer can construct an 8 MiB response containing 100,000 minimal JSON elements in Vec<GameAvailability>, forcing full heap allocation before semantic validation rejects the payload.
Actionable Remediation Guidance
Apply streaming length checks or implement bounded deserialization helpers for large collections during the serde pass.
Finding NET-06: Unauthenticated Game Chunk and Stream-Install Egress on QUIC Server
- Severity Rating: Informational
- Category: Architectural Policy / Access Control
- Affected Component:
crates/lanspread-peer(Transfer & Stream Services) - File & Line References:
crates/lanspread-peer/src/services/transfer.rs:258-341crates/lanspread-peer/src/services/stream.rs:273-303
Vulnerability Description & Root Cause Analysis
handle_peer_stream allows any connected client to request arbitrary game file chunks (Request::GetGameFileChunk) or initiate streamed RAR installation (Request::StreamInstall). admit_outbound_transfer verifies that the game is in the catalog and ready, but performs no authentication or caller authorization.
Actionable Remediation Guidance
Combine with Finding NET-01 (mTLS) to record authenticated PeerIds for all file transfers, enabling audit logging and per-peer transfer throttling.
5.2 File Transfer, Storage Safety, Chunk Verification & Archive Extraction
Finding EXP2-SEC-01: Unsafe Archive Extraction in SidecarUnpacker (Missing Post-Unpack Manifest Validation & Symlink Redirection Risk)
- Severity Rating: High (CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)
- Category: Exploitable Vulnerability / Path Traversal & Integrity
- Affected Component:
crates/lanspread-peer(install/transaction.rs),crates/lanspread-tauri-deno-ts(src-tauri/src/lib.rs) - File & Line References:
crates/lanspread-peer/src/install/transaction.rs:461-468, 490-495, 514-544(install_inner,unpack_archives)crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:3195-3238(run_unrar_sidecar)
Vulnerability Description & Root Cause Analysis
In install/transaction.rs, the game installation workflow executes:
let staging = installing_dir(game_root);
prepare_owned_empty_dir(&staging)?;
root_capability.sync_game_root()?;
unpack_archives(game_root, &staging, unpacker, cancel_token).await?;
rename_path(&staging, &local)
.wrap_err_with(|| format!("failed to promote install for {id}"))?;
root_capability.sync_game_root()?;
In src-tauri/src/lib.rs, run_unrar_sidecar spawns unrar x -p- archive.eti -y -o destination_dir.
When unrar x completes with exit status 0, install_inner immediately executes rename_path(&staging, &local).
Root Cause:
- There is no post-extraction inspection or validation of the files materialized in
staging. - The application does not check whether
unrarcreated symlinks or Windows directory junctions pointing outsidestaging/local. - The application does not verify extracted files against the catalog manifest's expected file list (
CatalogContentManifest) or compute BLAKE3 hashes of extracted contents. - Unlike
stream_install.rs(which explicitly validates each entry againstCatalogExtractedEntryand checks BLAKE3 hashes), the.etiarchive path completely trusts the externalunrartool output.
Threat & Impact Analysis
If a malicious peer provides an .eti (RAR) archive containing symlinks (e.g. link -> /home/victim/.ssh/ or link -> C:\Windows\), unrar x may extract these symlinks depending on the platform and unrar build. When lanspread later traverses local/ during launch configuration (apply_launch_settings_once), it can read or modify files outside the game root.
Concrete Attack Scenario / PoC
- Attacker creates an
.etiarchive containing:- Entry 1: Symlink
config -> /home/victim/.config/ - Entry 2: File
config/autostart.sh
- Entry 1: Symlink
- Victim downloads and installs the game.
unrar xextracts the symlink and payload into.local.installing.install_innerrenames.local.installingtolocal/without inspecting contents.- Subsequent operations traversing
local/follow the symlink outside the game directory.
Actionable Remediation Guidance
Before calling rename_path(&staging, &local) in install_inner, execute a mandatory audit of staging:
for entry in walkdir::WalkDir::new(&staging) {
let entry = entry?;
let meta = std::fs::symlink_metadata(entry.path())?;
if meta.file_type().is_symlink() {
eyre::bail!("Unsafe symlink detected in extracted archive: {}", entry.path().display());
}
}
Verify that all regular files match the expected sizes and BLAKE3 digests in the catalog manifest.
Finding EXP2-SEC-02: Unbounded Archive Decompression (Zip/RAR Bomb) Leading to Host Disk Exhaustion Denial of Service
- Severity Rating: Medium (CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H)
- Category: Exploitable Vulnerability / Denial of Service
- Affected Component:
crates/lanspread-tauri-deno-ts(src-tauri),crates/lanspread-peer(install/transaction.rs) - File & Line References:
crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:3166-3240(run_unrar_sidecar)crates/lanspread-peer/src/install/transaction.rs:514-544
Vulnerability Description & Root Cause Analysis
In run_unrar_sidecar, ScopedProcess::spawn captures stdout/stderr up to UNRAR_LOG_CAPTURE_LIMIT (1 MB), but enforces no limit on the volume of uncompressed data written to disk. The application does not check available disk space prior to spawning unrar, nor does it monitor staging folder size during extraction.
Threat & Impact Analysis
An attacker delivering a highly compressed RAR bomb (e.g. a 20 MB archive that expands to 500 GB of zeros) will cause unrar to consume all available disk space (ENOSPC), causing system-wide application crashes, database corruption, and host instability.
Actionable Remediation Guidance
- Query available disk space using
fs2::available_spaceprior to extraction, ensuringavailable_space >= expected_uncompressed_bytes + SAFETY_BUFFER. - Enforce a maximum uncompressed extraction ceiling (e.g.,
MAX_CATALOG_TOTAL_BYTES).
Finding EXP2-SEC-03: Incomplete Path Sanitization in path_validation.rs (Omission of Windows Device Names & Trailing Dots/Spaces)
- Severity Rating: Medium (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:M/A:L)
- Category: Exploitable Vulnerability / Windows Path Traversal
- Affected Component:
crates/lanspread-peer(path_validation.rs) - File & Line References:
crates/lanspread-peer/src/path_validation.rs:14-40(sanitize_relative_path)crates/lanspread-peer/src/path_validation.rs:46-88(validate_relative_path)
Vulnerability Description & Root Cause Analysis
path_validation.rs checks for empty strings, null bytes, UNC prefixes (//), and drive separators (:/). However, unlike download/manifest.rs:381-391 and content_manifest/path.rs:194-204:
- It does not check for Windows DOS reserved device names (
CON,PRN,AUX,NUL,COM1..COM9,LPT1..LPT9). - It does not reject components ending in dots or spaces (
.or' '), which Win32 file APIs strip automatically (e.g.file.txt.becomesfile.txt). - It does not check for drive-relative paths such as
C:temp.
Threat & Impact Analysis
On Windows hosts, creating or resolving files named CON or AUX causes file APIs to block indefinitely or interact with console devices, leading to Denial of Service or unhandled I/O failures.
Actionable Remediation Guidance
Harmonize path_validation.rs with content_manifest/path.rs:
fn validate_component(component: &str) -> eyre::Result<()> {
if component.is_empty() || matches!(component, "." | "..") {
eyre::bail!("Invalid component: {component:?}");
}
if component.ends_with([' ', '.']) {
eyre::bail!("Path component has trailing dot or space: {component}");
}
if is_windows_device_name(component) {
eyre::bail!("Path uses Windows device name: {component}");
}
Ok(())
}
Finding EXP2-SEC-04: Under-Constrained game_id Validation in lanspread-proto Wire Protocol Boundary
- Severity Rating: Medium (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N)
- Category: Defense-in-Depth / Input Validation
- Affected Component:
crates/lanspread-proto - File & Line References:
crates/lanspread-proto/src/lib.rs:769-780, 912-923(validate_game_id)
Vulnerability Description & Root Cause Analysis
In lanspread-proto, wire requests (Request::GetGameFileChunk, Request::StreamInstall) validate game_id via:
fn validate_game_id(game_id: &str) -> Result<(), ControlValidationError> {
if game_id.trim().is_empty() {
return Err(ControlValidationError::EmptyField { field: "game ID" });
}
if game_id.len() > MAX_GAME_ID_BYTES {
return Err(ControlValidationError::FieldTooLong { field: "game ID", maximum: MAX_GAME_ID_BYTES });
}
Ok(())
}
This allows path traversal characters (../, /, \), null bytes, and control characters through the wire protocol decoding step. In contrast, download/manifest.rs and content_manifest/path.rs enforce single component checks, NFC normalization, and reserved directory name rejection.
Actionable Remediation Guidance
Add character and component constraints to validate_game_id in lanspread-proto:
if game_id.contains(['/', '\\', '\0']) || game_id.contains("..") {
return Err(ControlValidationError::InvalidPathCharacters { field: "game ID" });
}
Finding EXP2-SEC-05: Direct Pre-Verification Chunk Writes to Filesystem
- Severity Rating: Low (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N)
- Category: Defense-in-Depth / Crash Consistency
- Affected Component:
crates/lanspread-peer(download/transport.rs) - File & Line References:
crates/lanspread-peer/src/download/transport.rs:367-402(receive_chunk)
Vulnerability Description & Root Cause Analysis
In receive_chunk, raw incoming bytes from peer streams are written directly to the target file via write_chunk_bytes(&mut file, &bytes) before verifier.finish(...) completes the BLAKE3 hash check. While uncommitted files are guarded by version.ini deferral and cleanup journals, a host crash or sudden power failure during an active download leaves corrupt chunk bytes on disk.
Actionable Remediation Guidance
Buffer the chunk in memory (chunks are up to 4 MiB) and compute the BLAKE3 hash before writing the verified buffer to disk.
Finding EXP2-SEC-06: Predictable Fallback State Directory in World-Writable Shared /tmp Location
- Severity Rating: Low (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N)
- Category: Hardening / Multi-User Safety
- Affected Component:
crates/lanspread-peer(state_paths.rs) - File & Line References:
crates/lanspread-peer/src/state_paths.rs:18-32(resolve_state_dir)
Vulnerability Description & Root Cause Analysis
If neither LANSPREAD_STATE_DIR nor HOME/USERPROFILE environment variables are set, resolve_state_dir falls back to std::env::temp_dir().join("lanspread") (/tmp/lanspread). On multi-user systems, /tmp is world-writable, allowing a local attacker to pre-create /tmp/lanspread with malicious permissions or symlinks.
Actionable Remediation Guidance
Incorporate the process UID into the fallback path on Unix (/tmp/lanspread-<UID>) and enforce 0o700 directory permissions upon creation.
Finding EXP2-SEC-07: Ambient File Creation in stream_install.rs Bypassing Capability-Based ConfinedGameRoot
- Severity Rating: Low (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N)
- Category: Hardening / Architectural Parity
- Affected Component:
crates/lanspread-peer(stream_install.rs) - File & Line References:
crates/lanspread-peer/src/stream_install.rs:1295-1316, 1895-1900
Vulnerability Description & Root Cause Analysis
Unlike the standard download manager which uses capability-based ConfinedGameRoot with FollowSymlinks::No, stream_install.rs uses ambient std::fs::create_dir_all and File::create(&path). While staging paths are validated as empty on initialization, using ambient filesystem APIs diverges from the project's capability-based confinement architecture.
Actionable Remediation Guidance
Refactor StreamInstallReceiveState to operate through ConfinedGameRoot or MutationGameRoot handles with FollowSymlinks::No.
5.3 Tauri Desktop Shell, IPC Boundaries, Webview Isolation & Frontend Security
Finding SEC-IPC-01: Elevated Administrator Execution of Untrusted P2P Batch Scripts with Fragile Parameter Parsing
- Severity Rating: High (CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)
- Category: Exploitable Vulnerability / Remote Code Execution & Privilege Escalation
- Affected Component:
crates/lanspread-tauri-deno-ts(src-tauri/src/lib.rs) - File & Line References:
crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:1474-1487(sanitize_username)crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:1506-1513(script_params_with_mode)crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:1555-1582(run_as_admin_detached)crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:1818-1864(run_as_admin_and_wait)crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:1867-1959(run_game_windows)crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:2008-2064(start_server_windows)
Vulnerability Description & Root Cause Analysis
When running a game or starting a dedicated server on Windows, run_game_windows and start_server_windows execute setup and launch scripts (game_setup.cmd, game_start.cmd, server_start.cmd) located in the downloaded game directory using ShellExecuteExW / ShellExecuteW with lpVerb = "runas", triggering Administrator UAC elevation:
// src-tauri/src/lib.rs:1913-1918
run_as_admin_and_wait(
"cmd.exe",
&setup_params,
&game_dir,
windows::Win32::UI::WindowsAndMessaging::SW_HIDE,
)
These scripts originate from untrusted remote P2P peers.
Furthermore, parameters are assembled via:
format!(
r#"/d /s {cmd_mode} ""{}" "local" "{}" "{}" "{}"""#,
script_path.display(),
id,
settings.language,
settings.username,
)
sanitize_username only strips control characters, ", and % (!c.is_control() && *c != '"' && *c != '%'). It does not filter shell meta-characters such as &, |, ^, <, >, (, ).
Threat & Impact Analysis
- Elevated Remote Code Execution: An attacker sharing a game package on the LAN containing a malicious
game_setup.cmdachieves full Administrator execution on the victim machine as soon as the user clicks "Play". - Parameter Injection: Unsanitized username or language values containing
&or|can trigger arbitrary command chaining insidecmd.exe.
Concrete Attack Scenario / PoC
- Attacker hosts a game on the LAN with a crafted
game_setup.cmdthat executes administrative commands (e.g. modifying system registry or installing persistent services). - Victim downloads the game via P2P and clicks "Play".
lanspreadprompts the user with a standard Windows UAC dialog (invokingcmd.exeas Administrator).- Upon user confirmation, the attacker's script executes with full Local Administrator privileges.
Actionable Remediation Guidance
- Enforce Cryptographic Script Verification: Before executing any
.cmdor executable file, verify its exact BLAKE3 digest against the authoritativeCatalogContentManifest. Reject execution if the hash does not match. - Strict Username Whitelist: Constrain username and language parameters to
[a-zA-Z0-9_-]. - Avoid Shell Invocation: Execute target binaries directly rather than wrapping them in
cmd.exe /cwhere feasible.
Finding SEC-IPC-02: Disabled Content Security Policy (csp: null) in Tauri Webview Configuration
- Severity Rating: High (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N)
- Category: Defense-in-Depth / Webview Isolation
- Affected Component:
crates/lanspread-tauri-deno-ts(src-tauri/tauri.conf.json) - File & Line References:
crates/lanspread-tauri-deno-ts/src-tauri/tauri.conf.json:20-22
Vulnerability Description & Root Cause Analysis
In tauri.conf.json:
"app": {
"security": {
"csp": null
}
}
Setting "csp": null explicitly disables Tauri's Content Security Policy injection.
Threat & Impact Analysis
Without a CSP, the webview renderer has no browser-level restrictions on network connections, script sources, or object embedding. If an XSS vulnerability occurs anywhere in the frontend (e.g. via peer chat messages, manipulated game metadata, or a compromised frontend dependency), an attacker can execute arbitrary scripts, connect to external servers, or invoke accessible Tauri IPC commands.
Actionable Remediation Guidance
Define a strict Content Security Policy in tauri.conf.json:
"security": {
"csp": "default-src 'self'; img-src 'self' data: asset:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src ipc:; frame-src 'none'; object-src 'none'; base-uri 'none';"
}
Finding SEC-IPC-03: Unvalidated game_id Path Resolution & Leftover Debug Macro in get_game_thumbnail
- Severity Rating: Medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)
- Category: Exploitable Vulnerability / Path Resolution
- Affected Component:
crates/lanspread-tauri-deno-ts(src-tauri/src/lib.rs) - File & Line References:
crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:1534-1552(get_game_thumbnail)
Vulnerability Description & Root Cause Analysis
get_game_thumbnail is implemented as:
#[tauri::command]
async fn get_game_thumbnail(
game_id: String,
app_handle: tauri::AppHandle,
state: tauri::State<'_, LanSpreadState>,
) -> tauri::Result<String> {
use base64::Engine;
let _app_invoke = enter_app_invoke(state.inner())?;
let resource_path = app_handle.path().resolve(
format!("assets/{game_id}.jpg"),
tauri::path::BaseDirectory::Resource,
)?;
dbg!(&resource_path);
let image_data = scoped_blocking(|| std::fs::read(&resource_path))?;
let base64_data = base64::engine::general_purpose::STANDARD.encode(&image_data);
Ok(format!("data:image/jpeg;base64,{base64_data}"))
}
Unlike run_game_windows, get_game_thumbnail does not call is_single_component_game_id(&game_id). Passing ../ sequences allows resolving and reading arbitrary .jpg files from the resource bundle. Additionally, line 1547 contains dbg!(&resource_path), which emits filesystem paths to stderr in release builds.
Actionable Remediation Guidance
Validate game_id with is_single_component_game_id(&game_id) and remove the dbg! macro.
Finding SEC-IPC-04: Unsandboxed External Archive Extraction (unrar) for Untrusted P2P Archives
- Severity Rating: Medium (CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:M/I:M/A:N)
- Category: Defense-in-Depth / Subprocess Sandboxing
- Affected Component:
crates/lanspread-peer-cli,crates/lanspread-tauri-deno-ts(src-tauri) - File & Line References:
crates/lanspread-peer-cli/src/lib.rs:344-370(ExternalUnrarUnpacker)crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:3195-3221(run_unrar_sidecar)
Vulnerability Description & Root Cause Analysis
ExternalUnrarUnpacker and run_unrar_sidecar invoke binaries/unrar directly without OS-level sandboxing (e.g. Landlock, pledge, AppContainer) or symlink stripping flags (-sl-). If an untrusted RAR contains directory traversal paths or symlinks, unrar could write outside the destination directory.
Actionable Remediation Guidance
Pass symlink-disabling flags (-sl-) to unrar and verify that all extracted files reside strictly within the destination staging directory.
Finding SEC-IPC-05: Broad Webview Window Creation Capabilities (allow-create-webview-window)
- Severity Rating: Low (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:N)
- Category: Hardening / Least Privilege
- Affected Component:
crates/lanspread-tauri-deno-ts(src-tauri/capabilities/default.json) - File & Line References:
crates/lanspread-tauri-deno-ts/src-tauri/capabilities/default.json:11
Vulnerability Description & Root Cause Analysis
capabilities/default.json grants "core:webview:allow-create-webview-window" to the default webview. If an XSS vulnerability occurs, script injection could dynamically spawn unmonitored browser windows.
Actionable Remediation Guidance
Statically declare companion windows (main-logs, unpack-logs) in tauri.conf.json and remove dynamic window creation permissions from default.json.
Finding SEC-FE-01: Client-Side Regular Expression Denial of Service (ReDoS) in Log Viewers
- Severity Rating: Low (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L)
- Category: Exploitable Vulnerability / UI DoS
- Affected Component:
crates/lanspread-tauri-deno-ts(Frontend) - File & Line References:
crates/lanspread-tauri-deno-ts/src/MainLogsWindow.tsx:160-164crates/lanspread-tauri-deno-ts/src/UnpackLogsWindow.tsx:109-113
Vulnerability Description & Root Cause Analysis
User-supplied filter strings are compiled via new RegExp(regexInput, 'i') and executed synchronously over thousands of log lines on every keypress. A pattern with nested quantifiers (e.g. (a+)+$) triggers catastrophic backtracking, freezing the UI thread.
Actionable Remediation Guidance
Enforce a length limit on regexInput, debounce execution, or execute regex evaluation inside a Web Worker.
5.4 Database & Data Persistence Integrity
Finding SEC-DB-01: Missing Defensive SQLite PRAGMAs (trusted_schema = OFF) on Catalog Database Pool
- Severity Rating: Low (CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N)
- Category: Defense-in-Depth / Database Hardening
- Affected Component:
crates/lanspread-compat - File & Line References:
crates/lanspread-compat/src/catalog_bundle.rs:67-75crates/lanspread-compat/src/eti.rs:29-45
Vulnerability Description & Root Cause Analysis
When opening SQLite database pools, SqliteConnectOptions configures .read_only(true), but does not explicitly set PRAGMA trusted_schema = OFF; or PRAGMA cell_size_check = ON;. Setting trusted_schema = OFF ensures that SQLite triggers or virtual table functions in loaded databases cannot execute untrusted code.
Actionable Remediation Guidance
Configure connection pools with defensive SQLite PRAGMAs:
SqliteConnectOptions::new()
.filename(db_path)
.read_only(true)
.pragma("trusted_schema", "OFF")
.pragma("cell_size_check", "ON")
6. Prioritized Recommendations & Hardening Roadmap
To systematically resolve the identified findings, the remediation efforts are prioritized into three actionable tiers:
Tier 1: Immediate Remediation (High Priority / P0)
Target Timeline: Immediate release / next sprint
- Mitigate Elevated Batch Script Execution (SEC-IPC-01):
- Enforce cryptographic BLAKE3 manifest verification for all
.cmdand.exescripts prior to execution. - Enforce strict username/language character sanitization (
[a-zA-Z0-9_-]).
- Enforce cryptographic BLAKE3 manifest verification for all
- Deploy Strict Webview Content Security Policy (SEC-IPC-02):
- Configure
"csp": "default-src 'self'; img-src 'self' data: asset:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src ipc:; frame-src 'none'; object-src 'none'; base-uri 'none';"intauri.conf.json.
- Configure
- Enforce Mutual TLS & Inbound Identity Binding (NET-01):
- Require client certificates in TLS 1.3 server config and verify that inbound
ChangeHintmessages originate from the connection's authenticatedPeerId.
- Require client certificates in TLS 1.3 server config and verify that inbound
- Post-Extraction Manifest & Symlink Audit (EXP2-SEC-01):
- Add a recursive audit step in
install/transaction.rsthat validates all extracted files in.local.installingagainst manifest BLAKE3 digests and rejects any symbolic links or NTFS junctions.
- Add a recursive audit step in
Tier 2: Medium-Term Hardening (P1)
Target Timeline: Next minor version milestone
- mDNS Destination Sanitization (NET-02): Reject multicast, broadcast, unspecified, and zero-port endpoints in
validated_candidate_endpoint. - Asymmetric Request Framing Bounds (NET-03): Reduce inbound request
FramedReadlimits to 64 KiB while maintaining 8 MiB for response snapshots. - Discovery Rate Limiter LRU Eviction (NET-04): Transition
RecentCandidatesfrom hard drop to FIFO/LRU eviction when at capacity. - Decompression Bomb Protection (EXP2-SEC-02): Preflight available disk space before spawning
unrarand enforce maximum extraction size limits. - Path Validation Harmonization (EXP2-SEC-03, EXP2-SEC-04): Align
path_validation.rsandlanspread-protowithdownload/manifest.rs(checking Windows reserved device names, NFC normalization, and trailing dots/spaces). - Thumbnail Command Sanitization (SEC-IPC-03): Enforce single-component validation in
get_game_thumbnailand remove debug macros. - Unrar Subprocess Flags (SEC-IPC-04): Pass
-sl-tounrarand verify destination containment.
Tier 3: Architectural Enhancements (P2)
Target Timeline: Long-term architectural refinement
- Pre-Verification Chunk Memory Buffering (EXP2-SEC-05): Buffer chunks in memory and verify BLAKE3 hashes prior to disk persistence.
- UID-Isolated Temp State Directories (EXP2-SEC-06): Append process UID and enforce
0o700permissions for fallback state paths in/tmp. - Capability VFS for Stream Install (EXP2-SEC-07): Refactor
stream_install.rsto useConfinedGameRoothandles withFollowSymlinks::No. - Log Viewer ReDoS Guards (SEC-FE-01): Enforce length limits and debouncing on frontend regex filters.
- Defensive SQLite PRAGMAs (SEC-DB-01): Set
trusted_schema = OFFandcell_size_check = ONon all SQLite connection pools. - Webview Capability Minimization (SEC-IPC-05): Statically configure log windows and revoke dynamic window creation capabilities.
7. Codebase Health & Verification Results
The workspace build, test suite, and linter were executed to verify codebase integrity. All commands passed cleanly with zero regressions:
Test Suite Execution
- Command:
just test - Result: PASS (100% of workspace tests passing)
- Summary:
lanspread-mdns: 21 tests passedlanspread-proto: 25 tests passed (22 unit tests + 3 integration tests)lanspread-tauri-deno-ts(Rust backend): 62 tests passed (56 unit tests + 6 catalog gate tests)lanspread-db: all database tests passedlanspread-peer: all core peer tests passed
Frontend Unit Test Execution
- Command:
just frontend-test - Result: PASS (91 frontend reducer and state persistence tests passed in 249ms)
Clippy Linting & Static Analysis
- Command:
just clippy - Result: PASS (Zero warnings across all workspace crates with pedantic clippy lints enabled)
Report compiled and certified for the lanspread project security audit.