Skip to content

What's new

A version-by-version history of Fendix. v3.7.0 shipped October 2026: the GitHub Action fails the job when Semgrep cannot run, and a fatal Semgrep run is recorded as failed instead of passing as a clean scan. Each entry below lists what changed in that release.

A finding keeps its identity when your code moves

August 2026

  • Adding a comment above a vulnerability used to turn it into a different vulnerability. A finding was identified partly by the line it sat on, so any edit above it gave it a new identity: your baseline stopped recognising it, your .fendix-ignore rule stopped suppressing it, and something you had already triaged came back as new work. Findings are now identified by what they are — which rule fired, in which file, in which function, about which operation — and not by where they happen to sit today. Scanned before and after an edit that moved every line of the file, every finding was still recognised; the old scheme lost roughly a quarter of them and reported the same issues over again as brand-new.
  • Findings no longer claim more than they proved. A path-traversal finding said "user input flows to filesystem path" even when Fendix had only seen the filesystem call and could not trace any user input reaching it. A fully proven SSRF, meanwhile, still described itself as "potential". The wording now follows the evidence: "Potential" when only the dangerous operation was observed, and a plain statement when the path from user input was actually traced. The finding does not change identity when its wording strengthens — it is the same issue, better understood.
  • GitHub now shows what Fendix actually believes about a finding. A placeholder API key in a test fixture — scored low-confidence, held as a warning, never going to fail your build — was still handed to GitHub ranked as High with nothing to say otherwise. An alert now carries the rule's inherent severity, Fendix's confidence in that specific instance, and the two combined, each stated separately. Where they disagree, the alert says so in the first line you read.
  • Rate limiting is tested with the method it reports. Fendix reported "no rate limiting observed" against POST /login having only ever sent a GET — a claim about a test that never happened. It now probes the operation's own method where that is safe: a passive scan will not fire twenty writes at your production API, and a DELETE is never burst-probed at any setting. Where an operation cannot be probed safely it is reported as not tested, rather than quietly counted as passing.
  • A rate-limit sweep reads as one finding instead of eight hundred locations. A sweep across a large API produced a single result carrying a separate location for every operation it touched, which no tool renders legibly. You now get a readable sample with the true total beside it, and the complete list is still attached.
  • Separate scan types stop clearing each other's GitHub alerts. A code scan and a live scan of the same repository were filed under one category, so whichever ran last marked everything the other had found as fixed. Each scan type now keeps its own set of alerts.
  • Before you upgrade, re-save your baselines. Because findings are identified differently now, a baseline written by an older version matches nothing — the first scan after upgrading would report every finding as new. Run --save-baseline once to regenerate it, and rewrite any .fendix-ignore rule that pins a fingerprint: value. Rules using the supported id, endpoint, or category selectors are unaffected by the fingerprint change, although positional SEC-NNN ids are suitable only for short-lived triage. GitHub code-scanning alerts also re-file themselves once, under their new rule names.

A running scan shows you where it is

August 2026

  • The progress bar moves while a scan is actually scanning. Fendix reports its progress at checkpoints, and there was no checkpoint during the longest part of a scan — probing your endpoints, which takes even longer with active probing on. The bar reached that point and sat there, so a scan that was working looked stuck. It now advances through that phase as the endpoints are covered.

Bring findings from your other scanners

August 2026

  • Import SARIF from CodeQL, Semgrep, Trivy or anything else. fendix import report.sarif runs another scanner's findings through the same pipeline your own scans use — deterministic confidence scoring, dedup, .fendix-ignore and baseline filtering, and the confidence-gated fail-on decision. Attach one to a scan with fendix scan --import report.sarif, or upload it in the dashboard. Imported findings are labelled as such and never claim to be verified by Fendix's own engines.
  • When two engines independently agree, the finding says so. A finding now shows which other tool confirmed it. Confirmation is deliberately hard to earn: a genuinely different tool has to report the same weakness (exact CWE) at the same location, within five lines. Matching titles, matching categories, the same tool run twice, or two findings that merely sit in the same file never count — a false "both engines confirm" is worse than a missed one.

Reports that name the engine that produced them

