diff --git a/scripts/verify-package.mjs b/scripts/verify-package.mjs index 4603ea416..2ce2d9785 100644 --- a/scripts/verify-package.mjs +++ b/scripts/verify-package.mjs @@ -170,72 +170,88 @@ try { throw new Error("packaged artifact inventory CLI failed"); // prettier-ignore await verifyPackagedInvestigation({ cli, workspace, evidenceRoot, artifactArchive, environment }); - const overview = json( - await run(cli, ["analyze", process.execPath, "--json"], environment), - ); - if ( - overview.operation !== "binary_overview" || - overview.normalized_result?.procedure_count < 1 - ) - throw new Error( - `packaged Hopper-backed analyze CLI failed: ${JSON.stringify(overview)}`, + if (process.platform === "linux") { + const unsupportedExecution = await runWithStatus( + cli, + ["analyze", process.execPath, "--json"], + environment, ); - const inspected = json( - await run( - cli, - [ - "inspect", - process.execPath, - "--detail", - "detailed", - "--limit", - "1", - "--json", - ], - environment, - ), - ); - if ( - inspected.operation !== "binary_overview" || - inspected.normalized_result?.detail !== "detailed" - ) - throw new Error("packaged inspect CLI failed"); - const functionResult = json( - await run( - cli, - ["function", process.execPath, "0x1000", "--json"], - environment, - ), - ); - if ( - functionResult.operation !== "analyze_function" || - functionResult.provider?.id !== "hopper" || - functionResult.normalized_result?.procedure?.address !== "0x1000" - ) - throw new Error( - `packaged function CLI failed: ${JSON.stringify(functionResult)}`, + const unsupported = json(unsupportedExecution.stdout); + if ( + unsupportedExecution.status !== 1 || + unsupported.details?.failure_code !== "unsupported_hopper_build" + ) + throw new Error( + `packaged Linux Hopper verification did not fail closed: ${JSON.stringify(unsupported)}`, + ); + } else { + const overview = json( + await run(cli, ["analyze", process.execPath, "--json"], environment), ); - const xrefs = json( - await run( - cli, - ["xrefs", process.execPath, "0x1000", "--json"], - environment, - ), - ); - if ( - xrefs.operation !== "xrefs" || - JSON.stringify(xrefs.normalized_result) !== JSON.stringify(["0x1000"]) - ) - throw new Error(`packaged xrefs CLI failed: ${JSON.stringify(xrefs)}`); - const trace = json( - await run( - cli, - ["trace", process.execPath, "fixture", "--json"], - environment, - ), - ); - if (trace.operation !== "trace_feature") - throw new Error(`packaged trace CLI failed: ${JSON.stringify(trace)}`); + if ( + overview.operation !== "binary_overview" || + overview.normalized_result?.procedure_count < 1 + ) + throw new Error( + `packaged Hopper-backed analyze CLI failed: ${JSON.stringify(overview)}`, + ); + const inspected = json( + await run( + cli, + [ + "inspect", + process.execPath, + "--detail", + "detailed", + "--limit", + "1", + "--json", + ], + environment, + ), + ); + if ( + inspected.operation !== "binary_overview" || + inspected.normalized_result?.detail !== "detailed" + ) + throw new Error("packaged inspect CLI failed"); + const functionResult = json( + await run( + cli, + ["function", process.execPath, "0x1000", "--json"], + environment, + ), + ); + if ( + functionResult.operation !== "analyze_function" || + functionResult.provider?.id !== "hopper" || + functionResult.normalized_result?.procedure?.address !== "0x1000" + ) + throw new Error( + `packaged function CLI failed: ${JSON.stringify(functionResult)}`, + ); + const xrefs = json( + await run( + cli, + ["xrefs", process.execPath, "0x1000", "--json"], + environment, + ), + ); + if ( + xrefs.operation !== "xrefs" || + JSON.stringify(xrefs.normalized_result) !== JSON.stringify(["0x1000"]) + ) + throw new Error(`packaged xrefs CLI failed: ${JSON.stringify(xrefs)}`); + const trace = json( + await run( + cli, + ["trace", process.execPath, "fixture", "--json"], + environment, + ), + ); + if (trace.operation !== "trace_feature") + throw new Error(`packaged trace CLI failed: ${JSON.stringify(trace)}`); + } const capabilities = json( await run(cli, ["capabilities", "--json"], environment), ); @@ -247,20 +263,22 @@ try { providers.providers.some(({ id }) => typeof id !== "string") ) throw new Error("packaged providers CLI failed"); - const searchResult = json( - await run( - cli, - ["search", process.execPath, "fixture", "--json"], - environment, - ), - ); - if ( - searchResult.operation !== "search_strings" || - searchResult.normalized_result?.items?.length !== 1 - ) - throw new Error( - `packaged search CLI failed: ${JSON.stringify(searchResult)}`, + if (process.platform !== "linux") { + const searchResult = json( + await run( + cli, + ["search", process.execPath, "fixture", "--json"], + environment, + ), ); + if ( + searchResult.operation !== "search_strings" || + searchResult.normalized_result?.items?.length !== 1 + ) + throw new Error( + `packaged search CLI failed: ${JSON.stringify(searchResult)}`, + ); + } const referenceImport = json( await run( cli, @@ -478,30 +496,41 @@ try { ); if (opened.isError === true) throw new Error("packaged MCP could not open a binary"); - await prompts.verifyPromptCompletion(client, mcpOptions, true); + await prompts.verifyPromptCompletion( + client, + mcpOptions, + process.platform !== "linux", + ); const current = await client.callTool( { name: "current_document", arguments: {} }, mcpOptions, ); - if ( - current.isError === true || - json(prompts.mcpText(current)).normalized_result !== "fixture" - ) - throw new Error("packaged MCP bridge call failed"); - const batch = await client.callTool( - { name: "batch_decompile", arguments: { addresses: ["0x1000"] } }, - mcpOptions, - ); - const batchResult = json(prompts.mcpText(batch)).normalized_result; - if ( - batch.isError === true || - batchResult?.total !== 1 || - batchResult?.succeeded !== 1 || - batchResult?.failed !== 0 || - batchResult?.items?.[0]?.status !== "ok" || - batchResult?.items?.[0]?.pseudocode !== "return 0;" - ) - throw new Error("packaged MCP structured batch result failed"); + if (process.platform === "linux") { + if ( + current.isError !== true || + !prompts + .mcpText(current) + .includes("not supported for unattended Linux analysis") + ) + throw new Error("packaged Linux MCP did not reject an unpinned Hopper"); + } else { + if (json(prompts.mcpText(current)).normalized_result !== "fixture") + throw new Error("packaged MCP bridge call failed"); + const batch = await client.callTool( + { name: "batch_decompile", arguments: { addresses: ["0x1000"] } }, + mcpOptions, + ); + const batchResult = json(prompts.mcpText(batch)).normalized_result; + if ( + batch.isError === true || + batchResult?.total !== 1 || + batchResult?.succeeded !== 1 || + batchResult?.failed !== 0 || + batchResult?.items?.[0]?.status !== "ok" || + batchResult?.items?.[0]?.pseudocode !== "return 0;" + ) + throw new Error("packaged MCP structured batch result failed"); + } const closed = await client.callTool( { name: "close_binary", arguments: {} }, mcpOptions, diff --git a/tests/runtime.test.ts b/tests/runtime.test.ts index da3bfa55f..b58b50441 100644 --- a/tests/runtime.test.ts +++ b/tests/runtime.test.ts @@ -37,7 +37,7 @@ describe("production stdio runtime", () => { name: "current_document", arguments: {}, }); - expect(result.isError).not.toBe(true); + expect(result.isError === true).toBe(process.platform === "linux"); } finally { await client.close(); await transport.close(); @@ -53,7 +53,7 @@ describe("production stdio runtime", () => { mode: "mcp", layer: "server", tool: "current_document", - status: "ok", + status: process.platform === "linux" ? "error" : "ok", }), ); expect(stderr).not.toContain("HOPPER_LOADER_ARGS_JSON");