Files
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

312 lines
11 KiB
TypeScript

import { createHash } from "node:crypto";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it, vi } from "vitest";
import * as comparison from "../../../src/application/managed/ManagedMemberComparisonService.js";
import { ArtifactResourceScope } from "../../../src/artifacts/ArtifactResourceScope.js";
import { registerManagedCommands } from "../../../src/cli/managedCommands.js";
import { CLI_COMMANDS } from "../../../src/cliCommandNames.js";
import { AnalysisOutputError } from "../../../src/domain/analysisErrorCore.js";
import { err, ok } from "../../../src/domain/result.js";
import { silentLogger } from "../../../src/logger.js";
import { parseEvidence } from "../../../src/domain/evidence.js";
import { managedMemberInspectionSchema } from "../../../src/domain/managed/managedArtifact.js";
import { managedMemberComparisonResultSchema } from "../../../src/domain/managed/managedMemberComparison.js";
import { buildManagedPeFixture } from "../../../src/dotnet/ManagedPe.fixture.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest(
"attributes malformed managed comparison input to its selected argument and retains completed peer Evidence",
async ({ cli }) => {
const directory = await createTestTempDirectory(
"rea-managed-malformed-cli-",
);
const good = join(directory, "good.dll");
const bad = join(directory, "bad.dll");
const bytes = buildManagedPeFixture();
await writeFile(good, bytes);
bytes.writeUInt16LE(0, bytes.readUInt32LE(0x3c) + 6);
await writeFile(bad, bytes);
const healthy = await cli.run({
arguments: ["compare-managed-members", good, good, "--json"],
});
expect(healthy.exitCode, healthy.stderr).toBe(0);
expect(healthy.json).toMatchObject({
operation: "compare_managed_members",
parameters: { left_path: good, right_path: good },
});
for (const field of ["leftPath", "rightPath"] as const) {
const result = await cli.run({
arguments: [
"compare-managed-members",
field === "leftPath" ? bad : good,
field === "rightPath" ? bad : good,
"--json",
],
});
expect(result.exitCode, result.stderr).toBe(1);
expect(result.json).toMatchObject({
code: "invalid_request",
category: "invalid_input",
retryable: false,
details: {
issues: [
{
path: [field],
reason: "invalid_format",
message: `Selected artifact ${bad}: PE section count must be positive`,
},
],
},
});
if (field === "rightPath")
expect(result.json).toMatchObject({
details: {
partial_observation: {
operation: "inspect_managed_members",
subject: { local_path: good },
},
},
});
else
expect(result.json).not.toHaveProperty("details.partial_observation");
}
const inspection = await cli.run({
arguments: ["inspect-managed-members", bad, "--json"],
});
expect(inspection.exitCode, inspection.stderr).toBe(1);
expect(inspection.json).toMatchObject({
code: "invalid_request",
category: "invalid_input",
details: {
issues: [
{
path: ["path"],
reason: "invalid_format",
message: `Selected artifact ${bad}: PE section count must be positive`,
},
],
},
});
},
);
cliTest(
"keeps malformed signatures unknown through CLI and stdio MCP comparison while a valid control stays unchanged",
async ({ cli }) => {
const directory = await createTestTempDirectory(
"rea-managed-signature-cli-",
);
const call = await cli.sharedMcpCall!();
for (const malformed of [true, false]) {
const name = malformed ? "truncated" : "control";
const path = join(directory, `${name}.dll`);
const peerPath = join(directory, `${name}-peer.dll`);
const bytes = buildManagedPeFixture({
methodSignature: Buffer.from(
malformed ? [0x00, 0x80] : [0x00, 0x00, 0x01],
),
ilBody: Buffer.from([0x06, 0x2a]),
});
const sha256 = createHash("sha256").update(bytes).digest("hex");
await writeFile(path, bytes);
await writeFile(peerPath, bytes);
const inspected = await cli.sharedCliRun!({
arguments: ["inspect-managed-members", path, "--json"],
});
expect(inspected.exitCode, inspected.stderr).toBe(0);
const cliEvidence = parseEvidence(inspected.json);
const mcpInspected = await call("inspect_managed_members", {
path: peerPath,
});
expect(mcpInspected.isError, JSON.stringify(mcpInspected)).not.toBe(true);
const mcpEvidence = parseEvidence(mcpInspected.structuredContent);
for (const [evidence, sourcePath] of [
[cliEvidence, path],
[mcpEvidence, peerPath],
] as const) {
const report = managedMemberInspectionSchema.parse(
evidence.normalized_result,
);
expect(report.artifact).toMatchObject({ path: sourcePath, sha256 });
expect(report.coverage.state).toBe(malformed ? "partial" : "complete");
expect(report.methods[0]?.signature).toMatchObject({
parse_status: malformed ? "malformed" : "decoded",
...(malformed
? { issue: "truncated compressed integer", resolved_sha256: null }
: {}),
});
}
const compared = await cli.sharedCliRun!({
arguments: ["compare-managed-members", path, path, "--json"],
});
expect(compared.exitCode, compared.stderr).toBe(0);
// Identical bytes under separate paths provide distinct authenticated
// observations for MCP, whose Evidence inputs must have different IDs.
const mcpCompared = await call("compare_managed_members", {
left: cliEvidence,
right: mcpEvidence,
});
expect(mcpCompared.isError, JSON.stringify(mcpCompared)).not.toBe(true);
for (const evidence of [
parseEvidence(compared.json),
parseEvidence(mcpCompared.structuredContent),
]) {
const report = managedMemberComparisonResultSchema.parse(
evidence.normalized_result,
);
expect(report.left.artifact_sha256).toBe(sha256);
expect(report.right.artifact_sha256).toBe(sha256);
expect(report.coverage.status).toBe(
malformed ? "partial" : "complete-within-inputs",
);
expect(report.summary).toEqual({
unchanged: malformed ? 1 : 2,
changed: 0,
added: 0,
removed: 0,
unknown: malformed ? 1 : 0,
});
expect(report.methods).toEqual([
expect.objectContaining({
status: malformed ? "unknown" : "unchanged",
match: expect.objectContaining({
status: "matched",
basis: "exact-il-signature",
confidence: "exact",
}),
limitations: malformed
? [expect.stringMatching(/^signature-malformed:/)]
: [],
}),
]);
expect(report.fields[0]?.status).toBe("unchanged");
}
}
},
);
const managedComparisonEvidence = async () => {
const directory = await createTestTempDirectory("rea-managed-finalization-");
const path = join(directory, "comparison.dll");
await writeFile(path, buildManagedPeFixture());
const result = await comparison.compareManagedMemberPaths({
leftPath: path,
rightPath: path,
});
if (!result.ok) throw result.error;
return result.value;
};
const registeredComparisonCommand = () => {
const commands = new Map<string, { run(input: unknown): Promise<unknown> }>();
const cli = {
command: (
name: string,
definition: { run(input: unknown): Promise<unknown> },
) => {
commands.set(name, definition);
return cli;
},
} as unknown as Parameters<typeof registerManagedCommands>[0];
registerManagedCommands(cli, silentLogger, async () => ({
ok: false,
error: null,
}));
return commands.get(CLI_COMMANDS.compareManagedMembers)!;
};
it.each([true, false])(
"retains the comparison outcome when final cleanup fails (success: %s)",
async (success) => {
const evidence = await managedComparisonEvidence();
const run = vi
.spyOn(comparison, "compareManagedMemberPaths")
.mockResolvedValue(
success
? ok(evidence)
: err(
new AnalysisOutputError(
"compare_managed_members",
"primary comparison failure",
{
partialObservation: evidence,
},
),
),
);
const close = vi
.spyOn(ArtifactResourceScope.prototype, "close")
.mockRejectedValue(new Error("comparison close denied"));
try {
const result = await registeredComparisonCommand().run({
args: { leftPath: "left.dll", rightPath: "right.dll" },
});
expect(result).toMatchObject({
error: "Managed member comparison failed",
code: "cleanup_incomplete",
details: {
partial_observation: { evidence_id: evidence.evidence_id },
},
});
expect(result).toMatchObject({
details: {
diagnostics: success
? { reason: "comparison close denied" }
: {
primary_error: {
code: "unreadable_output",
details: { reason: "primary comparison failure" },
},
cleanup_error: {
code: "cleanup_incomplete",
details: {
diagnostics: { reason: "comparison close denied" },
},
},
},
},
});
expect(close).toHaveBeenCalledOnce();
} finally {
run.mockRestore();
close.mockRestore();
}
},
);
it("retains completed comparison Evidence when interrupted during final cleanup", async () => {
const evidence = await managedComparisonEvidence();
const run = vi
.spyOn(comparison, "compareManagedMemberPaths")
.mockResolvedValue(ok(evidence));
const close = vi
.spyOn(ArtifactResourceScope.prototype, "close")
.mockImplementation(async () => {
process.emit("SIGINT");
});
const previousExitCode = process.exitCode;
try {
const result = await registeredComparisonCommand().run({
args: { leftPath: "left.dll", rightPath: "right.dll" },
});
expect(result).toMatchObject({
error: "Managed member comparison failed",
code: "cancelled",
details: { partial_observation: { evidence_id: evidence.evidence_id } },
});
expect(process.exitCode).toBe(130);
} finally {
process.exitCode = previousExitCode;
run.mockRestore();
close.mockRestore();
}
});