fix(portable): validate executable path boundaries

Reject executables outside the source folder and
reuse the package path normalization logic during
stale file cleanup.

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2026-09-02 15:36:26 +08:00
parent 7192f8230e
commit 09a75482ae
4 changed files with 13 additions and 12 deletions

View File

@@ -0,0 +1 @@
timestamp = 1787231066609

View File

@@ -133,11 +133,16 @@ if __name__ == '__main__':
# So we need to check if the executable is in the folder, and if so, concat again.
if os.path.exists(os.path.join(folder, options.executable)):
options.executable = os.path.join(folder, options.executable)
folder_path = os.path.abspath(folder)
exe: str = os.path.abspath(options.executable)
if not exe.startswith(os.path.abspath(folder)):
try:
in_source_folder = os.path.commonpath([folder_path, exe]) == folder_path
except ValueError:
in_source_folder = False
if not in_source_folder:
print("The executable must locate in source folder")
exit(-1)
exe = '.' + exe[len(os.path.abspath(folder)):]
exe = '.' + exe[len(folder_path):]
print("Executable path: " + exe)
print("Compression level: " + str(options.level))
md5_table = generate_md5_table(

View File

@@ -90,7 +90,7 @@ fn merge(
(files, exe)
}
fn normalize_path(path: &str) -> String {
pub(crate) fn normalize_path(path: &str) -> String {
path.replace('\\', "/")
.trim_start_matches("./")
.to_lowercase()

View File

@@ -5,7 +5,7 @@ use std::{
process::{Command, Stdio},
};
use bin_reader::BinaryReader;
use bin_reader::{normalize_path, BinaryReader};
pub mod bin_reader;
#[cfg(windows)]
@@ -84,12 +84,6 @@ fn previous_package_files(dir: &Path) -> Vec<String> {
.collect()
}
fn normalized(path: &str) -> String {
path.replace('\\', "/")
.trim_start_matches("./")
.to_lowercase()
}
// meta.toml is plain text in a user-writable directory, and it now drives deletion,
// so the path is rebuilt from plain components rather than joined as written. A
// prefix, root or parent component would otherwise escape the extraction directory:
@@ -133,10 +127,11 @@ fn remove_dropped_package_files_with<F>(
where
F: FnMut(&Path) -> std::io::Result<()>,
{
let keep: std::collections::HashSet<String> = current.iter().map(|p| normalized(p)).collect();
let keep: std::collections::HashSet<String> =
current.iter().map(|p| normalize_path(p)).collect();
let mut failed = Vec::new();
for previous in previous_package_files(dir) {
if keep.contains(&normalized(&previous)) {
if keep.contains(&normalize_path(&previous)) {
continue;
}
let Some(path) = resolve_within(dir, &previous) else {