From 53568568bf32b36274ba3a532f1e2cfb2daed271 Mon Sep 17 00:00:00 2001 From: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Date: Fri, 9 Oct 2026 04:13:40 +0000 Subject: [PATCH] fix(ghidra): reject relative installation and Java paths in doctor and setup A relative GHIDRA_INSTALL_DIR (or JAVA_HOME) that resolved from the current directory passed doctor's Ghidra inspection, so doctor reported a healthy provider and setup wrote the relative path into the MCP registration, but REA's configuration parser rejects relative paths, so `providers`, `capabilities` and the registered MCP server all failed. The installation inspection's configuration check now applies the same absolute-path rule, judged by the inspected platform's path syntax, and names the relative setting. Doctor reports the provider unavailable with that reason, and setup, which registers only an available inspection's environment, no longer writes the path. Fixes #1183 Co-Authored-By: Claude Opus 5.5 --- src/ghidra/GhidraInstallation.test.ts | 45 +++++++++++++++++++++++++++ src/ghidra/GhidraInstallation.ts | 42 ++++++++++++++++++++----- 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/src/ghidra/GhidraInstallation.test.ts b/src/ghidra/GhidraInstallation.test.ts index 18f45e26be..3c6d46354c 100644 --- a/src/ghidra/GhidraInstallation.test.ts +++ b/src/ghidra/GhidraInstallation.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; +import { projectGhidraDoctorInspection } from "./GhidraDoctor.js"; import { ghidraJavaEnvironment, inspectGhidraInstallation, @@ -378,3 +379,47 @@ describe("Ghidra release line and JDK bounds", () => { ).toMatchObject({ status: "available", javaVersion: "17.0.20.1" }); }); }); + +describe("Ghidra configuration paths", () => { + it.each([ + [{ installDir: "./ghidra" }, "GHIDRA_INSTALL_DIR must be absolute"], + [{ installDir: INSTALL, javaHome: "./jdk" }, "JAVA_HOME must be absolute"], + [ + { installDir: "ghidra", javaHome: "jdk" }, + "GHIDRA_INSTALL_DIR and JAVA_HOME must be absolute", + ], + ])( + "rejects relative paths that REA's configuration parser rejects (%o)", + (paths, detail) => { + const result = inspectGhidraInstallation( + { ...paths, platform: "linux", architecture: "x64" }, + host(), + ); + expect(result).toMatchObject({ + status: "unavailable", + rejection: { + code: "not_configured", + reason: expect.stringContaining("reject relative paths"), + }, + }); + expect(result.checks).toContainEqual( + expect.objectContaining({ name: "configuration", detail }), + ); + // Setup registers only an available inspection's environment. + expect(projectGhidraDoctorInspection(result)).toMatchObject({ + available: false, + registrationEnvironment: {}, + }); + }, + ); + + it("judges absolute paths by the inspected platform", () => { + const result = inspectGhidraInstallation( + { installDir: "C:\\ghidra", platform: "win32", architecture: "x64" }, + host(), + ); + expect(result.checks).toContainEqual( + expect.objectContaining({ name: "configuration", status: "passed" }), + ); + }); +}); diff --git a/src/ghidra/GhidraInstallation.ts b/src/ghidra/GhidraInstallation.ts index a521fc171b..d0906fb527 100644 --- a/src/ghidra/GhidraInstallation.ts +++ b/src/ghidra/GhidraInstallation.ts @@ -306,13 +306,7 @@ const installationChecks = ({ javaHome, host, }: GhidraInstallationCheckContext): readonly GhidraInstallationCheck[] => [ - installationCheck({ - name: "configuration", - passed: coordinates.installDir !== null, - code: "not_configured", - detail: coordinates.installDir ?? "GHIDRA_INSTALL_DIR is not set", - remediation: `Set GHIDRA_INSTALL_DIR to an extracted Ghidra ${ghidraReleaseLine()} release directory.`, - }), + configurationCheck(coordinates.installDir, javaHome, platform), installationCheck({ name: "platform", passed: NATIVE_PLATFORMS[platform] !== undefined, @@ -366,6 +360,40 @@ const installationChecks = ({ ), ]; +/** + * REA's configuration parser, and so the MCP server and analysis, accept only + * absolute paths; a relative one that happens to resolve here must not pass. + */ +const configurationCheck = ( + installDir: string | null, + javaHome: string | undefined, + platform: NodeJS.Platform, +): GhidraInstallationCheck => { + const { isAbsolute } = platform === "win32" ? win32 : posix; + const relative = [ + ...(installDir !== null && !isAbsolute(installDir) + ? ["GHIDRA_INSTALL_DIR"] + : []), + ...(javaHome !== undefined && !isAbsolute(javaHome) ? ["JAVA_HOME"] : []), + ]; + if (installDir === null || relative.length === 0) + return installationCheck({ + name: "configuration", + passed: installDir !== null, + code: "not_configured", + detail: installDir ?? "GHIDRA_INSTALL_DIR is not set", + remediation: `Set GHIDRA_INSTALL_DIR to an extracted Ghidra ${ghidraReleaseLine()} release directory.`, + }); + const settings = relative.join(" and "); + return installationCheck({ + name: "configuration", + passed: false, + code: "not_configured", + detail: `${settings} must be absolute`, + remediation: `Set ${settings} to ${relative.length === 1 ? "an absolute path" : "absolute paths"}. REA's analysis and MCP server reject relative paths even when they resolve from the current directory.`, + }); +}; + /** Project an installation probe into caller-visible, secret-free diagnostics. */ export const ghidraInstallationDiagnostics = ( inspection: GhidraInstallationInspection,