fix(targets): remove path advice for format refusals

Readable executable format refusals report their source format reason and an unsupported-target category instead of misleading missing-path advice. Preserve actual path/permission errors, WASM static recovery, and native target source inspection.

Verified on Linux x64 through actual CLI/fresh stdio MCP: three NE/LE/LX controls, exact canonical projection parity, unchanged bytes, responsiveness and a missing-path control. 103 focused tests across nine files, full static gate and generated documentation passed.

Fixes #1979
Refs #1852

Co-authored-by: Ha1baraA11 <243105435+Ha1baraA11@users.noreply.github.com>
Co-authored-by: Ysopwnable <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
This commit is contained in:
Ha1baraA1
2026-10-12 08:08:16 +08:00
committed by GitHub
parent e9fe6437a7
commit f28f338283
4 changed files with 91 additions and 9 deletions
+19 -3
View File
@@ -93,6 +93,7 @@ export const parseBinaryTarget = async (
new BinaryTargetError(
path,
"raw-binary format requires an executable target kind",
{ constraint: "file_format_rejected" },
),
);
const admitted = admitRawBinary({
@@ -101,7 +102,11 @@ export const parseBinaryTarget = async (
fileBytes: metadata.size,
});
if (!admitted.ok)
return err(new BinaryTargetError(path, admitted.error));
return err(
new BinaryTargetError(path, admitted.error, {
constraint: "file_format_rejected",
}),
);
return ok({
path,
sourcePath: canonical,
@@ -127,10 +132,16 @@ export const parseBinaryTarget = async (
new BinaryTargetError(
path,
"DOS COM format requires an executable target kind",
{ constraint: "file_format_rejected" },
),
);
const length = validateDosComLength(metadata.size);
if (!length.ok) return err(new BinaryTargetError(path, length.error));
if (!length.ok)
return err(
new BinaryTargetError(path, length.error, {
constraint: "file_format_rejected",
}),
);
return ok({
path,
sourcePath: canonical,
@@ -168,7 +179,12 @@ export const parseBinaryTarget = async (
hostArchitecture,
signal,
);
if (!detected.ok) return err(new BinaryTargetError(path, detected.error));
if (!detected.ok)
return err(
new BinaryTargetError(path, detected.error, {
constraint: "file_format_rejected",
}),
);
return ok({
path,
sourcePath: process.platform === "win32" ? candidate : canonical,
+10 -2
View File
@@ -76,6 +76,11 @@ export const analysisErrorRemediationAction = (
error.constraint === "directory_requires_file"
)
return "For a JavaScript/Electron application directory, call analyze_javascript_application with input_path or run `rea analyze <directory>`. For binary analysis, select its executable file.";
if (
error instanceof BinaryTargetError &&
error.constraint === "file_format_rejected"
)
return "Review the reported file-format reason and select a supported, well-formed target.";
if (
error instanceof BinaryTargetError &&
error.constraint === "wasm_requires_static_inspection"
@@ -102,7 +107,8 @@ export const analysisErrorCategory = (
): AnalysisErrorProjection["category"] => {
if (
error instanceof BinaryTargetError &&
error.constraint === "wasm_requires_static_inspection"
(error.constraint === "wasm_requires_static_inspection" ||
error.constraint === "file_format_rejected")
)
return "unsupported_target";
if (
@@ -231,7 +237,9 @@ export const analysisErrorUserMessage = (error: AnalysisError): string => {
if (error instanceof BinaryTargetError)
return error.constraint === "directory_requires_file"
? "open_binary accepts files and macOS app bundles. This target is a directory; JavaScript/Electron application directories can be analyzed directly with analyze_javascript_application or `rea analyze <directory>`."
: `${error.message}. Check that the path exists, is readable, and points to a supported file.`;
: error.constraint === "file_format_rejected"
? `${error.message}.`
: `${error.message}. Check that the path exists, is readable, and points to a supported file.`;
if (error._tag === "ProcessCaptureError")
return (
error.userMessage ??
+2 -1
View File
@@ -40,7 +40,8 @@ export class NoBinaryOpenError extends AnalysisError {
export interface BinaryTargetErrorOptions extends AnalysisErrorOptions {
readonly constraint?:
| "directory_requires_file"
| "wasm_requires_static_inspection";
| "wasm_requires_static_inspection"
| "file_format_rejected";
}
/** A supplied target path could not be safely opened as a supported app or binary. */
@@ -6,6 +6,51 @@ import { parseBinaryTarget } from "../../../src/application/BinaryTargetResolver
import { projectAnalysisError } from "../../../src/domain/analysisErrorProjection.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
describe("executable format refusal diagnostics", () => {
it.each(["NE", "LE", "LX"] as const)(
"reports unsupported %s executables without path advice",
async (signature) => {
const directory = await createTestTempDirectory(
`rea-target-${signature.toLowerCase()}-`,
);
const path = join(directory, "unsupported.exe");
const bytes = Buffer.alloc(0x80 + 24);
bytes.write("MZ", 0, "ascii");
bytes.writeUInt32LE(0x80, 0x3c);
bytes.write(signature, 0x80, "ascii");
await writeFile(path, bytes);
const result = await parseBinaryTarget(path);
if (result.ok)
throw new Error(`unsupported ${signature} target was admitted`);
const reason = `unsupported ${signature} executable in MZ image`;
const projected = projectAnalysisError(result.error);
expect(projected).toMatchObject({
code: "target_unavailable",
category: "unsupported_target",
retryable: false,
message: `Cannot open artifact: ${reason}.`,
details: {
reason,
constraint: "file_format_rejected",
},
remediation: {
action:
"Review the reported file-format reason and select a supported, well-formed target.",
},
});
expect(projected.message).not.toContain("Check that the path");
expect(await systemDoctorHost().inspectTarget?.(path)).toMatchObject({
classification: "unsupported_target",
details: { constraint: "file_format_rejected" },
remediation:
"Review the reported file-format reason and select a supported, well-formed target.",
});
},
);
});
describe("target route diagnostics", () => {
it.each(["module.wasm", "module.bin", "misleading.js"])(
"identifies a WASM v1 header in %s without suggesting native provider installation",
@@ -53,7 +98,11 @@ describe("target route diagnostics", () => {
const result = await parseBinaryTarget(path);
if (result.ok) throw new Error("unknown header was admitted");
const projected = projectAnalysisError(result.error);
expect(projected.details).not.toHaveProperty("constraint");
expect(projected).toMatchObject({
category: "unsupported_target",
retryable: false,
details: { constraint: "file_format_rejected" },
});
expect(projected.remediation.action).not.toContain(
"inspect_wasm_artifact",
);
@@ -107,10 +156,14 @@ describe("target route diagnostics", () => {
});
const missing = await parseBinaryTarget(join(directory, "missing"));
if (missing.ok) throw new Error("missing target was admitted");
expect(projectAnalysisError(missing.error)).toMatchObject({
const missingProjection = projectAnalysisError(missing.error);
expect(missingProjection).toMatchObject({
details: { reason: expect.stringContaining("does not exist") },
message: expect.stringContaining("does not exist"),
});
expect(missingProjection.message).toContain(
"Check that the path exists, is readable, and points to a supported file.",
);
expect(
await doctor.inspectTarget?.(join(directory, "missing")),
).toMatchObject({
@@ -129,10 +182,14 @@ describe("target route diagnostics", () => {
try {
const result = await parseBinaryTarget(path);
if (result.ok) throw new Error("unreadable target was admitted");
expect(projectAnalysisError(result.error)).toMatchObject({
const projected = projectAnalysisError(result.error);
expect(projected).toMatchObject({
details: { reason: expect.stringContaining("permission denied") },
message: expect.stringContaining("permission denied"),
});
expect(projected.message).toContain(
"Check that the path exists, is readable, and points to a supported file.",
);
expect(await systemDoctorHost().inspectTarget?.(path)).toMatchObject({
ok: false,
classification: "config_drift",