fix(ghidra): keep the NativeAOT target refusal's own recovery advice

An unsupported-target error always projected the generic "select a target"
remediation, which hid the adapter's available workflows (omit
REA_GHIDRA_NATIVEAOT_JAR for ordinary analysis, or use
inspect_managed_artifact). AnalysisUnsupportedTargetError accepts a
refusal-specific remediation, as AnalysisResourceConstraintError does, and
the NativeAOT refusal supplies its reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822
2026-10-09 04:09:50 +00:00
co-authored by Claude Opus 5.5
parent 42c276af1e
commit f198993f41
4 changed files with 18 additions and 5 deletions
+4 -1
View File
@@ -7,13 +7,16 @@ import {
/** A readable supplied artifact lies outside this operation's supported target formats. */
export class AnalysisUnsupportedTargetError extends AnalysisError {
readonly _tag = "AnalysisUnsupportedTargetError";
/** Refusal-specific recovery, when the producer knows an available workflow. */
readonly remediationAction: string | undefined;
constructor(
readonly operation: string,
readonly path: string,
readonly reason: string,
options?: AnalysisErrorOptions,
options?: AnalysisErrorOptions & { readonly remediationAction?: string },
) {
super(`Unsupported target for ${operation} at ${path}: ${reason}`, options);
this.remediationAction = options?.remediationAction;
}
}
+4 -1
View File
@@ -39,7 +39,10 @@ export const analysisErrorRemediationAction = (
if (error instanceof AnalysisSnapshotMismatchError)
return "Run analysis without this snapshot, then save a fresh snapshot using the intended artifact, provider, and analysis profile.";
if (error instanceof AnalysisUnsupportedTargetError)
return "Select a target supported by this operation or choose an operation supporting the reported target format.";
return (
error.remediationAction ??
"Select a target supported by this operation or choose an operation supporting the reported target format."
);
if (error instanceof AnalysisResourceConstraintError)
return (
error.remediationAction ??
@@ -106,6 +106,9 @@ describe("optional Ghidra extension refusals", () => {
if (!architecture.ok)
expect(projectAnalysisError(architecture.error)).toMatchObject({
code: "unsupported_target",
remediation: {
action: expect.stringContaining("omit REA_GHIDRA_NATIVEAOT_JAR"),
},
details: {
operation: "resolve_analysis_profile",
path: target.path,
@@ -312,9 +315,11 @@ describe("Ghidra extension profile and producer validation", () => {
);
expect(resolved.ok).toBe(false);
if (!resolved.ok)
expect(resolved.error).toMatchObject({
_tag: "AnalysisUnsupportedTargetError",
reason: expect.stringContaining("inspect_managed_artifact"),
expect(projectAnalysisError(resolved.error)).toMatchObject({
code: "unsupported_target",
remediation: {
action: expect.stringContaining("inspect_managed_artifact"),
},
});
});
it("does not substitute another artifact under an unchanged configured path", async () => {
@@ -198,6 +198,8 @@ const extensionRefusalError = (
"resolve_analysis_profile",
target.path,
refusal.reason,
// The reason names the workflows that remain available.
{ remediationAction: refusal.reason },
);
/** Snapshot committed extension bytes into the private runtime before Java loads them. */