mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
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:
@@ -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,
|
||||
|
||||
@@ -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 ??
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user