rea-agents 1.7.0 → 2.0.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.
Files changed (156) hide show
  1. package/README.md +38 -31
  2. package/dist/application/CapabilityInventory.js +18 -0
  3. package/dist/application/CrossVersionInvestigation.js +10 -6
  4. package/dist/application/Doctor.js +56 -6
  5. package/dist/application/ElectronBoundaryAnalysis.js +96 -0
  6. package/dist/application/ElectronBoundaryGraph.js +9 -0
  7. package/dist/application/ElectronBoundaryGraphContext.js +62 -0
  8. package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
  9. package/dist/application/ElectronBoundaryGraphNative.js +86 -0
  10. package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
  11. package/dist/application/EnhancedTools.js +38 -13
  12. package/dist/application/InvestigationProviders.js +30 -0
  13. package/dist/application/JavaScriptApplicationEvidence.js +26 -0
  14. package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
  15. package/dist/application/JavaScriptApplicationService.js +74 -0
  16. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
  17. package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
  18. package/dist/application/JavaScriptArtifactAnalysis.js +7 -1
  19. package/dist/application/JavaScriptArtifactGraphBuilder.js +59 -16
  20. package/dist/application/JavaScriptArtifactGraphEvidence.js +24 -14
  21. package/dist/application/JavaScriptArtifactReconstruction.js +3 -0
  22. package/dist/application/JavaScriptArtifactReconstructionInput.js +2 -83
  23. package/dist/application/JavaScriptReplayPlanning.js +201 -0
  24. package/dist/application/JavaScriptReplayService.js +266 -0
  25. package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
  26. package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
  27. package/dist/application/ManagedApplicationGraphService.js +55 -0
  28. package/dist/application/ManagedMemberComparisonService.js +122 -0
  29. package/dist/application/ManagedNativeVerificationService.js +39 -0
  30. package/dist/application/ManagedReconstructionService.js +58 -0
  31. package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
  32. package/dist/application/PermissionAuthority.js +1 -1
  33. package/dist/application/ProcessSampling.js +15 -11
  34. package/dist/application/ProjectPermissionStore.js +1 -0
  35. package/dist/application/runtime.js +2 -0
  36. package/dist/browser/CdpCaptureEvents.js +49 -0
  37. package/dist/browser/CdpElectronInspection.js +106 -162
  38. package/dist/browser/CdpElectronProvider.js +5 -1
  39. package/dist/browser/CdpElectronScriptEvents.js +69 -0
  40. package/dist/browser/CdpElectronScripts.js +94 -0
  41. package/dist/browser/CdpElectronWorkers.js +56 -0
  42. package/dist/browser/CdpPageCapture.js +8 -4
  43. package/dist/catalogIdentity.js +4 -1
  44. package/dist/cli.js +388 -0
  45. package/dist/cliApplicationCommands.js +62 -0
  46. package/dist/cliCommandNames.js +13 -0
  47. package/dist/cliElectronCommands.js +103 -1
  48. package/dist/cliJsonInput.js +54 -0
  49. package/dist/config.js +83 -0
  50. package/dist/contracts/applicationToolContracts.js +91 -0
  51. package/dist/contracts/artifactToolContracts.js +5 -9
  52. package/dist/contracts/browserToolContracts.js +14 -62
  53. package/dist/contracts/electronToolContracts.js +45 -19
  54. package/dist/contracts/errorSchemas.js +1 -0
  55. package/dist/contracts/investigationExamples.js +0 -6
  56. package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
  57. package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
  58. package/dist/contracts/managedToolContracts.js +153 -0
  59. package/dist/contracts/managedWorkflowExamples.js +346 -0
  60. package/dist/contracts/managedWorkflowToolContracts.js +203 -0
  61. package/dist/contracts/nativeToolContracts.js +2 -6
  62. package/dist/contracts/promptContracts.js +18 -2
  63. package/dist/contracts/toolContractExamples.js +10 -25
  64. package/dist/contracts/toolContracts.js +47 -45
  65. package/dist/contracts/toolEffects.js +160 -0
  66. package/dist/contracts/toolOutputSchemas.js +46 -31
  67. package/dist/doctorRuntime.js +42 -1
  68. package/dist/domain/artifactComparison.js +3 -4
  69. package/dist/domain/browserObservation.js +3 -0
  70. package/dist/domain/bundleComparison.js +4 -4
  71. package/dist/domain/electronObservation.js +13 -0
  72. package/dist/domain/electronStaticAnalysis.js +9 -0
  73. package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
  74. package/dist/domain/electronStaticAnalysisIpc.js +171 -0
  75. package/dist/domain/electronStaticAnalysisNative.js +164 -0
  76. package/dist/domain/electronStaticAnalysisTypes.js +1 -0
  77. package/dist/domain/electronStaticAnalysisValues.js +103 -0
  78. package/dist/domain/errors.js +67 -1
  79. package/dist/domain/functionComparisonSchemas.js +3 -8
  80. package/dist/domain/inputIssueProjection.js +52 -0
  81. package/dist/domain/javascriptApplicationAnalysis.js +155 -0
  82. package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
  83. package/dist/domain/javascriptApplicationEvidenceSchemas.js +18 -0
  84. package/dist/domain/javascriptApplicationGraph.js +2 -1
  85. package/dist/domain/javascriptApplicationGraphSchemas.js +7 -0
  86. package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
  87. package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
  88. package/dist/domain/javascriptApplicationVersionItems.js +221 -0
  89. package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
  90. package/dist/domain/javascriptFeatureSeed.js +140 -0
  91. package/dist/domain/javascriptFeatureTrace.js +232 -0
  92. package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
  93. package/dist/domain/javascriptFeatureTraversal.js +88 -0
  94. package/dist/domain/javascriptNativeHandoff.js +85 -0
  95. package/dist/domain/javascriptReplay.js +361 -0
  96. package/dist/domain/javascriptRuntimeLoadState.js +106 -0
  97. package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
  98. package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
  99. package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
  100. package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
  101. package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
  102. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
  103. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
  104. package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
  105. package/dist/domain/javascriptStaticAnalysis.js +31 -43
  106. package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
  107. package/dist/domain/javascriptStaticAnalysisHelpers.js +10 -0
  108. package/dist/domain/javascriptStaticAnalysisState.js +6 -0
  109. package/dist/domain/managedApplicationGraph.js +556 -0
  110. package/dist/domain/managedArtifact.js +444 -0
  111. package/dist/domain/managedMemberComparison.js +565 -0
  112. package/dist/domain/managedNativeVerification.js +408 -0
  113. package/dist/domain/managedReconstruction.js +183 -0
  114. package/dist/domain/managedRuntimeCorrelation.js +175 -0
  115. package/dist/domain/reconstructionVerificationSchemas.js +1 -3
  116. package/dist/dotnet/ManagedArtifactInspector.js +318 -0
  117. package/dist/dotnet/ManagedMemberInspector.js +887 -0
  118. package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
  119. package/dist/dotnet/ManagedMetadataInventory.js +368 -0
  120. package/dist/dotnet/ManagedMetadataLayout.js +322 -0
  121. package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
  122. package/dist/dotnet/ManagedPeReader.js +157 -0
  123. package/dist/dotnet/ManagedReaderFailure.js +10 -0
  124. package/dist/dotnet/ManagedStaticProvider.js +203 -0
  125. package/dist/generatedPackageMetadata.js +2 -2
  126. package/dist/ghidra/GhidraProvider.js +6 -0
  127. package/dist/main.js +14 -0
  128. package/dist/replay/JavaScriptReplayWorker.js +286 -0
  129. package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
  130. package/dist/replay/LinuxRuntimeClosure.js +46 -0
  131. package/dist/replay/LinuxSeccompPolicy.js +40 -0
  132. package/dist/replay/ReplayWorkerProtocol.js +55 -0
  133. package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
  134. package/dist/server/createServer.js +52 -0
  135. package/dist/server/registerApplicationTools.js +115 -0
  136. package/dist/server/registerArtifactComparisonTool.js +16 -9
  137. package/dist/server/registerBrowserTools.js +33 -8
  138. package/dist/server/registerBundleComparisonTool.js +17 -20
  139. package/dist/server/registerElectronTools.js +45 -3
  140. package/dist/server/registerEnhancedTools.js +58 -13
  141. package/dist/server/registerEvidenceTools.js +5 -1
  142. package/dist/server/registerFunctionComparisonTool.js +14 -7
  143. package/dist/server/registerInvestigationTools.js +21 -17
  144. package/dist/server/registerManagedTools.js +6 -0
  145. package/dist/server/registerManagedWorkflowTools.js +277 -0
  146. package/dist/server/registerOfficialTools.js +8 -8
  147. package/dist/server/registerProcessComparisonTool.js +25 -47
  148. package/dist/server/registerSessionStatusTool.js +7 -7
  149. package/dist/server/registerSessionTools.js +51 -86
  150. package/dist/server/sessionEvidence.js +14 -19
  151. package/dist/server/toolInputValidation.js +77 -0
  152. package/dist/server/toolRegistrationOptions.js +79 -2
  153. package/dist/server/toolResult.js +25 -17
  154. package/package.json +12 -4
  155. package/scripts/rea.mjs +1 -1
  156. package/skills/rea-analysis/SKILL.md +59 -3
