mirror of
https://github.com/morluto/rea
synced 2026-10-28 15:07:26 -04:00
ec271121e4
Normalize path facts once at the owning filesystem boundary, preserve caller spelling and unavailable inventory members, and retain typed read failures. Record exclusion entry kinds so only proven directories can establish descendant symlink targets; omitted legacy kinds stay unknown. Reviewed fixes include #1969, #2642, #2759, and #2994. Validation: 26 reference graph source tests passed; prior artifact checks passed 275 tests with one Windows-only skip. Updated compiled import workflows are included in final validation after regeneration.
210 lines
6.6 KiB
TypeScript
210 lines
6.6 KiB
TypeScript
import { z } from "zod";
|
|
import { localPathStringSchema } from "../localPath.js";
|
|
|
|
const target = {
|
|
path: localPathStringSchema.describe(
|
|
"Local APK path; the target is never executed",
|
|
),
|
|
};
|
|
const classTarget = {
|
|
...target,
|
|
class_name: z.string().min(1).describe("Exact fully qualified class name"),
|
|
};
|
|
|
|
/** Caller intent for static Android inspection, independent of its engine. */
|
|
export const androidInputSchemas = {
|
|
inspect_android_package: z.strictObject(target),
|
|
search_android_classes: z.strictObject({
|
|
...target,
|
|
query: z
|
|
.string()
|
|
.describe("Case-sensitive class-name substring; empty lists all classes"),
|
|
}),
|
|
inspect_android_class: z.strictObject(classTarget),
|
|
inspect_android_method: z.strictObject({
|
|
...classTarget,
|
|
method_name: z.string().min(1),
|
|
overload_index: z
|
|
.number()
|
|
.int()
|
|
.nonnegative()
|
|
.optional()
|
|
.describe(
|
|
"Explicit index among same-name methods; required when ambiguous",
|
|
),
|
|
}),
|
|
trace_android_references: z.strictObject({
|
|
...classTarget,
|
|
method_name: z
|
|
.string()
|
|
.min(1)
|
|
.optional()
|
|
.describe(
|
|
"Omit for class references; select ambiguous methods with overload_index",
|
|
),
|
|
overload_index: z
|
|
.number()
|
|
.int()
|
|
.nonnegative()
|
|
.optional()
|
|
.describe(
|
|
"Index among same-name methods from inspect_android_class; requires method_name",
|
|
),
|
|
}),
|
|
} as const;
|
|
|
|
/** Optional retained delivery is separate from the engine's APK request. */
|
|
export const androidPackageInspectionInputSchema =
|
|
androidInputSchemas.inspect_android_package.extend({
|
|
detail: z.enum(["complete", "summary"]).optional(),
|
|
});
|
|
|
|
/** Supported static Android operations. */
|
|
export type AndroidOperation = keyof typeof androidInputSchemas;
|
|
/** Validated Android requests carry their selected operation. */
|
|
export type AndroidRequest = {
|
|
[Name in AndroidOperation]: {
|
|
operation: Name;
|
|
input: z.infer<(typeof androidInputSchemas)[Name]>;
|
|
};
|
|
}[AndroidOperation];
|
|
|
|
/** Validate operation and input together so their types remain correlated. */
|
|
export const androidRequestSchema = z.discriminatedUnion("operation", [
|
|
z.strictObject({
|
|
operation: z.literal("inspect_android_package"),
|
|
input: androidInputSchemas.inspect_android_package,
|
|
}),
|
|
z.strictObject({
|
|
operation: z.literal("search_android_classes"),
|
|
input: androidInputSchemas.search_android_classes,
|
|
}),
|
|
z.strictObject({
|
|
operation: z.literal("inspect_android_class"),
|
|
input: androidInputSchemas.inspect_android_class,
|
|
}),
|
|
z.strictObject({
|
|
operation: z.literal("inspect_android_method"),
|
|
input: androidInputSchemas.inspect_android_method,
|
|
}),
|
|
z.strictObject({
|
|
operation: z.literal("trace_android_references"),
|
|
input: androidInputSchemas.trace_android_references,
|
|
}),
|
|
]);
|
|
|
|
const textCoverage = z.strictObject({
|
|
status: z.enum(["complete", "partial"]),
|
|
text: z.string(),
|
|
reported_total_bytes: z.number().int().nonnegative().nullable(),
|
|
});
|
|
/** A DEX prototype; hex parentheses also keep the advertised class valid in v mode. */
|
|
export const dexDescriptorSchema = z
|
|
.string()
|
|
.regex(
|
|
/^\((?:\[*(?:[ZBSCIJFD]|L[^;\x28\x29]+;))*\)(?:V|\[*(?:[ZBSCIJFD]|L[^;\x28\x29]+;))$/u,
|
|
);
|
|
|
|
const method = z.strictObject({
|
|
name: z.string(),
|
|
reported_signature: z.string(),
|
|
overload_index: z.number().int().nonnegative(),
|
|
is_constructor: z.boolean(),
|
|
dex_descriptor: dexDescriptorSchema,
|
|
});
|
|
const engine = z.strictObject({
|
|
name: z.string().min(1),
|
|
version: z.string().nullable(),
|
|
artifact_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
source_revision: z.string().nullable(),
|
|
worker_count: z.number().int().positive(),
|
|
heap_limit_mib: z.number().int().positive().nullable(),
|
|
});
|
|
const common = { engine };
|
|
|
|
/** Portable observations with explicit decompiler and identity limitations. */
|
|
export const androidResultSchemas = {
|
|
inspect_android_package: z.strictObject({
|
|
...common,
|
|
package_name: z.string().nullable(),
|
|
version_name: z.string().nullable(),
|
|
version_code: z.string().nullable(),
|
|
min_sdk: z.string().nullable(),
|
|
target_sdk: z.string().nullable(),
|
|
permissions: z.array(z.string()),
|
|
manifest: textCoverage,
|
|
class_count: z.number().int().nonnegative(),
|
|
resource_count: z.number().int().nonnegative(),
|
|
signature_verification: z.literal("not_performed"),
|
|
}),
|
|
search_android_classes: z.strictObject({
|
|
...common,
|
|
query: z.string(),
|
|
total_classes: z.number().int().nonnegative(),
|
|
matches: z.array(z.string()),
|
|
duplicate_names: z.array(
|
|
z.strictObject({
|
|
name: z.string(),
|
|
occurrences: z.number().int().min(2),
|
|
}),
|
|
),
|
|
coverage: z.literal("complete"),
|
|
}),
|
|
inspect_android_class: z.strictObject({
|
|
...common,
|
|
class_name: z.string(),
|
|
methods: z.array(method),
|
|
fields: z.array(
|
|
z.strictObject({ name: z.string(), reported_type: z.string() }),
|
|
),
|
|
inner_classes: z.array(z.string()),
|
|
}),
|
|
inspect_android_method: z.strictObject({
|
|
...common,
|
|
class_name: z.string(),
|
|
method: method,
|
|
overload_count: z.number().int().positive(),
|
|
representation: z.enum(["java", "smali"]),
|
|
body_status: z.enum(["available", "not_available"]),
|
|
fell_back: z.boolean(),
|
|
markers: z.array(z.string()),
|
|
source: textCoverage,
|
|
}),
|
|
trace_android_references: z.strictObject({
|
|
...common,
|
|
class_name: z.string(),
|
|
method_name: z.string().nullable(),
|
|
overload_index: z.number().int().nonnegative().nullable(),
|
|
reported_signature: z.string().nullable(),
|
|
dex_descriptor: dexDescriptorSchema.nullable(),
|
|
total: z.number().int().nonnegative(),
|
|
references: z.array(
|
|
z.strictObject({
|
|
kind: z.string(),
|
|
name: z.string(),
|
|
reported_full_name: z.string(),
|
|
containing_class: z.string(),
|
|
top_class: z.string(),
|
|
source_line: z.null(),
|
|
dex_offset: z.null(),
|
|
/** false means transport-bounded instruction evidence; null is unknown. */
|
|
direct_invokes_exhaustive: z.boolean().nullable(),
|
|
/** Raw direct invoke instructions inside this using node, when available. */
|
|
direct_invokes: z
|
|
.array(
|
|
z.strictObject({
|
|
dex_entry: z.string().min(1),
|
|
source_raw_method_id: z.string().min(1),
|
|
dex_code_unit_offset: z.number().int().nonnegative(),
|
|
dex_method_index: z.number().int().nonnegative(),
|
|
opcode: z.string().regex(/^INVOKE_/u),
|
|
}),
|
|
)
|
|
.nullable(),
|
|
}),
|
|
),
|
|
coverage: z.literal("complete"),
|
|
reference_unit: z.literal("using_node"),
|
|
}),
|
|
} as const;
|