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.
- package/README.md +38 -31
- package/dist/application/CapabilityInventory.js +18 -0
- package/dist/application/CrossVersionInvestigation.js +10 -6
- package/dist/application/Doctor.js +56 -6
- package/dist/application/ElectronBoundaryAnalysis.js +96 -0
- package/dist/application/ElectronBoundaryGraph.js +9 -0
- package/dist/application/ElectronBoundaryGraphContext.js +62 -0
- package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
- package/dist/application/ElectronBoundaryGraphNative.js +86 -0
- package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
- package/dist/application/EnhancedTools.js +38 -13
- package/dist/application/InvestigationProviders.js +30 -0
- package/dist/application/JavaScriptApplicationEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
- package/dist/application/JavaScriptApplicationService.js +74 -0
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
- package/dist/application/JavaScriptArtifactAnalysis.js +7 -1
- package/dist/application/JavaScriptArtifactGraphBuilder.js +59 -16
- package/dist/application/JavaScriptArtifactGraphEvidence.js +24 -14
- package/dist/application/JavaScriptArtifactReconstruction.js +3 -0
- package/dist/application/JavaScriptArtifactReconstructionInput.js +2 -83
- package/dist/application/JavaScriptReplayPlanning.js +201 -0
- package/dist/application/JavaScriptReplayService.js +266 -0
- package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
- package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
- package/dist/application/ManagedApplicationGraphService.js +55 -0
- package/dist/application/ManagedMemberComparisonService.js +122 -0
- package/dist/application/ManagedNativeVerificationService.js +39 -0
- package/dist/application/ManagedReconstructionService.js +58 -0
- package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
- package/dist/application/PermissionAuthority.js +1 -1
- package/dist/application/ProcessSampling.js +15 -11
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/runtime.js +2 -0
- package/dist/browser/CdpCaptureEvents.js +49 -0
- package/dist/browser/CdpElectronInspection.js +106 -162
- package/dist/browser/CdpElectronProvider.js +5 -1
- package/dist/browser/CdpElectronScriptEvents.js +69 -0
- package/dist/browser/CdpElectronScripts.js +94 -0
- package/dist/browser/CdpElectronWorkers.js +56 -0
- package/dist/browser/CdpPageCapture.js +8 -4
- package/dist/catalogIdentity.js +4 -1
- package/dist/cli.js +388 -0
- package/dist/cliApplicationCommands.js +62 -0
- package/dist/cliCommandNames.js +13 -0
- package/dist/cliElectronCommands.js +103 -1
- package/dist/cliJsonInput.js +54 -0
- package/dist/config.js +83 -0
- package/dist/contracts/applicationToolContracts.js +91 -0
- package/dist/contracts/artifactToolContracts.js +5 -9
- package/dist/contracts/browserToolContracts.js +14 -62
- package/dist/contracts/electronToolContracts.js +45 -19
- package/dist/contracts/errorSchemas.js +1 -0
- package/dist/contracts/investigationExamples.js +0 -6
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
- package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
- package/dist/contracts/managedToolContracts.js +153 -0
- package/dist/contracts/managedWorkflowExamples.js +346 -0
- package/dist/contracts/managedWorkflowToolContracts.js +203 -0
- package/dist/contracts/nativeToolContracts.js +2 -6
- package/dist/contracts/promptContracts.js +18 -2
- package/dist/contracts/toolContractExamples.js +10 -25
- package/dist/contracts/toolContracts.js +47 -45
- package/dist/contracts/toolEffects.js +160 -0
- package/dist/contracts/toolOutputSchemas.js +46 -31
- package/dist/doctorRuntime.js +42 -1
- package/dist/domain/artifactComparison.js +3 -4
- package/dist/domain/browserObservation.js +3 -0
- package/dist/domain/bundleComparison.js +4 -4
- package/dist/domain/electronObservation.js +13 -0
- package/dist/domain/electronStaticAnalysis.js +9 -0
- package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
- package/dist/domain/electronStaticAnalysisIpc.js +171 -0
- package/dist/domain/electronStaticAnalysisNative.js +164 -0
- package/dist/domain/electronStaticAnalysisTypes.js +1 -0
- package/dist/domain/electronStaticAnalysisValues.js +103 -0
- package/dist/domain/errors.js +67 -1
- package/dist/domain/functionComparisonSchemas.js +3 -8
- package/dist/domain/inputIssueProjection.js +52 -0
- package/dist/domain/javascriptApplicationAnalysis.js +155 -0
- package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
- package/dist/domain/javascriptApplicationEvidenceSchemas.js +18 -0
- package/dist/domain/javascriptApplicationGraph.js +2 -1
- package/dist/domain/javascriptApplicationGraphSchemas.js +7 -0
- package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
- package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
- package/dist/domain/javascriptApplicationVersionItems.js +221 -0
- package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
- package/dist/domain/javascriptFeatureSeed.js +140 -0
- package/dist/domain/javascriptFeatureTrace.js +232 -0
- package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
- package/dist/domain/javascriptFeatureTraversal.js +88 -0
- package/dist/domain/javascriptNativeHandoff.js +85 -0
- package/dist/domain/javascriptReplay.js +361 -0
- package/dist/domain/javascriptRuntimeLoadState.js +106 -0
- package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
- package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
- package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
- package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
- package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
- package/dist/domain/javascriptStaticAnalysis.js +31 -43
- package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +10 -0
- package/dist/domain/javascriptStaticAnalysisState.js +6 -0
- package/dist/domain/managedApplicationGraph.js +556 -0
- package/dist/domain/managedArtifact.js +444 -0
- package/dist/domain/managedMemberComparison.js +565 -0
- package/dist/domain/managedNativeVerification.js +408 -0
- package/dist/domain/managedReconstruction.js +183 -0
- package/dist/domain/managedRuntimeCorrelation.js +175 -0
- package/dist/domain/reconstructionVerificationSchemas.js +1 -3
- package/dist/dotnet/ManagedArtifactInspector.js +318 -0
- package/dist/dotnet/ManagedMemberInspector.js +887 -0
- package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
- package/dist/dotnet/ManagedMetadataInventory.js +368 -0
- package/dist/dotnet/ManagedMetadataLayout.js +322 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
- package/dist/dotnet/ManagedPeReader.js +157 -0
- package/dist/dotnet/ManagedReaderFailure.js +10 -0
- package/dist/dotnet/ManagedStaticProvider.js +203 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraProvider.js +6 -0
- package/dist/main.js +14 -0
- package/dist/replay/JavaScriptReplayWorker.js +286 -0
- package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
- package/dist/replay/LinuxRuntimeClosure.js +46 -0
- package/dist/replay/LinuxSeccompPolicy.js +40 -0
- package/dist/replay/ReplayWorkerProtocol.js +55 -0
- package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
- package/dist/server/createServer.js +52 -0
- package/dist/server/registerApplicationTools.js +115 -0
- package/dist/server/registerArtifactComparisonTool.js +16 -9
- package/dist/server/registerBrowserTools.js +33 -8
- package/dist/server/registerBundleComparisonTool.js +17 -20
- package/dist/server/registerElectronTools.js +45 -3
- package/dist/server/registerEnhancedTools.js +58 -13
- package/dist/server/registerEvidenceTools.js +5 -1
- package/dist/server/registerFunctionComparisonTool.js +14 -7
- package/dist/server/registerInvestigationTools.js +21 -17
- package/dist/server/registerManagedTools.js +6 -0
- package/dist/server/registerManagedWorkflowTools.js +277 -0
- package/dist/server/registerOfficialTools.js +8 -8
- package/dist/server/registerProcessComparisonTool.js +25 -47
- package/dist/server/registerSessionStatusTool.js +7 -7
- package/dist/server/registerSessionTools.js +51 -86
- package/dist/server/sessionEvidence.js +14 -19
- package/dist/server/toolInputValidation.js +77 -0
- package/dist/server/toolRegistrationOptions.js +79 -2
- package/dist/server/toolResult.js +25 -17
- package/package.json +12 -4
- package/scripts/rea.mjs +1 -1
- 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
|
+
};
|
package/dist/domain/errors.js
CHANGED
|
@@ -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.
|
|
29
|
-
|
|
30
|
-
|
|
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
|
+
});
|