mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
refactor(evidence): centralize JSON payload admission
Give ordinary Evidence creation an explicit unknown-payload input contract while retaining the strong JSON contract for producer helpers and cooperative immutable creation. Keep schema admission, input snapshotting and identity hashing with the Evidence owner. Remove 32 generic JSON preparses that only fed ordinary Evidence construction. Preserve semantic format validation, selected-source bindings, standalone JSON boundaries, typed executions and scenario digest contracts. Invalid JSON now reports its Evidence parameters/normalized_result/raw_result prefix. Validation: fresh runtime/catalog build, 560 focused and README tests passed, 4 platform-specific cases skipped, full static/docs checks, test discovery, duplication and diff checks. New regressions cover omitted semantic optionals, raw-result defaults and nested undefined refusal without input mutation. Installed providers, real browsers/Frida/JADX and Linux-only recovery remain unverified; boundary providers are authored fixtures.
This commit is contained in:
1 parent
835904e4ee
commit
58fb824105
26 files changed
+162
-69
No files matched your search
@@ -241,6 +241,17 @@ Put provider protocols/parsing in adapters and shared CLI/MCP workflows in the
|
||||
application layer. Share Evidence provenance, unknown projection, and session
|
||||
retention semantics; matching payloads alone do not establish parity.
|
||||
|
||||
Let `createEvidence` own JSON validation and snapshotting of ordinary observation
|
||||
payloads. `EvidenceObservationInput` accepts semantically parsed parameters,
|
||||
results, and raw results whose JSON validity still needs admission. Keep format,
|
||||
selection, and identity validation at their producing boundary. Invalid JSON
|
||||
issues are rooted at `parameters`, `normalized_result`, or `raw_result` in the
|
||||
Evidence envelope.
|
||||
|
||||
`EvidenceObservation` retains the stronger JSON contract used by producer
|
||||
helpers and `createImmutableEvidenceSteps`. The cooperative constructor also
|
||||
requires an authenticated immutable result; preserve that ownership proof.
|
||||
|
||||
Measure the resource before choosing a budget and enforce it before the costly
|
||||
step. Account for value alternatives, trace-frame products, retained records,
|
||||
serialization expansion, and temporary-file growth separately. Derive facet
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
type Evidence,
|
||||
type EvidenceObservation,
|
||||
} from "../domain/evidence.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import { sanitizeBrowserUrl } from "../domain/browserObservation.js";
|
||||
import type {
|
||||
InspectWebPageInput,
|
||||
@@ -77,7 +76,7 @@ export const createBrowserEvidence = (
|
||||
predicateType: browserPredicate(operation),
|
||||
operation,
|
||||
parameters: browserParameters(input, result),
|
||||
result: jsonValueSchema.parse(result),
|
||||
result,
|
||||
confidence: "observed",
|
||||
authority: "external-service",
|
||||
environment:
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
type Evidence,
|
||||
type EvidenceObservation,
|
||||
} from "../domain/evidence.js";
|
||||
import { jsonObjectSchema, jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import { jsonObjectSchema } from "../domain/jsonValue.js";
|
||||
|
||||
const browserScenarioParameters = (
|
||||
scenario: BrowserScenario,
|
||||
@@ -26,7 +26,7 @@ export const createBrowserScenarioEvidence = (
|
||||
predicateType: "rea.browser-scenario-capture",
|
||||
operation: "capture_browser_scenario",
|
||||
parameters: browserScenarioParameters(scenario),
|
||||
result: jsonValueSchema.parse(capture),
|
||||
result: capture,
|
||||
confidence: "observed",
|
||||
authority: "controlled-replay",
|
||||
environment: {
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
createEvidenceBundle,
|
||||
type EvidenceBundle,
|
||||
} from "../domain/evidenceBundle.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import {
|
||||
reconstructionObligationLedgerSchema,
|
||||
type ReconstructionObligationLedgerInput,
|
||||
@@ -47,12 +46,10 @@ export const processEvidence = (
|
||||
predicateType: "rea.process-capture",
|
||||
operation: "capture_process_scenario",
|
||||
parameters: {},
|
||||
result: jsonValueSchema.parse(
|
||||
processCaptureSchema.parse({
|
||||
...EMPTY_PROCESS_CAPTURE_EXAMPLE,
|
||||
...overrides,
|
||||
}),
|
||||
),
|
||||
result: processCaptureSchema.parse({
|
||||
...EMPTY_PROCESS_CAPTURE_EXAMPLE,
|
||||
...overrides,
|
||||
}),
|
||||
confidence: "observed",
|
||||
authority: "controlled-replay",
|
||||
});
|
||||
@@ -70,9 +67,7 @@ export const boundProofEvidence = (
|
||||
predicateType: "rea.reconstruction-proof",
|
||||
operation: "verify_reconstruction_obligations",
|
||||
parameters: {},
|
||||
result: jsonValueSchema.parse(
|
||||
reconstructionObligationProofSchema.parse(result),
|
||||
),
|
||||
result: reconstructionObligationProofSchema.parse(result),
|
||||
confidence: "observed",
|
||||
authority,
|
||||
},
|
||||
|
||||
@@ -261,7 +261,7 @@ export class WebModuleTraceService {
|
||||
},
|
||||
{
|
||||
operation: OPERATION,
|
||||
parameters: jsonObjectSchema.parse(input),
|
||||
parameters: input,
|
||||
result: normalized.data,
|
||||
rawResult: resolution?.rawResult ?? null,
|
||||
confidence: "derived",
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
AnalysisOutputError,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { createEvidence, type Evidence } from "../domain/evidence.js";
|
||||
import { jsonObjectSchema } from "../domain/jsonValue.js";
|
||||
import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js";
|
||||
import { err, ok, type Result } from "../domain/result.js";
|
||||
import {
|
||||
@@ -162,7 +161,7 @@ export class WebRuntimeService {
|
||||
createEvidence(undefined, this.provider.identity(), {
|
||||
predicateType: `rea.${operation.replaceAll("_", "-")}`,
|
||||
operation,
|
||||
parameters: jsonObjectSchema.parse(input),
|
||||
parameters: input,
|
||||
result,
|
||||
confidence: "observed",
|
||||
authority: "external-service",
|
||||
|
||||
@@ -21,7 +21,6 @@ import { AnalysisError } from "../domain/analysisErrorBase.js";
|
||||
import { createEvidence, type Evidence } from "../domain/evidence.js";
|
||||
import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js";
|
||||
import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
||||
import { ProviderCleanupError } from "../domain/providerCleanupError.js";
|
||||
import { err, ok, type Result } from "../domain/result.js";
|
||||
@@ -136,7 +135,7 @@ export const exportWebScriptsValidated = async (
|
||||
capture_path: input.capture_path,
|
||||
output_directory: input.output_directory,
|
||||
},
|
||||
result: jsonValueSchema.parse(result),
|
||||
result,
|
||||
rawResult: null,
|
||||
confidence: "derived",
|
||||
authority: "historical-reference",
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { createEvidence, type Evidence } from "../domain/evidence.js";
|
||||
import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js";
|
||||
import { jsonObjectSchema } from "../domain/jsonValue.js";
|
||||
import { err, ok, type Result } from "../domain/result.js";
|
||||
import {
|
||||
selectedWebScriptArtifactsSchema,
|
||||
@@ -171,7 +170,7 @@ export class WebSourceLocationService {
|
||||
},
|
||||
{
|
||||
operation: OPERATION,
|
||||
parameters: jsonObjectSchema.parse(input),
|
||||
parameters: input,
|
||||
result: normalized.data,
|
||||
rawResult: { source_map_text: map.text },
|
||||
confidence: "derived",
|
||||
|
||||
@@ -2,7 +2,6 @@ import { setImmediate } from "node:timers/promises";
|
||||
import { dispatchQueuedInterrupts } from "../../process/queuedInterrupts.js";
|
||||
import { readImmutableEvidenceBundle } from "../EvidenceBundleFiles.js";
|
||||
import { INSPECT_ANALYSIS_VIEW_OPERATION } from "../../domain/analysisView/analysisViewErrors.js";
|
||||
import { jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
|
||||
import { ANALYSIS_VIEW_PROVIDER } from "../../domain/investigationProviders.js";
|
||||
import type { EvidenceLookup } from "../EvidenceInputResolver.js";
|
||||
@@ -346,9 +345,9 @@ const createAnalysisViewEvidence = (
|
||||
operation: INSPECT_ANALYSIS_VIEW_OPERATION,
|
||||
parameters: {
|
||||
parent_evidence_id: parent.evidence_id,
|
||||
view: jsonValueSchema.parse(input.view),
|
||||
view: input.view,
|
||||
},
|
||||
result: jsonValueSchema.parse(result),
|
||||
result,
|
||||
rawResult: null,
|
||||
confidence:
|
||||
// Grouping is whole-capture, so any templated group or parameter makes the result inferred;
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
import { AnalysisCancelledError } from "../../domain/analysisErrorCore.js";
|
||||
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import { createEvidence, type Evidence } from "../../domain/evidence.js";
|
||||
import { jsonObjectSchema } from "../../domain/jsonValue.js";
|
||||
import { err, type Result } from "../../domain/result.js";
|
||||
import { completeProviderEvidence } from "../ProviderOperationExecution.js";
|
||||
|
||||
@@ -62,7 +61,7 @@ export class AndroidAnalysisService {
|
||||
execution.provider,
|
||||
{
|
||||
operation,
|
||||
parameters: jsonObjectSchema.parse(request.data.input),
|
||||
parameters: request.data.input,
|
||||
result: execution.result,
|
||||
rawResult: execution.rawResult,
|
||||
limitations: execution.limitations,
|
||||
|
||||
@@ -3,7 +3,6 @@ import { access, stat } from "node:fs/promises";
|
||||
import { setTimeout as delay } from "node:timers/promises";
|
||||
|
||||
import { createEvidence, type Evidence } from "../../domain/evidence.js";
|
||||
import { jsonObjectSchema, jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
import {
|
||||
resolvedAnalysisError,
|
||||
type AnalysisError,
|
||||
@@ -458,8 +457,8 @@ const fridaEvidence = (
|
||||
{
|
||||
predicateType: "rea.frida.instrumentation-observation",
|
||||
operation,
|
||||
parameters: jsonObjectSchema.parse(parameters),
|
||||
result: jsonValueSchema.parse(result),
|
||||
parameters,
|
||||
result,
|
||||
...(script?.sourceRepresentation === undefined
|
||||
? {}
|
||||
: {
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { CanonicalElectronActiveObservationInput } from "./ElectronActiveOb
|
||||
import type { Evidence, EvidenceObservation } from "../../domain/evidence.js";
|
||||
import { createEvidence } from "../../domain/evidence.js";
|
||||
import type { ElectronActiveObservationResult } from "../../domain/javascript/electronActiveObservation.js";
|
||||
import { jsonObjectSchema, jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
import { jsonObjectSchema } from "../../domain/jsonValue.js";
|
||||
|
||||
/** Commit the selected Electron experiment and its observed result. */
|
||||
export const createElectronActiveEvidence = (
|
||||
@@ -16,7 +16,7 @@ export const createElectronActiveEvidence = (
|
||||
predicateType: "rea.electron-active-scenario",
|
||||
operation: "capture_electron_scenario",
|
||||
parameters: parameters(input),
|
||||
result: jsonValueSchema.parse(result),
|
||||
result,
|
||||
confidence: "observed",
|
||||
authority: "controlled-replay",
|
||||
environment: {
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
javascriptRecoveryInputSchema,
|
||||
javascriptRecoveryResultSchema,
|
||||
} from "../../domain/javascript/javascriptRecovery.js";
|
||||
import { jsonObjectSchema, jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
||||
import { err, type Result } from "../../domain/result.js";
|
||||
import type { ExecutionOptions } from "../AnalysisProvider.js";
|
||||
@@ -115,8 +114,8 @@ export class JavaScriptRecoveryService {
|
||||
execution.value.provider,
|
||||
{
|
||||
operation: OPERATION,
|
||||
parameters: jsonObjectSchema.parse(parsed.data),
|
||||
result: jsonValueSchema.parse(result.data),
|
||||
parameters: parsed.data,
|
||||
result: result.data,
|
||||
rawResult: execution.value.rawResult,
|
||||
locations: execution.value.locations,
|
||||
limitations: execution.value.limitations,
|
||||
|
||||
@@ -10,7 +10,6 @@ import type {
|
||||
ListJavaScriptRuntimeTargetsInput,
|
||||
ObserveJavaScriptRuntimeInput,
|
||||
} from "../../domain/javascript/javascriptRuntimeObservation.js";
|
||||
import { jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
|
||||
type RuntimeObservationOperation =
|
||||
| "list_javascript_runtime_targets"
|
||||
@@ -30,7 +29,7 @@ export const createJavaScriptRuntimeObservationEvidence = (
|
||||
: "rea.javascript-runtime-observation",
|
||||
operation,
|
||||
parameters: parameters(input),
|
||||
result: jsonValueSchema.parse(result),
|
||||
result,
|
||||
rawResult: null,
|
||||
confidence: "observed",
|
||||
authority: "external-service",
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
PROCESS_PROVIDER,
|
||||
isProcessEvidenceProvider,
|
||||
} from "../../domain/process/processEvidenceProvider.js";
|
||||
import { jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
import {
|
||||
safeParseProcessCapture,
|
||||
type ProcessCapture,
|
||||
@@ -91,7 +90,7 @@ export const createProcessCaptureEvidence = (
|
||||
predicateType: PROCESS_CAPTURE_EVIDENCE.predicate,
|
||||
operation: PROCESS_CAPTURE_EVIDENCE.operation,
|
||||
parameters: processEvidenceParameters(scenario, capture.manifest.platform),
|
||||
result: jsonValueSchema.parse(capture),
|
||||
result: capture,
|
||||
confidence: "observed",
|
||||
authority: "controlled-replay",
|
||||
environment: {
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
} from "../../domain/sqlite/sqliteDatabase.js";
|
||||
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
||||
import { err, ok, type Result } from "../../domain/result.js";
|
||||
import { jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
|
||||
const OPERATION = "inspect_sqlite_database";
|
||||
|
||||
@@ -115,7 +114,7 @@ export class SqliteDatabaseService {
|
||||
createEvidence(execution.subject, execution.provider, {
|
||||
operation: OPERATION,
|
||||
parameters: sqliteInspectionParameters(input.data),
|
||||
result: jsonValueSchema.parse(report.data),
|
||||
result: report.data,
|
||||
...(execution.rawResult === null
|
||||
? {}
|
||||
: { rawResult: execution.rawResult }),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createEvidence } from "../domain/evidence.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import { FUNCTION_COMPARISON_PROVIDER } from "../domain/comparisonEvidenceIdentity.js";
|
||||
import { compareProcessCaptureEvidence } from "../domain/process/processComparisonEvidence.js";
|
||||
import { PROCESS_PROVIDER } from "../domain/process/processEvidenceProvider.js";
|
||||
@@ -31,7 +30,7 @@ export const FUNCTION_COMPARISON_EVIDENCE = createEvidence(
|
||||
left_evidence_id: left.value.evidence.evidence_id,
|
||||
right_evidence_id: right.value.evidence.evidence_id,
|
||||
},
|
||||
result: jsonValueSchema.parse(comparison),
|
||||
result: comparison,
|
||||
confidence: "derived",
|
||||
authority: "analyst-inference",
|
||||
limitations: comparison.limitations,
|
||||
@@ -48,7 +47,7 @@ const captureEvidence = (scenario: string) =>
|
||||
predicateType: "rea.process-capture",
|
||||
operation: "capture_process_scenario",
|
||||
parameters: { scenario },
|
||||
result: jsonValueSchema.parse(capture),
|
||||
result: capture,
|
||||
confidence: "observed",
|
||||
authority: "controlled-replay",
|
||||
environment: {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
type EvidenceSubjectTarget,
|
||||
} from "./evidence.js";
|
||||
import type { AnalysisProfileCommitment } from "./analysisProfile.js";
|
||||
import { jsonObjectSchema, type JsonValue } from "./jsonValue.js";
|
||||
import type { JsonValue } from "./jsonValue.js";
|
||||
import type { RelationshipCoverage } from "./relationshipCoverage.js";
|
||||
|
||||
/** Provider observations that can be committed to Evidence. */
|
||||
@@ -44,7 +44,7 @@ export const createAnalysisExecutionEvidence = (
|
||||
execution.provider,
|
||||
{
|
||||
operation,
|
||||
parameters: jsonObjectSchema.parse(input),
|
||||
parameters: input,
|
||||
result: execution.result,
|
||||
rawResult: execution.rawResult,
|
||||
limitations: execution.limitations,
|
||||
|
||||
@@ -138,6 +138,102 @@ it("projects known provider fields and preserves open JSON payloads", () => {
|
||||
expect(parseEvidence(evidence)).toEqual(evidence);
|
||||
});
|
||||
|
||||
it("admits typed semantic payloads with omitted optional facts without changing inputs or identity", () => {
|
||||
interface Selection {
|
||||
readonly procedure: string;
|
||||
readonly address?: string;
|
||||
}
|
||||
interface SymbolRow {
|
||||
readonly name: string;
|
||||
readonly size?: number;
|
||||
}
|
||||
interface NativeRecord {
|
||||
readonly producer: string;
|
||||
readonly diagnostic?: string;
|
||||
}
|
||||
const parameters: Selection = { procedure: "main" };
|
||||
const row: SymbolRow = { name: "main" };
|
||||
const result = { symbols: [row] };
|
||||
const rawResult: NativeRecord = { producer: "authored fixture" };
|
||||
const evidence = createEvidence(TARGET, PROVIDER, {
|
||||
operation: "inspect",
|
||||
parameters,
|
||||
result,
|
||||
rawResult,
|
||||
});
|
||||
const equivalent = createEvidence(TARGET, PROVIDER, {
|
||||
operation: "inspect",
|
||||
parameters: { procedure: "main" },
|
||||
result: { symbols: [{ name: "main" }] },
|
||||
rawResult: { producer: "authored fixture" },
|
||||
});
|
||||
|
||||
expect(evidence).toEqual(equivalent);
|
||||
expect(evidence.evidence_id).toBe(equivalent.evidence_id);
|
||||
expect(parameters).toEqual({ procedure: "main" });
|
||||
expect(result).toEqual({ symbols: [{ name: "main" }] });
|
||||
expect(result.symbols[0]).toBe(row);
|
||||
expect(rawResult).toEqual({ producer: "authored fixture" });
|
||||
expect(Object.hasOwn(parameters, "address")).toBe(false);
|
||||
expect(Object.hasOwn(row, "size")).toBe(false);
|
||||
expect(Object.hasOwn(rawResult, "diagnostic")).toBe(false);
|
||||
expect(Object.isFrozen(parameters)).toBe(false);
|
||||
expect(Object.isFrozen(result)).toBe(false);
|
||||
expect(Object.isFrozen(row)).toBe(false);
|
||||
expect(Object.isFrozen(rawResult)).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps an omitted, undefined, or null raw payload equivalent", () => {
|
||||
const observation = { operation: "inspect", parameters: {}, result: true };
|
||||
const omitted = createEvidence(TARGET, PROVIDER, observation);
|
||||
const explicitUndefined = createEvidence(TARGET, PROVIDER, {
|
||||
...observation,
|
||||
rawResult: undefined,
|
||||
});
|
||||
const explicitNull = createEvidence(TARGET, PROVIDER, {
|
||||
...observation,
|
||||
rawResult: null,
|
||||
});
|
||||
|
||||
expect(omitted.raw_result).toBeNull();
|
||||
expect(explicitUndefined).toEqual(omitted);
|
||||
expect(explicitNull).toEqual(omitted);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ field: "parameters", prefix: "parameters" },
|
||||
{ field: "result", prefix: "normalized_result" },
|
||||
{ field: "rawResult", prefix: "raw_result" },
|
||||
])(
|
||||
"rejects explicit nested undefined in $field at its Evidence prefix",
|
||||
({ field, prefix }) => {
|
||||
const invalid = { nested: { value: undefined } };
|
||||
let failure: unknown;
|
||||
try {
|
||||
createEvidence(TARGET, PROVIDER, {
|
||||
operation: "inspect",
|
||||
parameters: {},
|
||||
result: true,
|
||||
[field]: invalid,
|
||||
});
|
||||
} catch (cause: unknown) {
|
||||
failure = cause;
|
||||
}
|
||||
|
||||
expect(failure).toMatchObject({
|
||||
name: "ZodError",
|
||||
issues: [
|
||||
{
|
||||
path: [prefix],
|
||||
message: expect.stringContaining("JSON"),
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(Object.hasOwn(invalid.nested, "value")).toBe(true);
|
||||
expect(invalid.nested.value).toBeUndefined();
|
||||
},
|
||||
);
|
||||
|
||||
it("preserves identity and bytes when reusing an owned immutable result", () => {
|
||||
const value = { nested: { values: ["observed", 1] } };
|
||||
const mutable = createEvidence(TARGET, PROVIDER, {
|
||||
|
||||
+16
-8
@@ -242,13 +242,14 @@ type ExecutionEnvironment = z.infer<typeof executionEnvironmentSchema>;
|
||||
|
||||
export type EvidenceProvider = Readonly<z.output<typeof providerSchema>>;
|
||||
|
||||
export interface EvidenceObservation {
|
||||
/** Payloads admitted and snapshotted by the ordinary Evidence constructor. */
|
||||
export interface EvidenceObservationInput {
|
||||
readonly predicateType?: string;
|
||||
readonly operation: string;
|
||||
readonly parameters: Readonly<Record<string, JsonValue>>;
|
||||
readonly result: JsonValue;
|
||||
readonly parameters: unknown;
|
||||
readonly result: unknown;
|
||||
readonly analysisProfile?: AnalysisProfileCommitment;
|
||||
readonly rawResult?: JsonValue;
|
||||
readonly rawResult?: unknown;
|
||||
readonly confidence?: "observed" | "derived" | "inferred";
|
||||
readonly authority?: EvidenceAuthority;
|
||||
readonly environment?: ExecutionEnvironment | null;
|
||||
@@ -260,6 +261,13 @@ export interface EvidenceObservation {
|
||||
readonly evidenceLinks?: readonly string[];
|
||||
}
|
||||
|
||||
/** Producer facts that already promise JSON, including cooperative Evidence input. */
|
||||
export interface EvidenceObservation extends EvidenceObservationInput {
|
||||
readonly parameters: Readonly<Record<string, JsonValue>>;
|
||||
readonly result: JsonValue;
|
||||
readonly rawResult?: JsonValue;
|
||||
}
|
||||
|
||||
type EvidenceWithoutId = Omit<EvidenceSchemaValue, "evidence_id">;
|
||||
|
||||
/** Content committed by an Evidence ID, excluding local request/display metadata. */
|
||||
@@ -403,11 +411,11 @@ export interface EvidenceCreationOptions {
|
||||
readonly immutable?: boolean;
|
||||
}
|
||||
|
||||
/** Build deterministic Evidence from an immutable artifact subject. */
|
||||
/** Admit payloads into deterministic Evidence bound to an immutable artifact subject. */
|
||||
export const createEvidence = (
|
||||
target: EvidenceSubjectTarget | BinaryTarget | undefined,
|
||||
provider: EvidenceProvider,
|
||||
observation: EvidenceObservation,
|
||||
observation: EvidenceObservationInput,
|
||||
options?: EvidenceCreationOptions,
|
||||
): Evidence => {
|
||||
const { normalized, sharedResult } = normalizeEvidenceObservation(
|
||||
@@ -460,7 +468,7 @@ export function* createImmutableEvidenceSteps(
|
||||
const normalizeEvidenceObservation = (
|
||||
target: EvidenceSubjectTarget | BinaryTarget | undefined,
|
||||
provider: EvidenceProvider,
|
||||
observation: EvidenceObservation,
|
||||
observation: EvidenceObservationInput,
|
||||
) => {
|
||||
const subject =
|
||||
target === undefined
|
||||
@@ -509,7 +517,7 @@ const normalizeEvidenceObservation = (
|
||||
: { relationship_coverage: observation.relationshipCoverage }),
|
||||
locations: [...(observation.locations ?? [])],
|
||||
evidence_links: [...(observation.evidenceLinks ?? [])],
|
||||
} satisfies JsonValue;
|
||||
};
|
||||
const sharedResult =
|
||||
typeof observation.result === "object" &&
|
||||
observation.result !== null &&
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createEvidence } from "../evidence.js";
|
||||
import { jsonObjectSchema, type JsonValue } from "../jsonValue.js";
|
||||
import type { JsonValue } from "../jsonValue.js";
|
||||
import type { PcapRequest } from "./pcapAnalysis.js";
|
||||
|
||||
/** Domain facts needed by both successful and retained partial observations. */
|
||||
@@ -21,7 +21,7 @@ export const createPcapEvidence = (
|
||||
) =>
|
||||
createEvidence(observation.subject ?? undefined, observation.provider, {
|
||||
operation: request.operation,
|
||||
parameters: jsonObjectSchema.parse(request.input),
|
||||
parameters: request.input,
|
||||
result: observation.result,
|
||||
rawResult: observation.rawResult,
|
||||
limitations: observation.limitations,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createEvidence, type Evidence } from "../evidence.js";
|
||||
import { jsonValueSchema } from "../jsonValue.js";
|
||||
import type { ProcessCapture } from "./processCaptureParsing.js";
|
||||
import {
|
||||
compareProcessCaptures,
|
||||
@@ -37,9 +36,9 @@ export const compareProcessCaptureEvidence = (
|
||||
: { max_capture_age_ms: options.maxCaptureAgeMs }),
|
||||
...(options.traceSpecification === undefined
|
||||
? {}
|
||||
: { trace_spec: jsonValueSchema.parse(options.traceSpecification) }),
|
||||
: { trace_spec: options.traceSpecification }),
|
||||
},
|
||||
result: jsonValueSchema.parse(comparison),
|
||||
result: comparison,
|
||||
confidence: "derived",
|
||||
authority: "analyst-inference",
|
||||
limitations: comparison.limitations,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { compareArtifacts } from "../domain/artifactComparison.js";
|
||||
import { ARTIFACT_COMPARISON_PROVIDER } from "../domain/comparisonEvidenceIdentity.js";
|
||||
import { createEvidence } from "../domain/evidence.js";
|
||||
import { readEvidence } from "../domain/evidenceValidation.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import { resolveArtifactComparisonEvidence } from "../application/artifacts/ArtifactComparisonEvidence.js";
|
||||
import type { RecordUnknownInput } from "../domain/residualUnknown.js";
|
||||
import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
|
||||
@@ -78,7 +77,7 @@ export const registerArtifactComparisonTool = (
|
||||
left_evidence_ids: leftEvidenceIds,
|
||||
right_evidence_ids: rightEvidenceIds,
|
||||
},
|
||||
result: jsonValueSchema.parse(comparison),
|
||||
result: comparison,
|
||||
confidence: "derived",
|
||||
authority: "analyst-inference",
|
||||
limitations: comparison.limitations,
|
||||
|
||||
@@ -5,7 +5,6 @@ import { readImmutableEvidenceBundle } from "../application/EvidenceBundleFiles.
|
||||
import { toolContract } from "../contracts/toolContracts.js";
|
||||
import { compareBundles } from "../domain/bundleComparison.js";
|
||||
import { createEvidence } from "../domain/evidence.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import { runDerivedOperation } from "./runDerivedOperation.js";
|
||||
import { recordEvidenceSources } from "../application/investigation/recordEvidenceSources.js";
|
||||
import { BUNDLE_COMPARISON_PROVIDER } from "../domain/investigationProviders.js";
|
||||
@@ -82,7 +81,7 @@ export const registerBundleComparisonTool = (
|
||||
right_bundle_sha256: comparison.right_bundle_sha256,
|
||||
record_pairs: input.record_pairs,
|
||||
},
|
||||
result: jsonValueSchema.parse(comparison),
|
||||
result: comparison,
|
||||
confidence: "derived",
|
||||
authority: "analyst-inference",
|
||||
limitations: comparison.limitations,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { toolContract } from "../contracts/toolContracts.js";
|
||||
import { createEvidence } from "../domain/evidence.js";
|
||||
import { parseFunctionEvidence } from "../domain/functionDossierEvidence.js";
|
||||
import { FUNCTION_COMPARISON_PROVIDER } from "../domain/comparisonEvidenceIdentity.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import type { RecordUnknownInput } from "../domain/residualUnknown.js";
|
||||
import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
|
||||
import { recordEvidenceSources } from "../application/investigation/recordEvidenceSources.js";
|
||||
@@ -84,7 +83,7 @@ export const registerFunctionComparisonTool = (
|
||||
left_evidence_id: leftEvidence.evidence_id,
|
||||
right_evidence_id: rightEvidence.evidence_id,
|
||||
},
|
||||
result: jsonValueSchema.parse(comparison),
|
||||
result: comparison,
|
||||
confidence: "derived",
|
||||
authority: "analyst-inference",
|
||||
limitations: comparison.limitations,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { toolContract } from "../contracts/toolContracts.js";
|
||||
import { buildCallPath } from "../domain/callPath.js";
|
||||
import { findChangedBehavior } from "../domain/changedBehavior.js";
|
||||
import { createEvidence } from "../domain/evidence.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import { correlateStaticAndRuntime } from "../domain/staticRuntimeCorrelation.js";
|
||||
import {
|
||||
comparisonClosure,
|
||||
@@ -88,7 +87,7 @@ const registerChangedBehavior = (
|
||||
parameters: {
|
||||
comparison_evidence_ids: [...comparisonEvidenceIds],
|
||||
},
|
||||
result: jsonValueSchema.parse(result),
|
||||
result,
|
||||
confidence: "derived",
|
||||
authority: "analyst-inference",
|
||||
limitations: result.limitations,
|
||||
@@ -161,7 +160,7 @@ const registerCallPath = (
|
||||
start: input.start.address,
|
||||
goal: input.goal.address,
|
||||
},
|
||||
result: jsonValueSchema.parse(result),
|
||||
result,
|
||||
confidence: "derived",
|
||||
authority: "analyst-inference",
|
||||
limitations: result.limitations,
|
||||
@@ -236,7 +235,7 @@ const registerStaticRuntime = (
|
||||
parameters: {
|
||||
mapping_count: input.mappings.length,
|
||||
},
|
||||
result: jsonValueSchema.parse(result),
|
||||
result,
|
||||
confidence: "inferred",
|
||||
authority: "analyst-inference",
|
||||
limitations: result.limitations,
|
||||
@@ -311,7 +310,7 @@ const registerReconstruction = (
|
||||
({ claim_id: id }) => id,
|
||||
),
|
||||
},
|
||||
result: jsonValueSchema.parse(result),
|
||||
result,
|
||||
confidence: "derived",
|
||||
authority: "analyst-inference",
|
||||
limitations: result.limitations,
|
||||
|
||||
Reference in new issue
Block a user