Skip to content
03RELEASE NOTES

What's new

A version-by-version history of Fendix development. v1.1.0 shipped July 2026 — real-world precision: a new benchmark scores Fendix against real repositories, and the dominant false-positive classes are closed (on a live Django app the SAST taint surface fell from ~90 findings to 5 while the one true positive stayed flagged), with the synthetic accuracy gate held at F1 1.000 throughout. A performance fix took a full code scan of a large real project from ~50 minutes to 9 seconds, verify now covers correlated and active-probe findings, and scanner coverage, decision counts, and finding source-tier are surfaced in the app. It builds on v1.0.0 — the production milestone: decision reports (BLOCK / WARN / INFO with a deterministic confidence band), Java SAST coverage, accuracy you can reproduce (every number CI-gated), and signed, reproducible releases (cosign, SBOMs, SLSA provenance).

Real-world precision — less noise, proven on real code, and a redesigned workspace

July 2026

  • **Quieter, more trustworthy findings.** We built a benchmark that scores Fendix against real repositories — not just synthetic test cases — and used it to close the biggest sources of false positives: constant SQL misread as injection, safe guards, test-fixture code, sanitized output, and more. On a real Django app the noise dropped from roughly 90 findings to 5 while the one genuine vulnerability stayed flagged, and the synthetic accuracy score held at a perfect 1.000 throughout.
  • **Dramatically faster scans.** A performance fix in the code analyzer took a full scan of a large real project from around 50 minutes to 9 seconds — the same finding quality, a fraction of the wait.
  • **Scanner coverage and decisions, visible in the app.** Scan detail now shows which scanners ran, were skipped, or failed, the BLOCK / WARN / INFO decision summary at a glance, how many endpoints were discovered versus scanned, and the source tier of each finding — so you can see not just what was found, but how much ground the scan actually covered.
  • **A redesigned scan workspace.** The new-scan flow is now a clean step-by-step layout, the issues page is a proper triage table with bulk actions and saved filters, and the whole dashboard moves onto a consistent design-token system — a calmer, more professional workspace end to end.
  • **Verify anything.** The verify action now re-checks correlated and active-probe findings too, so you can confirm a fix resolved an issue no matter how it was detected.

Production release — decisions, confidence, Java coverage, and accuracy you can reproduce

July 2026

  • **Decision reports.** Every finding now carries a decision — BLOCK, WARN, or INFO — plus a deterministic, explainable confidence score, surfaced in the JSON, SARIF, HTML, CLI, and PR-comment output. Your build fails only on what genuinely blocks it, so the PR check answers "can I merge?" at a glance instead of handing you a wall of findings.
  • **Java SAST coverage.** Java joins the scanned languages with eleven line-local rules — command injection, SQL injection, weak crypto, insecure deserialization, XXE, insecure cookies, weak randomness, LDAP injection, SSRF, reflected XSS, and path traversal.
  • **Accuracy you can reproduce.** Every published number is backed by a re-runnable benchmark and gated in CI: the synthetic corpus scores F1 1.000, the interprocedural taint engine 1.000, and the DVWA / OWASP Juice Shop DAST baselines verify clean. No marketing figure ships without a command that reproduces it — see BENCHMARKS.md.
  • **Faster, calmer developer experience.** Incremental scans now cost only the changed files, error messages teach the fix, and the PR comment leads with the merge verdict.
  • **Signed, reproducible release.** v1.0.0 ships cosign keyless-signed binaries, `.deb` / `.rpm` packages, multi-arch Docker images, CycloneDX + SPDX SBOMs, and SLSA build provenance — every artifact verifiable, no telemetry.
v0.19.0 Complete

Spec-file upload + false-positive precision — scan from an uploaded OpenAPI spec

June 2026

  • **Scan from an uploaded OpenAPI/Swagger spec file.** You can now start an API scan by uploading a spec file directly, instead of only pointing at a hosted `/schema/` URL — for internal or pre-prod APIs whose schema isn't publicly reachable, or specs exported from other tools. JSON and YAML are accepted; the upload is size-capped and parsed with YAML aliases disabled (billion-laughs-safe) before the engine reads it.
  • **FastAPI auth check scoped to real FastAPI routes.** The "route missing authentication dependency" check matched any `@object.method(...)` decorator with no framework check, so on Django/DRF projects it flagged Celery tasks (`@shared_task`), DRF actions (`@action`), drf-spectacular getters (`@extend_schema_field`) and even Pydantic validators (`@field_validator`) as unauthenticated FastAPI routes. It now fires only inside files that actually use FastAPI, only on real HTTP-verb routes, and recognises `Depends` / `Security` / `Annotated[...]` auth — eliminating that false-positive class (a real Django monorepo dropped from 256 to 10 findings, the 10 being genuine unauthenticated routes in embedded FastAPI services).
  • **Sharper password-exposure guard.** The black-box password-field check no longer fires on i18n/label dictionaries (where `"password"` is just the translated UI label), while genuine plaintext-credential leaks — verbose user records, combined payloads, and non-Latin credentials — are still flagged. The guard now keys off the matched value's shape instead of a blunt body-wide heuristic.
  • **Taint-analysis false positives suppressed.** A batch of whitebox taint false positives surfaced by real-world scans were closed, each backed by a regression case, tightening precision without dropping recall.
v0.18.1 Complete

CI-integration hardening — container fix, fewer false positives, stable suppressions

June 2026

  • **Whitebox taint engine now loads in containers.** The runtime image set `FENDIX_PYTHON_ENGINE`, but the binary resolves the engine via `FENDIX_ENGINE` — so in every Docker run the Python taint engine silently fell back to a missing path and disabled itself, leaving only the native Go scanners. The Dockerfile now exports the variable the engine actually reads, so interprocedural auth/injection taint analysis runs in CI and any other container.
  • **Fewer secret false positives, at the source.** The secrets scanner now recognises a *reference* to a secret (an AWS Secrets Manager ARN, an `rds!db-` managed-secret name, a GCP `projects/*/secrets/*` resource) versus an actual credential value, and filters unambiguous placeholders (`YOUR_TOKEN_HERE`, `<...>`, `${VAR}`, `changeme`). Deliberately narrow — it never suppresses on the bare substring `example`, so AWS's documented example keys are still flagged. These two classes previously needed hand-written per-line suppressions.
  • **Stable finding fingerprints for durable suppressions.** Every finding now carries a content-derived `fingerprint` (hash of category + location + title) that doesn't change between runs, unlike the positional `SEC-NNN` id. `.fendix-ignore` gains a `fingerprint:` rule (and `fendix ignore validate` recognises it) so a suppression keeps matching across scans and code moves instead of silently drifting onto the wrong finding. The baseline diff shares the same identity.
  • **Coverage is no longer silent + baseline fails closed.** Reports now include `endpoints_discovered` and `endpoints_truncated` so a scan capped by `--max-endpoints` is distinguishable from a complete one (no more invisible partial coverage). And a corrupt `--baseline` file now fails the run (exit 2) instead of silently scanning with no diff — a missing baseline still fail-opens for the legitimate first run.
v0.18.0 Complete

DAST module upgrade — 15 black-box checks, 3 critical fixes

June 2026

  • **Black-box DAST expanded 8 → 15 checks.** Seven new check types ship in this release: **cookie-flags** (passive, CWE-1004/614/1275 — missing `Secure` / `HttpOnly` / `SameSite`), **open-redirect** (active, CWE-601), **reflected XSS** (active, CWE-79), **in-band SSRF** (active, CWE-918), **host-header injection** (active, CWE-644/601), **GraphQL introspection** (active, CWE-200), and **HTTP method tampering** (active, CWE-650/693/285). The full registered set is now configleak, headers, cors, exposure, ratelimit, cookie-flags, auth, idor, injection, open-redirect, xss, ssrf, host-header, graphql, and method-tamper.
  • **3 criticals fixed.** `addAuth` double-prefix (auth header value getting the scheme prepended twice on retry), an SSRF-guard bypass that affected 6 active checks (the guard ran before payload mutation on those paths), and a redirect-follow path that re-issued requests without re-validating the resolved IP against the private/loopback denylist.
  • **45 verified accuracy fixes.** A batch of false-positive reductions across the active and passive probes, each backed by a regression case, sharpening precision without dropping recall on the labeled corpus.
  • **New finding categories.** The report schema now emits `xss`, `cookie`, `redirect`, `host_header`, `graphql`, and `method_tamper` alongside the existing categories, so the dashboard and SARIF output classify the new check types correctly.
