remove old gemini security audit doc
This commit is contained in:
@@ -1,922 +0,0 @@
|
||||
# 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](#1-executive-summary--overall-security-posture)
|
||||
2. [Threat Model & Attack Surface Breakdown](#2-threat-model--attack-surface-breakdown)
|
||||
- [2.1 Adversary Model & Trust Assumptions](#21-adversary-model--trust-assumptions)
|
||||
- [2.2 LAN mDNS Discovery & Peer Spoofing](#22-lan-mdns-discovery--peer-spoofing)
|
||||
- [2.3 QUIC Transport, TLS 1.3 & Wire Protocol Framing](#23-quic-transport-tls-13--wire-protocol-framing)
|
||||
- [2.4 Download Path Traversal, Chunk Verification & Archive Extraction](#24-download-path-traversal-chunk-verification--archive-extraction)
|
||||
- [2.5 Tauri IPC Boundaries, Webview Isolation & Frontend Security](#25-tauri-ipc-boundaries-webview-isolation--frontend-security)
|
||||
- [2.6 Database & Persistence Integrity](#26-database--persistence-integrity)
|
||||
3. [Audit Scope & Component Coverage](#3-audit-scope--component-coverage)
|
||||
4. [Comprehensive Findings Matrix](#4-comprehensive-findings-matrix)
|
||||
5. [Detailed Vulnerability & Hardening Write-ups](#5-detailed-vulnerability--hardening-write-ups)
|
||||
- [5.1 P2P Networking, Discovery & Wire Protocol](#51-p2p-networking-discovery--wire-protocol)
|
||||
- [Finding NET-01: Unauthenticated Inbound QUIC Streams & Forged Change Hints Trigger Reflected State-Pull Flooding (High)](#finding-net-01-unauthenticated-inbound-quic-streams--forged-change-hints-trigger-reflected-state-pull-flooding)
|
||||
- [Finding NET-02: Missing IP Address & Port Validation in Discovered mDNS Services Enables Handshake Redirection to Multicast / Broadcast Targets (Medium)](#finding-net-02-missing-ip-address--port-validation-in-discovered-mdns-services-enables-handshake-redirection-to-multicast--broadcast-targets)
|
||||
- [Finding NET-03: Inbound Request Framing Uses Excessive 8 MiB Limit Allowing Stream Memory Exhaustion DoS (Medium)](#finding-net-03-inbound-request-framing-uses-excessive-8-mib-limit-allowing-stream-memory-exhaustion-dos)
|
||||
- [Finding NET-04: Global Capacity Cap in RecentCandidates Allows LAN Adversary to Deny Peer Discovery (Medium)](#finding-net-04-global-capacity-cap-in-recentcandidates-allows-lan-adversary-to-deny-peer-discovery)
|
||||
- [Finding NET-05: Unbounded Collection Deserialization in Response::decode Prior to Semantic Bounds Validation (Low)](#finding-net-05-unbounded-collection-deserialization-in-responsedecode-prior-to-semantic-bounds-validation)
|
||||
- [Finding NET-06: Unauthenticated Game Chunk and Stream-Install Egress on QUIC Server (Informational)](#finding-net-06-unauthenticated-game-chunk-and-stream-install-egress-on-quic-server)
|
||||
- [5.2 File Transfer, Storage Safety, Chunk Verification & Archive Extraction](#52-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-01-unsafe-archive-extraction-in-sidecarunpacker-missing-post-unpack-manifest-validation--symlink-redirection-risk)
|
||||
- [Finding EXP2-SEC-02: Unbounded Archive Decompression (Zip/RAR Bomb) Leading to Host Disk Exhaustion Denial of Service (Medium)](#finding-exp2-sec-02-unbounded-archive-decompression-ziprar-bomb-leading-to-host-disk-exhaustion-denial-of-service)
|
||||
- [Finding EXP2-SEC-03: Incomplete Path Sanitization in path_validation.rs (Omission of Windows Device Names & Trailing Dots/Spaces) (Medium)](#finding-exp2-sec-03-incomplete-path-sanitization-in-path_validationrs-omission-of-windows-device-names--trailing-dotsspaces)
|
||||
- [Finding EXP2-SEC-04: Under-Constrained game_id Validation in lanspread-proto Wire Protocol Boundary (Medium)](#finding-exp2-sec-04-under-constrained-game_id-validation-in-lanspread-proto-wire-protocol-boundary)
|
||||
- [Finding EXP2-SEC-05: Direct Pre-Verification Chunk Writes to Filesystem (Low / Defense-in-Depth)](#finding-exp2-sec-05-direct-pre-verification-chunk-writes-to-filesystem)
|
||||
- [Finding EXP2-SEC-06: Predictable Fallback State Directory in World-Writable Shared /tmp Location (Low)](#finding-exp2-sec-06-predictable-fallback-state-directory-in-world-writable-shared-tmp-location)
|
||||
- [Finding EXP2-SEC-07: Ambient File Creation in stream_install.rs Bypassing Capability-Based ConfinedGameRoot (Low / Hardening)](#finding-exp2-sec-07-ambient-file-creation-in-stream_installrs-bypassing-capability-based-confinedgameroot)
|
||||
- [5.3 Tauri Desktop Shell, IPC Boundaries, Webview Isolation & Frontend Security](#53-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-01-elevated-administrator-execution-of-untrusted-p2p-batch-scripts-with-fragile-parameter-parsing)
|
||||
- [Finding SEC-IPC-02: Disabled Content Security Policy (csp: null) in Tauri Webview Configuration (High)](#finding-sec-ipc-02-disabled-content-security-policy-csp-null-in-tauri-webview-configuration)
|
||||
- [Finding SEC-IPC-03: Unvalidated game_id Path Resolution & Leftover Debug Macro in get_game_thumbnail (Medium)](#finding-sec-ipc-03-unvalidated-game_id-path-resolution--leftover-debug-macro-in-get_game_thumbnail)
|
||||
- [Finding SEC-IPC-04: Unsandboxed External Archive Extraction (unrar) for Untrusted P2P Archives (Medium)](#finding-sec-ipc-04-unsandboxed-external-archive-extraction-unrar-for-untrusted-p2p-archives)
|
||||
- [Finding SEC-IPC-05: Broad Webview Window Creation Capabilities (allow-create-webview-window) (Low)](#finding-sec-ipc-05-broad-webview-window-creation-capabilities-allow-create-webview-window)
|
||||
- [Finding SEC-FE-01: Client-Side Regular Expression Denial of Service (ReDoS) in Log Viewers (Low)](#finding-sec-fe-01-client-side-regular-expression-denial-of-service-redos-in-log-viewers)
|
||||
- [5.4 Database & Data Persistence Integrity](#54-database--data-persistence-integrity)
|
||||
- [Finding SEC-DB-01: Missing Defensive SQLite PRAGMAs (trusted_schema = OFF) on Catalog Database Pool (Low / Defense-in-Depth)](#finding-sec-db-01-missing-defensive-sqlite-pragmas-trusted_schema--off-on-catalog-database-pool)
|
||||
6. [Prioritized Recommendations & Hardening Roadmap](#6-prioritized-recommendations--hardening-roadmap)
|
||||
7. [Codebase Health & Verification Results](#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_id`s, 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`<br>`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`<br>`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`<br>`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`<br>`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`<br>`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`<br>`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`<br>`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`<br>`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`<br>`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:
|
||||
```rust
|
||||
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`:
|
||||
```rust
|
||||
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:
|
||||
```json
|
||||
{"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 2–3 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`:
|
||||
```rust
|
||||
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`:
|
||||
```rust
|
||||
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`:
|
||||
```rust
|
||||
pub const MAX_CONTROL_FRAME_BYTES: usize = 8 * 1024 * 1024; // 8 MiB
|
||||
```
|
||||
In `stream.rs`:
|
||||
```rust
|
||||
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:
|
||||
```rust
|
||||
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`:
|
||||
```rust
|
||||
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 `PeerId`s 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:
|
||||
```rust
|
||||
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 `PeerId`s 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:
|
||||
```rust
|
||||
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`:
|
||||
```rust
|
||||
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`:
|
||||
```rust
|
||||
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:
|
||||
```rust
|
||||
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`:
|
||||
```rust
|
||||
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:
|
||||
```rust
|
||||
// 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:
|
||||
```rust
|
||||
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`:
|
||||
```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`:
|
||||
```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:
|
||||
```rust
|
||||
#[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:
|
||||
```rust
|
||||
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.*
|
||||
Reference in New Issue
Block a user