mirror of
https://github.com/morluto/rea.git
synced 2026-11-04 22:06:10 +00:00
* fix(ci): retire unused internal analysis declarations * test: use an admitted app bundle for provider routing (cherry picked from commit00aa17f12a) * test: keep bundle routing coverage outside oversized group (cherry picked from commit8658353b0d) * fix(ci): align regression fixtures with current admission contracts * test: bind extraction close fixtures to their ZIP digest (cherry picked from commit626d0250db) * 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>
176 lines
6.9 KiB
TypeScript
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,
|
|
);
|
|
},
|
|
);
|