fix(security): land the 2026-07 audit fixes — SSRF guards, roster lane, SYSTEM path hygiene
The low/medium findings from the July host+Windows security review, as implemented in the audit session's working tree: - Webhooks and library-art fetches refuse loopback/link-local/metadata targets and no longer follow redirects (SSRF pivots from the privileged host process). - The paired-client rosters (/clients, /native/clients) move off the streaming-client auth lane — one paired device could enumerate every other device's name + fingerprint; only the bearer/loopback console keeps them. - Device-name sanitizing extends to bidi/format control characters (spoofable rendering) via the shared native_pairing::is_spoofy_char; stream-marker quoting uses the same set. - The SYSTEM service resolves powershell by its full System32 path — CreateProcess checks the launching EXE's own directory first, so a planted powershell.exe beside the host binary would have run as SYSTEM. - The pf-vdisplay driver's opt-in file log moves from world-writable C:\Users\Public to WUDFHost's own temp dir. - GameStream pairing sessions are single-use (removed whatever the outcome). - Uninstall also removes the pf_mouse driver-store entry (rider from the virtual-HID-mouse work). - openapi.json regenerated (hardened-config-dir doc wording). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,16 +13,7 @@ pub(crate) fn sanitize_device_name(name: &str, fp_hex: &str) -> String {
|
||||
let cleaned: String = name
|
||||
.chars()
|
||||
.map(|c| if c == '\t' || c == '\n' { ' ' } else { c })
|
||||
.filter(|&c| {
|
||||
!c.is_control()
|
||||
// Bidi/format controls that could spoof or reorder the displayed name.
|
||||
&& !('\u{202A}'..='\u{202E}').contains(&c) // LRE..RLO/PDF
|
||||
&& !('\u{2066}'..='\u{2069}').contains(&c) // LRI..PDI
|
||||
&& c != '\u{200E}' // LRM
|
||||
&& c != '\u{200F}' // RLM
|
||||
&& c != '\u{061C}' // ALM
|
||||
&& c != '\u{FEFF}' // BOM / zero-width no-break space
|
||||
})
|
||||
.filter(|&c| !c.is_control() && !is_spoofy_char(c))
|
||||
.collect();
|
||||
// Collapse internal whitespace runs, trim, cap at the wire limit.
|
||||
let collapsed = cleaned.split_whitespace().collect::<Vec<_>>().join(" ");
|
||||
@@ -42,6 +33,19 @@ pub(crate) fn sanitize_device_name(name: &str, fp_hex: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// A Unicode bidi/format control that could spoof or reorder a displayed name (an `RLO` making a
|
||||
/// hostile device read like a trusted one). The canonical set — shared by every place that scrubs an
|
||||
/// untrusted client name before display/storage (device names here, the stream marker) so the set
|
||||
/// can't drift. Does NOT include C0/C1 controls; callers combine this with `char::is_control`.
|
||||
pub(crate) fn is_spoofy_char(c: char) -> bool {
|
||||
('\u{202A}'..='\u{202E}').contains(&c) // LRE..RLO/PDF
|
||||
|| ('\u{2066}'..='\u{2069}').contains(&c) // LRI..PDI
|
||||
|| c == '\u{200E}' // LRM
|
||||
|| c == '\u{200F}' // RLM
|
||||
|| c == '\u{061C}' // ALM
|
||||
|| c == '\u{FEFF}' // BOM / zero-width no-break space
|
||||
}
|
||||
|
||||
/// Max stored device-name length (matches the `Hello` wire cap, `quic::HELLO_NAME_MAX`).
|
||||
const NAME_MAX: usize = 64;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user