From 8c8cb49c237eec2f82192dd831ec7308905f2c0a Mon Sep 17 00:00:00 2001 From: JJ Liebig Date: Mon, 28 Sep 2026 13:16:10 +0200 Subject: fix(windows): read agent command lines without PROCESS_VM_READ (#4708) * fix(windows): read agent command lines without PROCESS_VM_READ Windows process command lines were read by opening each process with PROCESS_VM_READ and walking its PEB. Hardened runtimes (Electron/Node) and security products deny that access, so those processes resolved to a bare `node.exe`/`bun.exe` name with no argv, argv0, or cmdline. A runtime-wrapped agent launched from such a process was then indistinguishable from an unrelated runtime process, and the pane stayed on the shell with `agent_status: unknown`. Prefer `NtQueryInformationProcess` with `ProcessCommandLineInformation` (Windows 8.1+), which needs only `PROCESS_QUERY_LIMITED_INFORMATION`, and keep the existing PEB read as a fallback for processes that do not expose a stored command line. The fallback still requires `PROCESS_VM_READ`, so this only widens coverage. refs #4579 * fix(windows): handle growing command lines before failure test The retry added for a command line that grows between the size probe and the read was unreachable. NTSTATUS is signed, so STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, and STATUS_INFO_LENGTH_MISMATCH are all negative and were treated as failures by the earlier `status < 0` check. Check the growth statuses first and retry once with the reported size. Also read the returned UNICODE_STRING header unaligned, since a Vec buffer only guarantees byte alignment. * test(windows): keep command-line marker test process alive Passing the marker as a second argument to `ping` let `ping` exit as soon as it saw one target, so the command-line query could race the process exit. Move the marker into a `rem` after `&` so it stays in cmd.exe's own command line and `ping -n 11` keeps the process alive for the read. --- src/detect/mod.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/detect') diff --git a/src/detect/mod.rs b/src/detect/mod.rs index 146c3089..37d5481a 100644 --- a/src/detect/mod.rs +++ b/src/detect/mod.rs @@ -1497,6 +1497,9 @@ mod tests { #[test] fn identify_agent_in_job_detects_node_wrapped_pi_bundled_cli() { + // Hardened runtimes deny `PROCESS_VM_READ`, so the command line can come + // from a LimitedInformation query with the launcher path intact. The + // detection path must not depend on how that command line was obtained. let job = crate::platform::ForegroundJob { process_group_id: 123, processes: vec![foreground_process( -- cgit v1.2.3