| 1 | # Cyclops v2 - execution-grounded vulnerability research |
| 2 | |
| 3 | A ground-up rebuild. v1 was a code-*reader* that pattern-matched known bug shapes, |
| 4 | drifted to low-severity IDOR/BAC/SSRF, never proved RCE, and only ever found |
| 5 | sibling-CVEs. v2 is built on one law: **only an execution artifact may declare a |
| 6 | finding** - an ASan crash, a popped shell, a retrieved secret. Never an LLM's opinion. |
| 7 | |
| 8 | ## The three pillars |
| 9 | |
| 10 | | Pillar | What | Where | Fixes | |
| 11 | |--------|------|-------|-------| |
| 12 | | **1 - Memory** | KuzuDB graph (targets→hypotheses→proofs→findings + **cleared-axes**) + Chroma (semantic recall) + Redis (queue/cache) + Obsidian `vault/` | `memory/`, `vault/` | drift, tunnel-vision, re-screening | |
| 13 | | **2 - Brain** | Semgrep fast-pass → **Joern CPG dataflow** (reachable source→sink) → CodeQL deep | `brain/` | sibling-CVE-only; finds *novel* unexplored sinks | |
| 14 | | **3 - Oracle** | libFuzzer/AFL++ + **ASan/UBSan** → crash triage (GEF) → Docker/CT detonation | `oracle/` | never proved RCE; the only thing that creates a finding | |
| 15 | |
| 16 | The **severity gate** (`vault/00-doctrine/SEVERITY-GATE.md`) is a *refusal engine*: |
| 17 | 8 P1 impact families, each with its exact oracle and its v1 overclaim traps |
| 18 | ("injection reachable" ≠ "ATO completes"; "read DoS" ≠ "RCE"). Anything below the |
| 19 | gate goes to the cleared-axes log, never to a finding. Enforced in code: |
| 20 | `memory.promote_to_finding()` raises unless a non-DISPROVEN `Proof` exists. |
| 21 | |
| 22 | ## Where it runs |
| 23 | |
| 24 | Everything runs **on the forge box (CT 236, REDACTED-IP)** - it holds the heavy |
| 25 | tools (Joern, CodeQL, AFL++, clang/ASan, Ghidra, KLEE, SymCC) and the venv |
| 26 | (`/opt/cyclops-venv`). Redis + KuzuDB are forge-local (Redis bound to localhost - |
| 27 | never LAN, that's a gate family). The repo + human-readable `vault/` live on CT 215 |
| 28 | (`/shared/projects/cyclops-v2`, Samba-readable so Zion opens the vault in Obsidian). |
| 29 | Sync: `rsync -az cyclops-v2/ root@REDACTED-IP:/opt/cyclops-v2/`. |
| 30 | |
| 31 | ## Operate |
| 32 | |
| 33 | ```bash |
| 34 | # on forge (CYCLOPS_HOME=/opt/cyclops-v2, PY=/opt/cyclops-venv/bin/python) |
| 35 | $PY orchestrator/seed_graph.py # seed archetypes + sink classes + gate |
| 36 | $PY orchestrator/feed.py 21 # poll fresh releases (<=21d) -> targets queue |
| 37 | $PY orchestrator/orchestrator.py drain-targets # brain pass on each fresh target |
| 38 | $PY orchestrator/orchestrator.py drain-oracle # route hypotheses to proof |
| 39 | $PY orchestrator/orchestrator.py status # graph + queues + GATED findings |
| 40 | ``` |
| 41 | |
| 42 | Configuration is via environment variables, each with an in-code default: copy |
| 43 | `.env.example` to `.env` and set only the values that differ from your box. |
| 44 | |
| 45 | The loop: **feed → brain (hypotheses) → oracle (proof) → gate (finding) → vault**. |
| 46 | Novelty by construction: hunt days-old code (`feed.py`), CPG finds unexplored |
| 47 | source→sink paths, sibling-CVE gate runs before deep work. |
| 48 | |
| 49 | ## Validated |
| 50 | |
| 51 | `orchestrator/e2e_proof.sh` runs the whole chain on real cJSON source (brain CPG → |
| 52 | oracle fuzz → ASan crash → triage → gated finding). `memory/selftest.py` proves the |
| 53 | gate refuses findings without proof and honors DISPROVEN (9/9 pass). |
| 54 | |
| 55 | ## Tooling reality (honest) |
| 56 | |
| 57 | - Present + verified: Joern (CPG dataflow proven), CodeQL 2.25.1, Semgrep 1.166, |
| 58 | AFL++ 4.0, clang-14 + libFuzzer/ASan/UBSan (compile proven), GEF, Ghidra, KLEE, |
| 59 | SymCC, Docker 29, KuzuDB 0.11, Chroma 1.5, Redis 8. |
| 60 | - **rr unavailable**: this is an unprivileged LXC (`perf_event_paranoid=4`, no HW |
| 61 | perf counters). Crash→exploit triage uses GEF + the ASan report instead. |
| 62 | - **Firecracker/microVM unavailable**: no `/dev/kvm` in the LXC. Detonation uses |
| 63 | Docker on forge + the isolated detonation CT (302) for risky targets. |