rea-agents 1.4.0 → 1.6.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 +37 -13
- package/dist/application/AnalysisSnapshotCache.js +8 -6
- package/dist/application/ArtifactGraphConstruction.js +11 -2
- package/dist/application/ArtifactInventory.js +3 -2
- package/dist/application/AuthorizedArtifactInventory.js +2 -1
- package/dist/application/BinarySession.js +26 -11
- package/dist/application/BoundedJsonFiles.js +6 -29
- package/dist/application/BrowserEvidence.js +107 -0
- package/dist/application/BrowserObservationPort.js +1 -0
- package/dist/application/BrowserObservationService.js +116 -0
- package/dist/application/CapabilityInventory.js +14 -0
- package/dist/application/ClientConfigPath.js +33 -0
- package/dist/application/ConfiguredRoots.js +19 -0
- package/dist/application/CrossVersionInventory.js +16 -3
- package/dist/application/CrossVersionInvestigation.js +23 -62
- package/dist/application/CrossVersionInvestigationEvidence.js +64 -0
- package/dist/application/CrossVersionInvestigationReplay.js +170 -0
- package/dist/application/DeferredFileAuthorization.js +61 -0
- package/dist/application/DirectAnalysis.js +38 -18
- package/dist/application/Doctor.js +16 -11
- package/dist/application/ElectronEvidence.js +35 -0
- package/dist/application/ElectronObservationPort.js +1 -0
- package/dist/application/ElectronObservationService.js +46 -0
- package/dist/application/EvidenceLedger.js +11 -7
- package/dist/application/InvestigationWorkspaceStore.js +2 -26
- package/dist/application/PermissionAuthority.js +30 -0
- package/dist/application/ProcessCaptureAuthority.js +3 -2
- package/dist/application/ProcessCaptureLifecycle.js +19 -13
- package/dist/application/ProcessCheckpoints.js +1 -1
- package/dist/application/ProcessCli.js +17 -5
- package/dist/application/ProcessHarness.js +19 -26
- package/dist/application/ProcessNormalization.js +17 -2
- package/dist/application/ProcessOwnership.js +44 -23
- package/dist/application/ProcessSampling.js +111 -25
- package/dist/application/ProjectPermissionStore.js +107 -3
- package/dist/application/ReferenceSourceImport.js +13 -16
- package/dist/application/ReferenceSourceImportPolicy.js +3 -2
- package/dist/application/Setup.js +89 -30
- package/dist/application/SetupClients.js +7 -4
- package/dist/application/SetupInstallFailure.js +24 -0
- package/dist/application/SetupPlan.js +11 -7
- package/dist/application/SetupSkill.js +20 -3
- package/dist/application/TerminalRenderer.js +1 -1
- package/dist/application/Uninstall.js +20 -7
- package/dist/application/Upgrade.js +14 -3
- package/dist/artifacts/MachOSliceArtifactReader.js +1 -1
- package/dist/artifacts/NativeDmgArtifactReader.js +1 -1
- package/dist/artifacts/SafeOutputTree.js +3 -3
- package/dist/browser/CdpBrowserProvider.js +261 -0
- package/dist/browser/CdpCaptureCompleteness.js +90 -0
- package/dist/browser/CdpCaptureDocuments.js +299 -0
- package/dist/browser/CdpCaptureEvents.js +547 -0
- package/dist/browser/CdpCaptureStorage.js +53 -0
- package/dist/browser/CdpCaptureValues.js +68 -0
- package/dist/browser/CdpConnection.js +241 -0
- package/dist/browser/CdpElectronInspection.js +330 -0
- package/dist/browser/CdpElectronProvider.js +134 -0
- package/dist/browser/CdpEndpoint.js +173 -0
- package/dist/browser/CdpObservationSession.js +301 -0
- package/dist/browser/CdpOptionalCommand.js +15 -0
- package/dist/browser/CdpPageCapture.js +376 -0
- package/dist/browser/CdpSafeMetadata.js +203 -0
- package/dist/browser/CdpScreenshot.js +101 -0
- package/dist/browser/CdpTargetSession.js +47 -0
- package/dist/browser/CdpWebMcpDiscovery.js +242 -0
- package/dist/browser/ElectronFileScope.js +50 -0
- package/dist/browser/PngVisualDiff.js +177 -0
- package/dist/browser/SensitiveTextCapture.js +25 -0
- package/dist/browser/WebSourceMapFetcher.js +357 -0
- package/dist/catalogIdentity.js +10 -0
- package/dist/cli.js +6 -0
- package/dist/cliBrowserAdvancedCommands.js +169 -0
- package/dist/cliBrowserCommands.js +312 -0
- package/dist/cliElectronCommands.js +114 -0
- package/dist/cliInvestigationCommands.js +26 -34
- package/dist/cliPolicyCommands.js +60 -12
- package/dist/config.js +80 -1
- package/dist/contracts/browserToolContracts.js +381 -0
- package/dist/contracts/electronToolContracts.js +73 -0
- package/dist/contracts/errorSchemas.js +2 -0
- package/dist/contracts/toolContracts.js +5 -5
- package/dist/domain/browserCompleteness.js +101 -0
- package/dist/domain/browserObservation.js +587 -0
- package/dist/domain/browserSession.js +63 -0
- package/dist/domain/changedBehavior.js +4 -15
- package/dist/domain/electronObservation.js +162 -0
- package/dist/domain/errors.js +41 -0
- package/dist/domain/investigationWorkspace.js +4 -0
- package/dist/domain/jsonLimits.js +26 -0
- package/dist/domain/jsonShape.js +125 -0
- package/dist/domain/permissionPolicy.js +6 -2
- package/dist/domain/processCapture.js +3 -1
- package/dist/domain/processComparison.js +22 -34
- package/dist/domain/reconstructionVerification.js +2 -15
- package/dist/domain/reconstructionVerificationSchemas.js +2 -0
- package/dist/domain/referenceSourceGraph.js +3 -1
- package/dist/domain/staticRuntimeCorrelation.js +2 -0
- package/dist/domain/symbolAnalysis.js +1 -1
- package/dist/domain/webBundleAnalysis.js +195 -0
- package/dist/domain/webBundleAnalyzer.js +423 -0
- package/dist/domain/webCaptureDiff.js +184 -0
- package/dist/domain/webContentArtifact.js +47 -0
- package/dist/domain/webInventory.js +61 -0
- package/dist/domain/webMcpDiscovery.js +68 -0
- package/dist/domain/webScreenshot.js +114 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/hopper/BridgeLauncher.js +2 -3
- package/dist/hopper/HopperClient.js +51 -11
- package/dist/hopper/HopperProvider.js +6 -1
- package/dist/main.js +42 -31
- package/dist/native/CommandRunner.js +11 -4
- package/dist/native/NativeMacOSProvider.js +3 -2
- package/dist/native/NativeMachoInspection.js +21 -6
- package/dist/server/createServer.js +14 -0
- package/dist/server/registerBrowserTools.js +95 -0
- package/dist/server/registerElectronTools.js +36 -0
- package/dist/server/registerInvestigationTools.js +31 -37
- package/dist/server/registerProcessComparisonTool.js +2 -0
- package/dist/server/registerSessionTools.js +16 -2
- package/dist/server/runDerivedOperation.js +10 -2
- package/package.json +6 -1
- package/scripts/rea.mjs +2 -2
- package/skills/rea-analysis/SKILL.md +28 -3
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { allowedSanitizedUrl, boundedText, isHttpUrl, numberValue, recordValue, recordsValue, requiredRecord, stringValue, } from "./CdpCaptureValues.js";
|
|
2
|
+
/** Normalize allowed frames while retaining a bounded prefix and total count. */
|
|
3
|
+
export const captureFrames = (result, allowedOrigins, maximum, completeness) => {
|
|
4
|
+
const root = recordValue(requiredRecord(result).frameTree);
|
|
5
|
+
if (root === undefined)
|
|
6
|
+
return { total: 0, items: [] };
|
|
7
|
+
const items = [];
|
|
8
|
+
let total = 0;
|
|
9
|
+
for (const tree of walkFrameTrees(root)) {
|
|
10
|
+
const frame = recordValue(tree.frame);
|
|
11
|
+
const frameId = stringValue(frame?.id);
|
|
12
|
+
const sanitized = allowedSanitizedUrl(frame?.url, allowedOrigins);
|
|
13
|
+
if (frame === undefined || frameId === undefined || frameId.length > 256) {
|
|
14
|
+
completeness?.exclude("frames", "invalid_protocol_value");
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (sanitized === undefined || sanitized.origin === null) {
|
|
18
|
+
completeness?.exclude("frames", exclusionReasonForUrl(stringValue(frame.url)));
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
total += 1;
|
|
22
|
+
if (items.length >= maximum)
|
|
23
|
+
continue;
|
|
24
|
+
items.push({
|
|
25
|
+
frame_id: frameId,
|
|
26
|
+
parent_frame_id: (stringValue(frame.parentId) ?? "").slice(0, 256) || null,
|
|
27
|
+
url: sanitized.url,
|
|
28
|
+
origin: sanitized.origin,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return { total, items };
|
|
32
|
+
};
|
|
33
|
+
/** Read the current main-frame URL from an untrusted Page.getFrameTree result. */
|
|
34
|
+
export const mainFrameUrl = (result) => stringValue(recordValue(recordValue(requiredRecord(result).frameTree)?.frame)?.url);
|
|
35
|
+
/** Normalize allowed resources while retaining a bounded prefix and total count. */
|
|
36
|
+
export const captureResources = (result, allowedOrigins, maximum, completeness) => {
|
|
37
|
+
const root = recordValue(requiredRecord(result).frameTree);
|
|
38
|
+
if (root === undefined)
|
|
39
|
+
return { total: 0, items: [] };
|
|
40
|
+
const items = [];
|
|
41
|
+
let total = 0;
|
|
42
|
+
for (const tree of walkFrameTrees(root)) {
|
|
43
|
+
for (const resource of recordsValue(tree.resources)) {
|
|
44
|
+
const url = allowedSanitizedUrl(resource.url, allowedOrigins);
|
|
45
|
+
if (url === undefined || url.origin === null) {
|
|
46
|
+
completeness?.exclude("resources", exclusionReasonForUrl(stringValue(resource.url)));
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
total += 1;
|
|
50
|
+
if (items.length >= maximum)
|
|
51
|
+
continue;
|
|
52
|
+
const contentSize = numberValue(resource.contentSize);
|
|
53
|
+
items.push({
|
|
54
|
+
rawUrl: stringValue(resource.url) ?? "",
|
|
55
|
+
url: url.url,
|
|
56
|
+
origin: url.origin,
|
|
57
|
+
type: (stringValue(resource.type) ?? "Other").slice(0, 100),
|
|
58
|
+
mime_type: (stringValue(resource.mimeType) ?? "").slice(0, 256),
|
|
59
|
+
content_size: contentSize === undefined ? null : Math.max(0, contentSize),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { total, items };
|
|
64
|
+
};
|
|
65
|
+
const walkFrameTrees = function* (root) {
|
|
66
|
+
const pending = [root];
|
|
67
|
+
while (pending.length > 0) {
|
|
68
|
+
const tree = pending.pop();
|
|
69
|
+
if (tree === undefined)
|
|
70
|
+
return;
|
|
71
|
+
const children = recordsValue(tree.childFrames);
|
|
72
|
+
for (let index = children.length - 1; index >= 0; index -= 1) {
|
|
73
|
+
const child = children[index];
|
|
74
|
+
if (child !== undefined)
|
|
75
|
+
pending.push(child);
|
|
76
|
+
}
|
|
77
|
+
yield tree;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
export const captureDom = (result, allowedOrigins, input, completeness) => {
|
|
81
|
+
const root = requiredRecord(result);
|
|
82
|
+
const strings = Array.isArray(root.strings)
|
|
83
|
+
? root.strings.map((value) => stringValue(value) ?? "")
|
|
84
|
+
: [];
|
|
85
|
+
const nodes = [];
|
|
86
|
+
const urls = [];
|
|
87
|
+
const agentHints = [];
|
|
88
|
+
let excludedUrls = 0;
|
|
89
|
+
let total = 0;
|
|
90
|
+
for (const document of recordsValue(root.documents)) {
|
|
91
|
+
const documentUrl = indexedString(strings, document.documentURL);
|
|
92
|
+
if (allowedSanitizedUrl(documentUrl, allowedOrigins) === undefined) {
|
|
93
|
+
completeness?.exclude("dom", exclusionReasonForUrl(documentUrl));
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const documentNodes = recordValue(document.nodes);
|
|
97
|
+
if (documentNodes === undefined)
|
|
98
|
+
continue;
|
|
99
|
+
const nodeTypes = numberArray(documentNodes.nodeType);
|
|
100
|
+
const nodeNames = numberArray(documentNodes.nodeName);
|
|
101
|
+
const nodeValues = numberArray(documentNodes.nodeValue);
|
|
102
|
+
const parents = numberArray(documentNodes.parentIndex);
|
|
103
|
+
const attributes = Array.isArray(documentNodes.attributes)
|
|
104
|
+
? documentNodes.attributes
|
|
105
|
+
: [];
|
|
106
|
+
const baseIndex = nodes.length;
|
|
107
|
+
total += nodeTypes.length;
|
|
108
|
+
for (let index = 0; index < nodeTypes.length && nodes.length < input.limits.max_dom_nodes; index += 1) {
|
|
109
|
+
const attributeIndexes = numberArray(attributes[index]);
|
|
110
|
+
const parent = Math.trunc(parents[index] ?? -1);
|
|
111
|
+
const nodeIndex = nodes.length;
|
|
112
|
+
const nodeName = indexedString(strings, nodeNames[index]).slice(0, 256);
|
|
113
|
+
nodes.push({
|
|
114
|
+
index: nodeIndex,
|
|
115
|
+
parent_index: parent < 0 ? -1 : baseIndex + parent,
|
|
116
|
+
node_type: Math.max(0, Math.trunc(nodeTypes[index] ?? 0)),
|
|
117
|
+
node_name: nodeName,
|
|
118
|
+
node_value_length: indexedString(strings, nodeValues[index]).length,
|
|
119
|
+
attribute_names: attributeIndexes
|
|
120
|
+
.filter((_value, attributeIndex) => attributeIndex % 2 === 0)
|
|
121
|
+
.map((value) => indexedString(strings, value).slice(0, 256))
|
|
122
|
+
.slice(0, 200),
|
|
123
|
+
});
|
|
124
|
+
const metadata = domMetadata(strings, attributeIndexes, documentUrl, nodeIndex, nodeName, allowedOrigins);
|
|
125
|
+
for (const url of metadata.urls) {
|
|
126
|
+
if (urls.length >= input.limits.max_resources) {
|
|
127
|
+
completeness?.truncate("metadata");
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
urls.push(url);
|
|
131
|
+
if (url.destination_scope !== "approved") {
|
|
132
|
+
excludedUrls += 1;
|
|
133
|
+
completeness?.exclude("metadata", url.destination_scope === "outside_policy"
|
|
134
|
+
? "disallowed_origin"
|
|
135
|
+
: "unsupported_url");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
agentHints.push(...metadata.agentHints);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
total,
|
|
143
|
+
nodes,
|
|
144
|
+
urls,
|
|
145
|
+
agentHints: agentHints.slice(0, input.limits.max_resources),
|
|
146
|
+
excludedUrls,
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
export const captureAccessibility = (results, maximum, options) => {
|
|
150
|
+
const all = results.flatMap((result) => recordsValue(requiredRecord(result).nodes));
|
|
151
|
+
let retainedBytes = 0;
|
|
152
|
+
let excludedFields = 0;
|
|
153
|
+
let truncatedFields = 0;
|
|
154
|
+
const nodes = all.slice(0, maximum).map((node) => {
|
|
155
|
+
const captureText = (value) => {
|
|
156
|
+
const raw = stringValue(recordValue(value)?.value);
|
|
157
|
+
if (raw === undefined)
|
|
158
|
+
return null;
|
|
159
|
+
if (!options.includeText) {
|
|
160
|
+
excludedFields += 1;
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const available = Math.max(0, options.maximumTotalBytes - retainedBytes);
|
|
164
|
+
const captured = boundedUtf8(raw, Math.min(options.maximumFieldBytes, available));
|
|
165
|
+
retainedBytes += captured.bytes;
|
|
166
|
+
if (captured.truncated) {
|
|
167
|
+
truncatedFields += 1;
|
|
168
|
+
if (captured.text === "" && raw !== "") {
|
|
169
|
+
excludedFields += 1;
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return captured.text;
|
|
174
|
+
};
|
|
175
|
+
return {
|
|
176
|
+
node_id: (stringValue(node.nodeId) ?? "").slice(0, 256),
|
|
177
|
+
parent_id: (stringValue(node.parentId) ?? "").slice(0, 256) || null,
|
|
178
|
+
role: axText(node.role),
|
|
179
|
+
name: captureText(node.name),
|
|
180
|
+
description: captureText(node.description),
|
|
181
|
+
ignored: node.ignored === true,
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
for (const node of all.slice(maximum)) {
|
|
185
|
+
for (const value of [node.name, node.description]) {
|
|
186
|
+
if (stringValue(recordValue(value)?.value) === undefined)
|
|
187
|
+
continue;
|
|
188
|
+
excludedFields += 1;
|
|
189
|
+
if (options.includeText)
|
|
190
|
+
truncatedFields += 1;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
total: all.length,
|
|
195
|
+
nodes,
|
|
196
|
+
textCapture: {
|
|
197
|
+
status: options.unavailable
|
|
198
|
+
? "unavailable"
|
|
199
|
+
: !options.includeText
|
|
200
|
+
? "not_approved"
|
|
201
|
+
: truncatedFields > 0
|
|
202
|
+
? "truncated"
|
|
203
|
+
: "included",
|
|
204
|
+
retained_bytes: retainedBytes,
|
|
205
|
+
excluded_fields: excludedFields,
|
|
206
|
+
truncated_fields: truncatedFields,
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
const axText = (value) => boundedText(recordValue(value)?.value, 1_024);
|
|
211
|
+
const indexedString = (strings, index) => {
|
|
212
|
+
const integer = numberValue(index);
|
|
213
|
+
return integer === undefined ? "" : (strings[Math.trunc(integer)] ?? "");
|
|
214
|
+
};
|
|
215
|
+
const numberArray = (value) => Array.isArray(value)
|
|
216
|
+
? value.flatMap((item) => {
|
|
217
|
+
const number = numberValue(item);
|
|
218
|
+
return number === undefined ? [] : [number];
|
|
219
|
+
})
|
|
220
|
+
: [];
|
|
221
|
+
const domMetadata = (strings, attributes, documentUrl, nodeIndex, nodeName, allowedOrigins) => {
|
|
222
|
+
const pairs = new Map();
|
|
223
|
+
for (let index = 0; index + 1 < attributes.length; index += 2) {
|
|
224
|
+
const name = indexedString(strings, attributes[index]).toLowerCase();
|
|
225
|
+
const value = indexedString(strings, attributes[index + 1]);
|
|
226
|
+
pairs.set(name, value);
|
|
227
|
+
}
|
|
228
|
+
const urls = [];
|
|
229
|
+
for (const attribute of domUrlAttributes) {
|
|
230
|
+
const value = pairs.get(attribute);
|
|
231
|
+
if (value === undefined)
|
|
232
|
+
continue;
|
|
233
|
+
const destination = domDestination(value, documentUrl, allowedOrigins);
|
|
234
|
+
urls.push({
|
|
235
|
+
node_index: nodeIndex,
|
|
236
|
+
attribute,
|
|
237
|
+
url: destination.url,
|
|
238
|
+
destination_scope: destination.scope,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
const rel = (pairs.get("rel") ?? "")
|
|
242
|
+
.toLowerCase()
|
|
243
|
+
.split(/\s+/u)
|
|
244
|
+
.filter((value) => agentRelValues.includes(value));
|
|
245
|
+
const href = urls.find(({ attribute }) => attribute === "href")?.url ?? null;
|
|
246
|
+
const agentHints = nodeName.toLowerCase() === "link" && rel.length > 0
|
|
247
|
+
? rel.map((declaration) => ({
|
|
248
|
+
mechanism: "dom_link_rel",
|
|
249
|
+
declaration,
|
|
250
|
+
url: href,
|
|
251
|
+
trust: "page-declared-untrusted",
|
|
252
|
+
}))
|
|
253
|
+
: [];
|
|
254
|
+
return { urls, agentHints };
|
|
255
|
+
};
|
|
256
|
+
const domDestination = (value, documentUrl, allowedOrigins) => {
|
|
257
|
+
try {
|
|
258
|
+
const parsed = new URL(value, documentUrl);
|
|
259
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
260
|
+
return { url: null, scope: "unsupported" };
|
|
261
|
+
if (!allowedOrigins.has(parsed.origin))
|
|
262
|
+
return { url: null, scope: "outside_policy" };
|
|
263
|
+
return {
|
|
264
|
+
url: allowedSanitizedUrl(parsed.href, allowedOrigins)?.url ?? null,
|
|
265
|
+
scope: "approved",
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return { url: null, scope: "unsupported" };
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
const boundedUtf8 = (value, maximumBytes) => {
|
|
273
|
+
const total = Buffer.byteLength(value);
|
|
274
|
+
if (total <= maximumBytes)
|
|
275
|
+
return { text: value, bytes: total, truncated: false };
|
|
276
|
+
let text = "";
|
|
277
|
+
let bytes = 0;
|
|
278
|
+
for (const character of value) {
|
|
279
|
+
const characterBytes = Buffer.byteLength(character);
|
|
280
|
+
if (bytes + characterBytes > maximumBytes)
|
|
281
|
+
break;
|
|
282
|
+
text += character;
|
|
283
|
+
bytes += characterBytes;
|
|
284
|
+
}
|
|
285
|
+
return { text, bytes, truncated: true };
|
|
286
|
+
};
|
|
287
|
+
const exclusionReasonForUrl = (value) => value === undefined || value === ""
|
|
288
|
+
? "unattributed_origin"
|
|
289
|
+
: isHttpUrl(value)
|
|
290
|
+
? "disallowed_origin"
|
|
291
|
+
: "unsupported_url";
|
|
292
|
+
const domUrlAttributes = [
|
|
293
|
+
"href",
|
|
294
|
+
"src",
|
|
295
|
+
"action",
|
|
296
|
+
"formaction",
|
|
297
|
+
"poster",
|
|
298
|
+
];
|
|
299
|
+
const agentRelValues = ["mcp", "model-context", "ai-plugin", "service-desc"];
|