Skip to content

Latest commit

 

History

History
424 lines (340 loc) · 14.2 KB

File metadata and controls

424 lines (340 loc) · 14.2 KB

ExploitLens System Architecture

1. Architecture decision

Build ExploitLens as a local-first TypeScript monorepo around a pinned Foundry runner. The runner generates versioned JSON evidence bundles. The React application reads those bundles and renders the Differential Replay Workbench. A small local API coordinates runs, but it is not the source of truth.

For the public hackathon link, deploy a read-only static frontend with a committed, pre-generated demo bundle. Run the live Foundry comparison locally during the demo. This preserves reliability and avoids exposing an unsafe arbitrary-code execution service.

2. Architectural principles

  1. Evidence before interface.
  2. Deterministic artifacts before database state.
  3. Semantic trace identity before row-by-row diffing.
  4. Effects and invariants before revert status.
  5. Honest ambiguity before fabricated certainty.
  6. Narrow allowlisted execution before hosted generality.
  7. AO coordinates development but is not shipped in the runtime.

3. Stack

Layer Choice Reason
Monorepo pnpm workspaces Fast, simple package boundaries and shared TypeScript configuration.
Frontend React, Vite, TypeScript Static-deployable, fast feedback, no server framework needed for artifact rendering.
Styling Tailwind CSS plus CSS variables Buildable visual system with explicit tokens and no dependency on generic component themes.
Client data TanStack Query Clear run polling and cached artifact reads.
Local API Fastify, TypeScript Small typed HTTP surface and predictable request validation.
Schema Zod plus checked-in JSON Schema Shared runtime validation and portable artifact contracts.
Execution Foundry: Forge and optional Anvil Native reproduction of supplied Solidity exploit tests.
Process control Node child_process.spawn with argument arrays Streams output without shell interpolation.
Core storage Versioned JSON artifact directories Portable, inspectable, hashable evidence.
Optional index SQLite Local run discovery only, never authoritative.
Unit tests Vitest Shared TypeScript tests.
Browser tests Playwright Judge-flow and artifact-rendering smoke tests.
Contract tests Forge Fixture, exploit, control, and variant execution.
CI GitHub Actions Lint, typecheck, unit, fixture, verifier, build, and smoke gates.
Web deployment Cloudflare Pages Static frontend and prepared artifacts.
Optional runner deployment Private Render Docker service Controlled internal demo only, with allowlisted fixture and no public arbitrary input.

4. Runtime topology

flowchart TD
    UI["React workbench"] --> API["Local Fastify API"]
    API --> RUN["Execution coordinator"]
    RUN --> FOUNDRY["Pinned Forge and Anvil"]
    RUN --> CORE["Normalizer, aligner, gates, variants"]
    FOUNDRY --> ART["Versioned JSON evidence bundle"]
    CORE --> ART
    ART --> UI
    ART --> VERIFY["Independent verifier"]
Loading

The deployed static site replaces the local API with a prepared artifact bundle. It uses the same frontend artifact adapter and therefore demonstrates the same evidence model.

5. Repository structure

exploitlens/
  apps/
    web/                    React workbench
    runner/                 Fastify local API and coordinator
  packages/
    manifest/               Exploit Bundle schema, canonicalization, hashing
    executor/               workspaces, Foundry commands, timeouts, capture
    trace-ir/               versioned canonical call/effect types
    trace-normalizer/       raw Foundry output to Trace IR
    effects/                native/token/event/storage effect extraction
    aligner/                semantic tree alignment and divergence
    invariants/             security and legitimate-control gate logic
    variants/               deterministic candidate generation and scoring
    receipt/                receipt creation and evidence references
    verifier/               offline hash and consistency verification
    ui-contracts/           frontend-safe artifact view models
  fixtures/
    demo-exploit/           pinned vulnerable, patched, exploit, and control case
  schemas/                  generated and reviewed JSON Schemas
  artifacts/
    demo/                   committed fallback bundle
  scripts/
    reproduce-demo.sh
    verify-demo.sh
  docs/
    architecture.md
    evidence-model.md
    trace-alignment.md
    limitations.md
  .github/workflows/ci.yml
  pnpm-workspace.yaml
  package.json

6. Execution lifecycle

stateDiagram-v2
    [*] --> ValidateManifest
    ValidateManifest --> Invalid: schema or policy failure
    ValidateManifest --> Baseline
    Baseline --> Invalid: exploit not reproduced
    Baseline --> PatchedReplay
    PatchedReplay --> Control
    Control --> NormalizeAlign
    NormalizeAlign --> Variants
    Variants --> Receipt
    Receipt --> Complete
    Invalid --> [*]
    Complete --> [*]