v0.16.1 Complete

Runs on every commit — diff-aware scans, pre-commit hook, transitive Python SCA

June 13, 2026

  • **Diff-aware scan — `fendix scan --code . --diff --staged --fast`.** Resolves changed files via `git diff` and threads a file-allowlist through every whitebox scanner; SCA runs only when a manifest/lockfile changed. Scopes a scan to just the files a commit touches — **~18ms on a 200-file monorepo**, fast enough to run on every commit instead of only in CI. (Distinct from the existing `--baseline` finding-suppression mechanism.)
  • **Pre-commit hook — `fendix hook install`.** Drops a pre-commit script that runs the diff-aware scan (secrets + textscan, sub-second budget) and blocks the commit the moment a secret or HIGH+ finding is staged. Honours `core.hooksPath`, refuses to clobber a foreign hook, and `git commit --no-verify` is the escape hatch.
  • **Proven Path v1 (SAST taint chains).** For Python/Django/Flask/FastAPI, route-table extraction binds a request route to its handler and exports the full **route → handler → source → sink** taint chain as SARIF `codeFlows`/`threadFlows`, so GitHub renders the step-through inline in the Security tab. A new `source_tier` provenance tag (`native_go` / `tree_sitter_sidecar` / `semgrep_shim`) travels end-to-end so a regex-tier finding can never ride correlation up to CRITICAL.
  • **Transitive Python SCA — `poetry.lock` + `Pipfile.lock`.** Both lockfiles are now parsed as the full resolved dependency closure, so a CVE three dependencies deep is caught — closing the `requirements.txt`-only direct-deps gap. (Separate from the npm `package-lock.json` transitive tree shipped in v0.8.0.)
  • **Released, signed, and shipped to production.** v0.16.1 publishes a cosign-signed multi-arch Docker image with CycloneDX SBOM + SLSA provenance, and is live on the hosted platform. The GitHub Action is pinned via `uses: abdel-rahmansaied/fendix@v1`.
v0.14.1 Complete

Enterprise-readiness complete — audit pass + CI stability

May 18, 2026

  • **Enterprise-readiness work complete.** The full enterprise-readiness program shipped across the prior releases: trust fixes (v0.11.1), the unified SAST engine (v0.12.0), the REST API served by the backend SaaS layer (v0.12.1), offline mode plus i18n and PDF reports (v0.13.0), integrations (v0.13.1), and the benchmark harness, CI templates, and rule pack (v0.14.0). This patch closes the CI loop and locks in a stable state for the next feature cycle.
  • **CI red-state sweep.** Resolved three pre-existing CI flakies: context-cancellation tests widened to 8s budget + EPIPE tolerance on plugin stdin writes (macOS-specific pipe close sequence); two hybrid-correlator E2E tests fixed by honouring the `FENDIX_ENGINE` env var so they opt into `--python-engine` correctly on CI runners; gofmt drift corrected across 21 files that had accumulated whitespace / import-ordering skew. All 21 Go packages race-clean, 180 Python tests green, 14 E2E tests green.
  • **Pre-existing YAML quoting fix in `auth.yaml`.** The `python-jwt-decode-no-verification` rule embedded `{"verify_signature": False}` unquoted — valid to Python's ruamel-yaml but rejected by `gopkg.in/yaml.v3`. A new YAML catalog test surfaced it; single-quoted in this patch. Behaviour unchanged at scan time.
  • **Backend and frontend synced to v0.14.1.** The backend absorbed all new CLI flags and schema fields. The frontend was updated with version literals, changelog entries, CLI reference (new subcommands + flags), checks page (textscan + expanded Semgrep), and integrations page (Jira + Slack/Teams + GitLab/CircleCI).
v0.14.0 Complete

Enterprise benchmark harness + GitLab/CircleCI templates + Semgrep rule pack

May 18, 2026

  • **Enterprise SAST comparison harness.** `scripts/benchmark-enterprise/` provides an apples-to-apples comparison of fendix vs. semgrep vs. bandit on a shared ~100-LOC fixture with 5 labeled true positives and 5 false-positive probes. Measures wall-clock, peak RSS, TP count, and FP count. Tools not on PATH are honestly reported as 'skipped' — no silent zeros. A new `benchmark-enterprise.yml` GitHub Actions workflow runs on release tags and `workflow_dispatch`, installs both competitors, and posts results as a job summary.
  • **GitLab CI + CircleCI templates via `fendix init --ci`.** The init command now auto-detects the CI system from project root files (`.github/`, `.gitlab-ci.yml`, `.circleci/`) and emits a native CI template. `--ci github` (unchanged), `--ci gitlab` (`.gitlab-ci.fendix.yml` + SAST report + `NEXT-STEPS.md`), `--ci circleci` (inline snippet + `NEXT-STEPS.md`). Without `--ci`, auto-detect falls back to `github`. Every emitted YAML is parse-validated at test time so typos can't ship in a release.
  • **Semgrep rule pack expanded 9 → 24 rules.** A new crypto rule file adds 4 cryptography rules. Additions across existing files: 2 new auth rules (Django function-based view missing decorator, Flask route missing auth decorator), 5 new injection rules (Django ORM raw SQL, Flask `render_template_string` SSTI, `subprocess(shell=True)` high-precision variant, `pickle.loads`, `yaml.load` without SafeLoader), 4 new secrets rules (GCP service-account JSON inline, AWS access-key ID, Slack webhook URL, PEM private key). Every rule carries `metadata.category`, `metadata.fendix_severity`, `metadata.confidence`, `metadata.cwe`. A new YAML-only catalog test enforces these invariants for every current and future rule.
v0.13.1 Complete

Integrations: GitHub App + Jira + Slack/Teams

May 18, 2026

  • **GitHub App handler doc cleanup.** The GitHub App webhook documentation now describes what's actually there: `HandlePullRequest` (clone → scan → comment → SARIF upload), `HandleCheckRun` (re-run button), `HandlePush` (no-op baseline placeholder).
  • **Jira integration via `fendix jira`.** Idempotent Jira sync: each finding above `FENDIX_JIRA_MIN_SEVERITY` (default HIGH) gets exactly one Jira issue. Idempotency key is `fendix-id:<finding.ID>` on the issue's label field — re-running the command on the same findings is safe. Severity → priority mapping: CRITICAL→Highest, HIGH→High, MEDIUM→Medium, LOW/INFO→Low. Configured via four environment variables (`FENDIX_JIRA_URL`, `FENDIX_JIRA_PROJECT_KEY`, `FENDIX_JIRA_EMAIL`, `FENDIX_JIRA_API_TOKEN`). Works against both Jira Cloud and Server tiers (plaintext description format, ADF rendering is server-side).
  • **Slack + Teams webhook alerts via `fendix notify`.** Post Slack Block Kit and Teams Adaptive Card alerts for findings above a configurable severity floor. Both sinks are optional — set `FENDIX_SLACK_WEBHOOK_URL` and/or `FENDIX_TEAMS_WEBHOOK_URL`; whichever are set receive alerts. `FENDIX_NOTIFY_MIN_SEVERITY` controls the floor (default: CRITICAL). `FENDIX_NOTIFY_DEDUP_WINDOW` (Go duration, default 1h) prevents re-alerting the same finding ID within the window (in-memory; restart re-arms). Per-sink errors are isolated so a failing Teams endpoint doesn't block Slack delivery.
v0.13.0 Complete

Offline mode + Arabic HTML + PDF executive report

May 18, 2026

  • **Air-gapped CVE database + `fendix db`.** A JSON snapshot format (schema v1) for OSV advisory exports. Three management subcommands: `fendix db update --source <osv-export.json>` ingests an OSV export into a local snapshot; `fendix db list [--path]` prints snapshot metadata; `fendix db verify [--path]` prints the SHA-256 for integrity checking. New `--offline` and `--offline-db <path>` flags on `fendix scan` enable fully air-gapped operation. Designed for government / enterprise environments with no outbound internet access.
  • **Arabic HTML report + i18n foundation.** `fendix scan` and `fendix report` accept `--lang ar` to render an HTML report right-to-left with Arabic strings. Adding a new language is a single new constructor plus a switch case. JSON, SARIF, and PDF outputs stay English (machine-consumed; localisation would break downstream tooling). Unknown `--lang` values fall back to English with a stderr warning.
  • **PDF executive report via `--format pdf`.** New PDF output format via `fendix scan --format pdf` and `fendix report --format pdf`. Structure: cover page → executive summary with severity-counts table and top-3 findings → paginated findings table with severity-coloured cells → remediation plan (CRITICAL + HIGH only) → metadata appendix. New `--classification <text>` flag (default `INTERNAL`) renders a red classification banner at the top-right of every page; empty string disables the banner. Uses `github.com/go-pdf/fpdf` (MIT, pure Go, no CGo). Arabic PDF is deferred — fpdf's built-in fonts do not render Arabic glyphs.
