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 { if needs_os_info {
let mut osmap = serde_json::Map::new(); let mut osmap = serde_json::Map::new();
if let Some(name) = System::name() { osmap.insert("ProductName".into(), json!(name)); } if let Some(name) = System::name() {
if let Some(ver) = System::os_version() { osmap.insert("Version".into(), json!(ver)); } osmap.insert("ProductName".into(), json!(name));
if let Some(build) = System::kernel_version() { osmap.insert("BuildNumber".into(), json!(build)); } }
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)); 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 // Normalização de software/serviços no topo do inventário
if let Some(obj) = inventory.as_object_mut() { if let Some(obj) = inventory.as_object_mut() {
let extended_snapshot = obj let extended_snapshot = obj.get("extended").and_then(|v| v.as_object()).cloned();
.get("extended")
.and_then(|v| v.as_object())
.cloned();
// Merge software // Merge software
let mut software: Vec<serde_json::Value> = Vec::new(); let mut software: Vec<serde_json::Value> = Vec::new();
if let Some(existing) = obj.get("software").and_then(|v| v.as_array()) { 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::os::windows::process::CommandExt;
use std::process::Command; use std::process::Command;
const CREATE_NO_WINDOW: u32 = 0x08000000; 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> { fn ps(cmd: &str) -> Option<serde_json::Value> {
let ps_cmd = format!( let ps_cmd = format!(
"$ErrorActionPreference='SilentlyContinue'; {} | ConvertTo-Json -Depth 4 -Compress", "$ErrorActionPreference='SilentlyContinue'; {} | ConvertTo-Json -Depth 4 -Compress",
@ -694,7 +719,7 @@ fn collect_windows_extended() -> serde_json::Value {
if out.stdout.is_empty() { if out.stdout.is_empty() {
return None; 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"#) 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 os_info_obj = expect_object(os_info, "windows.osInfo");
let is_activated = os_info_obj.get("IsActivated").unwrap_or_else(|| { let is_activated = os_info_obj.get("IsActivated").unwrap_or_else(|| {
panic!( panic!("campo IsActivated ausente em windows.osInfo: {os_info_obj:?}")
"campo IsActivated ausente em windows.osInfo: {os_info_obj:?}"
)
}); });
assert!( assert!(
is_activated.as_bool().is_some(), is_activated.as_bool().is_some(),
@ -1077,9 +1100,7 @@ mod windows_tests {
); );
let license_status = os_info_obj.get("LicenseStatus").unwrap_or_else(|| { let license_status = os_info_obj.get("LicenseStatus").unwrap_or_else(|| {
panic!( panic!("campo LicenseStatus ausente em windows.osInfo: {os_info_obj:?}")
"campo LicenseStatus ausente em windows.osInfo: {os_info_obj:?}"
)
}); });
assert!( assert!(
license_status.as_i64().is_some(), license_status.as_i64().is_some(),