Architecture Decision Records (ADRs) and the design rationale behind the Fendix hybrid scanner.
Fendix is a hybrid scanner with two engines communicating via newline-delimited JSON over stdin/stdout.
User CLI Command
|
v
+------------------+
| Go Binary |
| - CLI (cobra) |
| - HTTP Scanner | <-- Black-box: sends real HTTP requests
| - Orchestrator |
| - Correlator |
| - Reporters |
+--------+---------+
|
| JSON over stdin/stdout
|
+--------+---------+
| Python Engine |
| - Secrets | <-- White-box: analyzes source code
| - Semgrep |
| - Spec Parser |
| - AST Analyzer |
| - Deps Checker |
+------------------+Fendix needs two fundamentally different capabilities: black-box HTTP scanning (high concurrency, low latency, single binary distribution) and white-box static analysis (best security tooling ecosystem in Python — Semgrep, Bandit, detect-secrets). No single language excels at both.
The Go orchestrator needs to communicate with the Python engine. The protocol must be simple, debuggable, streamable, and reliable.
Options considered: gRPC (too complex), Unix socket + JSON-RPC (socket management overhead), newline-delimited JSON over stdin/stdout (simplest).
ScanRequest (Go → Python stdin)
{
"mode": "whitebox",
"spec": "./openapi.yaml",
"code_path": "./src/",
"language": "python",
"checks": ["secrets", "auth", "injection", "semgrep", "deps"],
"verbose": false
}Finding (Python → Go stdout, one per line)
{
"id": "SEC-001",
"title": "Hardcoded API key detected",
"severity": "CRITICAL",
"source": "whitebox",
"category": "secrets",
"endpoint": "src/config.py:14",
"evidence": "API_KEY = 'sk-live-abc...' [truncated]",
"fix": "Move to environment variable. Rotate the exposed key immediately.",
"references": ["CWE-798"],
"confidence": "HIGH",
"line": "src/config.py:14"
}Stream terminator (final line)
{"done": true, "total": 12}Every finding is scored based on impact category, detection confidence, and whether multiple detection methods agree (correlated source gets a 1.1x multiplier).
Score = ImpactBase[category] x ConfidenceMult[confidence] x SourceMult[source]
CRITICAL >= 9.0 | ImpactBase: ConfidenceMult: SourceMult:
HIGH >= 7.0 | auth_bypass: 10.0 HIGH: 1.0 correlated: 1.1
MEDIUM >= 4.0 | injection: 9.5 MEDIUM: 0.75 blackbox: 1.0
LOW >= 1.0 | secrets: 9.0 LOW: 0.5 whitebox: 0.9
INFO < 1.0 | idor: 8.5
| data_exposure: 7.0
| cors: 6.5
| headers: 4.0
| info_disclosure: 2.0