Files
reverse-engineering-anything/src/cli/il2cppCommands.ts
T
morluto 9d7779d365 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.
2026-10-24 07:28:09 +08:00

110 lines
4.1 KiB
TypeScript

import { localPathStringSchema } from "../domain/localPath.js";
import { absoluteCallerPath } from "../filesystem/callerPath.js";
import { z } from "incur";
import { AnalysisInputError } from "../domain/analysisErrorCore.js";
import { err } from "../domain/result.js";
import { createIl2cppMetadataService } from "../composition/il2cpp.js";
import { CLI_COMMANDS } from "../cliCommandNames.js";
import { logCliCommand } from "../cliLogging.js";
import type { Logger } from "pino";
import type { CliInstance } from "./types.js";
import { withCommandCancellation } from "./commandCancellation.js";
import {
analysisEvidenceDeliveryOptions,
deliverCliAnalysisEvidence,
} from "./analysisEvidenceDelivery.js";
/** CLI and MCP share inspection; one-shot summaries preserve a complete Evidence file. */
export const registerIl2cppCommands = (
cli: CliInstance,
logger: Logger,
): void => {
cli.command(CLI_COMMANDS.inspectIl2cppMetadata, {
description:
"Inventory IL2CPP global-metadata.dat names, owners and string literals, optionally linking method addresses from the native binary",
args: z.object({
path: localPathStringSchema.describe(
"Local global-metadata.dat (version 24, 27, 29, 31 or 39)",
),
}),
options: z.object({
...analysisEvidenceDeliveryOptions,
binary: localPathStringSchema
.optional()
.describe(
"Matching native binary (Mach-O GameAssembly.dylib, PE GameAssembly.dll or ELF libil2cpp.so) to link method addresses; read only",
),
architecture: z
.enum(["arm64", "amd64"])
.optional()
.describe("Slice of a universal Mach-O binary"),
ghidraSeeds: localPathStringSchema
.optional()
.describe(
"Also write REA_GHIDRA_SEED_FILE function seeds named Type$$Method for every linked method; needs --binary",
),
seedBase: z
.string()
.regex(/^0x[0-9a-fA-F]{1,16}$/u)
.optional()
.describe(
"Address Ghidra loads the binary at; seeds are seed-base + RVA (default: Ghidra's loader default, 0x100000 for an ELF linked at 0, else the link base)",
),
}),
run: ({ args, options }) =>
withCommandCancellation((signal) =>
logCliCommand(logger, CLI_COMMANDS.inspectIl2cppMetadata, async () => {
const service = createIl2cppMetadataService();
return deliverCliAnalysisEvidence({
run: () => {
const input = {
path: absoluteCallerPath(args.path),
detail: "complete" as const,
...(options.binary === undefined
? {}
: { binary_path: absoluteCallerPath(options.binary) }),
...(options.architecture === undefined
? {}
: { binary_architecture: options.architecture }),
};
if (options.ghidraSeeds !== undefined)
return service.inspectWithGhidraSeeds(
input,
{
path: absoluteCallerPath(options.ghidraSeeds),
...(options.seedBase === undefined
? {}
: { base: options.seedBase }),
},
{ signal },
);
if (options.seedBase !== undefined)
return Promise.resolve(
err(
new AnalysisInputError(
"inspect_il2cpp_metadata",
undefined,
[
{
path: ["seed_base"],
reason: "invalid_value",
message: "--seed-base requires --ghidra-seeds.",
},
],
),
),
);
return service.inspect(input, { signal });
},
service,
providerId: "il2cpp-metadata",
operation: "inspect_il2cpp_metadata",
signal,
detail: options.detail,
evidenceOutput: options.evidence_output,
});
}),
),
});
};