Skip to content

v2.90.0: agent-actionable security candidates, LSP pull diagnostics

Choose a tag to compare

@BartWaardenburg BartWaardenburg released this 09 Jun 07:31
· 63 commits to main since this release
v2.90.0
c36e774

v2.90.0 turns the opt-in fallow security surface into an agent-actionable layer: every candidate now carries a structured record an AI agent can act on, a new exfiltration category, an editor diagnostic path, and a PR regression gate. It also modernizes the LSP server (pull diagnostics for Neovim, Zed, and Helix), adds editor controls for diagnostic noise, and ships two CLI ergonomics flags.

Security findings remain candidates for verification, not confirmed vulnerabilities. The whole surface stays default-off and never appears under bare fallow or fallow audit.

Security: agent-actionable candidates

  • Structured candidate records (--format json). Every finding now carries a candidate object with three slots an agent can act on directly: source_kind (the untrusted input that reaches the sink, as a stable catalogue id like http-request-input or process-env), a self-contained sink (path, line, column, category, CWE, callee), and a boundary (client/server or module crossing, plus the architecture zone). Findings with a traced flow also carry a compact taint_flow. Each finding has a stable finding_id (identical to its SARIF partialFingerprints value) so an agent can correlate a candidate across runs after a rebase. All additive and output-only.
  • secret-to-network exfil category (opt-in). Detects a non-public process.env / import.meta.env secret reaching the body or options argument of a network call (fetch, axios, got, http.request, ...) when the same identifier carries the secret into the request, the classic data-exfiltration shape. Because legitimate auth is also "a secret reaching a network call," it is opt-in via security.categories.include: ["secret-to-network"]. Each candidate carries candidate.network.destination (the request URL when literal, or absent when dynamic, the higher-signal case). The same change stops treating public-by-convention env vars (NEXT_PUBLIC_, VITE_, ...) as secrets across the catalogue and adds Vite's import.meta.env as a recognized source.
  • Editor diagnostics (opt-in). Raise the security-sink or security-client-server-leak rule to warn / error and the fallow LSP server reports each candidate as an information-level diagnostic in any LSP editor. The hover leads with confidence signals (source-backed, entry-reachable) and a pointer to fallow security --file; a quick-fix dismisses the candidate. Structured data lets agents reading editor diagnostics triage without re-running the CLI.
  • --gate new regression gate. fallow security --gate new --changed-since <ref> (or git diff --cached --unified=0 | fallow security --gate new --diff-stdin) gates a PR only on candidates introduced on changed lines, exiting 8 when any exist. A refactor that merely touches a file with an existing sink passes; a diff the gate cannot compute is a loud exit 2, never a green gate. Exit code 8 is dedicated so CI can soft-gate it (GitLab allow_failure: exit_codes: [8]).
  • More catalogue coverage. Source-backed resource amplification (CWE-400, new Array(n) / Buffer.alloc / String.prototype.repeat from request input), ReDoS regex (CWE-1333), dynamic RegExp construction, disabled TLS validation, jsonwebtoken verify without an algorithm allowlist, secrets or PII reaching logs (source-gated), Vite import.meta.env client reads, one-hop helper-return source flows, dead-code cross-linking, more entry-point source inputs (GraphQL args, tRPC input, webhooks, DOM-XSS reads), and MCP path scoping for agent edit loops.

Editors

  • LSP modernization with pull diagnostics. The server moved to the maintained tower-lsp-server + ls-types, unlocking LSP 3.17 pull diagnostics with workspace/diagnostic/refresh. Pull clients (Neovim, Zed, Helix) avoid duplicate push/pull namespaces while push-only clients keep working, and the docs now cover Neovim setup. Thanks @tris203 for the patch (#1033).
  • VS Code complexity breakdown is now progressive disclosure. A compact lens sits above each over-threshold function and the dense per-line breakdown is revealed per function (via Health-view selection, lens click, or hover) instead of all at once.
  • Quieter diagnostics, on your terms. Set fallow.diagnostics.severity to information or hint, commit a team mute baseline with fallow.diagnostics.mutedCategories, and toggle all squiggles from a one-click status-bar button. The Security Candidates view groups candidates by kind / CWE with per-group counts.
  • Sidebar and editor now agree on production mode. fallow.production is a tri-state auto / on / off forwarded to both the CLI-driven sidebar and the LSP server; the CLI gains a global --no-production flag to force it off.

CLI

  • --output-file / -o writes the rendered report to a file for any --format (ANSI-free even on a TTY), with progress kept on stderr.
  • Configurable cache location. Set cache.dir in config or FALLOW_CACHE_DIR to keep the analysis cache outside the default .fallow/ directory.

Bug fixes

  • pnpm workspace dependencies imported through symlinked workspace packages are now credited when analyzing a package directly. Thanks @alvis for the report (#1008).
  • Prose import("...") examples inside JSDoc comments no longer create false unresolved-import findings. Thanks @jsgoldman for the patch (#1010).
  • User-facing messages now name the canonical fallow dead-code command instead of the deprecated check alias.

Full Changelog: v2.89.0...v2.90.0