* fix(native): refuse inspect_signature on non-Mach-O targets This provider's signature inspection covers Mach-O code signatures only. On a xar pkg, codesign's "code object is not signed" verdict cannot see the archive-level signature layer, so a signed or invalidly-signed package was reported as signed:false with "Artifact is not signed." Match the existing inspect_macho/list_architectures gate and report capability_unavailable instead of an unsigned claim for a layer that was never evaluated. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(native): scope the signature-gate comment to this provider Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(native): align signature capability discovery with Mach-O support Bind inspect_signature availability to Mach-O targets in capability discovery and align the public tool description. Source inspection and git diff --check; tests/builds/typechecks/CI reruns skipped at explicit user request. --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
135 lines
5.8 KiB
TypeScript
135 lines
5.8 KiB
TypeScript
import { z } from "zod";
|
|
import { nativeCallObservationInputSchema } from "../../domain/native/nativeCallObservation.js";
|
|
import {
|
|
nativeUiObservationInputSchema,
|
|
nativeUiScenarioInputSchema,
|
|
} from "../../domain/native/nativeUiObservation.js";
|
|
|
|
import type { ToolContract } from "../toolContractTypes.js";
|
|
import { nativeOutputSchemas } from "../toolOutputSchemaGroups.js";
|
|
import { jsonValueSchema } from "../../domain/jsonValue.js";
|
|
import { localPathStringSchema } from "../../domain/localPath.js";
|
|
import { toolContractMetadata } from "../toolEffects.js";
|
|
import { requireOutputSchema } from "../toolOutputSchemaPrimitives.js";
|
|
import { analysisDetailSchema } from "../analysisDetail.js";
|
|
|
|
const examples: Readonly<Record<string, Readonly<Record<string, unknown>>>> = {
|
|
inspect_macho: {},
|
|
inspect_signature: {},
|
|
inspect_plist: { path: "/Applications/Example.app/Contents/Info.plist" },
|
|
list_architectures: {},
|
|
observe_native_ui: { pid: 123, window_id: 456 },
|
|
capture_native_ui_scenario: {
|
|
pid: 123,
|
|
window_id: 456,
|
|
steps: [{ kind: "wait", milliseconds: 100 }],
|
|
},
|
|
demangle_swift: { symbols: ["$s4Test3fooyyF"] },
|
|
observe_native_calls: {
|
|
breakpoints: [
|
|
{
|
|
kind: "objc-method",
|
|
class_name: "NSURLSession",
|
|
selector: "dataTaskWithRequest:completionHandler:",
|
|
},
|
|
{ kind: "function", name: "open", module: "libsystem_kernel.dylib" },
|
|
],
|
|
arguments: ["--help"],
|
|
duration_ms: 5000,
|
|
backtrace_frames: 4,
|
|
},
|
|
};
|
|
|
|
const native = <Name extends string, Schema extends z.ZodObject>(
|
|
name: Name,
|
|
description: string,
|
|
inputSchema: Schema,
|
|
) => {
|
|
const outputSchema = requireOutputSchema(nativeOutputSchemas, name);
|
|
const strictInputSchema = inputSchema.strict();
|
|
return {
|
|
name,
|
|
...toolContractMetadata(name),
|
|
description,
|
|
kind: "native-provider",
|
|
inputSchema: strictInputSchema,
|
|
outputSchema,
|
|
examples: [
|
|
{
|
|
title: `Example ${name.replaceAll("_", " ")} request`,
|
|
input: z
|
|
.record(z.string(), jsonValueSchema)
|
|
.parse(examples[name] ?? {}),
|
|
},
|
|
],
|
|
} satisfies ToolContract<Name, typeof strictInputSchema, typeof outputSchema>;
|
|
};
|
|
|
|
/** Ordered single-line symbols; `swift-demangle` ends each result with LF. */
|
|
export const swiftSymbolsSchema = z
|
|
.array(
|
|
z
|
|
.string()
|
|
.min(1)
|
|
.regex(/^[^\n]*$/u, "Each Swift symbol must be one line.")
|
|
.regex(/^[^\x00]*$/u, "Swift symbols cannot contain NUL."),
|
|
)
|
|
.min(1);
|
|
|
|
/** Provider-neutral semantic operations backed initially by macOS utilities. */
|
|
export const NATIVE_TOOL_CONTRACTS = [
|
|
native(
|
|
"observe_native_ui",
|
|
"Observe an already-running native app by PID and window ID, bound to the active Mach-O target. Captures a bounded accessibility tree and selected-window screenshot; OS permission denial fails without broad capture or target launch.",
|
|
nativeUiObservationInputSchema,
|
|
),
|
|
native(
|
|
"capture_native_ui_scenario",
|
|
"Run selected-element press, increment/decrement scroll, AXValue text entry and bounded wait steps in one exact native app window. Returns ordered before/after captures and action/capture failures. Actions can change app data, network activity and persistent state; application state is left as-is and restoration is not attempted.",
|
|
nativeUiScenarioInputSchema,
|
|
),
|
|
native(
|
|
"observe_native_calls",
|
|
"Launch the active Mach-O as an owned process under LLDB and record each entry into caller-selected functions or Objective-C methods: thread, module, symbol, load and file address, raw argument registers, selector, receiver class and optional caller frames. Every hit auto-continues. No expression is evaluated and no other process is attached. The process is killed when max_events or duration_ms is reached; exit status, captured stdout/stderr, unresolved breakpoints and limitations are returned inline. The process runs with the current user's permissions and may change files, show UI or use the network. A hardened-runtime target needs the get-task-allow entitlement.",
|
|
nativeCallObservationInputSchema,
|
|
),
|
|
native(
|
|
"inspect_macho",
|
|
"Inspect Mach-O slices, load commands, imports, exports, dependencies, build metadata, segments, sections, permissions, and exact command provenance without launching Hopper. Large frameworks return megabytes of symbols; detail summary retains the complete Evidence and returns slice identity with per-collection counts, then page rows with inspect_analysis_view kind macho.",
|
|
z.object({
|
|
detail: analysisDetailSchema
|
|
.unwrap()
|
|
.optional()
|
|
.describe(
|
|
"Omitted or complete returns the complete inspection Evidence. summary retains that complete Evidence in this session and returns its inspect_analysis_view summary; use normalized_result.parent_evidence_id for macho pages.",
|
|
),
|
|
}),
|
|
),
|
|
native(
|
|
"inspect_signature",
|
|
"Inspect the active Mach-O executable's code-signing identity, hashes, authorities, requirements, entitlements, hardened-runtime state, and exact command provenance.",
|
|
z.object({}),
|
|
),
|
|
native(
|
|
"inspect_plist",
|
|
"Parse Info.plist from the active artifact by default, or pass any local plist path. Returns normalized JSON rather than plutil text.",
|
|
z.object({
|
|
path: localPathStringSchema.optional(),
|
|
}),
|
|
),
|
|
native(
|
|
"list_architectures",
|
|
"List thin or universal Mach-O slices with offsets, sizes, alignment, explicit coverage, and native-tool provenance.",
|
|
z.object({}),
|
|
),
|
|
native(
|
|
"demangle_swift",
|
|
"Demangle an ordered list of single-line Swift symbols without requiring Hopper. Each input returns demangled, unchanged, or invalid status.",
|
|
z.object({
|
|
symbols: swiftSymbolsSchema,
|
|
}),
|
|
),
|
|
] as const satisfies readonly ToolContract[];
|
|
|
|
export type NativeToolName = (typeof NATIVE_TOOL_CONTRACTS)[number]["name"];
|