v0.12.0 Complete

Unified Go/JS/IaC textscan SAST engine

May 18, 2026

  • **Unified regex SAST engine.** A single codebase drives Go, JavaScript/TypeScript, Dockerfile, and Kubernetes YAML rules using shared scanner scaffolding. 16 rules total across 4 language targets: Go rules (SQL injection via string concat, `exec.Command` shell invocation, weak hash for passwords, hardcoded AWS key ID); JS/TS rules (eval with non-literal arg, innerHTML from non-literal, child_process.exec, document.write, require with non-literal path, hardcoded AWS key); IaC rules (Dockerfile `FROM` without privilege drop, `ADD` vs `COPY`, `:latest` tag; Kubernetes `privileged: true`, `hostNetwork: true`, `allowPrivilegeEscalation: true`, `runAsUser: 0`).
  • **Extension-based routing.** Filename extension determines which rule set applies: `.go` → Go rules; `.js` / `.ts` / `.jsx` / `.tsx` → JS rules; `Dockerfile` / `*.dockerfile` → Docker rules; `.yaml` / `.yml` → Kubernetes rules (heuristic: skips files that look like GitLab/CircleCI CI configs). Skips noisy build directories: `node_modules`, `vendor`, `.git`, `build`, `dist`.
  • **Pure stdlib — no new deps, no CGo.** The textscan engine is wired into the orchestrator between the Semgrep and Python passes; runs whenever `--code` is set. Go XXE and insecure-rand rules, JS prototype-pollution and insecure-RNG rules, and Terraform HCL support are deferred follow-ups requiring either AST context or MPL-2.0 dep acceptance.
v0.11.1 Complete

Trust fixes — pip-audit naming, OSV batch queries, verify scope

May 14, 2026

  • **pip-audit naming gap fixed.** The Python dep-CVE scanner was marking every finding from the in-process pip-audit path with the tool's internal name instead of the human-facing advisory title. Findings now surface as `<package>@<version>: <CVE-ID> — <advisory title>` matching what users see in `pip audit` output. New `--no-pip-audit-fallback` flag disables the local-list fallback for strict environments.
  • **OSV batch queries for pip and npm.** The PyPI and npm dep-CVE scanners now send a single `POST /v1/querybatch` request with all (package, version) pairs instead of N sequential single-package queries. Typical reduction: 30 packages × 1 query → 1 batch query. Both scanners respect a new `--osv-concurrency <n>` flag (default 4) to cap parallel batch requests when the project has many lockfiles.
  • **`fendix verify` scope fixed.** `fendix verify --id SEC-042` now exits 1 when the finding is confirmed (vulnerability still present) and exits 0 when it is resolved — matching the documented intent. Previously the exit codes were inverted. New explicit `--confirmed-ok` flag for the rare case where an operator wants the CI job to pass even on confirmed findings.
Engine evaluation Complete

Engine evaluation — 3-track real-world accuracy scorecard

May 13, 2026

  • **Three independent evaluation tracks against v0.11.0** — synthetic precision/recall against canonical patterns, real-world DAST against OWASP Juice Shop, real-world SAST against PyGoat's Django OWASP Top 10 demo. All three say the engine catches what it claims at the latency the benchmark publishes on the breadth real-world codebases need. Headline numbers and the methodology now live on a dedicated <Link href="/accuracy">/accuracy</Link> page.
  • **Track 1 — Synthetic labeled corpus (56 cases, 7 categories):** F1 = 1.000, precision = 1.000, recall = 1.000. 38 true-positives / 0 false-positives / 0 false-negatives across sqli / cmdi / path-traversal / ssrf / open-redirect / xss / secrets. The corpus exists in `scripts/accuracy/corpus/`; ground truth in `scripts/accuracy/manifest.json`; harness in `scripts/accuracy/run.py`.
  • **Track 2 — OWASP Juice Shop refresh vs v0.6.1 baseline:** 12 findings (5 CRITICAL / 4 MEDIUM / 2 LOW / 1 INFO) in 27 s. **+5 CRITICALs vs v0.6.1**, scan duration **−35 %**. All 5 new CRITICALs are exposed-config-file detections (CWE-538): `.DS_Store`, `.env` family (3 paths), `.git/*` (3 paths), `.htaccess`, `.htpasswd`. Caveat: juice-shop's SPA returns 200 for unknown paths so these could be SPA-fallback responses — still a real security issue (cache poisoning + WAF confusion).
  • **Track 3 — PyGoat real-world SAST:** 147 findings in 17.1 s on a 52-Python-file Django app. 1 CRITICAL (unsafe pickle deserialization), 146 HIGH. Categories: 135 deps (real CVE-tagged: certifi / cryptography / django), 9 injection (eval / subprocess(shell) / SSRF / innerHTML XSS / yaml.unsafe_load / open-redirect at **9 sites** / pickle), 3 secrets. Every OWASP Top 10 category PyGoat advertises was detected.
  • **5 real engine improvements shipped during the evaluation arc:** (1) `_is_open_redirect` upgraded to taint-chain posture parity — pre-fix the detector only matched direct `redirect(request.args.get('x'))`; multi-hop assignments were silently missed; the other 6 reachable sinks already had the chain treatment. Open-redirect recall: 0/3 → 3/3. (2) cmd-injection posture aligned with the other reachable sinks via new `_cmdi_arg_is_dangerous` helper — `os.system('echo hello')` no longer fires HIGH on literal-string args. Precision: 0.833 → 1.000. (3) Orchestrator `runWhiteboxScan` now resolves `code_path` and `spec` to absolute paths before sending the ScanRequest — pre-fix the Python subprocess silently saw 0 files on every real codebase using `--python-engine` without `FENDIX_ENGINE` set. (4) `run-juice-shop.sh` portability fix: replaced GNU `timeout` (not on macOS) with `--max-duration`. (5) 3 cmdi unit tests updated to assert the new no-emit-on-literal posture.
  • **Caveats documented honestly.** The synthetic 1.000 means fendix never misses these 56 canonical patterns — not that it never misses anything. PyGoat lacks a machine-readable ground-truth manifest so we report category coverage rather than precision/recall. Juice Shop's SPA-fallback means the CRITICALs could be SPA-fallback responses (still a real issue, but remediation may be server-config not secret-rotation). The full caveat list + follow-up backlog lives in `docs/accuracy.md`.
v0.11.0 Complete

FP discipline + path-traversal reachability

