Docs

Architecture

Architecture Decision Records (ADRs) and the design rationale behind the Fendix hybrid scanner.

System Overview

Fendix is a hybrid scanner with two engines communicating via newline-delimited JSON over stdin/stdout.

architecture
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  |
+------------------+

ADR-001: Go + Python Hybrid Architecture

Accepted

Context

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.

Decision

  • Go for the CLI interface, HTTP scanner, orchestrator, correlator, and report renderer. Compiles to a single binary with excellent concurrency primitives.
  • Python for the static analysis engine. Runs as a subprocess spawned by Go. Has Semgrep, Bandit, detect-secrets, and a mature AST library.
  • Communication via newline-delimited JSON over stdin/stdout (see ADR-002 below).

Positive Consequences

  • Best tool for each job — Go for networking, Python for analysis
  • Single binary distribution for the CLI
  • Python engine is independently runnable for debugging
  • Clean separation of concerns between engines
  • Each engine can be tested independently

Trade-offs & Mitigations

  • Two language ecosystems to maintain (Go modules + pip)
  • Subprocess startup adds ~1-2s latency (measured and budgeted)
  • Graceful degradation if Python is not installed
  • IPC contract documented and tested end-to-end in CI

ADR-002: Newline-Delimited JSON IPC Contract

Accepted

Context

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).

IPC Schema

ScanRequest (Go → Python stdin)

ScanRequest
{
  "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)

Finding
{
  "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)

terminator
{"done": true, "total": 12}

Positive Consequences

  • Zero dependencies — JSON and stdin/stdout exist in every language
  • Streamable — findings appear in Go as Python discovers them
  • Debuggable — pipe to jq or cat for inspection
  • Python engine independently testable
  • No network ports, sockets, or connection management

Trade-offs & Mitigations

  • No schema validation at protocol level (mitigated by tests)
  • No bidirectional communication mid-scan
  • Evidence fields truncated to 200 characters maximum
  • End-to-end contract tests run in CI

Severity Scoring Model

Every finding is scored based on impact category, detection confidence, and whether multiple detection methods agree (correlated source gets a 1.1x multiplier).

scoring-model
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