mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
9dde3fd6a4
* fix(artifacts): retain caller selection and applicable publication recovery * fix(artifacts): retain DMG producer failures and bound integrity recovery claims * fix(apk): classify malformed supported Zip64 records as input failures * fix(browser): compare script and tool facts without metadata identity churn * fix(environment): retain path segments counters and parent diagnostics * fix(discovery): retain Frida optional provider failure dispatch * test(native): share Swift context in vtable fixture admission * fix(native): narrow capability refusal before retaining its reason * fix(javascript): retain private receiver and iterative disposal effects * fix(native): preserve negative Mach-O admission identity * fix(javascript): bind Next page admission to source context * fix: preserve process filesystem scope and admission failures * fix(evidence): reject runtime locations for managed static facts * fix(hopper): bind ordinary executable opens to selected bytes * fix(browser): admit legacy capture uncertainty at retained input boundaries * fix(unknowns): distinguish rejected creation from committed mutation delivery * fix(evidence): preserve authentication failures and diagnose prior commitments * fix(cli): preserve explicit Evidence record pairing in bundle comparison * fix(artifacts): publish addressable inspection sources through both adapters * docs(evidence): define authentication and committed delivery recovery * fix(javascript): own reference roles and invocation result identities * fix(hopper): pass private X authority cookie through stdin * fix(ghidra): preserve catalog and metadata admission failures * fix(jeb): retain selected inputs and completed primary replies * fix(native): preserve literal environment map members * test: retain direct hash cleanup retainer contract * test: distinguish corrected port normalization commitment * fix: recheck attached worker policy after final artifacts * fix(javascript): share iteration and equality invocation facts * fix(artifacts): preserve typed resource limit diagnoses * fix(javascript): preserve semantic query identity and source coverage * docs(readme): align translated setup and analysis facts * fix(cli): reject surplus positional arguments before execution * fix(kuna): retain setting-specific doctor recovery * fix(setup): preserve semantic configuration refusal during uninstall * fix(setup): retain publication operation failures and recovery facts * fix(setup): distinguish Hopper inspection and archive staging failures * docs(managed): distinguish oracle conformance from host verification * docs(skill): keep one setup prerequisite policy * fix(firmware): preserve admission and extraction coverage outcomes * fix(contracts): preserve safe defaults and client literal guidance * fix(javascript): retain worker storage and projection failures * fix(evidence): preserve valid output paths near filesystem pathname limits * fix(cli): accept named native API selectors beginning with a dash * fix(artifacts): retain DMG carrier observations when child traversal is unavailable * fix(artifacts): retain dylib enumeration readers through failed cleanup * fix(browser): resolve empty storage-world origins from exact frame evidence * fix(browser): retain unknown popup storage initialization coverage * test(reconstruction): isolate derived surface ownership in fixtures * test(filesystem): probe actual permission denial before host assertions * test(managed): assert large issue retention without full-array diffs * fix(javascript): preserve source facts across graph projections * fix(javascript): preserve copied values and export publication lifetimes * fix(javascript): preserve per-field publication and provenance coverage * fix(javascript): retain initialized class self binding at body reads * fix(artifacts): retain permission omissions through JavaScript lookups * test(graph): narrow summary owner and complete PE fixture facts * test(javascript): supply source graph to native handoff checks * fix(sourcemaps): bind cleanup status to remaining codec owners * fix(browser): retain excluded source-map declaration coverage * test(javascript): preserve exact empty property keys in declarations * fix(browser): keep code absence unknown without worker inventory * fix(browser): preserve WebMCP optional fields and replay coverage gaps * fix(browser): publish executable examples with truthful capture coverage * fix(browser): preserve observed document invalidation and scope recovery * fix(native): share exact-name xref ambiguity resolution * fix(hopper): own bookmark API capability checks * fix(native): preserve procedure selector coordinate intent * test(discovery): select the declared GDB console contract * fix(ghidra): bind session refusal to retained cleanup owner * fix(apple): retain selected dylib identity after path removal * fix(apple): preserve asset failures and authored resource coverage * fix(apple): retain binary UID identity through archive projection * fix: retain completed historical replies through finalization * fix: retain unresolved browser secret declarations as input errors * test(browser): resolve network fixture secrets through typed input contract * fix(environment): distinguish destination refusal and write access failures * fix(artifacts): preserve exact ZIP and DMG caller cancellation reasons * fix(artifacts): require regular source-map target occurrences * fix(reference): separate local filesystem diagnostics from graph commitments * fix(setup): retain proven PATH aliases in installed registrations * test(package): join owned updater after registry fixture failure * fix(javascript): preserve recovery acquisition and cancellation failures * fix(session): retain cleanup blockers and snapshot request identity * fix(evidence): preserve format-owned JSON recovery diagnostics * test(evidence): cover BOM recovery across bundle commands * fix(tracing): retain caller reasons at traversal checkpoints * fix(inventory): require released cleanup before capability fallback * fix(hopper): drain retained publishers after current close failure * fix(native): preserve unknown Swift extension contexts * fix(apple): qualify unexamined nib content coverage * fix(frida): preserve session refusal and close execution effects * fix(rizin): retain cancellation and recover cleanup correlation * fix: retain Electron file admission facts across adapters * fix: preserve selected Electron display authorization * fix(javascript): retain unavailable HTML reference scopes * fix(javascript): preserve capture timing and detached receiver authority * fix(ida): preserve producer failure and caller abort outcomes * fix(cli): retain nested facts in Markdown tables * fix(cli): present argument guidance without validator cause dumps * docs(setup): define optional native update verification limits * docs(clients): distinguish discovery and schema recovery boundaries * docs(setup): explain npm workspace launcher failures * fix(verification): bind Copilot schema profile to observed inputs * fix(javascript): scope Next package admission by container identity * fix(apple): align asset allowance and category fixture types * fix(protocols): honor runtime queue and Frida identity types * test: parse Electron listing Evidence before comparison * fix(evidence): omit absent format recovery metadata * fix(contracts): validate historical source examples as exact JSON values * fix(javascript): consume per-field CommonJS publication facts * fix(gdb): correlate raw-output completion and retain read-only TTY proof * fix(native): retain completed LLDB facts across output-close cancellation * fix(hopper): retain failed private-display probe cleanup authority * style(lifecycle): format traversal and fallback edits * fix(apple): own ordered nib collections and registry parentage * fix(process): attribute trace omissions without changing shared successors * fix(cli): share builtin argument ownership with MCP routing * fix(cli): count emitted documents and normalize token windows * fix(inspector): retain captures through authorization cleanup failures * fix(artifacts): retain declared ASAR companion occurrence facts * fix(javascript): separate endpoint syntax correspondence from target identity * fix: distinguish native process exit from sampling failure * fix(javascript): retain selected-root access and filesystem failure facts * fix(browser): retain truthful cleanup facts and document request ownership * fix(native): share exact Objective-C class symbol interpretation * fix(go): retain completed artifact facts through finalization * fix(native): preserve seed analysis failures in value traces * fix(native): classify observed dSYM inspection refusal * test(apple): assert observed collection edge contract * fix(javascript): retain primary interruption through worker cleanup * fix(cutter): retain completion-read failures during cleanup retry * fix(cli): validate output windows and finish pending writes * fix(mcp): require absolute JavaScript application inputs * fix(capabilities): report missing navigation targets * fix(cli): support system Bash completion registration * fix(cli): retain narrowed token-count format handling * test(ida): preserve exact optional resource method shape * test(javascript): retain valid unknown endpoint coverage shape * fix(ghidra): try keg JDKs after failed PATH launchers * fix(browser): retain capacity refusal through terminal progress * fix(reference): require exact optional JSON diagnostics at the source schema * fix(apple): retain reader outcomes and cleanup ownership * fix(elf): qualify exports without dynamic symbol declarations * fix(mips): retain unexamined RELR declaration coverage * fix(metadata): preserve canonical PowerPC subject identity * fix(native): preserve LLDB discovery failure classifications * fix(native): report absent launch process before cleanup classification * fix(rizin): preserve primary outcomes across final cleanup * fix(browser): preserve admission failures and retain policy transport cleanup * fix(objdump): preserve input issues and provider launch failures * test(apple): attribute omitted edges to their node capacity limit * chore(metadata): synchronize packaged skill version * fix(cli): consume canonical output validation state * fix(views): preserve owning layout field diagnostics * test(pyinstaller): distinguish path and metadata allowance failures * fix(native): compose the selected command cause once * fix(setup): require HTTPS throughout Hopper download redirects * fix(ghidra): narrow fallback probe failures before projection * fix(native): require observed symbol origin for named identity * fix(javascript): retain unavailable source processing scopes * fix(cli): share declared output-error redaction * docs(windows): explain MCP registration launcher limits * fix(session): preserve binding commitments during restoration * fix(environment): retain primary facts through server cleanup failure * fix(pe): distinguish unsupported overlapping header layouts * fix(cli): keep positional admission placeholders omittable * test(cli): expose JavaScript progress workflow failure output * test(browser): match retained subframe limitations structurally * fix(javascript): preserve capture and property-key effect ownership * fix(ghidra): attribute import policy conflicts to settings * fix(native): validate Mach-O command framing before admission * docs(mcp): state plist inspection session prerequisite * fix(electron): separate script frame admission from declared source labels * test(environment): read asynchronous owner observations through contracts * test(javascript): distinguish resolver URLs from existing module targets * fix(ghidra): retain neutral recovery for observed SIGKILL * fix(hopper): preserve unknown relationship completeness * docs(native): clarify Darwin runtime breakpoint names * fix(browser): preserve unknown script ownership through reconciliation * fix(electron): keep optional provenance fields within JSON evidence values * fix(native): retain Mach-O architecture discriminant narrowing * test: give oversized registration groups semantic owners * test: group boundary registration by observed behavior * fix: normalize retained terminal observations with sampled identities * fix: identify missing Electron lineage ownership markers * fix(ida): retain registration constraints and identity refusal guidance * fix(kuna): retain published exports through staging cleanup failures * fix(nativeaot): charge only examined candidate table entries * docs: clarify batch decompilation retention limits * fix(javascript): distinguish deferred bootstrap installer writes * fix(native)!: preserve UI scalar facts and selection failures BREAKING CHANGE: Accessibility node value can also be a JSON number or boolean. Strings and null retain their prior meaning. * fix(javascript): retain activation order for captured scope exits * test(pyinstaller): use an absolute drive path for refusal coverage * fix(javascript): preserve route selection owners and uncertainty * fix(javascript): prove native URL constructor paths * refactor(tests): separate fixtures and registration groups * test(native): distinguish qualified symbol identity controls * fix(ida): preserve busy details across timeout attribution * fix(cli): unify builtin and global completion scope * test(ghidra): group exit-signal evidence controls * fix: retain narrowed terminal observation values * fix(cli): bind completion input to public registry type * fix(flutter): qualify unsupported unified snapshot profiles * test(native): bind origin comparison to containing entries * fix(cli): remove completion markers on system Bash * test(ida): preserve physical workspace traversal controls * fix(native): retain unknown UI executable identity * fix(native): unify LLDB discovery preparation failures * fix(evidence): preserve primary semantics during pending cleanup * fix(evidence): retain owner finalization reports across cleanup envelopes * test(browser): inspect provenance in owned graph observations * test(flutter): assert canonical unsupported profile error code * fix(javascript): capture primitive return values at evaluation * fix(mcp): own cancellation and raw input throughout startup * test(ghidra): construct exact import policy targets * fix(gdb): preserve configured executable launch recovery * test(ghidra): own fixed import target fixtures * docs(readme): align localized guide link occurrences * test(evidence): retain structured input issues through cleanup owners * test(mcp): await observed EOF before releasing startup * fix(electron): retain bounded source frame claims * fix(elf): validate declared file extents before provider admission * fix(elf): align metadata and admission fixture error types * test(wasm): place parsed interface comparison at provider boundary * test(ida): place capability composition at provider boundary * test(browser): place PNG contract execution at provider boundary * perf(test): share SDK catalog compilation across deferred clients * fix(cli): trim repeated selector discovery guidance * revert(test): remove unproven SDK compiler sharing This reverts commit 56eca648d. The equivalent completed two-client measurement increased elapsed and CPU time, so compiler sharing has not established a benefit. * refactor(managed): retain decoded metadata token facts * test(managed): scope cleanup failures to snapshot reads * test(cli): bound selector rejection cases by command * test(mips): distinguish damaged ELF admission from partial ABI evidence * test(reconstruction): declare authored function symbol identity * test(rizin): register terminal admission at provider boundary * test(process): align root admission fixture with observer allocation ownership * test(mips): project inspected ABI facts without undefined overrides * refactor(browser): remove obsolete web script reconciliation * refactor(javascript): remove obsolete in-process projection runner * refactor(managed): retain TypeDef row ownership through projections * test(process): assert canonical admission failure through cleanup envelopes * fix(managed): select types from retained definition records --------- Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
292 lines
10 KiB
TypeScript
292 lines
10 KiB
TypeScript
import { z } from "incur";
|
|
|
|
import { FridaInstrumentationService } from "../application/frida/FridaInstrumentationService.js";
|
|
import type {
|
|
FridaInstrumentationPort,
|
|
FridaRemoteConnection,
|
|
} from "../application/frida/FridaInstrumentationPort.js";
|
|
import { FridaInstrumentationManager } from "../frida/FridaInstrumentationManager.js";
|
|
import { CLI_COMMANDS } from "../cliCommandNames.js";
|
|
import { logCliCommand } from "../cliLogging.js";
|
|
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
|
import { AnalysisInputError } from "../domain/analysisErrorCore.js";
|
|
import { ok } from "../domain/result.js";
|
|
import { FRIDA_KEEPALIVE_INTERVAL_DESCRIPTION } from "../contracts/fridaToolContracts.js";
|
|
import { isValidFridaRemoteAddress } from "../contracts/fridaRemoteAddress.js";
|
|
import type { Logger } from "pino";
|
|
import type { CliInstance } from "./types.js";
|
|
import { withCommandCancellation } from "./commandCancellation.js";
|
|
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
|
|
|
/** Script sources stay optional in the options schema; exactly one is required. */
|
|
const exactlyOneScriptSource =
|
|
"Provide exactly one of --script or --script-file.";
|
|
|
|
/** Register Frida discovery and one-shot instrumentation CLI operations. */
|
|
export const registerFridaCommands = (
|
|
cli: CliInstance,
|
|
logger: Logger,
|
|
provider: FridaInstrumentationPort = new FridaInstrumentationManager(),
|
|
): void => {
|
|
const service = new FridaInstrumentationService(provider);
|
|
// CLI calls own a disposable provider; MCP retains its shared provider until
|
|
// server shutdown. Await cleanup here, including failed startup resources.
|
|
const cleanup = { close: () => provider.closeAll() };
|
|
const remoteOptions = z.object({
|
|
remoteAddress: z
|
|
.string()
|
|
.min(1)
|
|
.max(2048)
|
|
.refine(isValidFridaRemoteAddress)
|
|
.optional()
|
|
.describe("Remote Frida host address, optionally with a port"),
|
|
token: z.string().min(1).optional().describe("Remote authentication token"),
|
|
certificate: z
|
|
.string()
|
|
.min(1)
|
|
.optional()
|
|
.describe("Remote TLS certificate"),
|
|
origin: z.string().min(1).optional().describe("Remote connection origin"),
|
|
keepaliveInterval: z
|
|
.number()
|
|
.int()
|
|
.positive()
|
|
.optional()
|
|
.describe(FRIDA_KEEPALIVE_INTERVAL_DESCRIPTION),
|
|
});
|
|
const remoteFrom = (
|
|
options: z.infer<typeof remoteOptions>,
|
|
): FridaRemoteConnection | undefined =>
|
|
options.remoteAddress === undefined
|
|
? undefined
|
|
: {
|
|
address: options.remoteAddress,
|
|
...(options.token === undefined ? {} : { token: options.token }),
|
|
...(options.certificate === undefined
|
|
? {}
|
|
: { certificate: options.certificate }),
|
|
...(options.origin === undefined ? {} : { origin: options.origin }),
|
|
...(options.keepaliveInterval === undefined
|
|
? {}
|
|
: { keepaliveInterval: options.keepaliveInterval }),
|
|
};
|
|
|
|
const remoteAddressRequired = (
|
|
options: z.infer<typeof remoteOptions>,
|
|
): boolean =>
|
|
options.remoteAddress !== undefined ||
|
|
(options.token === undefined &&
|
|
options.certificate === undefined &&
|
|
options.origin === undefined &&
|
|
options.keepaliveInterval === undefined);
|
|
|
|
cli.command(CLI_COMMANDS.listFridaDevices, {
|
|
description: "List local Frida devices and an optional remote endpoint",
|
|
args: z.object({}),
|
|
options: remoteOptions.refine(
|
|
remoteAddressRequired,
|
|
"Remote credentials and connection options require remoteAddress",
|
|
),
|
|
run: ({ options }) =>
|
|
withCommandCancellation((signal) =>
|
|
logCliCommand(logger, CLI_COMMANDS.listFridaDevices, () =>
|
|
finishAnalysisCommand(
|
|
() => service.listDevices(remoteFrom(options)),
|
|
cleanup,
|
|
"frida",
|
|
"list_frida_devices",
|
|
signal,
|
|
),
|
|
),
|
|
),
|
|
});
|
|
cli.command(CLI_COMMANDS.listFridaProcesses, {
|
|
description: "List processes visible through a Frida device",
|
|
args: z.object({}),
|
|
options: remoteOptions
|
|
.extend({
|
|
deviceId: z
|
|
.string()
|
|
.min(1)
|
|
.optional()
|
|
.describe("Frida device identifier"),
|
|
})
|
|
.refine(
|
|
(options) =>
|
|
options.remoteAddress === undefined || options.deviceId === undefined,
|
|
"Choose either deviceId or remoteAddress, not both",
|
|
)
|
|
.refine(
|
|
remoteAddressRequired,
|
|
"Remote credentials and connection options require remoteAddress",
|
|
),
|
|
run: ({ options }) =>
|
|
withCommandCancellation((signal) =>
|
|
logCliCommand(logger, CLI_COMMANDS.listFridaProcesses, () => {
|
|
const remote = remoteFrom(options);
|
|
return finishAnalysisCommand(
|
|
() =>
|
|
service.listProcesses(
|
|
remote === undefined
|
|
? { deviceId: options.deviceId ?? "local" }
|
|
: { remote },
|
|
),
|
|
cleanup,
|
|
"frida",
|
|
"list_frida_processes",
|
|
signal,
|
|
);
|
|
}),
|
|
),
|
|
});
|
|
cli.command(CLI_COMMANDS.instrumentWithFrida, {
|
|
description: `Run one Frida script against a selected process and detach. ${exactlyOneScriptSource}`,
|
|
args: z.object({
|
|
mode: z
|
|
.enum(["attach", "spawn"])
|
|
.describe("Attach to a PID or spawn a program"),
|
|
target: z
|
|
.string()
|
|
.min(1)
|
|
.describe("Process ID for attach or program path for spawn"),
|
|
}),
|
|
options: remoteOptions
|
|
.extend({
|
|
deviceId: z
|
|
.string()
|
|
.min(1)
|
|
.optional()
|
|
.describe("Frida device identifier"),
|
|
script: z
|
|
.string()
|
|
.optional()
|
|
.describe(`Inline JavaScript to load. ${exactlyOneScriptSource}`),
|
|
scriptFile: z
|
|
.string()
|
|
.min(1)
|
|
.optional()
|
|
.describe(
|
|
`Path to JavaScript source file. ${exactlyOneScriptSource}`,
|
|
),
|
|
argument: z
|
|
.array(z.string())
|
|
.optional()
|
|
.describe("Arguments passed to a spawned target"),
|
|
durationMs: z
|
|
.number()
|
|
.int()
|
|
.min(0)
|
|
.max(60_000)
|
|
.default(1_000)
|
|
.describe("Instrumentation duration in milliseconds"),
|
|
})
|
|
.refine(
|
|
(options) =>
|
|
options.remoteAddress === undefined || options.deviceId === undefined,
|
|
"Choose either deviceId or remoteAddress, not both",
|
|
)
|
|
.refine(
|
|
remoteAddressRequired,
|
|
"Remote credentials and connection options require remoteAddress",
|
|
),
|
|
run: ({ args, options }) =>
|
|
withCommandCancellation((signal) =>
|
|
logCliCommand(logger, CLI_COMMANDS.instrumentWithFrida, async () => {
|
|
if (args.mode === "attach" && options.argument !== undefined)
|
|
return projectAnalysisError(
|
|
new AnalysisInputError(
|
|
CLI_COMMANDS.instrumentWithFrida,
|
|
undefined,
|
|
[
|
|
{
|
|
path: ["argument"],
|
|
reason: "invalid_value",
|
|
message:
|
|
"--argument is only valid in spawn mode; remove it when attaching to a process.",
|
|
},
|
|
],
|
|
),
|
|
);
|
|
if (
|
|
(options.script === undefined) ===
|
|
(options.scriptFile === undefined)
|
|
)
|
|
return projectAnalysisError(
|
|
new AnalysisInputError(
|
|
CLI_COMMANDS.instrumentWithFrida,
|
|
undefined,
|
|
[
|
|
{
|
|
path: ["script"],
|
|
reason: "invalid_value",
|
|
message:
|
|
"Provide exactly one of --script or --script-file.",
|
|
},
|
|
],
|
|
),
|
|
);
|
|
const remote = remoteFrom(options);
|
|
const selected =
|
|
remote === undefined
|
|
? { deviceId: options.deviceId ?? "local" }
|
|
: { remote };
|
|
const targetInput =
|
|
args.mode === "attach"
|
|
? /^[1-9][0-9]*$/u.test(args.target) &&
|
|
Number.isSafeInteger(Number(args.target)) &&
|
|
Number(args.target) > 0
|
|
? {
|
|
...selected,
|
|
mode: "attach" as const,
|
|
pid: Number(args.target),
|
|
}
|
|
: undefined
|
|
: {
|
|
...selected,
|
|
mode: "spawn" as const,
|
|
program: args.target,
|
|
...(options.argument === undefined
|
|
? {}
|
|
: { argv: options.argument }),
|
|
};
|
|
if (targetInput === undefined)
|
|
return projectAnalysisError(
|
|
new AnalysisInputError(
|
|
CLI_COMMANDS.instrumentWithFrida,
|
|
undefined,
|
|
[
|
|
{
|
|
path: ["target"],
|
|
reason: "invalid_value",
|
|
message: "attach mode needs a positive integer process ID.",
|
|
},
|
|
],
|
|
),
|
|
);
|
|
return finishAnalysisCommand(
|
|
async () => {
|
|
const result = await service.instrument(
|
|
{
|
|
...targetInput,
|
|
source:
|
|
options.scriptFile === undefined
|
|
? { sourceKind: "inline", source: options.script ?? "" }
|
|
: { sourceKind: "file", path: options.scriptFile },
|
|
durationMs: options.durationMs,
|
|
},
|
|
signal,
|
|
);
|
|
if (!result.ok) return result;
|
|
if (result.value.cleanupError !== null) process.exitCode = 1;
|
|
return ok(result.value.evidence);
|
|
},
|
|
cleanup,
|
|
"frida",
|
|
"instrument_with_frida",
|
|
signal,
|
|
);
|
|
}),
|
|
),
|
|
});
|
|
};
|