mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
refactor: give Inspector its own capability directory (#869)
This commit is contained in:
@@ -20,10 +20,10 @@ REA is a local-only tool. Preserve caller-selected inputs, captured output, URLs
|
||||
REA is a layered ESM TypeScript application. Dependencies flow inward from pure domain logic through contracts, providers, application workflows, and CLI/MCP adapters. See [docs/architecture.mermaid](docs/architecture.mermaid) for the component map.
|
||||
|
||||
- `src/domain/` owns pure provider-neutral semantics; `src/contracts/` owns caller-visible schemas and the canonical tool inventory.
|
||||
- `src/hopper/`, `src/ghidra/`, `src/ida/`, `src/browser/`, `src/native/`, `src/artifacts/`, and `src/dotnet/` own provider-specific boundaries. Keep provider protocols out of domain and application code.
|
||||
- `src/hopper/`, `src/ghidra/`, `src/ida/`, `src/browser/`, `src/inspector/`, `src/native/`, `src/artifacts/`, and `src/dotnet/` own provider-specific boundaries. Keep provider protocols out of domain and application code.
|
||||
- `src/application/` composes shared CLI/MCP workflows; `src/server/` translates MCP requests; `src/cli.ts` and `src/main.ts` are the CLI and MCP entry points.
|
||||
- `src/process/` owns shared process lifecycle primitives, not provider wire protocols. `bridge/` contains provider-side adapters.
|
||||
- `tests/` contains unit, composition, boundary, acceptance, and conformance tests. `scripts/verify-*` contains real-toolchain checks.
|
||||
- `tests/` contains unit, composition, boundary, acceptance, and conformance tests. `scripts/verify-*` and capability directories under `scripts/verify/` contain real-toolchain checks.
|
||||
- `docs/product-catalog.json` is generated. Update its source contracts and regenerate it; do not edit it directly.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
@@ -28,6 +28,7 @@ graph TD
|
||||
IDA_MCP["External ida-pro-mcp<br/>GUI proxy or database supervisor<br/>upstream installation and engine lifecycle"]
|
||||
AUX["src/artifacts/ + src/native/<br/>disjoint artifact and native capabilities"]
|
||||
BROWSER["src/browser/<br/>CDP / Playwright capture<br/>captured script adapters and publication"]
|
||||
INSPECTOR["src/inspector/<br/>passive V8 Inspector discovery and capture<br/>script and target locations"]
|
||||
DOTNET["src/dotnet/<br/>Managed static metadata provider<br/>PE/CLI metadata inventory, members, IL"]
|
||||
FIRMWARE["src/firmware/<br/>Binwalk / Unblob adapters<br/>owned processes, report normalization, private extraction"]
|
||||
ANDROID["src/android/<br/>Static APK adapter<br/>owned SDK stdio, pinned headless JADX, normalization"]
|
||||
@@ -94,6 +95,9 @@ graph TD
|
||||
SERVER --> CONTRACTS
|
||||
SERVER --> APP
|
||||
APP --> BROWSER
|
||||
APP -->|runtime observation port| INSPECTOR
|
||||
INSPECTOR -->|shared CDP and file-location helpers| BROWSER
|
||||
INSPECTOR --> DOMAIN
|
||||
APP --> DOTNET
|
||||
DOTNET --> DOMAIN
|
||||
HOPPER --> HOPPER_BRIDGE
|
||||
|
||||
@@ -167,6 +167,24 @@ behavior. Snapshots still require an active target and concrete analysis profile
|
||||
extracting investigation ownership does not make them a target-free record store.
|
||||
#721 remains responsible for any future lifetime-policy change.
|
||||
|
||||
## Inspector adapter ownership
|
||||
|
||||
`src/inspector/` owns V8 Inspector discovery, passive capture, script/target
|
||||
location interpretation and its lightweight provider identity. The composition
|
||||
factory and optional loader use this owner. Browser CDP connection, endpoint/value
|
||||
and Electron file-location helpers remain deliberate shared adapter utilities at
|
||||
their existing paths; no provider wire protocol moves into domain or process.
|
||||
|
||||
Loopback fixtures and producer boundaries now live under
|
||||
`tests/fixtures/inspector/` and `tests/boundary/inspector/`. The socket-backed
|
||||
unresolved-target reconciliation test moves from composition into the forked
|
||||
boundary lane with its original assertions. Adapter tests retain fork isolation.
|
||||
`npm run verify:inspector` keeps its public development entrypoint and delegates
|
||||
to `scripts/verify/inspector/runtime-observation.mjs`, using the real Node fixture
|
||||
under `tests/conformance/inspector/`. Catalog imports, source guards and test
|
||||
discovery follow the new paths. Observation authority, lifecycle, producer
|
||||
interpretation and caller-visible contracts stay unchanged.
|
||||
|
||||
## Auxiliary source declaration pilot
|
||||
|
||||
Artifact, macOS native and managed static metadata now belongs to each adapter's
|
||||
|
||||
@@ -171,6 +171,15 @@ the CLI and stdio MCP, including special filenames, unresolved discovery
|
||||
locations, and independently resolved loaded scripts. Double-quote filenames
|
||||
are tested on POSIX only because Windows does not support them.
|
||||
|
||||
Inspector adapters live in `src/inspector/`, with loopback fixtures under
|
||||
`tests/fixtures/inspector/` and forked producer boundaries under
|
||||
`tests/boundary/inspector/`. The real lane is owned by
|
||||
`scripts/verify/inspector/runtime-observation.mjs` and
|
||||
`tests/conformance/inspector/`. Browser and Inspector deliberately share the
|
||||
existing browser CDP transport/value and file-location helpers. Node fixture
|
||||
success proves this Inspector workflow on the tested host; it does not prove
|
||||
Electron GUI behavior or another engine.
|
||||
|
||||
## Android APK analysis
|
||||
|
||||
`npm run verify:android` requires an existing Java 17+ and an explicit
|
||||
|
||||
+1
-1
@@ -135,7 +135,7 @@
|
||||
"verify:browser:dom": "node scripts/verify-browser-dom-destinations.mjs",
|
||||
"verify:browser:network": "npm run build:cached && node scripts/verify-browser-network.mjs",
|
||||
"verify:electron": "npm run build:cached && node scripts/verify-real-electron.mjs",
|
||||
"verify:inspector": "npm run build:cached && node scripts/verify-real-javascript-runtime-observation.mjs",
|
||||
"verify:inspector": "npm run build:cached && node scripts/verify/inspector/runtime-observation.mjs",
|
||||
"verify:javascript-runtime": "npm run build:cached && node scripts/verify-local-javascript-runtime.mjs",
|
||||
"verify:javascript:digests": "npm run build:cached && node scripts/verify-large-javascript-digests.mjs",
|
||||
"verify:application-workflows": "npm run build:cached && node scripts/verify-local-application-workflows.mjs",
|
||||
|
||||
@@ -68,7 +68,7 @@ const SOURCE_PATHS = {
|
||||
browserScenarioProvider: "dist/browser/PlaywrightBrowserScenarioProvider.js",
|
||||
electronProvider: "dist/browser/CdpElectronProvider.js",
|
||||
electronActiveProvider: "dist/browser/PlaywrightElectronActiveProvider.js",
|
||||
v8InspectorProvider: "dist/browser/V8InspectorProvider.js",
|
||||
v8InspectorProvider: "dist/inspector/V8InspectorProvider.js",
|
||||
evidence: "dist/domain/evidence.js",
|
||||
evidenceBundle: "dist/domain/evidenceBundle.js",
|
||||
evidenceCompletion: "dist/domain/evidenceCompletionLedger.js",
|
||||
|
||||
@@ -10,6 +10,7 @@ const MIGRATED_PROVIDER_ROOTS = new Set([
|
||||
"ghidra",
|
||||
"hopper",
|
||||
"ida",
|
||||
"inspector",
|
||||
"javascript",
|
||||
]);
|
||||
|
||||
|
||||
+2
-4
@@ -11,7 +11,7 @@ import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
|
||||
const execute = promisify(execFile);
|
||||
const repository = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const repository = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
|
||||
const root = await realpath(await mkdtemp(join(tmpdir(), "rea-inspector-")));
|
||||
const environment = {
|
||||
...process.env,
|
||||
@@ -43,9 +43,7 @@ try {
|
||||
for (const name of names) {
|
||||
const targetPath =
|
||||
name === "node-target.mjs"
|
||||
? await realpath(
|
||||
join(repository, "tests/conformance/runtime-inspector", name),
|
||||
)
|
||||
? await realpath(join(repository, "tests/conformance/inspector", name))
|
||||
: join(root, name);
|
||||
if (name !== "node-target.mjs")
|
||||
await writeFile(targetPath, "setInterval(() => {}, 1000);\n");
|
||||
@@ -29,10 +29,3 @@ export const PLAYWRIGHT_ELECTRON_ACTIVE_PROVIDER_IDENTITY: ProviderIdentity =
|
||||
name: "REA Playwright active Electron observation provider",
|
||||
version: "1",
|
||||
});
|
||||
|
||||
/** Identity available without loading the V8InspectorProvider implementation. */
|
||||
export const V8_INSPECTOR_PROVIDER_IDENTITY: ProviderIdentity = Object.freeze({
|
||||
id: "rea-v8-inspector",
|
||||
name: "REA passive Node/Electron V8 Inspector provider",
|
||||
version: "1",
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { JavaScriptRuntimeObservationPort } from "../application/JavaScriptRuntimeObservationPort.js";
|
||||
import { V8InspectorProvider } from "../browser/V8InspectorProvider.js";
|
||||
import { V8InspectorProvider } from "../inspector/V8InspectorProvider.js";
|
||||
|
||||
/** Construct a fresh provider without opening a target or acquiring an engine. */
|
||||
export const createJavaScriptRuntimeObservationProvider =
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
PLAYWRIGHT_BROWSER_SCENARIO_PROVIDER_IDENTITY,
|
||||
CDP_ELECTRON_PROVIDER_IDENTITY,
|
||||
PLAYWRIGHT_ELECTRON_ACTIVE_PROVIDER_IDENTITY,
|
||||
V8_INSPECTOR_PROVIDER_IDENTITY,
|
||||
} from "../browser/providerIdentities.js";
|
||||
import { V8_INSPECTOR_PROVIDER_IDENTITY } from "../inspector/providerIdentity.js";
|
||||
import { err, ok, type Result } from "../domain/result.js";
|
||||
|
||||
/** Precisely typed startup factories for the five existing observation ports. */
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
JavaScriptRuntimeLocation,
|
||||
JavaScriptRuntimeTargetLocation,
|
||||
} from "../domain/javascriptRuntimeObservation.js";
|
||||
import { authorizedElectronFile } from "./ElectronFileScope.js";
|
||||
import { authorizedElectronFile } from "../browser/ElectronFileScope.js";
|
||||
|
||||
export type RuntimeLocationDecision =
|
||||
| { readonly allowed: true; readonly location: JavaScriptRuntimeLocation }
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
} from "../domain/javascriptRuntimeObservation.js";
|
||||
import { BrowserObservationError } from "../domain/browserObservationError.js";
|
||||
import { type BrowserObservationOperation } from "../domain/browserObservationErrors.js";
|
||||
import { parseCdpEndpointValue, readCdpJson } from "./CdpEndpoint.js";
|
||||
import { parseCdpEndpointValue, readCdpJson } from "../browser/CdpEndpoint.js";
|
||||
|
||||
const versionSchema = z
|
||||
.object({
|
||||
@@ -21,8 +21,8 @@ import {
|
||||
recordValue,
|
||||
cdpStringValue,
|
||||
delayWithCancellation,
|
||||
} from "./CdpCaptureValues.js";
|
||||
import { CdpConnection, type CdpEvent } from "./CdpConnection.js";
|
||||
} from "../browser/CdpCaptureValues.js";
|
||||
import { CdpConnection, type CdpEvent } from "../browser/CdpConnection.js";
|
||||
import {
|
||||
authorizeRuntimeTargetLocation,
|
||||
inspectorExclusionKey,
|
||||
@@ -39,8 +39,8 @@ import {
|
||||
} from "./V8InspectorEndpoint.js";
|
||||
|
||||
/** Public identity committed by passive V8 Inspector observations. */
|
||||
export { V8_INSPECTOR_PROVIDER_IDENTITY } from "./providerIdentities.js";
|
||||
import { V8_INSPECTOR_PROVIDER_IDENTITY } from "./providerIdentities.js";
|
||||
export { V8_INSPECTOR_PROVIDER_IDENTITY } from "./providerIdentity.js";
|
||||
import { V8_INSPECTOR_PROVIDER_IDENTITY } from "./providerIdentity.js";
|
||||
|
||||
export interface ScriptDraft {
|
||||
readonly rawUrl: string;
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { ProviderIdentity } from "../application/AnalysisProvider.js";
|
||||
|
||||
/** Identity available without loading the V8InspectorProvider implementation. */
|
||||
export const V8_INSPECTOR_PROVIDER_IDENTITY: ProviderIdentity = Object.freeze({
|
||||
id: "rea-v8-inspector",
|
||||
name: "REA passive Node/Electron V8 Inspector provider",
|
||||
version: "1",
|
||||
});
|
||||
@@ -7,9 +7,9 @@ import { promisify } from "node:util";
|
||||
import { afterEach, describe, expect, test } from "vitest";
|
||||
|
||||
import { observeJavaScriptRuntime } from "../../../src/application/JavaScriptRuntimeObservationService.js";
|
||||
import { V8InspectorProvider } from "../../../src/browser/V8InspectorProvider.js";
|
||||
import { V8InspectorProvider } from "../../../src/inspector/V8InspectorProvider.js";
|
||||
import { observeJavaScriptRuntimeInputSchema } from "../../../src/domain/javascriptRuntimeObservation.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/fakeV8Inspector.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/inspector/fakeV8Inspector.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
const execute = promisify(execFile);
|
||||
|
||||
@@ -22,7 +22,7 @@ import { CDP_BROWSER_PROVIDER_IDENTITY } from "../../../src/browser/CdpBrowserPr
|
||||
import { CDP_ELECTRON_PROVIDER_IDENTITY } from "../../../src/browser/CdpElectronProvider.js";
|
||||
import { PLAYWRIGHT_ELECTRON_ACTIVE_PROVIDER_IDENTITY } from "../../../src/browser/PlaywrightElectronActiveProvider.js";
|
||||
import { PLAYWRIGHT_BROWSER_SCENARIO_PROVIDER_IDENTITY } from "../../../src/browser/PlaywrightBrowserScenarioProvider.js";
|
||||
import { V8_INSPECTOR_PROVIDER_IDENTITY } from "../../../src/browser/V8InspectorProvider.js";
|
||||
import { V8_INSPECTOR_PROVIDER_IDENTITY } from "../../../src/inspector/V8InspectorProvider.js";
|
||||
import { CLI_COMMAND_NAMES } from "../../../src/cliCommandNames.js";
|
||||
import { createCli } from "../../../src/cli.js";
|
||||
import {
|
||||
|
||||
+3
-3
@@ -10,8 +10,8 @@ import {
|
||||
observeJavaScriptRuntime,
|
||||
} from "../../../src/application/JavaScriptRuntimeObservationService.js";
|
||||
import { reconcileJavaScriptRuntimeEvidence } from "../../../src/application/JavaScriptRuntimeReconciliationService.js";
|
||||
import { V8_INSPECTOR_PROVIDER_IDENTITY } from "../../../src/browser/V8InspectorProvider.js";
|
||||
import { V8InspectorProvider } from "../../../src/browser/V8InspectorProvider.js";
|
||||
import { V8_INSPECTOR_PROVIDER_IDENTITY } from "../../../src/inspector/V8InspectorProvider.js";
|
||||
import { V8InspectorProvider } from "../../../src/inspector/V8InspectorProvider.js";
|
||||
import { JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE } from "../../../src/contracts/javascriptRuntimeReconciliationExample.js";
|
||||
import type {
|
||||
JavaScriptRuntimeObservation,
|
||||
@@ -19,7 +19,7 @@ import type {
|
||||
} from "../../../src/domain/javascriptRuntimeObservation.js";
|
||||
import { observeJavaScriptRuntimeInputSchema } from "../../../src/domain/javascriptRuntimeObservation.js";
|
||||
import { javascriptRuntimeReconciliationResultSchema } from "../../../src/domain/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/fakeV8Inspector.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/inspector/fakeV8Inspector.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
describe("passive V8 Inspector provider", () => {
|
||||
+2
-2
@@ -4,8 +4,8 @@ import { pathToFileURL } from "node:url";
|
||||
|
||||
import { afterEach, expect, test } from "vitest";
|
||||
|
||||
import { V8InspectorProvider } from "../../../src/browser/V8InspectorProvider.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/fakeV8Inspector.js";
|
||||
import { V8InspectorProvider } from "../../../src/inspector/V8InspectorProvider.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/inspector/fakeV8Inspector.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
const roots: string[] = [];
|
||||
+2
-2
@@ -6,9 +6,9 @@ import { expect, test } from "vitest";
|
||||
|
||||
import { analyzeJavaScriptApplication } from "../../../src/application/JavaScriptApplicationService.js";
|
||||
import { createJavaScriptRuntimeObservationEvidence } from "../../../src/application/JavaScriptRuntimeObservationEvidence.js";
|
||||
import { V8InspectorProvider } from "../../../src/browser/V8InspectorProvider.js";
|
||||
import { V8InspectorProvider } from "../../../src/inspector/V8InspectorProvider.js";
|
||||
import { reconcileJavaScriptRuntime } from "../../../src/domain/javascriptRuntimeReconciliation.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/fakeV8Inspector.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/inspector/fakeV8Inspector.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
test("keeps unresolved target identity unknown while matching verified loaded scripts", async () => {
|
||||
@@ -7,7 +7,7 @@ import { afterEach, describe, expect, test } from "vitest";
|
||||
|
||||
import { createTestBinarySession } from "../../fixtures/binarySession.js";
|
||||
import { observeJavaScriptRuntime } from "../../../src/application/JavaScriptRuntimeObservationService.js";
|
||||
import { V8InspectorProvider } from "../../../src/browser/V8InspectorProvider.js";
|
||||
import { V8InspectorProvider } from "../../../src/inspector/V8InspectorProvider.js";
|
||||
import {
|
||||
javascriptRuntimeObservationSchema,
|
||||
javascriptRuntimeTargetListSchema,
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
} from "../../../src/domain/javascriptRuntimeObservation.js";
|
||||
import { createServer } from "../../../src/server/createServer.js";
|
||||
import { observed } from "../../fixtures/analysisExecution.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/fakeV8Inspector.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/inspector/fakeV8Inspector.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
describe("JavaScript runtime observation MCP tools", () => {
|
||||
|
||||
@@ -23,7 +23,9 @@ describe("incremental module import boundaries", () => {
|
||||
["src/application/probe.ts", "../composition/android.js", false],
|
||||
["src/server/probe.ts", "../firmware/FirmwareProvider.js", false],
|
||||
["src/server/probe.ts", "../browser/CdpBrowserProvider.js", false],
|
||||
["src/application/probe.ts", "../browser/V8InspectorProvider.js", false],
|
||||
["src/application/probe.ts", "../inspector/V8InspectorProvider.js", false],
|
||||
["src/server/probe.ts", "../inspector/V8InspectorEndpoint.js", false],
|
||||
["src/composition/probe.ts", "../inspector/V8InspectorProvider.js", true],
|
||||
["src/composition/probe.ts", "../browser/CdpBrowserProvider.js", true],
|
||||
[
|
||||
"src/application/probe.ts",
|
||||
@@ -40,6 +42,7 @@ describe("incremental module import boundaries", () => {
|
||||
["src/composition/probe.ts", "../firmware/FirmwareProvider.js", true],
|
||||
["src/domain/android/probe.ts", "./types.js", true],
|
||||
["src/domain/javascript/probe.ts", "./types.js", true],
|
||||
["src/domain/inspector/probe.ts", "./types.js", true],
|
||||
["src/domain/firmware/probe.ts", "../android/types.js", true],
|
||||
["src/domain/android/probe.ts", "../../android/JadxProvider.js", false],
|
||||
["src/application/android/probe.ts", "../../domain/android/types.js", true],
|
||||
|
||||
+2
-4
@@ -1,9 +1,7 @@
|
||||
import type { BrowserObservationPort } from "../../src/application/BrowserObservationPort.js";
|
||||
import type { JavaScriptRuntimeObservationPort } from "../../src/application/JavaScriptRuntimeObservationPort.js";
|
||||
import {
|
||||
CDP_BROWSER_PROVIDER_IDENTITY,
|
||||
V8_INSPECTOR_PROVIDER_IDENTITY,
|
||||
} from "../../src/browser/providerIdentities.js";
|
||||
import { CDP_BROWSER_PROVIDER_IDENTITY } from "../../src/browser/providerIdentities.js";
|
||||
import { V8_INSPECTOR_PROVIDER_IDENTITY } from "../../src/inspector/providerIdentity.js";
|
||||
import { createBrowserScenarioProvider } from "../../src/composition/browserScenario.js";
|
||||
import { createElectronObservationProvider } from "../../src/composition/electronObservation.js";
|
||||
import { createElectronScenarioProvider } from "../../src/composition/electronScenario.js";
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ const TEST_PROJECTS = [
|
||||
name: "adapters",
|
||||
include: [
|
||||
"src/*.test.ts",
|
||||
"src/{artifacts,browser,dotnet,firmware,ghidra,hopper,ida,javascript,native,process,reference,server,windows}/**/*.test.ts",
|
||||
"src/{artifacts,browser,dotnet,firmware,ghidra,hopper,ida,inspector,javascript,native,process,reference,server,windows}/**/*.test.ts",
|
||||
],
|
||||
pool: "forks" as const,
|
||||
maxWorkers: MAX_TEST_WORKERS,
|
||||
|
||||
Reference in New Issue
Block a user