May 13, 2026

  • **The engine kept its fast cold-start while adding three new detection paths.** This release ships false-positive round 2 plus path-traversal detection, and the engine held its v0.8.0 cold-start (6.1 ms p50 default) while adding three new detection paths on top.
  • **Native-Go exposed-config-file scanner.** New `internal/scanner/configleak.go` (~280 LOC). 32 patterns (27 basename + 5 directory prefix) cover `.env` / `.env.local` / `.env.production`, web-server config (`.htaccess`, `.htpasswd`, `web.config`), package-manager creds (`.npmrc`, `.pypirc`, `.netrc`), Docker (`docker-compose.override.yml`), IDE/OS leftovers (`.DS_Store`), and directory-style leaks (`.git/`, `.aws/`, `.ssh/`). Fires CRITICAL with CWE-538 on any 2xx response to a known config-file path. Body sample (capped 512 bytes) gets `[REDACTED]` masking of common secret-shape tokens before landing in evidence — no engine leak of leaked credentials. Inverts the prior FP shape where the engine would emit noisy MEDIUM `missing-CSP-on-/.env` findings into one CRITICAL `exposed config file` finding. 9 race-clean unit tests.
  • **Path-traversal as the 7th reachable taint-chain sink class.** New `SEC-PY_PATH_TRAVERSAL` finding (CWE-22, HIGH severity, MEDIUM confidence, category=injection). Four filesystem-path sinks recognised: `open(x)` (stdlib), `Path(x)` / `pathlib.Path(x)`, `send_file(x)` (Flask), `send_from_directory(safe_dir, x)` (Flask — the user-controlled arg is at index 1, handled by `_path_traversal_arg_index`). When user input flows to the path arg, `_collect_taint_chain` records the chain and sets `reachable: true`; both the correlator and orchestrator severity escalations apply. Brings the engine's reachable sink categories to **7**: SQLi / SSRF / open-redirect + XSS + cmd-injection + path-traversal. 9 new Python tests.
  • **`fendix ignore` subcommand tree** (`internal/ignorecmd/`, ~300 LOC). Three subcommands close the suppression-bookkeeping gap: `fendix ignore list` renders a tabular view with EXPIRED / expiring-soon (within 30 days) / active / no-expiry / INVALID-DATE status; `fendix ignore validate` reports schema and date errors with non-zero exit for CI gating; `fendix ignore prune [--dry-run]` removes expired rules and rewrites the file (preserves rules with invalid dates — `validate` surfaces those). All three default to `.fendix-ignore` in cwd; `--file <path>` targets a different file. 14 race-clean unit tests.
  • **Cold-start benchmark refreshed.** Default v0.11 = 6.1 ms p50 (+0.5 ms vs v0.9, within noise — configleak doesn't fire with zero discovered endpoints on the secrets-only fixture). `--python-engine` opt-in v0.11 = 40.7 ms p50 (+16.3 ms vs v0.9 — real cost of the new path-traversal sink in the AST analyzer). Net: default v0.11 is still 16 % faster than v0.8.0 while adding three new detection paths on top.
  • **Spawner bug fix.** `internal/engine/spawner.go` could compose `python/python/engine.py` for users on the local-fallback path because both `engineDir` and the relative script path were used together as `cmd.Dir` + arg. Fixed by resolving via `filepath.Abs` before composing. Closes a silent exit-2 for every `--python-engine` invocation without `FENDIX_ENGINE` set.
  • **FP corpus re-triaged.** No new shipping artifact, but the false-positive corpus catalog now records which of the 35 catalogued FPs are addressed, which the new config-file scanner picks up (dotfile inversion), and which is deferred until real post-launch user data exists (SPA-fallback dedup).
v0.10.0 Complete

Plugin ecosystem polish

May 13, 2026

  • **The plugin ecosystem is genuinely usable now.** The plugin wire contract from v0.7 is unchanged; what changed is the surface around it. An external author can ship a plugin against an installed binary in 60 seconds — copy-paste quickstart in the rewritten author docs, two new reference plugins in Node + Ruby prove the contract is language-agnostic, `fendix plugins` subcommand tree handles install + list, CI smoke test catches wire-contract regressions automatically.
  • **`docs/plugins.md` rewritten for external authors.** ~580 LOC restructured around the outside-contributor audience. 60-second copy-paste quickstart (TODO-comment finder, ~30 LOC) before any conceptual content; new 'How plugins fit into a scan' pipeline diagram; Python / Node / Bash skeleton entrypoints; 'Testing your plugin locally' section with inside-engine and outside-engine smoke-test recipes; 'Common errors' 8-row diagnosis table; 'Distributing your plugin' covering both `git clone` and `fendix plugins install`; tightened security model with explicit auth-token handling guidance; authoring checklist expanded 8 → 13 items.
  • **Two new reference plugins in non-Go languages.** `examples/plugins/license-header-check/` (Node, stdlib-only, ~150 LOC) walks the source tree and flags files lacking an `SPDX-License-Identifier` header. `examples/plugins/dockerfile-best-practices/` (Ruby, stdlib-only, ~210 LOC) walks for `Dockerfile` / `Dockerfile.*` and emits up to 5 distinct findings per file (`:latest` tag, `curl | sh`, `ADD <url>`, root-by-default, missing HEALTHCHECK). Both ship with a README. Reference-plugin shelf is now 5 plugins covering Python ×2, Bash ×1, Node ×1, Ruby ×1.
  • **`fendix plugins` CLI subcommand tree.** `fendix plugins list` enumerates discovered plugins (NAME / VERSION / MODE / DIR) using the same discovery roots a real scan uses — what's printed is exactly what would run. `fendix plugins install <git-url>` is a thin wrapper over `git clone --depth=1`: derives the on-disk name from the URL (handles `.git` suffix, scp-style `git@host:org/repo`, query strings, trailing slashes), refuses to overwrite preexisting directories, validates the cloned tree's `plugin.yaml` after clone, and removes the directory on validation failure so users never end up with a half-installed plugin that WARNs every scan. Also includes a symlink-discovery fix: plugin discovery now `os.Stat`s each entry before `IsDir()`-checking, so symlinked plugin dirs work too.
  • **Plugin smoke test in CI.** New `internal/e2e/refplugins_test.go` (build-tag `e2e`, ~330 LOC) under the existing `make e2e` umbrella. One subtest per reference plugin (5 total) that copies the plugin into a temp scan root, runs `fendix scan` against a deterministic fixture, and asserts the expected findings flow through with the engine-attached `fendix-plugin:<name>` provenance tag. Each subtest skips cleanly when its required runtime (node, ruby, python3, bash + jq) isn't installed on the CI runner — so the test still passes on minimal images while exercising every plugin on full ones. Catches wire-contract regressions before plugin authors hit them.

Cold start under 6 ms, no Python required

May 13, 2026

  • **Secrets and semgrep checks now run as native Go in-process scanners; the embedded Python distribution is no longer bundled in the binary.** Default cold start = **5.6 ms p50** (was 7.3 ms on v0.8 — 23 % faster). fendix no longer carries a Python interpreter requirement at all in the default scan path. New `--python-engine` opt-in flag re-enables the Python `auth` / `injection` / `deps` checks for users who still want them — costs ~24.4 ms p50 (4.4× the default; that gap is the actual cost of Python interpreter startup + engine extraction we removed). All numbers reproduced via `scripts/bench/coldstart.py`.
  • **Native Go secrets scanner.** New `internal/scanner/secrets/` package ports the Python secrets analyzer to in-process Go: all 15 patterns (7 generic + 8 provider-specific — GitHub `ghp_*` / `ghs_*`, Stripe `sk_live_*`, Slack `xox*`, Google `AIza*`, Anthropic `sk-ant-*`, OpenAI `sk-*` / `sk-proj-*` / `sk-svcacct-*`, npm `npm_*`, GCP service-account JSON) plus the `.env`-only `ENV_SECRET` regex, walker with skip-dirs / extension gate / `.env`-name match / 1 MB cap / minified-JS-line skip, and evidence truncation. Same `SEC-<PATTERN_ID>` finding IDs as the Python implementation so any overlap (user explicitly passes `--checks secrets`) dedupes cleanly. Go RE2 doesn't support `(?<![A-Za-z0-9])` lookbehinds, so the 9 provider-prefix patterns carry a `boundaryOK` post-match validator instead. **Real-world parity verified** against the Python fixture suite: 30 unique (title, endpoint) tuples emitted by both engines, set-diff empty in both directions. 24 race-clean unit tests.
  • **Semgrep shelled-out, not embedded.** New `internal/scanner/semgrep/` package wraps the host's installed `semgrep` binary instead of running it through the embedded Python engine. The fendix rule pack (`auth.yaml` / `injection.yaml` / `secrets.yaml`) is bundled into the Go binary via `//go:embed` and extracted to a per-process temp dir on first scan. Result mapping mirrors the Python wrapper byte-for-byte: `SEC-<RULE_ID>` IDs, `metadata.fendix_severity` preferred over Semgrep's ERROR / WARNING / INFO mapping, evidence truncated at 200 chars, title at 120. **Graceful absence:** `exec.LookPath("semgrep")` failure returns `ErrSemgrepUnavailable` and the orchestrator logs an install hint and continues. Non-fatal exit codes (1 for matches, 2 / 5 / 7 for rule-parse errors that still emit valid JSON) are absorbed in parity with the Python wrapper. 28 race-clean unit tests cover mapping, graceful absence, ctx cancellation, fake-semgrep happy path, and rule embedding extraction.
  • **Embedded Python distribution dropped from binary + redundant Python wrappers deleted.** The `Makefile`'s `embed-engine` target no longer copies `python/` into the embedded engine tree; the binary's `//go:embed` directive bundles only a placeholder. The now-redundant Python secrets and semgrep-runner wrappers and their test files were deleted (215 → 159 Python tests; the 56 deleted tests were strictly the redundant wrapper coverage). Python whitebox spawning is now opt-in via the new `--python-engine` CLI flag, requires a local `python/` source tree (or explicit `FENDIX_ENGINE` env var), and is silently skipped when no Python engine is resolvable. New `scripts/bench/coldstart.py` reproduction harness; `docs/benchmarks.md` adds a cold-start latency section with the table, methodology, binary-size delta (-99 KB), and re-run instructions.
  • **Plugin wire-contract compatibility audit.** All three reference plugins (`custom-secret-pattern`, `custom-blackbox-check`, `custom-semgrep-pack`) re-verified end-to-end against the new binary: discovery works, NDJSON in/out works, findings flow through correlation + dedup unchanged. Plugins do not depend on the embedded engine, the extracted `~/.fendix/engine/` tree, or `--python-engine` being set. **Pre-existing limitation surfaced and documented:** `os.ReadDir().IsDir()` returns false for symlinked plugin directories, so plugins must be installed as real directories (`cp -R` / `git clone`, not `ln -s`). Documented in `docs/plugins.md`.
  • **No backend / frontend schema delta.** The Go secrets and Go semgrep scanners emit findings with the same `SEC-*` IDs and the same Finding fields as the prior Python path. Dropping the embedded Python distribution doesn't touch the wire shape — only the runtime topology and the cold-start cost. Plugin findings still flow through the same Finding shape. So this release ships frontend-side as version-literal bumps + this changelog entry, and backend-side as a no-op (the existing `_finding_defaults` ingest path already absorbed all the relevant fields).

Detection depth + FP discipline

May 12, 2026

  • **Native in-process dep-CVE scanners across all three ecosystems.** Go (`internal/scanner/deps/govulncheck/`) — uses the upstream `golang.org/x/vuln/scan` API in-process; inherits the same call-graph reachability filter as the upstream `govulncheck` binary but without requiring the binary on the user's PATH. PyPI (`internal/scanner/deps/pip/`) — parses `requirements.txt` (== pins only; ranges deliberately skipped, matching pip-audit's posture), POSTs each (package, version) to OSV.dev `/v1/query`, caches responses at `~/.fendix/cache/osv-pypi/<pkg>@<ver>.json` with 24h TTL. npm (`internal/scanner/deps/npm/`) — parses `package-lock.json` v2/v3 (full transitive tree, dedup on (name,version)), OSV.dev queries against the `npm` ecosystem, scoped-package aware (`@scope/name` handled correctly in path parsing + cache keys), 24h cache. All three behind a single `--no-native-deps` escape hatch. **Toolchain bump 1.21 → 1.22** required (x/tools v0.17.0 has a constant-folding bug under modern Go; v1.1.4 of x/vuln is the earliest version that picks up a fixed x/tools, and v1.1.4 needs go 1.22).
  • **Reachable XSS taint chains.** New `SEC-PY_XSS_HTML_SINK` finding (CWE-79, HIGH severity, MEDIUM confidence). Three HTML-render sinks detected: `Markup(x)` / `flask.Markup(x)` / `markupsafe.Markup(x)` (bypasses Jinja2 auto-escaping), `mark_safe(x)` / `django.utils.safestring.mark_safe(x)` (bypasses Django's `{{ }}` escaping), `render_template_string(x)` (Flask/Jinja2 SSTI + reflective XSS when the template body is user-controlled). When intra-function dataflow proves a request source reaches the sink, the finding carries `taint_chain` + `reachable: true` — the correlator then applies a second severity bump for correlated-reachable pairs.
  • **Reachable command-injection taint chains.** Extended existing `PY_OS_SYSTEM` and `PY_SUBPROCESS_SHELL` sinks with `_collect_taint_chain` capture; added new `PY_OS_POPEN` sink (deprecated form with the same shell-injection surface). Brings the engine's reachable sink categories to 6 across SQLi/SSRF/open-redirect, XSS, and cmd-injection.
  • **FP corpus + targeted gates.** New `scripts/fp-corpus/run.sh` runner plus a false-positive catalog: 35 false-positives across 4 distinct root-cause patterns (test fixtures flagged as production findings, header/CORS check fires on 4xx, rate-limit check on static-file path, metrics-endpoint headers). Two math-shaped levers shipped: **4xx-response gate** on header + CORS checks (a missing CSP/HSTS or CORS misconfig on a 404 page isn't actionable — early-return) and **static-file path regex** on rate-limit check (skips .DS_Store, favicon.ico, robots.txt, .css, .js, .map, .woff2, etc. before sending any probe requests).
  • **One-click suppression snippet in PR comments.** Every top finding in the GitHub-App-posted PR comment now ships with a fenced `yaml` block under the bullet — copy-paste straight into `.fendix-ignore` to suppress. Keyed on a stable `(title, category, endpoint)` SHA-256 hash (truncated to 8 hex chars in a trailing `# fp-<hash>` comment) so the suppression survives SEC-NNN reassignment across scans. Addresses the dominant FP corpus pattern: 31 of 35 FPs come from test fixtures being flagged as production findings; the snippet lets a developer suppress the whole cluster in one paste.
  • **Severity scoring refresh — reachable_code multiplier first-class.** New `ReachableMult = 1.5` constant in `internal/models/scoring.go` + new `CalculateSeverityReachable()` function (existing `CalculateSeverity` is a backwards-compatible wrapper). A new orchestrator step bumps severity by one level on pure-whitebox findings with `reachable: true` (the correlator already bumps the correlated-reachable case in `mergeFindings`). The existing confidence cap still applies — the bump amplifies what the confidence allows; it can't override the cap. EPSS / KEV multipliers are explicitly deferred.
  • **Cross-repo sync: backend persists taint_chain + reachable + affected_endpoints.** The engine had been emitting these reachability fields on `Finding` since v0.7.0, but the backend's `_finding_defaults` silently stripped them on ingest. New migration `0006_finding_reachability` adds 3 columns to `ScanFinding` (`affected_endpoints` + `taint_chain` JSONField, `reachable` Bool) with safe defaults. `_coerce_taint_chain` defensive parser drops malformed links rather than failing the whole finding. Frontend `Finding` interface extended with new `TaintLink` type + optional `taint_chain?` / `reachable?` fields; `app/types/api.ts` regenerated via `npm run codegen`. **No UI surfacing yet** — deferred until a future findings-detail iteration when there's enough reachable-pattern volume to earn its design cost.
  • **ADR-008: read-only AI permitted, auto-remediation permanently forbidden.** New `docs/adr/ADR-008-readonly-ai.md` formalises the strategic decision. The boundary: read-only AI (finding explanation + fix suggestion as text, delivered by the cloud product) is permitted and planned. Auto-PR generation, auto-merge, and any LLM calls from the OSS engine binary are permanently forbidden. Rationale covers three options, trust-story constraints, regulatory trajectory, and four constraints on future PRs (no `import anthropic` in `go/` or `python/`).

Open & Extensible — the wedge is now defensible

May 1, 2026

  • **The wedge is now defensible.** The correlator now distinguishes 'DAST + SAST agreed' from 'DAST + SAST agreed AND we can prove the exploit path' — the latter gets a double severity escalation, which is exactly what makes the wedge defensible against vendor noise. This release closes out the GitHub App business logic and ships open-source ratification, the plugin system, and reachability/dataflow correlation.
  • **Open-source posture ratified via ADR-007.** What was tactical in v0.1.0 (an MIT `LICENSE` shipped because the project needed *a* license) is now a deliberate strategic decision: MIT, single repo, no open-core split planned. ADR-007 records the rejected alternatives (Apache 2.0, AGPL 3.0, dual-license, open-core) and why each was the wrong fit. README hero gains a fourth bullet 'Open source under MIT — read the source, audit the wedge, fork it, ship plugins.' CONTRIBUTING.md gains a 'Licensing of contributions' section: by submitting a PR, you agree to MIT for your work; no CLA, no copyright assignment. **Out-of-tree plugins choose their own license** — only plugins shipped inside this repo (under `examples/plugins/`) are required to be MIT to match the rest of the tree.
  • **Plugin system: out-of-tree extension via NDJSON IPC.** New `internal/plugin` package: `Discover` walks `<repo>/.fendix/plugins/` (repo-local, takes precedence) + `~/.fendix/plugins/` (user-global), parses each child's `plugin.yaml` with strict `KnownFields(true)` parsing (typos like `entry_point` → loud error, not silent drop), and dedups by name. `(*Plugin).Run` invokes the entrypoint with a JSON `ScanRequest` on stdin and reads NDJSON Findings on stdout — **same wire contract as the embedded Python engine** (ADR-002), so plugin authors writing in Python can reuse most of `engine.py`. Per-plugin timeout (default 30s, max 5m) bounds wall-clock; partial findings before a kill are preserved; plugins inherit `FENDIX_PLUGIN_NAME` + `FENDIX_PLUGIN_DIR` env vars; every emitted finding gets `fendix-plugin:<name>` appended to References for provenance. Plugin findings flow through the same Correlate / Dedup / Sort / ID-assignment pipeline as embedded engine findings — a custom-secret-pattern plugin correlates against blackbox auth checks identically to the built-in secrets analyzer. New **`--no-plugins`** CLI flag disables discovery for sandboxed CI or debugging. Three reference plugins under `examples/plugins/`: `custom-secret-pattern` (Python, regex-based custom secret detection), `custom-blackbox-check` (Python, custom HTTP-response assertion), `custom-semgrep-pack` (shell, wraps a custom Semgrep rule pack). Author guide: `docs/plugins.md` covers discovery, IPC schema, security model, and an authoring checklist.
  • **Reachability/dataflow correlation: `correlated:reachable` for proven exploit paths.** The Python AST analyzer now records taint chains for SQLi, SSRF, and open-redirect findings: when `_collect_taint_chain` proves intra-function dataflow from a request source (`request.args/POST/form/data/json/headers` plus the Flask handler-arg form `req`) through one or more variable assignments to a dangerous sink, the emitted finding carries `taint_chain: [{file, line, expr}, …]` plus `reachable: true`. The chain walks recursively through scope assignments — `q = request.args.get('q'); sql = '...' + q; cursor.execute(sql)` resolves three links without false positives on literal-only chains. **Correlator escalation:** when the whitebox half of a correlated pair carries a chain, the merged finding inherits the chain plus `reachable: true` AND gets a *second* severity escalation. So MEDIUM blackbox + MEDIUM whitebox + reachable jumps to CRITICAL (vs. HIGH without reachability). HTML reporter renders the chain as an ordered list under finding details ('Reachable dataflow (N steps)').
  • **GitHub App business logic wired end-to-end.** Replaced the v0.6.1 scaffold's stub handlers with the full PR workflow on top of the credentials/auth layer. On every `pull_request.{opened,synchronize,reopened}`, the webhook handler now: (1) fetches an installation token from the cached `TokenSource`; (2) clones the PR head SHA via `git init` + shallow `fetch --depth=1 origin <sha>` + `checkout FETCH_HEAD` (only the exact commit, no history; auth via `x-access-token:<token>@…` userinfo on the HTTPS clone URL); (3) runs `fendix scan --code <tmp> --format json`; (4) re-renders SARIF via `fendix report --format sarif` so the PR comment + Code Scanning tab describe identical findings; (5) renders a Markdown PR comment matching the `examples/github-actions/fendix-scan.yml` github-script template byte-for-byte modulo whitespace; (6) POSTs the comment to `/repos/{o}/{r}/issues/{n}/comments`; (7) gzip+base64-encodes the SARIF and uploads to `/repos/{o}/{r}/code-scanning/sarifs` against `refs/pull/<n>/head`. SARIF upload is best-effort (Code Scanning disabled or `security_events: write` missing → log warning, comment still posts). **`check_run.action == "rerequested"`** re-runs the scan against the recorded head SHA. **Tempdir always cleaned up** via `defer os.RemoveAll`. **Tokens redacted** from any error surfaced for git-step failures. **Per-scan timeout:** 15 minutes wall-clock. **Distribution:** new `Dockerfile.app` (multi-stage; bundles `fendix` + `fendix-app` + Python engine + `git` + tini in ~250 MiB Debian-slim). 26 new ghapp tests under `-race`.
  • **Vulnerable-app benchmark numbers captured.** Stock `fendix scan --url http://localhost:3000` against `bkimminich/juice-shop:v17.1.1` on Fendix v0.6.1 produced **97 endpoints discovered / 7 deduped findings (4 MEDIUM + 2 LOW + 1 INFO) / 41.5s scan time / 0 correlated** (passive-only run; juice-shop's intentional SQLi/XSS/IDOR vulns need `--enable-active` and/or `--code` to surface). Pre-dedup the engine emitted **391 raw findings** that deduplication collapsed to 7. Numbers published in `docs/benchmarks.md` 'Latest results' table with a 'Reading the row' explanation that documents what the row does NOT measure.
  • **GitHub App scaffold.** New `cmd/fendix-app` binary (separate from the `fendix` CLI; long-running webhook server) plus `internal/ghapp` package and `app/manifest.yml` for one-click App registration via GitHub's manifest flow. Webhook layer: HMAC-SHA256 signature verification (legacy `sha1=` rejected), event router, 4 MiB body cap. Auth layer: pure-stdlib RS256 App-JWT signing (no `golang-jwt` dep added — preserves the project's zero-runtime-deps posture), `/app/installations/{id}/access_tokens` exchange, single-flight installation-token cache. Setup guide: `docs/github-app.md`. 28 unit tests under `-race`. Marketplace listing is an operator step distinct from the code deliverable.
  • **`fendix demo` command.** New cobra subcommand spins up `bkimminich/juice-shop:v17.1.1` in Docker on `localhost:3000`, runs a stock scan, renders an HTML report, and (with `--open`) opens it in the user's default browser. Container always cleaned up on exit. Flags: `--open`, `--port`, `--output`, `--image`. Removes the cold-start 'what does a real scan look like?' question for first-time evaluators.
  • **`.fendix.yaml` repo-committed policy.** New `internal/policy` package + new `--config <path>` flag on `fendix scan`. Teams commit a `.fendix.yaml` at repo root encoding scan posture (severity threshold, scan budgets, auth profile reference, crawler defaults, format) and invoke `fendix scan` with one CLI flag instead of the prior six-flag-and-growing wall. **Precedence** matches `git config`: cobra defaults < `.fendix.yaml` values < explicit CLI flags. **Strict YAML parsing** (`yaml.KnownFields(true)`) rejects typos. Schema versioned; future v2 forward-rejected. **`fendix init` now writes 3 files** (workflow + `.fendix.yaml` + `.fendix-ignore`).
  • **Backend not extended for plugins, `--config`, or the GitHub App at v0.7.0 release time.** Plugins run on the host filesystem (backend container can't see them); `--config` is a host-filesystem flag (the API itself accepts every policy field directly); `fendix-app` is a separate deployable on the GitHub-event side of the pipeline.
  • **Reachability fields persisted backend-side and typed frontend-side.** v0.7.0 originally landed `reachable` + `taint_chain` + `affected_endpoints` on the engine's `Finding` JSON output, but the backend's `_finding_defaults` silently stripped them on ingest, so `GET /api/findings` served lossy rows. Closed in a follow-up: backend migration `0006_finding_reachability` adds 3 columns to `ScanFinding` (`affected_endpoints` + `taint_chain` JSONField, `reachable` Bool) with safe defaults; `_coerce_taint_chain` defensive parser drops malformed links rather than failing the whole finding; serializer exposes all three; `openapi.json` regenerated; 3 new tests. Frontend gains a `TaintLink` interface and `Finding.taint_chain?` + `Finding.reachable?`; `app/types/api.ts` regenerated via `npm run codegen`. **No UI surfacing yet** — deferred until the engine ships XSS and command-injection reachability patterns; a real findings-detail iteration earns its design cost against 5 reachable categories, not 1.

Patch — install.sh `mkdir -p` fix

May 1, 2026

  • **Critical install-pipe fix.** `scripts/install.sh` now `mkdir -p` the install directory before the `mv`. Previously, `curl -fsSL https://get.fendix.dev/install.sh | FENDIX_DIR=$HOME/.local/bin sh` failed on any system where `$HOME/.local/bin` didn't pre-exist with `mv: cannot move 'fendix' to '/home/runner/.local/bin/fendix': No such file or directory`. Blocked the benchmark CI on every run and broke any first-time user who set `FENDIX_DIR` to a non-existent dir. The fix tries `mkdir -p` non-sudo first, falling back to `sudo mkdir -p` only when a parent up the chain isn't writable — POSIX-sh clean. Mirrored to `get.fendix.dev/install.sh` automatically by the release pipeline's mirror-sync job
  • **`fendix init` zero-config workflow generator.** New `fendix init` subcommand detects the project's stack (Go via `go.mod`, Python via `pyproject.toml`/`requirements.txt`/`setup.py`/`Pipfile`, Node.js, Ruby, Rust, Java/Kotlin, PHP) plus a colocated OpenAPI/Swagger spec at any of 14 conventional paths. Writes `.github/workflows/fendix.yml` (drop-in PR-gated DAST + SAST scan, embedded via `go:embed`) plus `.fendix-ignore` (commented starter for finding-level suppressions). Refuses to overwrite by default; `--force` overrides; `--print` dry-runs to stdout. **Note:** `fendix init` was later extended to also write `.fendix.yaml` — see the v0.7.0 entry
  • **README repositioned around the wedge.** Hero rewritten from generic 'Find vulnerabilities before attackers do.' to 'DAST + SAST in one PR check. Fails only when both engines confirm.' Three-bullet trust block matches the get.fendix.dev landing page (confirmed findings, single binary, signed + silent)
  • **'What Fendix sends to the network' section at top of README.** Five-row table covering default scan / active probing / white-box / no-flags / telemetry. Explicit 'no telemetry code; verify with tcpdump' claim. Plus a 'Verifying signed releases' section with the full cosign keyless verify recipe
  • **Vulnerable-app benchmark scaffold.** New `scripts/benchmark/run-juice-shop.sh` + `make benchmark` + `.github/workflows/benchmark.yml` (`workflow_dispatch` only) + `docs/benchmarks.md`. CI workflow installs Fendix via `https://get.fendix.dev/install.sh` (doubles as install-pipe smoke test). Real juice-shop numbers captured in the v0.7.0 entry

First Stable Signed Release

April 30, 2026

  • **First stable signed release.** Every binary, `.deb`, `.rpm`, and Docker manifest ships with `.crt` + `.sig` cosign sidecars verifiable against the build's GitHub Actions OIDC identity — no static public key, no rotation surface, no key-loss recovery story
  • Cosign keyless signing fully active on the release pipeline (Sigstore Fulcio + GitHub Actions OIDC). `COSIGN_ENABLED=true` flipped on the engine repo on 2026-04-30T14:07Z; rc2 was the first tag to exercise the full signed-release path end-to-end, then promoted to v0.6.0 final after pipeline validation. Cosign steps hard-fail the release job, so a broken signing path can't silently ship unsigned artifacts
  • `https://get.fendix.dev/install.sh` is live — DNS CNAME at the registrar, GitHub Pages on the homebrew-fendix mirror, auto-provisioned Let's Encrypt cert. End-to-end smoke test verified the install pipe end-to-end. Mirror URL retained as a documented fallback. Engine repo is now the single source of truth for everything served at get.fendix.dev — auto-synced by `release.yml` on every `v*` tag
  • Linux arm64 release binary added — `brew install fendix` and `curl -fsSL https://get.fendix.dev/install.sh | sh` now serve native ARM builds for Graviton, Ampere, Raspberry Pi, and ARM Linux laptops
  • Multi-arch Docker image at `ghcr.io/abdel-rahmansaied/fendix:vX.Y.Z` — manifest list covers `linux/amd64` + `linux/arm64`; `docker pull` picks the right arch automatically
  • Linux `.deb` and `.rpm` packages built via nfpm — install with `sudo dpkg -i fendix-*.deb && sudo apt-get install -f` or `sudo dnf install ./fendix-*.rpm`. Declares `python3` as required and `semgrep` as recommended
  • Documentation pass for external evaluators: 5-minute juice-shop walkthrough (`docs/walkthrough-juice-shop.md`), Semgrep rule-author guide (`docs/semgrep-rules.md`), triage workflow (`docs/triage-workflow.md`), JSON schema reference (`docs/schema.md`), all cross-linked from a top-level Documentation index in the README
  • New `--debug-bundle <path>` flag writes a redacted diagnostic `.tar.gz` for attaching to bug reports — auth values masked as `[REDACTED]`, full DEBUG slog stream tee'd into the bundle, probe audit log on `--enable-active`. Auth credentials never leak (e2e regression locks this in)
  • `SECURITY.md` + active-scanner threat model (`docs/threat-model.md`): vulnerability disclosure policy, supported-versions, cosign verification instructions, and the 7-threat safety envelope every active probe must maintain
  • Performance benchmark suite: scan time vs endpoint count, peak goroutine count, allocation profile — published in README. Reference: 1000 endpoints in 31.7 ms / 24.7 MB / 166 peak goroutines (Apple M1, Go 1.21)
v0.6.0-rc1RC Complete

External Release Readiness

April 30, 2026

  • Linux arm64 release binary added — `brew install fendix` and `curl -fsSL .../install.sh | sh` now serve native ARM builds for Graviton, Ampere, Raspberry Pi, and ARM Linux laptops
  • Multi-arch Docker image at `ghcr.io/abdel-rahmansaied/fendix:vX.Y.Z` — manifest list covers `linux/amd64` + `linux/arm64`; `docker pull` picks the right arch automatically
  • Cosign keyless signing wired (Sigstore Fulcio + GitHub Actions OIDC; opt-in via the `COSIGN_ENABLED=true` repo variable). When enabled, every binary ships with `.sig` + `.crt` sidecar files
  • Linux `.deb` and `.rpm` packages built via nfpm — install with `sudo dpkg -i fendix-*.deb && sudo apt-get install -f` or `sudo dnf install ./fendix-*.rpm`. Declares `python3` as required and `semgrep` as recommended
  • New `docs/install.md` install reference covers every install path with cosign verification one-liners; `get.fendix.dev` short-URL installer planned (operator-action: domain registration + GitHub Pages CNAME)
  • Documentation pass for external evaluators: 5-minute juice-shop walkthrough (`docs/walkthrough-juice-shop.md`), Semgrep rule-author guide (`docs/semgrep-rules.md`), triage workflow (`docs/triage-workflow.md`), JSON schema reference (`docs/schema.md`), all cross-linked from a top-level Documentation index in the README
  • New `--debug-bundle <path>` flag writes a redacted diagnostic `.tar.gz` for attaching to bug reports — auth values masked as `[REDACTED]`, full DEBUG slog stream tee'd into the bundle, probe audit log on `--enable-active`. Auth credentials never leak (e2e regression locks this in)
  • `SECURITY.md` + active-scanner threat model (`docs/threat-model.md`): vulnerability disclosure policy, supported-versions, cosign verification instructions, and the 7-threat safety envelope every active probe must maintain
  • Performance benchmark suite: scan time vs endpoint count, peak goroutine count, allocation profile — published in README. Reference: 1000 endpoints in 31.7 ms / 24.7 MB / 166 peak goroutines (Apple M1, Go 1.21)
  • Release-candidate cut to validate the new pipeline (cosign + nfpm + ghcr) end-to-end before tagging clean v0.6.0; signed-release validation pending `COSIGN_ENABLED=true`

Quality & Ops

April 30, 2026

  • Public JSON output schema published (`docs/schema.md` + `docs/schema.json` draft-07); validator runs in tests against every emitted report; `findings: []` instead of `null` when empty
  • Path-parameter substitution: templated endpoints like `/users/{id}` now scan against `/users/1` (or schema-derived sample). Resolution order: `schema.example` → `schema.enum[0]` → type-driven default → name heuristic → fallback `1`. Endpoint.Path stays templated for reports; FullURL is concrete
  • Logging hygiene: aggregated WARN volume — max 3 per check key per scan, rest downgraded to DEBUG. New `INFO warning summary` line at scan end. Real-world: 30 WARN lines → 9 + 1 summary on a 10-endpoint scan against an unreachable target
  • Scan budgets: new `--max-requests` (soft cap on total HTTP requests; discovery exempt), `--max-duration` (Go duration string, e.g. `5m`), `--respect-robots` (treat robots.txt Disallow as hard restriction across all discovery sources)
  • New auth profile: `--auth-type apikey-query` puts the credential in the URL query string instead of a header — common for legacy/sensor APIs that prefer query placement
  • Auth profiles end-to-end: bearer / apikey-header / apikey-query / basic / cookie all covered by e2e tests that record what reaches the server
  • Concurrency review: race-clean proof at 1000 endpoints × 32 workers under `go test -race`; new `FuzzWorkerPool_CancelTiming` fuzzer (4455 execs / zero failures over 15s of fuzzing)
  • Severity↔confidence consistency enforced — LOW confidence caps severity at MEDIUM, MEDIUM caps at HIGH (matches the scoring formula's implicit max). Inconsistent findings get severity downgraded with an aggregated WARN line
  • Drop-in GitHub Actions workflow: `examples/github-actions/fendix-scan.yml` does scan → `actions/cache` baseline → SARIF upload → PR summary comment via `actions/github-script@v7`

Coverage Parity

April 29, 2026

  • Correlator finalized: HTTP method-prefix stripping + path-suffix matching (handles base-path skew like spec `/pet/findByStatus` ↔ live `/api/v3/pet/findByStatus`); debug instrumentation with `match_kind=exact|suffix|fuzzy`; blackbox findings consumed at most once
  • Real CVE coverage: pip-audit (PyPI) + npm audit (JS) + govulncheck (Go) as primary paths; hardcoded list as offline fallback. Real-world: badcode/requirements.txt = 6 deps findings (offline) → 97 with pip-audit installed (16× coverage)
  • Go module support — `go.mod` files now scanned by govulncheck; only emits findings on actually-called code paths (vendored-but-uncalled noise dropped)
  • Crawler upgrade: robots.txt + sitemap.xml + HTML link parsing with recursive depth. Real-world: httpbin.org discovery went from 1 endpoint to 3 (Disallow /deny + linked /forms/post + brute-force /robots.txt)
  • New flags: `--wordlist`, `--crawl-depth` (default 1), `--max-endpoints` (default 500); built-in `CommonPaths` expanded ~50 → 117 with admin/dashboard/source-control/DevOps tooling paths
  • Findings deduplication: identical issues across N endpoints collapse into one finding with `affected_endpoints` (real-world: petstore 160 → 10, 16× reduction)
  • Static analyzer: 6 new patterns — pickle.load, yaml.load without SafeLoader, MD5/SHA1 for passwords, open redirect, SSRF, auth-header trust
  • Multi-step SQLi detection via intra-function scope tracking (`sql = '...' + var; cursor.execute(sql)`)
  • Active scanner: body & header param probing; error-based + boolean-based SQLi; SQLite + Oracle time-based payloads (5 DB types total)
  • New `--max-probes-per-endpoint` flag (default 20) for active scan budget control
  • Secrets analyzer: 8 new provider patterns — GitHub, Stripe, Slack, Google, Anthropic, OpenAI, npm, GCP service-account JSON (15 total, was 7)
  • .env file scanning fixed (dotfile walker now yields env-files; unquoted KEY=value pattern gated to .env*)
  • v0.4.0 ships the planned v0.3.0 batch under a single tag
v0.2.0 Complete

P0 Flag Wiring

April 29, 2026

  • `--save-baseline` now actually writes a file (was previously a silent no-op at the CLI)
  • `--code`-only scans run successfully (orchestrator no longer early-exits when only --code is given)
  • Active scanner now uses spec-defined query/path parameters instead of hardcoded `id`
  • `--spec` now accepts `https://...` URLs in addition to local file paths (fetched with content-type detection, 50 MB cap)
  • SARIF rule IDs are now stable per check type (`fendix.<category>.<title-slug>`) — breaking change for v0.1 SARIF baseline consumers
  • End-to-end test infrastructure: every CLI flag now has a test that runs the binary and asserts observable effect
v0.1.0 Complete

Initial Release

April 2026

  • Initial production release — ~594 tests across the engine
  • MIT License, CHANGELOG, .fendix-ignore.example template
  • Ready for production use with full documentation
Phase 9 Complete

Hardening

April 2026

  • Performance benchmark suite across all critical paths
  • Go native fuzz testing (362k+ executions, 0 panics)
  • Python hypothesis fuzz testing — found and fixed 3 real bugs
  • Self-audit: 0 production code vulnerabilities
  • 29 resilience tests: garbage responses, timeouts, crashes, malformed streams
  • Memory profiling: 2.3KB/finding, 15MB/1000 correlations
  • Error message audit — 7 messages improved with actionable guidance
Phase 8 Complete

Documentation

April 2026

  • Complete README.md with all 10 required sections
  • CONTRIBUTING.md with development setup and check-writing guides
  • 11 individual check documentation pages in docs/checks/
  • 6 Architecture Decision Records (ADR-001 through ADR-006)
  • CHANGELOG.md following Keep a Changelog format
  • Full godoc (92 Go symbols) and docstring (16 Python symbols) coverage
Phase 7 Complete

Distribution

April 2026

  • Python engine embedded into Go binary via go:embed
  • Auto-extraction to ~/.fendix/engine/ on first run with version stamp
  • Graceful fallback if Python not installed (whitebox skipped with clear message)
  • GitHub Actions release workflow: linux/amd64, darwin/amd64, darwin/arm64
  • Multi-stage Dockerfile with non-root user
  • curl-pipe installer with platform detection and sha256 verification
  • Homebrew formula (brew tap fendix/tap && brew install fendix)
Phase 6 Complete

Reporting & CI/CD

April 2026

  • JSON reporter with full scan metadata (mode, endpoints, source counts)
  • Self-contained HTML report with JS sorting, expand/collapse, print CSS
  • SARIF 2.1.0 reporter — GitHub Code Scanning compatible
  • fendix report command — re-render JSON to HTML/SARIF without re-scanning
  • Baseline diff mode: --baseline and --save-baseline for PR workflows
  • CI/CD integration docs with GitHub Actions examples
Phase 5 Complete

Active Scanner

March 2026

  • Safe probe framework with audit log and legal disclaimer
  • Time-based blind SQL injection detection (MySQL, PostgreSQL, MSSQL)
  • Command injection canary detection with safe echo payloads
  • CRLF header injection probes
  • Per-endpoint probe rate limiter (max 20 probes/endpoint)
  • --enable-active gate ensures probes never run by default
Phase 4 Complete

Hybrid Engine & Orchestration

March 2026

  • Go subprocess spawner for Python engine with full IPC
  • Streaming Finding reader with malformed-line resilience
  • Correlator: endpoint normalization, fuzzy matching, severity escalation
  • .fendix-ignore suppression with YAML rules and expiry dates
  • Baseline diff for tracking new vs. existing findings
  • --fail-on exit code logic for CI/CD gates
Phase 3 Complete

Auth Scanner

March 2026

  • AuthContext model with multi-source credential resolution
  • Unauthenticated access detection on protected endpoints
  • JWT bypass checks: none algorithm, expired token, signature strip
  • IDOR two-account access control verification
  • Credential masking ([REDACTED]) in all report formats
  • ~/.fendix/profiles/ persistent config system
Phase 2 Complete

White-Box Engine

March 2026

  • Python engine.py with full IPC contract and error handling
  • Secrets analyzer: 7 pattern types (AWS, PEM, API key, JWT, DB URI, etc.)
  • OpenAPI spec parser for 2.0 and 3.x with 4 auth checks
  • Semgrep rules for auth, injection, and secrets
  • AST analyzer for Python and JavaScript code patterns
  • Dependency CVE checker via pip-audit and npm audit
Phase 1 Complete

Passive Scanner & Reporting

February 2026

  • Endpoint crawler with spec parsing, JS discovery, and brute-force
  • Security headers, CORS, data exposure, and rate limit checks
  • Worker pool concurrency model
  • JSON reporter with scan metadata
  • Self-contained HTML reporter with color-coded severity
  • Orchestrator wiring for all passive checks
Phase 0 Complete

Foundation

February 2026

  • Go module and Python package initialization
  • Finding, ScanConfig, and severity scoring models
  • Cobra CLI skeleton with version command
  • GitHub Actions CI workflow
  • Makefile for build, test, lint, and clean
  • ADR-001 and ADR-002 architecture decision records

v1.1.0 shipped July 2026 — real-world precision. A new benchmark scores Fendix against real repositories, not just synthetic cases, and the dominant false-positive classes are closed: constant SQL misread as injection, non-dominating safe guards, test-fixture code, double-sanitized output, and more. On a live Django app the SAST taint surface fell from ~90 findings to 5 while the single genuine vulnerability stayed flagged, and the synthetic accuracy gate held at F1 1.000 throughout. A performance fix in the code analyzer took a full scan of a large real project from ~50 minutes to 9 seconds, the verify action now re-checks correlated and active-probe findings, and scanner coverage, decision counts, and per-finding source-tier are surfaced in the app. It builds on v1.0.0 — the production milestone: decision reports (BLOCK / WARN / INFO with a deterministic, explainable confidence band), Java SAST coverage, accuracy every claim of which is reproducible and CI-gated, and signed, reproducible releases with cosign keyless signatures, CycloneDX / SPDX SBOMs, and SLSA build provenance.