Files
lanspread/SECURITY_AUDIT_2026-08-28_GEMINI-3.7-HIGH_TEAMWORK.md
T
2026-09-02 22:08:37 +02:00

65 KiB
Raw Blame History

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

  1. Executive Summary & Overall Security Posture
  2. Threat Model & Attack Surface Breakdown
  3. Audit Scope & Component Coverage
  4. Comprehensive Findings Matrix
  5. Detailed Vulnerability & Hardening Write-ups
  6. Prioritized Recommendations & Hardening Roadmap
  7. 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 lanspread crafted to harvest metadata, exhaust node resources, disrupt network discovery, or deliver malicious archive content and scripts.

Major Architectural Strengths

  1. Capability-Based Directory Confinement (cap_primitives): The core download engine enforces filesystem sandboxing using ConfinedGameRoot and handle-relative file operations with FollowSymlinks::No, preventing path traversal during standard chunk transfers.
  2. 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.
  3. 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.
  4. Parameterized Database Layer: SQLite interactions in lanspread-db and lanspread-compat utilize sqlx prepared queries with parameter binding, eliminating classic SQL injection vulnerabilities.
  5. 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, lanspread executes batch scripts (game_setup.cmd, game_start.cmd, server_start.cmd) downloaded from untrusted peers with elevated Administrator privileges (runas verb via cmd.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 forged ChangeHint messages impersonating other peers, triggering concurrent outbound QUIC connection floods directed at victim nodes.
  • Disabled Webview Content Security Policy: tauri.conf.json explicitly 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 external unrar sidecar 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.

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.

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.exe with 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.

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-134
src/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-412
lanspread-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:13
src/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-341
src/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-544
src-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-3240
src/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-370
src-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-164
src/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-75
src/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
  1. Reflected DoS / Network Amplification: An attacker on the LAN can establish unauthenticated QUIC streams to every peer on the subnet and send forged ChangeHint messages claiming Peer_Victim has published revision 999999. Every peer node on the network will simultaneously initiate an outbound QUIC connection to Peer_Victim and pull its full HelloSnapshot, exhausting Peer_Victim's network bandwidth, QUIC connection limits, and CPU.
  2. Unauthenticated Information Harvesting: Any anonymous client can connect to any running peer and issue Request::Hello or Request::Ping to 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
  1. An attacker on the LAN listens to mDNS broadcasts and identifies Peer_Victim (PeerId_V, IP 192.168.1.50:42424) and 30 other participating peers (Peer_1 through Peer_30).
  2. The attacker opens a standard QUIC connection to Peer_1 (192.168.1.10:42424).
  3. Over a bidirectional stream, the attacker sends:
    {"LibraryChanged":{"claimed_peer_id":"<PeerId_V>","runtime_session_id":"00000000000000000000000000000000","revision":999999}}
    
  4. Peer_1 receives the frame, observes that PeerId_V has a new revision, and initiates perform_refresh_for_peer(PeerId_V).
  5. The attacker repeats steps 23 across Peer_2 through Peer_30.
  6. All 30 peers concurrently open QUIC connections to Peer_Victim and request full library snapshots, saturating Peer_Victim's connection permits and CPU.
Actionable Remediation Guidance
  1. Enforce Mutual TLS (mTLS):
    • Configure ServerConfig to require client certificates via a custom ClientCertVerifier that validates the client's Ed25519 self-signed certificate and extracts their public key.
    • Configure ClientConfig to present the local PeerIdentity certificate during outbound handshakes.
  2. Bind Inbound Commands to Verified Identity:
    • Extract the authenticated client PeerId from the TLS connection metadata in handle_peer_connection.
    • In stream.rs, assert hint.claimed_peer_id == authenticated_peer_id. Reject and drop any hint where the claimed identity does not match the TLS identity.

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
  1. Attacker transmits an mDNS response for service _lanspread._udp.local. with TXT record proto_ver=8, peer_id=<valid_base32_id>, and host address 224.0.0.251:42424.
  2. MdnsBrowser parses the record and sends an MdnsService event with addr = 224.0.0.251:42424.
  3. validated_candidate_endpoint accepts the endpoint.
  4. run_peer_discovery dispatches a QUIC connection attempt to 224.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-341
    • crates/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:

  1. There is no post-extraction inspection or validation of the files materialized in staging.
  2. The application does not check whether unrar created symlinks or Windows directory junctions pointing outside staging / local.
  3. The application does not verify extracted files against the catalog manifest's expected file list (CatalogContentManifest) or compute BLAKE3 hashes of extracted contents.
  4. Unlike stream_install.rs (which explicitly validates each entry against CatalogExtractedEntry and checks BLAKE3 hashes), the .eti archive path completely trusts the external unrar tool 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
  1. Attacker creates an .eti archive containing:
    • Entry 1: Symlink config -> /home/victim/.config/
    • Entry 2: File config/autostart.sh
  2. Victim downloads and installs the game.
  3. unrar x extracts the symlink and payload into .local.installing.
  4. install_inner renames .local.installing to local/ without inspecting contents.
  5. 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
  1. Query available disk space using fs2::available_space prior to extraction, ensuring available_space >= expected_uncompressed_bytes + SAFETY_BUFFER.
  2. 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:

  1. It does not check for Windows DOS reserved device names (CON, PRN, AUX, NUL, COM1..COM9, LPT1..LPT9).
  2. It does not reject components ending in dots or spaces (. or ' '), which Win32 file APIs strip automatically (e.g. file.txt. becomes file.txt).
  3. 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.cmd achieves 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 inside cmd.exe.
Concrete Attack Scenario / PoC
  1. Attacker hosts a game on the LAN with a crafted game_setup.cmd that executes administrative commands (e.g. modifying system registry or installing persistent services).
  2. Victim downloads the game via P2P and clicks "Play".
  3. lanspread prompts the user with a standard Windows UAC dialog (invoking cmd.exe as Administrator).
  4. Upon user confirmation, the attacker's script executes with full Local Administrator privileges.
Actionable Remediation Guidance
  1. Enforce Cryptographic Script Verification: Before executing any .cmd or executable file, verify its exact BLAKE3 digest against the authoritative CatalogContentManifest. Reject execution if the hash does not match.
  2. Strict Username Whitelist: Constrain username and language parameters to [a-zA-Z0-9_-].
  3. Avoid Shell Invocation: Execute target binaries directly rather than wrapping them in cmd.exe /c where 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-164
    • crates/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-75
    • crates/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

  1. Mitigate Elevated Batch Script Execution (SEC-IPC-01):
    • Enforce cryptographic BLAKE3 manifest verification for all .cmd and .exe scripts prior to execution.
    • Enforce strict username/language character sanitization ([a-zA-Z0-9_-]).
  2. 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';" in tauri.conf.json.
  3. Enforce Mutual TLS & Inbound Identity Binding (NET-01):
    • Require client certificates in TLS 1.3 server config and verify that inbound ChangeHint messages originate from the connection's authenticated PeerId.
  4. Post-Extraction Manifest & Symlink Audit (EXP2-SEC-01):
    • Add a recursive audit step in install/transaction.rs that validates all extracted files in .local.installing against manifest BLAKE3 digests and rejects any symbolic links or NTFS junctions.

Tier 2: Medium-Term Hardening (P1)

Target Timeline: Next minor version milestone

  1. mDNS Destination Sanitization (NET-02): Reject multicast, broadcast, unspecified, and zero-port endpoints in validated_candidate_endpoint.
  2. Asymmetric Request Framing Bounds (NET-03): Reduce inbound request FramedRead limits to 64 KiB while maintaining 8 MiB for response snapshots.
  3. Discovery Rate Limiter LRU Eviction (NET-04): Transition RecentCandidates from hard drop to FIFO/LRU eviction when at capacity.
  4. Decompression Bomb Protection (EXP2-SEC-02): Preflight available disk space before spawning unrar and enforce maximum extraction size limits.
  5. Path Validation Harmonization (EXP2-SEC-03, EXP2-SEC-04): Align path_validation.rs and lanspread-proto with download/manifest.rs (checking Windows reserved device names, NFC normalization, and trailing dots/spaces).
  6. Thumbnail Command Sanitization (SEC-IPC-03): Enforce single-component validation in get_game_thumbnail and remove debug macros.
  7. Unrar Subprocess Flags (SEC-IPC-04): Pass -sl- to unrar and verify destination containment.

Tier 3: Architectural Enhancements (P2)

Target Timeline: Long-term architectural refinement

  1. Pre-Verification Chunk Memory Buffering (EXP2-SEC-05): Buffer chunks in memory and verify BLAKE3 hashes prior to disk persistence.
  2. UID-Isolated Temp State Directories (EXP2-SEC-06): Append process UID and enforce 0o700 permissions for fallback state paths in /tmp.
  3. Capability VFS for Stream Install (EXP2-SEC-07): Refactor stream_install.rs to use ConfinedGameRoot handles with FollowSymlinks::No.
  4. Log Viewer ReDoS Guards (SEC-FE-01): Enforce length limits and debouncing on frontend regex filters.
  5. Defensive SQLite PRAGMAs (SEC-DB-01): Set trusted_schema = OFF and cell_size_check = ON on all SQLite connection pools.
  6. 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 passed
    • lanspread-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 passed
    • lanspread-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.