mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(kuna)!: separate verification policy from analysis identity (#8699)
* feat(kuna): verify release 1.989 * fix(kuna)!: separate verification policy from analysis identity * docs(kuna): show historical evidence recovery * test(kuna): cover verification warnings through MCP
This commit is contained in:
1 parent
3c05b4d3fe
commit
75c3cab540
10 files changed
+106
-19
No files matched your search
@@ -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. |
|
||||
|
||||
+19
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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" },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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 },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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-",
|
||||
|
||||
Reference in new issue
Block a user