Loading

Detailed sequence

  1. Parse the manifest and reject paths or inputs outside policy.
  2. Canonicalize and hash the frozen manifest.
  3. Create temporary workspaces for both commits.
  4. Confirm toolchain and dependency identity.
  5. Execute the vulnerable PoC and validate the expected exploit effect.
  6. Execute the same PoC on the patch.
  7. Execute the legitimate control on the patch.
  8. Normalize both traces and extract effects/state.
  9. Align the semantic trees and identify the first causal divergence.
  10. Compute the security and liveness gates.
  11. Generate candidate variants, validate them on vulnerable, then replay valid cases on patched.
  12. Write the receipt, artifact index, hashes, and verification script atomically.

7. Core contracts

Exploit Bundle

type ExploitBundle = {
  schemaVersion: "1.0";
  caseId: string;
  repository: { path: string; baselineCommit: string; patchedCommit: string };
  foundry: {
    testPath: string;
    testSelector: string;
    controlSelector: string;
    profile?: string;
    fuzzSeed: string;
    compilerVersion: string;
    evmVersion: string;
  };
  fork?: { chainId: number; blockNumber: number; rpcAlias: string };
  expectedExploit: { effectType: string; asset?: string; minimumDelta?: string };
  invariant: { id: string; description: string; evaluator: string };
  variants: { strategy: string; count: number; seed: string };
};

Canonical call node

type CallNode = {
  id: string;
  parentId: string | null;
  depth: number;
  contractRole?: string;
  address: string;
  codeIdentity?: string;
  selector?: string;
  functionName?: string;
  callType: "CALL" | "STATICCALL" | "DELEGATECALL" | "CREATE" | "CREATE2";
  semanticPath: string;
  msgValue: string;
  effects: EffectRef[];
  storageReads: StorageAccess[];
  storageWrites: StorageAccess[];
  events: EventRef[];
  result: { class: "return" | "revert" | "halt"; label?: string };
  children: string[];
};

Alignment record

type AlignmentRecord = {
  baselineNodeId?: string;
  patchedNodeId?: string;
  relation: "match" | "insert" | "delete" | "replace" | "ambiguous";
  score: number;
  reasons: string[];
};

Gate result

type GateResult = {
  gate: "baseline" | "exact-replay" | "legitimate-control" | "neighborhood";
  status: "pass" | "fail" | "inconclusive";
  evidenceRefs: string[];
  explanation: string;
};

8. Semantic alignment strategy

Candidate identity

Build candidate matches using:

  • Call type.
  • Contract role or code identity.
  • Function selector.
  • Semantic ancestry.
  • Relative sibling neighborhood.
  • Security-relevant effects.
  • Return or revert class.

Noise exclusions

Do not use these as primary identity:

  • Gas used.
  • Trace row number.
  • Source byte offset.
  • Absolute address when the same role is deterministically redeployed at a different address.

Safety behavior

  • Low-confidence matches remain ambiguous.
  • The causal seam must reference evidence on both sides or an explicit insertion/deletion.
  • The divergence algorithm should prioritize the earliest changed predicate, call, or effect that dominates the missing prohibited outcome.
  • Golden alignment fixtures are required before UI polish.

9. API design

Create run

POST /api/runs

{
  "caseId": "demo-reentrancy",
  "manifestPath": "fixtures/demo-exploit/exploitlens.manifest.json"
}

Response:

{
  "runId": "run_01",
  "status": "queued",
  "statusUrl": "/api/runs/run_01"
}

The API accepts an allowlisted case identifier and manifest path, not a raw shell command or arbitrary repository URL.

Run status

GET /api/runs/:runId

{
  "runId": "run_01",
  "status": "running",
  "phase": "align",
  "progress": 62,
  "gates": [],
  "error": null
}

Artifact reads

The replay, variants, receipt, and artifact-index endpoints return schema-versioned JSON and strong ETags derived from artifact hashes.

10. Storage model

artifacts/runs/<run-id>/
  artifact-index.json
  manifest.json
  receipt.json
  vulnerable/
  patched/
  alignment.json
  divergence.json
  variants/

Writes use a temporary directory and an atomic rename only after required artifacts validate. A partially completed run remains separate and cannot be shown as a verified receipt.

SQLite, if added, stores only run discovery metadata. Artifact hashes and receipt references remain the authority.

11. Authentication and trust boundaries