@@ -0,0 +1,164 @@
1
+ import * as t from "@babel/types";
2
+ import { addLocatedFinding } from "./javascriptStaticAnalysisFindings.js";
3
+ import { argumentNode, calleeName, compareCodePoints, propertyName, range, stringValue, } from "./javascriptStaticAnalysisHelpers.js";
4
+ const MAX_NATIVE_MEMBERS = 64;
5
+ /** Inspect JavaScript-side imports and re-exports of native .node addons. */
6
+ export const inspectElectronNativeNode = (node, context) => {
7
+ if (t.isImportDeclaration(node))
8
+ inspectImport(node, context);
9
+ else if (t.isExportNamedDeclaration(node))
10
+ inspectNamedExport(node, context);
11
+ else if (t.isExportAllDeclaration(node))
12
+ inspectExportAll(node, context);
13
+ else if (t.isVariableDeclarator(node))
14
+ inspectRequireBinding(node, context);
15
+ else if (t.isAssignmentExpression(node))
16
+ inspectReExport(node, context);
17
+ };
18
+ const inspectImport = (node, context) => {
19
+ if (!isNativeSpecifier(node.source.value))
20
+ return;
21
+ const members = node.specifiers.map((specifier) => {
22
+ if (t.isImportDefaultSpecifier(specifier))
23
+ return "default";
24
+ if (t.isImportNamespaceSpecifier(specifier))
25
+ return "*";
26
+ return propertyName(specifier.imported) || "[dynamic-import]";
27
+ });
28
+ addBinding({
29
+ context,
30
+ node,
31
+ specifier: node.source.value,
32
+ kind: "import",
33
+ members,
34
+ });
35
+ };
36
+ const inspectNamedExport = (node, context) => {
37
+ if (node.source === null ||
38
+ node.source === undefined ||
39
+ !isNativeSpecifier(node.source.value))
40
+ return;
41
+ const members = node.specifiers.map((specifier) => {
42
+ if (t.isExportSpecifier(specifier))
43
+ return propertyName(specifier.local);
44
+ return "*";
45
+ });
46
+ addBinding({
47
+ context,
48
+ node,
49
+ specifier: node.source.value,
50
+ kind: "re-export",
51
+ members,
52
+ });
53
+ };
54
+ const inspectExportAll = (node, context) => {
55
+ if (node.source === undefined || !isNativeSpecifier(node.source.value))
56
+ return;
57
+ addBinding({
58
+ context,
59
+ node,
60
+ specifier: node.source.value,
61
+ kind: "re-export",
62
+ members: ["*"],
63
+ });
64
+ };
65
+ const inspectRequireBinding = (node, context) => {
66
+ const required = nativeRequire(node.init);
67
+ if (required === undefined)
68
+ return;
69
+ addBinding({
70
+ context,
71
+ node,
72
+ specifier: required.specifier,
73
+ kind: "require",
74
+ members: required.member === null ? bindingMembers(node.id) : [required.member],
75
+ });
76
+ };
77
+ const inspectReExport = (node, context) => {
78
+ const required = nativeRequire(node.right);
79
+ if (required === undefined || !isModuleExport(node.left))
80
+ return;
81
+ addBinding({
82
+ context,
83
+ node,
84
+ specifier: required.specifier,
85
+ kind: "re-export",
86
+ members: [required.member ?? exportedMember(node.left) ?? "*"],
87
+ });
88
+ };
89
+ const addBinding = (input) => {
90
+ const { context, node, specifier, kind } = input;
91
+ const unique = [
92
+ ...new Set(input.members.filter((member) => member !== "")),
93
+ ].sort(compareCodePoints);
94
+ const members = (unique.length === 0 ? ["*"] : unique).slice(0, MAX_NATIVE_MEMBERS);
95
+ const omitted = Math.max(0, unique.length - MAX_NATIVE_MEMBERS);
96
+ if (omitted > 0) {
97
+ context.accumulator.structuralTruncation = true;
98
+ context.accumulator.droppedFindings += omitted;
99
+ }
100
+ addLocatedFinding(context, {
101
+ collection: context.accumulator.nativeAddonBindings,
102
+ key: `native-addon-binding\0${kind}\0${specifier}\0${members.join("\0")}`,
103
+ node,
104
+ value: {
105
+ specifier,
106
+ binding_kind: kind,
107
+ members,
108
+ members_truncated: omitted > 0,
109
+ module_key: null,
110
+ location: range(node),
111
+ },
112
+ });
113
+ };
114
+ const nativeRequire = (node) => {
115
+ if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) {
116
+ const nested = t.isNode(node.object)
117
+ ? nativeRequire(node.object)
118
+ : undefined;
119
+ if (nested === undefined)
120
+ return undefined;
121
+ const member = propertyName(node.property);
122
+ return { ...nested, member: member === "" ? nested.member : member };
123
+ }
124
+ if (!t.isCallExpression(node))
125
+ return undefined;
126
+ const name = calleeName(node.callee);
127
+ if (name !== "require" &&
128
+ !name.endsWith(".require") &&
129
+ !name.includes("__webpack_require__"))
130
+ return undefined;
131
+ const specifier = stringValue(argumentNode(node.arguments[0]));
132
+ return specifier !== undefined && isNativeSpecifier(specifier)
133
+ ? { specifier, member: null }
134
+ : undefined;
135
+ };
136
+ const bindingMembers = (pattern) => {
137
+ if (t.isIdentifier(pattern))
138
+ return ["*"];
139
+ if (!t.isObjectPattern(pattern))
140
+ return ["*"];
141
+ return pattern.properties.map((property) => {
142
+ if (t.isRestElement(property))
143
+ return "*";
144
+ return propertyName(property.key) || "[dynamic-import]";
145
+ });
146
+ };
147
+ const isModuleExport = (node) => {
148
+ const name = calleeName(node);
149
+ return (name === "module.exports" ||
150
+ name.startsWith("module.exports.") ||
151
+ name.startsWith("exports."));
152
+ };
153
+ const exportedMember = (node) => {
154
+ const name = calleeName(node);
155
+ if (name.startsWith("module.exports."))
156
+ return name.slice("module.exports.".length);
157
+ if (name.startsWith("exports."))
158
+ return name.slice("exports.".length);
159
+ return undefined;
160
+ };
161
+ const isNativeSpecifier = (specifier) => {
162
+ const path = specifier.split("#", 1)[0]?.split("?", 1)[0] ?? "";
163
+ return path.toLowerCase().endsWith(".node");
164
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,103 @@
1
+ import * as t from "@babel/types";
2
+ import { compareCodePoints, propertyName, sourceSlice, } from "./javascriptStaticAnalysisHelpers.js";
3
+ const MAX_EXPRESSION_CHARACTERS = 4_096;
4
+ /** Preserve one literal value or the exact bounded inert expression. */
5
+ export const electronStaticValue = (source, node) => {
6
+ const literal = literalValue(node);
7
+ return literal.found
8
+ ? { status: "literal", value: literal.value, expression: null }
9
+ : {
10
+ status: "dynamic",
11
+ value: null,
12
+ expression: boundedExpression(source, node),
13
+ };
14
+ };
15
+ /** Return an actionable bounded expression without evaluating it. */
16
+ export const boundedExpression = (source, node) => {
17
+ if (node === null || node === undefined)
18
+ return "[missing-expression]";
19
+ const expression = sourceSlice(source, node).trim();
20
+ return (expression === "" ? `[${node.type}]` : expression).slice(0, MAX_EXPRESSION_CHARACTERS);
21
+ };
22
+ /** Read one named object property without following spreads or bindings. */
23
+ export const objectProperty = (object, name) => object.properties.find((property) => t.isObjectProperty(property) && propertyName(property.key) === name);
24
+ /** Classify a handler argument while retaining its exact source range. */
25
+ export const handlerKind = (node) => {
26
+ if (node === null || node === undefined)
27
+ return "missing";
28
+ if (t.isArrowFunctionExpression(node) ||
29
+ t.isFunctionExpression(node) ||
30
+ t.isFunctionDeclaration(node))
31
+ return "inline-function";
32
+ if (t.isIdentifier(node))
33
+ return "identifier";
34
+ if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node))
35
+ return "member-expression";
36
+ return "dynamic-expression";
37
+ };
38
+ /** Collect bounded dotted keys from one literal contextBridge API object. */
39
+ export const collectContextBridgeMembers = (node, maximum = 128) => {
40
+ if (node === null || node === undefined)
41
+ return {
42
+ status: "missing",
43
+ members: [],
44
+ unknown: 0,
45
+ omitted: 0,
46
+ };
47
+ if (!t.isObjectExpression(node))
48
+ return {
49
+ status: "dynamic",
50
+ members: [],
51
+ unknown: 1,
52
+ omitted: 0,
53
+ };
54
+ const state = { members: [], unknown: 0 };
55
+ collectMembersAt(node, "", 0, state);
56
+ const members = [...new Set(state.members)].sort(compareCodePoints);
57
+ return {
58
+ status: "object-literal",
59
+ members: members.slice(0, maximum),
60
+ unknown: state.unknown,
61
+ omitted: Math.max(0, members.length - maximum),
62
+ };
63
+ };
64
+ const collectMembersAt = (object, prefix, depth, state) => {
65
+ if (depth >= 8) {
66
+ state.unknown += 1;
67
+ return;
68
+ }
69
+ for (const property of object.properties) {
70
+ if (t.isSpreadElement(property)) {
71
+ state.unknown += 1;
72
+ continue;
73
+ }
74
+ const name = propertyName(property.key);
75
+ if (name === "" || property.computed) {
76
+ state.unknown += 1;
77
+ continue;
78
+ }
79
+ const path = (prefix === "" ? name : `${prefix}.${name}`).slice(0, MAX_EXPRESSION_CHARACTERS);
80
+ state.members.push(path);
81
+ if (t.isObjectProperty(property) && t.isObjectExpression(property.value))
82
+ collectMembersAt(property.value, path, depth + 1, state);
83
+ }
84
+ };
85
+ const literalValue = (node) => {
86
+ if (t.isStringLiteral(node) || t.isNumericLiteral(node))
87
+ return { found: true, value: node.value };
88
+ if (t.isBooleanLiteral(node))
89
+ return { found: true, value: node.value };
90
+ if (t.isNullLiteral(node))
91
+ return { found: true, value: null };
92
+ if (t.isTemplateLiteral(node) && node.expressions.length === 0)
93
+ return {
94
+ found: true,
95
+ value: (node.quasis[0]?.value.cooked ??
96
+ node.quasis[0]?.value.raw ??
97
+ "").slice(0, MAX_EXPRESSION_CHARACTERS),
98
+ };
99
+ if (t.isUnaryExpression(node, { operator: "-" }) &&
100
+ t.isNumericLiteral(node.argument))
101
+ return { found: true, value: -node.argument.value };
102
+ return { found: false };
103
+ };
@@ -28,6 +28,7 @@ const ANALYSIS_ERROR_TAGS = [
28
28
  "NoBinaryOpenError",
29
29
  "BinaryTargetError",
30
30
  "PermissionRequiredError",
31
+ "ReplayPlanStaleError",
31
32
  ];
