mirror of
https://github.com/morluto/rea
synced 2026-10-27 22:45:19 -04:00
173da755cd
* fix(reconstruction): reject contradictory function comparison status * test(reconstruction): cover imported function match precedence --------- Co-authored-by: maxazure <405711+maxazure@users.noreply.github.com>
118 lines
3.4 KiB
TypeScript
118 lines
3.4 KiB
TypeScript
import { z } from "zod";
|
|
import { overallStatus } from "./functionComparisonResults.js";
|
|
import { evidenceSchema } from "./evidence.js";
|
|
import { digestSchema } from "./../domain/digests.js";
|
|
import { prefixedDigestSchema } from "./../domain/digests.js";
|
|
|
|
const comparisonStatusSchema = z.enum(["unchanged", "changed", "unknown"]);
|
|
export const functionComparisonDimensionNameSchema = z.enum([
|
|
"identity",
|
|
"pseudocode",
|
|
"assembly",
|
|
"comments",
|
|
"calls",
|
|
"references",
|
|
"strings_names",
|
|
"cfg",
|
|
]);
|
|
const evidenceIdSchema = prefixedDigestSchema("ev");
|
|
|
|
/** Inputs for explicit function-to-function comparison. */
|
|
export const functionComparisonInputSchema = z.strictObject({
|
|
left: evidenceSchema,
|
|
right: evidenceSchema,
|
|
});
|
|
|
|
const textDeltaSchema = z.object({
|
|
added_lines: z.number().int().min(0),
|
|
removed_lines: z.number().int().min(0),
|
|
hunks: z.number().int().min(0),
|
|
});
|
|
|
|
const functionDimensionContextShape = {
|
|
dimension: functionComparisonDimensionNameSchema,
|
|
left_count: z.number().int().min(0).nullable(),
|
|
right_count: z.number().int().min(0).nullable(),
|
|
evidence_links: z.array(evidenceIdSchema).min(2),
|
|
limitations: z.array(z.string()),
|
|
};
|
|
|
|
const functionDimensionSchema = z.union([
|
|
z.object({
|
|
...functionDimensionContextShape,
|
|
status: z.literal("unchanged"),
|
|
left_digest: digestSchema,
|
|
right_digest: digestSchema,
|
|
text_delta: textDeltaSchema.nullable(),
|
|
conclusion_kind: z.literal("derived_relationship"),
|
|
}),
|
|
z.object({
|
|
...functionDimensionContextShape,
|
|
status: z.literal("changed"),
|
|
left_digest: digestSchema,
|
|
right_digest: digestSchema,
|
|
text_delta: textDeltaSchema.nullable(),
|
|
conclusion_kind: z.enum(["derived_relationship", "contradiction"]),
|
|
}),
|
|
z.object({
|
|
...functionDimensionContextShape,
|
|
status: z.literal("unknown"),
|
|
left_digest: z.null(),
|
|
right_digest: z.null(),
|
|
text_delta: z.null(),
|
|
conclusion_kind: z.literal("unresolved_branch"),
|
|
}),
|
|
]);
|
|
|
|
const functionMatchSchema = z.union([
|
|
z.object({
|
|
status: z.literal("matched"),
|
|
method: z.literal("symbol"),
|
|
left_name: z.string(),
|
|
right_name: z.string(),
|
|
}),
|
|
z.object({
|
|
status: z.enum(["mismatched", "ambiguous"]),
|
|
method: z.literal("explicit"),
|
|
left_name: z.string(),
|
|
right_name: z.string(),
|
|
}),
|
|
]);
|
|
|
|
/** Deterministic dimension-classified function comparison Evidence payload. */
|
|
export const functionComparisonResultSchema = z
|
|
.object({
|
|
status: comparisonStatusSchema,
|
|
function_match: functionMatchSchema,
|
|
left_subject_sha256: digestSchema,
|
|
right_subject_sha256: digestSchema,
|
|
summary: z.object({
|
|
unchanged: z.number().int().min(0),
|
|
changed: z.number().int().min(0),
|
|
unknown: z.number().int().min(0),
|
|
}),
|
|
dimensions: z.array(functionDimensionSchema).length(8),
|
|
changes: z.array(functionDimensionSchema),
|
|
limitations: z.array(z.string()),
|
|
})
|
|
.superRefine((result, context) => {
|
|
if (
|
|
result.status !==
|
|
overallStatus(result.dimensions, result.function_match.status)
|
|
)
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: ["status"],
|
|
message:
|
|
"Function comparison status contradicts its dimensions or function match.",
|
|
});
|
|
});
|
|
|
|
export type FunctionComparisonResult = z.infer<
|
|
typeof functionComparisonResultSchema
|
|
>;
|
|
export type FunctionDimension = z.infer<typeof functionDimensionSchema>;
|
|
export type DimensionName = z.infer<
|
|
typeof functionComparisonDimensionNameSchema
|
|
>;
|