● Zion Boggan
repos/Cyclops V2/brain/joern-queries/README.md
zionboggan.com ↗
92 lines · markdown
History for this file →
1
# Cyclops v2 - the static-analysis "novelty brain"
2
 
3
This directory (`brain/joern-queries/`) plus its sibling (`brain/semgrep-rules/`) are the **novelty
4
brain** of Cyclops v2: the static layer that surfaces *candidate* source→sink dataflow paths to
5
high-severity sinks. It is the front of a three-stage pipeline.
6
 
7
```
8
   ┌──────────────────────┐   candidate sinks    ┌───────────────────────┐  reachable paths   ┌────────────────────┐
9
   │ 1. SEMGREP fast-pass │ ───────────────────▶ │ 2. JOERN dataflow     │ ─────────────────▶ │ 3. DYNAMIC ORACLE  │
10
   │ brain/semgrep-rules/ │   (cheap, per-file   │ brain/joern-queries/  │  (source→sink      │ oracle/ - crash /  │
11
   │                      │    regex/AST match)  │   reachableByFlows    │   reachability)    │ shell / secret     │
12
   └──────────────────────┘                      └───────────────────────┘                    └────────────────────┘
13
        SOURCE-shaped?                               SOURCE_CONFIRMED                         REPLICA_CONFIRMED →
14
        (sink present)                               (path exists)                            LIVE_CANARY_CONFIRMED
15
```
16
 
17
## The one rule that governs this whole directory
18
 
19
> **A reachable dataflow path is a HYPOTHESIS, never a finding.**
20
> Cyclops v1 reasoned its way from "the sink is reachable" to "this is exploitable" and wrote
21
> reports. v2 does not. Reachability ≠ exploitation. The **only** thing that promotes a candidate is
22
> an **execution artifact** produced by the dynamic oracle:
23
> an ASan/UBSan crash with a controlled fault, a popped shell / callback / command output, a
24
> retrieved secret, or a proven state change. See `vault/00-doctrine/OPERATING-CORE.md §2` and
25
> `vault/00-doctrine/SEVERITY-GATE.md`.
26
 
27
Every `FINDING:` line a Joern script prints is labelled in the pipeline as **SOURCE_CONFIRMED at
28
best** - it names a *plausible endpoint* on a demonstrable path to one of the eight impact families.
29
It is the oracle's job to fire the family's oracle artifact. If the oracle cannot, the candidate is
30
**DISPROVEN** and logged to `vault/04-cleared-axes/` - it never becomes a report.
31
 
32
## Why two stages (semgrep then Joern)
33
 
34
- **Semgrep (`brain/semgrep-rules/`)** is the cheap first pass. It runs per-file, no CPG build, and
35
  answers "does this repo even *contain* a dangerous sink of class X?" It is deliberately
36
  **HIGH-severity sinks only** - no hardening/lint noise (missing headers, weak ciphers, etc.). Each
37
  rule's `message` names the impact gate (which SEVERITY-GATE family) and carries `metadata.cwe`,
38
  `metadata.confidence`, `metadata.archetype`, and `metadata.impact`. A semgrep hit is a *candidate
39
  sink* - it tells the orchestrator which Joern script is worth the heavier CPG run.
40
- **Joern (`brain/joern-queries/`)** is the confirmation pass. For each candidate sink class it
41
  builds a CPG and runs `reachableByFlows` to ask "is there an actual taint path from an untrusted
42
  **source** to this **sink**?" A path here is a real source→sink chain, much stronger than sink
43
  presence - but still only a hypothesis until the oracle proves impact.
44
 
45
Semgrep narrows *which* sink classes exist; Joern proves *reachability*; the oracle proves *impact*.
46
 
47
## Sink classes (organized by target archetype)
48
 
49
Archetypes live in `vault/01-archetypes/`: `ml-model-loaders`, `ai-agent-mcp`, `devops-infra`,
50
`electron-desktop`, `memory-unsafe-parsers`, `cicd-supplychain`.
51
 
52
| Sink class | Joern script | Semgrep rule | Primary archetypes | Gate family |
53
|---|---|---|---|---|
54
| C/C++ memory corruption | `c-memory-corruption.sc` | *(use compiler+ASan; see note)* | memory-unsafe-parsers, ml-model-loaders | F1, F7 |
55
| Untrusted deserialization | `deserialization-rce.sc` | `deserialization.yaml`, `ml-unsafe-load.yaml` | ml-model-loaders, ai-agent-mcp | F1 |
56
| Command / code injection | `command-injection.sc` | `command-injection.yaml` | devops-infra, ai-agent-mcp, cicd-supplychain | F1 |
57
| Path traversal / zip-slip | `path-traversal.sc` | `path-traversal.yaml` | devops-infra, ml-model-loaders | F7 |
58
| SSRF | `ssrf.sc` | `ssrf.yaml` | ai-agent-mcp, devops-infra, ml-model-loaders | F7 |
59
| Template injection (SSTI) | `template-injection.sc` | `template-injection.yaml` | ai-agent-mcp, devops-infra | F1 |
60
| Electron renderer→main RCE | `js-electron-rce.sc` | `electron.yaml` | electron-desktop | F1 |
61
 
62
*(C memory-corruption has no semgrep rule by design - sink *presence* of `strcpy`/`memcpy` is too
63
noisy to be useful triage; the value is the dataflow path plus the ASan oracle. The Joern script
64
still prints `CANDIDATE_SINK` lines if you want the raw sink inventory.)*
65
 
66
## How the orchestrator uses this
67
 
68
1. **Recon → archetype.** Classify the target (an ML loader? an MCP agent? an Electron app?). This
69
   selects the relevant semgrep rule files and Joern scripts.
70
2. **Semgrep fast-pass.** `semgrep --config brain/semgrep-rules/ <target>` (or the subset for the
71
   archetype). Candidate sinks → which Joern scripts to run.
72
3. **Joern confirm.** Run each indicated script (`_runner.md`). Collect `FINDING:` lines = reachable
73
   source→sink paths. Prioritize: `[metadata-reachable]` SSRF, `shell=True`-confirmed cmdi,
74
   no-normalization path-traversal, `trust_remote_code=True` model loads.
75
4. **Hand to the oracle.** Each reachable path becomes an oracle task: build a replica
76
   (`vault/00-doctrine/OPERATING-CORE.md` replica-first rule), drive the sink with a crafted input,
77
   and look for the family's artifact. Artifact → REPLICA_CONFIRMED. No artifact → DISPROVEN + log.
78
5. **Only the oracle opens a report.** And only inside authorized scope may a LIVE_CANARY step follow.
79
 
80
## Output contract (both layers)
81
 
82
- Semgrep: standard semgrep JSON/SARIF. `metadata.impact` + `message` carry the gate mapping.
83
- Joern: parseable stdout - `CANDIDATE_SINK`, `CANDIDATE_SINKS[..]=n`, `FINDING`,
84
  `DATAFLOW_PATHS[..]=n`, `SCAN_COMPLETE[..]`. See `_runner.md` for the full grammar and a JSONL
85
  parser.
86
 
87
## Authorization reminder
88
 
89
Per `OPERATING-CORE §1`, these queries run only against **consenting targets** (owned infra,
90
local replicas, disposable labs, or explicitly in-scope bounty assets). Static analysis of a repo is
91
read-only, but the candidates it produces drive the oracle - and the oracle only ever detonates
92
against an owned replica first. Never point the pipeline at a non-consenting third party.