fix(install): skip and reject links when extracting .eti archives
Security audit findings EXP2-SEC-01, SEC-IPC-04 and Codex #3 (symlink redirection through externally extracted archives). The ordinary install path hands every root `.eti` archive to an external `unrar x` process and promotes the resulting staging directory to `local/` as soon as the extractor exits 0. Nothing inspected what unrar materialised. A symbolic link (or, on Windows, a junction) inside an archive would survive promotion and later redirect the launch-time settings rewrite in `apply_launch_settings_once`, the uninstall path, or any script the game ships. The archives themselves are BLAKE3 verified against the bundled catalog before they can be installed, so a hostile link would have to be published by the catalog operator; this is defense in depth rather than a live remote exploit. Two independent layers now guard promotion: - Both `unrar` invocations (Tauri sidecar and peer-cli external unpacker) pass `-ol-`, which makes unrar 7.x skip symbolic-link entries entirely. The bundled 7.10 sidecar was checked against a fixture archive. - `install_inner`/`update_inner` walk the staging tree without following links and refuse to promote it if any entry is a symlink or (on Windows) carries the reparse-point attribute. The normal rollback then removes staging and clears the install intent. The audit's `-sl-` suggestion does not exist in unrar; `-sl<size>` is a size filter. Post-extraction digest verification of every extracted file remains out of scope for the ordinary path; Stream Install already verifies each output entry. Test plan: `just test` (new unix test installs with a fake unpacker that plants a symlink and asserts install fails, `local/` is absent and the intent is cleared; the peer-cli controlled-unrar test checks the new argument position). Manual: install a fixture game via peer-cli. Claude-Session: https://claude.ai/code/session_017C3Nbgwpdm3YNwZhhFLHwg
This commit is contained in:
3 files changed
+99
-5
No files matched your search
@@ -347,6 +347,9 @@ impl Unpacker for ExternalUnrarUnpacker {
|
||||
std::ffi::OsString::from("x"),
|
||||
std::ffi::OsString::from("-o+"),
|
||||
std::ffi::OsString::from("-p-"),
|
||||
// Skip symbolic links inside the archive; the install
|
||||
// transaction also audits the extracted tree for links.
|
||||
std::ffi::OsString::from("-ol-"),
|
||||
archive.as_os_str().to_owned(),
|
||||
dest.as_os_str().to_owned(),
|
||||
],
|
||||
@@ -649,7 +652,8 @@ mod tests {
|
||||
r#"#!/bin/sh
|
||||
set -eu
|
||||
[ "$3" = "-p-" ]
|
||||
dest=$5
|
||||
[ "$4" = "-ol-" ]
|
||||
dest=$6
|
||||
printf '%s' "$$" > "$dest/child.pid"
|
||||
printf 'started' > "$dest/started"
|
||||
while [ ! -e "$dest/release" ]; do :; done
|
||||
|
||||
Reference in new issue
Block a user