32
33
  /** Base class for expected analysis, provider, and session failures. */
33
34
  export class AnalysisError extends Error {
@@ -46,10 +47,12 @@ export class AnalysisProtocolError extends AnalysisError {
46
47
  /** Caller input failed provider-neutral application parsing. */
47
48
  export class AnalysisInputError extends AnalysisError {
48
49
  operation;
50
+ issues;
49
51
  _tag = "AnalysisInputError";
50
- constructor(operation, options) {
52
+ constructor(operation, options, issues = []) {
51
53
  super(`Invalid analysis input for ${operation}`, options);
52
54
  this.operation = operation;
55
+ this.issues = issues;
53
56
  }
54
57
  }
55
58
  /** Provider output failed provider-neutral application parsing. */
@@ -196,6 +199,20 @@ export class ArtifactOperationError extends AnalysisError {
196
199
  export class EvidenceIntegrityError extends AnalysisError {
197
200
  _tag = "EvidenceIntegrityError";
198
201
  }
202
+ /** A session Evidence reference is missing or has the wrong semantic identity. */
203
+ export class EvidenceReferenceError extends EvidenceIntegrityError {
204
+ evidenceId;
205
+ reason;
206
+ expected;
207
+ actual;
208
+ constructor(evidenceId, reason, expected, actual) {
209
+ super(`Evidence reference ${reason}: ${evidenceId}`);
210
+ this.evidenceId = evidenceId;
211
+ this.reason = reason;
212
+ this.expected = expected;
213
+ this.actual = actual;
214
+ }
215
+ }
199
216
  /** A bounded evidence ledger cannot accept more records or serialized bytes. */
200
217
  export class EvidenceLimitError extends AnalysisError {
201
218
  limit;
@@ -329,6 +346,17 @@ export class PermissionRequiredError extends AnalysisError {
329
346
  this.restartRequired = restartRequired;
330
347
  }
331
348
  }
349
+ /** Approved replay commitment no longer matches the immediately rebuilt plan. */
350
+ export class ReplayPlanStaleError extends AnalysisError {
351
+ approvedDigest;
352
+ actualDigest;
353
+ _tag = "ReplayPlanStaleError";
354
+ constructor(approvedDigest, actualDigest) {
355
+ super("Controlled replay plan changed before execution");
356
+ this.approvedDigest = approvedDigest;
357
+ this.actualDigest = actualDigest;
358
+ }
359
+ }
332
360
  /** Project expected failures into exhaustive, secret-safe caller fields. */
333
361
  export const projectAnalysisError = (error) => {
334
362
  assertKnownTag(error._tag);
@@ -350,6 +378,8 @@ export const projectAnalysisError = (error) => {
350
378
  };
351
379
  };
352
380
  const errorCode = (error) => {
381
+ if (error instanceof ReplayPlanStaleError)
382
+ return "plan_stale";
353
383
  if (error instanceof PermissionRequiredError)
354
384
  return "permission_required";
355
385
  if (error instanceof ProviderSelectionError &&
@@ -453,9 +483,35 @@ const errorCode = (error) => {
453
483
  case "UnknownRegistryError":
454
484
  case "PermissionRequiredError":
455
485
  throw new TypeError("Unhandled specialized analysis error");
486
+ case "ReplayPlanStaleError":
487
+ throw new TypeError("Unhandled specialized replay error");
456
488
  }
457
489
  };
458
490
  const errorDetails = (error) => {
491
+ if (error instanceof AnalysisInputError && error.issues.length > 0)
492
+ return {
493
+ operation: error.operation,
494
+ issues: error.issues.map((issue) => ({
495
+ path: [...issue.path],
496
+ reason: issue.reason,
497
+ ...(issue.expected === undefined ? {} : { expected: issue.expected }),
498
+ ...(issue.minimum === undefined ? {} : { minimum: issue.minimum }),
499
+ ...(issue.maximum === undefined ? {} : { maximum: issue.maximum }),
500
+ })),
501
+ };
502
+ if (error instanceof EvidenceReferenceError)
503
+ return {
504
+ evidence_id: error.evidenceId,
505
+ reason: error.reason,
506
+ expected: error.expected,
507
+ actual: error.actual,
508
+ };
509
+ if (error instanceof ReplayPlanStaleError)
510
+ return {
511
+ approved_plan_digest: error.approvedDigest,
512
+ actual_plan_digest: error.actualDigest,
513
+ application_code_admitted: false,
514
+ };
459
515
  if (error instanceof PermissionRequiredError)
460
516
  return {
461
517
  capability: error.requested.capability,
@@ -555,12 +611,17 @@ const missingScopeDetails = (scope) => ({
555
611
  ...(scope.mount === undefined ? {} : { mount: scope.mount }),
556
612
  });
557
613
  const RETRYABLE_CODES = new Set([
614
+ "invalid_request",
558
615
  "provider_timeout",
559
616
  "cancelled",
560
617
  "revision_conflict",
561
618
  "provider_unavailable",
562
619
  ]);
563
620
  const remediationAction = (error) => {
621
+ if (error instanceof AnalysisInputError)
622
+ return "Correct the listed arguments and retry.";
623
+ if (error instanceof ReplayPlanStaleError)
624
+ return "Review the rebuilt replay plan and explicitly approve its new digest.";
564
625
  if (error instanceof PermissionRequiredError)
565
626
  return error.elicitationSupported
566
627
  ? "Approve the exact missing scope, then retry the operation."
@@ -568,6 +629,8 @@ const remediationAction = (error) => {
568
629
  return userMessage(error);
569
630
  };
570
631
  const errorCategory = (error) => {
632
+ if (error instanceof ReplayPlanStaleError)
633
+ return "integrity_mismatch";
571
634
  if (error instanceof PermissionRequiredError)
572
635
  return "permission_required";
573
636
  if (error instanceof ProviderSelectionError &&
@@ -632,6 +695,8 @@ const STATIC_ERROR_CATEGORIES = {
632
695
  BinaryTargetError: "unavailable",
633
696
  };
634
697
  const userMessage = (error) => {
698
+ if (error instanceof ReplayPlanStaleError)
699
+ return "The controlled replay plan changed before execution. Refresh the current state and try again.";
635
700
  if (error instanceof PermissionRequiredError)
636
701
  return "This operation needs additional local permission. Review the requested scope and remediation.";
637
702
  if (error instanceof AnalysisInputError)
@@ -770,6 +835,7 @@ const KNOWN_ERROR_TAGS = {
770
835
  NoBinaryOpenError: true,
771
836
  BinaryTargetError: true,
772
837
  PermissionRequiredError: true,
838
+ ReplayPlanStaleError: true,
773
839
  };
774
840
  const assertKnownTag = (tag) => {
775
841
  if (KNOWN_ERROR_TAGS[tag] !== true)
@@ -1,9 +1,4 @@
1
1
  import { z } from "zod";
2
- import { evidenceSchema } from "./evidence.js";
3
- const evidenceInput = z.union([
4
- evidenceSchema,
5
- z.array(evidenceSchema).min(1).max(100),
6
- ]);
7
2
  const comparisonStatusSchema = z.enum([
8
3
  "unchanged",
9
4
  "added",
@@ -25,9 +20,9 @@ const dimensionNameSchema = z.enum([
25
20
  const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
26
21
  const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
27
22
  /** Strict bounded input for explicit function-to-function comparison. */
28
- export const functionComparisonInputSchema = z.object({
29
- left: evidenceInput,
30
- right: evidenceInput,
23
+ export const functionComparisonInputSchema = z.strictObject({
24
+ left_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
25
+ right_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
31
26
  offset: z.number().int().min(0).default(0),
32
27
  limit: z.number().int().min(1).max(100).default(100),
33
28
  unknown_registry_approved: z.literal(true).optional(),
@@ -0,0 +1,52 @@
1
+ /** Project Zod failures to secret-safe caller correction metadata. */
2
+ export const projectInputIssues = (issues, input) => issues.flatMap((issue) => projectIssue(issue, input));
3
+ const projectIssue = (issue, input) => {
4
+ const path = issue.path.flatMap((part) => typeof part === "string" || typeof part === "number" ? [part] : []);
5
+ if (issue.code === "unrecognized_keys")
6
+ return issue.keys.map((key) => ({
7
+ path: [...path, key],
8
+ reason: "unknown_argument",
9
+ }));
10
+ if (issue.code === "invalid_type")
11
+ return [
12
+ {
13
+ path,
14
+ reason: valueAtPath(input, path) === undefined
15
+ ? "missing_argument"
16
+ : "invalid_type",
17
+ expected: safeExpected(issue.expected),
18
+ },
19
+ ];
20
+ if (issue.code === "too_small")
21
+ return [boundedIssue(path, "minimum", numericBound(issue.minimum))];
22
+ if (issue.code === "too_big")
23
+ return [boundedIssue(path, "maximum", numericBound(issue.maximum))];
24
+ if (issue.code === "invalid_format")
25
+ return [{ path, reason: "invalid_format", expected: issue.format }];
26
+ if (issue.code === "invalid_value")
27
+ return [
28
+ {
29
+ path,
30
+ reason: "invalid_value",
31
+ expected: issue.values.filter(isSafeExpected),
32
+ },
33
+ ];
34
+ return [{ path, reason: "invalid_value" }];
35
+ };
36
+ const valueAtPath = (input, path) => {
37
+ let current = input;
38
+ for (const part of path) {
39
+ if (typeof current !== "object" || current === null)
40
+ return undefined;
41
+ current = current[part];
42
+ }
43
+ return current;
44
+ };
45
+ const numericBound = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
46
+ const isSafeExpected = (value) => value === null || ["string", "number", "boolean"].includes(typeof value);
47
+ const safeExpected = (value) => isSafeExpected(value) ? value : "schema-defined value";
48
+ const boundedIssue = (path, key, value) => ({
49
+ path,
50
+ reason: "out_of_range",
51
+ ...(value === undefined ? {} : { [key]: value }),
52
+ });
@@ -0,0 +1,155 @@
1
+ import { isAbsolute } from "node:path";
2
+ import { z } from "zod";
3
+ import { javascriptApplicationGraphSchema } from "./javascriptApplicationGraph.js";
4
+ const MAX_GRAPH_NODES = 100_000;
5
+ const MAX_GRAPH_EDGES = 200_000;
6
+ const countSchema = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
7
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
8
+ /** Default hard bounds for one local JavaScript application analysis. */
9
+ export const JAVASCRIPT_APPLICATION_ANALYSIS_DEFAULT_LIMITS = {
10
+ max_entries: 8_000,
11
+ max_total_artifact_bytes: 512 * 1_024 * 1_024,
12
+ max_artifact_entry_bytes: 128 * 1_024 * 1_024,
13
+ max_compression_ratio: 1_000,
14
+ max_depth: 64,
15
+ max_path_bytes: 4_096,
16
+ max_text_files: 5_000,
17
+ max_total_text_bytes: 128 * 1_024 * 1_024,
18
+ max_text_file_bytes: 8 * 1_024 * 1_024,
19
+ max_ast_nodes: 2_000_000,
20
+ max_findings: 8_000,
21
+ max_modules: 20_000,
22
+ max_source_map_sources: 5_000,
23
+ max_parse_milliseconds: 30_000,
24
+ };
25
+ /** Combined artifact, text, AST, and graph projection bounds. */
26
+ export const javascriptApplicationAnalysisLimitsSchema = z
27
+ .strictObject({
28
+ max_entries: z.number().int().min(1).max(1_000_000).default(8_000),
29
+ max_total_artifact_bytes: z
30
+ .number()
31
+ .int()
32
+ .min(1)
33
+ .max(Number.MAX_SAFE_INTEGER)
34
+ .default(512 * 1_024 * 1_024),
35
+ max_artifact_entry_bytes: z
36
+ .number()
37
+ .int()
38
+ .min(1)
39
+ .max(Number.MAX_SAFE_INTEGER)
40
+ .default(128 * 1_024 * 1_024),
41
+ max_compression_ratio: z.number().min(1).max(100_000).default(1_000),
42
+ max_depth: z.number().int().min(1).max(100).default(64),
43
+ max_path_bytes: z.number().int().min(1).max(65_535).default(4_096),
44
+ max_text_files: z.number().int().min(1).max(100_000).default(5_000),
45
+ max_total_text_bytes: z
46
+ .number()
47
+ .int()
48
+ .min(1)
49
+ .max(512 * 1_024 * 1_024)
50
+ .default(128 * 1_024 * 1_024),
51
+ max_text_file_bytes: z
52
+ .number()
53
+ .int()
54
+ .min(1)
55
+ .max(64 * 1_024 * 1_024)
56
+ .default(8 * 1_024 * 1_024),
57
+ max_ast_nodes: z.number().int().min(1).max(20_000_000).default(2_000_000),
58
+ max_findings: z.number().int().min(1).max(200_000).default(8_000),
59
+ max_modules: z.number().int().min(1).max(100_000).default(20_000),
60
+ max_source_map_sources: z.number().int().min(1).max(100_000).default(5_000),
61
+ max_parse_milliseconds: z
62
+ .number()
63
+ .int()
64
+ .min(1)
65
+ .max(300_000)
66
+ .default(30_000),
67
+ })
68
+ .superRefine((limits, context) => {
69
+ const projectedNodes = 7 * limits.max_entries +
70
+ 2 * limits.max_findings +
71
+ limits.max_modules +
72
+ limits.max_source_map_sources +
73
+ 1;
74
+ if (projectedNodes > MAX_GRAPH_NODES)
75
+ context.addIssue({
76
+ code: "custom",
77
+ path: ["max_entries"],
78
+ message: "Combined application-analysis limits can exceed the 100000-node graph contract",
79
+ });
80
+ const projectedEdges = 9 * limits.max_entries +
81
+ 6 * limits.max_findings +
82
+ limits.max_modules +
83
+ limits.max_source_map_sources;
84
+ if (projectedEdges > MAX_GRAPH_EDGES)
85
+ context.addIssue({
86
+ code: "custom",
87
+ path: ["max_entries"],
88
+ message: "Combined application-analysis limits can exceed the 200000-edge graph contract",
89
+ });
90
+ });
91
+ /** Public target-free request for bounded static JavaScript application analysis. */
92
+ export const analyzeJavaScriptApplicationInputSchema = z.strictObject({
93
+ input_path: z
94
+ .string()
95
+ .min(1)
96
+ .max(16_384)
97
+ .refine(isAbsolute, "JavaScript application input path must be absolute"),
98
+ format: z.enum(["auto", "asar", "directory"]).default("auto"),
99
+ approved: z.literal(true),
100
+ source_map_read_approved: z.boolean().default(false),
101
+ limits: javascriptApplicationAnalysisLimitsSchema.default(JAVASCRIPT_APPLICATION_ANALYSIS_DEFAULT_LIMITS),
102
+ });
103
+ /** Compact counts for the high-level Electron architecture/security surface. */
104
+ const electronBoundarySummarySchema = z.strictObject({
105
+ browser_windows: countSchema,
106
+ explicit_web_preferences: countSchema,
107
+ preload_entrypoints: countSchema,
108
+ context_bridge_apis: countSchema,
109
+ exposed_api_members: countSchema,
110
+ ipc: z.strictObject({
111
+ operations: countSchema,
112
+ literal_channels: countSchema,
113
+ dynamic_channel_operations: countSchema,
114
+ renderer_transmissions: countSchema,
115
+ renderer_listeners: countSchema,
116
+ main_handlers: countSchema,
117
+ paired_renderer_transmissions: countSchema,
118
+ ambiguous_renderer_transmissions: countSchema,
119
+ unpaired_literal_renderer_transmissions: countSchema,
120
+ }),
121
+ sender_validation_observations: countSchema,
122
+ utility_processes: countSchema,
123
+ resolved_utility_entrypoints: countSchema,
124
+ native_addon_bindings: countSchema,
125
+ resolved_native_addon_bindings: countSchema,
126
+ });
127
+ const reconstructionStatisticsSchema = z.strictObject({
128
+ relevant_files: countSchema,
129
+ nested_asar_containers: countSchema,
130
+ text_files_selected: countSchema,
131
+ text_bytes_read: countSchema,
132
+ omitted_text_files: countSchema,
133
+ limit_omitted_text_files: countSchema,
134
+ policy_filtered_text_files: countSchema,
135
+ invalid_utf8_files: countSchema,
136
+ parsed_javascript_files: countSchema,
137
+ visited_ast_nodes: countSchema,
138
+ findings: countSchema,
139
+ modules: countSchema,
140
+ parse_failures: countSchema,
141
+ truncated_scopes: countSchema,
142
+ });
143
+ /** Strict high-level result returned inside Evidence v2. */
144
+ export const javascriptApplicationAnalysisResultSchema = z.strictObject({
145
+ schema_version: z.literal(1),
146
+ input_path: z.string().min(1).max(16_384),
147
+ format: z.enum(["asar", "directory"]),
148
+ root_artifact_sha256: digestSchema,
149
+ inventory_manifest_id: z.string().regex(/^agm_[a-f0-9]{64}$/u),
150
+ inventory_graph_sha256: digestSchema,
151
+ graph: javascriptApplicationGraphSchema,
152
+ summary: electronBoundarySummarySchema,
153
+ statistics: reconstructionStatisticsSchema,
154
+ limitations: z.array(z.string().min(1).max(4_096)).max(1_000),
155
+ });