mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
fix(cli): validate selected local paths before resolution
Reuse the nonempty NUL-free local path contract before normalization in artifact commands, auxiliary inputs, and output destinations. Empty selections now fail at parsing instead of selecting the working directory; explicit relative paths preserve their target. Public CLI checks cover primary and auxiliary empty paths, exact relative-file digest, explicit current-directory selection, symlink parent semantics, and environment export inputs. Correct the desktop fixture to assert the existing CLI validation envelope.
This commit is contained in:
@@ -39,6 +39,11 @@ pass an absolute path. REA reads and authenticates the file, then projects it
|
||||
like retained Evidence, so a large file is bounded by the file reader rather
|
||||
than charged as inline input.
|
||||
|
||||
Local file inspection commands reject empty paths and NUL bytes before resolving
|
||||
relative inputs, auxiliary files or output destinations. An empty string never
|
||||
selects the working directory; use an explicit `.` when selecting that directory
|
||||
is supported by the command.
|
||||
|
||||
For example, a 334 MB Obsidian application Evidence file yields a summary view
|
||||
of about 10 KB. Replace the view with
|
||||
`{"kind": "page", "collection": "modules", "offset": 0, "limit": 32}` for a
|
||||
|
||||
@@ -10,6 +10,7 @@ import { AnalysisInputError } from "../domain/analysisErrorCore.js";
|
||||
import { createEvidenceBundle } from "../domain/evidenceBundle.js";
|
||||
import { publishEvidenceDelivery } from "../application/EvidenceBundleCommands.js";
|
||||
import { inspectAnalysisViewValidated } from "../application/analysisView/AnalysisViewService.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
||||
|
||||
@@ -18,9 +19,7 @@ export const analysisEvidenceDeliveryOptions = {
|
||||
.enum(["complete", "summary"])
|
||||
.default("complete")
|
||||
.describe("Deliver complete Evidence, or a summary with --evidence_output"),
|
||||
evidence_output: z
|
||||
.string()
|
||||
.min(1)
|
||||
evidence_output: localPathStringSchema
|
||||
.optional()
|
||||
.describe(
|
||||
"Save complete Evidence as a bundle; required for summary delivery. Never overwrites.",
|
||||
|
||||
@@ -8,6 +8,7 @@ import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js";
|
||||
import { safeParseJson } from "../domain/safeJson.js";
|
||||
import { logCliCommand } from "../cliLogging.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
@@ -25,7 +26,9 @@ export const registerApkSignatureCommands = (
|
||||
cli.command(apkSignatureModule.cliCommand, {
|
||||
description:
|
||||
"Inspect declared APK signing schemes, signers and content digests",
|
||||
args: z.object({ path: z.string().describe("Selected local APK/ZIP/AAB") }),
|
||||
args: z.object({
|
||||
path: localPathStringSchema.describe("Selected local APK/ZIP/AAB"),
|
||||
}),
|
||||
options: z.object({
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
view: z
|
||||
|
||||
@@ -22,6 +22,7 @@ import { inspectNativeExportsInputSchema } from "../domain/native/nativeExports.
|
||||
import { inspectElfNotesInputSchema } from "../domain/native/elfNotes.js";
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import {
|
||||
analysisEvidenceDeliveryOptions,
|
||||
@@ -37,7 +38,9 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
cli.command(CLI_COMMANDS.inspectElfNotes, {
|
||||
description:
|
||||
"Inspect ELF PT_NOTE and SHT_NOTE identities without executing the image",
|
||||
args: z.object({ path: z.string().describe("Local ELF32/ELF64 image") }),
|
||||
args: z.object({
|
||||
path: localPathStringSchema.describe("Local ELF32/ELF64 image"),
|
||||
}),
|
||||
options: z.object({
|
||||
maxFileBytes: inspectElfNotesInputSchema.shape.max_file_bytes,
|
||||
maxEntries: inspectElfNotesInputSchema.shape.max_entries,
|
||||
@@ -71,7 +74,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
cli.command(CLI_COMMANDS.inspectByteEntropy, {
|
||||
description:
|
||||
"Inspect file-wide and block byte entropy without executing the file",
|
||||
args: z.object({ path: z.string().describe("Local file") }),
|
||||
args: z.object({ path: localPathStringSchema.describe("Local file") }),
|
||||
options: z.object({
|
||||
blockSize: inspectByteEntropyInputSchema.shape.block_size.describe(
|
||||
"Power-of-two block size from 256 to 65536 bytes (default 4096)",
|
||||
@@ -108,7 +111,9 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
cli.command(CLI_COMMANDS.inspectPeCertificates, {
|
||||
description:
|
||||
"Inspect PE Authenticode certificate table identities without verifying signatures",
|
||||
args: z.object({ path: z.string().describe("Local PE32/PE32+ image") }),
|
||||
args: z.object({
|
||||
path: localPathStringSchema.describe("Local PE32/PE32+ image"),
|
||||
}),
|
||||
options: z.object({
|
||||
maxFileBytes: inspectPeCertificatesInputSchema.shape.max_file_bytes,
|
||||
maxEntries: inspectPeCertificatesInputSchema.shape.max_entries,
|
||||
@@ -141,7 +146,9 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
cli.command(CLI_COMMANDS.inspectPeResources, {
|
||||
description:
|
||||
"Inspect PE resource identities, payload ranges, hashes and icon-group relationships",
|
||||
args: z.object({ path: z.string().describe("Local PE32/PE32+ image") }),
|
||||
args: z.object({
|
||||
path: localPathStringSchema.describe("Local PE32/PE32+ image"),
|
||||
}),
|
||||
options: z.object({
|
||||
maxFileBytes: inspectPeResourcesInputSchema.shape.max_file_bytes,
|
||||
maxEntries: inspectPeResourcesInputSchema.shape.max_entries,
|
||||
@@ -174,7 +181,9 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
cli.command(CLI_COMMANDS.inspectNativeExports, {
|
||||
description:
|
||||
"Inspect a PE or ELF image's export table without executing it",
|
||||
args: z.object({ path: z.string().describe("Local PE or ELF image") }),
|
||||
args: z.object({
|
||||
path: localPathStringSchema.describe("Local PE or ELF image"),
|
||||
}),
|
||||
options: z.object({
|
||||
maxFileBytes: inspectNativeExportsInputSchema.shape.max_file_bytes,
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
@@ -210,7 +219,9 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
description:
|
||||
"Inspect binary file/linked layout without executing the selected object",
|
||||
args: z.object({
|
||||
path: z.string().describe("Local ELF binary or relocatable object"),
|
||||
path: localPathStringSchema.describe(
|
||||
"Local ELF binary or relocatable object",
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
pwntoolsTimeoutMs:
|
||||
@@ -237,9 +248,9 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
description:
|
||||
"Inspect supplied core registers/signals and optional debugger mapping evidence",
|
||||
args: z.object({
|
||||
path: z
|
||||
.string()
|
||||
.describe("Local Linux ELF64 amd64 or AArch64 little-endian core"),
|
||||
path: localPathStringSchema.describe(
|
||||
"Local Linux ELF64 amd64 or AArch64 little-endian core",
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
debuggerContext: z
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { Logger } from "pino";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
export const registerCaptureCommands = (
|
||||
cli: CliInstance,
|
||||
@@ -68,7 +69,9 @@ export const registerCaptureCommands = (
|
||||
cli.command(CLI_COMMANDS.inspectPacketCapture, {
|
||||
description:
|
||||
"Inspect an existing packet capture with caller-supplied TShark",
|
||||
args: z.object({ path: z.string().describe("Local packet capture") }),
|
||||
args: z.object({
|
||||
path: localPathStringSchema.describe("Local packet capture"),
|
||||
}),
|
||||
options: z
|
||||
.object({
|
||||
...packetCaptureOptionsSchema.shape,
|
||||
|
||||
@@ -22,6 +22,7 @@ import { runCliJavaScriptApplicationAnalysis } from "./javascriptApplicationAnal
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import { javascriptApplicationOptions } from "../cliObservationOptions.js";
|
||||
import { AnalysisInputError } from "../domain/analysisErrorCore.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { registerDecompileDirectoryCommand } from "./decompileDirectoryCommand.js";
|
||||
|
||||
@@ -84,10 +85,9 @@ const registerOverviewCommands = (
|
||||
cli.command(CLI_COMMANDS.analyze, {
|
||||
description: "Get an overview of an app",
|
||||
args: z.object({
|
||||
path: z
|
||||
.string()
|
||||
.min(1)
|
||||
.describe("App, program, or analysis database path"),
|
||||
path: localPathStringSchema.describe(
|
||||
"App, program, or analysis database path",
|
||||
),
|
||||
}),
|
||||
options: overviewOptions.extend({
|
||||
integrityPolicy: laneIntegrityPolicy,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
nativeOpenOptionFields,
|
||||
} from "./options.js";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import type { JsonValue } from "../domain/jsonValue.js";
|
||||
|
||||
@@ -25,12 +26,9 @@ export const registerDecompileDirectoryCommand = (
|
||||
path: z.string().describe("App or program path"),
|
||||
}),
|
||||
options: z.object({
|
||||
output: z
|
||||
.string()
|
||||
.min(1)
|
||||
.describe(
|
||||
"Output directory. A relative path is resolved from the working directory. The parent must exist.",
|
||||
),
|
||||
output: localPathStringSchema.describe(
|
||||
"Output directory. A relative path is resolved from the working directory. The parent must exist.",
|
||||
),
|
||||
layout: z
|
||||
.enum(["address", "dwarf"])
|
||||
.default("address")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { basename } from "node:path";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import type { EnvironmentExportResult } from "../application/environment/environmentExport.js";
|
||||
import type { EnvironmentSource } from "../application/environment/environmentSource.js";
|
||||
@@ -393,16 +394,13 @@ const registerExportCommand = (cli: CliInstance, logger: Logger): void => {
|
||||
"Export an environment manifest and its capture Evidence as an agent environment bundle folder the agentenv framework runs with agent-env run",
|
||||
args: z.object({
|
||||
manifest: manifestArgument,
|
||||
directory: z
|
||||
.string()
|
||||
.describe(
|
||||
"Bundle directory to create; must be absent or empty, nothing is overwritten",
|
||||
),
|
||||
directory: localPathStringSchema.describe(
|
||||
"Bundle directory to create; must be absent or empty, nothing is overwritten",
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
evidence: evidenceOption,
|
||||
packageTarball: z
|
||||
.string()
|
||||
packageTarball: localPathStringSchema
|
||||
.optional()
|
||||
.describe(
|
||||
"Packed rea-agents tarball (npm pack) copied into the env folder and installed by the Dockerfile instead of the registry package pinned to this version",
|
||||
|
||||
@@ -4,6 +4,7 @@ import { logCliCommand } from "../cliLogging.js";
|
||||
import type { Logger } from "pino";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
||||
import { inspectEvmInterfaceInputSchema } from "../domain/evm/evmInterface.js";
|
||||
@@ -18,7 +19,7 @@ export const registerEvmCommands = (
|
||||
cli.command(evmInterfaceModule.cliCommand, {
|
||||
description: "Infer interface candidates from explicit local EVM bytecode",
|
||||
args: z.object({
|
||||
path: z.string().describe("Local bytecode carrier"),
|
||||
path: localPathStringSchema.describe("Local bytecode carrier"),
|
||||
encoding: z
|
||||
.enum(["raw", "hex"])
|
||||
.describe("Selected carrier representation"),
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
analysisEvidenceDeliveryOptions,
|
||||
deliverCliAnalysisEvidence,
|
||||
} from "./analysisEvidenceDelivery.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { machoSliceArchitectureSchema } from "../domain/apple/machoTargetSelection.js";
|
||||
|
||||
@@ -18,9 +19,9 @@ export const registerGoCommands = (cli: CliInstance, logger: Logger): void => {
|
||||
description:
|
||||
"Inspect embedded Go compiler, modules and build settings without target execution",
|
||||
args: z.object({
|
||||
path: z
|
||||
.string()
|
||||
.describe("Local ELF, PE, thin Mach-O or universal Mach-O image"),
|
||||
path: localPathStringSchema.describe(
|
||||
"Local ELF, PE, thin Mach-O or universal Mach-O image",
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { safeParseJson } from "../domain/safeJson.js";
|
||||
import { z } from "incur";
|
||||
import type { Logger } from "pino";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { createGpuCodeObjectsService } from "../composition/gpu.js";
|
||||
import { CLI_COMMANDS } from "../cliCommandNames.js";
|
||||
@@ -22,9 +23,9 @@ export const registerGpuCommands = (cli: CliInstance, logger: Logger): void => {
|
||||
description:
|
||||
"Inspect embedded CUDA/HIP targets and selected static kernel metadata",
|
||||
args: z.object({
|
||||
path: z
|
||||
.string()
|
||||
.describe("Local ELF/PE, CUDA fatbin, CUBIN, PTX or HIP bundle"),
|
||||
path: localPathStringSchema.describe(
|
||||
"Local ELF/PE, CUDA fatbin, CUBIN, PTX or HIP bundle",
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { z } from "incur";
|
||||
import { AnalysisInputError } from "../domain/analysisErrorCore.js";
|
||||
@@ -22,14 +23,13 @@ export const registerIl2cppCommands = (
|
||||
description:
|
||||
"Inventory IL2CPP global-metadata.dat names, owners and string literals, optionally linking method addresses from the native binary",
|
||||
args: z.object({
|
||||
path: z
|
||||
.string()
|
||||
.describe("Local global-metadata.dat (version 24, 27, 29, 31 or 39)"),
|
||||
path: localPathStringSchema.describe(
|
||||
"Local global-metadata.dat (version 24, 27, 29, 31 or 39)",
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
binary: z
|
||||
.string()
|
||||
binary: localPathStringSchema
|
||||
.optional()
|
||||
.describe(
|
||||
"Matching native binary (Mach-O GameAssembly.dylib, PE GameAssembly.dll or ELF libil2cpp.so) to link method addresses; read only",
|
||||
@@ -38,8 +38,7 @@ export const registerIl2cppCommands = (
|
||||
.enum(["arm64", "amd64"])
|
||||
.optional()
|
||||
.describe("Slice of a universal Mach-O binary"),
|
||||
ghidraSeeds: z
|
||||
.string()
|
||||
ghidraSeeds: localPathStringSchema
|
||||
.optional()
|
||||
.describe(
|
||||
"Also write REA_GHIDRA_SEED_FILE function seeds named Type$$Method for every linked method; needs --binary",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { safeParseJson } from "../domain/safeJson.js";
|
||||
import { z } from "incur";
|
||||
import type { Logger } from "pino";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { createModelArtifactService } from "../composition/model.js";
|
||||
import { CLI_COMMANDS } from "../cliCommandNames.js";
|
||||
@@ -25,7 +26,9 @@ export const registerModelCommands = (
|
||||
description:
|
||||
"Inspect GGUF or safetensors headers without loading model weights",
|
||||
args: z.object({
|
||||
path: z.string().describe("Local model file, detected by content"),
|
||||
path: localPathStringSchema.describe(
|
||||
"Local model file, detected by content",
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from "incur";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { createPythonPycService } from "../composition/pythonPyc.js";
|
||||
import { CLI_COMMANDS } from "../cliCommandNames.js";
|
||||
@@ -18,7 +19,7 @@ export const registerPythonPycCommands = (
|
||||
description:
|
||||
"Inspect a supported CPython 3.7–3.13 pyc header and marshal code objects without executing bytecode",
|
||||
args: z.object({
|
||||
path: z.string().describe("Local CPython .pyc file"),
|
||||
path: localPathStringSchema.describe("Local CPython .pyc file"),
|
||||
}),
|
||||
options: z.object({
|
||||
maxFileBytes: inspectPythonPycInputSchema.shape.max_file_bytes,
|
||||
|
||||
@@ -14,6 +14,7 @@ import { safeParseJson } from "../domain/safeJson.js";
|
||||
import type { Logger } from "pino";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { machoSliceArchitectureSchema } from "../domain/apple/machoTargetSelection.js";
|
||||
|
||||
@@ -26,9 +27,9 @@ export const registerRustCommands = (
|
||||
description:
|
||||
"Inspect an embedded cargo-auditable Rust dependency list without target execution",
|
||||
args: z.object({
|
||||
path: z
|
||||
.string()
|
||||
.describe("Local ELF, PE, thin Mach-O or universal Mach-O image"),
|
||||
path: localPathStringSchema.describe(
|
||||
"Local ELF, PE, thin Mach-O or universal Mach-O image",
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { Logger } from "pino";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
export const registerSignalCommands = (
|
||||
cli: CliInstance,
|
||||
@@ -70,7 +71,9 @@ export const registerSignalCommands = (
|
||||
cli.command(CLI_COMMANDS.decodeSignalRecording, {
|
||||
description:
|
||||
"Decode an existing signal recording with caller-supplied sigrok",
|
||||
args: z.object({ path: z.string().describe("Local signal recording") }),
|
||||
args: z.object({
|
||||
path: localPathStringSchema.describe("Local signal recording"),
|
||||
}),
|
||||
options: z
|
||||
.object({
|
||||
...signalRecordingOptionsSchema.shape,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { logCliCommand } from "../cliLogging.js";
|
||||
import type { Logger } from "pino";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
||||
import { inspectSqliteDatabaseInputSchema } from "../domain/sqlite/sqliteDatabase.js";
|
||||
@@ -20,7 +21,7 @@ export const registerSqliteCommands = (
|
||||
description:
|
||||
"Inspect SQLite schema and optional ordinary-table rows from a local snapshot",
|
||||
args: z.object({
|
||||
path: z.string().describe("Local SQLite database snapshot"),
|
||||
path: localPathStringSchema.describe("Local SQLite database snapshot"),
|
||||
}),
|
||||
options: z.object({
|
||||
workerTimeoutMs: inspectSqliteDatabaseInputSchema.shape.worker_timeout_ms,
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
analysisEvidenceDeliveryOptions,
|
||||
deliverCliAnalysisEvidence,
|
||||
} from "./analysisEvidenceDelivery.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { explicitSensitiveValueOptions } from "./explicitSensitiveArguments.js";
|
||||
|
||||
@@ -27,7 +28,7 @@ export const registerWebNetworkCaptureCommands = (
|
||||
description:
|
||||
"Inspect retained HAR, native mitmproxy, Fiddler SAZ or aioquic qlog records without fetching recorded URLs",
|
||||
args: z.object({
|
||||
capturePath: z.string().describe("Absolute local capture path"),
|
||||
capturePath: localPathStringSchema.describe("Local capture path"),
|
||||
format: z
|
||||
.enum(WEB_NETWORK_CAPTURE_FORMATS)
|
||||
.describe("Producer capture format"),
|
||||
@@ -42,10 +43,9 @@ export const registerWebNetworkCaptureCommands = (
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.describe("Exact body pointers; repeat to select several"),
|
||||
protobufSchema: z
|
||||
.string()
|
||||
protobufSchema: localPathStringSchema
|
||||
.optional()
|
||||
.describe("Absolute local compiled FileDescriptorSet path"),
|
||||
.describe("Local compiled FileDescriptorSet path"),
|
||||
protobufMessage: z
|
||||
.string()
|
||||
.optional()
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { expect } from "vitest";
|
||||
|
||||
import { parseEvidence } from "../../src/domain/evidence.js";
|
||||
import { pythonPycFixture } from "../../src/python/PythonPyc.fixture.js";
|
||||
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
|
||||
import { cliTest, runFileCli } from "../support/cli/cliFixture.js";
|
||||
|
||||
const fileCommands = [
|
||||
"inspect-sqlite-database",
|
||||
"inspect-python-pyc",
|
||||
"inspect-apk-signature",
|
||||
"inspect-packet-capture",
|
||||
"decode-signal-recording",
|
||||
"inspect-model-artifact",
|
||||
"inspect-elf-notes",
|
||||
"inspect-byte-entropy",
|
||||
"inspect-pe-resources",
|
||||
"inspect-pe-certificates",
|
||||
"inspect-native-exports",
|
||||
"inspect-binary-layout",
|
||||
"inspect-recorded-crash",
|
||||
"inspect-go-binary",
|
||||
"inspect-rust-binary",
|
||||
"inspect-gpu-code-objects",
|
||||
"inspect-il2cpp-metadata",
|
||||
] as const;
|
||||
|
||||
cliTest(
|
||||
"rejects empty file selections before they can become the working directory",
|
||||
async ({ cli }) => {
|
||||
const cwd = await createTestTempDirectory("rea-empty-local-path-");
|
||||
const selections = [
|
||||
...fileCommands.map((command) => ({
|
||||
arguments: [command, ""],
|
||||
field: "path",
|
||||
})),
|
||||
{ arguments: ["inspect-evm-interface", "", "hex"], field: "path" },
|
||||
{
|
||||
arguments: ["inspect-web-network-capture", "", "har"],
|
||||
field: "capturePath",
|
||||
},
|
||||
];
|
||||
for (const selection of selections) {
|
||||
const result = await runFileCli(cli, {
|
||||
arguments: [...selection.arguments, "--json"],
|
||||
cwd,
|
||||
});
|
||||
expect(result.exitCode, selection.arguments[0]).toBe(1);
|
||||
expect(result.json, selection.arguments[0]).toMatchObject({
|
||||
code: "VALIDATION_ERROR",
|
||||
fieldErrors: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
path: selection.field,
|
||||
code: "too_small",
|
||||
missing: false,
|
||||
}),
|
||||
]),
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
cliTest(
|
||||
"rejects empty auxiliary selections and output destinations before reading inputs",
|
||||
async ({ cli }) => {
|
||||
const cwd = await createTestTempDirectory("rea-empty-auxiliary-path-");
|
||||
const selections = [
|
||||
{
|
||||
arguments: ["inspect-il2cpp-metadata", "unread", "--binary", ""],
|
||||
field: "binary",
|
||||
},
|
||||
{
|
||||
arguments: ["inspect-il2cpp-metadata", "unread", "--ghidra-seeds", ""],
|
||||
field: "ghidraSeeds",
|
||||
},
|
||||
{
|
||||
arguments: [
|
||||
"inspect-web-network-capture",
|
||||
"unread",
|
||||
"har",
|
||||
"--protobuf-schema",
|
||||
"",
|
||||
],
|
||||
field: "protobufSchema",
|
||||
},
|
||||
{
|
||||
arguments: ["decompile-all", "unread", "--output", ""],
|
||||
field: "output",
|
||||
},
|
||||
{ arguments: ["environment-export", "unread", ""], field: "directory" },
|
||||
{
|
||||
arguments: [
|
||||
"environment-export",
|
||||
"unread",
|
||||
"output",
|
||||
"--package-tarball",
|
||||
"",
|
||||
],
|
||||
field: "packageTarball",
|
||||
},
|
||||
];
|
||||
for (const selection of selections) {
|
||||
const result = await runFileCli(cli, {
|
||||
arguments: [...selection.arguments, "--json"],
|
||||
cwd,
|
||||
});
|
||||
expect(result.exitCode, selection.field).toBe(1);
|
||||
expect(result.json, selection.field).toMatchObject({
|
||||
code: "VALIDATION_ERROR",
|
||||
fieldErrors: expect.arrayContaining([
|
||||
expect.objectContaining({ path: selection.field, code: "too_small" }),
|
||||
]),
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
cliTest(
|
||||
"still inspects an explicit relative file with its exact digest",
|
||||
async ({ cli }) => {
|
||||
const cwd = await createTestTempDirectory("rea-relative-local-path-");
|
||||
const bytes = pythonPycFixture();
|
||||
const path = join(cwd, "selected.pyc");
|
||||
await writeFile(path, bytes);
|
||||
const result = await runFileCli(cli, {
|
||||
arguments: ["inspect-python-pyc", "./selected.pyc", "--json"],
|
||||
cwd,
|
||||
});
|
||||
expect(result.exitCode, result.stderr).toBe(0);
|
||||
expect(parseEvidence(result.json)).toMatchObject({
|
||||
operation: "inspect_python_pyc",
|
||||
subject: {
|
||||
local_path: path,
|
||||
digest: { sha256: createHash("sha256").update(bytes).digest("hex") },
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -18,8 +18,10 @@ cliTest(
|
||||
});
|
||||
expect(empty.exitCode).not.toBe(0);
|
||||
expect(empty.json).toMatchObject({
|
||||
code: "invalid_request",
|
||||
category: "invalid_input",
|
||||
code: "VALIDATION_ERROR",
|
||||
fieldErrors: expect.arrayContaining([
|
||||
expect.objectContaining({ path: "path", code: "too_small" }),
|
||||
]),
|
||||
});
|
||||
const explicit = await cli.run({
|
||||
arguments: ["inspect-desktop-application", ".", "--json"],
|
||||
|
||||
Reference in New Issue
Block a user