Fix Windows PowerShell UTF-16 parsing

This commit is contained in:
Esdras Renan 2025-10-20 21:43:11 -03:00
parent 04a0127c6b
commit 459bd53693

View file

@ -303,9 +303,15 @@ fn build_inventory_metadata(system: &System) -> serde_json::Value {
};
if needs_os_info {
let mut osmap = serde_json::Map::new();
if let Some(name) = System::name() { osmap.insert("ProductName".into(), json!(name)); }
if let Some(ver) = System::os_version() { osmap.insert("Version".into(), json!(ver)); }
if let Some(build) = System::kernel_version() { osmap.insert("BuildNumber".into(), json!(build)); }
if let Some(name) = System::name() {
osmap.insert("ProductName".into(), json!(name));
}
if let Some(ver) = System::os_version() {
osmap.insert("Version".into(), json!(ver));
}
if let Some(build) = System::kernel_version() {
osmap.insert("BuildNumber".into(), json!(build));
}
win.insert("osInfo".into(), serde_json::Value::Object(osmap));
}
}
@ -324,10 +330,7 @@ fn build_inventory_metadata(system: &System) -> serde_json::Value {
// Normalização de software/serviços no topo do inventário
if let Some(obj) = inventory.as_object_mut() {
let extended_snapshot = obj
.get("extended")
.and_then(|v| v.as_object())
.cloned();
let extended_snapshot = obj.get("extended").and_then(|v| v.as_object()).cloned();
// Merge software
let mut software: Vec<serde_json::Value> = Vec::new();
if let Some(existing) = obj.get("software").and_then(|v| v.as_array()) {
@ -676,6 +679,28 @@ fn collect_windows_extended() -> serde_json::Value {
use std::os::windows::process::CommandExt;
use std::process::Command;
const CREATE_NO_WINDOW: u32 = 0x08000000;
fn parse_powershell_json(bytes: &[u8]) -> Option<serde_json::Value> {
if bytes.is_empty() {
return None;
}
if bytes.starts_with(&[0xFF, 0xFE]) {
// PowerShell usa UTF-16LE por padrão no Windows; converter para UTF-8.
let data = &bytes[2..];
if data.len() % 2 != 0 {
return None;
}
let utf16: Vec<u16> = data
.chunks_exact(2)
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
.collect();
let text = String::from_utf16(&utf16).ok()?;
serde_json::from_str(&text).ok()
} else {
serde_json::from_slice(bytes).ok()
}
}
fn ps(cmd: &str) -> Option<serde_json::Value> {
let ps_cmd = format!(
"$ErrorActionPreference='SilentlyContinue'; {} | ConvertTo-Json -Depth 4 -Compress",
@ -694,7 +719,7 @@ fn collect_windows_extended() -> serde_json::Value {
if out.stdout.is_empty() {
return None;
}
serde_json::from_slice::<serde_json::Value>(&out.stdout).ok()
parse_powershell_json(&out.stdout)
}
let software = ps(r#"@(Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'; Get-ItemProperty 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*') | Where-Object { $_.DisplayName } | Select-Object DisplayName, DisplayVersion, Publisher"#)
@ -1067,9 +1092,7 @@ mod windows_tests {
let os_info_obj = expect_object(os_info, "windows.osInfo");
let is_activated = os_info_obj.get("IsActivated").unwrap_or_else(|| {
panic!(
"campo IsActivated ausente em windows.osInfo: {os_info_obj:?}"
)
panic!("campo IsActivated ausente em windows.osInfo: {os_info_obj:?}")
});
assert!(
is_activated.as_bool().is_some(),
@ -1077,9 +1100,7 @@ mod windows_tests {
);
let license_status = os_info_obj.get("LicenseStatus").unwrap_or_else(|| {
panic!(
"campo LicenseStatus ausente em windows.osInfo: {os_info_obj:?}"
)
panic!("campo LicenseStatus ausente em windows.osInfo: {os_info_obj:?}")
});
assert!(
license_status.as_i64().is_some(),