rea-agents 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -21
- package/bridge/hopper_bridge.py +156 -14
- package/dist/application/AnalysisProvider.js +12 -1
- package/dist/application/ArtifactExtraction.js +166 -0
- package/dist/application/ArtifactGraphConstruction.js +257 -0
- package/dist/application/ArtifactInventory.js +253 -0
- package/dist/application/BinarySession.js +174 -14
- package/dist/application/CompositeProvider.js +73 -0
- package/dist/application/DirectAnalysis.js +44 -3
- package/dist/application/Doctor.js +35 -6
- package/dist/application/EnhancedTools.js +10 -7
- package/dist/application/EvidenceBundleCommands.js +51 -0
- package/dist/application/EvidenceBundleFiles.js +127 -0
- package/dist/application/EvidenceLedger.js +225 -18
- package/dist/application/FilesystemSnapshot.js +124 -0
- package/dist/application/LinuxHopper.js +186 -0
- package/dist/application/LoopbackReplay.js +195 -37
- package/dist/application/ProcessHarness.js +200 -191
- package/dist/application/ProcessNormalization.js +44 -0
- package/dist/application/ProcessOwnership.js +105 -0
- package/dist/application/ProcessSampling.js +284 -0
- package/dist/application/RealHopperAssertions.js +116 -0
- package/dist/application/ReferenceSourceImport.js +182 -0
- package/dist/application/ReferenceSourceImportEntries.js +122 -0
- package/dist/application/ReferenceSourceImportPolicy.js +73 -0
- package/dist/application/ReferenceSourceImportTypes.js +18 -0
- package/dist/application/ReferenceSourceVcsAdapter.js +34 -0
- package/dist/application/Setup.js +192 -41
- package/dist/application/Uninstall.js +130 -0
- package/dist/application/runtime.js +8 -1
- package/dist/artifacts/ArtifactPaths.js +51 -0
- package/dist/artifacts/ArtifactProvider.js +130 -0
- package/dist/artifacts/ArtifactReader.js +9 -0
- package/dist/artifacts/AsarArtifactReader.js +62 -0
- package/dist/artifacts/DirectoryArtifactReader.js +107 -0
- package/dist/artifacts/MachOSliceArtifactReader.js +66 -0
- package/dist/artifacts/SafeOutputTree.js +199 -0
- package/dist/artifacts/StreamBytes.js +10 -0
- package/dist/artifacts/ZipArtifactReader.js +109 -0
- package/dist/cli.js +229 -21
- package/dist/cliEvidenceCommands.js +68 -0
- package/dist/cliLogging.js +21 -0
- package/dist/config.js +35 -1
- package/dist/contracts/artifactComparisonExample.js +95 -0
- package/dist/contracts/artifactToolContracts.js +84 -0
- package/dist/contracts/enhancedInputs.js +4 -0
- package/dist/contracts/functionComparisonExample.js +57 -0
- package/dist/contracts/investigationExamples.js +118 -0
- package/dist/contracts/nativeToolContracts.js +52 -0
- package/dist/contracts/processCaptureExample.js +25 -0
- package/dist/contracts/toolContractExamples.js +69 -0
- package/dist/contracts/toolContracts.js +99 -36
- package/dist/contracts/toolOutputSchemas.js +156 -82
- package/dist/contracts/unknownContractExamples.js +33 -0
- package/dist/domain/artifactComparison.js +273 -0
- package/dist/domain/artifactGraph.js +194 -0
- package/dist/domain/artifactInventoryEvidence.js +150 -0
- package/dist/domain/binaryTarget.js +55 -0
- package/dist/domain/bundleComparison.js +266 -0
- package/dist/domain/callPath.js +346 -0
- package/dist/domain/changedBehavior.js +294 -0
- package/dist/domain/errors.js +199 -5
- package/dist/domain/evidence.js +41 -10
- package/dist/domain/evidenceBundle.js +187 -6
- package/dist/domain/functionComparison.js +201 -0
- package/dist/domain/functionComparisonNormalization.js +112 -0
- package/dist/domain/functionComparisonResults.js +54 -0
- package/dist/domain/functionComparisonSchemas.js +82 -0
- package/dist/domain/functionDossierEvidence.js +171 -0
- package/dist/domain/hopperValues.js +35 -8
- package/dist/domain/nativeInspection.js +142 -0
- package/dist/domain/processCapture.js +152 -54
- package/dist/domain/processComparison.js +106 -0
- package/dist/domain/reconstructionUnknowns.js +90 -0
- package/dist/domain/reconstructionVerification.js +285 -0
- package/dist/domain/reconstructionVerificationSchemas.js +126 -0
- package/dist/domain/referenceSourceClassification.js +496 -0
- package/dist/domain/referenceSourceGraph.js +376 -0
- package/dist/domain/referenceSourceImportParsing.js +235 -0
- package/dist/domain/referenceSourcePolicy.js +1 -0
- package/dist/domain/residualUnknown.js +239 -0
- package/dist/domain/staticRuntimeCorrelation.js +375 -0
- package/dist/hopper/BridgeLauncher.js +39 -3
- package/dist/hopper/HopperClient.js +14 -5
- package/dist/hopper/HopperProvider.js +57 -22
- package/dist/hopper/protocol.js +13 -2
- package/dist/identity.js +1 -0
- package/dist/main.js +5 -1
- package/dist/native/CommandRunner.js +156 -0
- package/dist/native/NativeMacOSProvider.js +306 -0
- package/dist/native/NativeMachoInspection.js +135 -0
- package/dist/native/parsers/codesign.js +55 -0
- package/dist/native/parsers/demangle.js +26 -0
- package/dist/native/parsers/dyldInfo.js +25 -0
- package/dist/native/parsers/lipo.js +67 -0
- package/dist/native/parsers/otool.js +193 -0
- package/dist/native/parsers/plist.js +23 -0
- package/dist/reference/ReferenceSourceReader.js +73 -0
- package/dist/reference/ReferenceSourceReaderEntries.js +206 -0
- package/dist/reference/ReferenceSourceReaderErrors.js +19 -0
- package/dist/reference/ReferenceSourceReaderFile.js +119 -0
- package/dist/reference/ReferenceSourceReaderPaths.js +23 -0
- package/dist/reference/ReferenceSourceReaderTypes.js +2 -0
- package/dist/reference/ReferenceSourceReaderValidate.js +71 -0
- package/dist/server/createServer.js +31 -5
- package/dist/server/recordDerivedEvidence.js +10 -0
- package/dist/server/registerArtifactComparisonTool.js +62 -0
- package/dist/server/registerArtifactTools.js +6 -0
- package/dist/server/registerBundleComparisonTool.js +47 -0
- package/dist/server/registerEnhancedTools.js +64 -12
- package/dist/server/registerEvidenceTools.js +36 -0
- package/dist/server/registerFunctionComparisonTool.js +68 -0
- package/dist/server/registerInvestigationTools.js +224 -0
- package/dist/server/registerNativeTools.js +6 -0
- package/dist/server/registerOfficialTools.js +47 -14
- package/dist/server/registerProcessComparisonTool.js +106 -0
- package/dist/server/registerSessionTools.js +179 -70
- package/dist/server/sessionEvidence.js +28 -0
- package/dist/server/sessionToolPolicies.js +64 -0
- package/dist/server/toolRegistrationOptions.js +7 -0
- package/dist/server/toolResult.js +8 -5
- package/install.sh +198 -0
- package/package.json +18 -1
- package/scripts/rea.mjs +5 -1
- package/skills/rea-analysis/SKILL.md +77 -2
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Log one CLI command with duration and a stable success or failure status. */
|
|
2
|
+
export const logCliCommand = async (logger, command, execute) => {
|
|
3
|
+
const startedAt = performance.now();
|
|
4
|
+
try {
|
|
5
|
+
const value = await execute();
|
|
6
|
+
logger.info({
|
|
7
|
+
command,
|
|
8
|
+
durationMs: Math.round((performance.now() - startedAt) * 100) / 100,
|
|
9
|
+
status: "ok",
|
|
10
|
+
}, "CLI command completed");
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
catch (cause) {
|
|
14
|
+
logger.error({
|
|
15
|
+
command,
|
|
16
|
+
durationMs: Math.round((performance.now() - startedAt) * 100) / 100,
|
|
17
|
+
status: "error",
|
|
18
|
+
}, "CLI command failed");
|
|
19
|
+
throw cause;
|
|
20
|
+
}
|
|
21
|
+
};
|
package/dist/config.js
CHANGED
|
@@ -2,6 +2,9 @@ import { z } from "zod";
|
|
|
2
2
|
import { ConfigurationError } from "./domain/errors.js";
|
|
3
3
|
import { err, ok } from "./domain/result.js";
|
|
4
4
|
const DEFAULT_HOPPER_LAUNCHER_PATH = "/Applications/Hopper Disassembler.app/Contents/MacOS/hopper";
|
|
5
|
+
const defaultHopperLauncherPath = () => process.platform === "linux"
|
|
6
|
+
? "/opt/hopper/bin/Hopper"
|
|
7
|
+
: DEFAULT_HOPPER_LAUNCHER_PATH;
|
|
5
8
|
const environmentSchema = z.object({
|
|
6
9
|
HOPPER_LAUNCHER_PATH: z.string().min(1).optional(),
|
|
7
10
|
HOPPER_TARGET_PATH: z.string().min(1).optional(),
|
|
@@ -11,9 +14,15 @@ const environmentSchema = z.object({
|
|
|
11
14
|
.enum(["trace", "debug", "info", "warn", "error", "fatal", "silent"])
|
|
12
15
|
.default("info"),
|
|
13
16
|
REA_PROCESS_CAPTURE_ENABLED: z.enum(["true", "false"]).default("false"),
|
|
17
|
+
REA_PROCESS_ALLOW_EXTERNAL_NETWORK: z
|
|
18
|
+
.enum(["true", "false"])
|
|
19
|
+
.default("false"),
|
|
14
20
|
REA_PROCESS_EXECUTABLE_ROOTS_JSON: z.string().default("[]"),
|
|
15
21
|
REA_PROCESS_WORKING_ROOTS_JSON: z.string().default("[]"),
|
|
16
22
|
REA_PROCESS_ALLOWED_ENV_JSON: z.string().default("[]"),
|
|
23
|
+
REA_EVIDENCE_ROOTS_JSON: z.string().default("[]"),
|
|
24
|
+
REA_REFERENCE_ROOTS_JSON: z.string().default("[]"),
|
|
25
|
+
REA_REFERENCE_SECRET_PATTERNS_JSON: z.string().default("[]"),
|
|
17
26
|
});
|
|
18
27
|
const parseStringArray = (encoded, name) => {
|
|
19
28
|
try {
|
|
@@ -69,9 +78,18 @@ export const parseConfig = (environment) => {
|
|
|
69
78
|
const allowedEnvironment = parseStringArray(parsedEnvironment.data.REA_PROCESS_ALLOWED_ENV_JSON, "REA_PROCESS_ALLOWED_ENV_JSON");
|
|
70
79
|
if (!allowedEnvironment.ok)
|
|
71
80
|
return allowedEnvironment;
|
|
81
|
+
const evidenceRoots = parseStringArray(parsedEnvironment.data.REA_EVIDENCE_ROOTS_JSON, "REA_EVIDENCE_ROOTS_JSON");
|
|
82
|
+
if (!evidenceRoots.ok)
|
|
83
|
+
return evidenceRoots;
|
|
84
|
+
const referenceRoots = parseStringArray(parsedEnvironment.data.REA_REFERENCE_ROOTS_JSON, "REA_REFERENCE_ROOTS_JSON");
|
|
85
|
+
if (!referenceRoots.ok)
|
|
86
|
+
return referenceRoots;
|
|
87
|
+
const secretPatterns = parseStringArray(parsedEnvironment.data.REA_REFERENCE_SECRET_PATTERNS_JSON, "REA_REFERENCE_SECRET_PATTERNS_JSON");
|
|
88
|
+
if (!secretPatterns.ok)
|
|
89
|
+
return secretPatterns;
|
|
72
90
|
return ok({
|
|
73
91
|
hopperLauncherPath: parsedEnvironment.data.HOPPER_LAUNCHER_PATH ??
|
|
74
|
-
|
|
92
|
+
defaultHopperLauncherPath(),
|
|
75
93
|
hopperTargetPath: parsedEnvironment.data.HOPPER_TARGET_PATH,
|
|
76
94
|
hopperTargetKind: parsedEnvironment.data.HOPPER_TARGET_KIND,
|
|
77
95
|
hopperLoaderArgs: parsedArgs.data,
|
|
@@ -81,6 +99,22 @@ export const parseConfig = (environment) => {
|
|
|
81
99
|
executableRoots: executableRoots.value,
|
|
82
100
|
workingRoots: workingRoots.value,
|
|
83
101
|
allowedEnvironment: allowedEnvironment.value,
|
|
102
|
+
allowExternalNetwork: parsedEnvironment.data.REA_PROCESS_ALLOW_EXTERNAL_NETWORK === "true",
|
|
103
|
+
},
|
|
104
|
+
evidenceFilePolicy: {
|
|
105
|
+
roots: evidenceRoots.value,
|
|
106
|
+
maxBytes: 64 * 1024 * 1024,
|
|
107
|
+
maxDepth: 64,
|
|
108
|
+
maxStringLength: 1024 * 1024,
|
|
109
|
+
maxNodes: 1_000_000,
|
|
110
|
+
},
|
|
111
|
+
referenceSourcePolicy: {
|
|
112
|
+
roots: referenceRoots.value,
|
|
113
|
+
secretPatterns: secretPatterns.value,
|
|
114
|
+
maxBytes: 16 * 1024 * 1024,
|
|
115
|
+
maxEntries: 10_000,
|
|
116
|
+
maxDepth: 32,
|
|
117
|
+
maxPathBytes: 4_096,
|
|
84
118
|
},
|
|
85
119
|
});
|
|
86
120
|
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createEvidence } from "../domain/evidence.js";
|
|
2
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
3
|
+
const inventory = (digit) => {
|
|
4
|
+
const sha = digit.repeat(64);
|
|
5
|
+
const artifactId = `art_${sha}`;
|
|
6
|
+
const occurrenceId = `occ_${sha}`;
|
|
7
|
+
return jsonValueSchema.parse({
|
|
8
|
+
manifest: {
|
|
9
|
+
schema_version: 1,
|
|
10
|
+
manifest_id: `agm_${sha}`,
|
|
11
|
+
root_artifact_id: artifactId,
|
|
12
|
+
root_sha256: sha,
|
|
13
|
+
root_format: "file",
|
|
14
|
+
graph_sha256: sha,
|
|
15
|
+
node_count: 1,
|
|
16
|
+
occurrence_count: 1,
|
|
17
|
+
edge_count: 0,
|
|
18
|
+
},
|
|
19
|
+
nodes: {
|
|
20
|
+
items: [
|
|
21
|
+
{
|
|
22
|
+
artifact_id: artifactId,
|
|
23
|
+
kind: "resource",
|
|
24
|
+
format: "file",
|
|
25
|
+
sha256: sha,
|
|
26
|
+
size: 1,
|
|
27
|
+
media_type: null,
|
|
28
|
+
architecture: null,
|
|
29
|
+
executable: false,
|
|
30
|
+
content_state: "materialized",
|
|
31
|
+
limitations: [],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
offset: 0,
|
|
35
|
+
limit: 100,
|
|
36
|
+
total: 1,
|
|
37
|
+
next_offset: null,
|
|
38
|
+
},
|
|
39
|
+
occurrences: {
|
|
40
|
+
items: [
|
|
41
|
+
{
|
|
42
|
+
occurrence_id: occurrenceId,
|
|
43
|
+
artifact_id: artifactId,
|
|
44
|
+
parent_occurrence_id: null,
|
|
45
|
+
logical_path: ".",
|
|
46
|
+
entry_kind: "file",
|
|
47
|
+
declared_size: 1,
|
|
48
|
+
compressed_size: null,
|
|
49
|
+
executable: false,
|
|
50
|
+
encrypted: false,
|
|
51
|
+
hash_status: "verified",
|
|
52
|
+
source_location: null,
|
|
53
|
+
limitations: [],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
offset: 0,
|
|
57
|
+
limit: 100,
|
|
58
|
+
total: 1,
|
|
59
|
+
next_offset: null,
|
|
60
|
+
},
|
|
61
|
+
edges: { items: [], offset: 0, limit: 100, total: 0, next_offset: null },
|
|
62
|
+
limits: {
|
|
63
|
+
max_entries: 100,
|
|
64
|
+
max_total_bytes: 1024,
|
|
65
|
+
max_entry_bytes: 1024,
|
|
66
|
+
max_compression_ratio: 10,
|
|
67
|
+
max_depth: 10,
|
|
68
|
+
max_path_bytes: 256,
|
|
69
|
+
},
|
|
70
|
+
provenance: [],
|
|
71
|
+
limitations: [],
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const provider = {
|
|
75
|
+
id: "rea-artifact",
|
|
76
|
+
name: "REA artifact graph",
|
|
77
|
+
version: "1",
|
|
78
|
+
};
|
|
79
|
+
/** Canonical complete inputs used to advertise artifact comparison. */
|
|
80
|
+
export const ARTIFACT_COMPARISON_EXAMPLE = {
|
|
81
|
+
left: createEvidence(undefined, provider, {
|
|
82
|
+
operation: "inventory_artifact",
|
|
83
|
+
parameters: {},
|
|
84
|
+
result: inventory("0"),
|
|
85
|
+
confidence: "observed",
|
|
86
|
+
authority: "shipped-artifact",
|
|
87
|
+
}),
|
|
88
|
+
right: createEvidence(undefined, provider, {
|
|
89
|
+
operation: "inventory_artifact",
|
|
90
|
+
parameters: {},
|
|
91
|
+
result: inventory("1"),
|
|
92
|
+
confidence: "observed",
|
|
93
|
+
authority: "shipped-artifact",
|
|
94
|
+
}),
|
|
95
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { artifactOutputSchemas } from "./toolOutputSchemas.js";
|
|
3
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
4
|
+
const pageInput = {
|
|
5
|
+
node_offset: z.number().int().min(0).default(0),
|
|
6
|
+
node_limit: z.number().int().min(1).max(500).default(100),
|
|
7
|
+
occurrence_offset: z.number().int().min(0).default(0),
|
|
8
|
+
occurrence_limit: z.number().int().min(1).max(500).default(100),
|
|
9
|
+
edge_offset: z.number().int().min(0).default(0),
|
|
10
|
+
edge_limit: z.number().int().min(1).max(500).default(100),
|
|
11
|
+
};
|
|
12
|
+
const traversalLimitsInput = {
|
|
13
|
+
max_entries: z.number().int().min(1).max(1_000_000).default(10_000),
|
|
14
|
+
max_total_bytes: z
|
|
15
|
+
.number()
|
|
16
|
+
.int()
|
|
17
|
+
.min(1)
|
|
18
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
19
|
+
.default(1_073_741_824),
|
|
20
|
+
max_entry_bytes: z
|
|
21
|
+
.number()
|
|
22
|
+
.int()
|
|
23
|
+
.min(1)
|
|
24
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
25
|
+
.default(268_435_456),
|
|
26
|
+
max_compression_ratio: z.number().min(1).max(100_000).default(1_000),
|
|
27
|
+
max_depth: z.number().int().min(0).max(100).default(20),
|
|
28
|
+
max_path_bytes: z.number().int().min(1).max(65_535).default(4_096),
|
|
29
|
+
};
|
|
30
|
+
/** Exact caller boundary for deterministic artifact inventory. */
|
|
31
|
+
export const artifactInventoryInputSchema = z.object({
|
|
32
|
+
...pageInput,
|
|
33
|
+
...traversalLimitsInput,
|
|
34
|
+
});
|
|
35
|
+
/** Exact caller boundary for approved artifact extraction. */
|
|
36
|
+
export const artifactExtractionInputSchema = z.object({
|
|
37
|
+
approved: z.literal(true),
|
|
38
|
+
output_root: z.string().min(1).max(4_096),
|
|
39
|
+
occurrence_ids: z
|
|
40
|
+
.array(z.string().regex(/^occ_[a-f0-9]{64}$/u))
|
|
41
|
+
.min(1)
|
|
42
|
+
.max(500),
|
|
43
|
+
offset: z.number().int().min(0).default(0),
|
|
44
|
+
limit: z.number().int().min(1).max(500).default(100),
|
|
45
|
+
...traversalLimitsInput,
|
|
46
|
+
});
|
|
47
|
+
const exampleInputSchema = z.record(z.string(), jsonValueSchema);
|
|
48
|
+
const examples = {
|
|
49
|
+
inventory_artifact: {},
|
|
50
|
+
extract_artifact: {
|
|
51
|
+
approved: true,
|
|
52
|
+
output_root: "/tmp/rea-extracted",
|
|
53
|
+
occurrence_ids: [`occ_${"0".repeat(64)}`],
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
const artifact = (name, description, inputSchema, mutatesFilesystem) => {
|
|
57
|
+
const outputSchema = artifactOutputSchemas[name];
|
|
58
|
+
if (outputSchema === undefined)
|
|
59
|
+
throw new Error(`Missing artifact output schema for ${name}`);
|
|
60
|
+
return {
|
|
61
|
+
name,
|
|
62
|
+
description,
|
|
63
|
+
kind: "artifact-provider",
|
|
64
|
+
inputSchema,
|
|
65
|
+
outputSchema,
|
|
66
|
+
annotations: {
|
|
67
|
+
readOnlyHint: !mutatesFilesystem,
|
|
68
|
+
destructiveHint: false,
|
|
69
|
+
idempotentHint: true,
|
|
70
|
+
openWorldHint: false,
|
|
71
|
+
},
|
|
72
|
+
examples: [
|
|
73
|
+
{
|
|
74
|
+
title: `Example ${name.replaceAll("_", " ")} request`,
|
|
75
|
+
input: exampleInputSchema.parse(examples[name] ?? {}),
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
/** Artifact-graph inventory and explicitly approved safe extraction contracts. */
|
|
81
|
+
export const ARTIFACT_TOOL_CONTRACTS = [
|
|
82
|
+
artifact("inventory_artifact", "Inventory the active application or package as a deterministic, content-addressed artifact graph. Returns bounded node and edge pages without extracting or mounting by default.", artifactInventoryInputSchema, false),
|
|
83
|
+
artifact("extract_artifact", "Extract selected graph artifacts beneath an explicit output root. Requires approval, rejects traversal and symlink escapes, never overwrites, enforces bomb limits, and verifies cleanup.", artifactExtractionInputSchema, true),
|
|
84
|
+
];
|
|
@@ -56,6 +56,10 @@ export const enhancedInputSchemas = {
|
|
|
56
56
|
case_sensitive: z.boolean().default(false),
|
|
57
57
|
limit: z.number().int().min(1).max(100).default(20),
|
|
58
58
|
max_operations: z.number().int().min(1).max(100).default(20),
|
|
59
|
+
unknown_registry_approved: z
|
|
60
|
+
.literal(true)
|
|
61
|
+
.optional()
|
|
62
|
+
.describe("Explicit approval to record bounded residuals durably"),
|
|
59
63
|
}),
|
|
60
64
|
};
|
|
61
65
|
/** Runtime parser for dispatching only the eight closed enhanced names. */
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { enhancedInputSchemas } from "./enhancedInputs.js";
|
|
2
|
+
import { createEvidence } from "../domain/evidence.js";
|
|
3
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
4
|
+
const bounded = (items) => ({
|
|
5
|
+
items,
|
|
6
|
+
total: items.length,
|
|
7
|
+
returned: items.length,
|
|
8
|
+
truncated: false,
|
|
9
|
+
next_offset: null,
|
|
10
|
+
});
|
|
11
|
+
const dossier = (text) => jsonValueSchema.parse({
|
|
12
|
+
procedure: {
|
|
13
|
+
address: "0x1000",
|
|
14
|
+
name: "main",
|
|
15
|
+
signature: "int main(void)",
|
|
16
|
+
locals: [],
|
|
17
|
+
},
|
|
18
|
+
pseudocode: {
|
|
19
|
+
text,
|
|
20
|
+
total_chars: [...text].length,
|
|
21
|
+
returned_chars: [...text].length,
|
|
22
|
+
truncated: false,
|
|
23
|
+
next_offset: null,
|
|
24
|
+
},
|
|
25
|
+
assembly: bounded([]),
|
|
26
|
+
comments: bounded([]),
|
|
27
|
+
callers: bounded([]),
|
|
28
|
+
callees: bounded([]),
|
|
29
|
+
incoming_references: bounded([]),
|
|
30
|
+
outgoing_references: bounded([]),
|
|
31
|
+
referenced_strings: bounded([]),
|
|
32
|
+
referenced_names: bounded([]),
|
|
33
|
+
basic_blocks: bounded([{ start: "0x1000", end: "0x1001", successors: [] }]),
|
|
34
|
+
instruction_scan: { scanned: 1, truncated: false },
|
|
35
|
+
});
|
|
36
|
+
const observe = (digit, text) => createEvidence({
|
|
37
|
+
path: `/tmp/function-${digit}`,
|
|
38
|
+
sha256: digit.repeat(64),
|
|
39
|
+
format: "mach-o",
|
|
40
|
+
}, {
|
|
41
|
+
id: "rea-workflow",
|
|
42
|
+
name: "REA composed investigation workflow",
|
|
43
|
+
version: "1",
|
|
44
|
+
}, {
|
|
45
|
+
operation: "analyze_function",
|
|
46
|
+
parameters: enhancedInputSchemas.analyze_function.parse({
|
|
47
|
+
procedure: "main",
|
|
48
|
+
}),
|
|
49
|
+
result: dossier(text),
|
|
50
|
+
confidence: "derived",
|
|
51
|
+
authority: "shipped-artifact",
|
|
52
|
+
});
|
|
53
|
+
/** Canonical explicit function pair used in public contract examples. */
|
|
54
|
+
export const FUNCTION_COMPARISON_EXAMPLE = {
|
|
55
|
+
left: observe("0", "return 0;"),
|
|
56
|
+
right: observe("1", "return 1;"),
|
|
57
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { compareFunctions } from "../domain/functionComparison.js";
|
|
2
|
+
import { createEvidence } from "../domain/evidence.js";
|
|
3
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
4
|
+
import { createEvidenceBundle } from "../domain/evidenceBundle.js";
|
|
5
|
+
import { compareProcessCaptures, processCaptureSchema, } from "../domain/processCapture.js";
|
|
6
|
+
import { FUNCTION_COMPARISON_EXAMPLE } from "./functionComparisonExample.js";
|
|
7
|
+
import { EMPTY_PROCESS_CAPTURE_EXAMPLE } from "./processCaptureExample.js";
|
|
8
|
+
const comparison = compareFunctions(FUNCTION_COMPARISON_EXAMPLE.left, FUNCTION_COMPARISON_EXAMPLE.right, 0, 100);
|
|
9
|
+
export const FUNCTION_COMPARISON_EVIDENCE = createEvidence(undefined, {
|
|
10
|
+
id: "rea-function-comparison",
|
|
11
|
+
name: "REA function comparison",
|
|
12
|
+
version: "1",
|
|
13
|
+
}, {
|
|
14
|
+
predicateType: "rea.function-comparison/v1",
|
|
15
|
+
operation: "compare_functions",
|
|
16
|
+
parameters: {
|
|
17
|
+
left_evidence_ids: [FUNCTION_COMPARISON_EXAMPLE.left.evidence_id],
|
|
18
|
+
right_evidence_ids: [FUNCTION_COMPARISON_EXAMPLE.right.evidence_id],
|
|
19
|
+
offset: 0,
|
|
20
|
+
limit: 100,
|
|
21
|
+
},
|
|
22
|
+
result: jsonValueSchema.parse(comparison),
|
|
23
|
+
confidence: "derived",
|
|
24
|
+
authority: "analyst-inference",
|
|
25
|
+
evidenceLinks: [
|
|
26
|
+
FUNCTION_COMPARISON_EXAMPLE.left.evidence_id,
|
|
27
|
+
FUNCTION_COMPARISON_EXAMPLE.right.evidence_id,
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
const PROCESS_PROVIDER = {
|
|
31
|
+
id: "rea-process",
|
|
32
|
+
name: "REA deterministic process harness",
|
|
33
|
+
version: "1",
|
|
34
|
+
};
|
|
35
|
+
const capture = processCaptureSchema.parse(EMPTY_PROCESS_CAPTURE_EXAMPLE);
|
|
36
|
+
const captureEvidence = (scenario) => createEvidence(undefined, PROCESS_PROVIDER, {
|
|
37
|
+
predicateType: "rea.process-capture/v2",
|
|
38
|
+
operation: "capture_process_scenario",
|
|
39
|
+
parameters: { scenario },
|
|
40
|
+
result: jsonValueSchema.parse(capture),
|
|
41
|
+
confidence: "observed",
|
|
42
|
+
authority: "controlled-replay",
|
|
43
|
+
environment: {
|
|
44
|
+
id: "fixture-linux",
|
|
45
|
+
platform: "linux",
|
|
46
|
+
architecture: "x86_64",
|
|
47
|
+
isolation: "container",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
export const PROCESS_CAPTURE_REFERENCE = captureEvidence("reference");
|
|
51
|
+
export const PROCESS_CAPTURE_RECONSTRUCTION = captureEvidence("reconstruction");
|
|
52
|
+
export const PROCESS_COMPARISON_EVIDENCE = createEvidence(undefined, PROCESS_PROVIDER, {
|
|
53
|
+
predicateType: "rea.process-comparison/v1",
|
|
54
|
+
operation: "compare_process_captures",
|
|
55
|
+
parameters: {
|
|
56
|
+
left_evidence_id: PROCESS_CAPTURE_REFERENCE.evidence_id,
|
|
57
|
+
right_evidence_id: PROCESS_CAPTURE_RECONSTRUCTION.evidence_id,
|
|
58
|
+
left_normalization: capture.normalization,
|
|
59
|
+
right_normalization: capture.normalization,
|
|
60
|
+
},
|
|
61
|
+
result: jsonValueSchema.parse(compareProcessCaptures(capture, capture)),
|
|
62
|
+
confidence: "derived",
|
|
63
|
+
authority: "analyst-inference",
|
|
64
|
+
evidenceLinks: [
|
|
65
|
+
PROCESS_CAPTURE_REFERENCE.evidence_id,
|
|
66
|
+
PROCESS_CAPTURE_RECONSTRUCTION.evidence_id,
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
/** Canonical inputs for comparison-composed investigation contracts. */
|
|
70
|
+
export const INVESTIGATION_EXAMPLES = {
|
|
71
|
+
find_changed_behavior: { comparisons: [FUNCTION_COMPARISON_EVIDENCE] },
|
|
72
|
+
build_call_path: {
|
|
73
|
+
functions: [FUNCTION_COMPARISON_EXAMPLE.left],
|
|
74
|
+
start: { address: "0x1000" },
|
|
75
|
+
goal: { address: "0x1000" },
|
|
76
|
+
},
|
|
77
|
+
correlate_static_and_runtime: {
|
|
78
|
+
static_comparisons: [FUNCTION_COMPARISON_EVIDENCE],
|
|
79
|
+
runtime_comparisons: [PROCESS_COMPARISON_EVIDENCE],
|
|
80
|
+
mappings: [
|
|
81
|
+
{
|
|
82
|
+
static: {
|
|
83
|
+
comparison_evidence_id: FUNCTION_COMPARISON_EVIDENCE.evidence_id,
|
|
84
|
+
selector: { kind: "function", dimension: "pseudocode" },
|
|
85
|
+
},
|
|
86
|
+
runtime: {
|
|
87
|
+
comparison_evidence_id: PROCESS_COMPARISON_EVIDENCE.evidence_id,
|
|
88
|
+
dimension: "terminal",
|
|
89
|
+
},
|
|
90
|
+
side_alignment: "left_to_left",
|
|
91
|
+
hypothesis: {
|
|
92
|
+
statement: "Static implementation changed without terminal change.",
|
|
93
|
+
expected_pattern: "static_only",
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
verify_reconstruction: {
|
|
99
|
+
specification: {
|
|
100
|
+
schema_version: 1,
|
|
101
|
+
name: "Terminal compatibility",
|
|
102
|
+
claims: [
|
|
103
|
+
{
|
|
104
|
+
kind: "behavioral",
|
|
105
|
+
claim_id: "terminal-output",
|
|
106
|
+
title: "Terminal output remains equivalent",
|
|
107
|
+
comparison_evidence_id: PROCESS_COMPARISON_EVIDENCE.evidence_id,
|
|
108
|
+
dimension: "terminal",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
evidence_bundle: createEvidenceBundle([
|
|
113
|
+
PROCESS_CAPTURE_REFERENCE,
|
|
114
|
+
PROCESS_CAPTURE_RECONSTRUCTION,
|
|
115
|
+
PROCESS_COMPARISON_EVIDENCE,
|
|
116
|
+
]),
|
|
117
|
+
},
|
|
118
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { nativeOutputSchemas } from "./toolOutputSchemas.js";
|
|
3
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
4
|
+
const examples = {
|
|
5
|
+
inspect_macho: {},
|
|
6
|
+
inspect_signature: {},
|
|
7
|
+
inspect_plist: { relative_path: "Contents/Info.plist" },
|
|
8
|
+
list_architectures: {},
|
|
9
|
+
demangle_swift: { symbols: ["$s4Test3fooyyF"] },
|
|
10
|
+
};
|
|
11
|
+
const native = (name, description, inputSchema) => {
|
|
12
|
+
const outputSchema = nativeOutputSchemas[name];
|
|
13
|
+
if (outputSchema === undefined)
|
|
14
|
+
throw new Error(`Missing native output schema for ${name}`);
|
|
15
|
+
return {
|
|
16
|
+
name,
|
|
17
|
+
description,
|
|
18
|
+
kind: "native-provider",
|
|
19
|
+
inputSchema,
|
|
20
|
+
outputSchema,
|
|
21
|
+
annotations: {
|
|
22
|
+
readOnlyHint: true,
|
|
23
|
+
destructiveHint: false,
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
openWorldHint: false,
|
|
26
|
+
},
|
|
27
|
+
examples: [
|
|
28
|
+
{
|
|
29
|
+
title: `Example ${name.replaceAll("_", " ")} request`,
|
|
30
|
+
input: z
|
|
31
|
+
.record(z.string(), jsonValueSchema)
|
|
32
|
+
.parse(examples[name] ?? {}),
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/** Provider-neutral semantic operations backed initially by macOS utilities. */
|
|
38
|
+
export const NATIVE_TOOL_CONTRACTS = [
|
|
39
|
+
native("inspect_macho", "Inspect Mach-O slices, load commands, imports, exports, dependencies, build metadata, segments, sections, permissions, and exact command provenance without launching Hopper.", z.object({})),
|
|
40
|
+
native("inspect_signature", "Inspect the active artifact's code-signing identity, hashes, authorities, requirements, entitlements, hardened-runtime state, and bounded command provenance.", z.object({})),
|
|
41
|
+
native("inspect_plist", "Parse a plist at a bounded relative path beneath the active artifact container. Symlink and traversal escapes are rejected; output is normalized JSON rather than plutil text.", z.object({
|
|
42
|
+
relative_path: z
|
|
43
|
+
.string()
|
|
44
|
+
.min(1)
|
|
45
|
+
.max(1_024)
|
|
46
|
+
.default("Contents/Info.plist"),
|
|
47
|
+
})),
|
|
48
|
+
native("list_architectures", "List thin or universal Mach-O slices with offsets, sizes, alignment, explicit coverage, and bounded native-tool provenance.", z.object({})),
|
|
49
|
+
native("demangle_swift", "Demangle an ordered bounded batch of Swift symbols without requiring Hopper. Each input returns demangled, unchanged, or invalid status.", z.object({
|
|
50
|
+
symbols: z.array(z.string().min(1).max(4_096)).min(1).max(500),
|
|
51
|
+
})),
|
|
52
|
+
];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Minimal valid process capture used only in public contract examples. */
|
|
2
|
+
export const EMPTY_PROCESS_CAPTURE_EXAMPLE = {
|
|
3
|
+
schema_version: 2,
|
|
4
|
+
normalization: {
|
|
5
|
+
paths: true,
|
|
6
|
+
pids: true,
|
|
7
|
+
ports: true,
|
|
8
|
+
time_bucket_ms: 10,
|
|
9
|
+
patterns: [],
|
|
10
|
+
},
|
|
11
|
+
frames: [],
|
|
12
|
+
exit: { code: 0, signal: null, reason: "exited" },
|
|
13
|
+
process_samples: [],
|
|
14
|
+
protocol_events: [],
|
|
15
|
+
files_before: [],
|
|
16
|
+
files_after: [],
|
|
17
|
+
filesystem_effects: [],
|
|
18
|
+
truncated: false,
|
|
19
|
+
limitations: [],
|
|
20
|
+
residual_unknowns: [],
|
|
21
|
+
cleanup: {
|
|
22
|
+
owned_process_group: "verified",
|
|
23
|
+
temporary_root: "removed",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { EMPTY_PROCESS_CAPTURE_EXAMPLE } from "./processCaptureExample.js";
|
|
2
|
+
import { UNKNOWN_CONTRACT_EXAMPLES } from "./unknownContractExamples.js";
|
|
3
|
+
import { ARTIFACT_COMPARISON_EXAMPLE } from "./artifactComparisonExample.js";
|
|
4
|
+
import { FUNCTION_COMPARISON_EXAMPLE } from "./functionComparisonExample.js";
|
|
5
|
+
import { INVESTIGATION_EXAMPLES } from "./investigationExamples.js";
|
|
6
|
+
/** Canonical examples for contracts whose required inputs have no defaults. */
|
|
7
|
+
export const TOOL_EXAMPLE_OVERRIDES = {
|
|
8
|
+
...UNKNOWN_CONTRACT_EXAMPLES,
|
|
9
|
+
...INVESTIGATION_EXAMPLES,
|
|
10
|
+
goto_address: { address: "0x1000" },
|
|
11
|
+
procedure_address: { procedure: "main" },
|
|
12
|
+
procedure_assembly: { procedure: "main" },
|
|
13
|
+
procedure_callees: { procedure: "main" },
|
|
14
|
+
procedure_callers: { procedure: "main" },
|
|
15
|
+
procedure_info: { procedure: "main" },
|
|
16
|
+
procedure_references: { procedure: "main" },
|
|
17
|
+
procedure_pseudo_code: { procedure: "main" },
|
|
18
|
+
resolve_containing_procedure: { address: "0x1000" },
|
|
19
|
+
search_procedures: { pattern: "main" },
|
|
20
|
+
search_strings: { pattern: "authorization failed" },
|
|
21
|
+
set_address_name: { address: "0x1000", name: "entry" },
|
|
22
|
+
set_addresses_names: { names: { "0x1000": "entry" } },
|
|
23
|
+
set_bookmark: { address: "0x1000" },
|
|
24
|
+
set_comment: { address: "0x1000", comment: "validated entry point" },
|
|
25
|
+
set_current_document: { document: "fixture" },
|
|
26
|
+
set_inline_comment: { address: "0x1000", comment: "calls parser" },
|
|
27
|
+
unset_bookmark: { address: "0x1000" },
|
|
28
|
+
get_call_graph: { address: "0x1000" },
|
|
29
|
+
find_xrefs_to_name: { name: "malloc" },
|
|
30
|
+
analyze_function: { procedure: "main" },
|
|
31
|
+
trace_feature: { query: "license" },
|
|
32
|
+
open_binary: { path: "/tmp/fixture" },
|
|
33
|
+
import_evidence_bundle: { path: "evidence.json" },
|
|
34
|
+
capture_process_scenario: {
|
|
35
|
+
approved: true,
|
|
36
|
+
executable: "/usr/bin/true",
|
|
37
|
+
working_directory: "/tmp",
|
|
38
|
+
},
|
|
39
|
+
compare_process_captures: {
|
|
40
|
+
left_evidence_id: `ev_${"0".repeat(64)}`,
|
|
41
|
+
left: EMPTY_PROCESS_CAPTURE_EXAMPLE,
|
|
42
|
+
right_evidence_id: `ev_${"1".repeat(64)}`,
|
|
43
|
+
right: EMPTY_PROCESS_CAPTURE_EXAMPLE,
|
|
44
|
+
},
|
|
45
|
+
compare_artifacts: ARTIFACT_COMPARISON_EXAMPLE,
|
|
46
|
+
compare_functions: FUNCTION_COMPARISON_EXAMPLE,
|
|
47
|
+
compare_bundles: {
|
|
48
|
+
left: {
|
|
49
|
+
bundle_version: 2,
|
|
50
|
+
artifacts: [],
|
|
51
|
+
providers: [],
|
|
52
|
+
environments: [],
|
|
53
|
+
scenarios: [],
|
|
54
|
+
captures: [],
|
|
55
|
+
unknowns: [],
|
|
56
|
+
records: [],
|
|
57
|
+
},
|
|
58
|
+
right: {
|
|
59
|
+
bundle_version: 2,
|
|
60
|
+
artifacts: [],
|
|
61
|
+
providers: [],
|
|
62
|
+
environments: [],
|
|
63
|
+
scenarios: [],
|
|
64
|
+
captures: [],
|
|
65
|
+
unknowns: [],
|
|
66
|
+
records: [],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|