August 2026

  • A scan run from the Docker image now records its real engine version. Every image ever published answered fendix version docker, so a scan run that way filed its engine as "docker" — in the scan record, and in the driver.version field of the SARIF handed to GitHub code scanning. A report has to be able to say which build produced it. The image now carries its release tag, exactly as the downloadable binaries always have. Detection and scan behaviour are unchanged from v2.0.0.

Trust & precision — findings that only claim what they can prove

August 2026

  • Your build no longer fails on a guess. A finding could previously fail your build on severity alone, even when the scan itself had recorded that it could not confirm the issue — the report literally said "unconfirmed by live scan" next to a failed build. Blocking now also requires the confidence to support the claim: high confidence blocks, medium blocks when something corroborates it, low warns instead. Real hardcoded credentials in production code still fail the build, exactly as before. If you want the old behaviour back, --enforce-confidence=false restores it precisely.
  • One finding per vulnerability, not one per advisory database. A vulnerable dependency is usually catalogued several times over — the same flaw filed once as a GHSA and again as a PYSEC record. You were seeing each copy. A recent cryptography pin produced six findings for three actual vulnerabilities; it now produces three, each naming its CVE and listing the other identifiers it covers.
  • Upgrade advice you can actually run. "Upgrade to a patched version (no fix listed)" appeared even when a fix existed, and in some cases the suggested version was a git commit hash rather than a release. Fix versions are now read correctly and ranked against the version you actually have, so a project on Django 5.2.16 is told to go to 5.2.17 — not pushed onto the next major release.
  • Test fixtures stop shouting. A FAKE_API_KEY in your test folder was failing builds like a live credential. Placeholder-shaped values are now recognised deterministically and step aside, while a genuine secret in test code still gets your attention if anything corroborates it. Nothing is hidden — the finding and its evidence remain, exactly as they always have.
  • Secrets never leave the scanner. A detected credential used to have the first characters of its value copied into the finding's evidence, which then travelled into every report and export. The value is now replaced with a length and a hash the moment it is detected, so the secret itself never reaches a report at all.
  • Reports that name real places. Dynamic findings were being labelled with a source file called "None", and a finding on a URL could end up filed under a file called "https". Findings from a live scan are now anchored to the endpoint they came from, and file-based findings to their real path and line.
  • Better GitHub code-scanning integration. Results now carry a stable identity, so an alert stays the same alert between runs instead of reappearing as new; a severity score, so GitHub ranks them correctly; and a readable description, with the offending line of code shown as a code snippet rather than pasted in as the title.
  • Fewer false alarms on Dockerfiles. A multi-stage Dockerfile referring to its own earlier stage — FROM base AS production — was reported as an unpinned image. Build stages are now understood. Separately, base images pinned only to a tag rather than a digest are now noted for information.

Sharper signal — one finding per problem, and severity that reflects proof

August 2026

  • One problem, one finding. Several analyzers cover the same ground, and when two of them recognised the same issue you used to see it reported two or three times under different names — a single hardcoded SECRET_KEY produced three separate findings at two different severities. Duplicates from different analyzers at the same line are now merged into the one from the most trustworthy engine, so the count in your triage queue is the number of problems you actually have.
  • Test code steps aside. Findings in tests, fixtures and conftest files now report as INFO by default, so production code leads your triage instead of competing with deliberately-vulnerable test data. Nothing is hidden — the finding and its evidence are still there — and a finding at or above your --fail-on threshold still blocks the build. Turn it off with --deescalate-tests=false if you want to gate on test code too.
  • Severity now reflects proof. The analyzer flags dangerous shapes as well as proven exploit paths, and both used to arrive at the same CRITICAL. A finding where we could not trace user input to the sink now carries a lower severity than one where we can show the whole path — so CRITICAL means we proved it, not that the code looked risky.
  • Context-aware confidence. A missing security header on a CDN-served asset, or a permissive CORS policy seen only behind a login wall, is a weaker signal than the same thing on a live API route. Those are now scored lower and tell you why in the finding's reasons, rather than sitting at full confidence alongside real issues.
  • Quiet failures made loud. A profile with its credential at the wrong nesting level used to scan completely unauthenticated with no warning — it now fails with the exact correction. Session cookies missing HttpOnly on a 401 login response are reported instead of discarded, multi-line JWTs are detected, and the code analyzer no longer stops early when an optional dependency is missing.

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.

