mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(desktop): read authenticode sigs with a working pwsh host (#1549)
This commit is contained in:
1 parent
6e66c92dca
commit
85c6a28dce
1 file changed
+80
-9
+80
-9
@@ -1912,6 +1912,29 @@ fn relative_display(root: &Path, path: &Path) -> String {
|
|||||||
.replace('\\', "/")
|
.replace('\\', "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn percent_decode_archive_name(name: &str) -> String {
|
||||||
|
if !name.contains('%') {
|
||||||
|
return name.to_string();
|
||||||
|
}
|
||||||
|
let bytes = name.as_bytes();
|
||||||
|
let mut decoded = Vec::with_capacity(bytes.len());
|
||||||
|
let mut index = 0;
|
||||||
|
while index < bytes.len() {
|
||||||
|
if bytes[index] == b'%' && index + 2 < bytes.len() {
|
||||||
|
let high = (bytes[index + 1] as char).to_digit(16);
|
||||||
|
let low = (bytes[index + 2] as char).to_digit(16);
|
||||||
|
if let (Some(high), Some(low)) = (high, low) {
|
||||||
|
decoded.push((high * 16 + low) as u8);
|
||||||
|
index += 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decoded.push(bytes[index]);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
String::from_utf8(decoded).unwrap_or_else(|_| name.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
fn assert_expected_windows_pe_inventory(
|
fn assert_expected_windows_pe_inventory(
|
||||||
root: &Path,
|
root: &Path,
|
||||||
files: &[PathBuf],
|
files: &[PathBuf],
|
||||||
@@ -1921,7 +1944,7 @@ fn assert_expected_windows_pe_inventory(
|
|||||||
let present = files
|
let present = files
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|path| path.file_name().and_then(OsStr::to_str))
|
.filter_map(|path| path.file_name().and_then(OsStr::to_str))
|
||||||
.map(ToOwned::to_owned)
|
.map(percent_decode_archive_name)
|
||||||
.collect::<BTreeSet<_>>();
|
.collect::<BTreeSet<_>>();
|
||||||
let expected = expected_windows_pe_inventory(arch, main_exe);
|
let expected = expected_windows_pe_inventory(arch, main_exe);
|
||||||
let missing = expected
|
let missing = expected
|
||||||
@@ -2011,14 +2034,18 @@ fn authenticode_report(files: &[PathBuf]) -> Result<Vec<SignatureRow>> {
|
|||||||
fs::write(&script_path, authenticode_report_script(&list_path))
|
fs::write(&script_path, authenticode_report_script(&list_path))
|
||||||
.with_context(|| format!("Failed to write {}", script_path.display()))?;
|
.with_context(|| format!("Failed to write {}", script_path.display()))?;
|
||||||
|
|
||||||
let output = capture(CommandSpec::new("powershell").args([
|
let output = capture(
|
||||||
"-NoProfile",
|
CommandSpec::new(powershell_host())
|
||||||
"-NonInteractive",
|
.args([
|
||||||
"-ExecutionPolicy",
|
"-NoProfile",
|
||||||
"Bypass",
|
"-NonInteractive",
|
||||||
"-File",
|
"-ExecutionPolicy",
|
||||||
script_path.to_string_lossy().as_ref(),
|
"Bypass",
|
||||||
]))?;
|
"-File",
|
||||||
|
script_path.to_string_lossy().as_ref(),
|
||||||
|
])
|
||||||
|
.env_remove("PSModulePath"),
|
||||||
|
)?;
|
||||||
ensure!(
|
ensure!(
|
||||||
output.status == 0,
|
output.status == 0,
|
||||||
"Get-AuthenticodeSignature failed with exit code {}",
|
"Get-AuthenticodeSignature failed with exit code {}",
|
||||||
@@ -2029,6 +2056,20 @@ fn authenticode_report(files: &[PathBuf]) -> Result<Vec<SignatureRow>> {
|
|||||||
parse_authenticode_report(stdout.trim())
|
parse_authenticode_report(stdout.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn powershell_host() -> &'static str {
|
||||||
|
if which_in_path("pwsh.exe").is_some() || which_in_path("pwsh").is_some() {
|
||||||
|
return "pwsh";
|
||||||
|
}
|
||||||
|
"powershell"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn which_in_path(program: &str) -> Option<PathBuf> {
|
||||||
|
let path = env::var_os("PATH")?;
|
||||||
|
env::split_paths(&path)
|
||||||
|
.map(|directory| directory.join(program))
|
||||||
|
.find(|candidate| candidate.is_file())
|
||||||
|
}
|
||||||
|
|
||||||
fn authenticode_report_script(list_path: &Path) -> String {
|
fn authenticode_report_script(list_path: &Path) -> String {
|
||||||
format!(
|
format!(
|
||||||
"$ErrorActionPreference = 'Stop'\n\
|
"$ErrorActionPreference = 'Stop'\n\
|
||||||
@@ -3772,6 +3813,36 @@ export const CHANNEL_DISPLAY_NAME = BUILD_CHANNEL;\n"
|
|||||||
assert!(zip.by_name("resources/app.asar").is_ok());
|
assert!(zip.by_name("resources/app.asar").is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn percent_encoded_archive_names_match_their_decoded_inventory_entry() {
|
||||||
|
assert_eq!(
|
||||||
|
percent_decode_archive_name("Fluxer%20Canary.exe"),
|
||||||
|
"Fluxer Canary.exe"
|
||||||
|
);
|
||||||
|
assert_eq!(percent_decode_archive_name("Fluxer.exe"), "Fluxer.exe");
|
||||||
|
assert_eq!(
|
||||||
|
percent_decode_archive_name("win-game-capture.win32-arm64-msvc.node"),
|
||||||
|
"win-game-capture.win32-arm64-msvc.node"
|
||||||
|
);
|
||||||
|
assert_eq!(percent_decode_archive_name("100%.dll"), "100%.dll");
|
||||||
|
assert_eq!(percent_decode_archive_name("a%zz.dll"), "a%zz.dll");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canary_nupkg_inventory_accepts_percent_encoded_main_executable() {
|
||||||
|
let root = Path::new("lib").join("app");
|
||||||
|
let files = expected_windows_pe_inventory("arm64", "Fluxer Canary.exe")
|
||||||
|
.into_iter()
|
||||||
|
.map(|name| {
|
||||||
|
if name == "Fluxer Canary.exe" {
|
||||||
|
return root.join("Fluxer%20Canary.exe");
|
||||||
|
}
|
||||||
|
root.join(name)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert_expected_windows_pe_inventory(&root, &files, "arm64", "Fluxer Canary.exe").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn known_optional_windows_pe_inventory_never_repeats_a_required_binary() {
|
fn known_optional_windows_pe_inventory_never_repeats_a_required_binary() {
|
||||||
for arch in ["x64", "arm64"] {
|
for arch in ["x64", "arm64"] {
|
||||||
|
|||||||
Reference in new issue
Block a user