From 39aca922398812bc1733296c513901e44afc4be1 Mon Sep 17 00:00:00 2001 From: Hasan Zayni <36798269+dark13b@users.noreply.github.com> Date: Sat, 24 Oct 2026 06:37:58 +0300 Subject: [PATCH] fix(windows): explain when Windows refuses to load the verified native addon (#6832) When Smart App Control or an App Control policy refuses rea-windows-x64.node, the loader reported the failure as the artifact stage. Doctor and provider diagnostics then told the user to restore or rebuild the native files, although their version and SHA-256 had already matched the package manifest. The loader now tags a failure of the module load itself, after the manifest and digest checks pass, as the load stage. The reason keeps the Windows error text and says that restoring the file cannot help, that Ghidra and JADX need the native controls, that rea setup can still register clients, and that REA must run where policy allows the file or the maintainers can be asked about a signed build. Readiness keeps the documented runtime_missing code for unloadable artifacts. Setup does not depend on the native controls: with the addon unavailable, the published 6.7.0 CLI still planned and applied the claude_code registration, so no setup gate is changed. Fixes #5723 --- docs/user-visible-errors.csv | 1 + docs/windows-ghidra-p0.md | 7 ++++-- src/process/WindowsAuthority.ts | 15 ++++++++----- src/windows/WindowsNativeLoader.test.ts | 29 +++++++++++++++++++++++++ src/windows/WindowsNativeLoader.ts | 23 ++++++++++++++++++-- 5 files changed, 65 insertions(+), 10 deletions(-) diff --git a/docs/user-visible-errors.csv b/docs/user-visible-errors.csv index c69819462f..ab9bb79967 100644 --- a/docs/user-visible-errors.csv +++ b/docs/user-visible-errors.csv @@ -119,3 +119,4 @@ Then rerun this installer.",implemented,verified by shell branch inspection 90.7,src/ghidra/GhidraProviderClient.ts:projectPrimarySessionError,One procedure's decompile reaches REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS,"Ghidra decompilation of {entry} exceeded the {N}-second limit set by REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS. Only this procedure's decompilation was stopped; the Ghidra session remains usable.",Low risk; names the setting and keeps the session,Keep current copy,implemented,provider projection test and real-provider lane 91,src/application/binaryDiagnostics/ByteEntropyService.ts,Invalid file or window,"Expected a regular file.; Selected file could not be found.; Selected byte window starts beyond the end of the file.; Selected byte window extends beyond the end of the file.; File size exceeds exact numeric offset representation.",Invalid selection cannot produce complete entropy Evidence,Choose a readable regular file and a window within its byte size,implemented,entropy service and MCP boundary tests 92,src/domain/analysisView/byteEntropyView.ts,Non-summary entropy view,Byte entropy Evidence supports the summary view; retrieve parent Evidence for the complete block series.,Unsupported view selection,Use the summary view or retrieve complete parent Evidence,implemented,typed input rejection +93,src/windows/WindowsNativeLoader.ts:load refused,Windows refuses to load the digest-verified native addon (for example Smart App Control or App Control policy),"REA Windows native controls are unavailable: Windows refused to load the verified native/windows/build/rea-windows-x64.node: {OS error} The file matched the package manifest, so restoring or reinstalling it will not help. ...",Previous artifact copy told users to restore or rebuild files that were already intact,"Name the OS refusal, say restoring cannot help, state that Ghidra and JADX need native controls while setup can still register clients, and point to a policy-allowed host or a maintainer-signed build",implemented,simulated loader refusal unit test; real Smart App Control block not reproduced diff --git a/docs/windows-ghidra-p0.md b/docs/windows-ghidra-p0.md index d5d8881aea..c98103633d 100644 --- a/docs/windows-ghidra-p0.md +++ b/docs/windows-ghidra-p0.md @@ -80,8 +80,11 @@ Windows x64 package; changing MCP registration cannot supply that artifact. A filesystem or access failure identifies the failed native constraint. An incompatible host uses `unsupported_host`. Missing, mismatched or unloadable native artifacts use `runtime_missing` and doctor classification -`missing_dependency`. A failed native invocation or invalid inspection uses -`runtime_unavailable` and doctor classification `runtime_failure`; it does not +`missing_dependency`. A verified artifact that Windows refuses to load, for +example under Smart App Control or an App Control policy, reports failure stage +`load`; restoring or reinstalling the file cannot fix it. A failed native +invocation or invalid inspection uses `runtime_unavailable` and doctor +classification `runtime_failure`; it does not mean Windows x64 itself is unsupported. Provider diagnostics retain the failure stage under `windows_native_failure`, together with the corresponding remediation. Self-test failures preserve the native operation and OS error text. Check the diff --git a/src/process/WindowsAuthority.ts b/src/process/WindowsAuthority.ts index 3c83fe6885..bef4f0c544 100644 --- a/src/process/WindowsAuthority.ts +++ b/src/process/WindowsAuthority.ts @@ -1,5 +1,6 @@ import { systemWindowsNativeAuthority, + WINDOWS_NATIVE_LOAD_REFUSED_REMEDIATION, type WindowsNativeLoadResult, } from "../windows/WindowsNativeLoader.js"; @@ -82,17 +83,19 @@ export const inspectWindowsNativeReadiness = ( code: failureStage === "host" ? "unsupported_host" - : failureStage === "artifact" + : failureStage === "artifact" || failureStage === "load" ? "runtime_missing" : "runtime_unavailable", remediation: failureStage === "host" ? "Use a Windows x64 host and a supported Node.js runtime with Node-API 8 or newer." - : failureStage === "artifact" - ? "Use the matching Windows x64 REA package, or restore native/windows/build/manifest.json and native/windows/build/rea-windows-x64.node from the same-version rea-agents tarball or build them with native/windows/README.md. The loader checks version, ABI, architecture, and digest before granting authority; no download or install is performed automatically." - : failureStage === "inspection" - ? "Install the matching Windows x64 REA package; its native inspection must prove every required security control." - : "Inspect the preserved native self-test error. Check that the process's temporary directory is writable local NTFS and that its filesystem, DACL, Job Object, and cleanup operations are permitted. Resolve the reported constraint, then rerun rea doctor; reinstalling Ghidra or Java cannot fix this self-test.", + : failureStage === "load" + ? WINDOWS_NATIVE_LOAD_REFUSED_REMEDIATION + : failureStage === "artifact" + ? "Use the matching Windows x64 REA package, or restore native/windows/build/manifest.json and native/windows/build/rea-windows-x64.node from the same-version rea-agents tarball or build them with native/windows/README.md. The loader checks version, ABI, architecture, and digest before granting authority; no download or install is performed automatically." + : failureStage === "inspection" + ? "Install the matching Windows x64 REA package; its native inspection must prove every required security control." + : "Inspect the preserved native self-test error. Check that the process's temporary directory is writable local NTFS and that its filesystem, DACL, Job Object, and cleanup operations are permitted. Resolve the reported constraint, then rerun rea doctor; reinstalling Ghidra or Java cannot fix this self-test.", }; }; diff --git a/src/windows/WindowsNativeLoader.test.ts b/src/windows/WindowsNativeLoader.test.ts index eada66fa8b..74db2eac37 100644 --- a/src/windows/WindowsNativeLoader.test.ts +++ b/src/windows/WindowsNativeLoader.test.ts @@ -2,6 +2,7 @@ import { createHash } from "node:crypto"; import { describe, expect, it } from "vitest"; +import { inspectWindowsNativeReadiness } from "../process/WindowsAuthority.js"; import { loadWindowsNativeAuthority, type WindowsNativeLoadHost, @@ -224,6 +225,34 @@ describe("packaged Windows native artifact validation", () => { }); }); + it("explains an OS policy refusal of the verified artifact without file-restore advice", () => { + const value = fixture(); + const loaded = loadWindowsNativeAuthority({ + ...value.host, + loadArtifact: () => { + throw new Error( + "An Application Control policy has blocked this file.\r\n\\\\?\\C:\\rea\\rea-windows-x64.node", + ); + }, + }); + expect(loaded).toMatchObject({ + available: false, + failureStage: "load", + reason: expect.stringContaining( + "An Application Control policy has blocked this file.", + ), + }); + expect(loaded).not.toHaveProperty("retryable"); + const readiness = inspectWindowsNativeReadiness("win32", loaded); + expect(readiness).toMatchObject({ + code: "runtime_missing", + remediation: expect.stringContaining("Smart App Control"), + }); + expect(readiness).not.toMatchObject({ + remediation: expect.stringContaining("restore native/windows/build"), + }); + }); + it("requires every inspected native fact", () => { const value = fixture(); const loaded = loadWindowsNativeAuthority({ diff --git a/src/windows/WindowsNativeLoader.ts b/src/windows/WindowsNativeLoader.ts index adf59fbd27..f213f85039 100644 --- a/src/windows/WindowsNativeLoader.ts +++ b/src/windows/WindowsNativeLoader.ts @@ -71,7 +71,12 @@ export type WindowsNativeLoadResult = | { readonly available: false; readonly reason: string; - readonly failureStage: "host" | "artifact" | "self-test" | "inspection"; + readonly failureStage: + | "host" + | "artifact" + | "load" + | "self-test" + | "inspection"; /** Only a failed native invocation can recover on a later query. */ readonly retryable?: true; }; @@ -90,6 +95,10 @@ export interface WindowsNativeLoadHost { const missingWindowsNativeFileReason = (file: string): string => `Missing packaged Windows native file native/windows/build/${file} (ENOENT). Restore both native/windows/build/manifest.json and native/windows/build/rea-windows-x64.node from the rea-agents@${PACKAGE_METADATA.version} tarball or build them with native/windows/README.md; no native artifact was loaded.`; +/** Recovery when Windows refuses to load the verified artifact, for example by code-integrity policy. */ +export const WINDOWS_NATIVE_LOAD_REFUSED_REMEDIATION = + "The file matched the package manifest, so restoring or reinstalling it will not help. If Windows reports that an Application Control policy blocked the file, Smart App Control or an App Control for Business policy refused it. Windows native controls, which Ghidra and JADX need on Windows, stay unavailable; rea setup can still register clients. Run REA on a host whose policy allows this file, or ask the REA maintainers about a signed build."; + const readPackagedWindowsNativeFile = (file: string, read: () => T): T => { try { return read(); @@ -139,7 +148,17 @@ export const loadWindowsNativeAuthority = ( "Windows native artifact SHA-256 does not match package metadata", ); assertWindowsX64Artifact(artifact); - const binding = bindingSchema.parse(host.loadArtifact()); + let loadedModule: unknown; + try { + loadedModule = host.loadArtifact(); + } catch (cause: unknown) { + return { + available: false, + failureStage: "load", + reason: `REA Windows native controls are unavailable: Windows refused to load the verified native/windows/build/rea-windows-x64.node: ${cause instanceof Error ? cause.message : String(cause)} ${WINDOWS_NATIVE_LOAD_REFUSED_REMEDIATION}`, + }; + } + const binding = bindingSchema.parse(loadedModule); let observed: unknown; try { observed = binding.call("inspect", []);