From 86b1b1431a4bf76db27f6aefcb5bede64644de13 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Tue, 10 Nov 2026 16:47:45 -0500 Subject: [PATCH] test: preserve method-free IL2CPP overflow verification --- scripts/verify-il2cpp-metadata.mjs | 21 ++++++++++---- .../process/il2cppVerifierRetention.test.ts | 29 +++++++++++++++---- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/scripts/verify-il2cpp-metadata.mjs b/scripts/verify-il2cpp-metadata.mjs index ac73c7f951..4bf956347c 100644 --- a/scripts/verify-il2cpp-metadata.mjs +++ b/scripts/verify-il2cpp-metadata.mjs @@ -6,6 +6,7 @@ import { Client } from "@modelcontextprotocol/client"; import { StdioClientTransport } from "@modelcontextprotocol/client/stdio"; import { absoluteCallerPath } from "../dist/filesystem/callerPath.js"; import { toolContract } from "../dist/contracts/toolContracts.js"; +import { il2cppMetadataViewRequestSchema } from "../dist/domain/analysisView/il2cppMetadataView.js"; import { ArtifactResourceScope } from "../dist/artifacts/ArtifactResourceScope.js"; import { requireMcpOutput, @@ -79,24 +80,32 @@ await withVerifierCleanup(async () => { assert.equal(error.code, "resource_constraint"); assert.equal(error.details.reported_limits.boundary, "mcp-response"); assert.match(error.details.reported_limits.evidence_id, /^ev_/u); + const collections = il2cppMetadataViewRequestSchema.options.map( + (option) => option.shape.collection.value, + ); + const collection = + ["methods", ...collections].find( + (name) => Array.isArray(report[name]) && report[name].length > 0, + ) ?? "methods"; + const expectedRows = report[collection].slice(0, 16); const retained = ( await call("inspect_analysis_view", { source: { kind: "retained-evidence", evidence_id: error.details.reported_limits.evidence_id, }, - view: { kind: "il2cpp-metadata", collection: "methods", limit: 16 }, + view: { kind: "il2cpp-metadata", collection, limit: 16 }, }) ).normalized_result; - assert.ok( - report.methods.length > 0, - "Overflow retention needs a nonempty method oracle", - ); assert.deepEqual( retained.items, - report.methods.slice(0, 16).map((row, index) => ({ ...row, index })), + expectedRows.map((row, index) => ({ ...row, index })), "Failed complete delivery must retain its exact Evidence before reanalysis", ); + if (expectedRows.length === 0) + console.log( + "[unverified] Retention lookup succeeded, but empty queryable tables provide no nonempty row-content witness", + ); console.log( `[ok] MCP complete delivery refused at the stdio budget (${error.details.reported_limits.response_bytes_at_least} bytes) with retained Evidence`, ); diff --git a/tests/boundary/process/il2cppVerifierRetention.test.ts b/tests/boundary/process/il2cppVerifierRetention.test.ts index 811348a9f2..26d8a07974 100644 --- a/tests/boundary/process/il2cppVerifierRetention.test.ts +++ b/tests/boundary/process/il2cppVerifierRetention.test.ts @@ -8,9 +8,18 @@ import { expect, it } from "vitest"; const execute = promisify(execFile); -it.each([true, false])( - "requires overflow Evidence to be retained before another analysis (retained=%s)", - async (retained) => { +it.each([ + ["methods", true], + ["methods", false], + ["types", true], + ["types", false], + ["sections", true], + ["sections", false], + ["empty", true], + ["empty", false], +] as const)( + "requires overflow Evidence before reanalysis (%s, retained=%s)", + async (collection, retained) => { const directory = await mkdtemp( join(tmpdir(), "rea-il2cpp-retention-oracle-"), ); @@ -24,7 +33,10 @@ import {promisify} from 'node:util'; const path=process.env.INPUT; const identity={sha256:'a'.repeat(64),bytes:1}; const report={artifact:{path,...identity},magic:0xfab11baf,sections:[],images:[{type_count:1}],types:[{declaring_type:-1,method_start:0,method_count:1}],methods:[{declaring_type:0,name:'AuthoredMethod'}],fields:[],properties:[],events:[],parameters:[],string_literals:[]}; -const counts={sections:0,images:1,types:1,methods:1,nested_types:0,fields:0,properties:0,events:0,parameters:0,string_literals:0,...Object.fromEntries(['code_gen_modules','method_addresses','method_address_states','return_types','field_types','parameter_types','generic_method_instances','generic_method_instance_states','generic_method_issues'].map(k=>[k,null]))}; +if(process.env.COLLECTION!=='methods'){report.methods=[];report.types[0].method_count=0;} +if(['sections','empty'].includes(process.env.COLLECTION)){report.images=[];report.types=[];} +if(process.env.COLLECTION==='sections')report.sections=[{name:'authored-nonmethod-table',offset:64,size:4096}]; +const counts={sections:report.sections.length,images:report.images.length,types:report.types.length,methods:report.methods.length,nested_types:0,fields:0,properties:0,events:0,parameters:0,string_literals:0,...Object.fromEntries(['code_gen_modules','method_addresses','method_address_states','return_types','field_types','parameter_types','generic_method_instances','generic_method_instance_states','generic_method_issues'].map(k=>[k,null]))}; let reanalyzed=false; export class Client { async connect(){} async close(){} async listTools(){return {tools:[{name:'inspect_il2cpp_metadata'}]};} @@ -76,13 +88,20 @@ registerHooks({resolve(specifier,context,next){ RECORDING: join(directory, "recording.mjs"), INPUT: join(directory, "metadata.dat"), RETAINED: String(retained), + COLLECTION: collection, }, }, ); if (retained) { - expect((await result).stdout).toContain( + const { stdout } = await result; + expect(stdout).toContain( "All asserted IL2CPP metadata CLI/MCP checks passed", ); + expect( + stdout.includes( + "empty queryable tables provide no nonempty row-content witness", + ), + ).toBe(collection === "empty"); } else { await expect(result).rejects.toMatchObject({ stderr: expect.stringContaining(