mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
bd4f5e9c19
Retain bounded FAT and offset-tagged PE observations through archive hashing, classification, and content nodes. Preserve PyInstaller member paths according to the observed carrier dialect and share APK v1 signing-name semantics. Read SQLite's implicit sqlite_schema table without inventing a stored schema declaration. Distinguish unsupported ELF numbering and decoded-name admission from malformed data and fixed scan allowances. Preserve Sigrok basenames and classify matching explicit input-format refusals at their owning boundary. Validation: 554 distinct tests passed, 3 skipped across serial focused runs; build, typecheck/lint, docs, formatting, and discovery (2141 files) passed. Sigrok command-boundary simulators do not establish real-tool decoding. Small repeated public PyInstaller CLI runs had equivalent inventory facts; warm median elapsed 0.5431s before / 0.5426s after, with no speed claim. Fixes #6733 Fixes #6741 Fixes #6722 Fixes #6732 Fixes #6736 Fixes #6734 Fixes #6764 Fixes #6766 Refs #6739
140 lines
5.2 KiB
TypeScript
140 lines
5.2 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { TextReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js";
|
|
import { expect } from "vitest";
|
|
|
|
import { androidApplicationProjectionResultSchema } from "../../../src/domain/android/androidApplication.js";
|
|
import { parseEvidence } from "../../../src/domain/evidence.js";
|
|
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
cliTest(
|
|
"Android runtime hints follow classified bytecode through CLI and MCP",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-android-runtime-hints-");
|
|
const environment = { HOME: root, USERPROFILE: root };
|
|
const { call } = await connectLocalToolsMcp(environment);
|
|
for (const prefix of ["", "named"]) {
|
|
const writer = new ZipWriter(new Uint8ArrayWriter());
|
|
await writer.add("AndroidManifest.xml", new TextReader("fixture"));
|
|
for (const extension of ["dex", "class"])
|
|
await writer.add(
|
|
`assets/${prefix}.${extension}`,
|
|
new TextReader("ordinary text"),
|
|
);
|
|
const path = join(root, `${prefix || "dot-names"}.apk`);
|
|
await writeFile(path, await writer.close());
|
|
const inspected = await cli.run({
|
|
arguments: ["inspect-artifact", path, "--json"],
|
|
environment,
|
|
});
|
|
expect(inspected.exitCode, inspected.stderr).toBe(0);
|
|
const source = parseEvidence(inspected.json);
|
|
const input = { inventory_evidence: [source] };
|
|
const projected = await cli.run({
|
|
arguments: [
|
|
"project-android-application-graph",
|
|
JSON.stringify(input),
|
|
"--json",
|
|
],
|
|
environment,
|
|
});
|
|
expect(projected.exitCode, projected.stderr).toBe(0);
|
|
const evidence = parseEvidence(projected.json);
|
|
const graph = androidApplicationProjectionResultSchema.parse(
|
|
evidence.normalized_result,
|
|
);
|
|
expect(graph.components.dex.map(({ path }) => path)).toEqual(
|
|
prefix ? ["assets/named.dex"] : [],
|
|
);
|
|
expect(graph.components.jvm_classes.map(({ path }) => path)).toEqual(
|
|
prefix ? ["assets/named.class"] : [],
|
|
);
|
|
expect(graph.runtime_families).toEqual(
|
|
prefix ? ["dalvik-art", "java-kotlin"] : [],
|
|
);
|
|
const mcp = await call("project_android_application_graph", input);
|
|
expect(mcp.isError).not.toBe(true);
|
|
expect(parseEvidence(mcp.structuredContent)).toEqual(evidence);
|
|
}
|
|
},
|
|
);
|
|
|
|
cliTest(
|
|
"projects nested APK v1 filename candidates as inventory metadata through CLI and MCP",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory(
|
|
"rea-android-signing-candidates-",
|
|
);
|
|
const environment = { HOME: root, USERPROFILE: root };
|
|
const { call } = await connectLocalToolsMcp(environment);
|
|
const entries = [
|
|
"META-INF/CERT.SF",
|
|
"META-INF/CERT.EC",
|
|
"META-INF/keys/NESTED.SF",
|
|
"META-INF/keys/NESTED.EC",
|
|
"META-INF/keys/readme.txt",
|
|
"assets/not-signing.SF",
|
|
];
|
|
const writer = new ZipWriter(new Uint8ArrayWriter());
|
|
await writer.add("AndroidManifest.xml", new TextReader("fixture"));
|
|
for (const entry of entries)
|
|
await writer.add(entry, new TextReader(`inventory:${entry}`));
|
|
const path = join(root, "signing-candidates.apk");
|
|
await writeFile(path, await writer.close());
|
|
|
|
const inspected = await cli.run({
|
|
arguments: ["inspect-artifact", path, "--json"],
|
|
environment,
|
|
});
|
|
expect(inspected.exitCode, inspected.stderr).toBe(0);
|
|
const input = { inventory_evidence: [parseEvidence(inspected.json)] };
|
|
const projected = await cli.run({
|
|
arguments: [
|
|
"project-android-application-graph",
|
|
JSON.stringify(input),
|
|
"--json",
|
|
],
|
|
environment,
|
|
});
|
|
expect(projected.exitCode, projected.stderr).toBe(0);
|
|
const cliEvidence = parseEvidence(projected.json);
|
|
const cliGraph = androidApplicationProjectionResultSchema.parse(
|
|
cliEvidence.normalized_result,
|
|
);
|
|
const mcp = await call("project_android_application_graph", input);
|
|
expect(mcp.isError).not.toBe(true);
|
|
const mcpEvidence = parseEvidence(mcp.structuredContent);
|
|
const mcpGraph = androidApplicationProjectionResultSchema.parse(
|
|
mcpEvidence.normalized_result,
|
|
);
|
|
|
|
const expectedPaths = [
|
|
"META-INF/CERT.EC",
|
|
"META-INF/CERT.SF",
|
|
"META-INF/keys/NESTED.EC",
|
|
"META-INF/keys/NESTED.SF",
|
|
];
|
|
expect(cliGraph.components.signing.map(({ path }) => path).sort()).toEqual(
|
|
expectedPaths,
|
|
);
|
|
expect(mcpGraph.components.signing).toEqual(cliGraph.components.signing);
|
|
for (const component of cliGraph.components.signing)
|
|
expect(component.sha256).toBe(
|
|
createHash("sha256")
|
|
.update(`inventory:${component.path}`)
|
|
.digest("hex"),
|
|
);
|
|
expect(JSON.stringify(cliGraph.components.signing)).not.toContain(
|
|
"readme.txt",
|
|
);
|
|
expect(JSON.stringify(cliGraph.components.signing)).not.toContain(
|
|
"not-signing.SF",
|
|
);
|
|
expect(mcpEvidence).toEqual(cliEvidence);
|
|
},
|
|
);
|