From ed86f9c3bb7910fb8e35cd7a3df3a50a50da58a8 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sun, 25 Oct 2026 07:53:17 +0800 Subject: [PATCH] fix(verifiers): propagate session assertion failures synchronously Remove the redundant async declaration from the Ghidra session assertion helper so its existing callers receive assertion failures within their synchronous error boundary. Keep call order unchanged. Validation: a public-helper Node subprocess accepts authored valid handshake facts and rejects incomplete analysis and a target-digest mismatch. The regression failed for the original async declaration with Missing expected exception, then passed with the fix. No installed Ghidra execution was performed. --- scripts/verify-real-ghidra-assertions.mjs | 6 +- .../process/ghidraVerifierAssertions.test.ts | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 tests/boundary/process/ghidraVerifierAssertions.test.ts diff --git a/scripts/verify-real-ghidra-assertions.mjs b/scripts/verify-real-ghidra-assertions.mjs index ae9dbbbbc3..f79f287df9 100644 --- a/scripts/verify-real-ghidra-assertions.mjs +++ b/scripts/verify-real-ghidra-assertions.mjs @@ -245,11 +245,7 @@ export function dossierSummary(dossier) { }; } -export async function assertSession( - session, - expectedDigest, - expectedTargetSha256, -) { +export function assertSession(session, expectedDigest, expectedTargetSha256) { if ( session.provider.id !== "ghidra" || session.provider.version !== SUPPORTED_GHIDRA_VERSION || diff --git a/tests/boundary/process/ghidraVerifierAssertions.test.ts b/tests/boundary/process/ghidraVerifierAssertions.test.ts new file mode 100644 index 0000000000..bd6554bf7e --- /dev/null +++ b/tests/boundary/process/ghidraVerifierAssertions.test.ts @@ -0,0 +1,68 @@ +import { expect } from "vitest"; + +import { processTest } from "../../support/process/processFixture.js"; + +processTest( + "Ghidra verifier session assertions fail within the caller's synchronous error boundary", + async ({ processes }) => { + const assertionsUrl = new URL( + "../../../scripts/verify-real-ghidra-assertions.mjs", + import.meta.url, + ).href; + const valuesUrl = new URL( + "../../../dist/ghidra/GhidraSessionValues.js", + import.meta.url, + ).href; + const policyUrl = new URL( + "../../../dist/ghidra/GhidraReleasePolicy.js", + import.meta.url, + ).href; + // Authored handshake facts exercise the verifier, not a running provider. + const source = ` + import assert from "node:assert/strict"; + import { assertSession } from ${JSON.stringify(assertionsUrl)}; + import { GHIDRA_SESSION_CAPABILITIES } from ${JSON.stringify(valuesUrl)}; + import { SUPPORTED_GHIDRA_VERSION } from ${JSON.stringify(policyUrl)}; + + const profileDigest = "a".repeat(64); + const targetSha256 = "b".repeat(64); + const session = { + name: "REA Ghidra bridge", + run_id: "d6fcbb66-e829-4ff6-a535-0035aec63139", + profile_digest: profileDigest, + provider: { id: "ghidra", version: SUPPORTED_GHIDRA_VERSION }, + read_only: false, + analysis_complete: true, + analysis_timed_out: false, + capabilities: [...GHIDRA_SESSION_CAPABILITIES], + target: { + name: "fixture.bin", + language_id: "x86:LE:64:default", + compiler_spec_id: "gcc", + image_base: "0x401000", + default_address_space: "ram", + sha256: targetSha256, + }, + }; + + assert.doesNotThrow(() => assertSession(session, profileDigest, targetSha256)); + assert.throws( + () => assertSession( + { ...session, analysis_complete: false }, profileDigest, targetSha256, + ), + /Ghidra session commitment drifted/, + ); + assert.throws( + () => assertSession(session, profileDigest, "c".repeat(64)), + /Ghidra session commitment drifted/, + ); + `; + const result = await processes.run( + process.execPath, + ["--input-type=module", "--eval", source], + { timeoutMs: 10_000 }, + ); + expect(result.signal, result.stderr).toBeNull(); + expect(result.exitCode, result.stderr).toBe(0); + }, +);