mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 10:43:30 +00:00
Give atomic Evidence and snapshot writes an explicit resource owner, preserve primary failures and completed publication receipts, and retry retained staging cleanup at the owning lifecycle. Verify inode identity before publishing or deleting; unknown path disappearance remains a cleanup failure. Keep snapshot admission reusable when a session closes and reopens. Consolidate JavaScript source-scope classification so cancellation cannot fabricate failures for unselected files. Classify configuration failures as invalid input. Remove stale assertions and reuse shared test cleanup ownership. Validation: 266 focused tests before final ownership tightening, 108 affected tests afterward, static and documentation checks, actual invalid-configuration CLI behavior, and isolated test temporary-directory hygiene. Refs #1968
106 lines
3.9 KiB
TypeScript
106 lines
3.9 KiB
TypeScript
import { chmod, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { withArtifactResourceScope } from "../../fixtures/artifactInventory.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
import { writeAnalysisSnapshot } from "../../../src/application/binary/AnalysisSnapshotFiles.js";
|
|
import { createDirectAnalysis } from "../../../src/composition/directAnalysis.js";
|
|
import type { AnalysisSnapshot } from "../../../src/domain/analysisSnapshot.js";
|
|
import {
|
|
snapshotBinding,
|
|
snapshotTarget,
|
|
} from "../../../src/domain/analysisSnapshot.js";
|
|
import { parseBinaryTarget } from "../../../src/application/BinaryTargetResolver.js";
|
|
import { createEvidence } from "../../../src/domain/evidence.js";
|
|
import { createEvidenceBundle } from "../../../src/domain/evidenceBundle.js";
|
|
import {
|
|
REA_WORKFLOW_PROVIDER,
|
|
workflowAnalysisProfile,
|
|
} from "../../../src/application/InvestigationProviders.js";
|
|
import { HOPPER_PROVIDER_IDENTITY } from "../../../src/hopper/HopperProviderCapabilities.js";
|
|
import { resolveHopperAnalysisProfile } from "../../../src/hopper/HopperAnalysisProfile.js";
|
|
|
|
afterEach(async () => {
|
|
vi.unstubAllEnvs();
|
|
});
|
|
|
|
describe("direct analysis snapshot files", () => {
|
|
it("forwards the CLI provider selector ahead of the environment preference", async () => {
|
|
const directory = await createTestTempDirectory("rea-direct-provider-");
|
|
const targetPath = join(directory, "fixture.hop");
|
|
await writeFile(targetPath, "fixture");
|
|
vi.stubEnv("HOPPER_LAUNCHER_PATH", process.execPath);
|
|
vi.stubEnv("REA_ANALYSIS_PROVIDER", "environment-provider");
|
|
|
|
await expect(
|
|
createDirectAnalysis(process.env).runDirectAnalysis(
|
|
targetPath,
|
|
"binary_overview",
|
|
{},
|
|
{
|
|
providerId: "request-provider",
|
|
},
|
|
),
|
|
).resolves.toMatchObject({
|
|
error: "Analysis failed",
|
|
code: "capability_unavailable",
|
|
details: {
|
|
selection_reason: "unknown_provider",
|
|
requested_provider_id: "request-provider",
|
|
candidate_ids: ["ghidra", "hopper", "ida"],
|
|
},
|
|
});
|
|
});
|
|
|
|
it("does not replay unbound historical Evidence from a supplied snapshot", async () => {
|
|
const directory = await createTestTempDirectory("rea-direct-snapshot-");
|
|
const snapshotPath = join(directory, "analysis.json");
|
|
const launcherPath = join(directory, "hopper-launcher");
|
|
await writeFile(launcherPath, "fixture Hopper launcher");
|
|
await chmod(launcherPath, 0o755);
|
|
const target = await parseBinaryTarget(process.execPath);
|
|
if (!target.ok) throw target.error;
|
|
const resolved = await resolveHopperAnalysisProfile(target.value, {
|
|
launcherPath,
|
|
loaderArgsOverride: [],
|
|
provider: HOPPER_PROVIDER_IDENTITY,
|
|
});
|
|
if (!resolved.ok || resolved.value.profile === null)
|
|
throw new Error("fixture Hopper profile did not resolve");
|
|
const profile = resolved.value.profile;
|
|
const workflowProfile = workflowAnalysisProfile(profile);
|
|
const evidence = createEvidence(target.value, REA_WORKFLOW_PROVIDER, {
|
|
operation: "binary_overview",
|
|
parameters: {},
|
|
result: { cached: true },
|
|
analysisProfile: workflowProfile,
|
|
});
|
|
const snapshot: AnalysisSnapshot = {
|
|
target: snapshotTarget(target.value),
|
|
binding: snapshotBinding(profile),
|
|
entries: [],
|
|
workflow_entries: [],
|
|
evidence_bundle: createEvidenceBundle([evidence]),
|
|
};
|
|
const written = await withArtifactResourceScope((resources) =>
|
|
writeAnalysisSnapshot(snapshot, snapshotPath, false, resources),
|
|
);
|
|
if (!written.ok) throw written.error;
|
|
vi.stubEnv("HOPPER_LAUNCHER_PATH", launcherPath);
|
|
|
|
await expect(
|
|
createDirectAnalysis(process.env).runDirectAnalysis(
|
|
process.execPath,
|
|
"binary_overview",
|
|
{},
|
|
{
|
|
snapshotPath,
|
|
},
|
|
),
|
|
).resolves.not.toEqual(evidence);
|
|
});
|
|
});
|