Files
reverse-engineering-anything/src/application/rust/RustBinaryService.ts
T
morluto 3b698645a0 fix(evidence): retain completed observations on late cancellation
Validate completed reports before cancellation projection, retain valid
Evidence and captured command diagnostics, and convert raw cleanup partials
at their application owner. Record partial Evidence consistently across MCP
handlers. Keep page inspection separate from unfinished bundle analysis.

Refs #4261
2026-10-16 06:46:25 +08:00

117 lines
3.7 KiB
TypeScript

import { isAbsolute } from "node:path";
import type { ExecutionOptions } from "../AnalysisProvider.js";
import type { RustBinaryPort } from "./RustBinaryPort.js";
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
import {
AnalysisCancelledError,
AnalysisInputError,
AnalysisOutputError,
} from "../../domain/analysisErrorCore.js";
import { createEvidence, type Evidence } from "../../domain/evidence.js";
import { BINARY_ARCHITECTURES } from "../../domain/binaryTargetTypes.js";
import {
inspectRustBinaryInputSchema,
rustBinarySchema,
} from "../../domain/rust/rustBinary.js";
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
import { err, ok, type Result } from "../../domain/result.js";
import { completeProviderEvidence } from "../ProviderOperationExecution.js";
const OPERATION = "inspect_rust_binary";
const architectureForSubject = (architecture: string) => {
switch (architecture) {
case "386":
return "x86";
case "amd64":
return "x86_64";
default:
return BINARY_ARCHITECTURES.find(
(supported) => supported === architecture,
);
}
};
/** Shared exact input validation and artifact-bound Evidence for CLI and MCP. */
export class RustBinaryService {
constructor(readonly provider: RustBinaryPort) {}
/** Inspect one explicitly selected file without acquiring a binary session. */
async inspect(
rawInput: unknown,
options?: ExecutionOptions,
): Promise<Result<Evidence, AnalysisError>> {
if (options?.signal?.aborted)
return err(new AnalysisCancelledError(OPERATION));
const input = inspectRustBinaryInputSchema.safeParse(rawInput);
if (!input.success)
return err(
analysisInputErrorFromIssues(OPERATION, input.error.issues, rawInput, {
cause: input.error,
}),
);
if (!isAbsolute(input.data.path))
return err(
new AnalysisInputError(OPERATION, undefined, [
{
path: ["path"],
reason: "invalid_format",
message: "Expected an absolute filesystem path on this host.",
},
]),
);
const inspected = await this.provider.inspect(input.data, options);
if (!inspected.ok) return inspected;
const report = rustBinarySchema.safeParse(inspected.value);
if (!report.success || report.data.artifact.path !== input.data.path)
return err(
new AnalysisOutputError(
OPERATION,
"Rust dependency reader returned malformed metadata or changed the selected artifact identity.",
),
);
return completeProviderEvidence(
ok(evidenceFor(this.provider.identity, input.data, report.data)),
OPERATION,
options,
);
}
}
const evidenceFor = (
provider: RustBinaryPort["identity"],
parameters: { readonly path: string },
value: ReturnType<typeof rustBinarySchema.parse>,
): Evidence => {
const architecture = architectureForSubject(value.architecture);
const section = value.auditable_metadata;
return createEvidence(
{
path: value.artifact.path,
sha256: value.artifact.sha256,
format: value.format === "macho" ? "mach-o" : value.format,
...(architecture === undefined ? {} : { architecture }),
},
provider,
{
operation: OPERATION,
parameters,
result: value,
confidence: "observed",
limitations: value.limitations,
locations: [
{ kind: "artifact-path", path: value.artifact.path },
...(section === null
? []
: [
{
kind: "file-offset-range" as const,
start: section.file_offset,
end: section.file_offset + section.section_bytes,
},
]),
],
},
);
};