rea-agents 2.0.0 → 2.2.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 +103 -38
- package/bridge/ghidra/ReaGhidraBridge.java +96 -11
- package/dist/application/AndroidApplicationService.js +17 -0
- package/dist/application/AppleApplicationService.js +17 -0
- package/dist/application/ArtifactExtraction.js +1 -1
- package/dist/application/ArtifactInventory/classify.js +42 -0
- package/dist/application/ArtifactInventory/hash.js +43 -0
- package/dist/application/ArtifactInventory/reader.js +41 -0
- package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
- package/dist/application/ArtifactInventory/scanReader.js +124 -0
- package/dist/application/ArtifactInventory/types.js +10 -0
- package/dist/application/ArtifactInventory.js +9 -351
- package/dist/application/AuthorizedArtifactInventory.js +2 -2
- package/dist/application/BinarySession.js +47 -270
- package/dist/application/BinarySessionExecution.js +28 -0
- package/dist/application/BinarySessionOpen.js +39 -0
- package/dist/application/BinarySessionRecords.js +149 -0
- package/dist/application/BinarySessionStatus.js +75 -0
- package/dist/application/CapabilityInventory.js +24 -2
- package/dist/application/ClientRegistrationStatus.js +11 -4
- package/dist/application/CrossVersionInventory.js +8 -2
- package/dist/application/CrossVersionInvestigation.js +1 -23
- package/dist/application/CrossVersionInvestigationCollections.js +23 -0
- package/dist/application/DirectAnalysis.js +47 -44
- package/dist/application/Doctor.js +156 -238
- package/dist/application/DoctorDiagnostics.js +180 -0
- package/dist/application/ElectronBoundaryAnalysis.js +23 -7
- package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
- package/dist/application/EnhancedToolPagination.js +22 -0
- package/dist/application/EnhancedToolTypes.js +4 -0
- package/dist/application/EnhancedTools.js +20 -37
- package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
- package/dist/application/InventoryProjectionEvidence.js +41 -0
- package/dist/application/InvestigationProviders.js +12 -0
- package/dist/application/InvestigationWorkspaceStore.js +24 -10
- package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
- package/dist/application/JavaScriptArtifactFiles.js +6 -2
- package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
- package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
- package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
- package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
- package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
- package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
- package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
- package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
- package/dist/application/JavaScriptJsonModules.js +39 -0
- package/dist/application/JavaScriptModuleRelationships.js +262 -0
- package/dist/application/JavaScriptReplayPermission.js +16 -0
- package/dist/application/JavaScriptReplayService.js +182 -156
- package/dist/application/LinuxHopper.js +10 -0
- package/dist/application/MacHopper.js +22 -11
- package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
- package/dist/application/PermissionAuthority.js +106 -63
- package/dist/application/ProcessCaptureCapability.js +1 -1
- package/dist/application/ProcessCapturePermission.js +13 -0
- package/dist/application/ProcessCli.js +2 -12
- package/dist/application/ProcessHarness.js +1 -1
- package/dist/application/ProcessPairedExperiment.js +72 -0
- package/dist/application/ProjectPermissionStore.js +48 -9
- package/dist/application/RealHopperAssertions.js +76 -0
- package/dist/application/ReconstructionCoverageService.js +69 -0
- package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
- package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
- package/dist/application/Setup.js +148 -260
- package/dist/application/SetupClientConfiguration.js +224 -0
- package/dist/application/SetupClients.js +24 -2
- package/dist/application/SetupPlan.js +124 -12
- package/dist/application/SetupSkill.js +54 -36
- package/dist/application/SupportedClients.js +1 -0
- package/dist/application/Uninstall.js +12 -3
- package/dist/application/Upgrade.js +16 -1
- package/dist/application/WindowsCapabilities.js +86 -0
- package/dist/artifacts/AsarArtifactReader.js +11 -0
- package/dist/browser/BrowserProcessStartup.js +88 -0
- package/dist/browser/CdpCaptureDocuments.js +12 -8
- package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
- package/dist/browser/CdpCaptureEventHandlers.js +336 -0
- package/dist/browser/CdpCaptureEventHelpers.js +71 -0
- package/dist/browser/CdpCaptureEventState.js +1 -0
- package/dist/browser/CdpCaptureEventTypes.js +1 -0
- package/dist/browser/CdpCaptureEvents.js +29 -496
- package/dist/browser/CdpObservationSession.js +51 -8
- package/dist/browser/CdpPageCapture.js +12 -133
- package/dist/browser/CdpPageCaptureScripts.js +97 -0
- package/dist/browser/CdpPageCaptureWorkers.js +46 -0
- package/dist/browser/CdpWebMcpDiscovery.js +38 -14
- package/dist/browser/PngVisualDiff.js +9 -2
- package/dist/catalogIdentity.js +4 -0
- package/dist/cli/artifactCommands.js +82 -0
- package/dist/cli/coreAnalysisCommands.js +232 -0
- package/dist/cli/managedCommands.js +397 -0
- package/dist/cli/managedProjectGraphCommand.js +24 -0
- package/dist/cli/options.js +9 -0
- package/dist/cli/setupCommands.js +113 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/utilityCommands.js +122 -0
- package/dist/cli.js +10 -768
- package/dist/cliApplicationCommands.js +118 -4
- package/dist/cliBrowserAdvancedCommands.js +73 -16
- package/dist/cliBrowserCommands.js +39 -115
- package/dist/cliCommandNames.js +4 -0
- package/dist/cliElectronCommands.js +33 -62
- package/dist/cliEvidenceCommands.js +13 -2
- package/dist/cliInvestigationCommands.js +53 -10
- package/dist/cliObservationOptions.js +137 -0
- package/dist/cliPolicyCommands.js +80 -46
- package/dist/cliSetup.js +306 -0
- package/dist/config/environment.js +107 -0
- package/dist/config/parseConfig.js +228 -0
- package/dist/config/parsers.js +68 -0
- package/dist/config/permissions.js +30 -0
- package/dist/config/types.js +1 -0
- package/dist/config.js +1 -375
- package/dist/contracts/applicationToolContracts.js +134 -0
- package/dist/contracts/electronToolContracts.js +51 -3
- package/dist/contracts/managedToolContracts.js +8 -3
- package/dist/contracts/managedWorkflowToolContracts.js +3 -3
- package/dist/contracts/providerSelection.js +2 -0
- package/dist/contracts/sessionStatusContract.js +26 -0
- package/dist/contracts/toolContractTypes.js +12 -1
- package/dist/contracts/toolContracts.js +5 -12
- package/dist/contracts/toolEffects.js +14 -0
- package/dist/contracts/toolOutputSchemaGroups.js +274 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +328 -0
- package/dist/contracts/toolOutputSchemas.js +2 -550
- package/dist/domain/analysisErrorPresentation.js +234 -0
- package/dist/domain/analysisErrorProjection.js +284 -0
- package/dist/domain/androidApplication.js +206 -0
- package/dist/domain/appleApplication.js +221 -0
- package/dist/domain/artifactComparison.js +34 -15
- package/dist/domain/binaryTarget.js +87 -37
- package/dist/domain/boundedCartesianProjection.js +15 -0
- package/dist/domain/browserObservation.js +1 -285
- package/dist/domain/browserObservationSchemas.js +286 -0
- package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
- package/dist/domain/errors.js +4 -486
- package/dist/domain/functionComparison.js +4 -165
- package/dist/domain/functionComparisonDimensions.js +164 -0
- package/dist/domain/javascriptApplicationVersionItems.js +18 -6
- package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
- package/dist/domain/javascriptExportInstrumentation.js +97 -0
- package/dist/domain/javascriptFeatureTraversal.js +19 -7
- package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
- package/dist/domain/javascriptSemanticAnalysis.js +386 -0
- package/dist/domain/javascriptSemanticIr.js +30 -0
- package/dist/domain/javascriptSemanticProjection.js +366 -0
- package/dist/domain/javascriptSemanticState.js +40 -0
- package/dist/domain/javascriptSemanticTraversal.js +22 -0
- package/dist/domain/javascriptSemanticValues.js +352 -0
- package/dist/domain/javascriptStaticAnalysis.js +5 -221
- package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
- package/dist/domain/knownPageCoverage.js +21 -0
- package/dist/domain/managedApplicationGraph.js +14 -364
- package/dist/domain/managedApplicationGraphCoverage.js +170 -0
- package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
- package/dist/domain/managedApplicationGraphNodes.js +336 -0
- package/dist/domain/managedArtifact.js +16 -8
- package/dist/domain/managedMemberComparison.js +37 -391
- package/dist/domain/managedMemberComparisonCoverage.js +72 -0
- package/dist/domain/managedMemberComparisonMatch.js +356 -0
- package/dist/domain/managedNativeVerification.js +27 -389
- package/dist/domain/managedNativeVerificationMatch.js +272 -0
- package/dist/domain/managedNativeVerificationSchemas.js +141 -0
- package/dist/domain/managedReconstruction.js +82 -69
- package/dist/domain/managedRuntimeCorrelation.js +12 -3
- package/dist/domain/nodeRuntimeCharacterization.js +66 -0
- package/dist/domain/permissionPolicy.js +2 -0
- package/dist/domain/processComparison.js +19 -0
- package/dist/domain/processPairedExperiment.js +128 -0
- package/dist/domain/reconstructionCoverage.js +387 -0
- package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
- package/dist/domain/reconstructionVerificationSchemas.js +8 -0
- package/dist/domain/replayMachine.js +316 -0
- package/dist/domain/replayMachineRuntime.js +215 -0
- package/dist/domain/runtimeCharacterization.js +85 -0
- package/dist/domain/runtimeIdentification.js +210 -0
- package/dist/domain/webBundleAnalyzer.js +3 -409
- package/dist/domain/webBundleAnalyzerAst.js +77 -0
- package/dist/domain/webBundleAnalyzerInspection.js +307 -0
- package/dist/domain/webBundleAnalyzerResult.js +120 -0
- package/dist/dotnet/ManagedArtifactInspector.js +50 -29
- package/dist/dotnet/ManagedMemberInspector.js +51 -785
- package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
- package/dist/dotnet/ManagedMemberRows.js +120 -0
- package/dist/dotnet/ManagedMetadataInventory.js +42 -306
- package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
- package/dist/dotnet/ManagedMetadataLayout.js +48 -111
- package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
- package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
- package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
- package/dist/dotnet/ManagedPeReader.js +59 -28
- package/dist/evaluation/CodexAgentEval.js +132 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraClient.js +76 -91
- package/dist/ghidra/GhidraClientStartup.js +29 -0
- package/dist/ghidra/GhidraClientWire.js +34 -0
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraDiagnostics.js +6 -1
- package/dist/ghidra/GhidraInstallation.js +20 -15
- package/dist/ghidra/GhidraLauncher.js +93 -29
- package/dist/ghidra/GhidraProvider.js +61 -161
- package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
- package/dist/ghidra/GhidraSessionValues.js +2 -0
- package/dist/ghidra/GhidraSocketConnection.js +44 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
- package/dist/ghidra/GhidraTransport.js +71 -0
- package/dist/identity.js +4 -2
- package/dist/main/messages.js +5 -0
- package/dist/main/reload.js +49 -0
- package/dist/main/shutdown.js +25 -0
- package/dist/main/startup.js +14 -0
- package/dist/main/state.js +26 -0
- package/dist/main/transport.js +61 -0
- package/dist/main/types.js +1 -0
- package/dist/main.js +35 -146
- package/dist/process/PrivateRuntimeRoot.js +3 -2
- package/dist/process/ProcessOwnership.js +35 -0
- package/dist/process/ProviderProcess.js +2 -0
- package/dist/replay/JavaScriptReplayWorker.js +64 -50
- package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
- package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
- package/dist/replay/ReplayOutcome.js +95 -0
- package/dist/replay/ReplayProcessLifecycle.js +71 -0
- package/dist/server/ProcessCaptureElicitation.js +157 -0
- package/dist/server/createServer.js +124 -56
- package/dist/server/javascriptApplicationResult.js +85 -0
- package/dist/server/registerApplicationTools/characterization.js +57 -0
- package/dist/server/registerApplicationTools/compareVersions.js +34 -0
- package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
- package/dist/server/registerApplicationTools/coverage.js +84 -0
- package/dist/server/registerApplicationTools/helpers.js +42 -0
- package/dist/server/registerApplicationTools/traceFeature.js +26 -0
- package/dist/server/registerApplicationTools/types.js +1 -0
- package/dist/server/registerApplicationTools.js +10 -112
- package/dist/server/registerBrowserTools.js +59 -83
- package/dist/server/registerElectronTools.js +48 -49
- package/dist/server/registerEnhancedTools.js +34 -42
- package/dist/server/registerEvidenceResources.js +56 -0
- package/dist/server/registerInvestigationTools/helpers.js +74 -0
- package/dist/server/registerInvestigationTools/types.js +1 -0
- package/dist/server/registerInvestigationTools.js +83 -116
- package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
- package/dist/server/registerManagedTools.js +18 -1
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
- package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
- package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
- package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
- package/dist/server/registerManagedWorkflowTools/types.js +1 -0
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
- package/dist/server/registerManagedWorkflowTools.js +10 -274
- package/dist/server/registerOfficialTools.js +73 -59
- package/dist/server/registerSessionRecordTools.js +137 -0
- package/dist/server/registerSessionStatusTool.js +97 -26
- package/dist/server/registerSessionTools.js +60 -164
- package/dist/server/sessionAvailabilityPolicy.js +10 -0
- package/dist/server/toolRegistrationOptions.js +101 -3
- package/dist/server/toolResult.js +15 -4
- package/package.json +11 -11
- package/skills/reverse-engineer-anything/SKILL.md +76 -0
- package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
- package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
- package/skills/reverse-engineer-anything/references/javascript-applications.md +25 -0
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
- package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
- package/scripts/prepare-node-pty.mjs +0 -35
- package/scripts/rebuild-native.mjs +0 -22
- package/skills/rea-analysis/SKILL.md +0 -222
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import canonicalize from "canonicalize";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { parseArtifactInventoryEvidence } from "./artifactInventoryEvidence.js";
|
|
5
|
+
import { projectBoundedCartesian } from "./boundedCartesianProjection.js";
|
|
6
|
+
import { evidenceSchema } from "./evidence.js";
|
|
7
|
+
const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
8
|
+
const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
|
|
9
|
+
const pathSchema = z.string().min(1).max(4_096);
|
|
10
|
+
const componentSchema = z.strictObject({
|
|
11
|
+
path: pathSchema,
|
|
12
|
+
artifact_id: z.string().regex(/^art_[a-f0-9]{64}$/u),
|
|
13
|
+
sha256: digestSchema,
|
|
14
|
+
format: z.string().min(1).max(100),
|
|
15
|
+
});
|
|
16
|
+
/** Authenticated APK inventory pages projected as one Android application. */
|
|
17
|
+
export const androidApplicationProjectionInputSchema = z.strictObject({
|
|
18
|
+
inventory_evidence: z.array(evidenceSchema).min(1).max(100),
|
|
19
|
+
limits: z
|
|
20
|
+
.strictObject({
|
|
21
|
+
max_components: z.number().int().min(1).max(10_000).default(1_000),
|
|
22
|
+
})
|
|
23
|
+
.default({ max_components: 1_000 }),
|
|
24
|
+
});
|
|
25
|
+
/** Deterministic, execution-free Android application inventory projection. */
|
|
26
|
+
export const androidApplicationProjectionResultSchema = z.strictObject({
|
|
27
|
+
schema_version: z.literal(1),
|
|
28
|
+
projection_id: z.string().regex(/^adp_[a-f0-9]{64}$/u),
|
|
29
|
+
root_sha256: digestSchema,
|
|
30
|
+
root_format: z.literal("apk"),
|
|
31
|
+
source_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
|
|
32
|
+
components: z.strictObject({
|
|
33
|
+
manifests: z.array(componentSchema),
|
|
34
|
+
resources: z.array(componentSchema),
|
|
35
|
+
dex: z.array(componentSchema),
|
|
36
|
+
jvm_classes: z.array(componentSchema),
|
|
37
|
+
native_libraries: z.array(componentSchema),
|
|
38
|
+
javascript: z.array(componentSchema),
|
|
39
|
+
signing: z.array(componentSchema),
|
|
40
|
+
}),
|
|
41
|
+
runtime_families: z.array(z.enum([
|
|
42
|
+
"dalvik-art",
|
|
43
|
+
"java-kotlin",
|
|
44
|
+
"native",
|
|
45
|
+
"javascript",
|
|
46
|
+
"react-native",
|
|
47
|
+
"flutter",
|
|
48
|
+
"unity",
|
|
49
|
+
])),
|
|
50
|
+
bridge_candidates: z.array(z.strictObject({
|
|
51
|
+
managed_path: pathSchema,
|
|
52
|
+
native_path: pathSchema,
|
|
53
|
+
basis: z.enum([
|
|
54
|
+
"managed-and-native-content",
|
|
55
|
+
"jni-library-convention",
|
|
56
|
+
"react-native-convention",
|
|
57
|
+
"flutter-convention",
|
|
58
|
+
"unity-convention",
|
|
59
|
+
]),
|
|
60
|
+
})),
|
|
61
|
+
coverage: z.strictObject({
|
|
62
|
+
status: z.enum(["complete-within-inventory", "partial", "truncated"]),
|
|
63
|
+
inventory_complete: z.boolean(),
|
|
64
|
+
omitted_components: z.number().int().min(0),
|
|
65
|
+
omitted_bridge_candidates: z.number().int().min(0),
|
|
66
|
+
}),
|
|
67
|
+
limitations: z.array(z.string().min(1).max(4_096)).max(100),
|
|
68
|
+
});
|
|
69
|
+
/** Project exact APK paths and hashes without decoding or executing target code. */
|
|
70
|
+
export const projectAndroidApplication = (input) => {
|
|
71
|
+
const parsed = androidApplicationProjectionInputSchema.parse(input);
|
|
72
|
+
const { evidence, inventory } = parseArtifactInventoryEvidence(parsed.inventory_evidence);
|
|
73
|
+
if (inventory.manifest.root_format !== "apk")
|
|
74
|
+
throw new TypeError("Android application projection requires APK Evidence");
|
|
75
|
+
const nodes = new Map(inventory.nodes.map((node) => [node.artifact_id, node]));
|
|
76
|
+
const all = inventory.occurrences
|
|
77
|
+
.filter((occurrence) => occurrence.artifact_id !== null && occurrence.logical_path !== ".")
|
|
78
|
+
.map((occurrence) => {
|
|
79
|
+
const node = nodes.get(occurrence.artifact_id ?? "");
|
|
80
|
+
if (node === undefined)
|
|
81
|
+
throw new TypeError("Android application occurrence has no artifact node");
|
|
82
|
+
return {
|
|
83
|
+
path: occurrence.logical_path,
|
|
84
|
+
artifact_id: node.artifact_id,
|
|
85
|
+
sha256: node.sha256,
|
|
86
|
+
format: node.format,
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
const classified = classify(all);
|
|
90
|
+
const components = retain(classified, parsed.limits.max_components);
|
|
91
|
+
const omittedComponents = count(classified) - count(components);
|
|
92
|
+
const bridgeProjection = bridgeCandidates([...components.dex, ...components.jvm_classes], components.native_libraries, parsed.limits.max_components);
|
|
93
|
+
const limitations = [
|
|
94
|
+
...(!inventory.complete
|
|
95
|
+
? ["Source inventory pages are incomplete; absence is unknown."]
|
|
96
|
+
: []),
|
|
97
|
+
...(omittedComponents > 0
|
|
98
|
+
? [
|
|
99
|
+
`${String(omittedComponents)} component observations were omitted by the projection limit.`,
|
|
100
|
+
]
|
|
101
|
+
: []),
|
|
102
|
+
...(bridgeProjection.omitted > 0
|
|
103
|
+
? [
|
|
104
|
+
`${String(bridgeProjection.omitted)} bridge hypotheses were omitted by the projection limit.`,
|
|
105
|
+
]
|
|
106
|
+
: []),
|
|
107
|
+
"Manifest, resource, signing, and bytecode semantics require a dedicated Android provider; this projection reports exact inventory paths and hashes only.",
|
|
108
|
+
"Bridge candidates are path-based hypotheses, not decoded JNI declarations or observed runtime calls.",
|
|
109
|
+
];
|
|
110
|
+
const withoutId = {
|
|
111
|
+
schema_version: 1,
|
|
112
|
+
root_sha256: inventory.manifest.root_sha256,
|
|
113
|
+
root_format: "apk",
|
|
114
|
+
source_evidence_ids: evidence
|
|
115
|
+
.map(({ evidence_id: id }) => id)
|
|
116
|
+
.sort(compare),
|
|
117
|
+
components,
|
|
118
|
+
runtime_families: runtimeFamilies(all),
|
|
119
|
+
bridge_candidates: bridgeProjection.candidates,
|
|
120
|
+
coverage: {
|
|
121
|
+
status: omittedComponents > 0 || bridgeProjection.omitted > 0
|
|
122
|
+
? "truncated"
|
|
123
|
+
: inventory.complete
|
|
124
|
+
? "complete-within-inventory"
|
|
125
|
+
: "partial",
|
|
126
|
+
inventory_complete: inventory.complete,
|
|
127
|
+
omitted_components: omittedComponents,
|
|
128
|
+
omitted_bridge_candidates: bridgeProjection.omitted,
|
|
129
|
+
},
|
|
130
|
+
limitations,
|
|
131
|
+
};
|
|
132
|
+
return androidApplicationProjectionResultSchema.parse({
|
|
133
|
+
...withoutId,
|
|
134
|
+
projection_id: `adp_${digest(withoutId)}`,
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
const classify = (all) => ({
|
|
138
|
+
manifests: all.filter(({ path, format }) => format === "android-manifest" ||
|
|
139
|
+
/(?:^|\/)AndroidManifest\.xml$/u.test(path)),
|
|
140
|
+
resources: all.filter(({ path, format }) => format === "android-resources" || /(?:^|\/)resources\.arsc$/u.test(path)),
|
|
141
|
+
dex: all.filter(({ path, format }) => format === "dex" || /(?:^|\/)[^/]+\.dex$/iu.test(path)),
|
|
142
|
+
jvm_classes: all.filter(({ path, format }) => format === "jvm-class" || /(?:^|\/)[^/]+\.class$/iu.test(path)),
|
|
143
|
+
native_libraries: all.filter(({ path, format }) => format === "elf" || /(?:^|\/)lib\/[^/]+\/[^/]+\.so$/iu.test(path)),
|
|
144
|
+
javascript: all.filter(({ format }) => format === "javascript-bundle"),
|
|
145
|
+
signing: all.filter(({ path }) => /^META-INF\/[^/]+\.(?:MF|RSA|DSA|EC|SF)$/iu.test(path)),
|
|
146
|
+
});
|
|
147
|
+
const retain = (groups, maximum) => {
|
|
148
|
+
let remaining = maximum;
|
|
149
|
+
return Object.fromEntries(Object.entries(groups).map(([key, values]) => {
|
|
150
|
+
const selected = [...values]
|
|
151
|
+
.sort((left, right) => compare(left.path, right.path))
|
|
152
|
+
.slice(0, remaining);
|
|
153
|
+
remaining -= selected.length;
|
|
154
|
+
return [key, selected];
|
|
155
|
+
}));
|
|
156
|
+
};
|
|
157
|
+
const count = (groups) => Object.values(groups).reduce((total, values) => total + values.length, 0);
|
|
158
|
+
const runtimeFamilies = (all) => {
|
|
159
|
+
const paths = all.map(({ path }) => path.toLowerCase());
|
|
160
|
+
const families = new Set();
|
|
161
|
+
if (all.some(({ path, format }) => format === "dex" || path.endsWith(".dex")))
|
|
162
|
+
families.add("dalvik-art");
|
|
163
|
+
if (all.some(({ path, format }) => format === "jvm-class" || path.endsWith(".class")))
|
|
164
|
+
families.add("java-kotlin");
|
|
165
|
+
if (all.some(({ format }) => format === "elf"))
|
|
166
|
+
families.add("native");
|
|
167
|
+
if (all.some(({ format }) => format === "javascript-bundle"))
|
|
168
|
+
families.add("javascript");
|
|
169
|
+
if (paths.some((path) => path.includes("reactnative") || path.includes("hermes")))
|
|
170
|
+
families.add("react-native");
|
|
171
|
+
if (paths.some((path) => path.includes("libflutter.so")))
|
|
172
|
+
families.add("flutter");
|
|
173
|
+
if (paths.some((path) => path.includes("libunity.so")))
|
|
174
|
+
families.add("unity");
|
|
175
|
+
return [...families].sort(compare);
|
|
176
|
+
};
|
|
177
|
+
const bridgeCandidates = (managed, native, maximum) => {
|
|
178
|
+
const projection = projectBoundedCartesian(managed, native, maximum, (source, target) => ({
|
|
179
|
+
managed_path: source.path,
|
|
180
|
+
native_path: target.path,
|
|
181
|
+
basis: bridgeBasis(target.path),
|
|
182
|
+
}));
|
|
183
|
+
return {
|
|
184
|
+
candidates: projection.values,
|
|
185
|
+
omitted: projection.omitted,
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
const bridgeBasis = (path) => {
|
|
189
|
+
const lower = path.toLowerCase();
|
|
190
|
+
if (lower.includes("react") || lower.includes("hermes"))
|
|
191
|
+
return "react-native-convention";
|
|
192
|
+
if (lower.includes("flutter"))
|
|
193
|
+
return "flutter-convention";
|
|
194
|
+
if (lower.includes("unity"))
|
|
195
|
+
return "unity-convention";
|
|
196
|
+
if (/lib\/[^/]+\/lib[^/]+\.so$/u.test(lower))
|
|
197
|
+
return "jni-library-convention";
|
|
198
|
+
return "managed-and-native-content";
|
|
199
|
+
};
|
|
200
|
+
const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
201
|
+
const digest = (value) => {
|
|
202
|
+
const encoded = canonicalize(value);
|
|
203
|
+
if (encoded === undefined)
|
|
204
|
+
throw new TypeError("Android application projection is not canonical JSON");
|
|
205
|
+
return createHash("sha256").update(encoded).digest("hex");
|
|
206
|
+
};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import canonicalize from "canonicalize";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { parseArtifactInventoryEvidence } from "./artifactInventoryEvidence.js";
|
|
5
|
+
import { projectBoundedCartesian } from "./boundedCartesianProjection.js";
|
|
6
|
+
import { evidenceSchema } from "./evidence.js";
|
|
7
|
+
const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
8
|
+
const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
|
|
9
|
+
const boundedPathSchema = z.string().min(1).max(4_096);
|
|
10
|
+
const componentSchema = z.strictObject({
|
|
11
|
+
path: boundedPathSchema,
|
|
12
|
+
artifact_id: z.string().regex(/^art_[a-f0-9]{64}$/u),
|
|
13
|
+
sha256: digestSchema,
|
|
14
|
+
format: z.string().min(1).max(100),
|
|
15
|
+
});
|
|
16
|
+
/** Authenticated artifact inventory pages projected as one Apple application. */
|
|
17
|
+
export const appleApplicationProjectionInputSchema = z.strictObject({
|
|
18
|
+
inventory_evidence: z.array(evidenceSchema).min(1).max(100),
|
|
19
|
+
limits: z
|
|
20
|
+
.strictObject({
|
|
21
|
+
max_components: z.number().int().min(1).max(10_000).default(1_000),
|
|
22
|
+
})
|
|
23
|
+
.default({ max_components: 1_000 }),
|
|
24
|
+
});
|
|
25
|
+
/** Deterministic, execution-free Apple application inventory projection. */
|
|
26
|
+
export const appleApplicationProjectionResultSchema = z.strictObject({
|
|
27
|
+
schema_version: z.literal(1),
|
|
28
|
+
projection_id: z.string().regex(/^aap_[a-f0-9]{64}$/u),
|
|
29
|
+
root_sha256: digestSchema,
|
|
30
|
+
root_format: z.literal("ipa"),
|
|
31
|
+
source_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
|
|
32
|
+
application_roots: z.array(boundedPathSchema),
|
|
33
|
+
components: z.strictObject({
|
|
34
|
+
bundle_metadata: z.array(componentSchema),
|
|
35
|
+
executables: z.array(componentSchema),
|
|
36
|
+
frameworks: z.array(componentSchema),
|
|
37
|
+
native_libraries: z.array(componentSchema),
|
|
38
|
+
javascript: z.array(componentSchema),
|
|
39
|
+
signing: z.array(componentSchema),
|
|
40
|
+
}),
|
|
41
|
+
runtime_families: z.array(z.enum([
|
|
42
|
+
"native",
|
|
43
|
+
"swift-objective-c",
|
|
44
|
+
"javascript",
|
|
45
|
+
"react-native",
|
|
46
|
+
"flutter",
|
|
47
|
+
"unity",
|
|
48
|
+
])),
|
|
49
|
+
bridge_candidates: z.array(z.strictObject({
|
|
50
|
+
source_path: boundedPathSchema,
|
|
51
|
+
native_path: boundedPathSchema,
|
|
52
|
+
basis: z.enum([
|
|
53
|
+
"javascript-and-native-content",
|
|
54
|
+
"react-native-convention",
|
|
55
|
+
"flutter-convention",
|
|
56
|
+
"unity-convention",
|
|
57
|
+
]),
|
|
58
|
+
})),
|
|
59
|
+
coverage: z.strictObject({
|
|
60
|
+
status: z.enum(["complete-within-inventory", "partial", "truncated"]),
|
|
61
|
+
inventory_complete: z.boolean(),
|
|
62
|
+
omitted_components: z.number().int().min(0),
|
|
63
|
+
omitted_bridge_candidates: z.number().int().min(0),
|
|
64
|
+
}),
|
|
65
|
+
limitations: z.array(z.string().min(1).max(4_096)).max(100),
|
|
66
|
+
});
|
|
67
|
+
/** Project exact IPA paths and hashes without parsing or executing target code. */
|
|
68
|
+
export const projectAppleApplication = (input) => {
|
|
69
|
+
const parsed = appleApplicationProjectionInputSchema.parse(input);
|
|
70
|
+
const { evidence, inventory } = parseArtifactInventoryEvidence(parsed.inventory_evidence);
|
|
71
|
+
if (inventory.manifest.root_format !== "ipa")
|
|
72
|
+
throw new TypeError("Apple application projection requires IPA Evidence");
|
|
73
|
+
const nodes = new Map(inventory.nodes.map((node) => [node.artifact_id, node]));
|
|
74
|
+
const all = inventory.occurrences
|
|
75
|
+
.filter((occurrence) => occurrence.artifact_id !== null && occurrence.logical_path !== ".")
|
|
76
|
+
.map((occurrence) => {
|
|
77
|
+
const node = nodes.get(occurrence.artifact_id ?? "");
|
|
78
|
+
if (node === undefined)
|
|
79
|
+
throw new TypeError("Apple application occurrence has no artifact node");
|
|
80
|
+
return {
|
|
81
|
+
path: occurrence.logical_path,
|
|
82
|
+
artifact_id: node.artifact_id,
|
|
83
|
+
sha256: node.sha256,
|
|
84
|
+
format: node.format,
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
const roots = [
|
|
88
|
+
...new Set(inventory.occurrences.flatMap(({ logical_path: path }) => {
|
|
89
|
+
const match = /^(Payload\/[^/]+\.app)(?:\/|$)/u.exec(path);
|
|
90
|
+
return match?.[1] === undefined ? [] : [match[1]];
|
|
91
|
+
})),
|
|
92
|
+
].sort(compare);
|
|
93
|
+
const classified = classifyComponents(all, roots);
|
|
94
|
+
const retained = retainComponents(classified, parsed.limits.max_components);
|
|
95
|
+
const omitted = componentCount(classified) - componentCount(retained);
|
|
96
|
+
const runtimeFamilies = identifyRuntimeFamilies(all);
|
|
97
|
+
const bridgeProjection = identifyBridgeCandidates(retained.javascript, deduplicateComponents([
|
|
98
|
+
...retained.executables,
|
|
99
|
+
...retained.native_libraries,
|
|
100
|
+
]), parsed.limits.max_components);
|
|
101
|
+
const limitations = projectionLimitations(inventory.complete, omitted, bridgeProjection.omitted, roots);
|
|
102
|
+
const withoutId = {
|
|
103
|
+
schema_version: 1,
|
|
104
|
+
root_sha256: inventory.manifest.root_sha256,
|
|
105
|
+
root_format: "ipa",
|
|
106
|
+
source_evidence_ids: evidence
|
|
107
|
+
.map(({ evidence_id: id }) => id)
|
|
108
|
+
.sort(compare),
|
|
109
|
+
application_roots: roots,
|
|
110
|
+
components: retained,
|
|
111
|
+
runtime_families: runtimeFamilies,
|
|
112
|
+
bridge_candidates: bridgeProjection.candidates,
|
|
113
|
+
coverage: {
|
|
114
|
+
status: omitted > 0 || bridgeProjection.omitted > 0
|
|
115
|
+
? "truncated"
|
|
116
|
+
: inventory.complete
|
|
117
|
+
? "complete-within-inventory"
|
|
118
|
+
: "partial",
|
|
119
|
+
inventory_complete: inventory.complete,
|
|
120
|
+
omitted_components: omitted,
|
|
121
|
+
omitted_bridge_candidates: bridgeProjection.omitted,
|
|
122
|
+
},
|
|
123
|
+
limitations,
|
|
124
|
+
};
|
|
125
|
+
return appleApplicationProjectionResultSchema.parse({
|
|
126
|
+
...withoutId,
|
|
127
|
+
projection_id: `aap_${digest(withoutId)}`,
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
const classifyComponents = (all, roots) => {
|
|
131
|
+
const withinApp = (path) => roots.some((root) => path.startsWith(`${root}/`));
|
|
132
|
+
return {
|
|
133
|
+
bundle_metadata: all.filter(({ path }) => /(?:^|\/)Info\.plist$/u.test(path)),
|
|
134
|
+
executables: all.filter(({ path, format }) => withinApp(path) && ["mach-o", "mach-o-universal"].includes(format)),
|
|
135
|
+
frameworks: all.filter(({ path }) => /\.framework\//u.test(path)),
|
|
136
|
+
native_libraries: all.filter(({ path }) => /\.(?:dylib|so)$/iu.test(path)),
|
|
137
|
+
javascript: all.filter(({ format }) => format === "javascript-bundle"),
|
|
138
|
+
signing: all.filter(({ path }) => /(?:^|\/)(?:embedded\.mobileprovision|_CodeSignature\/CodeResources)$/u.test(path)),
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
const retainComponents = (groups, maximum) => {
|
|
142
|
+
let remaining = maximum;
|
|
143
|
+
return Object.fromEntries(Object.entries(groups).map(([key, values]) => {
|
|
144
|
+
const retained = [...values]
|
|
145
|
+
.sort((left, right) => compare(left.path, right.path))
|
|
146
|
+
.slice(0, remaining);
|
|
147
|
+
remaining -= retained.length;
|
|
148
|
+
return [key, retained];
|
|
149
|
+
}));
|
|
150
|
+
};
|
|
151
|
+
const componentCount = (groups) => Object.values(groups).reduce((total, values) => total + values.length, 0);
|
|
152
|
+
const deduplicateComponents = (values) => [...new Map(values.map((value) => [value.path, value])).values()].sort((left, right) => compare(left.path, right.path));
|
|
153
|
+
const identifyRuntimeFamilies = (all) => {
|
|
154
|
+
const paths = all.map(({ path }) => path.toLowerCase());
|
|
155
|
+
const families = new Set();
|
|
156
|
+
if (all.some(({ format }) => ["mach-o", "mach-o-universal"].includes(format)))
|
|
157
|
+
families.add("native");
|
|
158
|
+
if (paths.some((path) => /\.(?:dylib|framework\/[^/]+)$/u.test(path)))
|
|
159
|
+
families.add("swift-objective-c");
|
|
160
|
+
if (all.some(({ format }) => format === "javascript-bundle"))
|
|
161
|
+
families.add("javascript");
|
|
162
|
+
if (paths.some((path) => path.includes("reactnative") ||
|
|
163
|
+
path.includes("react.framework") ||
|
|
164
|
+
path.includes("hermes")))
|
|
165
|
+
families.add("react-native");
|
|
166
|
+
if (paths.some((path) => path.includes("flutter.framework") || path.includes("app.framework")))
|
|
167
|
+
families.add("flutter");
|
|
168
|
+
if (paths.some((path) => path.includes("unityframework.framework")))
|
|
169
|
+
families.add("unity");
|
|
170
|
+
return [...families].sort(compare);
|
|
171
|
+
};
|
|
172
|
+
const identifyBridgeCandidates = (scripts, native, maximum) => {
|
|
173
|
+
const projection = projectBoundedCartesian(scripts, native, maximum, (script, item) => ({
|
|
174
|
+
source_path: script.path,
|
|
175
|
+
native_path: item.path,
|
|
176
|
+
basis: bridgeBasis(item.path),
|
|
177
|
+
}));
|
|
178
|
+
return {
|
|
179
|
+
candidates: projection.values,
|
|
180
|
+
omitted: projection.omitted,
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
const bridgeBasis = (path) => {
|
|
184
|
+
const lower = path.toLowerCase();
|
|
185
|
+
if (lower.includes("react"))
|
|
186
|
+
return "react-native-convention";
|
|
187
|
+
if (lower.includes("flutter") || lower.includes("app.framework"))
|
|
188
|
+
return "flutter-convention";
|
|
189
|
+
if (lower.includes("unity"))
|
|
190
|
+
return "unity-convention";
|
|
191
|
+
return "javascript-and-native-content";
|
|
192
|
+
};
|
|
193
|
+
const projectionLimitations = (complete, omitted, omittedBridges, roots) => [
|
|
194
|
+
...(!complete
|
|
195
|
+
? ["Source inventory pages are incomplete; absence is unknown."]
|
|
196
|
+
: []),
|
|
197
|
+
...(omitted > 0
|
|
198
|
+
? [
|
|
199
|
+
`${String(omitted)} component observations were omitted by the projection limit.`,
|
|
200
|
+
]
|
|
201
|
+
: []),
|
|
202
|
+
...(omittedBridges > 0
|
|
203
|
+
? [
|
|
204
|
+
`${String(omittedBridges)} bridge hypotheses were omitted by the projection limit.`,
|
|
205
|
+
]
|
|
206
|
+
: []),
|
|
207
|
+
...(roots.length === 0
|
|
208
|
+
? [
|
|
209
|
+
"No Payload/*.app directory was present in the supplied inventory pages.",
|
|
210
|
+
]
|
|
211
|
+
: []),
|
|
212
|
+
"Bundle identifiers and signing claims require dedicated plist and CMS parsing; this projection reports only exact paths and hashes.",
|
|
213
|
+
"Bridge candidates are path-based hypotheses, not observed runtime calls.",
|
|
214
|
+
];
|
|
215
|
+
const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
216
|
+
const digest = (value) => {
|
|
217
|
+
const encoded = canonicalize(value);
|
|
218
|
+
if (encoded === undefined)
|
|
219
|
+
throw new TypeError("Apple application projection is not canonical JSON");
|
|
220
|
+
return createHash("sha256").update(encoded).digest("hex");
|
|
221
|
+
};
|
|
@@ -161,39 +161,58 @@ const compareOccurrences = (left, right, evidenceLinks, complete) => {
|
|
|
161
161
|
}
|
|
162
162
|
return output;
|
|
163
163
|
};
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
164
|
+
const buildArtifactChangeBase = (input) => ({
|
|
165
|
+
logical_path: input.path,
|
|
166
|
+
left_occurrence_id: input.leftOccurrence?.occurrence_id ?? null,
|
|
167
|
+
right_occurrence_id: input.rightOccurrence?.occurrence_id ?? null,
|
|
168
|
+
left_artifact_id: input.leftOccurrence?.artifact_id ?? null,
|
|
169
|
+
right_artifact_id: input.rightOccurrence?.artifact_id ?? null,
|
|
170
|
+
evidence_links: [...input.evidenceLinks],
|
|
171
|
+
});
|
|
172
|
+
const classifyPresence = (input) => {
|
|
173
173
|
if (input.leftOccurrence === undefined)
|
|
174
174
|
return {
|
|
175
|
-
...
|
|
175
|
+
...buildArtifactChangeBase(input),
|
|
176
176
|
classification: input.complete ? "added" : "unknown",
|
|
177
177
|
dimensions: [input.complete ? "content" : "availability"],
|
|
178
178
|
};
|
|
179
179
|
if (input.rightOccurrence === undefined)
|
|
180
180
|
return {
|
|
181
|
-
...
|
|
181
|
+
...buildArtifactChangeBase(input),
|
|
182
182
|
classification: input.complete ? "removed" : "unknown",
|
|
183
183
|
dimensions: [input.complete ? "content" : "availability"],
|
|
184
184
|
};
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
return null;
|
|
186
|
+
};
|
|
187
|
+
const classifyIntegrity = (input, base) => {
|
|
188
|
+
if (input.leftOccurrence?.hash_status === "mismatched" ||
|
|
189
|
+
input.rightOccurrence?.hash_status === "mismatched")
|
|
187
190
|
return {
|
|
188
191
|
...base,
|
|
189
192
|
classification: "contradiction",
|
|
190
193
|
dimensions: ["integrity"],
|
|
191
194
|
};
|
|
192
|
-
|
|
193
|
-
|
|
195
|
+
return null;
|
|
196
|
+
};
|
|
197
|
+
const classifyAvailability = (input, base) => {
|
|
198
|
+
if (input.leftOccurrence?.hash_status !== "verified" ||
|
|
199
|
+
input.rightOccurrence?.hash_status !== "verified" ||
|
|
194
200
|
input.leftNode === undefined ||
|
|
195
201
|
input.rightNode === undefined)
|
|
196
202
|
return { ...base, classification: "unknown", dimensions: ["availability"] };
|
|
203
|
+
return null;
|
|
204
|
+
};
|
|
205
|
+
const classifyPath = (input) => {
|
|
206
|
+
const presence = classifyPresence(input);
|
|
207
|
+
if (presence !== null)
|
|
208
|
+
return presence;
|
|
209
|
+
const base = buildArtifactChangeBase(input);
|
|
210
|
+
const integrity = classifyIntegrity(input, base);
|
|
211
|
+
if (integrity !== null)
|
|
212
|
+
return integrity;
|
|
213
|
+
const availability = classifyAvailability(input, base);
|
|
214
|
+
if (availability !== null)
|
|
215
|
+
return availability;
|
|
197
216
|
const dimensions = changedDimensions(input);
|
|
198
217
|
return dimensions.length === 0
|
|
199
218
|
? null
|