MVP

  • The local API listens on 127.0.0.1.
  • No accounts or sessions.
  • The public frontend is read-only.
  • RPC secrets remain environment variables and are replaced with aliases in artifacts.

Trust boundaries

Boundary Risk Control
Manifest to runner Command or path injection Zod validation, allowlists, normalized paths, spawn argument arrays.
Repository code to host Malicious build/test behavior MVP uses a reviewed fixture; hosted arbitrary execution is disabled.
Fork RPC Secret leakage and nondeterminism Environment-only secret, pinned block, redacted logs, fallback artifact.
Raw trace to UI Oversized or malformed data Schema validation, size limits, normalized UI contracts.
Artifact bundle to verdict Tampering Manifest hash, artifact hashes, receipt consistency checks.

12. Frontend architecture

Routes

  • /cases/:caseId — Case overview.
  • /cases/:caseId/replay — Differential Replay Workbench.
  • /cases/:caseId/receipt — Fix Receipt and verifier result.

Major components

  • StaticHeader
  • CaseIdentity
  • GateStrip
  • ReplayRail
  • AlignedCallRow
  • CausalSeam
  • EffectLedger
  • VariantLedger
  • ReceiptPanel
  • RunProgress

UI state

TanStack Query polls status only while a local run is active. Completed views are rendered entirely from immutable artifacts. Selection and synchronized scrolling are local component state; they do not mutate evidence.

Visual contract

  • Editorial “Forensic Ledger” direction.
  • Warm mineral canvas, dark ink, restrained rules, scarce cobalt accent.
  • Muted red for vulnerable loss and muted green for preserved or blocked outcomes.
  • Static subtly translucent header; no overlay panels.
  • No gradients, glow, decorative stars, generic metric-card grid, or gratuitous scaling.
  • Motion limited to progress, synchronized selection, and causal-seam orientation.

13. Testing strategy

Test layer Required checks
Schema Valid and invalid manifests, trace IR, receipt, artifact index.
Executor Arguments, timeouts, cleanup, redaction, baseline stop behavior.
Normalizer Golden raw traces produce stable canonical IR.
Effects Known transfers, storage writes, events, and balance deltas.
Aligner Match, insertion, deletion, replacement, ambiguity, stable causal seam.
Gates All combinations, especially false success and failed control.
Variants Vulnerable-first validation and correct denominator.
Verifier Untouched bundle passes; any mutated referenced file fails.
Frontend Artifact loading, synchronized rails, errors, receipt states.
End to end One command creates and verifies the demo bundle.

14. CI pipeline

  1. Install pinned Node and pnpm.
  2. Install or restore pinned Foundry.
  3. Lint and format check.
  4. Typecheck all packages.
  5. Run TypeScript unit and golden tests.
  6. Run Forge fixture tests.
  7. Generate the demo artifact in CI or compare it with a stable snapshot.
  8. Run the independent verifier.
  9. Build the frontend.
  10. Run a Playwright smoke test against prepared artifacts.

No change merges through AO until the relevant checks pass and the orchestrator reviews the worker diff.

15. Deployment

Public judge deployment

  • Build apps/web as static assets.
  • Bundle artifacts/demo with the build.
  • Deploy to Cloudflare Pages.
  • Disable or omit mutation endpoints.
  • Show a clear “prepared evidence” state when live runner is unavailable.

Local live demo

  • Run Fastify on localhost.
  • Run the prepared Foundry fixture with a pinned command.
  • Serve generated artifacts to the same frontend.
  • Keep the committed artifact fallback ready in a second browser tab.

Optional private runner

A Dockerized Render service is acceptable only for the allowlisted fixture, with no arbitrary repository input, strict timeouts, and no stored RPC secret in artifacts.

16. AO development topology

flowchart TD
    ORCH["Claude Code orchestrator"] --> W1["Codex: executor and manifest"]
    ORCH --> W2["Codex: trace IR and effects"]
    ORCH --> W3["Codex: frontend artifact renderer"]
    W1 --> REVIEW["Review and CI feedback"]
    W2 --> REVIEW
    W3 --> REVIEW
    REVIEW --> ORCH
Loading

Use no more than two or three parallel workers initially. Shared schemas and repository foundation merge first. The aligner receives one owner and a separate review task because parallel edits to the same algorithm would increase conflict and reduce accountability.

Every worker brief must contain:

  • One task ID and objective.
  • Allowed files or directories.
  • Inputs and already-merged contracts.
  • Acceptance criteria.
  • Required validation commands.
  • A stop condition.
  • “Do not merge or push” unless explicitly authorized.

17. External references