219 lines
6.9 KiB
TypeScript
219 lines
6.9 KiB
TypeScript
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { expect } from "vitest";
|
|
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
import { analysisViewLayoutEvidence } from "../../fixtures/analysisView.js";
|
|
import { createEvidence } from "../../../src/domain/evidence.js";
|
|
import { ghidraFunctionDossier } from "../../../src/domain/ghidraValues.fixture.js";
|
|
import { createEvidenceBundle } from "../../../src/domain/evidenceBundle.js";
|
|
import { inspectEvidenceBundle } from "../../../src/application/investigation/InspectEvidenceBundle.js";
|
|
|
|
cliTest(
|
|
"discovers native records from a complete portable bundle using the shared workflow",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-evidence-discovery-cli-");
|
|
const parent = createEvidence(
|
|
{ path: "/fixtures/native.exe", format: "pe", sha256: "a".repeat(64) },
|
|
{ id: "ghidra", name: "Ghidra", version: "12.1.4" },
|
|
{
|
|
operation: "analyze_function",
|
|
parameters: {},
|
|
result: ghidraFunctionDossier(),
|
|
},
|
|
);
|
|
const bundle = createEvidenceBundle([parent]);
|
|
const path = join(root, "evidence.json");
|
|
await writeFile(path, JSON.stringify(bundle));
|
|
const options = {
|
|
detail: "summary" as const,
|
|
filters: { evidence_id: parent.evidence_id },
|
|
};
|
|
const result = await cli.run({
|
|
arguments: [
|
|
"inspect-evidence-bundle",
|
|
JSON.stringify({ path, ...options }),
|
|
"--json",
|
|
],
|
|
environment: { HOME: root, XDG_CONFIG_HOME: root, XDG_CACHE_HOME: root },
|
|
});
|
|
expect(result.exitCode).toBe(0);
|
|
const expected = inspectEvidenceBundle(bundle, options);
|
|
if (!expected.ok) throw expected.error;
|
|
expect(result.json).toEqual(expected.value);
|
|
const complete = await cli.run({
|
|
arguments: [
|
|
"inspect-evidence-bundle",
|
|
JSON.stringify({ path }),
|
|
"--json",
|
|
],
|
|
environment: { HOME: root, XDG_CONFIG_HOME: root, XDG_CACHE_HOME: root },
|
|
});
|
|
expect(complete.exitCode).toBe(0);
|
|
expect(complete.json).toEqual(bundle);
|
|
},
|
|
);
|
|
cliTest(
|
|
"projects one section from a JSON file of inline layout Evidence",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-analysis-view-cli-");
|
|
const parent = analysisViewLayoutEvidence();
|
|
const request = {
|
|
source: { kind: "inline", evidence: parent },
|
|
view: {
|
|
kind: "item",
|
|
collection: "sections",
|
|
selector: { name: ".data" },
|
|
},
|
|
};
|
|
const input = join(root, "view.json");
|
|
await mkdir(root, { recursive: true });
|
|
await writeFile(input, JSON.stringify(request));
|
|
const result = await cli.run({
|
|
arguments: ["inspect-analysis-view", input, "--json"],
|
|
environment: {
|
|
HOME: root,
|
|
XDG_CONFIG_HOME: root,
|
|
XDG_CACHE_HOME: root,
|
|
},
|
|
});
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.json).toMatchObject({
|
|
operation: "inspect_analysis_view",
|
|
normalized_result: {
|
|
kind: "item",
|
|
parent_evidence_id: parent.evidence_id,
|
|
item: { name: { display: ".data" } },
|
|
},
|
|
});
|
|
},
|
|
);
|
|
|
|
cliTest(
|
|
"projects native function pseudocode without requiring Ghidra",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-native-view-cli-");
|
|
const parent = createEvidence(
|
|
{ path: "/fixtures/native.exe", format: "pe", sha256: "a".repeat(64) },
|
|
{ id: "ghidra", name: "Ghidra", version: "12.1.4" },
|
|
{
|
|
operation: "analyze_function",
|
|
parameters: { address: "0x401000" },
|
|
result: ghidraFunctionDossier(),
|
|
},
|
|
);
|
|
const input = join(root, "native-view.json");
|
|
await writeFile(
|
|
input,
|
|
JSON.stringify({
|
|
source: { kind: "inline", evidence: parent },
|
|
view: { kind: "native", facet: "pseudocode", offset: 0, limit: 12 },
|
|
}),
|
|
);
|
|
const result = await cli.run({
|
|
arguments: ["inspect-analysis-view", input, "--json"],
|
|
environment: { HOME: root, XDG_CONFIG_HOME: root, XDG_CACHE_HOME: root },
|
|
});
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.json).toMatchObject({
|
|
operation: "inspect_analysis_view",
|
|
normalized_result: {
|
|
kind: "native",
|
|
parent_evidence_id: parent.evidence_id,
|
|
procedure_address: "0x401000",
|
|
item: { text: "int fixture_", unit: "utf16-code-units" },
|
|
},
|
|
locations: [
|
|
{ kind: "artifact-path", path: "/fixtures/native.exe" },
|
|
{ kind: "address", address: "0x401000" },
|
|
],
|
|
});
|
|
},
|
|
);
|
|
|
|
cliTest("rejects malformed JSON before projection", async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-analysis-view-cli-bad-");
|
|
const result = await cli.run({
|
|
arguments: ["inspect-analysis-view", "{", "--json"],
|
|
environment: {
|
|
HOME: root,
|
|
XDG_CONFIG_HOME: root,
|
|
XDG_CACHE_HOME: root,
|
|
},
|
|
});
|
|
expect(result.exitCode).toBe(1);
|
|
expect(result.json).toMatchObject({
|
|
code: "invalid_request",
|
|
category: "invalid_input",
|
|
details: { operation: "inspect-analysis-view" },
|
|
});
|
|
});
|
|
|
|
cliTest("rejects a request that omits source", async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-analysis-view-cli-missing-");
|
|
const input = join(root, "missing-source.json");
|
|
await writeFile(input, JSON.stringify({ view: { kind: "summary" } }));
|
|
const result = await cli.run({
|
|
arguments: ["inspect-analysis-view", input, "--json"],
|
|
environment: {
|
|
HOME: root,
|
|
XDG_CONFIG_HOME: root,
|
|
XDG_CACHE_HOME: root,
|
|
},
|
|
});
|
|
expect(result.exitCode).toBe(1);
|
|
expect(result.json).toMatchObject({
|
|
code: "invalid_request",
|
|
category: "invalid_input",
|
|
details: { operation: "inspect-analysis-view" },
|
|
});
|
|
});
|
|
|
|
for (const [view, reason] of [
|
|
[{ kind: "unknown-view" }, "invalid_value"],
|
|
[{}, "missing_argument"],
|
|
] as const) {
|
|
cliTest(
|
|
`reports correction details for ${reason} view kind`,
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-view-variant-");
|
|
const input = join(root, "view.json");
|
|
await writeFile(
|
|
input,
|
|
JSON.stringify({
|
|
source: {
|
|
kind: "inline",
|
|
evidence: analysisViewLayoutEvidence(),
|
|
},
|
|
view,
|
|
}),
|
|
);
|
|
const result = await cli.run({
|
|
arguments: ["inspect-analysis-view", input, "--json"],
|
|
environment: {
|
|
HOME: root,
|
|
XDG_CONFIG_HOME: root,
|
|
XDG_CACHE_HOME: root,
|
|
},
|
|
});
|
|
expect(result.exitCode).toBe(1);
|
|
expect(result.json).toMatchObject({
|
|
code: "invalid_request",
|
|
details: {
|
|
issues: [
|
|
{
|
|
path: ["view", "kind"],
|
|
reason,
|
|
expected: expect.arrayContaining(["summary", "page"]),
|
|
},
|
|
],
|
|
},
|
|
});
|
|
expect(JSON.stringify(result.json)).not.toContain("unknown-view");
|
|
},
|
|
);
|
|
}
|