v3.7.0 shipped October 2026 — the GitHub Action requires Semgrep for code scans and fails the job when it cannot run, installs wheels only and uploads SARIF only for a finished scan, and a fatal Semgrep run is recorded as failed; no report-schema, SARIF, fingerprint or decision-policy changes, and every exit code keeps its meaning. v3.6.5 shipped October 2026 — Semgrep 1.182.1, MCP 1.29.0 and golang.org/x/mod 0.40.0 fix the last fixable HIGH advisories in the official image, Semgrep no longer contacts semgrep.dev, and self-hosted Linux CI runners need glibc 2.34 or later for Semgrep; no scanner, fingerprint, report-schema, decision-policy or exit-code changes. v3.6.4 shipped October 2026 — rebuilt on Go 1.27.3 and certifi 2026.10.23, so TLS no longer trusts the eight compromised emSign intermediate CAs and a hostile HTTP/2 scan target can no longer burn the scanner's CPU; no scanner, fingerprint, report-schema, decision-policy or exit-code changes. v3.6.3 shipped October 2026 — four bundled Semgrep rules that never matched now report, WARN by default under the confidence-gated --fail-on; the Flask auth advice says the decorator goes below @app.route(...); eleven checks have static catalog copy; no finding-identity change. v3.6.2 shipped October 2026 — diff-aware scans with a relative --code scan the changed files again, and a diff scope that cannot be represented safely falls back to a full scan. v3.6.1 shipped October 2026 — SARIF for a scan with no findings is valid, and the GitHub Action verifies the release signature before it runs the binary. v3.6.0 shipped October 2026 — baselines, .fendix-ignore rules and SARIF act on each security occurrence, so a baselined or ignored test fixture can no longer hide a new production credential grouped with it; baseline format v2, one SARIF result per occurrence, and fendix jira and fendix verify track every occurrence. v3.5.1 shipped October 2026 — fendix init writes CI that installs a pinned, signature-verified Fendix, brew upgrade reaches installs older Homebrew recorded as version 64, and the installer can require a signature or a pinned SHA-256; no scanner, fingerprint, report-schema, decision-policy or exit-code changes. v3.5.0 shipped October 2026 — --fail-on refuses an invalid threshold instead of silently scanning with none, fendix verify no longer reads missing input as a fix, and images publish to Docker Hub; no fingerprint, report-schema or decision-policy changes. v3.4.1 shipped September 2026 — the engine images now ship Go 1.27, raising govulncheck's module ceiling from 1.25; no scanner, fingerprint or exit-code changes, and the Go module floor stays 1.25. v3.4.0 shipped September 2026 — a scan can no longer look complete when it wasn't: every analyzer records whether it ran and why not, the report carries a coverage block that names the gaps, a URL scan with zero endpoints writes its report instead of vanishing, and two opt-in flags turn a coverage gap into a failing build. v3.3.0 shipped September 2026 — an upgrade no longer splits a finding you were already tracking into a closed one and a new one, and the list of checks a scan ran is built from what completed rather than what was configured. v3.2.0 — every blocking decision explains itself. A BLOCK now carries the policy it was made under and the evidence class that justified it, all the way through Fendix's own storage into the SARIF you export, so a failed pipeline is answerable from the report alone. Findings held back say why, confidence scores come with the rules that produced them, and a build blocked only because confidence enforcement was switched off is labelled as such. v3.1.0 stopped findings claiming more than they proved — wildcard CORS with credentials, rate-limit prioritisation, response-aware header grading, path-traversal wording, test-fixture credentials and dependency applicability all became more precise. v3.0 gave findings a stable identity based on which rule fired, in which file, in which function, about which operation. Re-save your baselines once after upgrading from before v3.0, and rewrite any .fendix-ignore rule that pins a fingerprint: value — rules using the supported id, endpoint or category selectors are unaffected by the fingerprint change, although positional SEC-NNN ids are suitable only for short-lived triage.