mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
feat(analysis): make resource budgets recoverable across CLI and MCP (#9706)
* refactor(analysis): centralize resource budgets and recovery facts * feat(analysis): expose ASAR and Ghidra runtime admission controls * feat(artifacts): expose reader admission and preserve limited recognition * feat(javascript): expose recoverable semantic precision budgets * feat(web): add recoverable source-map budgets and compact retained views * feat(web): make historical capture decoding budgets recoverable * fix(desktop): enforce selected view policies across capture and export * feat(runtime): make Frida and LLDB retention budgets selectable * feat(native): expose diagnostic budgets and preserve exhaustion causes * feat(managed): expose recoverable snapshot and representation budgets * feat(metadata): make Go and Rust inspection budgets recoverable * feat(wasm): expose representation and producer admissions * feat(evm): expose per-call worker resource budgets * feat(android): make resource and string-pool omissions recoverable * feat(producers): expose command and recorded-capture budgets * docs(analysis): record recovery contracts and integrated validation * fix(docs): link calibration oracle from published verification pages * fix(docs): restore desktop task anchors and receipt links * test(analysis): align fixtures with selectable budget contracts * fix(analysis): remove source-location projector dependency cycle * test(analysis): refresh budget recovery and verifier regressions * test(analysis): preserve format and publication recovery expectations * test(windows): align conformance with owned runtime admission * fix(desktop): preserve selected alias kinds in exported state * test(analysis): align provider fixtures with recoverable budgets * test(desktop): validate emitted alias metadata structurally * fix(ghidra): classify bounded Windows endpoint refusals * test(cli): assert selectable artifact admission recovery * fix(cli): preserve capture provenance in environment builds
This commit is contained in:
1 parent
55b6d2ca20
commit
cb4e5cfbff
515 files changed
+21066
-3637
No files matched your search
@@ -76,6 +76,23 @@ export const registerApktoolCommands = (
|
||||
decodeTimeoutMs: cliDecimalField(
|
||||
apktoolInputSchemas.decode_android_resources.shape.decode_timeout_ms,
|
||||
),
|
||||
stringOffset: cliDecimalField(
|
||||
apktoolInputSchemas.decode_android_resources.shape.string_offset,
|
||||
),
|
||||
stringLimit: cliDecimalField(
|
||||
apktoolInputSchemas.decode_android_resources.shape.string_limit,
|
||||
),
|
||||
maxTotalStrings: cliDecimalField(
|
||||
apktoolInputSchemas.decode_android_resources.shape.max_total_strings,
|
||||
),
|
||||
maxStringTableBytes: cliDecimalField(
|
||||
apktoolInputSchemas.decode_android_resources.shape
|
||||
.max_string_table_bytes,
|
||||
),
|
||||
maxTotalStringTableBytes: cliDecimalField(
|
||||
apktoolInputSchemas.decode_android_resources.shape
|
||||
.max_total_string_table_bytes,
|
||||
),
|
||||
strings: z
|
||||
.boolean()
|
||||
.default(
|
||||
@@ -104,6 +121,23 @@ export const registerApktoolCommands = (
|
||||
...(options.decodeTimeoutMs === undefined
|
||||
? {}
|
||||
: { decode_timeout_ms: options.decodeTimeoutMs }),
|
||||
...(options.stringOffset === undefined
|
||||
? {}
|
||||
: { string_offset: options.stringOffset }),
|
||||
...(options.stringLimit === undefined
|
||||
? {}
|
||||
: { string_limit: options.stringLimit }),
|
||||
...(options.maxTotalStrings === undefined
|
||||
? {}
|
||||
: { max_total_strings: options.maxTotalStrings }),
|
||||
...(options.maxStringTableBytes === undefined
|
||||
? {}
|
||||
: { max_string_table_bytes: options.maxStringTableBytes }),
|
||||
...(options.maxTotalStringTableBytes === undefined
|
||||
? {}
|
||||
: {
|
||||
max_total_string_table_bytes: options.maxTotalStringTableBytes,
|
||||
}),
|
||||
},
|
||||
args.path,
|
||||
),
|
||||
|
||||
@@ -16,6 +16,8 @@ import { keyedArchiveInputSchema } from "../domain/apple/keyedArchive.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { z } from "incur";
|
||||
import { cliDecimalField } from "./options.js";
|
||||
import { producerLimitsOption } from "./producerLimitsOption.js";
|
||||
import { artifactReaderLimitsInputSchema } from "../domain/artifactReaderLimits.js";
|
||||
|
||||
import { artifactExtractionInputSchema } from "../contracts/artifactToolContracts.js";
|
||||
|
||||
@@ -32,6 +34,13 @@ const keyedArchiveInputBinding = {
|
||||
fields: { path: "archive" },
|
||||
} as const;
|
||||
|
||||
const artifactLimitsOption = producerLimitsOption(
|
||||
artifactReaderLimitsInputSchema,
|
||||
"artifact-limits",
|
||||
).describe(
|
||||
"JSON artifact producer admissions: bun, dotnet_bundle, node_sea, godot, and expanded_bytes. Omitted fields keep defaults; changes apply on the next call without restart. Independent of summary/paging and client delivery limits.",
|
||||
);
|
||||
|
||||
export const registerArtifactCommands = (
|
||||
cli: CliInstance,
|
||||
logger: Logger,
|
||||
@@ -254,6 +263,7 @@ const registerExtractionCommand = (
|
||||
}),
|
||||
options: z.object({
|
||||
// Incur needs an ordinary optional; keep the owning contract's metadata.
|
||||
artifactLimits: artifactLimitsOption,
|
||||
outputRoot: artifactExtractionInputSchema.shape.output_root
|
||||
.unwrap()
|
||||
.optional()
|
||||
@@ -277,6 +287,9 @@ const registerExtractionCommand = (
|
||||
"extract_artifact",
|
||||
{
|
||||
integrity_policy: options.integrityPolicy,
|
||||
...(options.artifactLimits === undefined
|
||||
? {}
|
||||
: { artifact_limits: options.artifactLimits }),
|
||||
output_root: createArtifactExtractionDestination(
|
||||
options.outputRoot,
|
||||
),
|
||||
@@ -306,6 +319,7 @@ const registerInspectionCommand = (
|
||||
}),
|
||||
options: z.object({
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
artifactLimits: artifactLimitsOption,
|
||||
integrityPolicy: z
|
||||
.enum(["fail", "record-and-continue"])
|
||||
.default("fail")
|
||||
@@ -327,7 +341,12 @@ const registerInspectionCommand = (
|
||||
const complete = await runProviderAnalysis(
|
||||
args.path,
|
||||
"inspect_artifact",
|
||||
{ integrity_policy: options.integrityPolicy },
|
||||
{
|
||||
integrity_policy: options.integrityPolicy,
|
||||
...(options.artifactLimits === undefined
|
||||
? {}
|
||||
: { artifact_limits: options.artifactLimits }),
|
||||
},
|
||||
logger,
|
||||
signal,
|
||||
undefined,
|
||||
|
||||
@@ -5,6 +5,8 @@ import { inspectBinaryHardeningInputSchema } from "../domain/native/binaryHarden
|
||||
import { inspectByteEntropyInputSchema } from "../domain/native/byteEntropy.js";
|
||||
import { z } from "incur";
|
||||
import { cliDecimalField } from "./options.js";
|
||||
import { producerLimitsOption } from "./producerLimitsOption.js";
|
||||
import { pwntoolsProducerLimitsSchema } from "../domain/native/pwntoolsResourceLimits.js";
|
||||
import {
|
||||
createByteEntropyService,
|
||||
createRecordedCrashService,
|
||||
@@ -272,6 +274,12 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
maxEntries: cliDecimalField(
|
||||
inspectKernelModuleInputSchema.shape.max_entries,
|
||||
),
|
||||
maxModinfoKeyBytes: cliDecimalField(
|
||||
inspectKernelModuleInputSchema.shape.max_modinfo_key_bytes,
|
||||
),
|
||||
maxModinfoValueBytes: cliDecimalField(
|
||||
inspectKernelModuleInputSchema.shape.max_modinfo_value_bytes,
|
||||
),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
withCommandCancellation(async (signal) => {
|
||||
@@ -288,6 +296,8 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
path: absoluteCallerPath(args.path),
|
||||
max_file_bytes: options.maxFileBytes,
|
||||
max_entries: options.maxEntries,
|
||||
max_modinfo_key_bytes: options.maxModinfoKeyBytes,
|
||||
max_modinfo_value_bytes: options.maxModinfoValueBytes,
|
||||
},
|
||||
{ signal },
|
||||
),
|
||||
@@ -745,6 +755,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
producerLimits: producerLimitsOption(pwntoolsProducerLimitsSchema),
|
||||
pwntoolsTimeoutMs: cliDecimalField(
|
||||
inspectBinaryLayoutInputSchema.shape.pwntools_timeout_ms,
|
||||
),
|
||||
@@ -755,6 +766,9 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
const result = await service.inspectAndClose(
|
||||
{
|
||||
path: absoluteCallerPath(args.path),
|
||||
...(options.producerLimits === undefined
|
||||
? {}
|
||||
: { producer_limits: options.producerLimits }),
|
||||
...(options.pwntoolsTimeoutMs === undefined
|
||||
? {}
|
||||
: { pwntools_timeout_ms: options.pwntoolsTimeoutMs }),
|
||||
@@ -777,6 +791,7 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
producerLimits: producerLimitsOption(pwntoolsProducerLimitsSchema),
|
||||
debuggerContext: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
@@ -792,6 +807,9 @@ export const registerBinaryDiagnosticsCommands = (
|
||||
{
|
||||
path: absoluteCallerPath(args.path),
|
||||
include_debugger_context: options.debuggerContext,
|
||||
...(options.producerLimits === undefined
|
||||
? {}
|
||||
: { producer_limits: options.producerLimits }),
|
||||
...(options.pwntoolsTimeoutMs === undefined
|
||||
? {}
|
||||
: { pwntools_timeout_ms: options.pwntoolsTimeoutMs }),
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import { parseCliJsonInput } from "../cliJsonInput.js";
|
||||
import {
|
||||
fetchedSourceMapLimitCliOptions,
|
||||
sourceMapLimitsFromCli,
|
||||
} from "./sourceMapLimits.js";
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { webBundleDeliveryOptions } from "./javascriptRouteDelivery.js";
|
||||
import {
|
||||
@@ -178,6 +183,12 @@ const registerBundleAnalysis = (
|
||||
...passiveBrowserScopeOptions,
|
||||
...webBundleDeliveryOptions,
|
||||
observationMs: observationDuration(500),
|
||||
semanticPrecision: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"Inline JSON or JSON file of semantic_precision evaluator budgets for each inert script.",
|
||||
),
|
||||
fetchSourceMaps: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
@@ -189,6 +200,7 @@ const registerBundleAnalysis = (
|
||||
totalAnalysisTimeoutMs: cliDecimalField(
|
||||
analyzeWebBundleInputSchema.shape.total_analysis_timeout_ms,
|
||||
),
|
||||
...fetchedSourceMapLimitCliOptions,
|
||||
sourceMapFetchTimeoutMs: cliDecimalField(
|
||||
analyzeWebBundleInputSchema.shape.source_map_fetch_timeout_ms,
|
||||
),
|
||||
@@ -202,6 +214,15 @@ const registerBundleAnalysis = (
|
||||
webRoutesDeliverySchema,
|
||||
);
|
||||
if (!delivery.ok) return projectAnalysisError(delivery.error);
|
||||
const precision =
|
||||
options.semanticPrecision === undefined
|
||||
? undefined
|
||||
: await parseCliJsonInput(
|
||||
options.semanticPrecision,
|
||||
"analyze_web_bundle",
|
||||
signal,
|
||||
);
|
||||
if (precision !== undefined && !precision.ok) return precision.error;
|
||||
const provider = createBrowserObservationProvider();
|
||||
const input = {
|
||||
cdp_endpoint: args.endpoint,
|
||||
@@ -210,6 +231,10 @@ const registerBundleAnalysis = (
|
||||
observation_ms: options.observationMs,
|
||||
include_script_sources: true,
|
||||
fetch_source_maps: options.fetchSourceMaps,
|
||||
...(precision === undefined
|
||||
? {}
|
||||
: { semantic_precision: precision.value }),
|
||||
...sourceMapLimitsFromCli(options),
|
||||
...(options.maxHeapMb === undefined
|
||||
? {}
|
||||
: { max_heap_mb: options.maxHeapMb }),
|
||||
|
||||
@@ -18,6 +18,7 @@ import { finishCliEvidenceCommand } from "./cliEvidenceCallerPath.js";
|
||||
import { cliDecimalField, cliDecimalShape } from "./options.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { producerLimitsOption } from "./producerLimitsOption.js";
|
||||
export const registerCaptureCommands = (
|
||||
cli: CliInstance,
|
||||
logger: Logger,
|
||||
@@ -27,11 +28,16 @@ export const registerCaptureCommands = (
|
||||
"inspect" | "inspectFields" | "close"
|
||||
> = createPacketCaptureService(environment),
|
||||
): void => {
|
||||
const { producer_limits: discoveryLimits, ...discoveryShape } =
|
||||
inspectPacketFieldsInputSchema.shape;
|
||||
const { producer_limits: selectedLimits, ...selectedShape } =
|
||||
packetCaptureOptionsSchema.shape;
|
||||
cli.command(CLI_COMMANDS.inspectPacketFields, {
|
||||
description: "Search installed TShark protocol and field definitions",
|
||||
options: z
|
||||
.object({
|
||||
...cliDecimalShape(inspectPacketFieldsInputSchema.shape),
|
||||
...cliDecimalShape(discoveryShape),
|
||||
producerLimits: producerLimitsOption(discoveryLimits.unwrap()),
|
||||
limit: cliDecimalField(
|
||||
z
|
||||
.number()
|
||||
@@ -54,13 +60,19 @@ export const registerCaptureCommands = (
|
||||
message: "all and limit are mutually exclusive",
|
||||
});
|
||||
}),
|
||||
run: ({ options: { all, ...options } }) =>
|
||||
run: ({ options: { all, producerLimits, ...options } }) =>
|
||||
withCommandCancellation((signal) =>
|
||||
logCliCommand(logger, CLI_COMMANDS.inspectPacketFields, async () =>
|
||||
finishAnalysisCommand(
|
||||
() =>
|
||||
service.inspectFields(
|
||||
{ ...options, ...(all ? { limit: null } : {}) },
|
||||
{
|
||||
...options,
|
||||
...(producerLimits === undefined
|
||||
? {}
|
||||
: { producer_limits: producerLimits }),
|
||||
...(all ? { limit: null } : {}),
|
||||
},
|
||||
{ signal },
|
||||
),
|
||||
service,
|
||||
@@ -79,7 +91,8 @@ export const registerCaptureCommands = (
|
||||
}),
|
||||
options: z
|
||||
.object({
|
||||
...cliDecimalShape(packetCaptureOptionsSchema.shape),
|
||||
...cliDecimalShape(selectedShape),
|
||||
producerLimits: producerLimitsOption(selectedLimits.unwrap()),
|
||||
match_limit: cliDecimalField(
|
||||
z
|
||||
.number()
|
||||
@@ -107,7 +120,7 @@ export const registerCaptureCommands = (
|
||||
withinPacketCaptureSelectorBudget,
|
||||
packetCaptureSelectorBudgetIssue,
|
||||
),
|
||||
run: ({ args, options: { all_matches, ...options } }) =>
|
||||
run: ({ args, options: { all_matches, producerLimits, ...options } }) =>
|
||||
withCommandCancellation((signal) =>
|
||||
logCliCommand(logger, CLI_COMMANDS.inspectPacketCapture, async () => {
|
||||
return finishCliEvidenceCommand(
|
||||
@@ -116,6 +129,9 @@ export const registerCaptureCommands = (
|
||||
service.inspect(
|
||||
{
|
||||
...options,
|
||||
...(producerLimits === undefined
|
||||
? {}
|
||||
: { producer_limits: producerLimits }),
|
||||
...(all_matches ? { match_limit: null } : {}),
|
||||
path: absoluteCallerPath(args.path),
|
||||
},
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { desktopLibraryPolicyInputSchema } from "../domain/desktop/libraryPolicy.js";
|
||||
import { desktopViewPolicyInputSchema } from "../domain/desktop/desktopViewPolicy.js";
|
||||
import { safeParseJson } from "../domain/safeJson.js";
|
||||
import { createInterface } from "node:readline";
|
||||
|
||||
import { z } from "incur";
|
||||
@@ -27,11 +30,52 @@ import { SessionRequestQueue } from "../desktop/DesktopSessionRequests.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import type { CliInstance } from "./types.js";
|
||||
|
||||
/** One coherent view policy; invalid JSON and unknown controls fail at admission. */
|
||||
const viewPolicyOption = z
|
||||
.string()
|
||||
.transform((text, context) => {
|
||||
const parsed = safeParseJson(text);
|
||||
if (!parsed.ok) {
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
message: "view-policy must be a JSON object",
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return parsed.value;
|
||||
})
|
||||
.pipe(desktopViewPolicyInputSchema)
|
||||
.optional()
|
||||
.describe(
|
||||
"JSON budget overrides: file_bytes, sqlite_tables, sqlite_rows, sqlite_view_bytes, text_lines, xml_nodes, xml_depth, parsed_bytes, json_depth, zip_entries, zip_entry_bytes, zip_bytes, zip_ratio. Positive safe integers. Applies on this call and to exported readers; no restart. Does not recover bytes the capture never retained or raise Evidence/worker limits.",
|
||||
);
|
||||
|
||||
/** Caller-selected inventory headroom; does not raise content-retention budgets. */
|
||||
const libraryPolicyOption = z
|
||||
.string()
|
||||
.transform((text, context) => {
|
||||
const parsed = safeParseJson(text);
|
||||
if (!parsed.ok) {
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
message: "library-policy must be a JSON object",
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return parsed.value;
|
||||
})
|
||||
.pipe(desktopLibraryPolicyInputSchema)
|
||||
.optional()
|
||||
.describe(
|
||||
"JSON dependency inventory budgets: libraries (default 512, also per-image DT_NEEDED entries), search_path_entries (16 distinct offsets per DT_RUNPATH/DT_RPATH kind), string_bytes (4096 per string including NUL), table_bytes (1048576 per ELF program/dynamic table). Positive safe integers; applies to this capture without restart. Does not raise content or query-output budgets.",
|
||||
);
|
||||
|
||||
const positive = (fallback: number) =>
|
||||
cliDecimalField(z.int().positive().default(fallback));
|
||||
|
||||
/** Options of `capture-desktop-application`. */
|
||||
const captureOptions = z.object({
|
||||
libraryPolicy: libraryPolicyOption,
|
||||
desktopEntry: z
|
||||
.string()
|
||||
.min(1)
|
||||
@@ -150,8 +194,8 @@ const withStateReaders = async (
|
||||
return finishAnalysisCommand(
|
||||
async () => {
|
||||
const result = await run({
|
||||
sqlite: (database, readSignal) =>
|
||||
readSqliteStateView(sqlite, database, readSignal),
|
||||
sqlite: (database, readSignal, policy) =>
|
||||
readSqliteStateView(sqlite, database, readSignal, policy),
|
||||
dconf: (database, readSignal) =>
|
||||
dumpDconfDatabase(database, environment, readSignal),
|
||||
});
|
||||
@@ -271,6 +315,7 @@ const registerDesktopExportCommand = (
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
viewPolicy: viewPolicyOption,
|
||||
name: z
|
||||
.string()
|
||||
.optional()
|
||||
@@ -351,6 +396,7 @@ const registerDesktopExportCommand = (
|
||||
? undefined
|
||||
: absoluteCallerPath(options.screens),
|
||||
...withheldRequestOf(options),
|
||||
viewPolicy: options.viewPolicy,
|
||||
},
|
||||
readers,
|
||||
signal,
|
||||
@@ -376,7 +422,7 @@ const registerDesktopTaskCommand = (
|
||||
"Folder written by capture-desktop-application",
|
||||
),
|
||||
}),
|
||||
options: z.object(checkpointOptions),
|
||||
options: z.object({ ...checkpointOptions, viewPolicy: viewPolicyOption }),
|
||||
run: ({ args, options }) =>
|
||||
withCommandCancellation((signal) =>
|
||||
logCliCommand(
|
||||
@@ -399,6 +445,7 @@ const registerDesktopTaskCommand = (
|
||||
end: options.endCheckpoint,
|
||||
withhold: withholdOf(options),
|
||||
},
|
||||
options.viewPolicy,
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -421,7 +468,16 @@ const registerDesktopStateCommand = (
|
||||
"Folder written by capture-desktop-application",
|
||||
),
|
||||
}),
|
||||
run: ({ args }) =>
|
||||
options: z.object({
|
||||
viewPolicy: viewPolicyOption,
|
||||
statePath: z
|
||||
.array(z.string().min(1))
|
||||
.optional()
|
||||
.describe(
|
||||
"Exact original captured path to inspect (repeatable); omission examines every path",
|
||||
),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
withCommandCancellation((signal) =>
|
||||
logCliCommand(
|
||||
logger,
|
||||
@@ -438,6 +494,7 @@ const registerDesktopStateCommand = (
|
||||
absoluteCallerPath(args.capture),
|
||||
readers,
|
||||
signal,
|
||||
{ viewPolicy: options.viewPolicy, paths: options.statePath },
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -477,6 +534,9 @@ export const registerDesktopCaptureCommands = (
|
||||
output: options.output,
|
||||
statePaths: options.statePath ?? [],
|
||||
excludes: options.exclude ?? [],
|
||||
...(options.libraryPolicy === undefined
|
||||
? {}
|
||||
: { libraryPolicy: options.libraryPolicy }),
|
||||
maxDurationSeconds: options.maxDurationSeconds,
|
||||
maxEntries: options.maxEntries,
|
||||
maxFileBytes: options.maxFileBytes,
|
||||
|
||||
@@ -365,11 +365,24 @@ const registerJavaScriptApplicationCommand = (
|
||||
signal,
|
||||
);
|
||||
if (!sourcePaths.ok) return sourcePaths.error;
|
||||
const precision =
|
||||
options.semanticPrecision === undefined
|
||||
? undefined
|
||||
: await parseCliJsonInput(
|
||||
options.semanticPrecision,
|
||||
"analyze_javascript_application",
|
||||
signal,
|
||||
);
|
||||
if (precision !== undefined && !precision.ok)
|
||||
return precision.error;
|
||||
return runCliJavaScriptApplicationAnalysis(
|
||||
{
|
||||
// An empty path names no file; leave it for input validation.
|
||||
input_path:
|
||||
args.path === "" ? args.path : absoluteCallerPath(args.path),
|
||||
...(precision === undefined
|
||||
? {}
|
||||
: { semantic_precision: precision.value }),
|
||||
format: options.artifactFormat,
|
||||
integrity_policy: options.integrityPolicy,
|
||||
...(options.maxSemanticNodes === undefined
|
||||
|
||||
@@ -335,7 +335,7 @@ const conflictingRules = (): JsonValue =>
|
||||
/** 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,
|
||||
callerCapture: string,
|
||||
callerInputPath: string,
|
||||
captureTimeoutMs: number | undefined,
|
||||
environment: Readonly<NodeJS.ProcessEnv>,
|
||||
signal: AbortSignal,
|
||||
@@ -358,9 +358,7 @@ const inspectBuildHar = async (
|
||||
: { capture_timeout_ms: captureTimeoutMs }),
|
||||
},
|
||||
{ signal },
|
||||
// Provenance keeps the caller's spelling. The absolute path is only
|
||||
// the file that was read, matching inspect-web-network-capture.
|
||||
{ capture: callerCapture },
|
||||
{ capture: callerInputPath },
|
||||
),
|
||||
service,
|
||||
"web-network-capture",
|
||||
|
||||
@@ -9,7 +9,11 @@ import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { finishCliEvidenceCommand } from "./cliEvidenceCallerPath.js";
|
||||
import { cliDecimalField } from "./options.js";
|
||||
import { inspectEvmInterfaceInputSchema } from "../domain/evm/evmInterface.js";
|
||||
import {
|
||||
inspectEvmInterfaceInputSchema,
|
||||
evmProducerLimitsSchema,
|
||||
} from "../domain/evm/evmInterface.js";
|
||||
import { producerLimitsOption } from "./producerLimitsOption.js";
|
||||
|
||||
/** Resolve operator paths and preserve explicit encoding through the shared workflow. */
|
||||
export const registerEvmCommands = (
|
||||
@@ -27,6 +31,7 @@ export const registerEvmCommands = (
|
||||
.describe("Selected carrier representation"),
|
||||
}),
|
||||
options: z.object({
|
||||
producerLimits: producerLimitsOption(evmProducerLimitsSchema),
|
||||
timeoutMs: cliDecimalField(
|
||||
inspectEvmInterfaceInputSchema.shape.timeout_ms,
|
||||
),
|
||||
@@ -41,6 +46,9 @@ export const registerEvmCommands = (
|
||||
{
|
||||
path: absoluteCallerPath(args.path),
|
||||
encoding: args.encoding,
|
||||
...(options.producerLimits === undefined
|
||||
? {}
|
||||
: { producer_limits: options.producerLimits }),
|
||||
...(options.timeoutMs === undefined
|
||||
? {}
|
||||
: { timeout_ms: options.timeoutMs }),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { safeParseJson } from "../domain/safeJson.js";
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import {
|
||||
evidenceSchema,
|
||||
@@ -11,6 +12,7 @@ import { CLI_COMMANDS } from "../cliCommandNames.js";
|
||||
import { logCliCommand } from "../cliLogging.js";
|
||||
import {
|
||||
flutterInputSchemas,
|
||||
dartPoolPolicySchema,
|
||||
type FlutterOperation,
|
||||
} from "../domain/flutter/flutterBuildAnalysis.js";
|
||||
import type { Logger } from "pino";
|
||||
@@ -58,11 +60,32 @@ export const registerFlutterCommands = (
|
||||
}),
|
||||
optionSchema: z.object({
|
||||
abi: flutterInputSchemas.inspect_dart_aot.shape.abi,
|
||||
poolPolicy: z
|
||||
.string()
|
||||
.transform((text, context) => {
|
||||
const parsed = safeParseJson(text);
|
||||
if (!parsed.ok) {
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
message: "pool-policy must be a JSON object",
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return parsed.value;
|
||||
})
|
||||
.pipe(dartPoolPolicySchema)
|
||||
.optional()
|
||||
.describe(
|
||||
"JSON printable-run selection: contains, uri_limit (500), token_limit (2000), run_bytes (1024), token_bytes (128). Filters apply before bounded collection; URI/token counts cover matches. Positive safe integers; next call, no restart. Does not raise APK/library/ELF/reply limits.",
|
||||
),
|
||||
}),
|
||||
operation: "inspect_dart_aot",
|
||||
toInput: (arguments_, options) => ({
|
||||
path: arguments_.path,
|
||||
...(options.abi === undefined ? {} : { abi: options.abi }),
|
||||
...(options.poolPolicy === undefined
|
||||
? {}
|
||||
: { pool_policy: options.poolPolicy }),
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { fridaCapturePolicyInputSchema } from "../domain/fridaCapturePolicy.js";
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
|
||||
@@ -176,6 +177,23 @@ export const registerFridaCommands = (
|
||||
.array(z.string())
|
||||
.optional()
|
||||
.describe("Arguments passed to a spawned target"),
|
||||
captureSessionBytes: cliDecimalField(
|
||||
fridaCapturePolicyInputSchema.shape.session_bytes,
|
||||
).describe(
|
||||
"Session retention bytes including encoded payloads; select before starting the experiment",
|
||||
),
|
||||
captureMessageTextBytes: cliDecimalField(
|
||||
fridaCapturePolicyInputSchema.shape.message_text_bytes,
|
||||
).describe("Retained bytes per Frida error text field"),
|
||||
captureMessageTypeBytes: cliDecimalField(
|
||||
fridaCapturePolicyInputSchema.shape.message_type_bytes,
|
||||
).describe("Retained bytes per Frida message type"),
|
||||
captureJsonNodes: cliDecimalField(
|
||||
fridaCapturePolicyInputSchema.shape.json_nodes,
|
||||
).describe("Retained JSON nodes per Frida payload"),
|
||||
captureJsonDepth: cliDecimalField(
|
||||
fridaCapturePolicyInputSchema.shape.json_depth,
|
||||
).describe("Retained JSON nesting depth per Frida payload"),
|
||||
durationMs: cliDecimalField(
|
||||
z
|
||||
.number()
|
||||
@@ -274,6 +292,39 @@ export const registerFridaCommands = (
|
||||
const result = await service.instrument(
|
||||
{
|
||||
...targetInput,
|
||||
...([
|
||||
options.captureSessionBytes,
|
||||
options.captureMessageTextBytes,
|
||||
options.captureMessageTypeBytes,
|
||||
options.captureJsonNodes,
|
||||
options.captureJsonDepth,
|
||||
].every((value) => value === undefined)
|
||||
? {}
|
||||
: {
|
||||
captureLimits: {
|
||||
...(options.captureSessionBytes === undefined
|
||||
? {}
|
||||
: { session_bytes: options.captureSessionBytes }),
|
||||
...(options.captureMessageTextBytes === undefined
|
||||
? {}
|
||||
: {
|
||||
message_text_bytes:
|
||||
options.captureMessageTextBytes,
|
||||
}),
|
||||
...(options.captureMessageTypeBytes === undefined
|
||||
? {}
|
||||
: {
|
||||
message_type_bytes:
|
||||
options.captureMessageTypeBytes,
|
||||
}),
|
||||
...(options.captureJsonNodes === undefined
|
||||
? {}
|
||||
: { json_nodes: options.captureJsonNodes }),
|
||||
...(options.captureJsonDepth === undefined
|
||||
? {}
|
||||
: { json_depth: options.captureJsonDepth }),
|
||||
},
|
||||
}),
|
||||
source:
|
||||
options.scriptFile === undefined
|
||||
? { sourceKind: "inline", source: options.script ?? "" }
|
||||
|
||||
+52
-8
@@ -12,11 +12,7 @@ import {
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { machoSliceArchitectureSchema } from "../domain/apple/machoTargetSelection.js";
|
||||
import {
|
||||
DEFAULT_MAX_GO_STRING_LITERALS,
|
||||
MAX_GO_STRING_LITERALS_LIMIT,
|
||||
goMaxStringLiteralsSchema,
|
||||
} from "../domain/go/goBinary.js";
|
||||
import { goResourceBudgetFields } from "../domain/go/goBinary.js";
|
||||
import { cliDecimalField } from "./options.js";
|
||||
|
||||
/** Resolve caller paths and inspect static build metadata through the shared workflow. */
|
||||
@@ -42,10 +38,26 @@ export const registerGoCommands = (cli: CliInstance, logger: Logger): void => {
|
||||
.describe(
|
||||
"Also recover bounded inferred Go UTF-8 string literals from static (ptr, len) headers",
|
||||
),
|
||||
maxInputBytes: cliDecimalField(goResourceBudgetFields.max_input_bytes),
|
||||
maxReportBytes: cliDecimalField(goResourceBudgetFields.max_report_bytes),
|
||||
maxBuildInfoBytes: cliDecimalField(
|
||||
goResourceBudgetFields.max_build_info_bytes,
|
||||
),
|
||||
maxHeaderSearchBytes: cliDecimalField(
|
||||
goResourceBudgetFields.max_header_search_bytes,
|
||||
),
|
||||
maxStringLiterals: cliDecimalField(
|
||||
goMaxStringLiteralsSchema.optional(),
|
||||
).describe(
|
||||
`Maximum distinct recovered string literals (1-${String(MAX_GO_STRING_LITERALS_LIMIT)}; default ${String(DEFAULT_MAX_GO_STRING_LITERALS)}; requires --include-string-literals)`,
|
||||
goResourceBudgetFields.max_string_literals,
|
||||
),
|
||||
maxStringLiteralBytes: cliDecimalField(
|
||||
goResourceBudgetFields.max_string_literal_bytes,
|
||||
),
|
||||
maxStringBytes: cliDecimalField(goResourceBudgetFields.max_string_bytes),
|
||||
maxStringHeaderScanBytes: cliDecimalField(
|
||||
goResourceBudgetFields.max_string_header_scan_bytes,
|
||||
),
|
||||
maxStringCandidates: cliDecimalField(
|
||||
goResourceBudgetFields.max_string_candidates,
|
||||
),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
@@ -70,9 +82,41 @@ export const registerGoCommands = (cli: CliInstance, logger: Logger): void => {
|
||||
include_string_literals:
|
||||
options.includeStringLiterals,
|
||||
}),
|
||||
...(options.maxInputBytes === undefined
|
||||
? {}
|
||||
: { max_input_bytes: options.maxInputBytes }),
|
||||
...(options.maxReportBytes === undefined
|
||||
? {}
|
||||
: { max_report_bytes: options.maxReportBytes }),
|
||||
...(options.maxBuildInfoBytes === undefined
|
||||
? {}
|
||||
: { max_build_info_bytes: options.maxBuildInfoBytes }),
|
||||
...(options.maxHeaderSearchBytes === undefined
|
||||
? {}
|
||||
: {
|
||||
max_header_search_bytes: options.maxHeaderSearchBytes,
|
||||
}),
|
||||
...(options.maxStringLiterals === undefined
|
||||
? {}
|
||||
: { max_string_literals: options.maxStringLiterals }),
|
||||
...(options.maxStringLiteralBytes === undefined
|
||||
? {}
|
||||
: {
|
||||
max_string_literal_bytes:
|
||||
options.maxStringLiteralBytes,
|
||||
}),
|
||||
...(options.maxStringBytes === undefined
|
||||
? {}
|
||||
: { max_string_bytes: options.maxStringBytes }),
|
||||
...(options.maxStringHeaderScanBytes === undefined
|
||||
? {}
|
||||
: {
|
||||
max_string_header_scan_bytes:
|
||||
options.maxStringHeaderScanBytes,
|
||||
}),
|
||||
...(options.maxStringCandidates === undefined
|
||||
? {}
|
||||
: { max_string_candidates: options.maxStringCandidates }),
|
||||
},
|
||||
{ signal },
|
||||
),
|
||||
|
||||
@@ -22,6 +22,32 @@ import { managedMembersInputSchema } from "../contracts/managed/managedToolContr
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
||||
import { managedInspectionBudgetShape } from "../domain/managed/managedInspectionLimits.js";
|
||||
import { cliDecimalField } from "./options.js";
|
||||
|
||||
const managedBudgetOptions = {
|
||||
maxSnapshotBytes: cliDecimalField(
|
||||
managedInspectionBudgetShape.max_snapshot_bytes,
|
||||
),
|
||||
maxMetadataRepresentationBytes: cliDecimalField(
|
||||
managedInspectionBudgetShape.max_metadata_representation_bytes,
|
||||
),
|
||||
};
|
||||
|
||||
const managedBudgetParameters = (options: {
|
||||
readonly maxSnapshotBytes?: number | undefined;
|
||||
readonly maxMetadataRepresentationBytes?: number | undefined;
|
||||
}) => ({
|
||||
...(options.maxSnapshotBytes === undefined
|
||||
? {}
|
||||
: { max_snapshot_bytes: options.maxSnapshotBytes }),
|
||||
...(options.maxMetadataRepresentationBytes === undefined
|
||||
? {}
|
||||
: {
|
||||
max_metadata_representation_bytes:
|
||||
options.maxMetadataRepresentationBytes,
|
||||
}),
|
||||
});
|
||||
|
||||
export const registerManagedCommands = (
|
||||
cli: CliInstance,
|
||||
@@ -49,12 +75,13 @@ const registerInspectManagedArtifact = (
|
||||
"Managed PE executable or assembly path",
|
||||
),
|
||||
}),
|
||||
run: ({ args }) =>
|
||||
options: z.object(managedBudgetOptions),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "inspect-managed-artifact", () =>
|
||||
runProviderAnalysis(
|
||||
args.path,
|
||||
"inspect_managed_artifact",
|
||||
{},
|
||||
managedBudgetParameters(options),
|
||||
logger,
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
@@ -75,6 +102,7 @@ const registerInspectManagedMembers = (
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
...managedBudgetOptions,
|
||||
type: managedMembersInputSchema.shape.type_names.describe(
|
||||
"Inspect only members declared by this exact type full name, and the string literals they load, such as System.Threading.Monitor; a nested type is named by its bare name, which selects every nested type of that name (repeatable)",
|
||||
),
|
||||
@@ -93,7 +121,12 @@ const registerInspectManagedMembers = (
|
||||
runProviderAnalysis(
|
||||
args.path,
|
||||
"inspect_managed_members",
|
||||
options.type === undefined ? {} : { type_names: options.type },
|
||||
{
|
||||
...managedBudgetParameters(options),
|
||||
...(options.type === undefined
|
||||
? {}
|
||||
: { type_names: options.type }),
|
||||
},
|
||||
logger,
|
||||
signal,
|
||||
{
|
||||
@@ -123,12 +156,13 @@ const registerInspectManagedNativeBoundaries = (
|
||||
"Managed PE executable or assembly path",
|
||||
),
|
||||
}),
|
||||
run: ({ args }) =>
|
||||
options: z.object(managedBudgetOptions),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "inspect-managed-native-boundaries", () =>
|
||||
runProviderAnalysis(
|
||||
args.path,
|
||||
"inspect_managed_native_boundaries",
|
||||
{},
|
||||
managedBudgetParameters(options),
|
||||
logger,
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
@@ -153,6 +187,7 @@ const registerCompareManagedMembers = (
|
||||
"Compare managed PE/CLI member inventories by exact declared type, name, and signature; names alone never form a match",
|
||||
args: compareManagedMemberArgs,
|
||||
options: z.object({
|
||||
...managedBudgetOptions,
|
||||
type: managedMembersInputSchema.shape.type_names.describe(
|
||||
"Compare only members declared by this exact type full name on both sides, as selected by inspect-managed-members --type; use it when a whole-assembly comparison exceeds capacity (repeatable)",
|
||||
),
|
||||
@@ -167,6 +202,15 @@ const registerCompareManagedMembers = (
|
||||
{
|
||||
leftPath: args.leftPath,
|
||||
rightPath: args.rightPath,
|
||||
...(options.maxSnapshotBytes === undefined
|
||||
? {}
|
||||
: { maxSnapshotBytes: options.maxSnapshotBytes }),
|
||||
...(options.maxMetadataRepresentationBytes === undefined
|
||||
? {}
|
||||
: {
|
||||
maxRepresentationBytes:
|
||||
options.maxMetadataRepresentationBytes,
|
||||
}),
|
||||
...(options.type === undefined
|
||||
? {}
|
||||
: { typeNames: options.type }),
|
||||
|
||||
@@ -29,7 +29,7 @@ export const registerNativeCallCommands = (
|
||||
inputJson: z
|
||||
.string()
|
||||
.describe(
|
||||
"Inline JSON or a JSON file path: breakpoints, arguments, environment, duration_ms, max_events, argument_registers, backtrace_frames",
|
||||
"Inline JSON or a JSON file path: breakpoints, arguments, environment, duration_ms, max_events, argument_registers, backtrace_frames, capture_limits",
|
||||
),
|
||||
}),
|
||||
run: ({ args }) =>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { z } from "incur";
|
||||
import { safeParseJson } from "../domain/safeJson.js";
|
||||
|
||||
/** Incur passes object flags as text; validate the selected producer's exact policy. */
|
||||
export const producerLimitsOption = <Schema extends z.ZodType>(
|
||||
schema: Schema,
|
||||
flagName = "producer-limits",
|
||||
) =>
|
||||
z
|
||||
.string()
|
||||
.transform((text, context) => {
|
||||
const parsed = safeParseJson(text);
|
||||
if (!parsed.ok) {
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
message: `${flagName} must be a JSON object`,
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return parsed.value;
|
||||
})
|
||||
.pipe(schema)
|
||||
.optional()
|
||||
.describe(
|
||||
"JSON producer budget overrides. Keys and units follow this operation's producer_limits contract; changes apply on this call without restart and preserve tighter inherited OS limits.",
|
||||
);
|
||||
@@ -17,6 +17,7 @@ import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
import {
|
||||
OBJDUMP_ARTIFACT_OPERATIONS,
|
||||
reverseCommandTimeoutSchema,
|
||||
reverseCommandOutputBytesSchema,
|
||||
rizinInputSchema,
|
||||
} from "../domain/reverseEngineering.js";
|
||||
import { logCliCommand } from "../cliLogging.js";
|
||||
@@ -173,6 +174,7 @@ export const registerReverseEngineeringCommands = (
|
||||
"Follow local DWARF debug links. External debug-file identity is not bound to Evidence.",
|
||||
),
|
||||
commandTimeoutMs: cliDecimalField(reverseCommandTimeoutSchema),
|
||||
maxOutputBytes: cliDecimalField(reverseCommandOutputBytesSchema),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
withCommandCancellation((signal) =>
|
||||
@@ -197,6 +199,9 @@ export const registerReverseEngineeringCommands = (
|
||||
...(options.symbol === undefined
|
||||
? {}
|
||||
: { symbol: options.symbol }),
|
||||
...(options.maxOutputBytes === undefined
|
||||
? {}
|
||||
: { max_output_bytes: options.maxOutputBytes }),
|
||||
...(options.commandTimeoutMs === undefined
|
||||
? {}
|
||||
: { command_timeout_ms: options.commandTimeoutMs }),
|
||||
@@ -220,6 +225,7 @@ export const registerReverseEngineeringCommands = (
|
||||
options: z.object({
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
commandTimeoutMs: cliDecimalField(reverseCommandTimeoutSchema),
|
||||
maxOutputBytes: cliDecimalField(reverseCommandOutputBytesSchema),
|
||||
}),
|
||||
run: ({ options }) =>
|
||||
withCommandCancellation((signal) =>
|
||||
@@ -244,6 +250,9 @@ export const registerReverseEngineeringCommands = (
|
||||
service.inspectWithObjdump(
|
||||
{
|
||||
operation: "architectures",
|
||||
...(options.maxOutputBytes === undefined
|
||||
? {}
|
||||
: { max_output_bytes: options.maxOutputBytes }),
|
||||
...(options.commandTimeoutMs === undefined
|
||||
? {}
|
||||
: { command_timeout_ms: options.commandTimeoutMs }),
|
||||
@@ -277,6 +286,7 @@ export const registerReverseEngineeringCommands = (
|
||||
asmCpu: rizinInputSchema.shape.asm_cpu,
|
||||
baseAddress: rizinInputSchema.shape.base_address,
|
||||
commandTimeoutMs: cliDecimalField(reverseCommandTimeoutSchema),
|
||||
maxOutputBytes: cliDecimalField(reverseCommandOutputBytesSchema),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
withCommandCancellation((signal) =>
|
||||
@@ -300,6 +310,9 @@ export const registerReverseEngineeringCommands = (
|
||||
...(options.baseAddress === undefined
|
||||
? {}
|
||||
: { base_address: options.baseAddress }),
|
||||
...(options.maxOutputBytes === undefined
|
||||
? {}
|
||||
: { max_output_bytes: options.maxOutputBytes }),
|
||||
...(options.commandTimeoutMs === undefined
|
||||
? {}
|
||||
: { command_timeout_ms: options.commandTimeoutMs }),
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { z } from "incur";
|
||||
import { producerLimitsOption } from "./producerLimitsOption.js";
|
||||
import { rustProducerLimitsSchema } from "../domain/rust/rustResourceLimits.js";
|
||||
import { inspectRustBinaryInputSchema } from "../domain/rust/rustBinary.js";
|
||||
import { cliDecimalField } from "./options.js";
|
||||
import { createRustBinaryService } from "../composition/rust.js";
|
||||
import { CLI_COMMANDS } from "../cliCommandNames.js";
|
||||
import { logCliCommand } from "../cliLogging.js";
|
||||
@@ -33,6 +37,10 @@ export const registerRustCommands = (
|
||||
}),
|
||||
options: z.object({
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
producerLimits: producerLimitsOption(rustProducerLimitsSchema),
|
||||
maxRustcHashes: cliDecimalField(
|
||||
inspectRustBinaryInputSchema.shape.max_rustc_hashes,
|
||||
),
|
||||
architecture: machoSliceArchitectureSchema
|
||||
.optional()
|
||||
.describe(
|
||||
@@ -76,6 +84,12 @@ export const registerRustCommands = (
|
||||
const view = decoded.value;
|
||||
const input = inspectRustBinaryRequestSchema.safeParse({
|
||||
path: absoluteCallerPath(args.path),
|
||||
...(options.producerLimits === undefined
|
||||
? {}
|
||||
: { producer_limits: options.producerLimits }),
|
||||
...(options.maxRustcHashes === undefined
|
||||
? {}
|
||||
: { max_rustc_hashes: options.maxRustcHashes }),
|
||||
...(options.architecture === undefined
|
||||
? {}
|
||||
: { architecture: options.architecture }),
|
||||
@@ -97,6 +111,12 @@ export const registerRustCommands = (
|
||||
service.inspect(
|
||||
{
|
||||
path: input.data.path,
|
||||
...(input.data.producer_limits === undefined
|
||||
? {}
|
||||
: { producer_limits: input.data.producer_limits }),
|
||||
...(input.data.max_rustc_hashes === undefined
|
||||
? {}
|
||||
: { max_rustc_hashes: input.data.max_rustc_hashes }),
|
||||
...(input.data.architecture === undefined
|
||||
? {}
|
||||
: { architecture: input.data.architecture }),
|
||||
|
||||
@@ -18,6 +18,7 @@ import { finishCliEvidenceCommand } from "./cliEvidenceCallerPath.js";
|
||||
import { cliDecimalField, cliDecimalShape } from "./options.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { producerLimitsOption } from "./producerLimitsOption.js";
|
||||
export const registerSignalCommands = (
|
||||
cli: CliInstance,
|
||||
logger: Logger,
|
||||
@@ -27,11 +28,16 @@ export const registerSignalCommands = (
|
||||
"decode" | "inspectDecoders" | "close"
|
||||
> = createSignalRecordingService(environment),
|
||||
): void => {
|
||||
const { producer_limits: discoveryLimits, ...discoveryShape } =
|
||||
inspectSignalDecodersInputSchema.shape;
|
||||
const { producer_limits: selectedLimits, ...selectedShape } =
|
||||
signalRecordingOptionsSchema.shape;
|
||||
cli.command(CLI_COMMANDS.inspectSignalDecoders, {
|
||||
description: "Search installed sigrok decoders or selected decoder help",
|
||||
options: z
|
||||
.object({
|
||||
...cliDecimalShape(inspectSignalDecodersInputSchema.shape),
|
||||
...cliDecimalShape(discoveryShape),
|
||||
producerLimits: producerLimitsOption(discoveryLimits.unwrap()),
|
||||
limit: cliDecimalField(
|
||||
z
|
||||
.number()
|
||||
@@ -56,13 +62,19 @@ export const registerSignalCommands = (
|
||||
message: "all and limit are mutually exclusive",
|
||||
});
|
||||
}),
|
||||
run: ({ options: { all, ...options } }) =>
|
||||
run: ({ options: { all, producerLimits, ...options } }) =>
|
||||
withCommandCancellation((signal) =>
|
||||
logCliCommand(logger, CLI_COMMANDS.inspectSignalDecoders, async () =>
|
||||
finishAnalysisCommand(
|
||||
() =>
|
||||
service.inspectDecoders(
|
||||
{ ...options, ...(all ? { limit: null } : {}) },
|
||||
{
|
||||
...options,
|
||||
...(producerLimits === undefined
|
||||
? {}
|
||||
: { producer_limits: producerLimits }),
|
||||
...(all ? { limit: null } : {}),
|
||||
},
|
||||
{ signal },
|
||||
),
|
||||
service,
|
||||
@@ -81,7 +93,8 @@ export const registerSignalCommands = (
|
||||
}),
|
||||
options: z
|
||||
.object({
|
||||
...cliDecimalShape(signalRecordingOptionsSchema.shape),
|
||||
...cliDecimalShape(selectedShape),
|
||||
producerLimits: producerLimitsOption(selectedLimits.unwrap()),
|
||||
line_limit: cliDecimalField(
|
||||
z
|
||||
.number()
|
||||
@@ -109,7 +122,7 @@ export const registerSignalCommands = (
|
||||
withinSignalRecordingSelectorBudget,
|
||||
signalRecordingSelectorBudgetIssue,
|
||||
),
|
||||
run: ({ args, options: { all_lines, ...options } }) =>
|
||||
run: ({ args, options: { all_lines, producerLimits, ...options } }) =>
|
||||
withCommandCancellation((signal) =>
|
||||
logCliCommand(logger, CLI_COMMANDS.decodeSignalRecording, async () => {
|
||||
return finishCliEvidenceCommand(
|
||||
@@ -118,6 +131,9 @@ export const registerSignalCommands = (
|
||||
service.decode(
|
||||
{
|
||||
...options,
|
||||
...(producerLimits === undefined
|
||||
? {}
|
||||
: { producer_limits: producerLimits }),
|
||||
...(all_lines ? { line_limit: null } : {}),
|
||||
path: absoluteCallerPath(args.path),
|
||||
},
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { cliDecimalField } from "./options.js";
|
||||
import {
|
||||
sourceMapLimitsSchema,
|
||||
type SourceMapLimitSelection,
|
||||
} from "../domain/sourceMapLimits.js";
|
||||
|
||||
const shape = sourceMapLimitsSchema.shape;
|
||||
const sharedOptions = {
|
||||
sourceMapOutputBytes: cliDecimalField(shape.output_bytes),
|
||||
sourceMapDecodedRows: cliDecimalField(shape.decoded_rows),
|
||||
sourceMapDecodedSegments: cliDecimalField(shape.decoded_segments),
|
||||
sourceMapDecodedRecords: cliDecimalField(shape.decoded_records),
|
||||
sourceMapSectionDepth: cliDecimalField(shape.section_depth),
|
||||
};
|
||||
export const localSourceMapLimitCliOptions = {
|
||||
...sharedOptions,
|
||||
sourceMapBytes: cliDecimalField(shape.map_bytes),
|
||||
sourceMapCodecHeapMib: cliDecimalField(shape.codec_heap_mib),
|
||||
};
|
||||
export const fetchedSourceMapLimitCliOptions = {
|
||||
...sharedOptions,
|
||||
sourceMapFetchBytes: cliDecimalField(shape.fetch_bytes),
|
||||
};
|
||||
/** Keep omitted caller fields omitted in Evidence parameters. */
|
||||
export const sourceMapLimitsFromCli = (options: {
|
||||
sourceMapBytes?: number | undefined;
|
||||
sourceMapOutputBytes?: number | undefined;
|
||||
sourceMapDecodedRows?: number | undefined;
|
||||
sourceMapDecodedSegments?: number | undefined;
|
||||
sourceMapDecodedRecords?: number | undefined;
|
||||
sourceMapSectionDepth?: number | undefined;
|
||||
sourceMapCodecHeapMib?: number | undefined;
|
||||
sourceMapFetchBytes?: number | undefined;
|
||||
}): { source_map_limits?: SourceMapLimitSelection } => {
|
||||
const selected: SourceMapLimitSelection = {
|
||||
...(options.sourceMapBytes === undefined
|
||||
? {}
|
||||
: { map_bytes: options.sourceMapBytes }),
|
||||
...(options.sourceMapOutputBytes === undefined
|
||||
? {}
|
||||
: { output_bytes: options.sourceMapOutputBytes }),
|
||||
...(options.sourceMapDecodedRows === undefined
|
||||
? {}
|
||||
: { decoded_rows: options.sourceMapDecodedRows }),
|
||||
...(options.sourceMapDecodedSegments === undefined
|
||||
? {}
|
||||
: { decoded_segments: options.sourceMapDecodedSegments }),
|
||||
...(options.sourceMapDecodedRecords === undefined
|
||||
? {}
|
||||
: { decoded_records: options.sourceMapDecodedRecords }),
|
||||
...(options.sourceMapSectionDepth === undefined
|
||||
? {}
|
||||
: { section_depth: options.sourceMapSectionDepth }),
|
||||
...(options.sourceMapCodecHeapMib === undefined
|
||||
? {}
|
||||
: { codec_heap_mib: options.sourceMapCodecHeapMib }),
|
||||
...(options.sourceMapFetchBytes === undefined
|
||||
? {}
|
||||
: { fetch_bytes: options.sourceMapFetchBytes }),
|
||||
};
|
||||
return Object.keys(selected).length === 0
|
||||
? {}
|
||||
: { source_map_limits: selected };
|
||||
};
|
||||
@@ -75,6 +75,21 @@ export const registerWasmCommands = (
|
||||
commandTimeoutMs: cliDecimalField(
|
||||
inspectWasmArtifactInputSchema.shape.command_timeout_ms,
|
||||
),
|
||||
maxInterfaceBytes: cliDecimalField(
|
||||
inspectWasmArtifactInputSchema.shape.max_interface_bytes,
|
||||
),
|
||||
maxArtifactPaths: cliDecimalField(
|
||||
inspectWasmArtifactInputSchema.shape.max_artifact_paths,
|
||||
),
|
||||
maxInputBytes: cliDecimalField(
|
||||
inspectWasmArtifactInputSchema.shape.max_input_bytes,
|
||||
),
|
||||
maxToolBytes: cliDecimalField(
|
||||
inspectWasmArtifactInputSchema.shape.max_tool_bytes,
|
||||
),
|
||||
maxOutputBytes: cliDecimalField(
|
||||
inspectWasmArtifactInputSchema.shape.max_output_bytes,
|
||||
),
|
||||
glue_paths: wasmArtifactPathsSchema(localPathStringSchema)
|
||||
.optional()
|
||||
.describe("Selected local JavaScript glue paths"),
|
||||
@@ -94,6 +109,21 @@ export const registerWasmCommands = (
|
||||
...(options.commandTimeoutMs === undefined
|
||||
? {}
|
||||
: { command_timeout_ms: options.commandTimeoutMs }),
|
||||
...(options.maxInterfaceBytes === undefined
|
||||
? {}
|
||||
: { max_interface_bytes: options.maxInterfaceBytes }),
|
||||
...(options.maxArtifactPaths === undefined
|
||||
? {}
|
||||
: { max_artifact_paths: options.maxArtifactPaths }),
|
||||
...(options.maxInputBytes === undefined
|
||||
? {}
|
||||
: { max_input_bytes: options.maxInputBytes }),
|
||||
...(options.maxToolBytes === undefined
|
||||
? {}
|
||||
: { max_tool_bytes: options.maxToolBytes }),
|
||||
...(options.maxOutputBytes === undefined
|
||||
? {}
|
||||
: { max_output_bytes: options.maxOutputBytes }),
|
||||
glue_paths: options.glue_paths?.map((path) =>
|
||||
absoluteCallerPath(path),
|
||||
),
|
||||
|
||||
@@ -55,6 +55,15 @@ export const registerWebNetworkCaptureCommands = (
|
||||
.optional()
|
||||
.describe("Zero-based producer ordinals; repeat to select several"),
|
||||
...explicitSensitiveValueOptions,
|
||||
maxInputBytes: cliDecimalField(
|
||||
inspectWebNetworkCaptureInputSchema.shape.max_input_bytes,
|
||||
),
|
||||
maxReplyBytes: cliDecimalField(
|
||||
inspectWebNetworkCaptureInputSchema.shape.max_reply_bytes,
|
||||
),
|
||||
maxNestingDepth: cliDecimalField(
|
||||
inspectWebNetworkCaptureInputSchema.shape.max_nesting_depth,
|
||||
),
|
||||
// Decimal tokens only: numeric coercion would read "0x1" as 1 ms.
|
||||
captureTimeoutMs: cliDecimalField(
|
||||
inspectWebNetworkCaptureInputSchema.shape.capture_timeout_ms,
|
||||
@@ -74,6 +83,15 @@ export const registerWebNetworkCaptureCommands = (
|
||||
capture_path: absoluteCallerPath(args.capturePath),
|
||||
format: args.format,
|
||||
sensitive_values: options.sensitiveValue,
|
||||
...(options.maxInputBytes === undefined
|
||||
? {}
|
||||
: { max_input_bytes: options.maxInputBytes }),
|
||||
...(options.maxReplyBytes === undefined
|
||||
? {}
|
||||
: { max_reply_bytes: options.maxReplyBytes }),
|
||||
...(options.maxNestingDepth === undefined
|
||||
? {}
|
||||
: { max_nesting_depth: options.maxNestingDepth }),
|
||||
...([
|
||||
options.protobufFraming,
|
||||
options.protobufBodyPointer,
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import {
|
||||
localSourceMapLimitCliOptions,
|
||||
sourceMapLimitsFromCli,
|
||||
} from "./sourceMapLimits.js";
|
||||
import {
|
||||
analysisEvidenceDeliveryOptions,
|
||||
parseCliAnalysisEvidenceDelivery,
|
||||
deliverCliAnalysisEvidence,
|
||||
} from "./analysisEvidenceDelivery.js";
|
||||
import { webSourceLocationDeliverySchema } from "../domain/analysisView/webSourceLocationView.js";
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
import { z } from "incur";
|
||||
import { createWebSourceLocationService } from "../composition/webSourceLocations.js";
|
||||
import { webSourceLocationInputSchema } from "../domain/webSourceLocation.js";
|
||||
@@ -8,7 +18,6 @@ import type { Logger } from "pino";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { cliDecimalField, cliIntegerArgument } from "./options.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
||||
|
||||
const sourceMapSelection = webSourceLocationInputSchema.shape.source_map;
|
||||
|
||||
@@ -35,15 +44,29 @@ export const registerWebSourceLocationCommands = (
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
...localSourceMapLimitCliOptions,
|
||||
...analysisEvidenceDeliveryOptions,
|
||||
view: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"JSON source-location matches/source-text view; requires --detail summary and --evidence_output",
|
||||
),
|
||||
decodeTimeoutMs: cliDecimalField(
|
||||
webSourceLocationInputSchema.shape.decode_timeout_ms,
|
||||
),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
withCommandCancellation((signal) =>
|
||||
logCliCommand(logger, CLI_COMMANDS.traceWebSourceLocation, () =>
|
||||
finishAnalysisCommand(
|
||||
() =>
|
||||
logCliCommand(logger, CLI_COMMANDS.traceWebSourceLocation, async () => {
|
||||
const delivery = parseCliAnalysisEvidenceDelivery(
|
||||
"trace_web_source_location",
|
||||
options,
|
||||
webSourceLocationDeliverySchema,
|
||||
);
|
||||
if (!delivery.ok) return projectAnalysisError(delivery.error);
|
||||
return deliverCliAnalysisEvidence({
|
||||
run: () =>
|
||||
service.trace(
|
||||
{
|
||||
manifest_path: args.manifestPath,
|
||||
@@ -53,6 +76,7 @@ export const registerWebSourceLocationCommands = (
|
||||
url: args.sourceMapUrl,
|
||||
},
|
||||
generated_position: { line: args.line, column: args.column },
|
||||
...sourceMapLimitsFromCli(options),
|
||||
...(options.decodeTimeoutMs === undefined
|
||||
? {}
|
||||
: { decode_timeout_ms: options.decodeTimeoutMs }),
|
||||
@@ -60,11 +84,16 @@ export const registerWebSourceLocationCommands = (
|
||||
{ signal },
|
||||
),
|
||||
service,
|
||||
"web-source-location",
|
||||
"trace_web_source_location",
|
||||
providerId: "web-source-location",
|
||||
operation: "trace_web_source_location",
|
||||
signal,
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
detail: delivery.value.detail,
|
||||
evidenceOutput: delivery.value.evidenceOutput,
|
||||
...(delivery.value.view === undefined
|
||||
? {}
|
||||
: { view: delivery.value.view }),
|
||||
});
|
||||
}),
|
||||
),
|
||||
});
|
||||
};
|
||||
Reference in new issue
Block a user