diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index e58c4d94f2..61426ff708 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -60,7 +60,7 @@ pin inspection sources; they are not included in the npm package. | ghidra-nativeaot | [MIT](https://github.com/Washi1337/ghidra-nativeaot/blob/effeb734fc570c32650f88b159608979dc7b423e/LICENSE.md) | Pinned inspection source; derived parser attribution above. | | JADX | [Apache-2.0](https://github.com/skylot/jadx/blob/v1.5.6/LICENSE) | The audited jadx-headless-mcp source uses JADX 1.5.6 for Android decompilation; the engine JAR is separately supplied. | | jadx-headless-mcp | [Apache-2.0](https://github.com/1013503897/jadx-headless-mcp/blob/5844800a486d2248fa949b74c9896285f5b54de2/LICENSE) | v0.7.1; pinned submodule and user-supplied JAR. | -| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.988/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.988/NOTICE) | Bring your own `kuna`; REA verified 1.988 and never downloads, installs or bundles it. REA invokes it as a separate process. | +| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.989/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.989/NOTICE) | Bring your own `kuna`; REA verified 1.989 and never downloads, installs or bundles it. REA invokes it as a separate process. | | Apktool | [Apache-2.0](https://github.com/iBotPeaches/Apktool/blob/master/LICENSE.md) | User-supplied Android resource decoder. | | Binwalk | [MIT](https://github.com/ReFirmLabs/binwalk/blob/4fdab3d464d97b68e0af9088df3f9e2e1545b21c/LICENSE) | v3.1.0; pinned submodule and user-supplied firmware engine. | | Unblob | [MIT](https://github.com/onekey-sec/unblob/blob/1fcc7a0a584a70a96c31f5a276c20944d199a089/LICENSE) | 26.6.4; pinned submodule and user-supplied firmware engine. | diff --git a/docs/kuna-provider.md b/docs/kuna-provider.md index c6266ce718..fdbde7526d 100644 --- a/docs/kuna-provider.md +++ b/docs/kuna-provider.md @@ -1,9 +1,27 @@ # Kuna provider -[Kuna](https://github.com/Noelo-Lab/kuna) is an Apache-2.0 decompiler distributed as one `kuna` executable. For a new installation, use Kuna 1.988, REA's recommended verified release. REA uses a `kuna` you supply; it does not download, install, update or bundle Kuna. REA accepts other releases, which it reports as unverified in `rea doctor` and in each analysis profile. The verified-release flag compares the reported version; it does not authenticate the executable. The 1.988 real-provider checks ran on macOS arm64; Linux and other hosts have not been verified with that release. +[Kuna](https://github.com/Noelo-Lab/kuna) is an Apache-2.0 decompiler distributed as one `kuna` executable. For a new installation, use Kuna 1.989, REA's recommended verified release. REA uses a `kuna` you supply; it does not download, install, update or bundle Kuna. REA accepts other releases; `rea doctor` and analysis Evidence report them as unverified. Verification compares the reported version; it does not authenticate the executable. Analysis profiles retain the observed version and executable digest, independently of REA's current verified-release policy. The 1.989 real-provider checks ran on macOS arm64; Linux and other hosts have not been verified with that release. Kuna is static: it reads the selected image and never executes it. +This profile change makes earlier Kuna analysis snapshots and resumable directory +exports incompatible, even with the same Kuna executable. Keep the old files: +their recorded Evidence remains historical. To continue analysis, open the +binary without the old snapshot and save a new one, or choose a new export +directory. Existing snapshot Evidence can be extracted from its +`evidence_bundle` JSON member and imported as a separate Evidence bundle; that +does not restore cached workflow entries or resume the old directory export. +For example, using `jq` and a new output path: + +```bash +jq -e '.evidence_bundle | select(type == "object")' old-snapshot.json > history.json +rea evidence-import history.json --json +``` + +The CLI command validates the bundle in a one-shot session. To use the records +in an MCP session, call `import_evidence_bundle` with the extracted file path. +This validates the embedded bundle, not the rest of the snapshot container. + ## Configure ```bash diff --git a/docs/testing-lanes.md b/docs/testing-lanes.md index 4a8dcd65cf..5e54caf8a2 100644 --- a/docs/testing-lanes.md +++ b/docs/testing-lanes.md @@ -451,6 +451,11 @@ Mach-O fixture and caller-built symbol-bearing and stripped x86-64 ELF fixtures. Linux and other hosts have not been verified with 1.988; this is not a repeat of the larger 1.833 matrix. +Kuna 1.989 passed the real-provider lane on macOS arm64 with the generated +symbol-bearing and stripped Mach-O fixtures, including cold and warm reads, +references, and cleanup. Linux, other hosts, and the larger 1.833 target matrix +were not verified with 1.989. + ## DOS Ghidra analysis `npm run verify:ghidra:dos` requires the supported Ghidra and JDK installation diff --git a/src/kuna/KunaProvider.ts b/src/kuna/KunaProvider.ts index 33960e3cc7..1b0b91981e 100644 --- a/src/kuna/KunaProvider.ts +++ b/src/kuna/KunaProvider.ts @@ -265,7 +265,6 @@ export class KunaProvider implements AnalysisProviderCandidate { kuna_sha256: value.sha256, kuna_command: value.configuration.command, kuna_executable: value.executable, - verified_release: value.verified, jobs: value.configuration.jobs, export: "decompile-project", reference_graph: { diff --git a/src/kuna/KunaRelease.ts b/src/kuna/KunaRelease.ts index a467c488a2..1d46a2aa30 100644 --- a/src/kuna/KunaRelease.ts +++ b/src/kuna/KunaRelease.ts @@ -1,5 +1,5 @@ /** Kuna release whose CLI and project layout REA verified; others report as unverified. */ -export const KUNA_VERIFIED_VERSION = "1.988"; +export const KUNA_VERIFIED_VERSION = "1.989"; /** Stable provider ID shared by readiness, doctor and Evidence. */ export const KUNA_PROVIDER_ID = "kuna"; diff --git a/src/kuna/KunaSessionClient.ts b/src/kuna/KunaSessionClient.ts index d7123dd106..f3640d91c9 100644 --- a/src/kuna/KunaSessionClient.ts +++ b/src/kuna/KunaSessionClient.ts @@ -89,7 +89,7 @@ import { kunaLimitations, type KunaOperation, } from "./KunaProviderCapabilities.js"; -import { KUNA_PROVIDER_ID } from "./KunaRelease.js"; +import { KUNA_PROVIDER_ID, KUNA_VERIFIED_VERSION } from "./KunaRelease.js"; import { KunaTargetCommands } from "./KunaTargetCommands.js"; import { KunaReferenceQueries } from "./KunaReferenceQueries.js"; import { KunaQueryWork } from "./KunaQueryWork.js"; @@ -285,6 +285,16 @@ const exportUnavailable = ( ); }; +/** Version verification is observation context, not an analysis input. */ +const kunaVerificationLimitations = ( + installation: KunaInstallation, +): readonly string[] => + installation.verified + ? [] + : [ + `Kuna ${installation.version} is not REA's verified ${KUNA_VERIFIED_VERSION} release; its project layout is unverified. Version matching does not authenticate the executable.`, + ]; + /** Kuna names a procedure by its inventory entry or by an exact entry address. */ const procedureSelectorSchema = z.string().min(1); @@ -652,7 +662,11 @@ export class KunaSessionClient implements AnalysisClient { : { strings: answer.stringSource }), }, }), - limitations: [...operationLimitations, ...answer.limitations], + limitations: [ + ...operationLimitations, + ...kunaVerificationLimitations(installation), + ...answer.limitations, + ], ...(answer.relationshipCoverage === undefined ? {} : { relationshipCoverage: answer.relationshipCoverage }), @@ -1469,6 +1483,7 @@ export class KunaSessionClient implements AnalysisClient { this.#deadline, this.#heapRequest, ), + ...kunaVerificationLimitations(installation), ...pseudocode.value.limitations, KUNA_RELATIONSHIP_LIMITATION, ...(relationships.ok diff --git a/tests/boundary/providers/kuna/kunaBatchDecompileMcp.test.ts b/tests/boundary/providers/kuna/kunaBatchDecompileMcp.test.ts index 4e8f191faf..59365f847e 100644 --- a/tests/boundary/providers/kuna/kunaBatchDecompileMcp.test.ts +++ b/tests/boundary/providers/kuna/kunaBatchDecompileMcp.test.ts @@ -89,6 +89,36 @@ test("keeps each Kuna no-pseudocode observation's limitations on its batch item" }) ).structuredContent, ); + // The provider's version policy travels with this observation, while its + // profile commits only analysis inputs. Composed and retained views must + // preserve the same warning for agents reading those results. + const verificationWarning = + "Kuna 1.833 is not REA's verified 1.989 release"; + expect(batch.limitations).toContainEqual( + expect.stringContaining(verificationWarning), + ); + expect(broken.analysis_profile?.parameters).not.toHaveProperty( + "verified_release", + ); + const summary = parseEvidence( + (await call("analyze_function", { procedure: "main", detail: "summary" })) + .structuredContent, + ); + const parent = summary.normalized_result as { parent_evidence_id: string }; + const page = parseEvidence( + ( + await call("inspect_analysis_view", { + source: { + kind: "retained-evidence", + evidence_id: parent.parent_evidence_id, + }, + view: { kind: "native", facet: "pseudocode" }, + }) + ).structuredContent, + ); + expect(page.limitations).toContainEqual( + expect.stringContaining(verificationWarning), + ); if (batchDecompile === undefined || getEvidenceBundle === undefined) throw new Error( "Missing batch_decompile or get_evidence_bundle contract", diff --git a/tests/boundary/providers/kuna/kunaDoctor.test.ts b/tests/boundary/providers/kuna/kunaDoctor.test.ts index 8c3a5f052a..6de81dfeba 100644 --- a/tests/boundary/providers/kuna/kunaDoctor.test.ts +++ b/tests/boundary/providers/kuna/kunaDoctor.test.ts @@ -34,7 +34,7 @@ it("reports an unconfigured Kuna as a missing optional engine", async () => { it("publishes the caller's explicit settings once the installation validates", async () => { const root = await createTestTempDirectory("rea-kuna-doctor-"); const kuna = await writeFakeKuna(root, { - version: { stdout: "kuna 1.988\n" }, + version: { stdout: "kuna 1.989\n" }, }); const cache = join(root, "cache"); @@ -48,7 +48,7 @@ it("publishes the caller's explicit settings once the installation validates", a expect(inspection).toMatchObject({ configured: true, available: true, - providerVersion: "1.988", + providerVersion: "1.989", registrationEnvironment: { REA_KUNA_COMMAND: kuna, REA_KUNA_CACHE_DIR: cache, @@ -59,10 +59,10 @@ it("publishes the caller's explicit settings once the installation validates", a name: "installation", ok: true, detail: expect.stringMatching( - /reports Kuna 1\.988 \(sha256 [a-f0-9]{64}\)\.$/u, + /reports Kuna 1\.989 \(sha256 [a-f0-9]{64}\)\.$/u, ), remediation: null, - details: { version: "1.988", verified_version: "1.988" }, + details: { version: "1.989", verified_version: "1.989" }, }, { name: "cache", @@ -88,7 +88,7 @@ it("flags an unverified release without failing the installation", async () => { "You may continue with the selected unverified release.", ), remediation: expect.stringContaining("setting REA_KUNA_COMMAND"), - details: { verified_version: "1.988" }, + details: { verified_version: "1.989" }, }); }); diff --git a/tests/boundary/providers/kuna/kunaInstallation.test.ts b/tests/boundary/providers/kuna/kunaInstallation.test.ts index 1f66253993..6082bf5063 100644 --- a/tests/boundary/providers/kuna/kunaInstallation.test.ts +++ b/tests/boundary/providers/kuna/kunaInstallation.test.ts @@ -22,7 +22,7 @@ const it = test.skipIf(process.platform === "win32"); it("identifies a caller-selected kuna by version and digest", async () => { const root = await createTestTempDirectory("rea-kuna-install-"); const kuna = await writeFakeKuna(root, { - version: { stdout: "kuna 1.988\n" }, + version: { stdout: "kuna 1.989\n" }, }); const link = join(root, "current-kuna"); await symlink(kuna, link); @@ -37,7 +37,7 @@ it("identifies a caller-selected kuna by version and digest", async () => { value: { configuration: { command: link, cacheRoot: join(root, "cache") }, executable: await realpath(kuna), - version: "1.988", + version: "1.989", verified: true, // A `#!` launcher reports its interpreter; ownership checks must not expect the path. script: true, @@ -104,14 +104,14 @@ fs.renameSync(process.argv[1] + ".new", process.argv[1]); it("accepts another release but reports it as unverified", async () => { const root = await createTestTempDirectory("rea-kuna-unverified-"); const kuna = await writeFakeKuna(root, { - version: { stdout: "kuna 1.986\n" }, + version: { stdout: "kuna 1.988\n" }, }); const result = await inspectKunaInstallation({ REA_KUNA_COMMAND: kuna }); expect(result).toMatchObject({ ok: true, - value: { version: "1.986", verified: false }, + value: { version: "1.988", verified: false }, }); }); diff --git a/tests/boundary/providers/kuna/kunaProvider.test.ts b/tests/boundary/providers/kuna/kunaProvider.test.ts index 8907678718..2b94145d65 100644 --- a/tests/boundary/providers/kuna/kunaProvider.test.ts +++ b/tests/boundary/providers/kuna/kunaProvider.test.ts @@ -182,22 +182,42 @@ it("waits for an aborted pre-session hash before retrying its late close owner", it("commits the observed Kuna release and digest to the analysis profile", async () => { const { profile, kuna } = await session("rea-kuna-profile-", { - version: { stdout: "kuna 1.988\n" }, + version: { stdout: "kuna 1.989\n" }, }); expect(profile).toMatchObject({ - provider: { id: "kuna", name: "Kuna", version: "1.988" }, + provider: { id: "kuna", name: "Kuna", version: "1.989" }, parameters: { - kuna_version: "1.988", + kuna_version: "1.989", kuna_sha256: expect.stringMatching(/^[0-9a-f]{64}$/u), kuna_command: kuna, - verified_release: true, jobs: 1, cache_policy: "live", }, }); }); +it("reports an unverified release on observations without binding policy into the profile", async () => { + const { profile, open } = await session("rea-kuna-policy-", { + version: { stdout: "kuna 1.988\n" }, + }); + expect(profile.parameters).not.toHaveProperty("verified_release"); + expect(profile.parameters).toMatchObject({ + kuna_version: "1.988", + kuna_sha256: expect.stringMatching(/^[0-9a-f]{64}$/u), + }); + const client = open(); + const inventory = await run(client, "list_procedures"); + const warning = "Kuna 1.988 is not REA's verified 1.989 release"; + expect(inventory.limitations.join("\n")).toContain(warning); + const dossier = await run(client, "analyze_function", { procedure: "main" }); + expect(dossier.limitations.join("\n")).toContain(warning); + expect( + (dossier.result as { limitations: string[] }).limitations.join("\n"), + ).toContain(warning); + await client.close(); +}); + it("lists the selected document without starting target analysis", async () => { const { kuna, target, cacheRoot, open } = await session( "rea-kuna-document-metadata-",