mirror of
https://github.com/morluto/rea.git
synced 2026-11-03 21:36:13 +00:00
fix(ghidra): report unusable existing-project selections as REA_GHIDRA_PROJECT_PATH configuration errors (#7922)
A selected .gpr marker that does not exist and a target that existing-project mode cannot serve both surfaced as execution_failure with no setting and a doctor action that cannot help. Both now return configuration_invalid naming REA_GHIDRA_PROJECT_PATH. The ENOENT diagnostic of the missing marker is kept. Fixes #7417 Fixes #7416
This commit is contained in:
1 parent
844bb46351
commit
32b3722d2f
4 files changed
+57
-10
No files matched your search
@@ -24,6 +24,7 @@ import {
|
||||
} from "../filesystem/RegularFile.js";
|
||||
import { ConfigurationError } from "../domain/configurationErrors.js";
|
||||
import { compareUnicodeCodePoints } from "../domain/unicodeCodePointOrder.js";
|
||||
import type { JsonValue } from "../domain/jsonValue.js";
|
||||
import type { GhidraProjectSelection } from "../config/ghidraExistingProject.js";
|
||||
import { ghidraProgramNameSchema } from "../config/ghidraExistingProject.js";
|
||||
|
||||
@@ -59,9 +60,11 @@ export type GhidraProjectCommitment = z.infer<
|
||||
const selectionFailure = (
|
||||
message: string,
|
||||
setting = "REA_GHIDRA_PROJECT_PATH",
|
||||
diagnostics?: Readonly<Record<string, JsonValue>>,
|
||||
): ConfigurationError =>
|
||||
new ConfigurationError(message, {
|
||||
settings: [{ setting, constraint: message }],
|
||||
...(diagnostics === undefined ? {} : { diagnostics }),
|
||||
});
|
||||
const entriesFor = (name: string) => [`${name}.gpr`, `${name}.rep`] as const;
|
||||
|
||||
@@ -176,7 +179,14 @@ export const resolveGhidraProject = async (
|
||||
// Admit the caller's exact marker before canonicalizing its parent.
|
||||
// Darwin realpath(file/..) can succeed despite native lookup rejecting
|
||||
// that traversal; hashing the canonical sibling would select a new input.
|
||||
if (!(await lstat(selection.path)).isFile())
|
||||
const marker = await lstat(selection.path).catch(absentProjectEntry);
|
||||
if (marker === undefined)
|
||||
throw selectionFailure(
|
||||
`Existing Ghidra project marker does not exist: ${selection.path}`,
|
||||
"REA_GHIDRA_PROJECT_PATH",
|
||||
{ system_code: "ENOENT", syscall: "lstat" },
|
||||
);
|
||||
if (!marker.isFile())
|
||||
throw selectionFailure(
|
||||
`Existing Ghidra project marker must be a regular file: ${selection.path}`,
|
||||
);
|
||||
|
||||
@@ -4620,6 +4620,26 @@ it("reports every Windows existing-project capability unavailable before open",
|
||||
}
|
||||
});
|
||||
|
||||
it("reports an unsupported existing-project target as an invalid REA_GHIDRA_PROJECT_PATH setting", async () => {
|
||||
const ghidra = provider(installationHost(), undefined, {
|
||||
REA_GHIDRA_PROJECT_PATH: join(process.cwd(), "selected.gpr"),
|
||||
});
|
||||
try {
|
||||
const resolved = await ghidra.resolveAnalysisProfile({
|
||||
...executableTarget("elf", "x86_64"),
|
||||
format: "dos-com",
|
||||
} as unknown as BinaryTarget);
|
||||
expect(resolved.ok).toBe(false);
|
||||
if (resolved.ok) return;
|
||||
expect(resolved.error).toMatchObject({
|
||||
_tag: "ConfigurationError",
|
||||
settings: [{ setting: "REA_GHIDRA_PROJECT_PATH" }],
|
||||
});
|
||||
} finally {
|
||||
await ghidra.closeOwnedResources();
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps saved project reports as shared historical Evidence without applying fresh overrides", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "rea-history-project-"));
|
||||
await mkdir(join(root, "history.rep"));
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
ghidraPinnedLanguage,
|
||||
resolveGhidraAnalysisProfile,
|
||||
} from "./GhidraAnalysisProfile.js";
|
||||
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
||||
import { ConfigurationError } from "../domain/configurationErrors.js";
|
||||
import { resolveGhidraProject } from "./GhidraProjectSnapshot.js";
|
||||
import { DeferredAnalysisClient } from "../providers/DeferredAnalysisClient.js";
|
||||
import type { ProviderInspectionScope } from "../application/ProviderInspectionScope.js";
|
||||
@@ -419,14 +419,7 @@ export class GhidraProvider implements AnalysisProviderCandidate {
|
||||
isMachOFormat(target.format)
|
||||
)
|
||||
)
|
||||
return err(
|
||||
new ProviderAdapterError("ghidra", "resolve_analysis_profile", {
|
||||
diagnostics: {
|
||||
reason:
|
||||
"Existing-project mode supports native ELF/PE/Mach-O programs on POSIX without fresh-import loader preparation or pinned language overrides; use fresh import for this target/host.",
|
||||
},
|
||||
}),
|
||||
);
|
||||
return err(existingProjectUnsupported());
|
||||
const project = await resolveGhidraProject(
|
||||
this.config.ghidraExistingProject,
|
||||
target,
|
||||
@@ -671,6 +664,15 @@ export class GhidraProvider implements AnalysisProviderCandidate {
|
||||
}
|
||||
}
|
||||
|
||||
/** The selected project, not the target or provider, is what this host cannot serve. */
|
||||
const existingProjectUnsupported = (): ConfigurationError => {
|
||||
const constraint =
|
||||
"Existing-project mode supports native ELF/PE/Mach-O programs on POSIX without fresh-import loader preparation or pinned language overrides; unset REA_GHIDRA_PROJECT_PATH to use fresh import for this target/host.";
|
||||
return new ConfigurationError(constraint, {
|
||||
settings: [{ setting: "REA_GHIDRA_PROJECT_PATH", constraint }],
|
||||
});
|
||||
};
|
||||
|
||||
const providerFailureText = (cause: unknown): string =>
|
||||
cause instanceof Error ? cause.message : String(cause);
|
||||
|
||||
|
||||
@@ -196,6 +196,21 @@ it.skipIf(process.platform === "win32")(
|
||||
},
|
||||
);
|
||||
|
||||
it("reports a missing project marker as an invalid REA_GHIDRA_PROJECT_PATH selection", async () => {
|
||||
const root = await createTestTempDirectory("rea-ghidra-project-missing-");
|
||||
await expect(
|
||||
resolveGhidraProject(
|
||||
{ path: join(root, "missing", "app.gpr") },
|
||||
{ path: join(root, "target") },
|
||||
resources,
|
||||
),
|
||||
).rejects.toMatchObject({
|
||||
_tag: "ConfigurationError",
|
||||
settings: [{ setting: "REA_GHIDRA_PROJECT_PATH" }],
|
||||
diagnostics: { system_code: "ENOENT" },
|
||||
});
|
||||
});
|
||||
|
||||
it.skipIf(process.platform === "win32")(
|
||||
"follows native parent symlinks and preserves missing marker failures",
|
||||
async () => {
|
||||
|
||||
Reference in new issue
Block a user