mirror of
https://github.com/morluto/rea
synced 2026-10-28 06:46:47 -04:00
refactor(ownership): consolidate snapshot and comparison facts
Retain authenticated immutable snapshot payloads while keeping mutable exports independent. Derive artifact metadata comparisons once and reuse the owning native trace limits.
This commit is contained in:
@@ -3,6 +3,7 @@ import { nativeDispatchMetadataInputSchema } from "../domain/native/objcSwiftMet
|
||||
import { nativeProcedureInputSchema } from "../domain/native/nativeProcedureInput.js";
|
||||
import { decompileDirectoryInputSchema } from "./decompileDirectory.js";
|
||||
import { nativeValueTraceInputSchema } from "../domain/native/nativeValueTrace.js";
|
||||
import { nativeInvestigationTraceLimitsSchema } from "../domain/native/nativeInvestigationGraph.js";
|
||||
const traceLiteralInputSchema = z.strictObject({
|
||||
query: z.string().min(1),
|
||||
case_sensitive: z.boolean().default(false),
|
||||
@@ -64,16 +65,13 @@ export const enhancedInputSchemas = {
|
||||
.optional(),
|
||||
direction: callRelationshipDirectionSchema,
|
||||
}),
|
||||
trace_native_ui_action: z.strictObject({
|
||||
trace_native_ui_action: nativeInvestigationTraceLimitsSchema.extend({
|
||||
action: z
|
||||
.string()
|
||||
.min(1)
|
||||
.describe(
|
||||
"A unique compiled UI action selector, interface object ID, native symbol or exact function address",
|
||||
),
|
||||
max_depth: z.int().min(0).max(32).default(8),
|
||||
max_nodes: z.int().min(1).max(2_000).default(250),
|
||||
max_edges: z.int().min(1).max(5_000).default(500),
|
||||
}),
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
analysisSnapshotSchema,
|
||||
analysisQueryId,
|
||||
parseAnalysisSnapshot,
|
||||
immutableAnalysisSnapshot,
|
||||
snapshotBinding,
|
||||
snapshotEvidenceForQuery,
|
||||
snapshotTarget,
|
||||
@@ -639,3 +640,93 @@ describe("analysis snapshot Evidence binding", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("analysis snapshot payload ownership", () => {
|
||||
it.each(["provider", "workflow"] as const)(
|
||||
"shares authenticated %s payloads only in immutable snapshots",
|
||||
(kind) => {
|
||||
const { snapshot } = snapshotWithHistoricalEvidence();
|
||||
const entry = snapshot.entries[0];
|
||||
if (entry === undefined) throw new Error("Missing fixture query");
|
||||
entry.execution.raw_result = { source: "raw" };
|
||||
const evidence = createEvidence(
|
||||
ANALYSIS_SNAPSHOT_TARGET,
|
||||
ANALYSIS_SNAPSHOT_PROVIDER,
|
||||
{
|
||||
operation: entry.operation,
|
||||
parameters: entry.parameters,
|
||||
result: entry.execution.result,
|
||||
rawResult: entry.execution.raw_result,
|
||||
analysisProfile: ANALYSIS_SNAPSHOT_PROFILE,
|
||||
confidence: kind === "provider" ? "observed" : "derived",
|
||||
},
|
||||
);
|
||||
const input = {
|
||||
...snapshot,
|
||||
entries: kind === "provider" ? [entry] : [],
|
||||
workflow_entries:
|
||||
kind === "workflow"
|
||||
? [
|
||||
{
|
||||
...entry,
|
||||
execution: {
|
||||
...entry.execution,
|
||||
analysis_profile: ANALYSIS_SNAPSHOT_PROFILE,
|
||||
},
|
||||
},
|
||||
]
|
||||
: [],
|
||||
evidence_bundle: createEvidenceBundle([evidence]),
|
||||
};
|
||||
const original = structuredClone(input);
|
||||
const immutable = immutableAnalysisSnapshot(input);
|
||||
const immutableEntry = (
|
||||
kind === "provider" ? immutable.entries : immutable.workflow_entries
|
||||
)[0];
|
||||
const immutableEvidence = immutable.evidence_bundle.records[0];
|
||||
if (immutableEntry === undefined || immutableEvidence === undefined)
|
||||
throw new Error("Missing immutable payload");
|
||||
expect(immutableEntry.execution.result).toBe(
|
||||
immutableEvidence.normalized_result,
|
||||
);
|
||||
expect(immutableEntry.execution.raw_result).toBe(
|
||||
immutableEvidence.raw_result,
|
||||
);
|
||||
expect(Object.isFrozen(immutableEntry.execution.result)).toBe(true);
|
||||
expect(Object.isFrozen(immutableEntry.execution.raw_result)).toBe(true);
|
||||
expect(Object.isFrozen(immutable)).toBe(true);
|
||||
expect(immutableAnalysisSnapshot(immutable)).toBe(immutable);
|
||||
expect(parseAnalysisSnapshot(immutable)).toBe(immutable);
|
||||
expect(input).toEqual(original);
|
||||
expect(Object.isFrozen(entry.execution.result)).toBe(false);
|
||||
expect(Object.isFrozen(entry.execution.raw_result)).toBe(false);
|
||||
const mutable = parseAnalysisSnapshot(input);
|
||||
const mutableEntry = (
|
||||
kind === "provider" ? mutable.entries : mutable.workflow_entries
|
||||
)[0];
|
||||
const mutableEvidence = mutable.evidence_bundle.records[0];
|
||||
if (mutableEntry === undefined || mutableEvidence === undefined)
|
||||
throw new Error("Missing mutable payload");
|
||||
expect(mutableEntry.execution.result).not.toBe(
|
||||
mutableEvidence.normalized_result,
|
||||
);
|
||||
expect(mutableEntry.execution.raw_result).not.toBe(
|
||||
mutableEvidence.raw_result,
|
||||
);
|
||||
mutableEntry.execution.result = { changed: true };
|
||||
mutableEntry.execution.raw_result = { changed: true };
|
||||
expect(mutableEvidence).toEqual(evidence);
|
||||
expect(input).toEqual(original);
|
||||
const tampered = structuredClone(input);
|
||||
const tamperedEntry = (
|
||||
kind === "provider" ? tampered.entries : tampered.workflow_entries
|
||||
)[0];
|
||||
if (tamperedEntry === undefined) throw new Error("Missing tamper query");
|
||||
tamperedEntry.execution.result = { changed: true };
|
||||
expect(() => immutableAnalysisSnapshot(tampered)).toThrow(
|
||||
/differs from its Evidence/u,
|
||||
);
|
||||
expect(Object.isFrozen(tamperedEntry.execution.result)).toBe(false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -607,13 +607,21 @@ export const createAnalysisSnapshotEntry = (input: {
|
||||
const validatedSnapshots = new WeakMap<object, AnalysisSnapshot>();
|
||||
|
||||
/** Parse a provider/profile-exact snapshot and verify its entries and evidence. */
|
||||
export const parseAnalysisSnapshot = (input: unknown): AnalysisSnapshot => {
|
||||
export const parseAnalysisSnapshot = (input: unknown): AnalysisSnapshot =>
|
||||
authenticateSnapshot(input).parsed;
|
||||
|
||||
const authenticateSnapshot = (
|
||||
input: unknown,
|
||||
): {
|
||||
readonly parsed: AnalysisSnapshot;
|
||||
readonly restored: AnalysisSnapshot;
|
||||
} => {
|
||||
if (typeof input === "object" && input !== null) {
|
||||
const owned = validatedSnapshots.get(input);
|
||||
if (owned !== undefined) return owned;
|
||||
if (owned !== undefined) return { parsed: owned, restored: owned };
|
||||
}
|
||||
const parsed = analysisSnapshotSchema.parse(input);
|
||||
restoreSnapshot(
|
||||
const restored = restoreSnapshot(
|
||||
parsed,
|
||||
parseEvidenceBundle(parsed.evidence_bundle),
|
||||
parsed.entries.map((entry) => ({
|
||||
@@ -635,12 +643,12 @@ export const parseAnalysisSnapshot = (input: unknown): AnalysisSnapshot => {
|
||||
),
|
||||
})),
|
||||
);
|
||||
return parsed;
|
||||
return { parsed, restored };
|
||||
};
|
||||
|
||||
/** Own an authenticated immutable cache while preserving mutable caller exports. */
|
||||
export const immutableAnalysisSnapshot = (input: unknown): AnalysisSnapshot =>
|
||||
sealSnapshot(parseAnalysisSnapshot(input));
|
||||
sealSnapshot(authenticateSnapshot(input).restored);
|
||||
|
||||
const sealSnapshot = (snapshot: AnalysisSnapshot): AnalysisSnapshot => {
|
||||
for (const entry of [...snapshot.entries, ...snapshot.workflow_entries]) {
|
||||
|
||||
@@ -289,19 +289,13 @@ const compareOccurrences = (
|
||||
}
|
||||
const leftOccurrence = leftMatches[0];
|
||||
const rightOccurrence = rightMatches[0];
|
||||
if (
|
||||
leftOccurrence !== undefined &&
|
||||
rightOccurrence !== undefined &&
|
||||
asarPackingUnknown({ leftOccurrence, rightOccurrence })
|
||||
)
|
||||
hasUnknownAsarPacking = true;
|
||||
if (
|
||||
leftOccurrence !== undefined &&
|
||||
rightOccurrence !== undefined &&
|
||||
nodeSeaMetadataUnknown({ leftOccurrence, rightOccurrence })
|
||||
)
|
||||
hasUnknownNodeSeaMetadata = true;
|
||||
const metadata = occurrenceMetadata({ leftOccurrence, rightOccurrence });
|
||||
if (leftOccurrence !== undefined && rightOccurrence !== undefined) {
|
||||
if (metadata.asarPacking === "unknown") hasUnknownAsarPacking = true;
|
||||
if (metadata.nodeSea === "unknown") hasUnknownNodeSeaMetadata = true;
|
||||
}
|
||||
const change = classifyPath({
|
||||
metadata,
|
||||
path,
|
||||
leftOccurrence,
|
||||
rightOccurrence,
|
||||
@@ -337,6 +331,7 @@ const occurrencesByPath = (
|
||||
};
|
||||
|
||||
interface ClassifyPathInput {
|
||||
readonly metadata: OccurrenceMetadata;
|
||||
readonly path: string;
|
||||
readonly leftOccurrence: ArtifactOccurrence | undefined;
|
||||
readonly rightOccurrence: ArtifactOccurrence | undefined;
|
||||
@@ -419,7 +414,8 @@ const classifyPath = (input: ClassifyPathInput): ArtifactChange | null => {
|
||||
if (declaration.handled) return declaration.change;
|
||||
const availability = classifyAvailability(input, base);
|
||||
if (availability !== null)
|
||||
return asarPackingChanged(input) || nodeSeaMetadataChanged(input)
|
||||
return input.metadata.asarPacking === "changed" ||
|
||||
input.metadata.nodeSea === "changed"
|
||||
? {
|
||||
...base,
|
||||
classification: "changed",
|
||||
@@ -427,11 +423,7 @@ const classifyPath = (input: ClassifyPathInput): ArtifactChange | null => {
|
||||
}
|
||||
: availability;
|
||||
const dimensions = changedDimensions(input);
|
||||
if (
|
||||
pyInstallerMetadataUnknown(input) ||
|
||||
asarPackingUnknown(input) ||
|
||||
nodeSeaMetadataUnknown(input)
|
||||
) {
|
||||
if (Object.values(input.metadata).includes("unknown")) {
|
||||
if (dimensions.length === 0)
|
||||
return {
|
||||
...base,
|
||||
@@ -450,35 +442,52 @@ type OccurrencePair = Pick<
|
||||
"leftOccurrence" | "rightOccurrence"
|
||||
>;
|
||||
|
||||
const asarPackingUnknown = (input: OccurrencePair): boolean =>
|
||||
(input.leftOccurrence?.unpacked === undefined) !==
|
||||
(input.rightOccurrence?.unpacked === undefined);
|
||||
type MetadataComparison = "unchanged" | "changed" | "unknown";
|
||||
|
||||
const asarPackingChanged = (input: OccurrencePair): boolean =>
|
||||
input.leftOccurrence?.unpacked !== undefined &&
|
||||
input.rightOccurrence?.unpacked !== undefined &&
|
||||
input.leftOccurrence.unpacked !== input.rightOccurrence.unpacked;
|
||||
interface OccurrenceMetadata {
|
||||
readonly asarPacking: MetadataComparison;
|
||||
readonly nodeSea: MetadataComparison;
|
||||
readonly pyInstaller: MetadataComparison;
|
||||
}
|
||||
|
||||
const nodeSeaMetadataUnknown = (input: OccurrencePair): boolean =>
|
||||
(input.leftOccurrence?.format_metadata?.kind === "node-sea-entry") !==
|
||||
(input.rightOccurrence?.format_metadata?.kind === "node-sea-entry");
|
||||
|
||||
const nodeSeaMetadataChanged = (input: OccurrencePair): boolean => {
|
||||
const left = input.leftOccurrence?.format_metadata;
|
||||
const right = input.rightOccurrence?.format_metadata;
|
||||
return (
|
||||
left?.kind === "node-sea-entry" &&
|
||||
right?.kind === "node-sea-entry" &&
|
||||
canonicalJson(nodeSeaEntryDeclaration(left)) !==
|
||||
canonicalJson(nodeSeaEntryDeclaration(right))
|
||||
);
|
||||
const compareOptionalMetadata = <T>(
|
||||
left: T | undefined,
|
||||
right: T | undefined,
|
||||
declaration: (value: T) => unknown,
|
||||
): MetadataComparison => {
|
||||
if (left === undefined || right === undefined)
|
||||
return left === right ? "unchanged" : "unknown";
|
||||
return canonicalJson(declaration(left)) === canonicalJson(declaration(right))
|
||||
? "unchanged"
|
||||
: "changed";
|
||||
};
|
||||
|
||||
const pyInstallerMetadataUnknown = (input: ClassifyPathInput): boolean =>
|
||||
(isPyInstallerEntry(input.leftOccurrence) &&
|
||||
!isPyInstallerEntry(input.rightOccurrence)) ||
|
||||
(isPyInstallerEntry(input.rightOccurrence) &&
|
||||
!isPyInstallerEntry(input.leftOccurrence));
|
||||
/** Interpret each pair's format declarations once, preserving historical omission. */
|
||||
const occurrenceMetadata = (input: OccurrencePair): OccurrenceMetadata => {
|
||||
const left = input.leftOccurrence;
|
||||
const right = input.rightOccurrence;
|
||||
return {
|
||||
asarPacking: compareOptionalMetadata(
|
||||
left?.unpacked,
|
||||
right?.unpacked,
|
||||
(value) => value,
|
||||
),
|
||||
nodeSea: compareOptionalMetadata(
|
||||
left?.format_metadata?.kind === "node-sea-entry"
|
||||
? left.format_metadata
|
||||
: undefined,
|
||||
right?.format_metadata?.kind === "node-sea-entry"
|
||||
? right.format_metadata
|
||||
: undefined,
|
||||
nodeSeaEntryDeclaration,
|
||||
),
|
||||
pyInstaller: compareOptionalMetadata(
|
||||
isPyInstallerEntry(left) ? left.format_metadata : undefined,
|
||||
isPyInstallerEntry(right) ? right.format_metadata : undefined,
|
||||
pyInstallerEntryDeclaration,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const isPyInstallerEntry = (
|
||||
occurrence: ArtifactOccurrence | undefined,
|
||||
@@ -518,13 +527,7 @@ const classifyPyInstallerDeclaration = (
|
||||
dimensions: ["availability"],
|
||||
},
|
||||
};
|
||||
const changed =
|
||||
canonicalJson(
|
||||
pyInstallerEntryDeclaration(input.leftOccurrence.format_metadata),
|
||||
) !==
|
||||
canonicalJson(
|
||||
pyInstallerEntryDeclaration(input.rightOccurrence.format_metadata),
|
||||
);
|
||||
const changed = input.metadata.pyInstaller === "changed";
|
||||
return {
|
||||
handled: true,
|
||||
change: changed
|
||||
@@ -534,6 +537,7 @@ const classifyPyInstallerDeclaration = (
|
||||
};
|
||||
|
||||
const changedDimensions = (input: {
|
||||
readonly metadata: OccurrenceMetadata;
|
||||
readonly leftNode: ArtifactNode | undefined;
|
||||
readonly rightNode: ArtifactNode | undefined;
|
||||
readonly leftOccurrence: ArtifactOccurrence | undefined;
|
||||
@@ -579,14 +583,15 @@ const changedDimensions = (input: {
|
||||
|
||||
const metadataChanged = (
|
||||
input: {
|
||||
readonly metadata: OccurrenceMetadata;
|
||||
readonly leftOccurrence: ArtifactOccurrence | undefined;
|
||||
readonly rightOccurrence: ArtifactOccurrence | undefined;
|
||||
},
|
||||
left: ArtifactNode,
|
||||
right: ArtifactNode,
|
||||
): boolean =>
|
||||
asarPackingChanged(input) ||
|
||||
nodeSeaMetadataChanged(input) ||
|
||||
input.metadata.asarPacking === "changed" ||
|
||||
input.metadata.nodeSea === "changed" ||
|
||||
left.media_type !== right.media_type ||
|
||||
left.architecture !== right.architecture ||
|
||||
left.content_state !== right.content_state ||
|
||||
@@ -595,14 +600,7 @@ const metadataChanged = (
|
||||
JSON.stringify(left.limitations) !== JSON.stringify(right.limitations) ||
|
||||
JSON.stringify(input.leftOccurrence?.limitations) !==
|
||||
JSON.stringify(input.rightOccurrence?.limitations) ||
|
||||
(isPyInstallerEntry(input.leftOccurrence) &&
|
||||
isPyInstallerEntry(input.rightOccurrence) &&
|
||||
canonicalJson(
|
||||
pyInstallerEntryDeclaration(input.leftOccurrence.format_metadata),
|
||||
) !==
|
||||
canonicalJson(
|
||||
pyInstallerEntryDeclaration(input.rightOccurrence.format_metadata),
|
||||
));
|
||||
input.metadata.pyInstaller === "changed";
|
||||
|
||||
const nodeFor = (
|
||||
occurrence: ArtifactOccurrence | undefined,
|
||||
|
||||
@@ -246,11 +246,7 @@ const validateInspectionSubject = (
|
||||
throw new ArtifactInventoryValidationError(
|
||||
"Artifact inspection subject does not match its inventory manifest",
|
||||
);
|
||||
if (
|
||||
subject === null ||
|
||||
subject.digest.sha256 !== manifest.root_sha256 ||
|
||||
subject.format !== manifest.root_format
|
||||
)
|
||||
if (!subjectMatchesInventoryRoot(subject, manifest))
|
||||
throw new ArtifactInventoryValidationError(
|
||||
"Artifact inspection Evidence subject does not match its root manifest",
|
||||
);
|
||||
@@ -295,16 +291,20 @@ const parseInventoryEvidencePage = (
|
||||
}
|
||||
};
|
||||
|
||||
const subjectMatchesInventoryRoot = (
|
||||
subject: Evidence["subject"],
|
||||
manifest: ArtifactInventoryResult["manifest"],
|
||||
): boolean =>
|
||||
subject !== null &&
|
||||
subject.digest.sha256 === manifest.root_sha256 &&
|
||||
subject.format === manifest.root_format;
|
||||
|
||||
/** Both inventory carriers require the same authenticated root identity. */
|
||||
export const validateArtifactInventorySubject = (
|
||||
subject: Evidence["subject"],
|
||||
manifest: ArtifactInventoryResult["manifest"],
|
||||
): void => {
|
||||
if (
|
||||
subject === null ||
|
||||
subject.digest.sha256 !== manifest.root_sha256 ||
|
||||
subject.format !== manifest.root_format
|
||||
)
|
||||
if (!subjectMatchesInventoryRoot(subject, manifest))
|
||||
throw new ArtifactInventoryValidationError(
|
||||
"Artifact inventory Evidence subject does not match its root digest",
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user