● Zion Boggan
repos/Cyclops V2/brain/joern-queries/_runner.md
zionboggan.com ↗
107 lines · markdown
History for this file →
1
# Joern query runner - how to invoke each script and read the output
2
 
3
These are the Cyclops v2 **dataflow-confirmation** scripts. Each takes a source path, builds (or
4
loads) a CPG, and runs `reachableByFlows` source→sink taint queries for one sink class.
5
 
6
> A `FINDING:` line is a **reachable dataflow path = HYPOTHESIS at SOURCE_CONFIRMED**. It is **never**
7
> a finding. Only the dynamic oracle (crash / popped shell / captured secret) promotes it. See
8
> `README.md` and `vault/00-doctrine/SEVERITY-GATE.md`.
9
 
10
## Binary
11
 
12
```
13
/opt/joern/joern-cli/joern        # symlinked to `joern` on the forge box
14
```
15
 
16
## Invoke one script against a target
17
 
18
```bash
19
joern --script /shared/projects/cyclops-v2/brain/joern-queries/<script>.sc \
20
      --param inputPath=/abs/path/to/target/src \
21
      --param projectName=mytarget        # optional; defaults per-script
22
```
23
 
24
`importCode` auto-detects the language (C/C++, Java/JVM, JS/TS, Python, Go, …). Point `inputPath` at
25
the repo root (or the relevant subtree). First run builds the CPG (slow on large trees); the
26
`projectName` workspace is cached under `~/.joern/workspace` for reuse.
27
 
28
### Scripts and what each targets
29
 
30
| Script | Languages | Sink class (SEVERITY-GATE family) |
31
|---|---|---|
32
| `c-memory-corruption.sc` | C/C++ | strcpy/memcpy/sprintf/alloca/int-overflow-alloc/array-index (F1 RCE, F7) |
33
| `deserialization-rce.sc` | Python / Java / JS | pickle/yaml/torch/joblib/ObjectInputStream/node-serialize (F1) |
34
| `command-injection.sc` | Py / JS / Java / C | os.system/subprocess(shell=True)/child_process/Runtime.exec/eval (F1) |
35
| `path-traversal.sc` | Py / JS / Java / C | open/sendFile/extractall(zip-slip)/fopen (F7) |
36
| `ssrf.sc` | Py / JS / Java / C | requests/axios/fetch/urllib/curl tainted URL (F7) |
37
| `template-injection.sc` | Py / JS / Java | render_template_string/Handlebars.compile/Freemarker (F1 SSTI) |
38
| `js-electron-rce.sc` | JS/TS | ipcMain handler arg / custom-protocol arg → exec/eval/shell (F1) |
39
 
40
## Reading the output
41
 
42
Each script prints machine-parseable lines on stdout:
43
 
44
```
45
CANDIDATE_SINK: <file>:<line> class=<class> code=<sink call source text>
46
CANDIDATE_SINKS[<class>]=<n>
47
FINDING: <file>:<line> class=<class> source=<tainted source code> sink=<sink code>
48
DATAFLOW_PATHS[<class>]=<n>
49
CONFIG_CANDIDATE: <file>:<line> class=<class> code=<...>     # electron only
50
SCAN_COMPLETE[<script>]
51
```
52
 
53
- **`CANDIDATE_SINK` / `CANDIDATE_SINKS[..]`** - the sink call exists in the code. No taint proven
54
  yet. This is the same signal the semgrep fast-pass gives; useful when dataflow under-resolves
55
  (e.g. Python dynamic dispatch). Feed to manual review / the oracle's "reachability unknown" queue.
56
- **`FINDING` / `DATAFLOW_PATHS[..]`** - Joern found a taint path from an untrusted source to the
57
  sink. The class suffix tells you the source set: `<-input` (IO/web/CLI calls), `<-argv` (C main),
58
  `<-io` / `<-params` (dynamic-lang IO calls vs entrypoint parameters), `<-ipc` / `<-proto` /
59
  `<-anyparam` (electron). `[metadata-reachable]` on an SSRF line means the path can reach a cloud
60
  metadata host - prioritize.
61
- **`DATAFLOW_PATHS[..]=0` with `CANDIDATE_SINKS[..]>0`** - sink present but no taint path resolved.
62
  Do **not** discard: dataflow can under-approximate (see limitations). Keep as a lower-priority
63
  candidate for the oracle.
64
 
65
### Parse to JSONL (example)
66
 
67
```bash
68
joern --script .../command-injection.sc --param inputPath=/t/src 2>/dev/null \
69
 | grep '^FINDING:' \
70
 | sed -E 's/^FINDING: ([^ ]+) class=([^ ]+) source=(.*) sink=(.*)$/{"loc":"\1","class":"\2","source":"\3","sink":"\4"}/' \
71
 > candidates.jsonl
72
```
73
 
74
Then hand `candidates.jsonl` to the orchestrator, which routes each to the dynamic oracle for proof.
75
 
76
## Batch all scripts over a target
77
 
78
```bash
79
TGT=/abs/path/to/target/src
80
for s in c-memory-corruption deserialization-rce command-injection path-traversal ssrf template-injection js-electron-rce; do
81
  echo "### $s"
82
  joern --script /shared/projects/cyclops-v2/brain/joern-queries/$s.sc --param inputPath="$TGT" 2>/dev/null \
83
    | grep -E '^(FINDING|DATAFLOW_PATHS|CANDIDATE_SINKS|SCAN_COMPLETE)'
84
done
85
```
86
 
87
(The C and electron scripts no-op cleanly on the wrong language - empty candidate sets - so running
88
the full sweep is safe; only the language-matching ones produce output.)
89
 
90
## Limitations / fallbacks (honesty)
91
 
92
- **Python / JS dynamic dispatch & aliasing.** `from pickle import loads as L; L(x)` or attribute
93
  calls through variables can hide the sink from `.name(...)`. The deser/cmdi/ssti/path scripts
94
  therefore match **both** the method name and the full call `.code` regex, and always print
95
  `CANDIDATE_SINK`. Inter-procedural taint through deep callback chains may still under-resolve -
96
  the candidate list is the safety net.
97
- **`shell=True` for subprocess** is not cheaply readable as a keyword value across Joern versions.
98
  We flag all subprocess sinks and add a `py-subprocess-shelltrue` refinement that looks for a
99
  literal `True`/`sh` argument near the call. Treat subprocess FINDINGs without confirmed
100
  `shell=True` as lower confidence.
101
- **Array-index OOB & pure integer overflow** are not first-class taint sinks; the C script
102
  approximates them via index operators and allocator size args. Lower confidence - the oracle's
103
  ASan run is the arbiter.
104
- **Electron inline arrow callbacks** may not always resolve via `MethodRef` in every Joern JS
105
  frontend version; `js-electron-rce.sc` adds an `<-anyparam` over-approximation source as a net.
106
- **Over-approximation is intentional.** More candidate paths = more hypotheses. The gate is the
107
  oracle, not the query. A noisy candidate list is acceptable; a missed sink is not.