fix(contracts)!: require observed provider output metadata
Reject omitted segment bounds, procedure classification and body facts, unresolved-call observations, dossier facets, and Ghidra parameter flow metadata. Providers emit explicit nulls for unsupported facets. Verify those unknowns in the real Hopper workflow. BREAKING CHANGE: Provider analysis outputs must explicitly supply metadata previously repaired with defaults; unsupported nullable facets must be reported as null.
This commit is contained in:
1 parent
e3bbb9ac45
commit
a4ba8aecd5
29 files changed
+465
-112
No files matched your search
@@ -58,6 +58,12 @@ async function verifyNavigation(
|
||||
procedure: address,
|
||||
});
|
||||
const info = await call("procedure_info", { procedure: address });
|
||||
assert.equal(info.classification, null);
|
||||
assert.equal(info.body.available, false);
|
||||
assert.match(
|
||||
info.body.reason,
|
||||
/public Python API does not expose complete function body ranges/u,
|
||||
);
|
||||
assert.ok(info.locals.length > 0, "fixture omitted real stack locals");
|
||||
for (const local of info.locals) {
|
||||
assert.equal(typeof local.name, "string");
|
||||
@@ -68,6 +74,18 @@ async function verifyNavigation(
|
||||
}
|
||||
const dossier = await call("analyze_function", { procedure: address });
|
||||
assert.deepEqual(dossier.procedure.locals, info.locals);
|
||||
assert.equal(dossier.procedure.classification, null);
|
||||
assert.equal(dossier.procedure.body.available, false);
|
||||
assert.match(
|
||||
dossier.procedure.body.reason,
|
||||
/public Python API does not expose complete function body ranges/u,
|
||||
);
|
||||
assert.equal(dossier.native_api, null);
|
||||
assert.equal(dossier.native_value_flow, null);
|
||||
assert.ok(
|
||||
Array.isArray(dossier.limitations) && dossier.limitations.length > 0,
|
||||
"function dossier omitted Hopper's unsupported-facet limitations",
|
||||
);
|
||||
const coordinates = instructions.instructions.map(
|
||||
(line) => /^0x[0-9a-f]+/u.exec(line)?.[0],
|
||||
);
|
||||
|
||||
@@ -11,15 +11,28 @@ export const requireTruthfulMemoryRegions = (segments) => {
|
||||
...(Array.isArray(segment.sections) ? segment.sections : []),
|
||||
]);
|
||||
for (const region of regions) {
|
||||
if (
|
||||
typeof region?.name !== "string" ||
|
||||
region.name.length === 0 ||
|
||||
typeof region.start !== "string" ||
|
||||
typeof region.end !== "string"
|
||||
) {
|
||||
throw new Error(
|
||||
"list_segments omitted observed region identity or bounds",
|
||||
);
|
||||
}
|
||||
for (const permission of ["readable", "writable", "executable"]) {
|
||||
if (![true, false, null].includes(region?.[permission])) {
|
||||
throw new Error(`list_segments omitted tri-state ${permission}`);
|
||||
if (region[permission] !== null) {
|
||||
throw new Error(
|
||||
`list_segments invented or omitted unknown ${permission}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (
|
||||
region.provenance !== "hopper-public-python-api" ||
|
||||
region.permissions?.available !== false ||
|
||||
typeof region.permissions.reason !== "string"
|
||||
typeof region.permissions.reason !== "string" ||
|
||||
region.permissions.reason.length === 0
|
||||
) {
|
||||
throw new Error("list_segments omitted permission provenance");
|
||||
}
|
||||
@@ -141,6 +154,15 @@ export async function verifyRealHopperFixture({
|
||||
"procedure_references",
|
||||
);
|
||||
assert.deepEqual(entryDossier.unresolved_calls, observed.unresolved_calls);
|
||||
assert.equal(
|
||||
observed.reference_kinds_available,
|
||||
false,
|
||||
"Hopper's partial CallReference observations do not expose detailed reference kinds",
|
||||
);
|
||||
assert.ok(
|
||||
Array.isArray(observed.unresolved_calls),
|
||||
"procedure_references omitted its observed unresolved CallReference results",
|
||||
);
|
||||
for (const call of calls) {
|
||||
assert.equal(call.call.provider_type, 2);
|
||||
assert.equal(
|
||||
|
||||
@@ -62,6 +62,10 @@ export function functionDossier(name) {
|
||||
thunk_target: null,
|
||||
provenance: "synthetic-provider",
|
||||
},
|
||||
body: {
|
||||
available: false,
|
||||
reason: "Synthetic provider does not supply function body ranges.",
|
||||
},
|
||||
signature: null,
|
||||
locals: [],
|
||||
},
|
||||
@@ -75,6 +79,8 @@ export function functionDossier(name) {
|
||||
referenced_strings: [],
|
||||
referenced_names: [],
|
||||
basic_blocks: [],
|
||||
native_api: null,
|
||||
native_value_flow: null,
|
||||
limitations: [],
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user