From 82fd2236385138dd71cf0a49be3b417acfc58292 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Tue, 27 Oct 2026 06:43:17 +0800 Subject: [PATCH] refactor(native): consolidate provider-neutral analysis contracts Move shared function, dossier, inventory, reference and output semantics from the legacy Hopper module to native domain owners. Migrate provider, adapter, test and verifier consumers directly. --- scripts/fixtures/managed/function-dossier.mjs | 2 +- scripts/lib/real-hopper-search.d.mts | 2 +- scripts/verify-real-ghidra-com.mjs | 2 +- scripts/verify-real-ghidra-dol.mjs | 2 +- scripts/verify-real-ghidra-dos.mjs | 6 +- .../verify-real-ghidra-mips-relocatable.mjs | 2 +- scripts/verify-real-ghidra-mips.mjs | 2 +- scripts/verify-real-ghidra-ppc.mjs | 2 +- scripts/verify-real-ghidra-riscv.mjs | 2 +- scripts/verify-real-ida.mjs | 2 +- src/application/AnalysisContextQueries.ts | 6 +- src/application/AnalysisDocument.ts | 2 +- src/application/CallPathTracing.ts | 2 +- src/application/EnhancedLiteralTracing.ts | 2 +- src/application/EnhancedToolTypes.ts | 2 +- src/application/EnhancedTools.ts | 10 +- src/application/FunctionAnalysisEvidence.ts | 2 +- src/application/WorkflowEvidence.test.ts | 2 +- .../analysisView/AnalysisViewService.test.ts | 2 +- .../native/NativeApiInspection.test.ts | 2 +- src/application/native/NativeApiInspection.ts | 2 +- src/application/native/NativeCallRoutes.ts | 6 +- .../NativeFunctionMatchingService.test.ts | 2 +- .../native/NativeValueTrace.test.ts | 2 +- src/application/native/NativeValueTrace.ts | 2 +- src/contracts/toolOutputSchemaGroups.ts | 10 +- src/contracts/toolOutputSchemaPrimitives.ts | 2 +- src/contracts/tupleSchemaParity.test.ts | 2 +- src/domain/analysisView/analysisView.test.ts | 2 +- src/domain/analysisView/analysisView.ts | 2 +- src/domain/analysisView/nativeFunctionView.ts | 2 +- .../analysisView/nativeInventoryView.ts | 2 +- .../analysisView/nativeObservationSchemas.ts | 6 +- src/domain/functionCallCoverage.ts | 2 +- src/domain/functionComparison.test.ts | 2 +- src/domain/functionDossierEvidence.ts | 5 +- src/domain/hopperValues.test.ts | 264 -------- src/domain/hopperValues.ts | 616 ------------------ .../managedNativeVerificationSource.ts | 2 +- src/domain/native/addressCoordinate.ts | 11 + src/domain/native/functionBody.test.ts | 134 ++++ src/domain/native/functionBody.ts | 110 ++++ src/domain/native/functionByteCoverage.ts | 2 +- src/domain/native/functionDossier.ts | 128 ++++ src/domain/native/functionInventory.ts | 4 +- .../native/functionStructuralCandidate.ts | 2 +- src/domain/native/literalFeatureMatch.ts | 2 +- src/domain/native/nativeDocument.ts | 21 + src/domain/native/nativeFactAvailability.ts | 6 + .../native/nativeFunctionAnnotations.ts | 2 +- src/domain/native/nativeInventory.ts | 36 +- src/domain/native/nativeObservation.ts | 62 +- .../native/nativeProviderOutput.test.ts | 129 ++++ src/domain/native/nativeProviderOutput.ts | 34 + src/domain/native/nativeReference.ts | 46 ++ src/domain/native/nativeSegments.ts | 87 +++ src/domain/native/nativeString.ts | 32 + src/domain/native/procedureInfo.ts | 48 +- src/domain/native/swiftStringLiteral.ts | 70 ++ ...uctionVerification.functionSources.test.ts | 2 +- src/domain/reconstructionVerification.ts | 2 +- src/domain/staticRuntimeCorrelation.test.ts | 2 +- src/domain/symbolAnalysis.ts | 2 +- src/ghidra/GhidraFunctionValues.test.ts | 2 +- src/ghidra/GhidraFunctionValues.ts | 8 +- src/ghidra/GhidraInventoryValues.ts | 6 +- src/ghidra/GhidraSnapshotCache.test.ts | 2 +- src/hopper/HopperRegexSearch.ts | 2 +- src/ida/IdaOperationRunner.ts | 2 +- src/ida/IdaSessionClient.test.ts | 2 +- src/kuna/KunaReferenceQueries.ts | 2 +- src/kuna/KunaSessionClient.ts | 2 +- src/kuna/KunaStringInventory.ts | 2 +- .../cli/analyzeFunctionDetail.test.ts | 2 +- tests/boundary/mcp/analysisViewMcp.test.ts | 2 +- .../mcp/analyzeFunctionDetail.test.ts | 2 +- .../mcp/callPathSearchExtentMcp.test.ts | 2 +- ...ts-incomplete-function-dossiers-at.test.ts | 2 +- .../mcp/functionCallDirectionsMcp.test.ts | 2 +- .../mcp/functionComparisonCoverageMcp.test.ts | 2 +- .../mcp/functionComparisonMcp.test.ts | 2 +- ...unctionComparisonReferenceFactsMcp.test.ts | 2 +- ...unctionComparisonStringDecodingMcp.test.ts | 2 +- tests/boundary/mcp/ghidraEvidenceMcp.test.ts | 2 +- tests/boundary/mcp/ghidraNativeApiMcp.test.ts | 2 +- .../managedNativeVerificationAdapters.test.ts | 2 +- .../mcp/nativeAddressContextMcp.test.ts | 2 +- tests/boundary/mcp/officialArguments.test.ts | 2 +- .../mcp/workflowUnknownIdentityMcp.test.ts | 2 +- .../hopper/hopperBridgeFacade.test.ts | 2 +- .../providers/ida/functionIdentity.test.ts | 2 +- .../managed/managedNativeVerification.test.ts | 2 +- tests/fixtures/analysisView.ts | 2 +- tests/fixtures/nativeFunctionMatching.ts | 2 +- 94 files changed, 1050 insertions(+), 985 deletions(-) delete mode 100644 src/domain/hopperValues.test.ts delete mode 100644 src/domain/hopperValues.ts create mode 100644 src/domain/native/functionBody.test.ts create mode 100644 src/domain/native/functionBody.ts create mode 100644 src/domain/native/functionDossier.ts create mode 100644 src/domain/native/nativeDocument.ts create mode 100644 src/domain/native/nativeFactAvailability.ts create mode 100644 src/domain/native/nativeProviderOutput.test.ts create mode 100644 src/domain/native/nativeProviderOutput.ts create mode 100644 src/domain/native/nativeReference.ts create mode 100644 src/domain/native/nativeSegments.ts create mode 100644 src/domain/native/nativeString.ts create mode 100644 src/domain/native/swiftStringLiteral.ts diff --git a/scripts/fixtures/managed/function-dossier.mjs b/scripts/fixtures/managed/function-dossier.mjs index 125d41e621..d47cae198a 100644 --- a/scripts/fixtures/managed/function-dossier.mjs +++ b/scripts/fixtures/managed/function-dossier.mjs @@ -1,4 +1,4 @@ -import { functionDossierSchema } from "../../../dist/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../dist/domain/native/functionDossier.js"; /** Validated synthetic native observations shared by managed verification lanes. */ export const functionDossier = (name) => diff --git a/scripts/lib/real-hopper-search.d.mts b/scripts/lib/real-hopper-search.d.mts index d3bb4c0d3a..7f9bf8d2ba 100644 --- a/scripts/lib/real-hopper-search.d.mts +++ b/scripts/lib/real-hopper-search.d.mts @@ -1,6 +1,6 @@ import type { z } from "zod"; -import type { analysisStringSchema } from "../../src/domain/hopperValues.js"; +import type { analysisStringSchema } from "../../src/domain/native/nativeString.js"; type VerifierCall = ( name: string, diff --git a/scripts/verify-real-ghidra-com.mjs b/scripts/verify-real-ghidra-com.mjs index 6139f633c2..80b997f990 100644 --- a/scripts/verify-real-ghidra-com.mjs +++ b/scripts/verify-real-ghidra-com.mjs @@ -14,7 +14,7 @@ import { promisify } from "node:util"; import { Client } from "@modelcontextprotocol/client"; import { StdioClientTransport } from "@modelcontextprotocol/client/stdio"; import { parseEvidence } from "../dist/domain/evidence.js"; -import { functionDossierSchema } from "../dist/domain/hopperValues.js"; +import { functionDossierSchema } from "../dist/domain/native/functionDossier.js"; import { inspectVerifierGhidraInstallation } from "./lib/ghidra-installation-inspection.mjs"; import { buildDosComFixture } from "../tests/conformance/ghidra/dos-com-fixture.mjs"; import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs"; diff --git a/scripts/verify-real-ghidra-dol.mjs b/scripts/verify-real-ghidra-dol.mjs index c025903498..b26bc7d28e 100644 --- a/scripts/verify-real-ghidra-dol.mjs +++ b/scripts/verify-real-ghidra-dol.mjs @@ -14,7 +14,7 @@ import { ArtifactResourceScope } from "../dist/artifacts/ArtifactResourceScope.j import { readGhidraLanguageCatalog } from "../dist/ghidra/GhidraLanguageCatalog.js"; import { parseEvidence } from "../dist/domain/evidence.js"; import { parseAnalysisSnapshot } from "../dist/domain/analysisSnapshot.js"; -import { functionDossierSchema } from "../dist/domain/hopperValues.js"; +import { functionDossierSchema } from "../dist/domain/native/functionDossier.js"; import { inspectVerifierGhidraInstallation } from "./lib/ghidra-installation-inspection.mjs"; import { buildGameCubeDolFixture } from "../tests/conformance/ghidra/dol-fixture.mjs"; import { diff --git a/scripts/verify-real-ghidra-dos.mjs b/scripts/verify-real-ghidra-dos.mjs index 1c685f8d97..75663b572f 100644 --- a/scripts/verify-real-ghidra-dos.mjs +++ b/scripts/verify-real-ghidra-dos.mjs @@ -21,10 +21,8 @@ import { promisify } from "node:util"; import { Client } from "@modelcontextprotocol/client"; import { StdioClientTransport } from "@modelcontextprotocol/client/stdio"; import { parseEvidence } from "../dist/domain/evidence.js"; -import { - functionBodySchema, - functionDossierSchema, -} from "../dist/domain/hopperValues.js"; +import { functionDossierSchema } from "../dist/domain/native/functionDossier.js"; +import { functionBodySchema } from "../dist/domain/native/functionBody.js"; import { inspectVerifierGhidraInstallation } from "./lib/ghidra-installation-inspection.mjs"; import { buildDosMzFixture } from "../tests/conformance/ghidra/dos-mz-fixture.mjs"; import { diff --git a/scripts/verify-real-ghidra-mips-relocatable.mjs b/scripts/verify-real-ghidra-mips-relocatable.mjs index 49de4c1bfe..95c3a8b8e0 100755 --- a/scripts/verify-real-ghidra-mips-relocatable.mjs +++ b/scripts/verify-real-ghidra-mips-relocatable.mjs @@ -12,7 +12,7 @@ import { StdioClientTransport } from "@modelcontextprotocol/client/stdio"; import { parseBinaryTarget } from "../dist/application/BinaryTargetResolver.js"; import { parseEvidence } from "../dist/domain/evidence.js"; import { parseAnalysisSnapshot } from "../dist/domain/analysisSnapshot.js"; -import { functionDossierSchema } from "../dist/domain/hopperValues.js"; +import { functionDossierSchema } from "../dist/domain/native/functionDossier.js"; import { inspectVerifierGhidraInstallation } from "./lib/ghidra-installation-inspection.mjs"; import { buildMipsRelocatableFixture } from "../tests/conformance/ghidra/mips-fixture.mjs"; import { diff --git a/scripts/verify-real-ghidra-mips.mjs b/scripts/verify-real-ghidra-mips.mjs index 7d7c2c47de..bbfa4b382d 100644 --- a/scripts/verify-real-ghidra-mips.mjs +++ b/scripts/verify-real-ghidra-mips.mjs @@ -21,7 +21,7 @@ import { } from "../tests/conformance/ghidra/mips-checks.mjs"; import { parseEvidence } from "../dist/domain/evidence.js"; import { parseAnalysisSnapshot } from "../dist/domain/analysisSnapshot.js"; -import { functionDossierSchema } from "../dist/domain/hopperValues.js"; +import { functionDossierSchema } from "../dist/domain/native/functionDossier.js"; import { inspectVerifierGhidraInstallation } from "./lib/ghidra-installation-inspection.mjs"; import { buildMipsFixture } from "../tests/conformance/ghidra/mips-fixture.mjs"; import { diff --git a/scripts/verify-real-ghidra-ppc.mjs b/scripts/verify-real-ghidra-ppc.mjs index 32d56f83e5..a8a99d9408 100644 --- a/scripts/verify-real-ghidra-ppc.mjs +++ b/scripts/verify-real-ghidra-ppc.mjs @@ -18,7 +18,7 @@ import { } from "../tests/conformance/ghidra/ppc-checks.mjs"; import { parseEvidence } from "../dist/domain/evidence.js"; import { parseAnalysisSnapshot } from "../dist/domain/analysisSnapshot.js"; -import { functionDossierSchema } from "../dist/domain/hopperValues.js"; +import { functionDossierSchema } from "../dist/domain/native/functionDossier.js"; import { inspectVerifierGhidraInstallation } from "./lib/ghidra-installation-inspection.mjs"; import { buildPowerPcFixture } from "../tests/conformance/ghidra/ppc-fixture.mjs"; import { requireMcpOperationResult } from "./lib/mcp-verifier-results.mjs"; diff --git a/scripts/verify-real-ghidra-riscv.mjs b/scripts/verify-real-ghidra-riscv.mjs index 29e67d88fa..0a79119de3 100644 --- a/scripts/verify-real-ghidra-riscv.mjs +++ b/scripts/verify-real-ghidra-riscv.mjs @@ -21,7 +21,7 @@ import addFormats from "ajv-formats"; import { parseBinaryTarget } from "../dist/application/BinaryTargetResolver.js"; import { parseAnalysisSnapshot } from "../dist/domain/analysisSnapshot.js"; import { parseEvidence } from "../dist/domain/evidence.js"; -import { functionDossierSchema } from "../dist/domain/hopperValues.js"; +import { functionDossierSchema } from "../dist/domain/native/functionDossier.js"; import { inspectVerifierGhidraInstallation } from "./lib/ghidra-installation-inspection.mjs"; import { buildRiscvFixture } from "../tests/conformance/ghidra/riscv-fixture.mjs"; import { diff --git a/scripts/verify-real-ida.mjs b/scripts/verify-real-ida.mjs index 1aff82bad6..438b2ed40e 100644 --- a/scripts/verify-real-ida.mjs +++ b/scripts/verify-real-ida.mjs @@ -60,7 +60,7 @@ const [ ] = await Promise.all([ importBuilt("contracts/toolContracts.js"), importBuilt("domain/evidence.js"), - importBuilt("domain/hopperValues.js"), + importBuilt("domain/native/functionDossier.js"), importBuilt("ida/IdaConfiguration.js"), importBuilt("ida/IdaMcpConnection.js"), importBuilt("ida/IdaProtocolValues.js"), diff --git a/src/application/AnalysisContextQueries.ts b/src/application/AnalysisContextQueries.ts index 6735bb308a..436cea3ad3 100644 --- a/src/application/AnalysisContextQueries.ts +++ b/src/application/AnalysisContextQueries.ts @@ -6,10 +6,8 @@ import type { import type { AnalysisError } from "../domain/analysisErrorBase.js"; import { AnalysisOutputError } from "../domain/analysisErrorCore.js"; import type { JsonValue } from "../domain/jsonValue.js"; -import { - analysisBookmarkSchema, - parseContainingProcedureResolution, -} from "../domain/hopperValues.js"; +import { analysisBookmarkSchema } from "../domain/native/nativeInventory.js"; +import { parseContainingProcedureResolution } from "../domain/native/nativeObservation.js"; import { resolveAnalysisDocument } from "./AnalysisDocument.js"; import { err, ok, type Result } from "../domain/result.js"; diff --git a/src/application/AnalysisDocument.ts b/src/application/AnalysisDocument.ts index 6f9200eb29..73e7418df2 100644 --- a/src/application/AnalysisDocument.ts +++ b/src/application/AnalysisDocument.ts @@ -9,7 +9,7 @@ import { AnalysisOutputError, } from "../domain/analysisErrorCore.js"; import type { AnalysisError } from "../domain/analysisErrorBase.js"; -import { parseDocuments } from "../domain/hopperValues.js"; +import { parseDocuments } from "../domain/native/nativeDocument.js"; import { err, ok, type Result } from "../domain/result.js"; const executionOptions = (signal: AbortSignal | undefined) => diff --git a/src/application/CallPathTracing.ts b/src/application/CallPathTracing.ts index cfe81081c2..a114b5efb8 100644 --- a/src/application/CallPathTracing.ts +++ b/src/application/CallPathTracing.ts @@ -8,7 +8,7 @@ import type { import { projectAnalysisError } from "../domain/analysisErrorProjection.js"; import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; import type { AnalysisError } from "../domain/analysisErrorBase.js"; -import { parseRelatedAddresses } from "../domain/hopperValues.js"; +import { parseRelatedAddresses } from "../domain/native/nativeObservation.js"; import type { JsonValue } from "../domain/jsonValue.js"; import { err, ok, type Result } from "../domain/result.js"; import type { EnhancedResult } from "./EnhancedToolTypes.js"; diff --git a/src/application/EnhancedLiteralTracing.ts b/src/application/EnhancedLiteralTracing.ts index 89dd10d1e5..d76a627a37 100644 --- a/src/application/EnhancedLiteralTracing.ts +++ b/src/application/EnhancedLiteralTracing.ts @@ -10,7 +10,7 @@ import type { JsonValue } from "../domain/jsonValue.js"; import { parseContainingProcedureResolution, type ContainingProcedureResolution, -} from "../domain/hopperValues.js"; +} from "../domain/native/nativeObservation.js"; import { err, ok, type Result } from "../domain/result.js"; import type { EnhancedResult, TraceReference } from "./EnhancedToolTypes.js"; diff --git a/src/application/EnhancedToolTypes.ts b/src/application/EnhancedToolTypes.ts index 3d4170ff6e..1352364d51 100644 --- a/src/application/EnhancedToolTypes.ts +++ b/src/application/EnhancedToolTypes.ts @@ -3,7 +3,7 @@ import type { z } from "zod"; import { AnalysisInputError } from "../domain/analysisErrorCore.js"; import type { AnalysisError } from "../domain/analysisErrorBase.js"; import type { JsonValue } from "../domain/jsonValue.js"; -import type { ContainingProcedureResolution } from "../domain/hopperValues.js"; +import type { ContainingProcedureResolution } from "../domain/native/nativeObservation.js"; import { err, type Result } from "../domain/result.js"; import { enhancedInputSchemas, diff --git a/src/application/EnhancedTools.ts b/src/application/EnhancedTools.ts index 1c56bb7ff1..89179d6c08 100644 --- a/src/application/EnhancedTools.ts +++ b/src/application/EnhancedTools.ts @@ -21,13 +21,16 @@ import { } from "../domain/analysisErrorCore.js"; import { projectAnalysisError } from "../domain/analysisErrorProjection.js"; import type { AnalysisError } from "../domain/analysisErrorBase.js"; +import { parseFunctionDossier } from "../domain/native/functionDossier.js"; import { - parseFunctionDossier, parseListCount, - parseRelatedAddresses, + addressedValueSchema, +} from "../domain/native/nativeInventory.js"; +import { parseRelatedAddresses } from "../domain/native/nativeObservation.js"; +import { parseSegments, projectBinaryOverview, -} from "../domain/hopperValues.js"; +} from "../domain/native/nativeSegments.js"; import { err, ok, type Result } from "../domain/result.js"; import { categorizeSwiftTypes, @@ -40,7 +43,6 @@ import { traceNativeValues } from "./native/NativeValueTrace.js"; import { traceNativeUiAction } from "./native/NativeUiActionTrace.js"; import { decompileAll } from "./decompileDirectory/decompileAll.js"; import { jsonValueSchema, type JsonValue } from "../domain/jsonValue.js"; -import { addressedValueSchema } from "../domain/native/nativeInventory.js"; import { nativeDispatchMetadataResultSchema, MAX_DISPATCH_METADATA_RECORDS, diff --git a/src/application/FunctionAnalysisEvidence.ts b/src/application/FunctionAnalysisEvidence.ts index d8f6463ea2..835f81ed80 100644 --- a/src/application/FunctionAnalysisEvidence.ts +++ b/src/application/FunctionAnalysisEvidence.ts @@ -6,7 +6,7 @@ import type { import { nativeProcedureInputSchema } from "../domain/native/nativeProcedureInput.js"; import type { BinaryTarget } from "../domain/binaryTargetTypes.js"; import { createAnalysisExecutionEvidence } from "../domain/analysisObservation.js"; -import { parseFunctionDossier } from "../domain/hopperValues.js"; +import { parseFunctionDossier } from "../domain/native/functionDossier.js"; import type { JsonValue } from "../domain/jsonValue.js"; import { completeProviderEvidence } from "./ProviderOperationExecution.js"; import type { Evidence } from "../domain/evidence.js"; diff --git a/src/application/WorkflowEvidence.test.ts b/src/application/WorkflowEvidence.test.ts index 88596f3653..0cb64d0cbf 100644 --- a/src/application/WorkflowEvidence.test.ts +++ b/src/application/WorkflowEvidence.test.ts @@ -16,7 +16,7 @@ import { import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; import { nativeApiBoundarySchema } from "../domain/native/nativeApiBoundary.js"; import { projectNativeApiInspection } from "./native/NativeApiInspection.js"; -import { functionDossierSchema } from "../domain/hopperValues.js"; +import { functionDossierSchema } from "../domain/native/functionDossier.js"; import { ghidraFunctionDossier } from "../domain/ghidraValues.fixture.js"; import { ANALYSIS_SNAPSHOT_PROFILE } from "../domain/analysisSnapshot.fixture.js"; import { createEvidence } from "../domain/evidence.js"; diff --git a/src/application/analysisView/AnalysisViewService.test.ts b/src/application/analysisView/AnalysisViewService.test.ts index 0b8a3311c5..ad762d5881 100644 --- a/src/application/analysisView/AnalysisViewService.test.ts +++ b/src/application/analysisView/AnalysisViewService.test.ts @@ -20,7 +20,7 @@ import { rebindEvidenceToTargetPath, } from "../../domain/evidence.js"; import { inspectAnalysisViewInputSchema } from "../../domain/analysisView/analysisView.js"; -import { functionDossierSchema } from "../../domain/hopperValues.js"; +import { functionDossierSchema } from "../../domain/native/functionDossier.js"; import { ghidraFunctionDossier } from "../../domain/ghidraValues.fixture.js"; import { inspectAnalysisView, diff --git a/src/application/native/NativeApiInspection.test.ts b/src/application/native/NativeApiInspection.test.ts index 9990106383..593b3dd299 100644 --- a/src/application/native/NativeApiInspection.test.ts +++ b/src/application/native/NativeApiInspection.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { functionDossierSchema } from "../../domain/hopperValues.js"; +import { functionDossierSchema } from "../../domain/native/functionDossier.js"; import { ghidraFunctionDossier } from "../../domain/ghidraValues.fixture.js"; import { nativeApiBoundarySchema } from "../../domain/native/nativeApiBoundary.js"; import { projectNativeApiInspection } from "./NativeApiInspection.js"; diff --git a/src/application/native/NativeApiInspection.ts b/src/application/native/NativeApiInspection.ts index 9e1aedbc33..bf9bef0567 100644 --- a/src/application/native/NativeApiInspection.ts +++ b/src/application/native/NativeApiInspection.ts @@ -1,4 +1,4 @@ -import type { FunctionDossier } from "../../domain/hopperValues.js"; +import type { FunctionDossier } from "../../domain/native/functionDossier.js"; import { nativeApiInspectionResultSchema, type NativeApiBoundary, diff --git a/src/application/native/NativeCallRoutes.ts b/src/application/native/NativeCallRoutes.ts index 9c082c1966..16d1568adb 100644 --- a/src/application/native/NativeCallRoutes.ts +++ b/src/application/native/NativeCallRoutes.ts @@ -4,10 +4,8 @@ import type { ExecutionOptions, ProviderIdentity, } from "../AnalysisProvider.js"; -import { - parseRelatedAddresses, - referenceKindSchema, -} from "../../domain/hopperValues.js"; +import { parseRelatedAddresses } from "../../domain/native/nativeObservation.js"; +import { referenceKindSchema } from "../../domain/native/nativeReference.js"; import { AnalysisOutputError } from "../../domain/analysisErrorCore.js"; import type { AnalysisError } from "../../domain/analysisErrorBase.js"; import { err, ok, type Result } from "../../domain/result.js"; diff --git a/src/application/native/NativeFunctionMatchingService.test.ts b/src/application/native/NativeFunctionMatchingService.test.ts index e51922d56a..fb0652c184 100644 --- a/src/application/native/NativeFunctionMatchingService.test.ts +++ b/src/application/native/NativeFunctionMatchingService.test.ts @@ -5,7 +5,7 @@ import { nativeFunctionMatchingSchema } from "../../domain/native/functionMatchi import { createEvidence, immutableEvidence } from "../../domain/evidence.js"; import { createAnalysisProfile } from "../../domain/analysisProfile.js"; import { AnalysisResourceConstraintError } from "../../domain/analysisErrorCore.js"; -import { functionDossierSchema } from "../../domain/hopperValues.js"; +import { functionDossierSchema } from "../../domain/native/functionDossier.js"; import { procedureInfoSchema } from "../../domain/native/procedureInfo.js"; import { nativeMatchingNamedInventory as nativeMatchingInventory, diff --git a/src/application/native/NativeValueTrace.test.ts b/src/application/native/NativeValueTrace.test.ts index b73a2a5de4..29dc6b3f67 100644 --- a/src/application/native/NativeValueTrace.test.ts +++ b/src/application/native/NativeValueTrace.test.ts @@ -5,7 +5,7 @@ import { type AnalysisOperationPort, } from "../AnalysisProvider.js"; import { ghidraFunctionDossier } from "../../domain/ghidraValues.fixture.js"; -import { functionDossierSchema } from "../../domain/hopperValues.js"; +import { functionDossierSchema } from "../../domain/native/functionDossier.js"; import { jsonValueSchema } from "../../domain/jsonValue.js"; import { nativeValueTraceSchema } from "../../domain/native/nativeValueTrace.js"; import { diff --git a/src/application/native/NativeValueTrace.ts b/src/application/native/NativeValueTrace.ts index 2eb932f5c1..2f330b55d5 100644 --- a/src/application/native/NativeValueTrace.ts +++ b/src/application/native/NativeValueTrace.ts @@ -1,4 +1,4 @@ -import { functionDossierSchema } from "../../domain/hopperValues.js"; +import { functionDossierSchema } from "../../domain/native/functionDossier.js"; import { nativeCallTargetsSchema } from "../../domain/native/nativeInstruction.js"; import { nativeValueTraceInputSchema, diff --git a/src/contracts/toolOutputSchemaGroups.ts b/src/contracts/toolOutputSchemaGroups.ts index dd4ee73d91..4dd8f6b25e 100644 --- a/src/contracts/toolOutputSchemaGroups.ts +++ b/src/contracts/toolOutputSchemaGroups.ts @@ -1,6 +1,7 @@ import { nativeAddressListSchema, nativeObservationSourceSchemas, + containingProcedureResolutionSchema, } from "../domain/native/nativeObservation.js"; import { nativeObservationSummaryViewResultSchemas, @@ -22,13 +23,14 @@ import { nativeByteReadSchema } from "../domain/native/nativeByteRead.js"; import { analysisDetailOutputSchemaOf } from "./analysisDetail.js"; import { functionDossierSchema, - analysisBookmarkSchema, functionInstructionWindowSchema, +} from "../domain/native/functionDossier.js"; +import { analysisBookmarkSchema } from "../domain/native/nativeInventory.js"; +import { referenceEdgeSchema, unresolvedCallSchema, - procedureIdentitySchema, - containingProcedureResolutionSchema, -} from "../domain/hopperValues.js"; +} from "../domain/native/nativeReference.js"; +import { procedureIdentitySchema } from "../domain/native/procedureInfo.js"; import { nativeInventoryRowsSchemas } from "../domain/native/nativeInventory.js"; import { z } from "zod"; import { jsonRecordSchema } from "../domain/jsonRecord.js"; diff --git a/src/contracts/toolOutputSchemaPrimitives.ts b/src/contracts/toolOutputSchemaPrimitives.ts index 3d2c7ca651..f66d02beab 100644 --- a/src/contracts/toolOutputSchemaPrimitives.ts +++ b/src/contracts/toolOutputSchemaPrimitives.ts @@ -2,7 +2,7 @@ import { procedureInfoSchema } from "../domain/native/procedureInfo.js"; import { z } from "zod"; import { utcDateTimeSchema } from "../domain/offsetDateTime.js"; -import { functionDossierSchema } from "../domain/hopperValues.js"; +import { functionDossierSchema } from "../domain/native/functionDossier.js"; import { analysisProfileSchema } from "../domain/analysisProfile.js"; import { evidenceSchema, diff --git a/src/contracts/tupleSchemaParity.test.ts b/src/contracts/tupleSchemaParity.test.ts index e5a64adac1..170254d60a 100644 --- a/src/contracts/tupleSchemaParity.test.ts +++ b/src/contracts/tupleSchemaParity.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest"; import { z } from "zod"; import { packetFieldsSchema } from "../domain/captures/packetCapture.js"; -import { swiftStringLiteralSchema } from "../domain/hopperValues.js"; +import { swiftStringLiteralSchema } from "../domain/native/swiftStringLiteral.js"; import { applicationVersionComparisonResultSchema } from "../domain/javascript/javascriptApplicationVersionComparisonSchemas.js"; import { kernelModuleSchema } from "../domain/native/kernelModule.js"; import { pinvokeVerificationSchema } from "../domain/managed/managedNativeVerificationSchemas.js"; diff --git a/src/domain/analysisView/analysisView.test.ts b/src/domain/analysisView/analysisView.test.ts index 334b1dc242..595fc9ff3f 100644 --- a/src/domain/analysisView/analysisView.test.ts +++ b/src/domain/analysisView/analysisView.test.ts @@ -5,7 +5,7 @@ import { } from "./analysisViewCoverage.js"; import { expect, it } from "vitest"; -import { functionDossierSchema } from "../hopperValues.js"; +import { functionDossierSchema } from "../native/functionDossier.js"; import { ghidraFunctionDossier } from "../ghidraValues.fixture.js"; import { JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE } from "../../contracts/javascript/javascriptRuntimeReconciliationExample.js"; import { ARTIFACT_COMPARISON_EXAMPLE } from "../../contracts/artifactComparisonExample.js"; diff --git a/src/domain/analysisView/analysisView.ts b/src/domain/analysisView/analysisView.ts index a8e8420231..cfd24de9c0 100644 --- a/src/domain/analysisView/analysisView.ts +++ b/src/domain/analysisView/analysisView.ts @@ -195,7 +195,7 @@ import { bundleResourceRootSchema } from "../apple/bundleResourceRoot.js"; import { functionDossierSchema, functionFacetAvailabilitySchema, -} from "../hopperValues.js"; +} from "../native/functionDossier.js"; import { artifactGraphManifestSchema, artifactInventoryCoverageSchema, diff --git a/src/domain/analysisView/nativeFunctionView.ts b/src/domain/analysisView/nativeFunctionView.ts index 0ff8a98222..dea093806e 100644 --- a/src/domain/analysisView/nativeFunctionView.ts +++ b/src/domain/analysisView/nativeFunctionView.ts @@ -2,7 +2,7 @@ import { analysisViewInputError } from "./analysisViewErrors.js"; import { analysisTextPage } from "./analysisViewText.js"; import type { AnalysisError } from "../analysisErrorBase.js"; import { uniqueSorted } from "../canonicalOrdering.js"; -import type { FunctionDossier } from "../hopperValues.js"; +import type { FunctionDossier } from "../native/functionDossier.js"; import { jsonValueSchema } from "../jsonValue.js"; import { err, ok, type Result } from "../result.js"; import type { diff --git a/src/domain/analysisView/nativeInventoryView.ts b/src/domain/analysisView/nativeInventoryView.ts index f63a5958d0..2ef5dedccc 100644 --- a/src/domain/analysisView/nativeInventoryView.ts +++ b/src/domain/analysisView/nativeInventoryView.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { analysisStringSchema } from "../hopperValues.js"; +import { analysisStringSchema } from "../native/nativeString.js"; import { jsonValueSchema } from "../jsonValue.js"; import { addressedValueSchema, diff --git a/src/domain/analysisView/nativeObservationSchemas.ts b/src/domain/analysisView/nativeObservationSchemas.ts index 91401875db..50f42c6c99 100644 --- a/src/domain/analysisView/nativeObservationSchemas.ts +++ b/src/domain/analysisView/nativeObservationSchemas.ts @@ -1,8 +1,10 @@ -import { NATIVE_OBSERVATION_OPERATIONS } from "../native/nativeObservation.js"; +import { + NATIVE_OBSERVATION_OPERATIONS, + containingProcedureResolutionSchema, +} from "../native/nativeObservation.js"; import { z } from "zod"; import { digestSchema } from "../digests.js"; import { evidenceSchema } from "../evidence.js"; -import { containingProcedureResolutionSchema } from "../hopperValues.js"; import { completeRelationshipCoverageSchema, partialRelationshipCoverageSchema, diff --git a/src/domain/functionCallCoverage.ts b/src/domain/functionCallCoverage.ts index b172263cc3..4c377d13bd 100644 --- a/src/domain/functionCallCoverage.ts +++ b/src/domain/functionCallCoverage.ts @@ -2,7 +2,7 @@ import { relationshipCoverageLimitations, type RelationshipCoverage, } from "./relationshipCoverage.js"; -import type { FunctionDossier } from "./hopperValues.js"; +import type { FunctionDossier } from "./native/functionDossier.js"; import { uniqueSorted } from "./canonicalOrdering.js"; const callerCoveragePrefix = "Callers: "; diff --git a/src/domain/functionComparison.test.ts b/src/domain/functionComparison.test.ts index 6f2f767cdb..027a448080 100644 --- a/src/domain/functionComparison.test.ts +++ b/src/domain/functionComparison.test.ts @@ -7,7 +7,7 @@ import { functionComparisonResultSchema } from "./functionComparisonSchemas.js"; import { createFunctionCoordinateContext } from "./native/functionCoordinateContext.js"; import { createAnalysisProfile } from "./analysisProfile.js"; import { createEvidence, type Evidence } from "./evidence.js"; -import { functionDossierSchema } from "./hopperValues.js"; +import { functionDossierSchema } from "./native/functionDossier.js"; import { canonicalJson } from "./comparisonSemantics.js"; import { digestCanonicalValue } from "./canonicalDigest.js"; import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js"; diff --git a/src/domain/functionDossierEvidence.ts b/src/domain/functionDossierEvidence.ts index 855836ada0..7757f3bf75 100644 --- a/src/domain/functionDossierEvidence.ts +++ b/src/domain/functionDossierEvidence.ts @@ -5,7 +5,10 @@ import { readEvidence, readEvidencePayload } from "./evidenceValidation.js"; import type { JsonValue } from "./jsonValue.js"; import { err, ok, type Result } from "./result.js"; import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js"; -import { functionDossierSchema, type FunctionDossier } from "./hopperValues.js"; +import { + functionDossierSchema, + type FunctionDossier, +} from "./native/functionDossier.js"; import { assessFunctionCallCoverage } from "./functionCallCoverage.js"; /** Complete function observation parsed from one analyze_function Evidence record. */ diff --git a/src/domain/hopperValues.test.ts b/src/domain/hopperValues.test.ts deleted file mode 100644 index 9eaff335e4..0000000000 --- a/src/domain/hopperValues.test.ts +++ /dev/null @@ -1,264 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { - functionBodySchema, - addressDistance, - procedureIdentitySchema, - parseDocuments, - parseListCount, - parseRelatedAddresses, - parseSegments, -} from "./hopperValues.js"; - -describe("Hopper boundary values", () => { - it("distinguishes measured zero, malformed, incomparable, reversed, and oversized extents", () => { - expect(addressDistance("not-an-address", "0x2000")).toBeNull(); - expect(addressDistance("ram:0x1000", "rom:0x2000")).toBeNull(); - expect(addressDistance("0x2000", "0x1000")).toBeNull(); - expect(addressDistance("0x1000", "0x1000")).toBe(0); - expect(addressDistance("0x1000", "0x2000")).toBe(4096); - expect(addressDistance("0x0", "0x20000000000000")).toBeNull(); - }); - - it("counts Hopper's address-keyed string maps", () => { - expect( - parseListCount({ "0x1000": "hello", "0x2000": "world" }, "strings"), - ).toEqual({ ok: true, value: 2 }); - }); - - it("parses direct and wrapped protocol shapes", () => { - expect(parseRelatedAddresses({ callers: ["0x3"] }, "callers")).toEqual({ - ok: true, - value: ["0x3"], - }); - expect( - parseSegments([ - { - name: "__TEXT", - start: "0x1", - end: "0x2", - readable: true, - writable: false, - executable: null, - }, - ]), - ).toEqual({ - ok: true, - value: [ - { - name: "__TEXT", - start: "0x1", - end: "0x2", - readable: true, - writable: false, - executable: null, - }, - ], - }); - expect(parseDocuments(["fixture"])).toEqual({ - ok: true, - value: ["fixture"], - }); - }); - - it.each([ - ["relations", () => parseRelatedAddresses([1], "callees")], - ["segments", () => parseSegments([{ name: 1 }])], - ["documents", () => parseDocuments([1])], - ])( - "rejects malformed %s instead of manufacturing empty output", - (_label, parse) => { - const result = parse(); - expect(result.ok).toBe(false); - if (!result.ok) expect(result.error._tag).toBe("AnalysisOutputError"); - }, - ); - - it("uses provider-neutral wording for malformed shared values", () => { - const result = parseSegments([{ name: 1 }]); - expect(result.ok).toBe(false); - if (!result.ok) { - expect(result.error._tag).toBe("AnalysisOutputError"); - expect(result.error.operation).toBe("list_segments"); - expect(result.error.reason).toBe( - "Provider returned an invalid segment list", - ); - } - }); -}); - -describe("provider-neutral inclusive function body evidence", () => { - const observed = () => ({ - available: true, - provenance: "test-address-set", - ranges: [ - { start: "0x1000", end: "0x1002" }, - { start: "0x1010", end: "0x1011" }, - ], - total_bytes: 5, - span_bytes: 18, - non_contiguous: true, - contains_entry: true, - }); - it("rejects missing provider body metadata", () => { - expect( - procedureIdentitySchema.safeParse({ address: "0x1000", name: "main" }) - .success, - ).toBe(false); - }); - it("rejects segments when required coordinates or unknown permissions are omitted", () => { - expect( - parseSegments([ - { - name: "__TEXT", - start: "0x1", - end: "0x2", - readable: null, - writable: null, - }, - ]).ok, - ).toBe(false); - expect( - parseSegments([{ name: "__TEXT", start: "0x1", end: "0x2" }]).ok, - ).toBe(false); - }); - it("counts inclusive one-byte ranges without assuming an enclosing continuous body", () => { - expect( - functionBodySchema.safeParse({ - ...observed(), - ranges: [{ start: "0x1000", end: "0x1000" }], - total_bytes: 1, - span_bytes: 1, - non_contiguous: false, - }).success, - ).toBe(true); - expect(functionBodySchema.parse(observed())).toMatchObject({ - total_bytes: 5, - span_bytes: 18, - }); - }); - it("preserves address-space identity and leaves a multi-space span unknown", () => { - expect( - functionBodySchema.safeParse({ - ...observed(), - ranges: [ - { start: "a:0x1000", end: "a:0x1000" }, - { start: "b:0x1000", end: "b:0x1001" }, - ], - total_bytes: 3, - span_bytes: null, - }).success, - ).toBe(true); - expect( - functionBodySchema.safeParse({ - ...observed(), - ranges: [{ start: "a:0x1000", end: "b:0x1000" }], - }).success, - ).toBe(false); - }); - it("allows an observed empty set without manufacturing entry membership", () => { - expect( - procedureIdentitySchema.safeParse({ - address: "0x1000", - name: "external", - classification: null, - body: { - ...observed(), - ranges: [], - total_bytes: 0, - span_bytes: 0, - non_contiguous: false, - contains_entry: false, - }, - }).success, - ).toBe(true); - }); - it("checks entry membership using address space and inclusive endpoints", () => { - expect( - procedureIdentitySchema.safeParse({ - address: "0x1002", - name: "last", - classification: null, - body: observed(), - }).success, - ).toBe(true); - expect( - procedureIdentitySchema.safeParse({ - address: "0x1003", - name: "gap", - classification: null, - body: observed(), - }).success, - ).toBe(false); - expect( - procedureIdentitySchema.safeParse({ - address: "other:0x1000", - name: "other", - classification: null, - body: observed(), - }).success, - ).toBe(false); - }); - it.each([ - { total_bytes: 18 }, - { span_bytes: 5 }, - { non_contiguous: false }, - { - ranges: [ - { start: "0x1000", end: "0x1002" }, - { start: "0x1003", end: "0x1004" }, - ], - }, - { - ranges: [ - { start: "0x1000", end: "0x1002" }, - { start: "0x1002", end: "0x1004" }, - ], - }, - { - ranges: [ - { start: "0x1010", end: "0x1011" }, - { start: "0x1000", end: "0x1002" }, - ], - }, - { - ranges: [{ start: "0x0", end: "0x20000000000000" }], - total_bytes: Number.MAX_SAFE_INTEGER, - span_bytes: Number.MAX_SAFE_INTEGER, - non_contiguous: false, - }, - ])("rejects contradictory or unsafe range facts %j", (change) => { - expect( - functionBodySchema.safeParse({ ...observed(), ...change }).success, - ).toBe(false); - }); -}); - -it.each(["procedures", "strings"])( - "counts every own entry of direct and wrapped %s maps", - (property) => { - for (const text of [ - '{"__proto__":"observed"}', - '{"__proto__":"observed","0x1000":"one"}', - ]) { - const source = JSON.parse(text); - const count = Object.keys(source).length; - expect(parseListCount(source, property)).toEqual({ - ok: true, - value: count, - }); - expect(parseListCount({ [property]: source }, property)).toEqual({ - ok: true, - value: count, - }); - expect(JSON.stringify(source)).toBe(text); - } - expect(parseListCount({ [property]: [] }, property)).toEqual({ - ok: true, - value: 0, - }); - expect(parseListCount({}, property)).toEqual({ ok: true, value: 0 }); - for (const value of [null, "not a list", 3, false]) - expect(parseListCount(value, property).ok).toBe(false); - }, -); diff --git a/src/domain/hopperValues.ts b/src/domain/hopperValues.ts deleted file mode 100644 index 02e05e0110..0000000000 --- a/src/domain/hopperValues.ts +++ /dev/null @@ -1,616 +0,0 @@ -import { nativeSymbolMetadataSchema } from "./native/nativeSymbol.js"; -import { z } from "zod"; -import { fixedTupleSchema } from "./fixedTupleSchema.js"; -import { addressCoordinate } from "./native/addressCoordinate.js"; - -import { jsonObjectSchema, type JsonValue } from "./jsonValue.js"; - -import { err, ok, type Result } from "./result.js"; -import { nativeApiBoundarySchema } from "./native/nativeApiBoundary.js"; -import { nativeValueFlowSchema } from "./native/nativeValueFlow.js"; -import { nativeCallClassificationSchema } from "./native/nativeCallReference.js"; -import { AnalysisOutputError } from "./analysisErrorCore.js"; - -export interface AddressedName { - readonly address: string; - readonly name: string; - readonly objc_class_label?: "hopper_legacy_class_object"; -} - -export interface SegmentSummary { - readonly name: string; - readonly start: string; - readonly end: string; - readonly readable: boolean | null; - readonly writable: boolean | null; - readonly executable: boolean | null; - readonly image_base?: string; - readonly address_space?: string; -} - -/** Return a bounded byte distance between canonical addresses in one space. */ -export const addressDistance = (start: string, end: string): number | null => { - const left = addressCoordinate(start); - const right = addressCoordinate(end); - if (left === null || right === null || left.space !== right.space) - return null; - const distance = right.offset - left.offset; - if (distance < 0n || distance > BigInt(Number.MAX_SAFE_INTEGER)) return null; - return Number(distance); -}; - -const segmentSchema = z.object({ - name: z.string(), - start: z.string(), - end: z.string(), - readable: z.boolean().nullable(), - writable: z.boolean().nullable(), - executable: z.boolean().nullable(), - image_base: z.string().min(1).exactOptional(), - address_space: z.string().min(1).exactOptional(), -}); -const unavailableAnalysisFactSchema = z - .object({ available: z.literal(false), reason: z.string() }) - .strict(); -/** Typed string evidence shared by inventories and referenced function literals. */ -export const analysisStringSchema = z.object({ - address: z.string(), - value: z.string(), - provider_value: z - .string() - .exactOptional() - .describe( - "Original provider display text, which may shorten the typed object.", - ), - decoding: unavailableAnalysisFactSchema - .exactOptional() - .describe( - "When present, value retains native display text because typed bytes could not be decoded.", - ), - string: z - .object({ - encoding: z.string().min(1), - encoding_status: z - .enum(["observed", "inferred"]) - .exactOptional() - .describe( - "Whether the provider declares the encoding or REA infers it from the typed bytes and native display. This does not establish the original source's intended encoding.", - ), - termination: z.enum(["missing", "present_or_not_required"]), - byte_length: z.int().min(0), - }) - .exactOptional(), -}); - -/** An analyzed bookmark retains a missing provider label as null. */ -export const analysisBookmarkSchema = z.strictObject({ - address: z.string(), - name: z.string().nullable(), -}); -/** Complete observed function-body ranges; every range endpoint is inclusive. */ -export const availableFunctionBodySchema = z - .strictObject({ - available: z.literal(true), - provenance: z.string().min(1), - ranges: z.array( - z.strictObject({ - start: z.string(), - end: z - .string() - .describe("Inclusive last address of this observed body range."), - }), - ), - total_bytes: z.int().min(0).max(Number.MAX_SAFE_INTEGER), - span_bytes: z.int().min(0).max(Number.MAX_SAFE_INTEGER).nullable(), - non_contiguous: z.boolean(), - contains_entry: z.boolean(), - }) - .superRefine((body, context) => { - let total = 0n; - const bounds = new Map(); - const closedSpaces = new Set(); - let previousSpace: string | null | undefined; - for (const range of body.ranges) { - const start = addressCoordinate(range.start); - const end = addressCoordinate(range.end); - if ( - start === null || - end === null || - start.space !== end.space || - end.offset < start.offset - ) { - context.addIssue({ - code: "custom", - message: "Function body range has invalid inclusive endpoints", - }); - return; - } - if (previousSpace !== undefined && previousSpace !== start.space) - closedSpaces.add(previousSpace); - const previous = bounds.get(start.space); - if ( - closedSpaces.has(start.space) || - (previous !== undefined && start.offset <= previous.end + 1n) - ) { - context.addIssue({ - code: "custom", - message: - "Function body ranges must be ordered, disjoint and maximal within each address space", - }); - return; - } - bounds.set(start.space, { - start: previous?.start ?? start.offset, - end: end.offset, - }); - previousSpace = start.space; - total += end.offset - start.offset + 1n; - } - const onlyBounds = bounds.size === 1 ? [...bounds.values()][0] : undefined; - const span = - bounds.size === 0 - ? 0n - : onlyBounds === undefined - ? null - : onlyBounds.end - onlyBounds.start + 1n; - const reportedSpan = - body.span_bytes === null ? null : BigInt(body.span_bytes); - if ( - total !== BigInt(body.total_bytes) || - span !== reportedSpan || - body.non_contiguous !== body.ranges.length > 1 - ) - context.addIssue({ - code: "custom", - message: - "Function body counts, enclosing span or contiguity disagree with inclusive ranges", - }); - }); - -export const functionBodySchema = z.discriminatedUnion("available", [ - unavailableAnalysisFactSchema, - availableFunctionBodySchema, -]); - -/** Check the declared entry membership against complete, inclusive body ranges. */ -export const functionBodyEntryAgrees = ( - body: z.infer, - address: string, -): boolean => { - if (!body.available) return true; - const entry = addressCoordinate(address); - if (entry === null) return false; - const contains = body.ranges.some((range) => { - const start = addressCoordinate(range.start); - const end = addressCoordinate(range.end); - return ( - start !== null && - end !== null && - start.space === entry.space && - entry.offset >= start.offset && - entry.offset <= end.offset - ); - }); - return contains === body.contains_entry; -}; - -export const procedureClassificationSchema = z - .object({ - external: z.boolean(), - thunk: z.boolean(), - thunk_target: z.string().nullable(), - provenance: z.string().min(1), - }) - .strict(); -export const procedureIdentitySchema = z - .object({ - address: z.string(), - name: z.string(), - symbol: nativeSymbolMetadataSchema.nullable().exactOptional(), - classification: procedureClassificationSchema.nullable(), - body: functionBodySchema, - }) - .strict() - .superRefine((identity, context) => { - if (!functionBodyEntryAgrees(identity.body, identity.address)) - context.addIssue({ - code: "custom", - message: - "Function body entry membership disagrees with procedure identity", - }); - }); -/** Exact containing-procedure outcome; valid absence is distinct from unreadable output. */ -export const containingProcedureResolutionSchema = z.discriminatedUnion( - "found", - [ - z.object({ - query_address: z.string(), - found: z.literal(true), - procedure: procedureIdentitySchema, - }), - z.object({ - query_address: z.string(), - found: z.literal(false), - procedure: z.null(), - reason: z.enum(["outside_segments", "not_in_procedure"]), - }), - ], -); -export type ContainingProcedureResolution = z.infer< - typeof containingProcedureResolutionSchema ->; - -/** Validate provider procedure facts before a workflow interprets absence. */ -export const parseContainingProcedureResolution = ( - value: JsonValue, -): Result => { - const parsed = containingProcedureResolutionSchema.safeParse(value); - return parsed.success - ? ok(parsed.data) - : err( - new AnalysisOutputError( - "resolve_containing_procedure", - "expected a containing-procedure resolution", - { cause: parsed.error }, - ), - ); -}; - -export const localVariableSchema = z - .object({ - description: z.string(), - provenance: z.string().min(1), - name: z.string().nullable().exactOptional(), - stack_displacement: z - .string() - .regex(/^-?\d+$/u) - .nullable() - .exactOptional() - .describe( - "Observed signed stack byte displacement, encoded as decimal to preserve precision.", - ), - }) - .strict(); -/** Provider-neutral complete raw-instruction list for one analyzed function. */ -export const functionInstructionWindowSchema = z - .object({ - procedure: procedureIdentitySchema, - instructions: z.array(z.string()), - limitations: z.array(z.string()), - }) - .strict(); - -export const availableReferenceKindSchema = z - .object({ - available: z.literal(true), - provenance: z.string().min(1), - type: z.string(), - flow: z.boolean(), - call: z.boolean(), - jump: z.boolean(), - data: z.boolean(), - read: z.boolean(), - write: z.boolean(), - indirect: z.boolean(), - computed: z.boolean(), - conditional: z.boolean(), - terminal: z.boolean(), - primary: z.boolean(), - operand_index: z.int(), - external: z.boolean(), - }) - .strict(); -export const referenceKindSchema = z.discriminatedUnion("available", [ - unavailableAnalysisFactSchema, - availableReferenceKindSchema, -]); -/** Reported call sites without resolved targets; unreported dispatch stays unknown. */ -export const unresolvedCallSchema = z.strictObject({ - address: z.string(), - reason: z.string(), -}); - -const swiftStringLiteralFields = { - source_address: z - .string() - .describe( - "Instruction that consumes both words: a call, tail call, return, or paired store.", - ), - value: z - .string() - .describe("Decoded UTF-8 text, including the canonical empty string."), - registers: fixedTupleSchema([z.string(), z.string()]), - words: fixedTupleSchema([z.string(), z.string()]).describe( - "Observed constant register values, in register order.", - ), -}; - -/** A Swift String literal decoded from the two words of its ABI value, not a data reference. */ -export const swiftSmallStringLiteralSchema = z.strictObject({ - ...swiftStringLiteralFields, - representation: z - .literal("small") - .describe("The UTF-8 bytes are inside the two words."), - address: z.null().describe("A small literal has no storage of its own."), -}); -export const swiftLargeStringLiteralSchema = z.strictObject({ - ...swiftStringLiteralFields, - representation: z - .literal("large") - .describe( - "An immortal literal whose words hold its count and a pointer to its bytes.", - ), - address: z.string().describe("Address of a large literal's bytes."), -}); -export const swiftStringLiteralSchema = z.discriminatedUnion("representation", [ - swiftSmallStringLiteralSchema, - swiftLargeStringLiteralSchema, -]); - -const swiftStringLiteralOmissionFields = { - source_address: swiftStringLiteralFields.source_address, - byte_length: z.int().min(0).max(Number.MAX_SAFE_INTEGER), - reason: z - .enum(["capacity", "unresolved_jump"]) - .describe( - "Why this candidate is omitted: capacity stopped decoding, or an unresolved jump left its use unknown.", - ), - registers: swiftStringLiteralFields.registers, - words: swiftStringLiteralFields.words, -}; - -/** A Swift literal candidate omitted because decoding capacity or control flow left it unknown. */ -export const swiftSmallStringLiteralOmissionSchema = z.strictObject({ - ...swiftStringLiteralOmissionFields, - representation: z.literal("small"), - address: z.null(), -}); -export const swiftLargeStringLiteralOmissionSchema = z.strictObject({ - ...swiftStringLiteralOmissionFields, - representation: z.literal("large"), - address: z.string().describe("Address of the literal's bytes."), -}); -export const swiftStringLiteralOmissionSchema = z.discriminatedUnion( - "representation", - [ - swiftSmallStringLiteralOmissionSchema, - swiftLargeStringLiteralOmissionSchema, - ], -); - -/** Observed endpoints with complete or partial reference classification evidence. */ -export const referenceEdgeSchema = z - .object({ - source_address: z.string(), - target_address: z.string(), - source_procedure: procedureIdentitySchema.nullable(), - target_procedure: procedureIdentitySchema.nullable(), - kind: referenceKindSchema, - call: nativeCallClassificationSchema.exactOptional(), - }) - .strict(); -const facetAvailabilitySchema = z.discriminatedUnion("available", [ - z.object({ available: z.literal(true) }).strict(), - z.object({ available: z.literal(false), reason: z.string().min(1) }).strict(), -]); - -/** Provider-declared availability per dossier facet; an omitted facet declares nothing. */ -export const functionFacetAvailabilitySchema = z - .object({ - pseudocode: facetAvailabilitySchema.exactOptional(), - assembly: facetAvailabilitySchema.exactOptional(), - comments: facetAvailabilitySchema.exactOptional(), - callers: facetAvailabilitySchema.exactOptional(), - callees: facetAvailabilitySchema.exactOptional(), - references: facetAvailabilitySchema.exactOptional(), - strings_names: facetAvailabilitySchema.exactOptional(), - cfg: facetAvailabilitySchema.exactOptional(), - }) - .strict(); - -export const functionDossierSchema = z - .object({ - procedure: procedureIdentitySchema.safeExtend({ - signature: z.string().nullable(), - locals: z.array(localVariableSchema), - }), - pseudocode: z.string(), - assembly: z.array(z.string()), - comments: z.array( - z - .object({ - address: z.string(), - kind: z.enum(["comment", "inline"]), - text: z.string(), - }) - .strict(), - ), - callers: z.array(procedureIdentitySchema), - callees: z.array(procedureIdentitySchema), - incoming_references: z.array(referenceEdgeSchema), - outgoing_references: z.array(referenceEdgeSchema), - unresolved_calls: z.array(unresolvedCallSchema).exactOptional(), - referenced_strings: z.array( - analysisStringSchema.extend({ source_address: z.string() }), - ), - referenced_names: z.array( - z - .object({ - address: z.string(), - value: z.string(), - source_address: z.string(), - }) - .strict(), - ), - swift_string_literals: z - .array(swiftStringLiteralSchema) - .exactOptional() - .describe( - "Swift String literals decoded from constant register values. Absent when the provider does not decode them for this target.", - ), - swift_string_literal_omissions: z - .array(swiftStringLiteralOmissionSchema) - .exactOptional() - .describe( - "Literal candidates omitted for capacity or unresolved control flow. Present exactly when swift_string_literals is present; empty when all examined use sites were classified.", - ), - basic_blocks: z.array( - z - .object({ - start: z.string(), - end: z.string(), - successors: z.array(z.string()), - }) - .strict(), - ), - native_api: nativeApiBoundarySchema.nullable(), - native_value_flow: nativeValueFlowSchema.nullable(), - facet_availability: functionFacetAvailabilitySchema.exactOptional(), - limitations: z.array(z.string()), - }) - .strict(); - -/** Strict analyzed-function dossier shared by provider and comparison boundaries. */ -export type FunctionDossier = z.infer; - -/** Strictly parse a complete provider-neutral function dossier. */ -export const parseFunctionDossier = ( - value: JsonValue, -): Result => { - const parsed = functionDossierSchema.safeParse(value); - return parsed.success - ? ok(parsed.data) - : err( - new AnalysisOutputError( - "analyze_function", - "provider output did not match the dossier contract", - { cause: parsed.error }, - ), - ); -}; - -/** Parse callee/caller strings from direct or wrapped provider results. */ -export const parseRelatedAddresses = ( - value: JsonValue, - relation: "callees" | "callers", - operation = relation === "callees" - ? "procedure_callees" - : "procedure_callers", -): Result => { - const parsed = z.array(z.string()).safeParse(unwrapProperty(value, relation)); - return parsed.success - ? ok(parsed.data) - : invalid(operation, `${relation} list`, parsed.error); -}; - -/** Preserve reported address context in direct or wrapped provider segments. */ -export const parseSegments = ( - value: JsonValue, - operation = "list_segments", -): Result => { - const parsed = z - .array(segmentSchema) - .safeParse(unwrapProperty(value, "segments")); - return parsed.success - ? ok(parsed.data) - : invalid(operation, "segment list", parsed.error); -}; - -/** Return a reported overview base only when every parsed segment agrees. */ -export const parseSegmentImageBase = ( - segments: readonly SegmentSummary[], -): string | undefined => { - const base = segments[0]?.image_base; - return base !== undefined && - segments.every((segment) => segment.image_base === base) - ? base - : undefined; -}; - -/** Project the same overview from complete lists or provider-owned aggregates. */ -export const projectBinaryOverview = (input: { - readonly document: string; - readonly procedure_count: number; - readonly string_count: number; - readonly segments?: readonly SegmentSummary[]; -}): JsonValue => { - const { document, procedure_count, string_count, segments } = input; - const imageBase = - segments === undefined ? undefined : parseSegmentImageBase(segments); - return { - document, - ...(imageBase === undefined ? {} : { image_base: imageBase }), - ...(segments === undefined - ? {} - : { - segments: segments.map( - ({ name, start, end, image_base, address_space }) => ({ - name, - start, - end, - length: addressDistance(start, end), - ...(image_base === undefined ? {} : { image_base }), - ...(address_space === undefined ? {} : { address_space }), - }), - ), - segment_count: segments.length, - }), - procedure_count, - string_count, - }; -}; - -/** Parse direct or wrapped provider document names. */ -export const parseDocuments = ( - value: JsonValue, - operation = "list_documents", -): Result => { - const parsed = z - .array(z.string()) - .safeParse(unwrapProperty(value, "documents")); - return parsed.success - ? ok(parsed.data) - : invalid(operation, "document list", parsed.error); -}; - -/** Parse a direct or wrapped list when only its cardinality is required. */ -export const parseListCount = ( - value: JsonValue, - property: string, - operation = property === "procedures" ? "list_procedures" : "list_strings", -): Result => { - const unwrapped = unwrapProperty(value, property); - const list = z.array(z.unknown()).safeParse(unwrapped); - if (list.success) return ok(list.data.length); - const map = jsonObjectSchema.safeParse(unwrapped); - return map.success - ? ok(Object.keys(map.data).length) - : invalid(operation, `${property} list`, map.error); -}; - -const unwrapProperty = (value: JsonValue, property: string): JsonValue => { - if ( - typeof value === "object" && - value !== null && - !Array.isArray(value) && - property in value - ) { - return value[property] ?? null; - } - return value; -}; - -const invalid = ( - operation: string, - expected: string, - cause: z.ZodError, -): Result => - err( - new AnalysisOutputError( - operation, - `Provider returned an invalid ${expected}`, - { - cause, - }, - ), - ); diff --git a/src/domain/managed/managedNativeVerificationSource.ts b/src/domain/managed/managedNativeVerificationSource.ts index b47d55f232..c49071e5bf 100644 --- a/src/domain/managed/managedNativeVerificationSource.ts +++ b/src/domain/managed/managedNativeVerificationSource.ts @@ -7,7 +7,7 @@ import { EvidenceIntegrityError } from "../evidenceErrors.js"; import { analysisInputErrorFromIssues } from "../inputIssueProjection.js"; import { readEvidence, readEvidencePayload } from "../evidenceValidation.js"; import { err, ok, type Result } from "../result.js"; -import { functionDossierSchema } from "../hopperValues.js"; +import { functionDossierSchema } from "../native/functionDossier.js"; import { inspectMachoSchema } from "../native/nativeInspection.js"; import { managedNativeBoundaryInspectionSchema, diff --git a/src/domain/native/addressCoordinate.ts b/src/domain/native/addressCoordinate.ts index d43b1b4f14..1511ebc167 100644 --- a/src/domain/native/addressCoordinate.ts +++ b/src/domain/native/addressCoordinate.ts @@ -9,3 +9,14 @@ export const addressCoordinate = ( if (!/^0x[0-9a-f]+$/u.test(offset)) return null; return { space, offset: BigInt(offset) }; }; + +/** Return a bounded byte distance between canonical addresses in one space. */ +export const addressDistance = (start: string, end: string): number | null => { + const left = addressCoordinate(start); + const right = addressCoordinate(end); + if (left === null || right === null || left.space !== right.space) + return null; + const distance = right.offset - left.offset; + if (distance < 0n || distance > BigInt(Number.MAX_SAFE_INTEGER)) return null; + return Number(distance); +}; diff --git a/src/domain/native/functionBody.test.ts b/src/domain/native/functionBody.test.ts new file mode 100644 index 0000000000..e3a4a2019d --- /dev/null +++ b/src/domain/native/functionBody.test.ts @@ -0,0 +1,134 @@ +import { describe, expect, it } from "vitest"; +import { functionBodySchema } from "./functionBody.js"; +import { procedureIdentitySchema } from "./procedureInfo.js"; + +describe("provider-neutral inclusive function body evidence", () => { + const observed = () => ({ + available: true, + provenance: "test-address-set", + ranges: [ + { start: "0x1000", end: "0x1002" }, + { start: "0x1010", end: "0x1011" }, + ], + total_bytes: 5, + span_bytes: 18, + non_contiguous: true, + contains_entry: true, + }); + it("rejects missing provider body metadata", () => { + expect( + procedureIdentitySchema.safeParse({ address: "0x1000", name: "main" }) + .success, + ).toBe(false); + }); + it("counts inclusive one-byte ranges without assuming an enclosing continuous body", () => { + expect( + functionBodySchema.safeParse({ + ...observed(), + ranges: [{ start: "0x1000", end: "0x1000" }], + total_bytes: 1, + span_bytes: 1, + non_contiguous: false, + }).success, + ).toBe(true); + expect(functionBodySchema.parse(observed())).toMatchObject({ + total_bytes: 5, + span_bytes: 18, + }); + }); + it("preserves address-space identity and leaves a multi-space span unknown", () => { + expect( + functionBodySchema.safeParse({ + ...observed(), + ranges: [ + { start: "a:0x1000", end: "a:0x1000" }, + { start: "b:0x1000", end: "b:0x1001" }, + ], + total_bytes: 3, + span_bytes: null, + }).success, + ).toBe(true); + expect( + functionBodySchema.safeParse({ + ...observed(), + ranges: [{ start: "a:0x1000", end: "b:0x1000" }], + }).success, + ).toBe(false); + }); + it("allows an observed empty set without manufacturing entry membership", () => { + expect( + procedureIdentitySchema.safeParse({ + address: "0x1000", + name: "external", + classification: null, + body: { + ...observed(), + ranges: [], + total_bytes: 0, + span_bytes: 0, + non_contiguous: false, + contains_entry: false, + }, + }).success, + ).toBe(true); + }); + it("checks entry membership using address space and inclusive endpoints", () => { + expect( + procedureIdentitySchema.safeParse({ + address: "0x1002", + name: "last", + classification: null, + body: observed(), + }).success, + ).toBe(true); + expect( + procedureIdentitySchema.safeParse({ + address: "0x1003", + name: "gap", + classification: null, + body: observed(), + }).success, + ).toBe(false); + expect( + procedureIdentitySchema.safeParse({ + address: "other:0x1000", + name: "other", + classification: null, + body: observed(), + }).success, + ).toBe(false); + }); + it.each([ + { total_bytes: 18 }, + { span_bytes: 5 }, + { non_contiguous: false }, + { + ranges: [ + { start: "0x1000", end: "0x1002" }, + { start: "0x1003", end: "0x1004" }, + ], + }, + { + ranges: [ + { start: "0x1000", end: "0x1002" }, + { start: "0x1002", end: "0x1004" }, + ], + }, + { + ranges: [ + { start: "0x1010", end: "0x1011" }, + { start: "0x1000", end: "0x1002" }, + ], + }, + { + ranges: [{ start: "0x0", end: "0x20000000000000" }], + total_bytes: Number.MAX_SAFE_INTEGER, + span_bytes: Number.MAX_SAFE_INTEGER, + non_contiguous: false, + }, + ])("rejects contradictory or unsafe range facts %j", (change) => { + expect( + functionBodySchema.safeParse({ ...observed(), ...change }).success, + ).toBe(false); + }); +}); diff --git a/src/domain/native/functionBody.ts b/src/domain/native/functionBody.ts new file mode 100644 index 0000000000..b2df20790b --- /dev/null +++ b/src/domain/native/functionBody.ts @@ -0,0 +1,110 @@ +import { z } from "zod"; +import { addressCoordinate } from "./addressCoordinate.js"; +import { unavailableNativeFactSchema } from "./nativeFactAvailability.js"; + +/** Complete observed function-body ranges; every range endpoint is inclusive. */ +export const availableFunctionBodySchema = z + .strictObject({ + available: z.literal(true), + provenance: z.string().min(1), + ranges: z.array( + z.strictObject({ + start: z.string(), + end: z + .string() + .describe("Inclusive last address of this observed body range."), + }), + ), + total_bytes: z.int().min(0).max(Number.MAX_SAFE_INTEGER), + span_bytes: z.int().min(0).max(Number.MAX_SAFE_INTEGER).nullable(), + non_contiguous: z.boolean(), + contains_entry: z.boolean(), + }) + .superRefine((body, context) => { + let total = 0n; + const bounds = new Map(); + const closedSpaces = new Set(); + let previousSpace: string | null | undefined; + for (const range of body.ranges) { + const start = addressCoordinate(range.start); + const end = addressCoordinate(range.end); + if ( + start === null || + end === null || + start.space !== end.space || + end.offset < start.offset + ) { + context.addIssue({ + code: "custom", + message: "Function body range has invalid inclusive endpoints", + }); + return; + } + if (previousSpace !== undefined && previousSpace !== start.space) + closedSpaces.add(previousSpace); + const previous = bounds.get(start.space); + if ( + closedSpaces.has(start.space) || + (previous !== undefined && start.offset <= previous.end + 1n) + ) { + context.addIssue({ + code: "custom", + message: + "Function body ranges must be ordered, disjoint and maximal within each address space", + }); + return; + } + bounds.set(start.space, { + start: previous?.start ?? start.offset, + end: end.offset, + }); + previousSpace = start.space; + total += end.offset - start.offset + 1n; + } + const onlyBounds = bounds.size === 1 ? [...bounds.values()][0] : undefined; + const span = + bounds.size === 0 + ? 0n + : onlyBounds === undefined + ? null + : onlyBounds.end - onlyBounds.start + 1n; + const reportedSpan = + body.span_bytes === null ? null : BigInt(body.span_bytes); + if ( + total !== BigInt(body.total_bytes) || + span !== reportedSpan || + body.non_contiguous !== body.ranges.length > 1 + ) + context.addIssue({ + code: "custom", + message: + "Function body counts, enclosing span or contiguity disagree with inclusive ranges", + }); + }); + +export const functionBodySchema = z.discriminatedUnion("available", [ + unavailableNativeFactSchema, + availableFunctionBodySchema, +]); + +/** Check the declared entry membership against complete, inclusive body ranges. */ +export const functionBodyEntryAgrees = ( + body: z.infer, + address: string, +): boolean => { + if (!body.available) return true; + const entry = addressCoordinate(address); + if (entry === null) return false; + const contains = body.ranges.some((range) => { + const start = addressCoordinate(range.start); + const end = addressCoordinate(range.end); + return ( + start !== null && + end !== null && + start.space === entry.space && + entry.offset >= start.offset && + entry.offset <= end.offset + ); + }); + return contains === body.contains_entry; +}; diff --git a/src/domain/native/functionByteCoverage.ts b/src/domain/native/functionByteCoverage.ts index c5f1a75ebf..8dcce92d63 100644 --- a/src/domain/native/functionByteCoverage.ts +++ b/src/domain/native/functionByteCoverage.ts @@ -1,6 +1,6 @@ import { createHash } from "node:crypto"; import type { z } from "zod"; -import type { functionBodySchema } from "../hopperValues.js"; +import type { functionBodySchema } from "../native/functionBody.js"; import type { nativeByteReadSchema } from "./nativeByteRead.js"; import { addressCoordinate } from "./addressCoordinate.js"; diff --git a/src/domain/native/functionDossier.ts b/src/domain/native/functionDossier.ts new file mode 100644 index 0000000000..7155530883 --- /dev/null +++ b/src/domain/native/functionDossier.ts @@ -0,0 +1,128 @@ +import { z } from "zod"; +import { err, ok, type Result } from "../result.js"; +import { AnalysisOutputError } from "../analysisErrorCore.js"; +import type { JsonValue } from "../jsonValue.js"; +import { + procedureIdentitySchema, + localVariableSchema, +} from "./procedureInfo.js"; +import { analysisStringSchema } from "./nativeString.js"; +import { + referenceEdgeSchema, + unresolvedCallSchema, +} from "./nativeReference.js"; +import { + swiftStringLiteralSchema, + swiftStringLiteralOmissionSchema, +} from "./swiftStringLiteral.js"; +import { nativeApiBoundarySchema } from "./nativeApiBoundary.js"; +import { nativeValueFlowSchema } from "./nativeValueFlow.js"; + +/** Provider-neutral complete raw-instruction list for one analyzed function. */ +export const functionInstructionWindowSchema = z + .object({ + procedure: procedureIdentitySchema, + instructions: z.array(z.string()), + limitations: z.array(z.string()), + }) + .strict(); + +const facetAvailabilitySchema = z.discriminatedUnion("available", [ + z.object({ available: z.literal(true) }).strict(), + z.object({ available: z.literal(false), reason: z.string().min(1) }).strict(), +]); + +/** Provider-declared availability per dossier facet; an omitted facet declares nothing. */ +export const functionFacetAvailabilitySchema = z + .object({ + pseudocode: facetAvailabilitySchema.exactOptional(), + assembly: facetAvailabilitySchema.exactOptional(), + comments: facetAvailabilitySchema.exactOptional(), + callers: facetAvailabilitySchema.exactOptional(), + callees: facetAvailabilitySchema.exactOptional(), + references: facetAvailabilitySchema.exactOptional(), + strings_names: facetAvailabilitySchema.exactOptional(), + cfg: facetAvailabilitySchema.exactOptional(), + }) + .strict(); + +export const functionDossierSchema = z + .object({ + procedure: procedureIdentitySchema.safeExtend({ + signature: z.string().nullable(), + locals: z.array(localVariableSchema), + }), + pseudocode: z.string(), + assembly: z.array(z.string()), + comments: z.array( + z + .object({ + address: z.string(), + kind: z.enum(["comment", "inline"]), + text: z.string(), + }) + .strict(), + ), + callers: z.array(procedureIdentitySchema), + callees: z.array(procedureIdentitySchema), + incoming_references: z.array(referenceEdgeSchema), + outgoing_references: z.array(referenceEdgeSchema), + unresolved_calls: z.array(unresolvedCallSchema).exactOptional(), + referenced_strings: z.array( + analysisStringSchema.extend({ source_address: z.string() }), + ), + referenced_names: z.array( + z + .object({ + address: z.string(), + value: z.string(), + source_address: z.string(), + }) + .strict(), + ), + swift_string_literals: z + .array(swiftStringLiteralSchema) + .exactOptional() + .describe( + "Swift String literals decoded from constant register values. Absent when the provider does not decode them for this target.", + ), + swift_string_literal_omissions: z + .array(swiftStringLiteralOmissionSchema) + .exactOptional() + .describe( + "Literal candidates omitted for capacity or unresolved control flow. Present exactly when swift_string_literals is present; empty when all examined use sites were classified.", + ), + basic_blocks: z.array( + z + .object({ + start: z.string(), + end: z.string(), + successors: z.array(z.string()), + }) + .strict(), + ), + native_api: nativeApiBoundarySchema.nullable(), + native_value_flow: nativeValueFlowSchema.nullable(), + facet_availability: functionFacetAvailabilitySchema.exactOptional(), + limitations: z.array(z.string()), + }) + .strict(); + +/** Strict analyzed-function dossier shared by provider and comparison boundaries. */ +export type FunctionDossier = z.infer; + +/** Strictly parse a complete provider-neutral function dossier. */ +export const parseFunctionDossier = ( + value: JsonValue, +): Result => { + const parsed = functionDossierSchema.safeParse(value); + return parsed.success + ? ok(parsed.data) + : err( + new AnalysisOutputError( + "analyze_function", + "provider output did not match the dossier contract", + { cause: parsed.error }, + ), + ); +}; diff --git a/src/domain/native/functionInventory.ts b/src/domain/native/functionInventory.ts index 668eb8a5b0..b43e1a1179 100644 --- a/src/domain/native/functionInventory.ts +++ b/src/domain/native/functionInventory.ts @@ -4,11 +4,11 @@ import type { Evidence } from "../evidence.js"; import { EvidenceIntegrityError } from "../evidenceErrors.js"; import { readEvidencePayload } from "../evidenceValidation.js"; import { digestCanonicalValue } from "../canonicalDigest.js"; +import { functionDossierSchema } from "../native/functionDossier.js"; import { functionBodySchema, functionBodyEntryAgrees, - functionDossierSchema, -} from "../hopperValues.js"; +} from "../native/functionBody.js"; import { nativeSymbolOrigin } from "./nativeSymbol.js"; import { addressedValueSchema, diff --git a/src/domain/native/functionStructuralCandidate.ts b/src/domain/native/functionStructuralCandidate.ts index e7d2529316..5fcb4d7e85 100644 --- a/src/domain/native/functionStructuralCandidate.ts +++ b/src/domain/native/functionStructuralCandidate.ts @@ -1,5 +1,5 @@ import type { Evidence } from "../evidence.js"; -import type { FunctionDossier } from "../hopperValues.js"; +import type { FunctionDossier } from "../native/functionDossier.js"; import { digestCanonicalValue } from "../canonicalDigest.js"; import { normalizeCfg } from "../functionComparisonNormalization.js"; import { hasStableNativeSymbol } from "./nativeSymbol.js"; diff --git a/src/domain/native/literalFeatureMatch.ts b/src/domain/native/literalFeatureMatch.ts index 0e5fc195af..26e7c5f4a3 100644 --- a/src/domain/native/literalFeatureMatch.ts +++ b/src/domain/native/literalFeatureMatch.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { analysisStringSchema } from "../hopperValues.js"; +import { analysisStringSchema } from "../native/nativeString.js"; import { addressedValueSchema } from "./nativeInventory.js"; /** Tracing reuses the search row's qualifications without reinterpreting them. */ diff --git a/src/domain/native/nativeDocument.ts b/src/domain/native/nativeDocument.ts new file mode 100644 index 0000000000..ddfe059052 --- /dev/null +++ b/src/domain/native/nativeDocument.ts @@ -0,0 +1,21 @@ +import { z } from "zod"; +import { ok, type Result } from "../result.js"; +import { AnalysisOutputError } from "../analysisErrorCore.js"; +import type { JsonValue } from "../jsonValue.js"; +import { + unwrapNativeProperty, + invalidNativeOutput, +} from "./nativeProviderOutput.js"; + +/** Parse direct or wrapped provider document names. */ +export const parseDocuments = ( + value: JsonValue, + operation = "list_documents", +): Result => { + const parsed = z + .array(z.string()) + .safeParse(unwrapNativeProperty(value, "documents")); + return parsed.success + ? ok(parsed.data) + : invalidNativeOutput(operation, "document list", parsed.error); +}; diff --git a/src/domain/native/nativeFactAvailability.ts b/src/domain/native/nativeFactAvailability.ts new file mode 100644 index 0000000000..19f33e09ed --- /dev/null +++ b/src/domain/native/nativeFactAvailability.ts @@ -0,0 +1,6 @@ +import { z } from "zod"; + +/** An explicitly unavailable provider fact preserves its reported reason. */ +export const unavailableNativeFactSchema = z + .object({ available: z.literal(false), reason: z.string() }) + .strict(); diff --git a/src/domain/native/nativeFunctionAnnotations.ts b/src/domain/native/nativeFunctionAnnotations.ts index 046200e920..0e5d5f12b8 100644 --- a/src/domain/native/nativeFunctionAnnotations.ts +++ b/src/domain/native/nativeFunctionAnnotations.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { functionDossierSchema } from "../hopperValues.js"; +import { functionDossierSchema } from "../native/functionDossier.js"; /** One explicit function's analyst-authored metadata changes. */ export const nativeFunctionAnnotationsInputSchema = z diff --git a/src/domain/native/nativeInventory.ts b/src/domain/native/nativeInventory.ts index 8e811c784d..ef7d932730 100644 --- a/src/domain/native/nativeInventory.ts +++ b/src/domain/native/nativeInventory.ts @@ -1,6 +1,14 @@ import { z } from "zod"; import { nativeSymbolMetadataSchema } from "./nativeSymbol.js"; -import { analysisStringSchema } from "../hopperValues.js"; +import { analysisStringSchema } from "./nativeString.js"; +import { ok, type Result } from "../result.js"; +import { AnalysisOutputError } from "../analysisErrorCore.js"; +import type { JsonValue } from "../jsonValue.js"; +import { jsonObjectSchema } from "../jsonValue.js"; +import { + unwrapNativeProperty, + invalidNativeOutput, +} from "./nativeProviderOutput.js"; /** Canonical named native observations; same-address alternatives stay separate. */ export const addressedValueSchema = z.object({ @@ -49,3 +57,29 @@ export const isNativeInventoryOperation = ( operation: string, ): operation is NativeInventoryOperation => NATIVE_INVENTORY_OPERATIONS.some((supported) => supported === operation); + +export interface AddressedName { + readonly address: string; + readonly name: string; + readonly objc_class_label?: "hopper_legacy_class_object"; +} + +/** An analyzed bookmark retains a missing provider label as null. */ +export const analysisBookmarkSchema = z.strictObject({ + address: z.string(), + name: z.string().nullable(), +}); +/** Parse a direct or wrapped list when only its cardinality is required. */ +export const parseListCount = ( + value: JsonValue, + property: string, + operation = property === "procedures" ? "list_procedures" : "list_strings", +): Result => { + const unwrapped = unwrapNativeProperty(value, property); + const list = z.array(z.unknown()).safeParse(unwrapped); + if (list.success) return ok(list.data.length); + const map = jsonObjectSchema.safeParse(unwrapped); + return map.success + ? ok(Object.keys(map.data).length) + : invalidNativeOutput(operation, `${property} list`, map.error); +}; diff --git a/src/domain/native/nativeObservation.ts b/src/domain/native/nativeObservation.ts index 4ad3723217..f8b170c342 100644 --- a/src/domain/native/nativeObservation.ts +++ b/src/domain/native/nativeObservation.ts @@ -1,7 +1,51 @@ +import { + unwrapNativeProperty, + invalidNativeOutput, +} from "./nativeProviderOutput.js"; import { z } from "zod"; -import { containingProcedureResolutionSchema } from "../hopperValues.js"; +import { procedureIdentitySchema } from "./procedureInfo.js"; +import { AnalysisOutputError } from "../analysisErrorCore.js"; +import type { JsonValue } from "../jsonValue.js"; +import { err, ok, type Result } from "../result.js"; import { nativeCallGraphSchema } from "./nativeCallGraph.js"; +/** Exact containing-procedure outcome; valid absence is distinct from unreadable output. */ +export const containingProcedureResolutionSchema = z.discriminatedUnion( + "found", + [ + z.object({ + query_address: z.string(), + found: z.literal(true), + procedure: procedureIdentitySchema, + }), + z.object({ + query_address: z.string(), + found: z.literal(false), + procedure: z.null(), + reason: z.enum(["outside_segments", "not_in_procedure"]), + }), + ], +); +export type ContainingProcedureResolution = z.infer< + typeof containingProcedureResolutionSchema +>; + +/** Validate provider procedure facts before a workflow interprets absence. */ +export const parseContainingProcedureResolution = ( + value: JsonValue, +): Result => { + const parsed = containingProcedureResolutionSchema.safeParse(value); + return parsed.success + ? ok(parsed.data) + : err( + new AnalysisOutputError( + "resolve_containing_procedure", + "expected a containing-procedure resolution", + { cause: parsed.error }, + ), + ); +}; + export const NATIVE_PRIMITIVE_OBSERVATION_OPERATIONS = [ "procedure_address", "procedure_callees", @@ -34,3 +78,19 @@ export const isNativeObservationOperation = ( operation: string, ): operation is NativeObservationOperation => NATIVE_OBSERVATION_OPERATIONS.some((supported) => supported === operation); + +/** Parse callee/caller strings from direct or wrapped provider results. */ +export const parseRelatedAddresses = ( + value: JsonValue, + relation: "callees" | "callers", + operation = relation === "callees" + ? "procedure_callees" + : "procedure_callers", +): Result => { + const parsed = z + .array(z.string()) + .safeParse(unwrapNativeProperty(value, relation)); + return parsed.success + ? ok(parsed.data) + : invalidNativeOutput(operation, `${relation} list`, parsed.error); +}; diff --git a/src/domain/native/nativeProviderOutput.test.ts b/src/domain/native/nativeProviderOutput.test.ts new file mode 100644 index 0000000000..f315587800 --- /dev/null +++ b/src/domain/native/nativeProviderOutput.test.ts @@ -0,0 +1,129 @@ +import { describe, expect, it } from "vitest"; + +import { addressDistance } from "./addressCoordinate.js"; +import { parseDocuments } from "./nativeDocument.js"; +import { parseListCount } from "./nativeInventory.js"; +import { parseRelatedAddresses } from "./nativeObservation.js"; +import { parseSegments } from "./nativeSegments.js"; + +describe("native provider boundary values", () => { + it("distinguishes measured zero, malformed, incomparable, reversed, and oversized extents", () => { + expect(addressDistance("not-an-address", "0x2000")).toBeNull(); + expect(addressDistance("ram:0x1000", "rom:0x2000")).toBeNull(); + expect(addressDistance("0x2000", "0x1000")).toBeNull(); + expect(addressDistance("0x1000", "0x1000")).toBe(0); + expect(addressDistance("0x1000", "0x2000")).toBe(4096); + expect(addressDistance("0x0", "0x20000000000000")).toBeNull(); + }); + + it("rejects segments when required coordinates or unknown permissions are omitted", () => { + expect( + parseSegments([ + { + name: "__TEXT", + start: "0x1", + end: "0x2", + readable: null, + writable: null, + }, + ]).ok, + ).toBe(false); + expect( + parseSegments([{ name: "__TEXT", start: "0x1", end: "0x2" }]).ok, + ).toBe(false); + }); + it("counts Hopper's address-keyed string maps", () => { + expect( + parseListCount({ "0x1000": "hello", "0x2000": "world" }, "strings"), + ).toEqual({ ok: true, value: 2 }); + }); + + it("parses direct and wrapped protocol shapes", () => { + expect(parseRelatedAddresses({ callers: ["0x3"] }, "callers")).toEqual({ + ok: true, + value: ["0x3"], + }); + expect( + parseSegments([ + { + name: "__TEXT", + start: "0x1", + end: "0x2", + readable: true, + writable: false, + executable: null, + }, + ]), + ).toEqual({ + ok: true, + value: [ + { + name: "__TEXT", + start: "0x1", + end: "0x2", + readable: true, + writable: false, + executable: null, + }, + ], + }); + expect(parseDocuments(["fixture"])).toEqual({ + ok: true, + value: ["fixture"], + }); + }); + + it.each([ + ["relations", () => parseRelatedAddresses([1], "callees")], + ["segments", () => parseSegments([{ name: 1 }])], + ["documents", () => parseDocuments([1])], + ])( + "rejects malformed %s instead of manufacturing empty output", + (_label, parse) => { + const result = parse(); + expect(result.ok).toBe(false); + if (!result.ok) expect(result.error._tag).toBe("AnalysisOutputError"); + }, + ); + + it("uses provider-neutral wording for malformed shared values", () => { + const result = parseSegments([{ name: 1 }]); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error._tag).toBe("AnalysisOutputError"); + expect(result.error.operation).toBe("list_segments"); + expect(result.error.reason).toBe( + "Provider returned an invalid segment list", + ); + } + }); +}); + +it.each(["procedures", "strings"])( + "counts every own entry of direct and wrapped %s maps", + (property) => { + for (const text of [ + '{"__proto__":"observed"}', + '{"__proto__":"observed","0x1000":"one"}', + ]) { + const source = JSON.parse(text); + const count = Object.keys(source).length; + expect(parseListCount(source, property)).toEqual({ + ok: true, + value: count, + }); + expect(parseListCount({ [property]: source }, property)).toEqual({ + ok: true, + value: count, + }); + expect(JSON.stringify(source)).toBe(text); + } + expect(parseListCount({ [property]: [] }, property)).toEqual({ + ok: true, + value: 0, + }); + expect(parseListCount({}, property)).toEqual({ ok: true, value: 0 }); + for (const value of [null, "not a list", 3, false]) + expect(parseListCount(value, property).ok).toBe(false); + }, +); diff --git a/src/domain/native/nativeProviderOutput.ts b/src/domain/native/nativeProviderOutput.ts new file mode 100644 index 0000000000..5dee5058dd --- /dev/null +++ b/src/domain/native/nativeProviderOutput.ts @@ -0,0 +1,34 @@ +import { z } from "zod"; +import { err, type Result } from "../result.js"; +import { AnalysisOutputError } from "../analysisErrorCore.js"; +import type { JsonValue } from "../jsonValue.js"; + +export const unwrapNativeProperty = ( + value: JsonValue, + property: string, +): JsonValue => { + if ( + typeof value === "object" && + value !== null && + !Array.isArray(value) && + property in value + ) { + return value[property] ?? null; + } + return value; +}; + +export const invalidNativeOutput = ( + operation: string, + expected: string, + cause: z.ZodError, +): Result => + err( + new AnalysisOutputError( + operation, + `Provider returned an invalid ${expected}`, + { + cause, + }, + ), + ); diff --git a/src/domain/native/nativeReference.ts b/src/domain/native/nativeReference.ts new file mode 100644 index 0000000000..4043fe463d --- /dev/null +++ b/src/domain/native/nativeReference.ts @@ -0,0 +1,46 @@ +import { z } from "zod"; +import { unavailableNativeFactSchema } from "./nativeFactAvailability.js"; +import { procedureIdentitySchema } from "./procedureInfo.js"; +import { nativeCallClassificationSchema } from "./nativeCallReference.js"; + +export const availableReferenceKindSchema = z + .object({ + available: z.literal(true), + provenance: z.string().min(1), + type: z.string(), + flow: z.boolean(), + call: z.boolean(), + jump: z.boolean(), + data: z.boolean(), + read: z.boolean(), + write: z.boolean(), + indirect: z.boolean(), + computed: z.boolean(), + conditional: z.boolean(), + terminal: z.boolean(), + primary: z.boolean(), + operand_index: z.int(), + external: z.boolean(), + }) + .strict(); +export const referenceKindSchema = z.discriminatedUnion("available", [ + unavailableNativeFactSchema, + availableReferenceKindSchema, +]); +/** Reported call sites without resolved targets; unreported dispatch stays unknown. */ +export const unresolvedCallSchema = z.strictObject({ + address: z.string(), + reason: z.string(), +}); + +/** Observed endpoints with complete or partial reference classification evidence. */ +export const referenceEdgeSchema = z + .object({ + source_address: z.string(), + target_address: z.string(), + source_procedure: procedureIdentitySchema.nullable(), + target_procedure: procedureIdentitySchema.nullable(), + kind: referenceKindSchema, + call: nativeCallClassificationSchema.exactOptional(), + }) + .strict(); diff --git a/src/domain/native/nativeSegments.ts b/src/domain/native/nativeSegments.ts new file mode 100644 index 0000000000..04c9a31102 --- /dev/null +++ b/src/domain/native/nativeSegments.ts @@ -0,0 +1,87 @@ +import { z } from "zod"; +import { ok, type Result } from "../result.js"; +import { AnalysisOutputError } from "../analysisErrorCore.js"; +import type { JsonValue } from "../jsonValue.js"; +import { + unwrapNativeProperty, + invalidNativeOutput, +} from "./nativeProviderOutput.js"; +import { addressDistance } from "./addressCoordinate.js"; + +export interface SegmentSummary { + readonly name: string; + readonly start: string; + readonly end: string; + readonly readable: boolean | null; + readonly writable: boolean | null; + readonly executable: boolean | null; + readonly image_base?: string; + readonly address_space?: string; +} + +const segmentSchema = z.object({ + name: z.string(), + start: z.string(), + end: z.string(), + readable: z.boolean().nullable(), + writable: z.boolean().nullable(), + executable: z.boolean().nullable(), + image_base: z.string().min(1).exactOptional(), + address_space: z.string().min(1).exactOptional(), +}); +/** Preserve reported address context in direct or wrapped provider segments. */ +export const parseSegments = ( + value: JsonValue, + operation = "list_segments", +): Result => { + const parsed = z + .array(segmentSchema) + .safeParse(unwrapNativeProperty(value, "segments")); + return parsed.success + ? ok(parsed.data) + : invalidNativeOutput(operation, "segment list", parsed.error); +}; + +/** Return a reported overview base only when every parsed segment agrees. */ +export const parseSegmentImageBase = ( + segments: readonly SegmentSummary[], +): string | undefined => { + const base = segments[0]?.image_base; + return base !== undefined && + segments.every((segment) => segment.image_base === base) + ? base + : undefined; +}; + +/** Project the same overview from complete lists or provider-owned aggregates. */ +export const projectBinaryOverview = (input: { + readonly document: string; + readonly procedure_count: number; + readonly string_count: number; + readonly segments?: readonly SegmentSummary[]; +}): JsonValue => { + const { document, procedure_count, string_count, segments } = input; + const imageBase = + segments === undefined ? undefined : parseSegmentImageBase(segments); + return { + document, + ...(imageBase === undefined ? {} : { image_base: imageBase }), + ...(segments === undefined + ? {} + : { + segments: segments.map( + ({ name, start, end, image_base, address_space }) => ({ + name, + start, + end, + length: addressDistance(start, end), + ...(image_base === undefined ? {} : { image_base }), + ...(address_space === undefined ? {} : { address_space }), + }), + ), + segment_count: segments.length, + }), + procedure_count, + string_count, + }; +}; diff --git a/src/domain/native/nativeString.ts b/src/domain/native/nativeString.ts new file mode 100644 index 0000000000..d8ab3ca3bf --- /dev/null +++ b/src/domain/native/nativeString.ts @@ -0,0 +1,32 @@ +import { z } from "zod"; +import { unavailableNativeFactSchema } from "./nativeFactAvailability.js"; + +/** Typed string evidence shared by inventories and referenced function literals. */ +export const analysisStringSchema = z.object({ + address: z.string(), + value: z.string(), + provider_value: z + .string() + .exactOptional() + .describe( + "Original provider display text, which may shorten the typed object.", + ), + decoding: unavailableNativeFactSchema + .exactOptional() + .describe( + "When present, value retains native display text because typed bytes could not be decoded.", + ), + string: z + .object({ + encoding: z.string().min(1), + encoding_status: z + .enum(["observed", "inferred"]) + .exactOptional() + .describe( + "Whether the provider declares the encoding or REA infers it from the typed bytes and native display. This does not establish the original source's intended encoding.", + ), + termination: z.enum(["missing", "present_or_not_required"]), + byte_length: z.int().min(0), + }) + .exactOptional(), +}); diff --git a/src/domain/native/procedureInfo.ts b/src/domain/native/procedureInfo.ts index 8baf87143f..102c5fe23d 100644 --- a/src/domain/native/procedureInfo.ts +++ b/src/domain/native/procedureInfo.ts @@ -1,11 +1,47 @@ -import { nativeSymbolMetadataSchema } from "./nativeSymbol.js"; import { z } from "zod"; -import { - functionBodySchema, - localVariableSchema, - procedureClassificationSchema, -} from "../hopperValues.js"; +import { nativeSymbolMetadataSchema } from "./nativeSymbol.js"; +import { functionBodySchema, functionBodyEntryAgrees } from "./functionBody.js"; +export const procedureClassificationSchema = z + .object({ + external: z.boolean(), + thunk: z.boolean(), + thunk_target: z.string().nullable(), + provenance: z.string().min(1), + }) + .strict(); +export const procedureIdentitySchema = z + .object({ + address: z.string(), + name: z.string(), + symbol: nativeSymbolMetadataSchema.nullable().exactOptional(), + classification: procedureClassificationSchema.nullable(), + body: functionBodySchema, + }) + .strict() + .superRefine((identity, context) => { + if (!functionBodyEntryAgrees(identity.body, identity.address)) + context.addIssue({ + code: "custom", + message: + "Function body entry membership disagrees with procedure identity", + }); + }); +export const localVariableSchema = z + .object({ + description: z.string(), + provenance: z.string().min(1), + name: z.string().nullable().exactOptional(), + stack_displacement: z + .string() + .regex(/^-?\d+$/u) + .nullable() + .exactOptional() + .describe( + "Observed signed stack byte displacement, encoded as decimal to preserve precision.", + ), + }) + .strict(); /** Provider-neutral procedure metadata, including exact body ranges. */ export const procedureInfoSchema = z.object({ name: z.string(), diff --git a/src/domain/native/swiftStringLiteral.ts b/src/domain/native/swiftStringLiteral.ts new file mode 100644 index 0000000000..c8a8760a89 --- /dev/null +++ b/src/domain/native/swiftStringLiteral.ts @@ -0,0 +1,70 @@ +import { z } from "zod"; +import { fixedTupleSchema } from "../fixedTupleSchema.js"; + +const swiftStringLiteralFields = { + source_address: z + .string() + .describe( + "Instruction that consumes both words: a call, tail call, return, or paired store.", + ), + value: z + .string() + .describe("Decoded UTF-8 text, including the canonical empty string."), + registers: fixedTupleSchema([z.string(), z.string()]), + words: fixedTupleSchema([z.string(), z.string()]).describe( + "Observed constant register values, in register order.", + ), +}; + +/** A Swift String literal decoded from the two words of its ABI value, not a data reference. */ +export const swiftSmallStringLiteralSchema = z.strictObject({ + ...swiftStringLiteralFields, + representation: z + .literal("small") + .describe("The UTF-8 bytes are inside the two words."), + address: z.null().describe("A small literal has no storage of its own."), +}); +export const swiftLargeStringLiteralSchema = z.strictObject({ + ...swiftStringLiteralFields, + representation: z + .literal("large") + .describe( + "An immortal literal whose words hold its count and a pointer to its bytes.", + ), + address: z.string().describe("Address of a large literal's bytes."), +}); +export const swiftStringLiteralSchema = z.discriminatedUnion("representation", [ + swiftSmallStringLiteralSchema, + swiftLargeStringLiteralSchema, +]); + +const swiftStringLiteralOmissionFields = { + source_address: swiftStringLiteralFields.source_address, + byte_length: z.int().min(0).max(Number.MAX_SAFE_INTEGER), + reason: z + .enum(["capacity", "unresolved_jump"]) + .describe( + "Why this candidate is omitted: capacity stopped decoding, or an unresolved jump left its use unknown.", + ), + registers: swiftStringLiteralFields.registers, + words: swiftStringLiteralFields.words, +}; + +/** A Swift literal candidate omitted because decoding capacity or control flow left it unknown. */ +export const swiftSmallStringLiteralOmissionSchema = z.strictObject({ + ...swiftStringLiteralOmissionFields, + representation: z.literal("small"), + address: z.null(), +}); +export const swiftLargeStringLiteralOmissionSchema = z.strictObject({ + ...swiftStringLiteralOmissionFields, + representation: z.literal("large"), + address: z.string().describe("Address of the literal's bytes."), +}); +export const swiftStringLiteralOmissionSchema = z.discriminatedUnion( + "representation", + [ + swiftSmallStringLiteralOmissionSchema, + swiftLargeStringLiteralOmissionSchema, + ], +); diff --git a/src/domain/reconstructionVerification.functionSources.test.ts b/src/domain/reconstructionVerification.functionSources.test.ts index df111467a8..a083719e2e 100644 --- a/src/domain/reconstructionVerification.functionSources.test.ts +++ b/src/domain/reconstructionVerification.functionSources.test.ts @@ -11,7 +11,7 @@ import { type EvidenceSubjectTarget, } from "./evidence.js"; import { createEvidenceBundle, parseEvidenceBundle } from "./evidenceBundle.js"; -import { functionDossierSchema } from "./hopperValues.js"; +import { functionDossierSchema } from "./native/functionDossier.js"; import { jsonObjectSchema, jsonValueSchema, diff --git a/src/domain/reconstructionVerification.ts b/src/domain/reconstructionVerification.ts index f23e83d868..f151b0a233 100644 --- a/src/domain/reconstructionVerification.ts +++ b/src/domain/reconstructionVerification.ts @@ -13,7 +13,7 @@ import { AnalysisInputError } from "./analysisErrorCore.js"; import { EvidenceIntegrityError } from "./evidenceErrors.js"; import { analysisInputErrorFromIssues } from "./inputIssueProjection.js"; import { readEvidencePayload } from "./evidenceValidation.js"; -import { functionDossierSchema } from "./hopperValues.js"; +import { functionDossierSchema } from "./native/functionDossier.js"; import { assessFunctionCallCoverage } from "./functionCallCoverage.js"; import { processCaptureSchema } from "./process/processCapture.js"; import { digestCanonicalValue } from "./canonicalDigest.js"; diff --git a/src/domain/staticRuntimeCorrelation.test.ts b/src/domain/staticRuntimeCorrelation.test.ts index 1fe58af428..e22bd341d2 100644 --- a/src/domain/staticRuntimeCorrelation.test.ts +++ b/src/domain/staticRuntimeCorrelation.test.ts @@ -11,7 +11,7 @@ import { createEvidence, type Evidence } from "./evidence.js"; import { functionComparisonResultSchema } from "./functionComparisonSchemas.js"; import { jsonValueSchema, type JsonValue } from "./jsonValue.js"; import { FUNCTION_COMPARISON_EXAMPLE } from "../contracts/functionComparisonExample.js"; -import { functionDossierSchema } from "./hopperValues.js"; +import { functionDossierSchema } from "./native/functionDossier.js"; import { compareFunctions } from "./functionComparison.js"; import { functionSnapshotFixture } from "./functionDossierEvidence.fixture.js"; import { diff --git a/src/domain/symbolAnalysis.ts b/src/domain/symbolAnalysis.ts index e3d197e0fb..e178437d1f 100644 --- a/src/domain/symbolAnalysis.ts +++ b/src/domain/symbolAnalysis.ts @@ -1,6 +1,6 @@ import { objcClassSymbol } from "./objcClassSymbol.js"; import type { JsonValue } from "./jsonValue.js"; -import type { AddressedName } from "./hopperValues.js"; +import type { AddressedName } from "./native/nativeInventory.js"; import type { MachoImportBinding, ObjcClassImportBindingCoverage, diff --git a/src/ghidra/GhidraFunctionValues.test.ts b/src/ghidra/GhidraFunctionValues.test.ts index bde8f319e8..9c33de190a 100644 --- a/src/ghidra/GhidraFunctionValues.test.ts +++ b/src/ghidra/GhidraFunctionValues.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest"; import type { JsonValue } from "../domain/jsonValue.js"; import { jsonValueSchema } from "../domain/jsonValue.js"; -import { functionDossierSchema } from "../domain/hopperValues.js"; +import { functionDossierSchema } from "../domain/native/functionDossier.js"; import { compactReference, compactInstruction, diff --git a/src/ghidra/GhidraFunctionValues.ts b/src/ghidra/GhidraFunctionValues.ts index 8f5ec281ce..dc24c22871 100644 --- a/src/ghidra/GhidraFunctionValues.ts +++ b/src/ghidra/GhidraFunctionValues.ts @@ -20,16 +20,20 @@ import { AnalysisOutputError, } from "../domain/analysisErrorCore.js"; import { - functionBodyEntryAgrees, functionInstructionWindowSchema, functionDossierSchema, +} from "../domain/native/functionDossier.js"; +import { availableReferenceKindSchema, unresolvedCallSchema, +} from "../domain/native/nativeReference.js"; +import { swiftSmallStringLiteralSchema, swiftLargeStringLiteralSchema, swiftSmallStringLiteralOmissionSchema, swiftLargeStringLiteralOmissionSchema, -} from "../domain/hopperValues.js"; +} from "../domain/native/swiftStringLiteral.js"; +import { functionBodyEntryAgrees } from "../domain/native/functionBody.js"; import { jsonObjectSchema, jsonValueSchema, diff --git a/src/ghidra/GhidraInventoryValues.ts b/src/ghidra/GhidraInventoryValues.ts index 7002f501e8..206a451065 100644 --- a/src/ghidra/GhidraInventoryValues.ts +++ b/src/ghidra/GhidraInventoryValues.ts @@ -19,12 +19,12 @@ import { type AnalysisInputError, AnalysisOutputError, } from "../domain/analysisErrorCore.js"; +import { projectBinaryOverview } from "../domain/native/nativeSegments.js"; import { availableFunctionBodySchema, - procedureIdentitySchema, functionBodyEntryAgrees, - projectBinaryOverview, -} from "../domain/hopperValues.js"; +} from "../domain/native/functionBody.js"; +import { procedureIdentitySchema } from "../domain/native/procedureInfo.js"; import { jsonValueSchema, type JsonValue } from "../domain/jsonValue.js"; import { err, ok, type Result } from "../domain/result.js"; import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js"; diff --git a/src/ghidra/GhidraSnapshotCache.test.ts b/src/ghidra/GhidraSnapshotCache.test.ts index e7b105dbc3..dde944b5c2 100644 --- a/src/ghidra/GhidraSnapshotCache.test.ts +++ b/src/ghidra/GhidraSnapshotCache.test.ts @@ -6,7 +6,7 @@ import { createTestBinarySession } from "../../tests/fixtures/binarySession.js"; import { parseConfig } from "../config/parseConfig.js"; import type { AnalysisOperation } from "../application/AnalysisProvider.js"; import type { BinaryTarget } from "../domain/binaryTargetTypes.js"; -import { functionDossierSchema } from "../domain/hopperValues.js"; +import { functionDossierSchema } from "../domain/native/functionDossier.js"; import { createAnalysisProfile } from "../domain/analysisProfile.js"; import { ghidraFunctionDossier } from "../domain/ghidraValues.fixture.js"; import { silentLogger } from "../logger.js"; diff --git a/src/hopper/HopperRegexSearch.ts b/src/hopper/HopperRegexSearch.ts index a0d34410fb..b49fac82dc 100644 --- a/src/hopper/HopperRegexSearch.ts +++ b/src/hopper/HopperRegexSearch.ts @@ -6,7 +6,7 @@ import { AnalysisCancelledError, AnalysisOutputError, } from "../domain/analysisErrorCore.js"; -import { analysisStringSchema } from "../domain/hopperValues.js"; +import { analysisStringSchema } from "../domain/native/nativeString.js"; import { HopperRemoteError } from "../domain/hopperErrors.js"; import type { JsonValue } from "../domain/jsonValue.js"; import { err, ok, type Result } from "../domain/result.js"; diff --git a/src/ida/IdaOperationRunner.ts b/src/ida/IdaOperationRunner.ts index 144487efc9..9c2121df78 100644 --- a/src/ida/IdaOperationRunner.ts +++ b/src/ida/IdaOperationRunner.ts @@ -25,7 +25,7 @@ import { import { functionDossierSchema, functionInstructionWindowSchema, -} from "../domain/hopperValues.js"; +} from "../domain/native/functionDossier.js"; import type { JsonValue } from "../domain/jsonValue.js"; import type { RelationshipCoverage } from "../domain/relationshipCoverage.js"; import { combineFunctionRelationshipCoverage } from "../domain/functionCallCoverage.js"; diff --git a/src/ida/IdaSessionClient.test.ts b/src/ida/IdaSessionClient.test.ts index 59908c79ff..38a8dac783 100644 --- a/src/ida/IdaSessionClient.test.ts +++ b/src/ida/IdaSessionClient.test.ts @@ -12,7 +12,7 @@ import { DEFAULT_REGEX_MATCHING_DEADLINE, REGEX_MATCHING_DEADLINE_SETTING, } from "../config/regexMatchingDeadline.js"; -import { functionDossierSchema } from "../domain/hopperValues.js"; +import { functionDossierSchema } from "../domain/native/functionDossier.js"; import { jsonValueSchema, type JsonValue } from "../domain/jsonValue.js"; import { AnalysisProtocolError, diff --git a/src/kuna/KunaReferenceQueries.ts b/src/kuna/KunaReferenceQueries.ts index 462293b52d..9576da3777 100644 --- a/src/kuna/KunaReferenceQueries.ts +++ b/src/kuna/KunaReferenceQueries.ts @@ -4,7 +4,7 @@ import { AnalysisResourceConstraintError, } from "../domain/analysisErrorCore.js"; import type { JsonValue } from "../domain/jsonValue.js"; -import type { FunctionDossier } from "../domain/hopperValues.js"; +import type { FunctionDossier } from "../domain/native/functionDossier.js"; import type { RelationshipCoverage } from "../domain/relationshipCoverage.js"; import { ProviderAdapterError } from "../domain/providerAdapterError.js"; import { err, ok, type Result } from "../domain/result.js"; diff --git a/src/kuna/KunaSessionClient.ts b/src/kuna/KunaSessionClient.ts index ca9ee6f86f..c2b1331ce8 100644 --- a/src/kuna/KunaSessionClient.ts +++ b/src/kuna/KunaSessionClient.ts @@ -34,7 +34,7 @@ import { import type { BinaryTarget } from "../domain/binaryTargetTypes.js"; import type { EvidenceLocation } from "../domain/evidence.js"; import { combineFunctionRelationshipCoverage } from "../domain/functionCallCoverage.js"; -import { functionDossierSchema } from "../domain/hopperValues.js"; +import { functionDossierSchema } from "../domain/native/functionDossier.js"; import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js"; import type { JsonValue } from "../domain/jsonValue.js"; import { projectAnalysisError } from "../domain/analysisErrorProjection.js"; diff --git a/src/kuna/KunaStringInventory.ts b/src/kuna/KunaStringInventory.ts index 83fd5ea3de..378f53d144 100644 --- a/src/kuna/KunaStringInventory.ts +++ b/src/kuna/KunaStringInventory.ts @@ -1,7 +1,7 @@ import { z } from "zod"; import type { AnalysisError } from "../domain/analysisErrorBase.js"; -import type { analysisStringSchema } from "../domain/hopperValues.js"; +import type { analysisStringSchema } from "../domain/native/nativeString.js"; import { err, ok, type Result } from "../domain/result.js"; import { compareKunaAddresses } from "./KunaAddress.js"; import { KunaJsonDocumentReader, parseKunaRow } from "./KunaJsonStream.js"; diff --git a/tests/boundary/cli/analyzeFunctionDetail.test.ts b/tests/boundary/cli/analyzeFunctionDetail.test.ts index 16d3e9891a..38b5c67a93 100644 --- a/tests/boundary/cli/analyzeFunctionDetail.test.ts +++ b/tests/boundary/cli/analyzeFunctionDetail.test.ts @@ -9,7 +9,7 @@ import { registerCoreBinaryCommands } from "../../../src/cli/coreBinaryCommands. import type { CliInstance } from "../../../src/cli/types.js"; import { CLI_COMMANDS } from "../../../src/cliCommandNames.js"; import { createEvidence } from "../../../src/domain/evidence.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { ghidraFunctionDossier } from "../../../src/domain/ghidraValues.fixture.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; diff --git a/tests/boundary/mcp/analysisViewMcp.test.ts b/tests/boundary/mcp/analysisViewMcp.test.ts index 25ecd16e2a..ba0ea9d6ed 100644 --- a/tests/boundary/mcp/analysisViewMcp.test.ts +++ b/tests/boundary/mcp/analysisViewMcp.test.ts @@ -26,7 +26,7 @@ import { jsonObjectSchema, type JsonValue, } from "../../../src/domain/jsonValue.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { inspectMachoSchema } from "../../../src/domain/native/nativeInspection.js"; import { ghidraFunctionDossier } from "../../../src/domain/ghidraValues.fixture.js"; import { ok } from "../../../src/domain/result.js"; diff --git a/tests/boundary/mcp/analyzeFunctionDetail.test.ts b/tests/boundary/mcp/analyzeFunctionDetail.test.ts index 1a92deffcc..7a88e84892 100644 --- a/tests/boundary/mcp/analyzeFunctionDetail.test.ts +++ b/tests/boundary/mcp/analyzeFunctionDetail.test.ts @@ -16,7 +16,7 @@ import { join } from "node:path"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import type { JsonValue } from "../../../src/domain/jsonValue.js"; import { ok } from "../../../src/domain/result.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { ghidraFunctionDossier } from "../../../src/domain/ghidraValues.fixture.js"; import { parseGhidraFunctionInput } from "../../../src/ghidra/GhidraFunctionValues.js"; import { EvidenceLedger } from "../../../src/application/investigation/EvidenceLedger.js"; diff --git a/tests/boundary/mcp/callPathSearchExtentMcp.test.ts b/tests/boundary/mcp/callPathSearchExtentMcp.test.ts index 9c72af6164..e859a7d647 100644 --- a/tests/boundary/mcp/callPathSearchExtentMcp.test.ts +++ b/tests/boundary/mcp/callPathSearchExtentMcp.test.ts @@ -4,7 +4,7 @@ import { expect, it } from "vitest"; import { FUNCTION_COMPARISON_EXAMPLE } from "../../../src/contracts/functionComparisonExample.js"; import { callPathResultSchema } from "../../../src/domain/callPathSchemas.js"; import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { createServer } from "../../../src/server/createServer.js"; import { createTestBinarySession } from "../../fixtures/binarySession.js"; diff --git a/tests/boundary/mcp/enhancedTools.06-rejects-incomplete-function-dossiers-at.test.ts b/tests/boundary/mcp/enhancedTools.06-rejects-incomplete-function-dossiers-at.test.ts index 449432e26d..e839b544c1 100644 --- a/tests/boundary/mcp/enhancedTools.06-rejects-incomplete-function-dossiers-at.test.ts +++ b/tests/boundary/mcp/enhancedTools.06-rejects-incomplete-function-dossiers-at.test.ts @@ -1,7 +1,7 @@ import { parseMcpToolError } from "../../fixtures/mcpToolError.js"; import { afterEach, describe, expect, it } from "vitest"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { ghidraFunctionDossier } from "../../../src/domain/ghidraValues.fixture.js"; import { observed } from "../../fixtures/analysisExecution.js"; import { closeEnhancedToolResources, connect } from "./enhancedToolsHarness.js"; diff --git a/tests/boundary/mcp/functionCallDirectionsMcp.test.ts b/tests/boundary/mcp/functionCallDirectionsMcp.test.ts index 3084c070e1..e72f9f575e 100644 --- a/tests/boundary/mcp/functionCallDirectionsMcp.test.ts +++ b/tests/boundary/mcp/functionCallDirectionsMcp.test.ts @@ -4,7 +4,7 @@ import { expect, it } from "vitest"; import { FUNCTION_COMPARISON_EXAMPLE } from "../../../src/contracts/functionComparisonExample.js"; import { createEvidence } from "../../../src/domain/evidence.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import type { RelationshipCoverage } from "../../../src/domain/relationshipCoverage.js"; import { createServer } from "../../../src/server/createServer.js"; import { createTestBinarySession } from "../../fixtures/binarySession.js"; diff --git a/tests/boundary/mcp/functionComparisonCoverageMcp.test.ts b/tests/boundary/mcp/functionComparisonCoverageMcp.test.ts index 3d3295fc45..aada5039c0 100644 --- a/tests/boundary/mcp/functionComparisonCoverageMcp.test.ts +++ b/tests/boundary/mcp/functionComparisonCoverageMcp.test.ts @@ -3,7 +3,7 @@ import { expect, it } from "vitest"; import { FUNCTION_COMPARISON_EXAMPLE } from "../../../src/contracts/functionComparisonExample.js"; import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { jsonValueSchema } from "../../../src/domain/jsonValue.js"; import { createServer } from "../../../src/server/createServer.js"; import { createTestBinarySession } from "../../fixtures/binarySession.js"; diff --git a/tests/boundary/mcp/functionComparisonMcp.test.ts b/tests/boundary/mcp/functionComparisonMcp.test.ts index c9380f8b97..ae08b1d7c9 100644 --- a/tests/boundary/mcp/functionComparisonMcp.test.ts +++ b/tests/boundary/mcp/functionComparisonMcp.test.ts @@ -16,7 +16,7 @@ import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js"; import { createServer } from "../../../src/server/createServer.js"; import { observed } from "../../fixtures/analysisExecution.js"; import { reconstructionVerificationResultSchema } from "../../../src/domain/reconstructionVerificationSchemas.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; it.each([ { leftSpace: "ram", rightSpace: "ram" }, diff --git a/tests/boundary/mcp/functionComparisonReferenceFactsMcp.test.ts b/tests/boundary/mcp/functionComparisonReferenceFactsMcp.test.ts index 4e38faf6f5..86788ce923 100644 --- a/tests/boundary/mcp/functionComparisonReferenceFactsMcp.test.ts +++ b/tests/boundary/mcp/functionComparisonReferenceFactsMcp.test.ts @@ -10,7 +10,7 @@ import { } from "../../../src/domain/analysisProfile.js"; import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js"; import { functionComparisonResultSchema } from "../../../src/domain/functionComparisonSchemas.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { reconstructionVerificationResultSchema } from "../../../src/domain/reconstructionVerificationSchemas.js"; import { createApplicationMcpHarness } from "../../fixtures/applicationMcpHarness.js"; diff --git a/tests/boundary/mcp/functionComparisonStringDecodingMcp.test.ts b/tests/boundary/mcp/functionComparisonStringDecodingMcp.test.ts index 9f2aba5bef..25abaad765 100644 --- a/tests/boundary/mcp/functionComparisonStringDecodingMcp.test.ts +++ b/tests/boundary/mcp/functionComparisonStringDecodingMcp.test.ts @@ -3,7 +3,7 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { FUNCTION_COMPARISON_EXAMPLE } from "../../../src/contracts/functionComparisonExample.js"; import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { functionComparisonResultSchema, type FunctionComparisonResult as Comparison, diff --git a/tests/boundary/mcp/ghidraEvidenceMcp.test.ts b/tests/boundary/mcp/ghidraEvidenceMcp.test.ts index 87050304bc..c9021f1d08 100644 --- a/tests/boundary/mcp/ghidraEvidenceMcp.test.ts +++ b/tests/boundary/mcp/ghidraEvidenceMcp.test.ts @@ -2,7 +2,7 @@ import { parseMcpToolError } from "../../fixtures/mcpToolError.js"; import { describe, expect, it } from "vitest"; import { EnhancedTools } from "../../../src/application/EnhancedTools.js"; import { connectGhidraMcp, sessionEvidence } from "./ghidraMcpHarness.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { functionComparisonResultSchema } from "../../../src/domain/functionComparisonSchemas.js"; import { ghidraFunctionDossier, diff --git a/tests/boundary/mcp/ghidraNativeApiMcp.test.ts b/tests/boundary/mcp/ghidraNativeApiMcp.test.ts index a4ef3ce5b4..50bc88d184 100644 --- a/tests/boundary/mcp/ghidraNativeApiMcp.test.ts +++ b/tests/boundary/mcp/ghidraNativeApiMcp.test.ts @@ -10,7 +10,7 @@ import { join } from "node:path"; import { jsonValueSchema } from "../../../src/domain/jsonValue.js"; import { nativeLoadImageSchema } from "../../../src/domain/native/nativeLoadImage.js"; import { nativeApiInspectionResultSchema } from "../../../src/domain/native/nativeApiBoundary.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { ok } from "../../../src/domain/result.js"; import { ghidraFunctionDossier } from "../../../src/domain/ghidraValues.fixture.js"; import { toolContract } from "../../../src/contracts/toolContracts.js"; diff --git a/tests/boundary/mcp/managedNativeVerificationAdapters.test.ts b/tests/boundary/mcp/managedNativeVerificationAdapters.test.ts index 8a76ea7ceb..995dd576f7 100644 --- a/tests/boundary/mcp/managedNativeVerificationAdapters.test.ts +++ b/tests/boundary/mcp/managedNativeVerificationAdapters.test.ts @@ -5,7 +5,7 @@ import { join, resolve } from "node:path"; import { afterAll, beforeAll, expect } from "vitest"; import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { MANAGED_APPLICATION_GRAPH_EXAMPLE, MANAGED_NATIVE_VERIFICATION_EXAMPLE, diff --git a/tests/boundary/mcp/nativeAddressContextMcp.test.ts b/tests/boundary/mcp/nativeAddressContextMcp.test.ts index 76a7666815..2fe4390840 100644 --- a/tests/boundary/mcp/nativeAddressContextMcp.test.ts +++ b/tests/boundary/mcp/nativeAddressContextMcp.test.ts @@ -9,7 +9,7 @@ import { import { observed as ok } from "../../fixtures/analysisExecution.js"; import { err } from "../../../src/domain/result.js"; import { AnalysisCapabilityUnavailableError } from "../../../src/domain/analysisErrorCore.js"; -import type { SegmentSummary } from "../../../src/domain/hopperValues.js"; +import type { SegmentSummary } from "../../../src/domain/native/nativeSegments.js"; afterEach(closeEnhancedToolResources); diff --git a/tests/boundary/mcp/officialArguments.test.ts b/tests/boundary/mcp/officialArguments.test.ts index 5af106f949..8621f13ce6 100644 --- a/tests/boundary/mcp/officialArguments.test.ts +++ b/tests/boundary/mcp/officialArguments.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it, onTestFinished } from "vitest"; import type { AnalysisOperationPort } from "../../../src/application/AnalysisProvider.js"; import { runDirectAnalysis } from "../../../src/application/DirectAnalysis.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { ghidraFunctionDossier } from "../../../src/domain/ghidraValues.fixture.js"; import { jsonValueSchema, diff --git a/tests/boundary/mcp/workflowUnknownIdentityMcp.test.ts b/tests/boundary/mcp/workflowUnknownIdentityMcp.test.ts index f1653c4ff3..01b994d605 100644 --- a/tests/boundary/mcp/workflowUnknownIdentityMcp.test.ts +++ b/tests/boundary/mcp/workflowUnknownIdentityMcp.test.ts @@ -3,7 +3,7 @@ import { expect, it } from "vitest"; import { FUNCTION_COMPARISON_EXAMPLE } from "../../../src/contracts/functionComparisonExample.js"; import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js"; -import { functionDossierSchema } from "../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../src/domain/native/functionDossier.js"; import { createApplicationMcpHarness } from "../../fixtures/applicationMcpHarness.js"; import { createTestBinarySession } from "../../fixtures/binarySession.js"; diff --git a/tests/boundary/providers/hopper/hopperBridgeFacade.test.ts b/tests/boundary/providers/hopper/hopperBridgeFacade.test.ts index 4165b78366..fd5eb1ef8e 100644 --- a/tests/boundary/providers/hopper/hopperBridgeFacade.test.ts +++ b/tests/boundary/providers/hopper/hopperBridgeFacade.test.ts @@ -6,7 +6,7 @@ import { describe, expect, it } from "vitest"; import { z } from "zod"; import { OFFICIAL_TOOL_CONTRACTS } from "../../../../src/contracts/officialToolContracts.js"; import { nativeSymbolMetadataSchema } from "../../../../src/domain/native/nativeSymbol.js"; -import { functionDossierSchema } from "../../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../../src/domain/native/functionDossier.js"; const execute = promisify(execFile); const bridgePath = new URL( diff --git a/tests/boundary/providers/ida/functionIdentity.test.ts b/tests/boundary/providers/ida/functionIdentity.test.ts index b3e317359d..237f883619 100644 --- a/tests/boundary/providers/ida/functionIdentity.test.ts +++ b/tests/boundary/providers/ida/functionIdentity.test.ts @@ -1,7 +1,7 @@ import { rm } from "node:fs/promises"; import { afterEach, describe, expect, it } from "vitest"; import { z } from "zod"; -import { functionDossierSchema } from "../../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../../src/domain/native/functionDossier.js"; import { jsonValueSchema, type JsonValue, diff --git a/tests/conformance/providers/managed/managedNativeVerification.test.ts b/tests/conformance/providers/managed/managedNativeVerification.test.ts index 1fc4dea7d0..3616ffed1b 100644 --- a/tests/conformance/providers/managed/managedNativeVerification.test.ts +++ b/tests/conformance/providers/managed/managedNativeVerification.test.ts @@ -19,7 +19,7 @@ import { } from "../../../../src/domain/managed/managedNativeVerificationSchemas.js"; import { managedNativeBoundaryInspectionSchema } from "../../../../src/domain/managed/managedArtifact.js"; import { inspectMachoSchema } from "../../../../src/domain/native/nativeInspection.js"; -import { functionDossierSchema } from "../../../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../../../src/domain/native/functionDossier.js"; import { verifyManagedNativeVerificationSource } from "../../../../src/domain/managed/managedNativeVerification.js"; import { readManagedNativeVerificationSource } from "../../../../src/domain/managed/managedNativeVerificationSource.js"; diff --git a/tests/fixtures/analysisView.ts b/tests/fixtures/analysisView.ts index af2e243551..64b005f532 100644 --- a/tests/fixtures/analysisView.ts +++ b/tests/fixtures/analysisView.ts @@ -21,7 +21,7 @@ import { createJavaScriptSemanticGraph } from "../../src/domain/javascript/javas import type { BinaryLayout } from "../../src/domain/native/binaryLayout.js"; import { binaryLayoutSchema } from "../../src/domain/native/binaryLayout.js"; import { ghidraFunctionDossier } from "../../src/domain/ghidraValues.fixture.js"; -import { functionDossierSchema } from "../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../src/domain/native/functionDossier.js"; import type { RelationshipCoverage } from "../../src/domain/relationshipCoverage.js"; /** Canonical portable Evidence; no native provider is started by this fixture. */ diff --git a/tests/fixtures/nativeFunctionMatching.ts b/tests/fixtures/nativeFunctionMatching.ts index bc0bf80b61..bb5ec9d303 100644 --- a/tests/fixtures/nativeFunctionMatching.ts +++ b/tests/fixtures/nativeFunctionMatching.ts @@ -2,7 +2,7 @@ import { createEvidence, type Evidence } from "../../src/domain/evidence.js"; import { FUNCTION_COMPARISON_EXAMPLE } from "../../src/contracts/functionComparisonExample.js"; import type { NativeSymbolMetadata } from "../../src/domain/native/nativeSymbol.js"; -import { functionDossierSchema } from "../../src/domain/hopperValues.js"; +import { functionDossierSchema } from "../../src/domain/native/functionDossier.js"; /** Explicit synthetic producer facts; they do not qualify any real provider. */ export const nativeMatchingProvider = {