rea-agents 2.4.0 → 3.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 +91 -19
- package/bridge/ghidra/ReaGhidraBridge.java +524 -4
- package/bridge/hopper_bridge.py +313 -28
- package/dist/application/AnalysisClientCleanup.js +14 -0
- package/dist/application/AnalysisContextQueries.js +130 -0
- package/dist/application/AnalysisSnapshotCache.js +4 -2
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
- package/dist/application/ArtifactInventory/scanCanonical.js +1 -1
- package/dist/application/BinarySession.js +20 -8
- package/dist/application/BinarySessionComposition.js +21 -0
- package/dist/application/BinarySessionRecords.js +79 -9
- package/dist/application/BinarySessionStatus.js +31 -1
- package/dist/application/BrowserEvidence.js +14 -0
- package/dist/application/BrowserScenarioCapturePort.js +1 -0
- package/dist/application/BrowserScenarioCaptureService.js +53 -0
- package/dist/application/BrowserScenarioEvidence.js +40 -0
- package/dist/application/CallPathTracing.js +160 -0
- package/dist/application/CapabilityInventory.js +84 -14
- package/dist/application/ClientRegistrationStatus.js +4 -1
- package/dist/application/CompositeProvider.js +10 -1
- package/dist/application/DirectAnalysis.js +13 -4
- package/dist/application/EnhancedLiteralTracing.js +118 -0
- package/dist/application/EnhancedTools.js +71 -127
- package/dist/application/EvidenceLedger.js +45 -16
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
- package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
- package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
- package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
- package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
- package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
- package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
- package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
- package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
- package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
- package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
- package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
- package/dist/application/LazyAnalysisProvider.js +77 -0
- package/dist/application/LoopbackReplay.js +1 -1
- package/dist/application/NativeApiInspection.js +80 -0
- package/dist/application/ProcessReactiveEffects.js +6 -0
- package/dist/application/ProcessReactiveHarness.js +16 -1
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/ReconstructionObligationAuthority.js +72 -0
- package/dist/application/ReconstructionObligationCandidates.js +265 -0
- package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
- package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
- package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
- package/dist/application/ReconstructionObligationLedgerService.js +72 -0
- package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
- package/dist/application/ReconstructionObligationPolicies.js +132 -0
- package/dist/application/ReconstructionReadinessService.js +48 -0
- package/dist/application/SessionProviderRouter.js +6 -6
- package/dist/application/SetupClientConfiguration.js +4 -2
- package/dist/application/runtime.js +26 -9
- package/dist/artifacts/ArtifactProvider.js +87 -22
- package/dist/browser/BrowserScenarioSecrets.js +53 -0
- package/dist/browser/BrowserScenarioSessionPort.js +1 -0
- package/dist/browser/CdpBrowserProvider.js +2 -2
- package/dist/browser/CdpCaptureDocuments.js +57 -0
- package/dist/browser/CdpCaptureStorage.js +71 -29
- package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
- package/dist/browser/CdpEndpoint.js +8 -6
- package/dist/browser/CdpPageCapture.js +2 -0
- package/dist/browser/JavaScriptRuntimeScope.js +38 -0
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +259 -0
- package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
- package/dist/browser/PlaywrightScenarioActions.js +62 -0
- package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
- package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
- package/dist/browser/PlaywrightScenarioEvents.js +301 -0
- package/dist/browser/PlaywrightScenarioSession.js +300 -0
- package/dist/browser/V8InspectorCaptureProjection.js +110 -0
- package/dist/browser/V8InspectorEndpoint.js +72 -0
- package/dist/browser/V8InspectorProvider.js +267 -0
- package/dist/catalogIdentity.js +11 -3
- package/dist/cli/artifactCommands.js +44 -3
- package/dist/cli/coreAnalysisCommands.js +78 -0
- package/dist/cli.js +4 -0
- package/dist/cliApplicationCommands.js +30 -2
- package/dist/cliBrowserAdvancedCommands.js +17 -5
- package/dist/cliBrowserCommands.js +1 -0
- package/dist/cliBrowserScenarioCommands.js +43 -0
- package/dist/cliCommandNames.js +9 -0
- package/dist/cliJavaScriptRuntimeCommands.js +164 -0
- package/dist/cliObservationOptions.js +4 -0
- package/dist/cliPolicyCommands.js +1 -0
- package/dist/config/environment.js +12 -0
- package/dist/config/parseConfig.js +88 -17
- package/dist/contracts/analysisSearchContract.js +14 -0
- package/dist/contracts/applicationToolContracts.js +86 -2
- package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
- package/dist/contracts/artifactToolContracts.js +29 -2
- package/dist/contracts/browserProviderToolContracts.js +7 -0
- package/dist/contracts/browserScenarioToolContracts.js +81 -0
- package/dist/contracts/browserToolContracts.js +88 -98
- package/dist/contracts/enhancedInputs.js +35 -6
- package/dist/contracts/functionInstructionContract.js +8 -0
- package/dist/contracts/functionWorkflowToolContracts.js +30 -0
- package/dist/contracts/hopperMemoryContracts.js +22 -0
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
- package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
- package/dist/contracts/promptContracts.js +14 -15
- package/dist/contracts/reconstructionReadinessExample.js +436 -0
- package/dist/contracts/toolContractExamples.js +8 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +34 -25
- package/dist/contracts/toolEffects.js +25 -0
- package/dist/contracts/toolOutputSchemaGroups.js +120 -30
- package/dist/contracts/toolOutputSchemaPrimitives.js +37 -0
- package/dist/domain/analysisErrorPresentation.js +8 -2
- package/dist/domain/analysisErrorProjection.js +9 -0
- package/dist/domain/artifactInspection.js +347 -0
- package/dist/domain/browserCaptureComparison.js +82 -0
- package/dist/domain/browserObservation.js +8 -1
- package/dist/domain/browserObservationErrors.js +1 -0
- package/dist/domain/browserObservationSchemas.js +26 -0
- package/dist/domain/browserScenario.js +165 -0
- package/dist/domain/browserScenarioCapture.js +57 -0
- package/dist/domain/browserScenarioCaptureValues.js +204 -0
- package/dist/domain/browserScenarioDiff.js +324 -0
- package/dist/domain/browserScenarioDiffValues.js +157 -0
- package/dist/domain/browserScenarioNormalization.js +45 -0
- package/dist/domain/browserScenarioValues.js +372 -0
- package/dist/domain/bytecodeProvider.js +179 -0
- package/dist/domain/conformancePackage.js +169 -0
- package/dist/domain/conformanceReplay.js +104 -0
- package/dist/domain/conformanceTrustGate.js +193 -0
- package/dist/domain/customProtocolCapture.js +137 -0
- package/dist/domain/eventProcessTree.js +214 -0
- package/dist/domain/evidenceBundle.js +47 -0
- package/dist/domain/hopperStartupFailure.js +1 -1
- package/dist/domain/hopperValues.js +14 -1
- package/dist/domain/inputIssueProjection.js +7 -1
- package/dist/domain/javascriptRuntimeObservation.js +170 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
- package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
- package/dist/domain/javascriptSemanticAnalysis.js +13 -16
- package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
- package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
- package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
- package/dist/domain/javascriptSemanticDataEffects.js +284 -0
- package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
- package/dist/domain/javascriptSemanticFingerprints.js +174 -0
- package/dist/domain/javascriptSemanticIr.js +21 -1
- package/dist/domain/javascriptSemanticObjects.js +115 -0
- package/dist/domain/javascriptSemanticPromises.js +290 -0
- package/dist/domain/javascriptSemanticResources.js +136 -0
- package/dist/domain/javascriptSourceParser.js +15 -0
- package/dist/domain/javascriptStaticAnalysis.js +12 -15
- package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
- package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
- package/dist/domain/mobileApplicationInvestigation.js +159 -0
- package/dist/domain/nativeApiBoundary.js +130 -0
- package/dist/domain/objcSwiftMetadata.js +150 -0
- package/dist/domain/packageAnalysis.js +123 -0
- package/dist/domain/peInspection.js +196 -0
- package/dist/domain/processCapture.js +8 -2
- package/dist/domain/processReactiveScenario.js +1 -0
- package/dist/domain/processReactiveTransition.js +9 -4
- package/dist/domain/protocolCapture.js +243 -0
- package/dist/domain/providerCleanupError.js +11 -0
- package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
- package/dist/domain/reconstructionReadiness.js +121 -0
- package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
- package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
- package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
- package/dist/domain/reconstructionReadinessSchemas.js +240 -0
- package/dist/domain/sourceToBundleComparison.js +335 -0
- package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
- package/dist/domain/sourceToBundleSignals.js +202 -0
- package/dist/domain/webCaptureDiff.js +141 -27
- package/dist/domain/webScreenshot.js +1 -1
- package/dist/dotnet/ManagedStaticProvider.js +34 -5
- package/dist/generatedMcpToolCatalog.js +37 -0
- package/dist/generatedPackageMetadata.js +4 -4
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +40 -2
- package/dist/ghidra/GhidraProviderCapabilities.js +47 -38
- package/dist/hopper/HopperCleanup.js +158 -0
- package/dist/hopper/HopperClient.js +93 -99
- package/dist/hopper/HopperProvider.js +61 -30
- package/dist/hopper/HopperRequestQueue.js +215 -0
- package/dist/hopper/HopperResponseStream.js +46 -0
- package/dist/hopper/protocol.js +63 -20
- package/dist/main/transport.js +24 -27
- package/dist/main.js +14 -6
- package/dist/mcpDoctor.js +27 -4
- package/dist/mcpStartupPolicy.js +14 -0
- package/dist/process/ProcessCommandIdentity.js +19 -0
- package/dist/process/ProcessOwnership.js +11 -8
- package/dist/server/DynamicToolAvailability.js +70 -0
- package/dist/server/LazyToolCatalog.js +134 -0
- package/dist/server/createServer.js +92 -154
- package/dist/server/hydrateServerTools.js +191 -0
- package/dist/server/lifecycleProgress.js +10 -0
- package/dist/server/permissionFailure.js +12 -0
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
- package/dist/server/registerApplicationTools/helpers.js +11 -7
- package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
- package/dist/server/registerApplicationTools/readiness.js +55 -0
- package/dist/server/registerApplicationTools.js +6 -0
- package/dist/server/registerArtifactTools.js +9 -1
- package/dist/server/registerBrowserScenarioTool.js +25 -0
- package/dist/server/registerBrowserTools.js +2 -2
- package/dist/server/registerCloseLifecycleTool.js +57 -0
- package/dist/server/registerEnhancedTools.js +28 -7
- package/dist/server/registerEvidenceResources.js +54 -101
- package/dist/server/registerEvidenceTools.js +9 -2
- package/dist/server/registerInvestigationTools.js +1 -2
- package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
- package/dist/server/registerReconstructionObligationLedgerResource.js +32 -0
- package/dist/server/registerReconstructionReadinessResource.js +32 -0
- package/dist/server/registerSessionRecordTools.js +82 -12
- package/dist/server/registerSessionStatusTool.js +11 -8
- package/dist/server/registerSessionTools.js +32 -58
- package/dist/server/sessionAvailabilityPolicy.js +10 -8
- package/dist/server/toolRegistrationOptions.js +21 -6
- package/dist/server/toolResult.js +12 -0
- package/package.json +28 -11
- package/scripts/package-runner-bootstrap.mjs +72 -0
- package/scripts/rea.mjs +11 -1
- package/skills/reverse-engineer-anything/SKILL.md +10 -3
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { semanticStaticPropertyName } from "./javascriptSemanticProjection.js";
|
|
3
|
+
import { resolveSemanticBindingState, } from "./javascriptSemanticState.js";
|
|
4
|
+
import { dataEffectLimitReached, outerDataEffectBinding, traverseDataEffects, } from "./javascriptSemanticDataEffectHelpers.js";
|
|
5
|
+
import { range } from "./javascriptStaticAnalysisHelpers.js";
|
|
6
|
+
const ACQUIRE_METHODS = [
|
|
7
|
+
"open",
|
|
8
|
+
"openSync",
|
|
9
|
+
"createReadStream",
|
|
10
|
+
"createWriteStream",
|
|
11
|
+
"connect",
|
|
12
|
+
"createConnection",
|
|
13
|
+
];
|
|
14
|
+
const RELEASE_METHODS = ["close", "destroy", "end"];
|
|
15
|
+
/** Recover built-in filesystem/network acquisition and local release handles. */
|
|
16
|
+
export const collectJavaScriptSemanticResources = (program, state, callables) => {
|
|
17
|
+
const context = {
|
|
18
|
+
state,
|
|
19
|
+
callableIds: new Set(callables.map(({ callableId }) => callableId)),
|
|
20
|
+
callableStack: [],
|
|
21
|
+
ancestors: [],
|
|
22
|
+
};
|
|
23
|
+
const acquisitions = collectAcquisitions(program, context);
|
|
24
|
+
return [
|
|
25
|
+
...acquisitions.map(immutableAcquisition),
|
|
26
|
+
...collectReleases(program, context, acquisitions),
|
|
27
|
+
];
|
|
28
|
+
};
|
|
29
|
+
const collectAcquisitions = (program, context) => {
|
|
30
|
+
const output = [];
|
|
31
|
+
traverseDataEffects(program, context, (node) => {
|
|
32
|
+
if (!t.isCallExpression(node) && !t.isOptionalCallExpression(node))
|
|
33
|
+
return;
|
|
34
|
+
const method = acquisitionMethod(node.callee, context.state);
|
|
35
|
+
if (method === null)
|
|
36
|
+
return;
|
|
37
|
+
if (resourceLimitReached(output.length, context.state))
|
|
38
|
+
return;
|
|
39
|
+
output.push({
|
|
40
|
+
node,
|
|
41
|
+
method,
|
|
42
|
+
ownerCallableId: context.callableStack.at(-1) ?? null,
|
|
43
|
+
resultBindingId: outerDataEffectBinding(node, context),
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
return output;
|
|
47
|
+
};
|
|
48
|
+
const collectReleases = (program, context, acquisitions) => {
|
|
49
|
+
const byBinding = new Map();
|
|
50
|
+
for (const acquisition of acquisitions) {
|
|
51
|
+
if (acquisition.resultBindingId === null)
|
|
52
|
+
continue;
|
|
53
|
+
const existing = byBinding.get(acquisition.resultBindingId) ?? [];
|
|
54
|
+
existing.push(acquisition);
|
|
55
|
+
byBinding.set(acquisition.resultBindingId, existing);
|
|
56
|
+
}
|
|
57
|
+
const output = [];
|
|
58
|
+
traverseDataEffects(program, context, (node) => {
|
|
59
|
+
if (!t.isCallExpression(node) && !t.isOptionalCallExpression(node))
|
|
60
|
+
return;
|
|
61
|
+
const member = memberCallee(node);
|
|
62
|
+
if (member === null || !t.isIdentifier(member.object))
|
|
63
|
+
return;
|
|
64
|
+
const method = RELEASE_METHODS.find((candidate) => candidate ===
|
|
65
|
+
semanticStaticPropertyName(member.property, member.computed));
|
|
66
|
+
if (method === undefined)
|
|
67
|
+
return;
|
|
68
|
+
const binding = resolveSemanticBindingState(context.state, member.object, member.object.name);
|
|
69
|
+
const linked = binding === undefined ? [] : (byBinding.get(binding.bindingId) ?? []);
|
|
70
|
+
if (linked.length === 0)
|
|
71
|
+
return;
|
|
72
|
+
if (resourceLimitReached(acquisitions.length + output.length, context.state))
|
|
73
|
+
return;
|
|
74
|
+
output.push({
|
|
75
|
+
resourceId: `resource:release:${String(node.start ?? -1)}:${String(node.end ?? -1)}`,
|
|
76
|
+
kind: "release",
|
|
77
|
+
method,
|
|
78
|
+
location: range(node),
|
|
79
|
+
ownerCallableId: context.callableStack.at(-1) ?? null,
|
|
80
|
+
resultBindingId: binding?.bindingId ?? null,
|
|
81
|
+
linkedResourceIds: linked.map(resourceCandidateId).sort(),
|
|
82
|
+
resolution: linked.length === 1 ? "complete" : "partial",
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return output;
|
|
86
|
+
};
|
|
87
|
+
const immutableAcquisition = (candidate) => ({
|
|
88
|
+
resourceId: resourceCandidateId(candidate),
|
|
89
|
+
kind: "acquire",
|
|
90
|
+
method: candidate.method,
|
|
91
|
+
location: range(candidate.node),
|
|
92
|
+
ownerCallableId: candidate.ownerCallableId,
|
|
93
|
+
resultBindingId: candidate.resultBindingId,
|
|
94
|
+
linkedResourceIds: [],
|
|
95
|
+
resolution: "complete",
|
|
96
|
+
});
|
|
97
|
+
const acquisitionMethod = (callee, state) => {
|
|
98
|
+
if (t.isIdentifier(callee)) {
|
|
99
|
+
const binding = resolveSemanticBindingState(state, callee, callee.name);
|
|
100
|
+
return methodFromBinding(binding);
|
|
101
|
+
}
|
|
102
|
+
if (!t.isMemberExpression(callee) && !t.isOptionalMemberExpression(callee))
|
|
103
|
+
return null;
|
|
104
|
+
const method = ACQUIRE_METHODS.find((candidate) => candidate ===
|
|
105
|
+
semanticStaticPropertyName(callee.property, callee.computed));
|
|
106
|
+
if (method === undefined || !t.isIdentifier(callee.object))
|
|
107
|
+
return null;
|
|
108
|
+
const binding = resolveSemanticBindingState(state, callee.object, callee.object.name);
|
|
109
|
+
return resourceNamespace(binding) ? method : null;
|
|
110
|
+
};
|
|
111
|
+
const methodFromBinding = (binding) => {
|
|
112
|
+
for (const { specifier, importedPath } of binding?.directOrigins ?? []) {
|
|
113
|
+
if (!resourceModule(specifier))
|
|
114
|
+
continue;
|
|
115
|
+
const method = ACQUIRE_METHODS.find((candidate) => candidate === importedPath.at(-1));
|
|
116
|
+
if (method !== undefined)
|
|
117
|
+
return method;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
};
|
|
121
|
+
const resourceNamespace = (binding) => binding?.directOrigins.some(({ specifier, importedPath }) => resourceModule(specifier) && importedPath.length === 0) ?? false;
|
|
122
|
+
const resourceModule = (specifier) => [
|
|
123
|
+
"fs",
|
|
124
|
+
"fs/promises",
|
|
125
|
+
"net",
|
|
126
|
+
"node:fs",
|
|
127
|
+
"node:fs/promises",
|
|
128
|
+
"node:net",
|
|
129
|
+
"node:tls",
|
|
130
|
+
"tls",
|
|
131
|
+
].includes(specifier);
|
|
132
|
+
const resourceLimitReached = (retained, state) => dataEffectLimitReached(retained, state.limits.maxResourceOperations, "maxResourceOperations", state);
|
|
133
|
+
const resourceCandidateId = (candidate) => `resource:acquire:${String(candidate.node.start ?? -1)}:${String(candidate.node.end ?? -1)}`;
|
|
134
|
+
const memberCallee = (node) => t.isMemberExpression(node.callee) || t.isOptionalMemberExpression(node.callee)
|
|
135
|
+
? node.callee
|
|
136
|
+
: null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { parse } from "@babel/parser";
|
|
2
|
+
/** Parse JavaScript or TypeScript once without retaining unused comment nodes. */
|
|
3
|
+
export const parseJavaScriptSource = (source) => {
|
|
4
|
+
try {
|
|
5
|
+
return parse(source, {
|
|
6
|
+
sourceType: "unambiguous",
|
|
7
|
+
errorRecovery: true,
|
|
8
|
+
attachComment: false,
|
|
9
|
+
plugins: ["jsx", "typescript"],
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
import { parse } from "@babel/parser";
|
|
2
1
|
import * as t from "@babel/types";
|
|
3
2
|
import { inspectElectronStaticNode } from "./electronStaticAnalysis.js";
|
|
4
3
|
import { detectVendors, failedJavaScriptStaticAnalysis, registrationKey, sortedUnique, stringValue, } from "./javascriptStaticAnalysisHelpers.js";
|
|
5
4
|
import { addReference, addSourceMapDirectives, inspectCall, inspectRouteProperty, inspectRoleProperty, } from "./javascriptStaticAnalysisCalls.js";
|
|
6
|
-
import { inspectBundlerRegistration } from "./javascriptStaticAnalysisBundler.js";
|
|
5
|
+
import { inspectBundlerRegistration, inspectEsbuildWrapper, } from "./javascriptStaticAnalysisBundler.js";
|
|
7
6
|
import { finalizeLocatedFindings } from "./javascriptStaticAnalysisFindings.js";
|
|
8
7
|
import { createJavaScriptAnalysisAccumulator, } from "./javascriptStaticAnalysisState.js";
|
|
8
|
+
import { parseJavaScriptSource, } from "./javascriptSourceParser.js";
|
|
9
9
|
/** Parse one bounded JavaScript artifact and recover static structure only. */
|
|
10
10
|
export const analyzeJavaScriptStaticSource = (source, limits) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
return failedJavaScriptStaticAnalysis();
|
|
21
|
-
}
|
|
11
|
+
const file = parseJavaScriptSource(source);
|
|
12
|
+
return file === null
|
|
13
|
+
? failedJavaScriptStaticAnalysis()
|
|
14
|
+
: analyzeParsedJavaScriptStaticSource(source, file, limits);
|
|
15
|
+
};
|
|
16
|
+
/** Recover static structure from an already parsed JavaScript artifact. */
|
|
17
|
+
export const analyzeParsedJavaScriptStaticSource = (source, file, limits) => {
|
|
22
18
|
const accumulator = createJavaScriptAnalysisAccumulator();
|
|
23
19
|
traverseStaticSource(source, file, accumulator, limits);
|
|
24
20
|
addSourceMapDirectives(source, accumulator, limits.maxFindings);
|
|
@@ -100,10 +96,11 @@ const inspectNode = (source, node, accumulator, limits) => {
|
|
|
100
96
|
inspectElectronStaticNode(node, findings);
|
|
101
97
|
if (t.isCallExpression(node)) {
|
|
102
98
|
inspectBundlerRegistration(source, node, accumulator, limits);
|
|
103
|
-
|
|
99
|
+
inspectEsbuildWrapper(source, node, accumulator, limits);
|
|
100
|
+
inspectCall(source, node, findings);
|
|
104
101
|
}
|
|
105
102
|
else if (t.isNewExpression(node))
|
|
106
|
-
inspectCall(node, findings);
|
|
103
|
+
inspectCall(source, node, findings);
|
|
107
104
|
if ((t.isImportDeclaration(node) || t.isExportAllDeclaration(node)) &&
|
|
108
105
|
node.source !== undefined)
|
|
109
106
|
addReference(findings, {
|
|
@@ -61,6 +61,44 @@ export const inspectBundlerRegistration = (source, call, accumulator, limits) =>
|
|
|
61
61
|
};
|
|
62
62
|
addBoundedFinding(accumulator, `registration\0${registrationKey(registration)}`, limits.maxFindings, () => accumulator.registrations.push(registration));
|
|
63
63
|
};
|
|
64
|
+
/** Inspect esbuild helper wrappers and recover their literal module tables. */
|
|
65
|
+
export const inspectEsbuildWrapper = (source, call, accumulator, limits) => {
|
|
66
|
+
const name = calleeName(call.callee);
|
|
67
|
+
const wrapperKind = name === "__commonJS" || name.endsWith(".__commonJS")
|
|
68
|
+
? "commonjs"
|
|
69
|
+
: name === "__esm" || name.endsWith(".__esm")
|
|
70
|
+
? "esm"
|
|
71
|
+
: null;
|
|
72
|
+
const table = call.arguments[0];
|
|
73
|
+
if (wrapperKind === null || !t.isObjectExpression(table))
|
|
74
|
+
return;
|
|
75
|
+
const recovered = recoverBundlerModules(source, table, accumulator, limits);
|
|
76
|
+
if (recovered.modules.length === 0)
|
|
77
|
+
return;
|
|
78
|
+
if (recovered.omittedAsyncChunkKeys > 0) {
|
|
79
|
+
accumulator.droppedFindings += recovered.omittedAsyncChunkKeys;
|
|
80
|
+
accumulator.structuralTruncation = true;
|
|
81
|
+
}
|
|
82
|
+
accumulator.unknownFindings += recovered.unknownAsyncChunkKeys;
|
|
83
|
+
const registration = {
|
|
84
|
+
bundler: "esbuild",
|
|
85
|
+
runtime: `esbuild-${wrapperKind}`,
|
|
86
|
+
chunk_keys: [`${wrapperKind}@${String(call.start ?? 0)}`],
|
|
87
|
+
omitted_chunk_keys: 0,
|
|
88
|
+
unknown_chunk_keys: 0,
|
|
89
|
+
runtime_require_name: null,
|
|
90
|
+
runtime_module_cache_status: "not-observed",
|
|
91
|
+
entry_module_keys: [],
|
|
92
|
+
omitted_entry_module_keys: 0,
|
|
93
|
+
unknown_entry_module_keys: 0,
|
|
94
|
+
async_chunk_keys: recovered.asyncChunkKeys,
|
|
95
|
+
omitted_async_chunk_keys: recovered.omittedAsyncChunkKeys,
|
|
96
|
+
unknown_async_chunk_keys: recovered.unknownAsyncChunkKeys,
|
|
97
|
+
modules: recovered.modules.sort((left, right) => compareCodePoints(left.module_key, right.module_key)),
|
|
98
|
+
location: range(call),
|
|
99
|
+
};
|
|
100
|
+
addBoundedFinding(accumulator, `registration\0${registrationKey(registration)}`, limits.maxFindings, () => accumulator.registrations.push(registration));
|
|
101
|
+
};
|
|
64
102
|
const recoverBundlerModules = (source, table, accumulator, limits) => {
|
|
65
103
|
const modules = [];
|
|
66
104
|
const asyncChunkKeys = [];
|
|
@@ -2,7 +2,7 @@ import * as t from "@babel/types";
|
|
|
2
2
|
import { addBoundedFinding, addLocatedFinding, moduleAtOffset, } from "./javascriptStaticAnalysisFindings.js";
|
|
3
3
|
import { STATIC_ROUTE_CALL_NAMES, argumentValue, calleeName, endpointArgument as endpointArgumentHelper, prefixedArgument, propertyName, range, rangeForOffsets, sanitizeCandidate, sourceSlice, staticPath, staticPathResolutionContext, storageKind, stringValue, } from "./javascriptStaticAnalysisHelpers.js";
|
|
4
4
|
/** Inspect one call or new expression for references, endpoints, storage, and roles. */
|
|
5
|
-
export const inspectCall = (node, context) => {
|
|
5
|
+
export const inspectCall = (source, node, context) => {
|
|
6
6
|
const { accumulator } = context;
|
|
7
7
|
const name = calleeName(node.callee);
|
|
8
8
|
const first = stringValue(node.arguments[0]);
|
|
@@ -22,6 +22,8 @@ export const inspectCall = (node, context) => {
|
|
|
22
22
|
kind: "dynamic-import",
|
|
23
23
|
specifier: chunkLoad.specifier,
|
|
24
24
|
});
|
|
25
|
+
for (const specifier of vitePreloadDependencySpecifiers(source, node, name))
|
|
26
|
+
addReference(context, { node, kind: "dynamic-import", specifier });
|
|
25
27
|
if (name === "Worker" || name.endsWith(".Worker"))
|
|
26
28
|
addReference(context, { node, kind: "worker", specifier: first });
|
|
27
29
|
if (name.endsWith("serviceWorker.register"))
|
|
@@ -73,6 +75,26 @@ const chunkLoadSpecifier = (node, name, moduleRequireName) => {
|
|
|
73
75
|
return null;
|
|
74
76
|
return { specifier: prefixedArgument("chunk:", node.arguments[0]) };
|
|
75
77
|
};
|
|
78
|
+
const vitePreloadDependencySpecifiers = (source, node, name) => {
|
|
79
|
+
if (name !== "__vite__mapDeps" || !t.isArrayExpression(node.arguments[0]))
|
|
80
|
+
return [];
|
|
81
|
+
const indexes = node.arguments[0].elements.flatMap((element) => t.isNumericLiteral(element) ? [element.value] : []);
|
|
82
|
+
if (indexes.length !== node.arguments[0].elements.length)
|
|
83
|
+
return [];
|
|
84
|
+
const table = vitePreloadDependencyTable(source);
|
|
85
|
+
return indexes.flatMap((index) => table[index] ?? []);
|
|
86
|
+
};
|
|
87
|
+
const vitePreloadDependencyTable = (source) => {
|
|
88
|
+
const match = /\.\s*f\s*\|\|\s*\(\s*[^)]*?\.f\s*=\s*\[([\s\S]*?)\]\s*\)/u.exec(source);
|
|
89
|
+
const body = match?.[1];
|
|
90
|
+
if (body === undefined)
|
|
91
|
+
return [];
|
|
92
|
+
return [
|
|
93
|
+
...body.matchAll(/"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gu),
|
|
94
|
+
]
|
|
95
|
+
.map((literal) => (literal[1] ?? literal[2] ?? "").slice(0, 4_096))
|
|
96
|
+
.slice(0, 256);
|
|
97
|
+
};
|
|
76
98
|
const inspectEndpointCall = (node, name, first, context) => {
|
|
77
99
|
const route = STATIC_ROUTE_CALL_NAMES.some((candidate) => name === candidate || name.endsWith(`.${candidate}`));
|
|
78
100
|
if (route && first !== undefined)
|
|
@@ -294,6 +294,12 @@ export const rangeForOffsets = (source, start, end) => ({
|
|
|
294
294
|
start: pointForOffset(source, start),
|
|
295
295
|
end: pointForOffset(source, end),
|
|
296
296
|
});
|
|
297
|
+
/** Compare two exact source ranges. */
|
|
298
|
+
export const sourceRangesEqual = (left, right) => left !== null &&
|
|
299
|
+
left.start.line === right.start.line &&
|
|
300
|
+
left.start.column === right.start.column &&
|
|
301
|
+
left.end.line === right.end.line &&
|
|
302
|
+
left.end.column === right.end.column;
|
|
297
303
|
const pointForOffset = (source, offset) => {
|
|
298
304
|
const before = source.slice(0, offset);
|
|
299
305
|
const lines = before.split("\n");
|
|
@@ -335,6 +341,8 @@ const vendorPatterns = [
|
|
|
335
341
|
{ name: "webpack", patterns: ["__webpack_require__", "webpackChunk"] },
|
|
336
342
|
{ name: "Rspack", patterns: ["__webpack_require__.f", "rspackChunk"] },
|
|
337
343
|
{ name: "Vite", patterns: ["__vite__", "import.meta.hot"] },
|
|
344
|
+
{ name: "Rollup", patterns: ["ROLLUP_FILE_URL", "import.meta.ROLLUP"] },
|
|
345
|
+
{ name: "esbuild", patterns: ["__commonJS", "__esm"] },
|
|
338
346
|
{ name: "React", patterns: ["React.createElement", "react/jsx-runtime"] },
|
|
339
347
|
{ name: "Vue", patterns: ["__VUE__", "createApp("] },
|
|
340
348
|
{ name: "Next.js", patterns: ["__NEXT_DATA__", "/_next/"] },
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Supported mobile artifact formats. */
|
|
3
|
+
export const mobileArtifactFormatSchema = z.enum([
|
|
4
|
+
"apk",
|
|
5
|
+
"aab",
|
|
6
|
+
"ipa",
|
|
7
|
+
"dex",
|
|
8
|
+
"native_lib",
|
|
9
|
+
]);
|
|
10
|
+
/** Mobile platform. */
|
|
11
|
+
export const mobilePlatformSchema = z.enum([
|
|
12
|
+
"android",
|
|
13
|
+
"ios",
|
|
14
|
+
"cross_platform",
|
|
15
|
+
]);
|
|
16
|
+
/** Android manifest entry. */
|
|
17
|
+
export const androidManifestSchema = z.strictObject({
|
|
18
|
+
package_name: z.string().min(1),
|
|
19
|
+
version_name: z.string().nullable(),
|
|
20
|
+
version_code: z.number().int().nullable(),
|
|
21
|
+
min_sdk: z.number().int().nullable(),
|
|
22
|
+
target_sdk: z.number().int().nullable(),
|
|
23
|
+
permissions: z.array(z.string()).default([]),
|
|
24
|
+
activities: z.array(z.string()).default([]),
|
|
25
|
+
services: z.array(z.string()).default([]),
|
|
26
|
+
receivers: z.array(z.string()).default([]),
|
|
27
|
+
providers: z.array(z.string()).default([]),
|
|
28
|
+
});
|
|
29
|
+
/** iOS app manifest (Info.plist). */
|
|
30
|
+
export const iosManifestSchema = z.strictObject({
|
|
31
|
+
bundle_identifier: z.string().min(1),
|
|
32
|
+
bundle_version: z.string().nullable(),
|
|
33
|
+
short_version: z.string().nullable(),
|
|
34
|
+
minimum_os_version: z.string().nullable(),
|
|
35
|
+
supported_platforms: z.array(z.string()).default([]),
|
|
36
|
+
ui_required_device_capabilities: z.array(z.string()).default([]),
|
|
37
|
+
app_permissions: z.array(z.string()).default([]),
|
|
38
|
+
});
|
|
39
|
+
/** Signing information. */
|
|
40
|
+
export const signingInfoSchema = z.strictObject({
|
|
41
|
+
is_signed: z.boolean().default(false),
|
|
42
|
+
signer_identity: z.string().nullable(),
|
|
43
|
+
certificate_fingerprint: z.string().nullable(),
|
|
44
|
+
signature_scheme: z.string().nullable(),
|
|
45
|
+
is_debuggable: z.boolean().default(false),
|
|
46
|
+
});
|
|
47
|
+
/** Native library entry. */
|
|
48
|
+
export const nativeLibrarySchema = z.strictObject({
|
|
49
|
+
path: z.string().min(1),
|
|
50
|
+
architecture: z.string().nullable(),
|
|
51
|
+
size: z.number().int().nonnegative(),
|
|
52
|
+
digest: z
|
|
53
|
+
.string()
|
|
54
|
+
.regex(/^[a-f0-9]{64}$/u)
|
|
55
|
+
.nullable(),
|
|
56
|
+
});
|
|
57
|
+
/** Resource entry within a mobile artifact. */
|
|
58
|
+
export const mobileResourceSchema = z.strictObject({
|
|
59
|
+
path: z.string().min(1),
|
|
60
|
+
type: z.enum([
|
|
61
|
+
"layout",
|
|
62
|
+
"drawable",
|
|
63
|
+
"string",
|
|
64
|
+
"asset",
|
|
65
|
+
"raw",
|
|
66
|
+
"native_lib",
|
|
67
|
+
"manifest",
|
|
68
|
+
"entitlements",
|
|
69
|
+
"provisioning",
|
|
70
|
+
"other",
|
|
71
|
+
]),
|
|
72
|
+
size: z.number().int().nonnegative(),
|
|
73
|
+
});
|
|
74
|
+
/** Static analysis result for a mobile artifact. */
|
|
75
|
+
export const mobileArtifactManifestSchema = z.strictObject({
|
|
76
|
+
format: mobileArtifactFormatSchema,
|
|
77
|
+
platform: mobilePlatformSchema,
|
|
78
|
+
app_name: z.string().nullable(),
|
|
79
|
+
android_manifest: androidManifestSchema.nullable(),
|
|
80
|
+
ios_manifest: iosManifestSchema.nullable(),
|
|
81
|
+
signing: signingInfoSchema,
|
|
82
|
+
resources: z.array(mobileResourceSchema).default([]),
|
|
83
|
+
native_libraries: z.array(nativeLibrarySchema).default([]),
|
|
84
|
+
total_size: z.number().int().nonnegative(),
|
|
85
|
+
architectures: z.array(z.string()).default([]),
|
|
86
|
+
});
|
|
87
|
+
/** Detect mobile artifact format from file extension. */
|
|
88
|
+
export function detectMobileFormat(path) {
|
|
89
|
+
const lower = path.toLowerCase();
|
|
90
|
+
if (lower.endsWith(".apk"))
|
|
91
|
+
return "apk";
|
|
92
|
+
if (lower.endsWith(".aab"))
|
|
93
|
+
return "aab";
|
|
94
|
+
if (lower.endsWith(".ipa"))
|
|
95
|
+
return "ipa";
|
|
96
|
+
if (lower.endsWith(".dex"))
|
|
97
|
+
return "dex";
|
|
98
|
+
if (lower.endsWith(".so") || lower.endsWith(".dylib") || lower.endsWith(".a"))
|
|
99
|
+
return "native_lib";
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
/** Detect platform from format. */
|
|
103
|
+
export function detectPlatform(format) {
|
|
104
|
+
switch (format) {
|
|
105
|
+
case "apk":
|
|
106
|
+
case "aab":
|
|
107
|
+
case "dex":
|
|
108
|
+
return "android";
|
|
109
|
+
case "ipa":
|
|
110
|
+
return "ios";
|
|
111
|
+
case "native_lib":
|
|
112
|
+
return "cross_platform";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Classify a resource by its path. */
|
|
116
|
+
export function classifyMobileResource(path) {
|
|
117
|
+
const lower = path.toLowerCase();
|
|
118
|
+
if (lower.includes("androidmanifest.xml"))
|
|
119
|
+
return "manifest";
|
|
120
|
+
if (lower.includes(".entitlements"))
|
|
121
|
+
return "entitlements";
|
|
122
|
+
if (lower.includes("provisioning") || lower.includes(".mobileprovision"))
|
|
123
|
+
return "provisioning";
|
|
124
|
+
if (lower.includes("/layout/"))
|
|
125
|
+
return "layout";
|
|
126
|
+
if (lower.includes("/drawable/") || lower.includes("/mipmap/"))
|
|
127
|
+
return "drawable";
|
|
128
|
+
if (lower.includes("/values/"))
|
|
129
|
+
return "string";
|
|
130
|
+
if (lower.includes("assets/") || lower.includes("raw/"))
|
|
131
|
+
return "asset";
|
|
132
|
+
if (lower.endsWith(".so") || lower.endsWith(".dylib"))
|
|
133
|
+
return "native_lib";
|
|
134
|
+
if (lower.endsWith(".xml"))
|
|
135
|
+
return "layout";
|
|
136
|
+
return "other";
|
|
137
|
+
}
|
|
138
|
+
/** Get all native libraries for a specific architecture. */
|
|
139
|
+
export function nativeLibsByArchitecture(manifest, arch) {
|
|
140
|
+
return manifest.native_libraries.filter((l) => l.architecture === arch);
|
|
141
|
+
}
|
|
142
|
+
/** Check if an Android artifact has internet permission. */
|
|
143
|
+
export function hasInternetPermission(manifest) {
|
|
144
|
+
if (!manifest.android_manifest)
|
|
145
|
+
return false;
|
|
146
|
+
return manifest.android_manifest.permissions.some((p) => p.includes("INTERNET") || p.includes("android.permission.INTERNET"));
|
|
147
|
+
}
|
|
148
|
+
/** Get all unique architectures across native libraries. */
|
|
149
|
+
export function getAllArchitectures(manifest) {
|
|
150
|
+
const archs = new Set();
|
|
151
|
+
for (const lib of manifest.native_libraries) {
|
|
152
|
+
if (lib.architecture)
|
|
153
|
+
archs.add(lib.architecture);
|
|
154
|
+
}
|
|
155
|
+
for (const arch of manifest.architectures) {
|
|
156
|
+
archs.add(arch);
|
|
157
|
+
}
|
|
158
|
+
return [...archs];
|
|
159
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const inferenceConfidenceSchema = z.enum(["low", "medium", "high"]);
|
|
3
|
+
const inferenceEvidenceSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
kind: z.enum([
|
|
6
|
+
"signature-source",
|
|
7
|
+
"decompiler-type",
|
|
8
|
+
"type-lock",
|
|
9
|
+
"calling-convention",
|
|
10
|
+
"jump-table",
|
|
11
|
+
]),
|
|
12
|
+
source: z.string().min(1),
|
|
13
|
+
detail: z.string().min(1),
|
|
14
|
+
})
|
|
15
|
+
.strict();
|
|
16
|
+
const inferredBoundaryTypeSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
role: z.enum(["return", "parameter"]),
|
|
19
|
+
ordinal: z.number().int().min(0).nullable(),
|
|
20
|
+
name: z.string().min(1).nullable(),
|
|
21
|
+
data_type: z.string().min(1),
|
|
22
|
+
size_bytes: z.number().int().min(0).nullable(),
|
|
23
|
+
storage: z.string().min(1).nullable(),
|
|
24
|
+
confidence: inferenceConfidenceSchema,
|
|
25
|
+
evidence: z.array(inferenceEvidenceSchema).min(1),
|
|
26
|
+
decompiler_artifacts: z.array(z.enum([
|
|
27
|
+
"recovered-signature",
|
|
28
|
+
"register-or-stack-storage",
|
|
29
|
+
"compiler-generated-variable",
|
|
30
|
+
])),
|
|
31
|
+
})
|
|
32
|
+
.strict();
|
|
33
|
+
const jumpTableDataSourceSchema = z
|
|
34
|
+
.object({
|
|
35
|
+
address: z.string().min(1),
|
|
36
|
+
provenance: z.enum([
|
|
37
|
+
"ghidra-decompiler-load-table",
|
|
38
|
+
"ghidra-dispatch-block-data-reference",
|
|
39
|
+
]),
|
|
40
|
+
entry_size_bytes: z.number().int().min(1).nullable(),
|
|
41
|
+
entry_count: z.number().int().min(0).nullable(),
|
|
42
|
+
confidence: inferenceConfidenceSchema,
|
|
43
|
+
evidence: z.array(inferenceEvidenceSchema).min(1),
|
|
44
|
+
})
|
|
45
|
+
.strict();
|
|
46
|
+
const jumpTableMappingSchema = z
|
|
47
|
+
.object({
|
|
48
|
+
case_value: z.number().int().nullable(),
|
|
49
|
+
target_address: z.string().min(1),
|
|
50
|
+
data_addresses: z.array(z.string().min(1)),
|
|
51
|
+
confidence: inferenceConfidenceSchema,
|
|
52
|
+
evidence: z.array(inferenceEvidenceSchema).min(1),
|
|
53
|
+
})
|
|
54
|
+
.strict();
|
|
55
|
+
const inferredJumpTableSchema = z
|
|
56
|
+
.object({
|
|
57
|
+
dispatch_address: z.string().min(1),
|
|
58
|
+
data_sources: z.array(jumpTableDataSourceSchema),
|
|
59
|
+
data_sources_truncated: z.boolean(),
|
|
60
|
+
mappings: z.array(jumpTableMappingSchema),
|
|
61
|
+
mappings_truncated: z.boolean(),
|
|
62
|
+
limitations: z.array(z.string()),
|
|
63
|
+
})
|
|
64
|
+
.strict();
|
|
65
|
+
const availableNativeApiBoundarySchema = z
|
|
66
|
+
.object({
|
|
67
|
+
available: z.literal(true),
|
|
68
|
+
provenance: z.string().min(1),
|
|
69
|
+
signature_source: z.string().min(1),
|
|
70
|
+
calling_convention: z.string().min(1),
|
|
71
|
+
return_type: inferredBoundaryTypeSchema,
|
|
72
|
+
parameters: z.array(inferredBoundaryTypeSchema),
|
|
73
|
+
parameters_truncated: z.boolean(),
|
|
74
|
+
jump_tables: z.array(inferredJumpTableSchema),
|
|
75
|
+
jump_tables_truncated: z.boolean(),
|
|
76
|
+
pseudocode: z
|
|
77
|
+
.object({
|
|
78
|
+
classification: z.literal("decompiler-generated-non-source"),
|
|
79
|
+
compilable: z.literal(false),
|
|
80
|
+
})
|
|
81
|
+
.strict(),
|
|
82
|
+
decompiler_artifacts: z.array(z.enum([
|
|
83
|
+
"recovered-signature",
|
|
84
|
+
"register-and-stack-variables",
|
|
85
|
+
"compiler-generated-control-flow",
|
|
86
|
+
"pointer-arithmetic",
|
|
87
|
+
"pseudocode",
|
|
88
|
+
])),
|
|
89
|
+
limitations: z.array(z.string()),
|
|
90
|
+
})
|
|
91
|
+
.strict();
|
|
92
|
+
const unavailableNativeApiBoundarySchema = z
|
|
93
|
+
.object({
|
|
94
|
+
available: z.literal(false),
|
|
95
|
+
reason: z.string().min(1),
|
|
96
|
+
residual_unknowns: z.array(z.string().min(1)).min(1),
|
|
97
|
+
})
|
|
98
|
+
.strict();
|
|
99
|
+
/** Provider-neutral native API boundary observations attached to a function dossier. */
|
|
100
|
+
export const nativeApiBoundarySchema = z.discriminatedUnion("available", [
|
|
101
|
+
unavailableNativeApiBoundarySchema,
|
|
102
|
+
availableNativeApiBoundarySchema,
|
|
103
|
+
]);
|
|
104
|
+
const nativeApiInspectionSubstepSchema = z
|
|
105
|
+
.object({
|
|
106
|
+
operation: z.enum([
|
|
107
|
+
"analyze_function",
|
|
108
|
+
"project_native_api_boundary",
|
|
109
|
+
"preserve_residual_unknowns",
|
|
110
|
+
]),
|
|
111
|
+
status: z.enum(["completed", "unsupported"]),
|
|
112
|
+
observations: z.array(z.string().min(1)),
|
|
113
|
+
})
|
|
114
|
+
.strict();
|
|
115
|
+
/** Agent-facing projection of structured native API reconstruction substeps. */
|
|
116
|
+
export const nativeApiInspectionResultSchema = z
|
|
117
|
+
.object({
|
|
118
|
+
schema_version: z.literal(1),
|
|
119
|
+
procedure: z
|
|
120
|
+
.object({
|
|
121
|
+
address: z.string().min(1),
|
|
122
|
+
name: z.string().min(1),
|
|
123
|
+
})
|
|
124
|
+
.strict(),
|
|
125
|
+
boundary: nativeApiBoundarySchema,
|
|
126
|
+
substeps: z.array(nativeApiInspectionSubstepSchema).min(3).max(3),
|
|
127
|
+
unsupported_branches: z.array(z.string().min(1)),
|
|
128
|
+
residual_unknowns: z.array(z.string().min(1)),
|
|
129
|
+
})
|
|
130
|
+
.strict();
|