From 3655dfd7d5db25f52a0a68c2651527d80935be2d Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:30:54 +0800 Subject: [PATCH] fix(elf): validate completed note coverage --- .../binaryDiagnostics/ElfNotesService.test.ts | 46 +++++++++++++++++++ .../binaryDiagnostics/ElfNotesService.ts | 3 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/application/binaryDiagnostics/ElfNotesService.test.ts b/src/application/binaryDiagnostics/ElfNotesService.test.ts index da0d0e67f9..1b51f0c763 100644 --- a/src/application/binaryDiagnostics/ElfNotesService.test.ts +++ b/src/application/binaryDiagnostics/ElfNotesService.test.ts @@ -6,6 +6,7 @@ import { ok } from "../../domain/result.js"; import { AnalysisCancelledError, AnalysisInputError, + AnalysisOutputError, } from "../../domain/analysisErrorCore.js"; const identity = ELF_NOTES_PROVIDER; @@ -61,6 +62,51 @@ it("records the selected ELF and an absent note table", async () => { }); }); +it.each(["examined_tables", "examined_entries"] as const)( + "rejects complete provider coverage with inconsistent %s", + async (count) => { + const result = await new ElfNotesService({ + identity, + inspect: () => + Promise.resolve( + ok({ + ...report, + coverage: { ...report.coverage, [count]: 1 }, + }), + ), + }).inspect({ path: report.artifact.path }); + expect(result.ok).toBe(false); + if (!result.ok) expect(result.error).toBeInstanceOf(AnalysisOutputError); + }, +); + +it("retains an examined empty table and rejects an uncounted table", async () => { + const table = { + kind: "program" as const, + index: 0, + name: null, + location: { offset: 128, bytes: 0 }, + alignment: 4, + }; + for (const examinedTables of [0, 1]) { + const result = await new ElfNotesService({ + identity, + inspect: () => + Promise.resolve( + ok({ + ...report, + tables: [table], + coverage: { ...report.coverage, examined_tables: examinedTables }, + }), + ), + }).inspect({ path: report.artifact.path }); + expect(result.ok).toBe(examinedTables === 1); + if (result.ok) + expect(result.value.normalized_result).toMatchObject({ tables: [table] }); + else expect(result.error).toBeInstanceOf(AnalysisOutputError); + } +}); + it("discards an observation if the caller cancels while the provider completes", async () => { const controller = new AbortController(); const service = new ElfNotesService({ diff --git a/src/application/binaryDiagnostics/ElfNotesService.ts b/src/application/binaryDiagnostics/ElfNotesService.ts index 2640172e23..47c9468af2 100644 --- a/src/application/binaryDiagnostics/ElfNotesService.ts +++ b/src/application/binaryDiagnostics/ElfNotesService.ts @@ -112,7 +112,8 @@ const validReport = ( if ( report.artifact.bytes > maxBytes || report.coverage.examined_entries > maxEntries || - report.entries.length > maxEntries + report.entries.length !== report.coverage.examined_entries || + report.tables.length !== report.coverage.examined_tables ) return false; return (