Files
reverse-engineering-anything/tests/acceptance/applications/nativePlistNesting.test.ts
T
1201c25542 fix(ci): align static checks and regression fixtures with current contracts (#7349)
* fix(ci): retire unused internal analysis declarations

* test: use an admitted app bundle for provider routing

(cherry picked from commit 00aa17f12a)

* test: keep bundle routing coverage outside oversized group

(cherry picked from commit 8658353b0d)

* fix(ci): align regression fixtures with current admission contracts

* test: bind extraction close fixtures to their ZIP digest

(cherry picked from commit 626d0250db)

* test(cli): match typed analysis outcomes in function detail fixture

* test(ghidra): preserve empty Java property separator framing

* test(electron): retain unknown IPC ownership for native module gaps

* fix(electron): share source-scoped call path identity

* test(captures): assert safe source identity on SAZ refusal

* test(apple): match root occurrence kind to inventory manifest

* fix(examples): align directory root occurrence identity

* test(electron): align equivalent fixture assertions with upstream

* test(process): assert retained capture root identity

* test(windows): declare the PE fixture optional header

* test(ci): refresh owned registration and verifier fixtures

* test(setup): use owned registrations for refresh fixtures

* test(process): control unavailable ownership in CLI cleanup fixture

* test(process): guard controlled compiler output arguments

* test(setup): align equivalent entrypoint guards with upstream

* test(process): release the cleanup target through an owned gate

* test(ci): align semantic identities and owned fixture facts

* test(pcap): retain the upstream DNS wire fixture branch

* test(pcap): use the canonical wire ownership fixture

* test(pcap): assert exact DNS wire query labels

* test(ci): preserve admission and ownership fixture prerequisites

* test(ci): align authenticated fixture and cleanup boundaries

* test(windows): align native artifact fixtures with upstream

* test(android): request complete evidence for inline MCP contracts

* test: avoid pinning diagnostic wording

* test: verify cleanup diagnostics at their source

* test: strengthen evidence and cleanup checks

* test: avoid pinning registration guidance

---------

Co-authored-by: Plastic-time <Plastic-time@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-27 21:24:30 +08:00

176 lines
6.9 KiB
TypeScript

import { execFile } from "node:child_process";
import { readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { buildBinary, type PlistValue } from "plist";
import { describe, expect } from "vitest";
import { thinMach } from "../../../src/domain/binaryTarget.fixture.js";
import { parseEvidence } from "../../../src/domain/evidence.js";
import { inspectPlistSchema } from "../../../src/domain/native/nativeInspection.js";
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
const lintXmlPlist = (path: string) =>
new Promise<{ exitCode: number; stdout: string; stderr: string }>(
(resolve, reject) => {
execFile(
"/usr/bin/plutil",
["-lint", "--", path],
{ timeout: 10_000 },
(error, stdout, stderr) => {
const exitCode = error === null ? 0 : error.code;
if (typeof exitCode !== "number") {
reject(error);
return;
}
resolve({ exitCode, stdout, stderr });
},
);
},
);
describe.skipIf(process.platform !== "darwin")(
"ordinary plist source nesting",
() => {
cliTest(
"retains depth-129 data and depth-200 integers through CLI and MCP",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-plist-nesting-");
const target = join(root, "target");
await writeFile(target, thinMach(0xfeedfacf, 0x0100000c));
const environment = {
HOME: root,
USERPROFILE: root,
REA_LOG_LEVEL: "silent",
REA_ANALYSIS_PROVIDER: "auto",
};
const { call } = await connectLocalToolsMcp(environment);
expect((await call("open_binary", { path: target })).isError).not.toBe(
true,
);
for (const fixture of [
{ format: "binary", depth: 129, data: true },
{ format: "xml", depth: 129, data: true },
{ format: "binary", depth: 200, data: false },
{ format: "xml", depth: 200, data: false },
{ format: "json", depth: 200, data: false },
{ format: "binary", depth: 998, data: true },
{ format: "binary", depth: 999, data: true },
{ format: "xml", depth: 998, data: true },
{ format: "xml", depth: 999, data: true },
{ format: "json", depth: 998, data: false },
{ format: "json", depth: 999, data: false },
] as const) {
const { format, depth, data } = fixture;
const path = join(root, `${format}-${depth}.plist`);
let value: PlistValue = data ? Buffer.from([0, 1]) : 1;
for (let level = 0; level < depth; level += 1)
value = { level: value };
const bytes =
format === "binary"
? Buffer.from(buildBinary(value))
: Buffer.from(
format === "json"
? JSON.stringify(value)
: `<plist>${"<dict><key>level</key>".repeat(depth)}${data ? "<data>AAE=</data>" : "<integer>1</integer>"}${"</dict>".repeat(depth)}</plist>`,
);
await writeFile(path, bytes);
// The decoder ceiling does not override native XML admission.
// Compare its upper-edge admission with the actual utility, while
// requiring the historical depth-129/200 controls to succeed below.
const native =
format === "xml" && depth === 998
? await lintXmlPlist(path)
: undefined;
const output = await cli.run({
arguments: ["inspect-plist", path, "--json"],
environment,
});
const response = await call("inspect_plist", { path });
expect(Buffer.compare(await readFile(path), bytes)).toBe(0);
if (depth === 999) {
expect(output.exitCode, output.stderr).toBe(1);
const expectedFailure = {
code: "resource_constraint",
category: "resource_constraint",
retryable: false,
details: {
resource: "memory",
reported_limits: { maximum_nesting_depth: 998 },
},
remediation: {
action: expect.stringMatching(/\S/u),
},
};
expect(output.json).toMatchObject(expectedFailure);
expect(parseMcpToolError(response).error).toMatchObject(
expectedFailure,
);
continue;
}
if (native !== undefined && native.exitCode !== 0) {
expect(output.exitCode, output.stderr).toBe(1);
const expectedFailure = {
code: "invalid_request",
retryable: false,
details: {
operation: "inspect_plist",
captured_output: {
exit_code: native.exitCode,
stdout: native.stdout,
stderr: native.stderr,
},
},
};
expect(output.json).toMatchObject(expectedFailure);
expect(parseMcpToolError(response).error).toMatchObject(
expectedFailure,
);
continue;
}
expect(
output.exitCode,
JSON.stringify({
format,
depth,
stderr: output.stderr,
failure: output.exitCode === 0 ? undefined : output.json,
}),
).toBe(0);
const cliResult = inspectPlistSchema.parse(
parseEvidence(output.json).normalized_result,
);
expect(response.isError, JSON.stringify(response)).not.toBe(true);
const mcpResult = inspectPlistSchema.parse(
parseEvidence(response.structuredContent).normalized_result,
);
expect(mcpResult).toEqual(cliResult);
// Generic JSON text remains unclassified; a JSON conversion alone
// does not establish a source-format observation.
expect(cliResult.format).toBe(format === "json" ? "unknown" : format);
expect(cliResult.provenance.map((item) => item.tool)).toEqual([
"file",
...(format === "xml" ? ["plutil"] : []),
]);
let leaf: unknown = cliResult.value;
for (let level = 0; level < depth; level += 1) {
expect(leaf).toHaveProperty("level");
leaf = (leaf as Record<string, unknown>).level;
}
expect(leaf).toEqual(
data ? { $plist_type: "data", base64: "AAE=" } : 1,
);
expect(
cliResult.limitations.some((item) =>
item.includes("XML conversion"),
),
).toBe(false);
}
},
60_000,
);
},
);