refactor(cli): retain typed analysis outcomes through delivery
This commit is contained in:
1 parent
0e1500dc8d
commit
329325049c
79 files changed
+581
-372
No files matched your search
@@ -132,7 +132,7 @@ export const runDirectAnalysis = async (
|
||||
readonly architecture?: BinaryArchitecture;
|
||||
readonly entryPoint?: string;
|
||||
} = {},
|
||||
): Promise<JsonValue> =>
|
||||
): Promise<Result<Evidence, JsonValue>> =>
|
||||
withProcessCancellation(options.signal, (signal) =>
|
||||
runAnalysis(dependencies, path, tool, arguments_, {
|
||||
logger: options.logger ?? silentLogger,
|
||||
@@ -175,31 +175,35 @@ export const runProviderAnalysis = async (
|
||||
readonly evidenceBundlePath?: string;
|
||||
},
|
||||
]
|
||||
): Promise<JsonValue> => {
|
||||
): Promise<Result<Evidence, JsonValue>> => {
|
||||
if (delivery !== undefined && tool !== "inspect_managed_members")
|
||||
return cliError(
|
||||
new AnalysisInputError(tool, undefined, [
|
||||
{
|
||||
path: ["delivery"],
|
||||
reason: "invalid_value",
|
||||
message:
|
||||
"Evidence delivery options are only supported by inspect_managed_members",
|
||||
},
|
||||
]),
|
||||
return err(
|
||||
cliError(
|
||||
new AnalysisInputError(tool, undefined, [
|
||||
{
|
||||
path: ["delivery"],
|
||||
reason: "invalid_value",
|
||||
message:
|
||||
"Evidence delivery options are only supported by inspect_managed_members",
|
||||
},
|
||||
]),
|
||||
),
|
||||
);
|
||||
if (
|
||||
delivery?.detail === "summary" &&
|
||||
delivery.evidenceBundlePath === undefined
|
||||
)
|
||||
return cliError(
|
||||
new AnalysisInputError(tool, undefined, [
|
||||
{
|
||||
path: ["evidence_bundle"],
|
||||
reason: "missing_argument",
|
||||
message:
|
||||
"summary detail requires --evidence-bundle so complete Evidence remains available to later CLI commands",
|
||||
},
|
||||
]),
|
||||
return err(
|
||||
cliError(
|
||||
new AnalysisInputError(tool, undefined, [
|
||||
{
|
||||
path: ["evidence_bundle"],
|
||||
reason: "missing_argument",
|
||||
message:
|
||||
"summary detail requires --evidence-bundle so complete Evidence remains available to later CLI commands",
|
||||
},
|
||||
]),
|
||||
),
|
||||
);
|
||||
return withProcessCancellation(signal, async (operationSignal) => {
|
||||
// The selected CLI path is a request fact. Keep it out of provider inputs
|
||||
@@ -215,14 +219,16 @@ export const runProviderAnalysis = async (
|
||||
operationSignal,
|
||||
);
|
||||
await dispatchQueuedInterrupts();
|
||||
if (!original.ok) return cliError(original.error);
|
||||
if (!original.ok) return err(cliError(original.error));
|
||||
const evidence = withEvidenceCallerInputPath(original.value, path);
|
||||
if (operationSignal.aborted)
|
||||
return cliError(
|
||||
new AnalysisCancelledError(tool, {
|
||||
cause: operationSignal.reason,
|
||||
partialObservation: evidence,
|
||||
}),
|
||||
return err(
|
||||
cliError(
|
||||
new AnalysisCancelledError(tool, {
|
||||
cause: operationSignal.reason,
|
||||
partialObservation: evidence,
|
||||
}),
|
||||
),
|
||||
);
|
||||
const result =
|
||||
delivery?.evidenceBundlePath === undefined
|
||||
@@ -233,7 +239,7 @@ export const runProviderAnalysis = async (
|
||||
delivery.detail,
|
||||
operationSignal,
|
||||
);
|
||||
return result.ok ? result.value : cliError(result.error);
|
||||
return result.ok ? ok(result.value) : err(cliError(result.error));
|
||||
}
|
||||
return runAnalysis(dependencies, path, tool, arguments_, {
|
||||
logger,
|
||||
@@ -321,7 +327,7 @@ const runAnalysis = async (
|
||||
readonly architecture?: BinaryArchitecture;
|
||||
readonly entryPoint?: string;
|
||||
},
|
||||
): Promise<JsonValue> => {
|
||||
): Promise<Result<Evidence, JsonValue>> => {
|
||||
const { logger, signal, snapshotPath } = options;
|
||||
const evidenceParameters = options.evidenceParameters ?? arguments_;
|
||||
let session: BinarySession;
|
||||
@@ -329,7 +335,7 @@ const runAnalysis = async (
|
||||
session = await options.createSession();
|
||||
} else {
|
||||
const config = dependencies.readConfiguration();
|
||||
if (!config.ok) return cliError(config.error);
|
||||
if (!config.ok) return err(cliError(config.error));
|
||||
session = await (
|
||||
tool === "inspect_native_dispatch_metadata" && snapshotPath === undefined
|
||||
? dependencies.createNativeDispatchBinarySession
|
||||
@@ -549,9 +555,9 @@ const runAnalysis = async (
|
||||
outcome.cleanupError.retainDiagnostics({
|
||||
analysis_snapshot: { ...publishedSnapshot },
|
||||
});
|
||||
return directAnalysisCleanupFailure(result, outcome.cleanupError);
|
||||
return err(directAnalysisCleanupFailure(result, outcome.cleanupError));
|
||||
}
|
||||
return result.ok ? result.value : cliError(result.error);
|
||||
return result.ok ? ok(result.value) : err(cliError(result.error));
|
||||
};
|
||||
|
||||
const executeAnalysisTool = async (input: {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { AnalysisInputError } from "../domain/analysisErrorCore.js";
|
||||
import { AdbDeviceAnalysisService } from "../application/adb/AdbDeviceAnalysisService.js";
|
||||
@@ -33,7 +34,7 @@ export const registerAdbCommands = (
|
||||
"adb",
|
||||
operation,
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import { inspectAnalysisViewValidated } from "../application/analysisView/Analys
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js";
|
||||
import type { ZodSafeParseResult } from "zod";
|
||||
|
||||
@@ -33,13 +34,6 @@ export const analysisEvidenceDeliveryOptions = {
|
||||
),
|
||||
};
|
||||
|
||||
const isEvidence = (value: JsonValue): value is Evidence =>
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
!Array.isArray(value) &&
|
||||
"evidence_id" in value &&
|
||||
"normalized_result" in value;
|
||||
|
||||
interface CliEvidenceDelivery {
|
||||
readonly view?: AnalysisViewRequest;
|
||||
readonly providerId: string;
|
||||
@@ -143,7 +137,7 @@ export const deliverCliAnalysisEvidence = async (
|
||||
signal,
|
||||
undefined,
|
||||
{ redactFailure },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
const complete = await finishAnalysisCommand(
|
||||
run,
|
||||
service,
|
||||
@@ -171,7 +165,7 @@ export const deliverCliAnalysisEvidence = async (
|
||||
|
||||
/** Publish already finalized CLI analysis without opening a second provider/session. */
|
||||
export const publishCliAnalysisEvidence = async (
|
||||
value: JsonValue,
|
||||
result: Result<Evidence, JsonValue>,
|
||||
input: CliEvidenceDelivery,
|
||||
): Promise<JsonValue> => {
|
||||
const { operation, signal, detail, evidenceOutput } = input;
|
||||
@@ -179,8 +173,8 @@ export const publishCliAnalysisEvidence = async (
|
||||
projectAnalysisError(
|
||||
redactExplicitFailure(error, input.sensitiveValues ?? []),
|
||||
);
|
||||
if (!isEvidence(value)) return value;
|
||||
const complete = parseEvidence(value);
|
||||
if (!result.ok) return result.error;
|
||||
const complete = parseEvidence(result.value);
|
||||
const inputError = cliEvidenceDeliveryInputError(input);
|
||||
if (inputError !== undefined) {
|
||||
inputError.retainPartialObservation(complete);
|
||||
|
||||
@@ -119,9 +119,11 @@ export const registerAnalysisViewCommands = (
|
||||
);
|
||||
// Preserve the inline command's existing error label without copying
|
||||
// the authenticated completed Evidence or its partial observation.
|
||||
return bundlePath === undefined && "code" in result
|
||||
? { error: "Application workflow failed", ...result }
|
||||
: result;
|
||||
return result.ok
|
||||
? result.value
|
||||
: bundlePath === undefined
|
||||
? { error: "Application workflow failed", ...result.error }
|
||||
: result.error;
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { AndroidAnalysisService } from "../application/android/AndroidAnalysisService.js";
|
||||
import { createAndroidAnalysisProvider } from "../composition/android.js";
|
||||
@@ -26,7 +27,7 @@ export const registerAndroidCommands = (
|
||||
"jadx",
|
||||
operation,
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
);
|
||||
const path = z.string().describe("Local standalone APK path");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { ApktoolResourceAnalysisService } from "../application/apktool/ApktoolResourceAnalysisService.js";
|
||||
import type { ApktoolResourceAnalysisPort } from "../application/apktool/ApktoolResourceAnalysisPort.js";
|
||||
@@ -32,7 +33,7 @@ export const registerApktoolCommands = (
|
||||
"apktool",
|
||||
operation,
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import {
|
||||
analysisEvidenceDeliveryOptions,
|
||||
cliEvidenceDeliveryInputError,
|
||||
@@ -63,7 +64,7 @@ export const registerArtifactCommands = (
|
||||
...(options.root === undefined ? {} : { root: options.root }),
|
||||
},
|
||||
logger,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -103,7 +104,7 @@ const registerDylibResolutionCommand = (
|
||||
: { architecture: options.architecture }),
|
||||
},
|
||||
logger,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -138,7 +139,7 @@ const registerAssetCatalogCommand = (
|
||||
"inspect_asset_catalog",
|
||||
{ offset: options.offset, limit: options.limit },
|
||||
logger,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -188,7 +189,7 @@ const registerInterfaceBuilderCommand = (
|
||||
max_connections: options.maxConnections,
|
||||
},
|
||||
logger,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -220,7 +221,7 @@ const registerExtractionCommand = (
|
||||
output_root: createArtifactExtractionDestination(),
|
||||
},
|
||||
logger,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { inspectDevicetreeInputSchema } from "../domain/firmware/devicetree.js";
|
||||
import { inspectPeLoadConfigInputSchema } from "../domain/native/peLoadConfig.js";
|
||||
import { inspectByteEntropyInputSchema } from "../domain/native/byteEntropy.js";
|
||||
@@ -76,7 +77,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
"rea-elf-notes",
|
||||
"inspect_elf_notes",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -120,7 +121,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
"rea-apple-ips",
|
||||
"inspect_apple_ips",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -157,7 +158,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
"rea-kernel-module",
|
||||
"inspect_kernel_module",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -192,7 +193,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
"rea-pe-load-config",
|
||||
"inspect_pe_load_config",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -230,7 +231,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
"rea-byte-entropy",
|
||||
"inspect_byte_entropy",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -265,7 +266,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
"rea-pe-certificates",
|
||||
"inspect_pe_certificates",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -300,7 +301,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
"rea-pe-imports",
|
||||
"inspect_pe_imports",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -335,7 +336,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
"rea-pe-resources",
|
||||
"inspect_pe_resources",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { Cli, z } from "incur";
|
||||
|
||||
import { browserCliError } from "./browserCliError.js";
|
||||
@@ -96,7 +97,7 @@ const registerWebMcp = (
|
||||
provider.identity().id,
|
||||
"discover_webmcp_tools",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -207,7 +208,7 @@ const registerScreenshot = (
|
||||
provider.identity().id,
|
||||
"capture_web_screenshot",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { webBundleDeliveryOptions } from "./javascriptRouteDelivery.js";
|
||||
import {
|
||||
deliverCliAnalysisEvidence,
|
||||
@@ -87,7 +88,7 @@ const registerTargetList = (
|
||||
provider.identity().id,
|
||||
"list_browser_targets",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -144,7 +145,7 @@ const registerPageInspection = (
|
||||
provider.identity().id,
|
||||
"inspect_web_page",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -283,7 +284,7 @@ const registerObservationSession = (
|
||||
provider.identity().id,
|
||||
"observe_web_session",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { inspectPacketFieldsInputSchema } from "../domain/captures/packetFields.js";
|
||||
import { z } from "incur";
|
||||
import type { PacketCaptureService } from "../application/captures/PacketCaptureService.js";
|
||||
@@ -62,7 +63,7 @@ export const registerCaptureCommands = (
|
||||
"tshark-capture",
|
||||
"inspect_packet_fields",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
@@ -117,7 +118,7 @@ export const registerCaptureCommands = (
|
||||
"tshark-capture",
|
||||
"inspect_packet_capture",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { realpath, stat } from "node:fs/promises";
|
||||
import { findAppBundleResourceRoot } from "../application/AppBundleExecutable.js";
|
||||
@@ -166,7 +167,7 @@ const registerOverviewCommands = (
|
||||
"binary_overview",
|
||||
{},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -183,7 +184,7 @@ const registerOverviewCommands = (
|
||||
"binary_overview",
|
||||
{},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -244,7 +245,7 @@ const registerDecompileCommand = (
|
||||
),
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -321,7 +322,7 @@ const registerXrefsCommand = (
|
||||
),
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -371,7 +372,7 @@ const registerTraceCommand = (
|
||||
case_sensitive: options.caseSensitive,
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -475,7 +476,7 @@ const registerInstructionsCommand = (
|
||||
),
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -536,7 +537,7 @@ const registerSearchCommand = (
|
||||
case_sensitive: options.caseSensitive,
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { nativeDispatchMetadataInputSchema } from "../domain/native/objcSwiftMetadata.js";
|
||||
import { z } from "incur";
|
||||
import {
|
||||
@@ -68,7 +69,7 @@ export const registerCoreNativeCommands = (
|
||||
...(options.cursor === undefined ? {} : { cursor: options.cursor }),
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
cli.command(CLI_COMMANDS.inspectNativeLoadImage, {
|
||||
@@ -123,7 +124,7 @@ export const registerCoreNativeCommands = (
|
||||
"read_bytes",
|
||||
{ address: args.address, length: options.length },
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
cli.command(CLI_COMMANDS.addressToFileOffset, {
|
||||
@@ -142,7 +143,7 @@ export const registerCoreNativeCommands = (
|
||||
"address_to_file_offset",
|
||||
{ address: args.address },
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
cli.command(CLI_COMMANDS.traceNativeValues, {
|
||||
@@ -220,7 +221,7 @@ export const registerCoreNativeCommands = (
|
||||
limit: options.limit,
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
cli.command(CLI_COMMANDS.inspectNativeDataType, {
|
||||
@@ -245,7 +246,7 @@ export const registerCoreNativeCommands = (
|
||||
: { address: options.address }),
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
for (const [command, operation] of [
|
||||
@@ -271,7 +272,7 @@ export const registerCoreNativeCommands = (
|
||||
operation,
|
||||
{ address: args.address },
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
}
|
||||
@@ -312,7 +313,7 @@ const registerNativeDispatchMetadataCommand = (
|
||||
: { max_input_bytes: options.maxInputBytes }),
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -370,7 +371,7 @@ const registerNativeUiActionCommand = (
|
||||
max_edges: options.maxEdges,
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -413,7 +414,7 @@ const registerNativeApiCommand = (
|
||||
),
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -459,7 +460,7 @@ const registerAnnotationCommand = (
|
||||
: { inline_comment: options["inline-comment"] }),
|
||||
},
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import type { Logger } from "pino";
|
||||
|
||||
@@ -33,7 +34,7 @@ export const registerCutterCommands = (
|
||||
"cutter",
|
||||
"list_cutter_sessions",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -80,7 +81,7 @@ export const registerCutterCommands = (
|
||||
"cutter",
|
||||
"cutter_command",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
|
||||
import type { DirectAnalysis } from "../composition/directAnalysis.js";
|
||||
@@ -87,7 +88,7 @@ export const registerDecompileDirectoryCommand = (
|
||||
"decompile_all",
|
||||
decompileAllArguments(options),
|
||||
directAnalysisOptionsFromCli(logger, options),
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { parseCliAnalysisEvidenceDelivery } from "./analysisEvidenceDelivery.js";
|
||||
import { javaScriptRoutesDeliverySchema } from "../domain/analysisView/javascriptRoutesView.js";
|
||||
import { isAbsolute, resolve, sep } from "node:path";
|
||||
@@ -78,7 +79,7 @@ const registerElectronFuseCommand = (
|
||||
service.provider.identity.id,
|
||||
"inspect_electron_fuses",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -158,9 +159,9 @@ const registerElectronActiveCommand = (
|
||||
"capture_electron_scenario",
|
||||
signal,
|
||||
);
|
||||
return "code" in result
|
||||
? { error: "Electron analysis failed", ...result }
|
||||
: result;
|
||||
return result.ok
|
||||
? result.value
|
||||
: { error: "Electron analysis failed", ...result.error };
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -249,7 +250,7 @@ const registerElectronTargetList = (
|
||||
provider.identity().id,
|
||||
"list_electron_targets",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -298,7 +299,7 @@ const registerElectronPageInspection = (
|
||||
provider.identity().id,
|
||||
"inspect_electron_page",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -330,12 +330,6 @@ const conflictingRules = (): JsonValue =>
|
||||
]),
|
||||
);
|
||||
|
||||
const isEvidenceValue = (value: unknown): boolean =>
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
"evidence_id" in value &&
|
||||
"normalized_result" in value;
|
||||
|
||||
/** Inspect a HAR as inspect-web-network-capture does and serialize the Evidence as it prints it; a refusal is the command's output. */
|
||||
const inspectBuildHar = async (
|
||||
capturePath: string,
|
||||
@@ -345,7 +339,6 @@ const inspectBuildHar = async (
|
||||
): Promise<Result<EnvironmentExportFile, JsonValue>> => {
|
||||
const { createWebNetworkCaptureService } =
|
||||
await import("../composition/webNetworkCaptures.js");
|
||||
const { parseEvidence } = await import("../domain/evidence.js");
|
||||
const { environmentExportEvidenceFile } =
|
||||
await import("../application/environment/environmentExport.js");
|
||||
const { ENVIRONMENT_BUILD_OPERATION } =
|
||||
@@ -368,15 +361,15 @@ const inspectBuildHar = async (
|
||||
"inspect_web_network_capture",
|
||||
signal,
|
||||
);
|
||||
if (!isEvidenceValue(inspected))
|
||||
if (!inspected.ok)
|
||||
return err({
|
||||
error: "Application workflow failed",
|
||||
...(inspected as Record<string, JsonValue>),
|
||||
...inspected.error,
|
||||
});
|
||||
const file = await environmentExportEvidenceFile(
|
||||
"evidence",
|
||||
capturePath,
|
||||
parseEvidence(inspected),
|
||||
inspected.value,
|
||||
ENVIRONMENT_BUILD_OPERATION,
|
||||
);
|
||||
return file.ok ? ok(file.value) : err(cliError(file.error));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { evmInterfaceModule } from "../composition/evm.js";
|
||||
import { logCliCommand } from "../cliLogging.js";
|
||||
@@ -46,7 +47,7 @@ export const registerEvmCommands = (
|
||||
"evmole-interface",
|
||||
"inspect_evm_interface",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AnalysisCancelledError } from "../domain/analysisErrorCore.js";
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
||||
import { ProviderCleanupError } from "../domain/providerCleanupError.js";
|
||||
import type { Result } from "../domain/result.js";
|
||||
import { err, ok, type Result } from "../domain/result.js";
|
||||
import { dispatchQueuedInterrupts } from "../process/queuedInterrupts.js";
|
||||
|
||||
/** Finish a one-shot CLI operation while retaining primary results through close. */
|
||||
@@ -30,7 +30,7 @@ export const finishAnalysisCommand = async <
|
||||
observation: AnalysisPartialObservation,
|
||||
) => Value | undefined;
|
||||
} = {},
|
||||
): Promise<Value | ReturnType<typeof projectAnalysisError>> => {
|
||||
): Promise<Result<Value, ReturnType<typeof projectAnalysisError>>> => {
|
||||
const projectFailure = (error: AnalysisError) =>
|
||||
projectAnalysisError(options.redactFailure?.(error) ?? error);
|
||||
let result: Result<Value, AnalysisError>;
|
||||
@@ -78,7 +78,7 @@ export const finishAnalysisCommand = async <
|
||||
}
|
||||
}
|
||||
await dispatchQueuedInterrupts();
|
||||
if (initialFailure !== undefined) return initialFailure;
|
||||
if (initialFailure !== undefined) return err(initialFailure);
|
||||
if (
|
||||
!result.ok &&
|
||||
result.error instanceof ProviderCleanupError &&
|
||||
@@ -126,5 +126,5 @@ export const finishAnalysisCommand = async <
|
||||
).bindCleanupDependencies([result.error, cleanup]),
|
||||
};
|
||||
}
|
||||
return result.ok ? result.value : projectFailure(result.error);
|
||||
return result.ok ? ok(result.value) : err(projectFailure(result.error));
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { FirmwareAnalysisService } from "../application/firmware/FirmwareAnalysisService.js";
|
||||
import { createFirmwareAnalysisProvider } from "../composition/firmware.js";
|
||||
@@ -36,7 +37,7 @@ export const registerFirmwareCommands = (
|
||||
operation === "inspect_firmware_regions" ? "binwalk" : "unblob",
|
||||
operation,
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
);
|
||||
const path = localPathStringSchema.describe(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { evidenceSchema } from "../domain/evidence.js";
|
||||
import { z } from "incur";
|
||||
import { FlutterBuildAnalysisService } from "../application/flutter/FlutterBuildAnalysisService.js";
|
||||
@@ -88,7 +89,7 @@ export const registerFlutterCommands = (
|
||||
return parsed.success ? parsed.data : undefined;
|
||||
},
|
||||
},
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
|
||||
import { FridaInstrumentationService } from "../application/frida/FridaInstrumentationService.js";
|
||||
@@ -96,7 +97,7 @@ export const registerFridaCommands = (
|
||||
"frida",
|
||||
"list_frida_devices",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
@@ -135,7 +136,7 @@ export const registerFridaCommands = (
|
||||
"frida",
|
||||
"list_frida_processes",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -284,7 +285,7 @@ export const registerFridaCommands = (
|
||||
"frida",
|
||||
"instrument_with_frida",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -134,10 +134,14 @@ export const runCliJavaScriptApplicationAnalysis = async (
|
||||
),
|
||||
);
|
||||
}
|
||||
const value = result.ok ? result.value : cliError(result.error);
|
||||
const finalized = result.ok
|
||||
? result
|
||||
: { ok: false as const, error: cliError(result.error) };
|
||||
return delivery === undefined
|
||||
? value
|
||||
: publishCliAnalysisEvidence(value, {
|
||||
? finalized.ok
|
||||
? finalized.value
|
||||
: finalized.error
|
||||
: publishCliAnalysisEvidence(finalized, {
|
||||
...delivery,
|
||||
providerId: JAVASCRIPT_APPLICATION_PROVIDER.id,
|
||||
operation: "analyze_javascript_application",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { javascriptRecoveryInputSchema } from "../domain/javascript/javascriptRecovery.js";
|
||||
import { z } from "incur";
|
||||
import { JavaScriptRecoveryService } from "../application/javascript/JavaScriptRecoveryService.js";
|
||||
@@ -65,7 +66,7 @@ export const registerJavaScriptRecoveryCommands = (
|
||||
"wakaru",
|
||||
"recover_javascript_sources",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { Cli, z } from "incur";
|
||||
|
||||
import {
|
||||
@@ -75,7 +76,7 @@ export const registerJavaScriptRuntimeObservationCommands = (
|
||||
"list_javascript_runtime_targets",
|
||||
signal,
|
||||
"V8 Inspector resources",
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -123,7 +124,7 @@ export const registerJavaScriptRuntimeObservationCommands = (
|
||||
"observe_javascript_runtime",
|
||||
signal,
|
||||
"V8 Inspector resources",
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { JebAnalysisService } from "../application/jeb/JebAnalysisService.js";
|
||||
import type { JebAnalysisPort } from "../application/jeb/JebAnalysisPort.js";
|
||||
@@ -28,7 +29,7 @@ export const registerJebCommands = (
|
||||
"jeb",
|
||||
operation,
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
|
||||
import { compareManagedMemberPaths } from "../application/managed/ManagedMemberComparisonService.js";
|
||||
@@ -44,7 +45,12 @@ const registerInspectManagedArtifact = (
|
||||
}),
|
||||
run: ({ args }) =>
|
||||
logCliCommand(logger, "inspect-managed-artifact", () =>
|
||||
runProviderAnalysis(args.path, "inspect_managed_artifact", {}, logger),
|
||||
runProviderAnalysis(
|
||||
args.path,
|
||||
"inspect_managed_artifact",
|
||||
{},
|
||||
logger,
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -90,7 +96,7 @@ const registerInspectManagedMembers = (
|
||||
? {}
|
||||
: { evidenceBundlePath: options.evidenceBundle }),
|
||||
},
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
@@ -114,7 +120,7 @@ const registerInspectManagedNativeBoundaries = (
|
||||
"inspect_managed_native_boundaries",
|
||||
{},
|
||||
logger,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -152,9 +158,9 @@ const registerCompareManagedMembers = (
|
||||
signal,
|
||||
"Managed member comparison resources",
|
||||
);
|
||||
return "evidence_id" in result
|
||||
? result
|
||||
: { error: "Managed member comparison failed", ...result };
|
||||
return result.ok
|
||||
? result.value
|
||||
: { error: "Managed member comparison failed", ...result.error };
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
|
||||
import type { DirectAnalysis } from "../composition/directAnalysis.js";
|
||||
@@ -51,7 +52,7 @@ export const registerNativeCallCommands = (
|
||||
OPERATION,
|
||||
z.record(z.string(), jsonValueSchema).parse(parsed.data),
|
||||
logger,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
});
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { PcapAnalysisService } from "../application/pcap/PcapAnalysisService.js";
|
||||
import type { PcapAnalysisPort } from "../application/pcap/PcapAnalysisPort.js";
|
||||
@@ -27,7 +28,7 @@ export const registerPcapCommands = (
|
||||
"pcap",
|
||||
operation,
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { JsonValue } from "../domain/jsonValue.js";
|
||||
import type { Result } from "../domain/result.js";
|
||||
|
||||
/** Flatten a finalized outcome only when returning the command's JSON output. */
|
||||
export const projectCliAnalysisResult = <Value>(
|
||||
result: Result<Value, JsonValue>,
|
||||
): Value | JsonValue => (result.ok ? result.value : result.error);
|
||||
@@ -185,7 +185,10 @@ export const registerReverseEngineeringCommands = (
|
||||
),
|
||||
);
|
||||
return result.ok
|
||||
? publishCliAnalysisEvidence(result.value, delivery)
|
||||
? publishCliAnalysisEvidence(
|
||||
{ ok: true, value: result.value },
|
||||
delivery,
|
||||
)
|
||||
: projectAnalysisError(result.error);
|
||||
}),
|
||||
),
|
||||
@@ -225,7 +228,10 @@ export const registerReverseEngineeringCommands = (
|
||||
),
|
||||
);
|
||||
return result.ok
|
||||
? publishCliAnalysisEvidence(result.value, delivery)
|
||||
? publishCliAnalysisEvidence(
|
||||
{ ok: true, value: result.value },
|
||||
delivery,
|
||||
)
|
||||
: projectAnalysisError(result.error);
|
||||
},
|
||||
),
|
||||
@@ -309,7 +315,11 @@ export const registerReverseEngineeringCommands = (
|
||||
const started = await manager.start(signal, {
|
||||
inferiorTty: options.inferiorTty,
|
||||
});
|
||||
if (!started.ok) return projectAnalysisError(started.error);
|
||||
if (!started.ok)
|
||||
return {
|
||||
ok: false as const,
|
||||
error: projectAnalysisError(started.error),
|
||||
};
|
||||
sessionId = started.value.session_id;
|
||||
const executed = await manager.execute(
|
||||
sessionId,
|
||||
@@ -317,8 +327,11 @@ export const registerReverseEngineeringCommands = (
|
||||
signal,
|
||||
);
|
||||
return executed.ok
|
||||
? executed.value.evidence
|
||||
: projectAnalysisError(executed.error);
|
||||
? { ok: true as const, value: executed.value.evidence }
|
||||
: {
|
||||
ok: false as const,
|
||||
error: projectAnalysisError(executed.error),
|
||||
};
|
||||
} finally {
|
||||
let cleanupFailed = false;
|
||||
if (sessionId !== undefined) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { inspectSignalDecodersInputSchema } from "../domain/signals/signalDecoders.js";
|
||||
import { z } from "incur";
|
||||
import type { SignalRecordingService } from "../application/signals/SignalRecordingService.js";
|
||||
@@ -64,7 +65,7 @@ export const registerSignalCommands = (
|
||||
"sigrok-recording",
|
||||
"inspect_signal_decoders",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
@@ -119,7 +120,7 @@ export const registerSignalCommands = (
|
||||
"sigrok-recording",
|
||||
"decode_signal_recording",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import type { Logger } from "pino";
|
||||
import type { CliInstance } from "./types.js";
|
||||
@@ -68,9 +69,9 @@ export const registerSourceToBundleCommand = (
|
||||
"compare_source_to_bundle",
|
||||
signal,
|
||||
);
|
||||
return "evidence_id" in result
|
||||
? result
|
||||
: { error: "Application workflow failed", ...result };
|
||||
return result.ok
|
||||
? result.value
|
||||
: { error: "Application workflow failed", ...result.error };
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { createSqliteDatabaseService } from "../composition/sqlite.js";
|
||||
import { CLI_COMMANDS } from "../cliCommandNames.js";
|
||||
@@ -80,7 +81,7 @@ export const registerSqliteCommands = (
|
||||
"sqlite",
|
||||
"inspect_sqlite_database",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
|
||||
import type { DirectAnalysis } from "../composition/directAnalysis.js";
|
||||
@@ -108,7 +109,7 @@ const registerNativeUiCommands = (
|
||||
"observe_native_ui",
|
||||
nativeUiScope(options),
|
||||
logger,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
const operation = "capture_native_ui_scenario";
|
||||
@@ -152,7 +153,7 @@ const registerNativeUiCommands = (
|
||||
? scenario
|
||||
: { ...scenario, native_ui_deadline_ms: deadline },
|
||||
logger,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
});
|
||||
};
|
||||
@@ -209,7 +210,7 @@ const registerNativeCommands = (
|
||||
? {}
|
||||
: { native_command_timeout_ms: options.nativeCommandTimeoutMs },
|
||||
logger,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
}
|
||||
@@ -239,7 +240,7 @@ const registerNativeCommands = (
|
||||
: { native_command_timeout_ms: options.nativeCommandTimeoutMs }),
|
||||
},
|
||||
logger,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
cli.command(CLI_COMMANDS.demangleSwift, {
|
||||
@@ -263,7 +264,7 @@ const registerNativeCommands = (
|
||||
: { native_command_timeout_ms: options.nativeCommandTimeoutMs }),
|
||||
},
|
||||
logger,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { compareWasmInterfaces } from "../application/wasm/WasmInterfaceComparisonService.js";
|
||||
import { parseCliJsonInput } from "../cliJsonInput.js";
|
||||
@@ -85,7 +86,7 @@ export const registerWasmCommands = (
|
||||
"wabt-artifact",
|
||||
"inspect_wasm_artifact",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { createWebModuleTraceService } from "../composition/webModules.js";
|
||||
import { CLI_COMMANDS } from "../cliCommandNames.js";
|
||||
@@ -67,7 +68,7 @@ export const registerWebModuleCommands = (
|
||||
"web-module-trace",
|
||||
"trace_web_module_imports",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { Cli, z } from "incur";
|
||||
import { createWebRuntimeService } from "../composition/webRuntime.js";
|
||||
import { browserCliError } from "./browserCliError.js";
|
||||
@@ -70,7 +71,7 @@ export const registerWebRuntimeCommands = (
|
||||
service.provider.identity().id,
|
||||
"observe_web_execution",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -111,7 +112,7 @@ export const registerWebRuntimeCommands = (
|
||||
service.provider.identity().id,
|
||||
"inspect_web_event_listeners",
|
||||
signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -47,9 +47,9 @@ export const registerWebScriptCommands = (
|
||||
signal,
|
||||
"Web script export resources",
|
||||
);
|
||||
return "evidence_id" in result
|
||||
? result
|
||||
: { error: "Script export failed", ...result };
|
||||
return result.ok
|
||||
? result.value
|
||||
: { error: "Script export failed", ...result.error };
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { createWebSourceLocationService } from "../composition/webSourceLocations.js";
|
||||
import { webSourceLocationInputSchema } from "../domain/webSourceLocation.js";
|
||||
@@ -62,7 +63,7 @@ export const registerWebSourceLocationCommands = (
|
||||
"web-source-location",
|
||||
"trace_web_source_location",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -83,8 +83,11 @@ export const registerBrowserScenarioCommands = (
|
||||
undefined,
|
||||
{ failureProjection: "initial" },
|
||||
);
|
||||
if (!("evidence_id" in result))
|
||||
return { error: "Browser scenario capture failed", ...result };
|
||||
if (!result.ok)
|
||||
return {
|
||||
error: "Browser scenario capture failed",
|
||||
...result.error,
|
||||
};
|
||||
return publishCliAnalysisEvidence(result, {
|
||||
providerId: "rea-playwright-browser-scenario",
|
||||
operation: OPERATION,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import {
|
||||
@@ -89,7 +90,9 @@ cliTest(
|
||||
await writer.add(name, new Uint8ArrayReader(fixture.nativeBytes));
|
||||
await writeFile(path, await writer.close());
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
const input = { inventory_evidence: [inventory] };
|
||||
const result = await cli.run({
|
||||
|
||||
@@ -43,7 +43,7 @@ it.each(["capabilities", "providers"])(
|
||||
readConfiguration: () => parseConfig({}),
|
||||
createBinarySession: () => session,
|
||||
}),
|
||||
runProviderAnalysis: async () => null,
|
||||
runProviderAnalysis: async () => ({ ok: false, error: null }),
|
||||
},
|
||||
);
|
||||
let stdout = "";
|
||||
@@ -132,7 +132,7 @@ it.each(["capabilities", "providers"])(
|
||||
}),
|
||||
createBinarySession: () => session,
|
||||
}),
|
||||
runProviderAnalysis: async () => null,
|
||||
runProviderAnalysis: async () => ({ ok: false, error: null }),
|
||||
},
|
||||
);
|
||||
let stdout = "";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { finishAnalysisCommand } from "../../../src/cli/finishAnalysisCommand.js";
|
||||
@@ -24,7 +25,7 @@ it("preserves producer-owned cleanup failure when service close does not resolve
|
||||
new AbortController().signal,
|
||||
);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
expect(result).toEqual(err(expected));
|
||||
expect(failure.cleanupIncomplete).toBe(true);
|
||||
});
|
||||
|
||||
@@ -44,8 +45,11 @@ it("returns completed partial Evidence when cancellation arrives during close",
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
code: "cancelled",
|
||||
details: { partial_observation: { evidence_id: evidence.evidence_id } },
|
||||
ok: false,
|
||||
error: {
|
||||
code: "cancelled",
|
||||
details: { partial_observation: { evidence_id: evidence.evidence_id } },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,7 +66,7 @@ it("preserves the primary operation error when cancellation arrives during close
|
||||
"fixture-provider",
|
||||
"inspect_web_page",
|
||||
controller.signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
code: "unreadable_output",
|
||||
@@ -92,7 +96,7 @@ it("retains late cancellation and completed Evidence when close also fails", asy
|
||||
"fixture-provider",
|
||||
"inspect_web_page",
|
||||
controller.signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
code: "cleanup_incomplete",
|
||||
category: "cancelled",
|
||||
@@ -134,7 +138,7 @@ it.each([false, true])(
|
||||
controller.signal,
|
||||
undefined,
|
||||
{ recoverCompletedObservation: (observation) => observation },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
if (!cancel) expect(result).toEqual(evidence);
|
||||
else
|
||||
expect(result).toMatchObject({
|
||||
@@ -188,7 +192,7 @@ it.each(["unresolved", "ordinary-partial", "initial-projection"] as const)(
|
||||
return observation;
|
||||
},
|
||||
},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(attemptedRecovery).toBe(false);
|
||||
expect(result).toEqual(
|
||||
kind === "initial-projection" ? initial : projectAnalysisError(failure),
|
||||
|
||||
@@ -280,7 +280,10 @@ describe("CLI result output surface", () => {
|
||||
if (output === undefined)
|
||||
throw new Error("Missing structured output surface");
|
||||
const cli = Cli.create("rea-output-test", { sync: false });
|
||||
registerManagedCommands(cli, silentLogger, async () => failure);
|
||||
registerManagedCommands(cli, silentLogger, async () => ({
|
||||
ok: false,
|
||||
error: failure,
|
||||
}));
|
||||
streamCliCommandResults(cli, output);
|
||||
let fallback = "";
|
||||
const previousExitCode = process.exitCode;
|
||||
@@ -408,12 +411,15 @@ describe("full-output token windows", () => {
|
||||
if (output === undefined) throw new Error("Missing TOON output surface");
|
||||
const cli = Cli.create("rea-output-test", { sync: false });
|
||||
registerManagedCommands(cli, silentLogger, async () => ({
|
||||
code: "target_unavailable",
|
||||
category: "unavailable",
|
||||
message: "The selected artifact is missing.",
|
||||
retryable: false,
|
||||
remediation: { action: "Check that the path exists." },
|
||||
details: { source: "artifact-open" },
|
||||
ok: false,
|
||||
error: {
|
||||
code: "target_unavailable",
|
||||
category: "unavailable",
|
||||
message: "The selected artifact is missing.",
|
||||
retryable: false,
|
||||
remediation: { action: "Check that the path exists." },
|
||||
details: { source: "artifact-open" },
|
||||
},
|
||||
}));
|
||||
streamCliCommandResults(cli, output);
|
||||
const previousExitCode = process.exitCode;
|
||||
@@ -667,7 +673,7 @@ describe("CLI result output surface", () => {
|
||||
registerManagedCommands(cli, silentLogger, async (...[path, operation]) => {
|
||||
expect(path).toBe("fixture.dll");
|
||||
expect(operation).toBe("inspect_managed_members");
|
||||
return value;
|
||||
return { ok: false, error: value };
|
||||
});
|
||||
// A terminal destination exercises the indented encoding past the limit.
|
||||
Object.defineProperty(destination, "isTTY", { value: true });
|
||||
@@ -707,7 +713,10 @@ describe("CLI result output surface", () => {
|
||||
if (tokenOutput === undefined)
|
||||
throw new Error("Missing token-count output surface");
|
||||
const tokenCli = Cli.create("rea-output-test", { sync: false });
|
||||
registerManagedCommands(tokenCli, silentLogger, async () => value);
|
||||
registerManagedCommands(tokenCli, silentLogger, async () => ({
|
||||
ok: false,
|
||||
error: value,
|
||||
}));
|
||||
streamCliCommandResults(tokenCli, tokenOutput);
|
||||
await tokenCli.serve(tokenArgv, {
|
||||
stdout: (text) => {
|
||||
|
||||
@@ -216,7 +216,10 @@ const registeredComparisonCommand = () => {
|
||||
return cli;
|
||||
},
|
||||
} as unknown as Parameters<typeof registerManagedCommands>[0];
|
||||
registerManagedCommands(cli, silentLogger, async () => null);
|
||||
registerManagedCommands(cli, silentLogger, async () => ({
|
||||
ok: false,
|
||||
error: null,
|
||||
}));
|
||||
return commands.get(CLI_COMMANDS.compareManagedMembers)!;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ it("preserves the relative request path in a load-image CLI summary and its comp
|
||||
),
|
||||
selectedPath,
|
||||
);
|
||||
const run = vi.fn(async () => parent);
|
||||
const run = vi.fn(async () => ({ ok: true as const, value: parent }));
|
||||
const cli = Cli.create("rea-load-image-cli-test", { sync: false });
|
||||
registerCoreNativeCommands(cli, pino({ enabled: false }), run);
|
||||
let stdout = "";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { expect, it } from "vitest";
|
||||
import { WasmArtifactService } from "../../../src/application/wasm/WasmArtifactService.js";
|
||||
@@ -31,7 +32,7 @@ it("delivers cancellation with completed WASM Evidence through the CLI boundary"
|
||||
"wabt-artifact",
|
||||
"inspect_wasm_artifact",
|
||||
signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
{
|
||||
on: events.on.bind(events),
|
||||
off: events.off.bind(events),
|
||||
|
||||
@@ -13,15 +13,12 @@ import { describe, expect, it } from "vitest";
|
||||
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";
|
||||
import {
|
||||
createNonApplicationZipInventory,
|
||||
inventoryApplicationPackage,
|
||||
requireSuccessfulProjection,
|
||||
} from "../../../support/applicationSessionFixture.js";
|
||||
|
||||
import { projectAndroidApplicationEvidence } from "../../../../src/application/android/AndroidApplicationService.js";
|
||||
import { createDirectAnalysis } from "../../../../src/composition/directAnalysis.js";
|
||||
import { androidApplicationProjectionResultSchema } from "../../../../src/domain/android/androidApplication.js";
|
||||
import { parseEvidence } from "../../../../src/domain/evidence.js";
|
||||
|
||||
const { runProviderAnalysis } = createDirectAnalysis({});
|
||||
|
||||
describe("Android application projection", () => {
|
||||
it("projects deterministic APK components and explicit bridge hypotheses", async () => {
|
||||
@@ -50,9 +47,7 @@ describe("Android application projection", () => {
|
||||
await writer.add("META-INF/FIXTURE.RSA", new TextReader("opaque signing"));
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
);
|
||||
const inventory = await inventoryApplicationPackage(path);
|
||||
const first = projectAndroidApplicationEvidence({
|
||||
inventory_evidence: [inventory],
|
||||
});
|
||||
@@ -144,9 +139,7 @@ describe("Android application projection", () => {
|
||||
await writer.add(entry, new TextReader("unrecognized bytes"));
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
);
|
||||
const inventory = await inventoryApplicationPackage(path);
|
||||
const result = projectAndroidApplicationEvidence({
|
||||
inventory_evidence: [inventory],
|
||||
});
|
||||
@@ -194,9 +187,7 @@ it("projects APK v1 metadata candidates at any META-INF depth", async () => {
|
||||
await writer.add(entry, new TextReader(`content:${entry}`));
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
);
|
||||
const inventory = await inventoryApplicationPackage(path);
|
||||
const projection = androidApplicationProjectionResultSchema.parse(
|
||||
requireSuccessfulProjection(
|
||||
projectAndroidApplicationEvidence({ inventory_evidence: [inventory] }),
|
||||
@@ -239,9 +230,7 @@ describe("APK family without a root manifest", () => {
|
||||
for (const [entry, text] of Object.entries(entries))
|
||||
await writer.add(entry, new TextReader(text));
|
||||
await writeFile(path, await writer.close());
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
);
|
||||
const inventory = await inventoryApplicationPackage(path);
|
||||
return androidApplicationProjectionResultSchema.parse(
|
||||
requireSuccessfulProjection(
|
||||
projectAndroidApplicationEvidence({ inventory_evidence: [inventory] }),
|
||||
@@ -269,9 +258,7 @@ it("retains encrypted native library paths as unknown component evidence", async
|
||||
{ password: "fixture-only" },
|
||||
);
|
||||
await writeFile(path, await writer.close());
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
);
|
||||
const inventory = await inventoryApplicationPackage(path);
|
||||
const projection = androidApplicationProjectionResultSchema.parse(
|
||||
requireSuccessfulProjection(
|
||||
projectAndroidApplicationEvidence({ inventory_evidence: [inventory] }),
|
||||
@@ -314,9 +301,7 @@ describe("case-distinct APK resource names", () => {
|
||||
await writer.add("res/2f.xml", new TextReader("lower"));
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
);
|
||||
const inventory = await inventoryApplicationPackage(path);
|
||||
expect(inventory.normalized_result).toMatchObject({
|
||||
coverage: { scope: "container-entries", status: "complete" },
|
||||
});
|
||||
@@ -358,9 +343,7 @@ describe("Android bridge candidate coverage", () => {
|
||||
await writer.add("lib/arm64-v8a/libnative.so", new TextReader("native"));
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
);
|
||||
const inventory = await inventoryApplicationPackage(path);
|
||||
const result = projectAndroidApplicationEvidence({
|
||||
inventory_evidence: [inventory],
|
||||
});
|
||||
@@ -392,9 +375,7 @@ describe("Android bridge candidate coverage", () => {
|
||||
);
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
);
|
||||
const inventory = await inventoryApplicationPackage(path);
|
||||
const result = projectAndroidApplicationEvidence({
|
||||
inventory_evidence: [inventory],
|
||||
});
|
||||
@@ -439,9 +420,7 @@ it("does not infer a runtime from library-named directories or their text descen
|
||||
await writer.add(`assets/${name}/NOTICE.txt`, new TextReader("inert text"));
|
||||
}
|
||||
await writeFile(path, await writer.close());
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
);
|
||||
const inventory = await inventoryApplicationPackage(path);
|
||||
const graph = androidApplicationProjectionResultSchema.parse(
|
||||
requireSuccessfulProjection(
|
||||
projectAndroidApplicationEvidence({ inventory_evidence: [inventory] }),
|
||||
@@ -460,9 +439,7 @@ it("retains an encrypted root manifest occurrence without claiming absence", asy
|
||||
password: "fixture-only",
|
||||
});
|
||||
await writeFile(path, await writer.close());
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
);
|
||||
const inventory = await inventoryApplicationPackage(path);
|
||||
const graph = androidApplicationProjectionResultSchema.parse(
|
||||
requireSuccessfulProjection(
|
||||
projectAndroidApplicationEvidence({ inventory_evidence: [inventory] }),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { mkdir, symlink, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
@@ -58,7 +59,9 @@ async function createCompleteAppleProjection() {
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
const fragments = fragmentInventoryEvidence(inventory, 3);
|
||||
const result = projectAppleApplicationEvidence({
|
||||
@@ -94,7 +97,9 @@ describe("Apple application projection", () => {
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
expect(inventory.normalized_result).toMatchObject({
|
||||
coverage: { scope: "container-entries", status: "complete" },
|
||||
@@ -147,7 +152,9 @@ describe("Apple application projection", () => {
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
const first = projectAppleApplicationEvidence({
|
||||
inventory_evidence: [inventory],
|
||||
@@ -216,7 +223,9 @@ describe("Apple application projection", () => {
|
||||
await writer.add("AndroidManifest.xml", new TextReader("<manifest/>"));
|
||||
await writeFile(path, await writer.close());
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
expect(
|
||||
projectAppleApplicationEvidence({ inventory_evidence: [inventory] }),
|
||||
@@ -250,7 +259,9 @@ async function projectArchive(
|
||||
);
|
||||
await writeFile(path, await writer.close());
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
return appleApplicationProjectionResultSchema.parse(
|
||||
requireSuccessfulProjection(
|
||||
@@ -352,7 +363,9 @@ describe("macOS application projection", () => {
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
const projection = appleApplicationProjectionResultSchema.parse(
|
||||
requireSuccessfulProjection(
|
||||
@@ -403,7 +416,9 @@ describe("macOS application projection", () => {
|
||||
);
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(app, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(app, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
const result = projectAppleApplicationEvidence({
|
||||
inventory_evidence: [inventory],
|
||||
@@ -476,7 +491,9 @@ describe("Apple application projection completeness", () => {
|
||||
await writeFile(path, await writer.close());
|
||||
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
const result = projectAppleApplicationEvidence({
|
||||
inventory_evidence: [inventory],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { readFile, symlink, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { TextReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js";
|
||||
@@ -48,7 +49,9 @@ it.skipIf(process.platform === "win32").each([
|
||||
expect(admitted.value.format).toBe(format);
|
||||
const { runProviderAnalysis } = createDirectAnalysis({});
|
||||
const observed = parseEvidence(
|
||||
await runProviderAnalysis(selected, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(selected, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
expect(observed.subject?.local_path).toBe(selected);
|
||||
expect(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { mkdir, open, readFile, truncate, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { createPackage } from "@electron/asar";
|
||||
@@ -293,7 +294,9 @@ it("applies the caller-selected environment, not the ambient one, to inventory a
|
||||
vi.stubEnv("REA_ASAR_MAX_HEADER_BYTES", "1");
|
||||
for (const operation of ["inventory_artifact", "inspect_artifact"] as const) {
|
||||
await expect(
|
||||
createDirectAnalysis(lowered).runProviderAnalysis(archive, operation, {}),
|
||||
createDirectAnalysis(lowered)
|
||||
.runProviderAnalysis(archive, operation, {})
|
||||
.then(projectCliAnalysisResult),
|
||||
).resolves.toMatchObject({
|
||||
code: "truncated",
|
||||
message: recovery,
|
||||
@@ -304,7 +307,9 @@ it("applies the caller-selected environment, not the ambient one, to inventory a
|
||||
},
|
||||
});
|
||||
await expect(
|
||||
createDirectAnalysis({}).runProviderAnalysis(archive, operation, {}),
|
||||
createDirectAnalysis({})
|
||||
.runProviderAnalysis(archive, operation, {})
|
||||
.then(projectCliAnalysisResult),
|
||||
).resolves.toMatchObject({ operation });
|
||||
}
|
||||
const refused = await analyzeJavaScriptApplication(
|
||||
@@ -365,11 +370,9 @@ it("refuses invalid settings at startup validation and at each owner", async ()
|
||||
const { archive } = await createFixture();
|
||||
const invalid = { REA_ASAR_MAX_HEADER_BYTES: "0" };
|
||||
await expect(
|
||||
createDirectAnalysis(invalid).runProviderAnalysis(
|
||||
archive,
|
||||
"inventory_artifact",
|
||||
{},
|
||||
),
|
||||
createDirectAnalysis(invalid)
|
||||
.runProviderAnalysis(archive, "inventory_artifact", {})
|
||||
.then(projectCliAnalysisResult),
|
||||
).resolves.toMatchObject({ code: "configuration_invalid" });
|
||||
const analyzed = await analyzeJavaScriptApplication(
|
||||
{ input_path: archive },
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { parseEvidence } from "../../../src/domain/evidence.js";
|
||||
import { parseBinaryTarget } from "../../../src/application/BinaryTargetResolver.js";
|
||||
import { createDirectAnalysis } from "../../../src/composition/directAnalysis.js";
|
||||
@@ -179,7 +180,9 @@ it.skipIf(process.platform === "win32")(
|
||||
await symlink(stored, selected);
|
||||
const { runProviderAnalysis } = createDirectAnalysis({});
|
||||
const observed = parseEvidence(
|
||||
await runProviderAnalysis(selected, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(selected, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
expect(observed.subject?.local_path).toBe(selected);
|
||||
const inventory = artifactInventoryResultSchema.parse(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { executeComposedWorkflow } from "../../../src/application/WorkflowEvidence.js";
|
||||
import { parseAnalysisSnapshot } from "../../../src/domain/analysisSnapshot.js";
|
||||
import { ok } from "../../../src/domain/result.js";
|
||||
@@ -24,7 +25,7 @@ it.each([0, 1, 12])(
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath },
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(current.evidence_links).toHaveLength(1);
|
||||
const snapshot = parseAnalysisSnapshot(
|
||||
@@ -42,7 +43,7 @@ it.each([0, 1, 12])(
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath },
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(unique.evidence_id).toBe(current.evidence_id);
|
||||
expect(unique.evidence_links).toEqual(current.evidence_links);
|
||||
@@ -77,7 +78,7 @@ it.each([0, 1, 12])(
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath },
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(delivered.normalized_result).toEqual(current.normalized_result);
|
||||
expect([current.evidence_id, historical.evidence_id]).not.toContain(
|
||||
@@ -98,7 +99,7 @@ it.each([0, 1, 12])(
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath },
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(refreshed.evidence_id).toBe(delivered.evidence_id);
|
||||
expect(refreshed.evidence_links).toEqual(delivered.evidence_links);
|
||||
@@ -134,7 +135,9 @@ it.each([false, true])(
|
||||
};
|
||||
try {
|
||||
const evidence = parseEvidence(
|
||||
await runDirectAnalysis(dependencies, path, "binary_overview", {}),
|
||||
await runDirectAnalysis(dependencies, path, "binary_overview", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
expect(evidence.normalized_result).toEqual(summary);
|
||||
expect(evidence.confidence).toBe("derived");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { chmod, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
@@ -39,14 +40,16 @@ describe("direct analysis snapshot files", () => {
|
||||
vi.stubEnv("REA_ANALYSIS_PROVIDER", "environment-provider");
|
||||
|
||||
await expect(
|
||||
createDirectAnalysis(process.env).runDirectAnalysis(
|
||||
targetPath,
|
||||
"binary_overview",
|
||||
{},
|
||||
{
|
||||
providerId: "request-provider",
|
||||
},
|
||||
),
|
||||
createDirectAnalysis(process.env)
|
||||
.runDirectAnalysis(
|
||||
targetPath,
|
||||
"binary_overview",
|
||||
{},
|
||||
{
|
||||
providerId: "request-provider",
|
||||
},
|
||||
)
|
||||
.then(projectCliAnalysisResult),
|
||||
).resolves.toMatchObject({
|
||||
error: "Analysis failed",
|
||||
code: "capability_unavailable",
|
||||
@@ -96,14 +99,16 @@ describe("direct analysis snapshot files", () => {
|
||||
vi.stubEnv("HOPPER_LAUNCHER_PATH", launcherPath);
|
||||
|
||||
await expect(
|
||||
createDirectAnalysis(process.env).runDirectAnalysis(
|
||||
process.execPath,
|
||||
"binary_overview",
|
||||
{},
|
||||
{
|
||||
snapshotPath,
|
||||
},
|
||||
),
|
||||
createDirectAnalysis(process.env)
|
||||
.runDirectAnalysis(
|
||||
process.execPath,
|
||||
"binary_overview",
|
||||
{},
|
||||
{
|
||||
snapshotPath,
|
||||
},
|
||||
)
|
||||
.then(projectCliAnalysisResult),
|
||||
).resolves.not.toEqual(evidence);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { parseConfig } from "../../../src/config/parseConfig.js";
|
||||
import { readFile, symlink, writeFile } from "node:fs/promises";
|
||||
import { basename, dirname, join } from "node:path";
|
||||
@@ -398,7 +399,7 @@ describe("snapshot output cleanup lifecycle", () => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(parseEvidence(output).operation).toBe("binary_overview");
|
||||
const saved = await readAnalysisSnapshot(fixture.snapshotPath);
|
||||
if (!saved.ok) throw saved.error;
|
||||
@@ -484,7 +485,7 @@ const verifyWindowsSnapshotAdmission = async (
|
||||
"search_strings",
|
||||
arguments_,
|
||||
options,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(first).toMatchObject({ operation: "search_strings" });
|
||||
expect(fixture.starts).toHaveLength(1);
|
||||
if (selectedPath === "junction")
|
||||
@@ -496,7 +497,7 @@ const verifyWindowsSnapshotAdmission = async (
|
||||
"search_strings",
|
||||
arguments_,
|
||||
options,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(second).toMatchObject({ error: "Analysis failed" });
|
||||
expect(fixture.starts).toHaveLength(2);
|
||||
expect(
|
||||
@@ -529,7 +530,7 @@ it.each([
|
||||
scenario.tool,
|
||||
scenario.arguments,
|
||||
options,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
const saved = await readAnalysisSnapshot(fixture.snapshotPath);
|
||||
if (!saved.ok) throw saved.error;
|
||||
@@ -556,7 +557,7 @@ it.each([
|
||||
scenario.tool,
|
||||
scenario.arguments,
|
||||
options,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(refreshed.limitations).toEqual(
|
||||
expect.arrayContaining(first.limitations),
|
||||
@@ -571,7 +572,7 @@ it.each([
|
||||
scenario.tool,
|
||||
scenario.arguments,
|
||||
options,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(replayed).toEqual(refreshed);
|
||||
expect(fixture.starts).toEqual(starts);
|
||||
expect(fixture.calls).toEqual(calls);
|
||||
@@ -613,7 +614,7 @@ describe("direct analysis composed snapshot replay", () => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(fixture.resolutions).toHaveLength(1);
|
||||
|
||||
const first = await runDirectAnalysis(
|
||||
@@ -622,7 +623,7 @@ describe("direct analysis composed snapshot replay", () => {
|
||||
scenario.tool,
|
||||
scenario.arguments,
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(first).toMatchObject({ operation: scenario.tool });
|
||||
expect(fixture.resolutions).toHaveLength(2);
|
||||
expect(fixture.starts).toHaveLength(2);
|
||||
@@ -634,7 +635,7 @@ describe("direct analysis composed snapshot replay", () => {
|
||||
scenario.tool,
|
||||
scenario.arguments,
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(replay).toEqual(first);
|
||||
expect(fixture.resolutions).toHaveLength(3);
|
||||
expect(fixture.starts).toHaveLength(2);
|
||||
@@ -675,7 +676,7 @@ describe("direct analysis composed snapshot replay", () => {
|
||||
scenario.tool,
|
||||
scenario.arguments,
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(first).toMatchObject({ operation: scenario.tool });
|
||||
const callsAfterFirst = [...fixture.calls];
|
||||
const startsAfterFirst = [...fixture.starts];
|
||||
@@ -693,7 +694,7 @@ describe("direct analysis composed snapshot replay", () => {
|
||||
scenario.tool,
|
||||
scenario.arguments,
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
|
||||
expect(second).toEqual(first);
|
||||
expect(fixture.calls).toEqual(callsAfterFirst);
|
||||
@@ -729,7 +730,7 @@ describe("Ghidra composed workflow snapshot replay", () => {
|
||||
tool,
|
||||
parameters,
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(first).toMatchObject({ operation: tool });
|
||||
const snapshot = await readAnalysisSnapshot(fixture.snapshotPath);
|
||||
if (!snapshot.ok) throw snapshot.error;
|
||||
@@ -744,7 +745,7 @@ describe("Ghidra composed workflow snapshot replay", () => {
|
||||
tool,
|
||||
parameters,
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(replay).toEqual(first);
|
||||
if (process.platform === "win32") {
|
||||
// The first provider call in a new CLI session admits its source.
|
||||
@@ -770,7 +771,7 @@ describe("binary overview snapshot replay", () => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(parseEvidence(first).caller_input_path).toBe(fixture.path);
|
||||
expect(fixture.calls.toSorted()).toEqual(operations.toSorted());
|
||||
expect(fixture.starts).toHaveLength(1);
|
||||
@@ -799,7 +800,7 @@ describe("binary overview snapshot replay", () => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(second).toEqual(first);
|
||||
expect(fixture.calls.toSorted()).toEqual(operations.toSorted());
|
||||
expect(fixture.starts).toHaveLength(1);
|
||||
@@ -812,7 +813,7 @@ describe("binary overview snapshot replay", () => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
const firstEvidence = parseEvidence(first);
|
||||
const relocatedEvidence = parseEvidence(relocated);
|
||||
expect(relocatedEvidence).toMatchObject({
|
||||
@@ -868,7 +869,7 @@ it.skipIf(process.platform === "win32")(
|
||||
"search_strings",
|
||||
parameters,
|
||||
options,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(first).toMatchObject({
|
||||
caller_input_path: alias,
|
||||
@@ -911,7 +912,7 @@ it.skipIf(process.platform === "win32")(
|
||||
"search_strings",
|
||||
parameters,
|
||||
options,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(replay).toMatchObject({
|
||||
evidence_id: first.evidence_id,
|
||||
@@ -953,7 +954,7 @@ it.skipIf(process.platform === "win32")(
|
||||
"search_strings",
|
||||
parameters,
|
||||
options,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(historicalReplay).toMatchObject({
|
||||
evidence_id: first.evidence_id,
|
||||
@@ -1028,14 +1029,14 @@ describe("preview and active route policies", () => {
|
||||
const first = parseEvidence(
|
||||
await runDirectAnalysis(dependencies, fixture.path, tool, arguments_, {
|
||||
snapshotPath: fixture.snapshotPath,
|
||||
}),
|
||||
}).then(projectCliAnalysisResult),
|
||||
);
|
||||
const starts = fixture.starts.length;
|
||||
const queries = fixture.queries.length;
|
||||
const second = parseEvidence(
|
||||
await runDirectAnalysis(dependencies, fixture.path, tool, arguments_, {
|
||||
snapshotPath: fixture.snapshotPath,
|
||||
}),
|
||||
}).then(projectCliAnalysisResult),
|
||||
);
|
||||
if (replayed) expect(second).toEqual(first);
|
||||
expect(second).toMatchObject({
|
||||
@@ -1069,7 +1070,7 @@ describe("snapshot replay provider identity", () => {
|
||||
"search_strings",
|
||||
{ pattern: "fixture", document: "fixture" },
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
const callsAfterFirst = [...fixture.calls];
|
||||
const startsAfterFirst = [...fixture.starts];
|
||||
const firstSnapshot = await readAnalysisSnapshot(fixture.snapshotPath);
|
||||
@@ -1084,7 +1085,7 @@ describe("snapshot replay provider identity", () => {
|
||||
"search_strings",
|
||||
{ pattern: "fixture", document: "fixture" },
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(fixture.calls).toEqual(callsAfterFirst);
|
||||
expect(fixture.starts).toEqual(startsAfterFirst);
|
||||
expect(second).toEqual(first);
|
||||
@@ -1103,7 +1104,7 @@ describe("snapshot replay provider identity", () => {
|
||||
"search_strings",
|
||||
{ pattern: "fixture", document: "fixture" },
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(changedProfileResult).not.toEqual(first);
|
||||
expect(changedProfileResult).toMatchObject({
|
||||
code: "evidence_integrity_mismatch",
|
||||
@@ -1130,7 +1131,7 @@ describe("snapshot replay provider identity", () => {
|
||||
"search_strings",
|
||||
{ pattern: "fixture", document: "fixture" },
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(changedProviderResult).not.toEqual(first);
|
||||
expect(changedProviderResult).toMatchObject({
|
||||
code: "evidence_integrity_mismatch",
|
||||
@@ -1154,7 +1155,7 @@ const assertEvidenceSurvivesSnapshotSaveFailure = async (): Promise<void> => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
code: "invalid_request",
|
||||
@@ -1189,7 +1190,7 @@ describe("workflow snapshot profile and cancellation binding", () => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
const loaded = await readAnalysisSnapshot(fixture.snapshotPath);
|
||||
if (!loaded.ok) throw loaded.error;
|
||||
const current = loaded.value.evidence_bundle.records.find(
|
||||
@@ -1213,7 +1214,7 @@ describe("workflow snapshot profile and cancellation binding", () => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(refreshed).toMatchObject({
|
||||
normalized_result: { document: "fixture" },
|
||||
});
|
||||
@@ -1233,7 +1234,7 @@ describe("workflow snapshot profile and cancellation binding", () => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
|
||||
const controller = new AbortController();
|
||||
const router = createTestProviderRouter(provider);
|
||||
@@ -1258,7 +1259,7 @@ describe("workflow snapshot profile and cancellation binding", () => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath: fixture.snapshotPath, signal: controller.signal },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
error: "Analysis failed",
|
||||
@@ -1493,7 +1494,7 @@ it("retains a native dispatch workflow snapshot without a deep provider profile"
|
||||
"inspect_native_dispatch_metadata",
|
||||
{ max_records: 100 },
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
operation: "inspect_native_dispatch_metadata",
|
||||
@@ -1514,7 +1515,7 @@ it("retains a native dispatch workflow snapshot without a deep provider profile"
|
||||
"inspect_native_dispatch_metadata",
|
||||
{ max_records: 100 },
|
||||
{ snapshotPath: fixture.snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(replay).toEqual(result);
|
||||
expect(dispatchCalls).toBe(1);
|
||||
|
||||
|
||||
@@ -61,6 +61,27 @@ const bundle = (result = true) =>
|
||||
),
|
||||
]);
|
||||
|
||||
it("preserves tagged refusal JSON without interpreting Evidence-shaped fields", async () => {
|
||||
const root = await createTestTempDirectory("rea-refused-delivery-");
|
||||
const refusal = {
|
||||
code: "unreadable_output",
|
||||
evidence_id: "caller-selected diagnostic",
|
||||
normalized_result: { partial: true },
|
||||
};
|
||||
const delivered = await publishCliAnalysisEvidence(
|
||||
{ ok: false, error: refusal },
|
||||
{
|
||||
providerId: "fixture",
|
||||
operation: "inspect_artifact",
|
||||
signal: new AbortController().signal,
|
||||
detail: "summary",
|
||||
evidenceOutput: join(root, "refused.json"),
|
||||
},
|
||||
);
|
||||
expect(delivered).toBe(refusal);
|
||||
expect(await readdir(root)).toEqual([]);
|
||||
});
|
||||
|
||||
it("preserves direct duplicate rejection and CLI identity merging for repeated inspection sources", async () => {
|
||||
const root = await createTestTempDirectory("rea-repeated-delivery-");
|
||||
const input = join(root, "input.txt");
|
||||
@@ -91,14 +112,17 @@ it("preserves direct duplicate rejection and CLI identity merging for repeated i
|
||||
deliverAnalysisEvidence(complete, join(root, "direct.json")),
|
||||
).rejects.toBeInstanceOf(DuplicateEvidenceRecordError);
|
||||
const path = join(root, "cli.json");
|
||||
const delivered = await publishCliAnalysisEvidence(complete, {
|
||||
providerId: "fixture",
|
||||
operation: "inspect_artifact",
|
||||
signal: new AbortController().signal,
|
||||
detail: "complete",
|
||||
evidenceOutput: path,
|
||||
sourceEvidence: [withEvidenceCallerInputPath(complete, "./old"), source],
|
||||
});
|
||||
const delivered = await publishCliAnalysisEvidence(
|
||||
{ ok: true, value: complete },
|
||||
{
|
||||
providerId: "fixture",
|
||||
operation: "inspect_artifact",
|
||||
signal: new AbortController().signal,
|
||||
detail: "complete",
|
||||
evidenceOutput: path,
|
||||
sourceEvidence: [withEvidenceCallerInputPath(complete, "./old"), source],
|
||||
},
|
||||
);
|
||||
expect(delivered).toEqual(complete);
|
||||
const saved = await readEvidenceBundle(path);
|
||||
if (!saved.ok) throw saved.error;
|
||||
@@ -126,13 +150,16 @@ it("retains a malformed artifact parent and refuses publication through both del
|
||||
);
|
||||
if (direct.ok) throw new Error("Expected invalid artifact closure");
|
||||
const projected = projectAnalysisError(direct.error);
|
||||
const cli = await publishCliAnalysisEvidence(evidence, {
|
||||
providerId: "fixture",
|
||||
operation: "inspect_artifact",
|
||||
signal: new AbortController().signal,
|
||||
detail: "complete",
|
||||
evidenceOutput: join(root, "cli.json"),
|
||||
});
|
||||
const cli = await publishCliAnalysisEvidence(
|
||||
{ ok: true, value: evidence },
|
||||
{
|
||||
providerId: "fixture",
|
||||
operation: "inspect_artifact",
|
||||
signal: new AbortController().signal,
|
||||
detail: "complete",
|
||||
evidenceOutput: join(root, "cli.json"),
|
||||
},
|
||||
);
|
||||
expect(cli).toEqual(projected);
|
||||
expect(JSON.stringify(projected)).toContain(evidence.evidence_id);
|
||||
expect(await readdir(root)).toEqual([]);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { createReadStream } from "node:fs";
|
||||
import {
|
||||
open,
|
||||
@@ -781,7 +782,7 @@ it("retains failed snapshot cleanup and retries it on close", async () => {
|
||||
"flutter",
|
||||
"identify_flutter_build",
|
||||
new AbortController().signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
code: "cleanup_incomplete",
|
||||
details: {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { TextReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js";
|
||||
@@ -299,7 +300,11 @@ describe("HarmonyOS packaging boundaries", () => {
|
||||
path,
|
||||
JSON.stringify({ log: { version: "1.2", creator: {}, entries: [] } }),
|
||||
);
|
||||
const result = await runProviderAnalysis(path, "inventory_artifact", {});
|
||||
const result = await runProviderAnalysis(
|
||||
path,
|
||||
"inventory_artifact",
|
||||
{},
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
error: "Analysis failed",
|
||||
code: "target_unavailable",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { createHash } from "node:crypto";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
@@ -177,7 +178,7 @@ it("retains cancelled source read ownership until service close retries it", asy
|
||||
"web-module-artifacts",
|
||||
"trace_web_module_imports",
|
||||
new AbortController().signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(finalized).toMatchObject({
|
||||
code: "cleanup_incomplete",
|
||||
details: { resources: [sourcePath] },
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
||||
import { expect, it } from "vitest";
|
||||
import { createServer } from "../../../src/server/createServer.js";
|
||||
@@ -9,7 +10,12 @@ import { composedWorkflowFixture } from "../../fixtures/composedWorkflowEvidence
|
||||
it("returns the same consumed atomic provenance through CLI application and MCP boundaries", async () => {
|
||||
const cli = await composedWorkflowFixture();
|
||||
const direct = parseEvidence(
|
||||
await runDirectAnalysis(cli.dependencies, cli.path, "binary_overview", {}),
|
||||
await runDirectAnalysis(
|
||||
cli.dependencies,
|
||||
cli.path,
|
||||
"binary_overview",
|
||||
{},
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
const fixture = await composedWorkflowFixture();
|
||||
const server = createServer(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
@@ -86,7 +87,11 @@ it("advertises and executes Android and Apple inventory projections", async () =
|
||||
|
||||
const root = await createTestTempDirectory("rea-mobile-graph-mcp-");
|
||||
const apkInventory = parseEvidence(
|
||||
await runProviderAnalysis(await writeApk(root), "inventory_artifact", {}),
|
||||
await runProviderAnalysis(
|
||||
await writeApk(root),
|
||||
"inventory_artifact",
|
||||
{},
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
const apkSources = fragmentInventoryEvidence(apkInventory, 2).toReversed();
|
||||
const android = await client.callTool({
|
||||
@@ -135,7 +140,11 @@ it("advertises and executes Android and Apple inventory projections", async () =
|
||||
);
|
||||
|
||||
const ipaInventory = parseEvidence(
|
||||
await runProviderAnalysis(await writeIpa(root), "inventory_artifact", {}),
|
||||
await runProviderAnalysis(
|
||||
await writeIpa(root),
|
||||
"inventory_artifact",
|
||||
{},
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
const apple = await client.callTool({
|
||||
name: "project_apple_application_graph",
|
||||
@@ -152,7 +161,11 @@ it("projects a single inventory page as partial and links only that source", asy
|
||||
const client = await connect();
|
||||
const root = await createTestTempDirectory("rea-mobile-partial-page-mcp-");
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(await writeApk(root), "inventory_artifact", {}),
|
||||
await runProviderAnalysis(
|
||||
await writeApk(root),
|
||||
"inventory_artifact",
|
||||
{},
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
const facts = artifactInventoryResultSchema.parse(
|
||||
inventory.normalized_result,
|
||||
@@ -271,10 +284,18 @@ it("rejects authentic pages from different roots without retaining either", asyn
|
||||
const client = await connect();
|
||||
const root = await createTestTempDirectory("rea-mobile-mixed-pages-mcp-");
|
||||
const apk = parseEvidence(
|
||||
await runProviderAnalysis(await writeApk(root), "inventory_artifact", {}),
|
||||
await runProviderAnalysis(
|
||||
await writeApk(root),
|
||||
"inventory_artifact",
|
||||
{},
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
const ipa = parseEvidence(
|
||||
await runProviderAnalysis(await writeIpa(root), "inventory_artifact", {}),
|
||||
await runProviderAnalysis(
|
||||
await writeIpa(root),
|
||||
"inventory_artifact",
|
||||
{},
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
|
||||
const rejected = await client.callTool({
|
||||
@@ -337,7 +358,9 @@ it.each([
|
||||
});
|
||||
await writeFile(path, await writer.close());
|
||||
const inventory = parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
const reply = await client.callTool({
|
||||
name: operation,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { parseConfig } from "../../../src/config/parseConfig.js";
|
||||
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
||||
import { describe, expect, it, onTestFinished } from "vitest";
|
||||
@@ -86,7 +87,9 @@ describe("official operation arguments", () => {
|
||||
],
|
||||
] as const) {
|
||||
const cli = parseEvidence(
|
||||
await runDirectAnalysis(dependencies, path, name, cliParameters),
|
||||
await runDirectAnalysis(dependencies, path, name, cliParameters).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
const mcp = await client.callTool({ name, arguments: mcpParameters });
|
||||
expect(mcp.isError).not.toBe(true);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { parseConfig } from "../../../src/config/parseConfig.js";
|
||||
import { access, chmod, readFile, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
@@ -341,7 +342,7 @@ describe("MCP composed workflow snapshot replay", () => {
|
||||
"binary_overview",
|
||||
{},
|
||||
{ snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
|
||||
expect(output).not.toHaveProperty("caller_input_path");
|
||||
expect(replay).toEqual({ ...output, caller_input_path: targetPath });
|
||||
@@ -642,9 +643,7 @@ const makeProvider = (
|
||||
]
|
||||
: operation === "list_documents"
|
||||
? ["fixture"]
|
||||
: operation === "list_procedures"
|
||||
? ["0x1000"]
|
||||
: [{ address: "0x1000", value: "fixture" }];
|
||||
: [{ address: "0x1000", value: "fixture" }];
|
||||
return ok(createAnalysisExecution(result, identity));
|
||||
},
|
||||
close: () => Promise.resolve(ok(null)),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
@@ -59,7 +60,7 @@ it("routes a CLI app resource path through the resource session and preserves nu
|
||||
bundlePath,
|
||||
"inspect_keyed_archive",
|
||||
{ path: "Resources/Archive.plist" },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
operation: "inspect_keyed_archive",
|
||||
subject: null,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
@@ -18,7 +19,9 @@ describe("managed direct-analysis path boundary", () => {
|
||||
await writeFile(path, buildManagedPeFixture());
|
||||
|
||||
const evidence = parseEvidence(
|
||||
await runProviderAnalysis(path, "inspect_managed_artifact", {}),
|
||||
await runProviderAnalysis(path, "inspect_managed_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
expect(evidence).toMatchObject({
|
||||
operation: "inspect_managed_artifact",
|
||||
@@ -31,7 +34,9 @@ describe("managed direct-analysis path boundary", () => {
|
||||
});
|
||||
|
||||
const memberEvidence = parseEvidence(
|
||||
await runProviderAnalysis(path, "inspect_managed_members", {}),
|
||||
await runProviderAnalysis(path, "inspect_managed_members", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
expect(memberEvidence).toMatchObject({
|
||||
operation: "inspect_managed_members",
|
||||
@@ -44,7 +49,11 @@ describe("managed direct-analysis path boundary", () => {
|
||||
});
|
||||
|
||||
const boundaryEvidence = parseEvidence(
|
||||
await runProviderAnalysis(path, "inspect_managed_native_boundaries", {}),
|
||||
await runProviderAnalysis(
|
||||
path,
|
||||
"inspect_managed_native_boundaries",
|
||||
{},
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(boundaryEvidence).toMatchObject({
|
||||
operation: "inspect_managed_native_boundaries",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
@@ -34,7 +35,9 @@ describe("artifact archive inventory", () => {
|
||||
zipResult.occurrences.map(({ artifact_kind }) => artifact_kind),
|
||||
).toEqual(expect.arrayContaining(["javascript", "dynamic-library"]));
|
||||
const cliEvidence = parseEvidence(
|
||||
await runProviderAnalysis(zipPath, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(zipPath, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
expect(cliEvidence).toMatchObject({
|
||||
operation: "inventory_artifact",
|
||||
@@ -42,7 +45,9 @@ describe("artifact archive inventory", () => {
|
||||
subject: { format: "apk" },
|
||||
});
|
||||
const inspectionEvidence = parseEvidence(
|
||||
await runProviderAnalysis(zipPath, "inspect_artifact", {}),
|
||||
await runProviderAnalysis(zipPath, "inspect_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
const inspection = artifactInspectionResultSchema.parse(
|
||||
inspectionEvidence.normalized_result,
|
||||
@@ -81,7 +86,7 @@ describe("artifact archive inventory", () => {
|
||||
{},
|
||||
undefined,
|
||||
cancellation.signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
).resolves.toMatchObject({ code: "cancelled" });
|
||||
await expect(
|
||||
runProviderAnalysis(
|
||||
@@ -90,7 +95,7 @@ describe("artifact archive inventory", () => {
|
||||
{},
|
||||
undefined,
|
||||
cancellation.signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
).resolves.toMatchObject({ code: "cancelled" });
|
||||
|
||||
const source = join(root, "asar-source");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { lstat, readFile, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
@@ -69,7 +70,9 @@ describe("artifact archive safety", () => {
|
||||
);
|
||||
expect(
|
||||
parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
),
|
||||
).toMatchObject({
|
||||
operation: "inventory_artifact",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
@@ -42,7 +43,9 @@ it("runs static .app decoding without registering native analysis engines", asyn
|
||||
const evidence = parseEvidence(
|
||||
await createDirectAnalysis({
|
||||
REA_ANALYSIS_PROVIDER: "auto",
|
||||
}).runProviderAnalysis(app, "decode_interface_builder", {}),
|
||||
})
|
||||
.runProviderAnalysis(app, "decode_interface_builder", {})
|
||||
.then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(evidence.operation).toBe("decode_interface_builder");
|
||||
expect(evidence.provider.id).toBe("rea-artifact-graph");
|
||||
@@ -64,7 +67,9 @@ it("inspects an artifact through the direct CLI workflow without an REA grant",
|
||||
await createPackageWithOptions(source, archive, {});
|
||||
|
||||
const evidence = parseEvidence(
|
||||
await runProviderAnalysis(archive, "inspect_artifact", {}),
|
||||
await runProviderAnalysis(archive, "inspect_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
expect(evidence.operation).toBe("inspect_artifact");
|
||||
expect(evidence.provider.id).toBe("rea-artifact-graph");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../../../src/cli/projectCliAnalysisResult.js";
|
||||
import { mkdir, symlink, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
@@ -32,7 +33,9 @@ const writeTree = async (
|
||||
|
||||
const probeOperations = async (path: string): Promise<string[]> => {
|
||||
const evidence = parseEvidence(
|
||||
await runProviderAnalysis(path, "inspect_artifact", {}),
|
||||
await runProviderAnalysis(path, "inspect_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
const inspection = artifactInspectionResultSchema.parse(
|
||||
evidence.normalized_result,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../src/cli/projectCliAnalysisResult.js";
|
||||
import {
|
||||
mkdir,
|
||||
open,
|
||||
@@ -163,7 +164,7 @@ it.each([false, true])(
|
||||
{},
|
||||
undefined,
|
||||
controller.signal,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(result.code).toBe(closeFails ? "cleanup_incomplete" : "cancelled");
|
||||
expect(result.category).toBe("cancelled");
|
||||
@@ -214,7 +215,7 @@ describe("one-shot finalization cancellation", () => {
|
||||
path,
|
||||
tool,
|
||||
parameters,
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
const controller = new AbortController();
|
||||
const reason = { request: "cancel during native close" };
|
||||
@@ -227,7 +228,7 @@ describe("one-shot finalization cancellation", () => {
|
||||
tool,
|
||||
parameters,
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toEqual({
|
||||
error: "Analysis failed",
|
||||
...projectAnalysisError(
|
||||
@@ -257,7 +258,7 @@ describe("one-shot finalization cancellation", () => {
|
||||
"read_bytes",
|
||||
{},
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
code: closeFails ? "cleanup_incomplete" : "provider_timeout",
|
||||
message: projectAnalysisError(primaryError).message,
|
||||
@@ -284,7 +285,7 @@ describe("one-shot finalization cancellation", () => {
|
||||
"read_bytes",
|
||||
{},
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
code: "cleanup_incomplete",
|
||||
category: "cancelled",
|
||||
@@ -341,7 +342,7 @@ describe("snapshot publication finalization", () => {
|
||||
"read_bytes",
|
||||
{},
|
||||
{ signal: controller.signal, snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
const loaded = await readAnalysisSnapshot(snapshotPath);
|
||||
if (!loaded.ok) throw loaded.error;
|
||||
const evidence = loaded.value.evidence_bundle.records.find(
|
||||
@@ -403,7 +404,7 @@ describe("snapshot publication finalization", () => {
|
||||
"read_bytes",
|
||||
{},
|
||||
{ signal: controller.signal, snapshotPath: failure.path },
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(result.code).toBe(projectAnalysisError(failure).code);
|
||||
expect(result.details).toMatchObject({
|
||||
@@ -428,7 +429,7 @@ describe("one-shot analysis cleanup outcomes", () => {
|
||||
path,
|
||||
"read_bytes",
|
||||
{},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
error: "Analysis failed",
|
||||
code: "cleanup_incomplete",
|
||||
@@ -458,7 +459,7 @@ describe("one-shot analysis cleanup outcomes", () => {
|
||||
path,
|
||||
"read_bytes",
|
||||
{},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
const callerError = analysisCliErrorEnvelopeSchema.parse(result);
|
||||
expect(callerError).toMatchObject({
|
||||
code: "cleanup_incomplete",
|
||||
@@ -490,7 +491,7 @@ describe("one-shot analysis cleanup outcomes", () => {
|
||||
path,
|
||||
"read_bytes",
|
||||
{},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
code: "cleanup_incomplete",
|
||||
details: {
|
||||
@@ -520,7 +521,9 @@ describe("one-shot analysis cleanup outcomes", () => {
|
||||
...(closeFails ? { cleanupError } : {}),
|
||||
});
|
||||
const result = analysisCliErrorEnvelopeSchema.parse(
|
||||
await runDirectAnalysis(dependencies, path, "read_bytes", {}),
|
||||
await runDirectAnalysis(dependencies, path, "read_bytes", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
code: "cleanup_incomplete",
|
||||
@@ -544,7 +547,9 @@ describe("one-shot analysis cleanup outcomes", () => {
|
||||
const thrownExecution = new Error("unexpected execution rejection");
|
||||
const { dependencies } = factories({ thrownExecution, cleanupError });
|
||||
await expect(
|
||||
runDirectAnalysis(dependencies, path, "read_bytes", {}),
|
||||
runDirectAnalysis(dependencies, path, "read_bytes", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
).rejects.toMatchObject({
|
||||
cause: thrownExecution,
|
||||
errors: [thrownExecution, cleanupError],
|
||||
@@ -559,7 +564,7 @@ describe("one-shot analysis cleanup outcomes", () => {
|
||||
path,
|
||||
"read_bytes",
|
||||
{},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(parseEvidence(result)).toMatchObject({
|
||||
operation: "read_bytes",
|
||||
normalized_result: { observation: "retained result" },
|
||||
@@ -580,7 +585,7 @@ it.each(["complete", "summary"] as const)(
|
||||
path,
|
||||
"inspect_managed_members",
|
||||
{},
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
);
|
||||
const controller = new AbortController();
|
||||
const reason = new Error("cancel during managed close");
|
||||
@@ -600,7 +605,7 @@ it.each(["complete", "summary"] as const)(
|
||||
detail,
|
||||
evidenceBundlePath: join(dirname(path), "bundle.json"),
|
||||
},
|
||||
)
|
||||
).then(projectCliAnalysisResult)
|
||||
: await runProviderAnalysis(
|
||||
dependencies,
|
||||
path,
|
||||
@@ -608,7 +613,7 @@ it.each(["complete", "summary"] as const)(
|
||||
{},
|
||||
undefined,
|
||||
controller.signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toEqual({
|
||||
error: "Analysis failed",
|
||||
...projectAnalysisError(
|
||||
@@ -640,7 +645,7 @@ it("preserves a managed provider failure when cancellation arrives during close"
|
||||
{},
|
||||
undefined,
|
||||
controller.signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toEqual({
|
||||
error: "Analysis failed",
|
||||
...projectAnalysisError(failure),
|
||||
@@ -727,7 +732,7 @@ describe("managed one-shot cleanup outcomes", () => {
|
||||
path,
|
||||
"inspect_managed_artifact",
|
||||
{},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(isCliOperationFailure(result)).toBe(true);
|
||||
expect(result).toMatchObject({
|
||||
error: "Analysis failed",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../src/cli/projectCliAnalysisResult.js";
|
||||
import { EvidenceFileError } from "../../src/domain/evidenceErrors.js";
|
||||
import { err, ok as resultOk } from "../../src/domain/result.js";
|
||||
import { dirname, join } from "node:path";
|
||||
@@ -68,7 +69,7 @@ it("requires a durable summary destination before acquiring a managed session",
|
||||
undefined,
|
||||
undefined,
|
||||
{ detail: "summary" },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
code: "invalid_request",
|
||||
details: {
|
||||
@@ -97,7 +98,9 @@ it.each([
|
||||
{ detail: "summary", evidenceBundlePath: "/fixture/bundle.json" },
|
||||
] as const;
|
||||
// @ts-expect-error JavaScript callers can bypass managed-members-only delivery.
|
||||
const result = await runProviderAnalysis(...invocation);
|
||||
const result = await runProviderAnalysis(...invocation).then(
|
||||
projectCliAnalysisResult,
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
code: "invalid_request",
|
||||
details: {
|
||||
@@ -117,7 +120,9 @@ describe("one-shot analysis factory boundary", () => {
|
||||
const interruptListeners = process.listenerCount("SIGINT");
|
||||
const terminateListeners = process.listenerCount("SIGTERM");
|
||||
await expect(
|
||||
runDirectAnalysis(factories.dependencies, path, "read_bytes", {}),
|
||||
runDirectAnalysis(factories.dependencies, path, "read_bytes", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
).rejects.toBe(failure);
|
||||
expect(factories.clientsClosed()).toBe(1);
|
||||
expect(process.listenerCount("SIGINT")).toBe(interruptListeners);
|
||||
@@ -136,7 +141,7 @@ describe("one-shot analysis factory boundary", () => {
|
||||
"read_bytes",
|
||||
{},
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
error: "Analysis failed",
|
||||
code: "cancelled",
|
||||
@@ -194,7 +199,7 @@ describe("one-shot analysis factory boundary", () => {
|
||||
"inspect_native_dispatch_metadata",
|
||||
{},
|
||||
{ snapshotPath },
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
).rejects.toBe(expected);
|
||||
expect(factories.sessions).toEqual([]);
|
||||
},
|
||||
@@ -227,7 +232,7 @@ describe("one-shot analysis factory boundary", () => {
|
||||
path,
|
||||
tool,
|
||||
arguments_,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
operation: tool,
|
||||
normalized_result: { operation: tool },
|
||||
@@ -256,7 +261,7 @@ describe("one-shot analysis factory boundary", () => {
|
||||
path,
|
||||
tool,
|
||||
{},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({ operation: tool });
|
||||
expect(factories.clientsClosed()).toBe(1);
|
||||
},
|
||||
@@ -279,7 +284,7 @@ it("preserves a managed execution profile in direct analysis Evidence", async ()
|
||||
path,
|
||||
"inspect_managed_artifact",
|
||||
{},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(evidence).toMatchObject({ analysis_profile: profile });
|
||||
});
|
||||
|
||||
@@ -298,7 +303,7 @@ it("direct analysis snapshot target mismatch retains actionable snapshot recover
|
||||
"read_bytes",
|
||||
{},
|
||||
{ snapshotPath },
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
).not.toHaveProperty("code");
|
||||
const mismatch = await runDirectAnalysis(
|
||||
factories.dependencies,
|
||||
@@ -306,7 +311,7 @@ it("direct analysis snapshot target mismatch retains actionable snapshot recover
|
||||
"read_bytes",
|
||||
{},
|
||||
{ snapshotPath },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(mismatch).toMatchObject({
|
||||
code: "evidence_integrity_mismatch",
|
||||
remediation: {
|
||||
@@ -321,7 +326,7 @@ it("direct analysis snapshot target mismatch retains actionable snapshot recover
|
||||
"read_bytes",
|
||||
{},
|
||||
{ snapshotPath },
|
||||
),
|
||||
).then(projectCliAnalysisResult),
|
||||
).not.toHaveProperty("code");
|
||||
});
|
||||
|
||||
@@ -362,7 +367,7 @@ it("retries snapshot file cleanup before final close even when a published ident
|
||||
"read_bytes",
|
||||
{},
|
||||
{ snapshotPath: join(dirname(path), "excluded.snapshot") },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(events[0]).toBe("file-cleanup");
|
||||
expect(events).toContain("session-close");
|
||||
expect(result).toMatchObject({ details: { published: true, bytes: 42 } });
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../src/cli/projectCliAnalysisResult.js";
|
||||
import { expect, it } from "vitest";
|
||||
import { parseConfig } from "../../src/config/parseConfig.js";
|
||||
import { runDirectAnalysis } from "../../src/application/DirectAnalysis.js";
|
||||
@@ -42,7 +43,7 @@ it("retains provider coverage caveats in a composed CLI result and closes the se
|
||||
path,
|
||||
"binary_overview",
|
||||
{},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(output).toMatchObject({
|
||||
limitations: [
|
||||
"Derived by an REA composed workflow.",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../src/cli/projectCliAnalysisResult.js";
|
||||
import { parseConfig } from "../../src/config/parseConfig.js";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { runDirectAnalysis } from "../../src/application/DirectAnalysis.js";
|
||||
@@ -114,7 +115,7 @@ describe("direct CLI cross-reference selectors", () => {
|
||||
path,
|
||||
"xrefs",
|
||||
{ address: "table_only", extra: true },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
code: "invalid_request",
|
||||
details: { operation: "xrefs", issues },
|
||||
@@ -132,7 +133,7 @@ describe("direct CLI cross-reference selectors", () => {
|
||||
const evidence = parseEvidence(
|
||||
await runDirectAnalysis(provider.dependencies, path, "xrefs", {
|
||||
address,
|
||||
}),
|
||||
}).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(evidence.parameters).toEqual({ address });
|
||||
expect(evidence.normalized_result).toEqual(["0x4000"]);
|
||||
@@ -160,7 +161,7 @@ describe("direct CLI cross-reference selectors", () => {
|
||||
const evidence = parseEvidence(
|
||||
await runDirectAnalysis(provider.dependencies, path, "xrefs", {
|
||||
address,
|
||||
}),
|
||||
}).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(evidence.parameters).toEqual({ address });
|
||||
expect(evidence.normalized_result).toEqual(["0x4000"]);
|
||||
@@ -178,7 +179,7 @@ describe("direct CLI cross-reference name resolution", () => {
|
||||
const evidence = parseEvidence(
|
||||
await runDirectAnalysis(provider.dependencies, path, "xrefs", {
|
||||
address,
|
||||
}),
|
||||
}).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(evidence.parameters).toEqual({ address });
|
||||
expect(evidence.normalized_result).toEqual(["0x4000"]);
|
||||
@@ -197,7 +198,7 @@ describe("direct CLI cross-reference name resolution", () => {
|
||||
const empty = parseEvidence(
|
||||
await runDirectAnalysis(provider.dependencies, path, "xrefs", {
|
||||
address: "unreferenced",
|
||||
}),
|
||||
}).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(empty.normalized_result).toEqual([]);
|
||||
const missing = await runDirectAnalysis(
|
||||
@@ -207,7 +208,7 @@ describe("direct CLI cross-reference name resolution", () => {
|
||||
{
|
||||
address: "missing_symbol",
|
||||
},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(missing).toMatchObject({
|
||||
code: "invalid_request",
|
||||
details: { operation: "xrefs", issues: [{ path: ["address"] }] },
|
||||
@@ -226,7 +227,7 @@ describe("direct CLI cross-reference name resolution", () => {
|
||||
{
|
||||
address: "ambiguous",
|
||||
},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({ code: "invalid_request" });
|
||||
expect(JSON.stringify(result)).toContain("0x1000");
|
||||
expect(JSON.stringify(result)).toContain("0x2000");
|
||||
@@ -245,7 +246,7 @@ describe("direct CLI cross-reference name resolution", () => {
|
||||
const evidence = parseEvidence(
|
||||
await runDirectAnalysis(provider.dependencies, path, "xrefs", {
|
||||
address: "table_only",
|
||||
}),
|
||||
}).then(projectCliAnalysisResult),
|
||||
);
|
||||
expect(evidence.parameters).toEqual({ address: "table_only" });
|
||||
expect(evidence.normalized_result).toEqual(["0x4000"]);
|
||||
@@ -270,7 +271,7 @@ describe("direct CLI cross-reference name resolution", () => {
|
||||
{
|
||||
address: "table_only",
|
||||
},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({ code });
|
||||
expect(provider.closed()).toBe(1);
|
||||
},
|
||||
|
||||
+3
-2
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../dist/cli/projectCliAnalysisResult.js";
|
||||
import { parseConfig } from "../../dist/config/parseConfig.js";
|
||||
import { access, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
@@ -97,13 +98,13 @@ const result =
|
||||
? await runDirectAnalysis(dependencies, target, "read_bytes", {
|
||||
address: "0x0",
|
||||
length: 1,
|
||||
})
|
||||
}).then(projectCliAnalysisResult)
|
||||
: await runProviderAnalysis(
|
||||
dependencies,
|
||||
target,
|
||||
mode === "managed" ? "inspect_managed_artifact" : "procedure_address",
|
||||
mode === "managed" ? {} : { procedure: "fixture" },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
clearInterval(keepAlive);
|
||||
if (
|
||||
process.listenerCount("SIGINT") !== interruptListeners ||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../src/cli/projectCliAnalysisResult.js";
|
||||
import { afterEach, expect, it, vi } from "vitest";
|
||||
|
||||
const statFailures = vi.hoisted(() => new Map<string, unknown>());
|
||||
@@ -69,7 +70,7 @@ it.each([
|
||||
path,
|
||||
"inspect_keyed_archive",
|
||||
{ path: "Resources/Archive.plist" },
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
|
||||
expect(output).toMatchObject({
|
||||
code: projectedCode,
|
||||
@@ -108,7 +109,7 @@ it("rethrows an unclassified stat failure instead of treating it as a file", asy
|
||||
await expect(
|
||||
runProviderAnalysis(dependencies, path, "inspect_keyed_archive", {
|
||||
path: "Resources/Archive.plist",
|
||||
}),
|
||||
}).then(projectCliAnalysisResult),
|
||||
).rejects.toBe(cause);
|
||||
expect(session.open).not.toHaveBeenCalled();
|
||||
expect(session.openBundleResources).not.toHaveBeenCalled();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../src/cli/projectCliAnalysisResult.js";
|
||||
import { access, readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, expect, it, vi } from "vitest";
|
||||
@@ -347,7 +348,7 @@ test.each([false, true])(
|
||||
"binwalk",
|
||||
"inspect_firmware_regions",
|
||||
new AbortController().signal,
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(delivered).toMatchObject({ code: "cleanup_incomplete" });
|
||||
expect(JSON.stringify(delivered).split("binwalk 3.1.0")).toHaveLength(2);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../src/cli/projectCliAnalysisResult.js";
|
||||
import { getHeapStatistics } from "node:v8";
|
||||
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
||||
import { createServer } from "../../src/server/createServer.js";
|
||||
@@ -324,12 +325,17 @@ it("projects a rejected managed parent by reference without cloning under low he
|
||||
detail: "summary",
|
||||
evidenceBundlePath: join(root, "parent.json"),
|
||||
},
|
||||
);
|
||||
).then(projectCliAnalysisResult);
|
||||
expect(result).toMatchObject({
|
||||
code: "resource_constraint",
|
||||
details: { operation: "inspect_managed_members" },
|
||||
});
|
||||
if (typeof result !== "object" || result === null || Array.isArray(result))
|
||||
if (
|
||||
typeof result !== "object" ||
|
||||
result === null ||
|
||||
Array.isArray(result) ||
|
||||
!("details" in result)
|
||||
)
|
||||
throw new Error("Expected CLI error");
|
||||
const details = result.details;
|
||||
if (typeof details !== "object" || details === null || Array.isArray(details))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { projectCliAnalysisResult } from "../../src/cli/projectCliAnalysisResult.js";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
@@ -53,7 +54,11 @@ export const writeApplicationZip = async (
|
||||
export const inventoryApplicationPackage = async (
|
||||
path: string,
|
||||
): Promise<Evidence> =>
|
||||
parseEvidence(await runProviderAnalysis(path, "inventory_artifact", {}));
|
||||
parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Project the same inventory Evidence twice and return the schema-parsed
|
||||
@@ -93,6 +98,8 @@ export const createNonApplicationZipInventory = async (
|
||||
await writer.add("one.js", new TextReader("one"));
|
||||
await writeFile(path, await writer.close());
|
||||
return parseEvidence(
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}),
|
||||
await runProviderAnalysis(path, "inventory_artifact", {}).then(
|
||||
projectCliAnalysisResult,
|
||||
),
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user