fix(pcap): report a refused tool launch as an unavailable tool (#10553)

* fix(pcap): report a refused tool launch as an unavailable tool

When a capture read's tool launch was refused with EPERM, pcap reported it
as unreadable tool output (AnalysisProtocolError) instead of an unavailable
tool. Pcap also never told "not executable" apart from "missing".

Add classifyLaunchFailure in src/process as the one owner of which spawn
errnos mean missing (ENOENT, ENOTDIR) and which mean present but not
executable (EACCES, EPERM, ENOEXEC, EISDIR). Pcap, apktool and gdb used three
different errno lists; all three now use it.

* refactor(process): classify Java and adb launch refusals with the shared owner (#10598)

The Ghidra and JADX Java probes and adb's version probe now take
missing-versus-not-executable from classifyLaunchFailure instead of their
own errno checks. ELOOP and ENAMETOOLONG join the owner's missing set, so
adb keeps its existing classification and both Java probes now treat a
non-directory path component as a missing runtime.

* fix(pcap): preserve command launch attribution

* test(pcap): expect an unknown failing stage when a limited capture tool exits unsuccessfully

* fix(pcap): attribute a limited capture's exit through the prlimit wrapper

* fix(process): identify the refused executable for every launcher shape

Name the executable on errnos Node throws synchronously from spawn
(ENOEXEC, ENOTDIR, ...) and read the Windows native launcher's
executable-launch constraint, so pcap attributes those refusals to the
selected tool or limiter instead of an unknown stage.

* test(pcap): prove synchronous launch naming with ENOTDIR on every POSIX host
This commit is contained in:
morluto authored and GitHub committed 2026-11-14 21:11:59 -05:00
1 parent 47925f3bef
commit 44fedc8fdd
13 files changed
+622 -51

No files matched your search

+9 -8
View File
@@ -1,4 +1,5 @@
import type { ProcessInspectionFailure } from "../process/ProcessInspectionCleanup.js";
import { classifyLaunchFailureCode } from "../process/launchFailure.js";
import { randomUUID } from "node:crypto";
import type { RetainedResourceScope } from "../application/AnalysisProvider.js";
import {
@@ -284,21 +285,21 @@ const commandProbeRefusal = (
command?.processError ?? (cause instanceof Error ? cause : undefined);
const systemCode =
processError === undefined ? undefined : Reflect.get(processError, "code");
const missing = classifyLaunchFailureCode(systemCode)?.kind === "missing";
const limited = command?.reason === "output-limit";
const timedOut = command?.reason === "timeout";
return {
code:
systemCode === "ENOENT"
? "runtime_missing"
: limited || timedOut
? "version_unresolved"
: "runtime_unavailable",
kind: systemCode === "ENOENT" ? "java_runtime" : "java_probe",
code: missing
? "runtime_missing"
: limited || timedOut
? "version_unresolved"
: "runtime_unavailable",
kind: missing ? "java_runtime" : "java_probe",
message: limited
? `The selected Java launcher ${java} exceeded the ${String(JAVA_MODULE_LISTING_MAX_OUTPUT_BYTES)}-byte module-listing output budget. This release has no runtime override. REA retained partial output but could not establish compiler or version availability. Inspect the selected launcher for verbose or unbounded output; java --list-modules should return a concise module listing. REA_JADX_OPERATION_TIMEOUT_MS does not raise this probe.`
: timedOut
? `The selected Java launcher ${java} did not finish the module-listing probe within ${String(timeoutMs)} ms. This release has no runtime override. Inspect its startup responsiveness and verify it with java --list-modules. REA_JADX_OPERATION_TIMEOUT_MS does not raise this probe.`
: `Cannot inspect selected Java runtime ${java}: ${systemCode === "ENOENT" ? "spawn java ENOENT" : cause instanceof Error ? cause.message : cause === undefined ? `exit ${String(output?.exit_code ?? "unknown")}${output?.signal == null ? "" : ` (${output.signal})`}` : String(cause)}. Review the selected launcher and retained diagnostics.`,
: `Cannot inspect selected Java runtime ${java}: ${missing ? `spawn java ${String(systemCode)}` : cause instanceof Error ? cause.message : cause === undefined ? `exit ${String(output?.exit_code ?? "unknown")}${output?.signal == null ? "" : ` (${output.signal})`}` : String(cause)}. Review the selected launcher and retained diagnostics.`,
diagnostics: {
java_executable: java,
phase: "module-inspection",