# Changelog All notable changes to ferret-scan will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [2.8.1] - 2026-06-26 ### Changed - **`AI-011` benign install phrasing now dampens instead of suppressing.** The filter for documentation phrasings like "add it to your `mcp.json`" previously dropped the finding entirely, which a hostile line phrased the same way could abuse to hide a real self-modification instruction. It now **downgrades the finding to INFO** (still visible in a full scan) rather than removing it, closing that evasion surface while keeping HIGH-severity noise off every MCP server's README. Implemented as a general rule capability (`dampenPatterns` / `dampenTo`) that any rule can use as a softer alternative to `excludePatterns`. ## [2.8.0] - 2026-06-26 ### Added - **Cursor `.cursor/rules/*.mdc` rule files are now scanned.** Cursor's modern rules format (markdown-with-frontmatter) is treated as AI-config markdown, so the existing credential, prompt-injection, and exfiltration rules apply to it. Previously the `.mdc` extension was unrecognized and these files were skipped entirely. Also honored in `--config-only` mode, where `.cursor/rules` is treated as a high-signal location. ### Changed - **Fewer documentation false positives.** Credential (`CRED-006`), exfiltration (`EXFIL-005`), and keychain (`CRED-007`) rules now bound the distance between a verb and its credential noun and stay within a sentence, so prose that mentions the words across unrelated clauses is no longer flagged. The keychain rule requires an access verb near "keychain" (e.g. "macOS Keychain integration" is no longer flagged), and the agent self-modification rule (`AI-011`) ignores benign install phrasing like "add it to your `mcp.json`". - **Zero-width detection is i18n-aware.** `OBF-003` now flags ZWNJ/ZWJ (`U+200C`/ `U+200D`) only when embedded between Latin alphanumerics — the smuggling case — instead of blanket-flagging them, since they are required characters in Arabic, Persian, and Indic scripts. A BOM (`U+FEFF`) is only flagged when it is not at the start of the file. - **Documentation dampening is generalized.** Severity downgrading in documentation paths now applies to a set of prose-prone rules (`CRED-001/006/007`, `EXFIL-005`, `AI-011`) rather than only `CRED-001`, downgrades any such finding above its mapped floor (never escalating), and only treats high-confidence, non-prose rules as corroborating signals that cancel the downgrade. ### Fixed - **Releases no longer show a false failure when re-triggered.** The publish workflow now skips `npm publish` for a version already on npm (for both `ferret-scan` and `ferret-lsp`), so a manual dispatch followed by the release tag push no longer fails the run on a duplicate-version error. ### Maintenance - Removed dead code in `bin/ferret.js` (an unused import and catch binding). - CI dependency bumps: `actions/checkout` v7, `actions/upload-artifact` v7, `actions/download-artifact` v8, and the dev-dependencies group. ## [2.7.3] - 2026-06-24 ### Fixed - **Ferret no longer scans its own working directories.** `.ferret-cache`, `.ferret-backups`, and `.ferret-intel` are now always excluded from scanning (alongside the existing `.ferret-quarantine`, `.git`, and `node_modules`). These directories hold detected secrets / malicious content by design — cached findings, backups of flagged files, and the threat-intel database — so re-scanning a repository that had already run Ferret previously surfaced false-positive findings on the scanner's own artifacts (e.g. a CRITICAL `CRED-003` inside `.ferret-cache/llm/*.json`). ### Changed - Deduplicated the documentation-dampening logic: `Scanner` now imports the shared `features/docDampening` module instead of carrying a byte-for-byte copy, so the two implementations can no longer drift. Behavior is unchanged. - The published npm package no longer ships compiled test files. Test sources are excluded from the build via a dedicated `tsconfig.build.json`, shrinking the tarball from 369 to 175 files (~347 kB → ~221 kB). ## [2.7.1] - 2026-06-23 ### Security - **Zero-width / bidi evasion normalization.** Pattern rules are now also run on a copy of each file with zero-width, BOM, soft-hyphen, bidi override/embedding, and modern bidi-isolate characters (incl. U+2066-U+2069) stripped. An attacker can no longer split a literal keyword (e.g. `ignore previous instructions`) to slip injection/jailbreak strings past the scanner while the LLM still acts on them. Raw-content matching is preserved, so zero-width obfuscation rules (OBF) still fire. - **Inline ignore directives no longer honored in untrusted content by default.** A malicious marketplace plugin or plugin-cache entry can no longer suppress detection of its OWN findings with `` / `ferret-ignore`. Suppression is skipped for clearly-untrusted paths (`.claude/plugins/marketplaces/`, `plugins/cache/`, `.claude/plugins/`) unless the new `honorIgnoreInUntrusted` option is set. User-owned config paths keep normal suppression behavior. ### Fixed - **Reporters no longer leak the scanned project's version.** SARIF/SBOM/HTML reports previously derived the tool version from the scanned project's `package.json` via `process.cwd()`, so scanning an external repo reported THAT project's version. The version is now baked in at build time as a constant (`src/generated/version.ts`, generated from `package.json` by `scripts/sync-version.mjs`), guaranteeing reports always emit Ferret's real version. - **Deterministic finding and error ordering under concurrency.** `sortFindings` is now a total order (severity → risk score → path → line → rule id), and per-file findings and errors are aggregated in discovery order, so scan output is byte-identical regardless of how many files are scanned in parallel. ### Added - **Bounded-concurrency scanning (`--concurrency `).** Files are processed by an in-process worker pool (no new dependency); default is `max(1, cpuCount - 2)`. Per-file error isolation is preserved, and LLM-bearing scans are pinned to a single worker to keep the shared LLM runtime counter race-free. - `version:sync` / `version:check` npm scripts; `version:sync` is run as part of `build` and `prepublishOnly`. ## [2.7.0] - 2026-05-29 ### Fixed - **`ferret rules install` now actually installs.** It previously only printed the equivalent `rules fetch` command without writing anything; it now fetches, validates, and writes the rules to `.ferret/rules.yml` (shared logic with `rules fetch`, honoring `--force`). ### Security - **RE2 regex engine now actually loads.** `safeRegex.ts` used a bare `require('re2')`, which always threw in the published ESM build (`require` is undefined under `"type": "module"`), silently disabling the linear-time engine in every release. Bridged via `createRequire(import.meta.url)` (isolated in `src/utils/esmRequire.ts`). `isRE2Active()` now returns `true` in the CLI. - **Hardened the fallback ReDoS screener** to reject the family of catastrophic patterns that previously slipped through (e.g. `(\d+)*$`, `(\w+)*`, `(.*a){20}`), used when the native `re2` addon is unavailable. - **SSRF protection for remote custom-rule fetching** (`--allow-remote-rules`): URLs that resolve to loopback/private/link-local/metadata addresses are rejected, and redirects are followed manually and re-validated per hop. - **Secrets redacted in webhook payloads:** the generic webhook formatter no longer egresses raw matched secret values. - **CSV formula-injection neutralization** in the CSV reporter (cells starting with `= + - @` tab/CR are quoted as text). ### Documentation & Maintenance - Added `docs/REPOSITORY_ANALYSIS.md` (architecture, landscape positioning, security scan, documentation review). - Removed patent-prosecution material (`docs/PATENT_ACTION_PLAN.md`, `docs/PATENT_LANDSCAPE_ANALYSIS.md`, `docs/ip-submissions/`) from the public repo. - README: added an exit-code table and the full set of `FERRET_EXIT_*` overrides, documented `.ferretignore`, added the `check`/`mcp`/`deps`/`capabilities`/`policy`/ `webhook` subcommands, fixed a broken `docs/RULES.md` link, pointed the security contact at `SECURITY.md`, and listed `sbom`/`aibom` as `--format` values. - Removed a dead, shadowed duplicate `self-scan` CI job; removed `.github/workflows/publish.yml.bak`; fixed a duplicate `## [2.6.0]` CHANGELOG header and the `typedoc.json` GitHub org link. ## [2.6.1] - 2026-05-20 ### Changed - Raised global test coverage thresholds to true 80%+ (lines/statements/functions) with pragmatic 70% branch floor. All existing per-file overrides retained. - Extracted large data tables and types from `capabilityMapping.ts` into `src/features/capabilities/{data.ts,types.ts}` (main file reduced from 639 → 493 LOC). - Added `npm run quality` (powered by `scripts/quality-check.mjs`) that enforces file size, function length heuristics, lint, typecheck, coverage, self-scan dogfooding, and production audit in one command. - Created `docs/QUALITY_GATES.md` documenting standards, limits, and how to run the gate. - Fixed the last outstanding TODO (LLM prompt placeholder type alignment). - Added `audit:prod`, `quality`, `quality:fix`, `quality:json` scripts. ### Improved - Self-scan, coverage, and size hygiene now have automated enforcement beyond CI. - The project itself now better exemplifies the security standards it scans for. ### Quality & Maintenance - Significant dead import cleanup in `bin/ferret.js` - Extracted HTML formatting helpers and CSS generation from `HtmlReporter.ts` into `src/reporters/html/` - Expanded `SIZE_EXCEPTIONS` in the quality gate script for the next tier of large modules ## [2.6.0] - 2026-05-16 ### Added — Major New Features - **SBOM + AIBOM Generation** (`SbomReporter.ts`) - `ferret scan --sbom` / `--format sbom|aibom` - Full CycloneDX 1.5 compliant output + AI-specific AIBOM extension (`aiSurface`, MCP trust, risk posture, injection/credential/exfil surface) - **Community Rule Sharing** - `github:owner/repo@ref/path` and `gitlab:` shorthand support via `resolveRuleSource()` - `ferret rules validate|fetch|install` - **Hard shadowing protection**: Custom rules cannot override built-in rule IDs (INJ-*, CRED-*, etc.) - **Language Server Protocol** (`lsp/` standalone package) - `ferret lsp` launches a full LSP server (diagnostics, hover on rule IDs, completion for categories/severities/rule IDs) - Works with Neovim, Emacs, Zed, Helix, Sublime, and VS Code (opt-in) - `ferret check` now supports `--format json` for machine consumption - **Lightweight Runtime Prompt Monitor** - `ferret monitor --stdio` and `--target ` - Real-time detection of injection, credentials, and exfiltration in prompts sent to LLM CLIs - Alerting-only by default (`--block` for enforcement). Emits structured JSON alerts to stderr. Extremely lightweight (reuses `PatternMatcher`). ### Changed - `ferret check` now accepts `--format json|console` - `loadCustomRulesSource` transparently resolves community shorthands before fetching ### Tests & Quality - New dedicated test files: `SbomReporter.test.ts`, `runtimeMonitor.test.ts`, expanded `customRules.extra.test.ts` - All new modules covered by real (non-mock) tests - `ferret scan --self --ci` continues to pass with expected evil-fixture detections ## [2.5.0] - 2026-05-16 ### Added - **`ferret scan --self`**: New dogfooding command that scans Ferret’s own source + the malicious fixtures in `test/fixtures/`. Includes a dedicated `self-scan` CI job that runs on every push/PR. - **Real integrated test expansion**: Hundreds of new real e2e tests (no mocks) covering complex FileDiscovery structures, docDampening scenarios, baseline error paths, analyzer error injection, and WatchMode real FS behavior. ### Changed - **Scanner architecture**: Extracted pure reporting utilities into `src/scanner/reporting.ts` and documentation dampening into `src/features/docDampening.ts`. - **LLM Analysis split**: Broke the monolithic 800+ LOC `llmAnalysis.ts` into a clean, maintainable `src/features/llm/` module (types, providers, prompts, cache, parser). - **VS Code extension**: Added settings for `--thorough`, `--llm-analysis`, `--mitre-atlas`, and `--semantic-analysis` for better CLI parity. - **Coverage thresholds**: Raised global and per-file targets in `jest.config.js` (now targeting 60%+ global, 80%+ on core modules). ### Tests - **~1935 tests** across 108 suites; **~87% statements / 88% lines / 89% functions** coverage (major jump from previous ~55%). ### Documentation - Added high-quality Mermaid diagrams (component overview, self-scan loop, data flow) to `docs/architecture.md`. - Refreshed `docs/TEST_RESULTS.md` with current test locations and coverage reality. See the full project review and implementation plan for details. ## [2.4.0] - 2026-04-27 ### Changed - **Dropped Node 18 support**: `engines.node` bumped from `>=18.0.0` to `>=20.0.0`. Node 18 reached end-of-life April 2025 and the bundled `re2` native module no longer builds on it. CI matrix updated to `['20', '22']`; ancillary workflows bumped from Node 18 → 20. ### Fixed - **`bench.mjs` config drift**: imports the canonical `DEFAULT_CONFIG` from `dist/types.js` instead of maintaining a hand-rolled copy that lost sync with `Scanner.ts` (Scanner crashed in benchmark with `Cannot read properties of undefined (reading 'enabled')` when `mitreAtlasCatalog` was missing) - **`getAIConfigPaths` test**: dropped `paths.length > 0` assertion — the function gates every entry through `existsSync()` against `$HOME` and CWD, so empty array is correct on a clean CI runner - **`cli.test.ts` FERRET_E2E gate**: replaced fragile gate that left fixtures undefined when the env var was unset. Now uses `const d = runCli ? describe : describe.skip` so all 120 CLI tests cleanly skip when FERRET_E2E is not set, instead of crashing with `TypeError` - **`publish.yml` ordering**: `Build` step now runs before `Run tests` (CLI integration tests spawn `bin/ferret.js` which imports from `dist/`); FERRET_E2E set on the test step - **`prepublishOnly`**: dropped redundant `npm run test` (the workflow's Test job already validates; running tests again during `npm publish` was wasteful) ### Tests - **1921 tests** across 107 suites; passing on Node 20 and Node 22 ### Code Quality - **Lint debt cleared**: 333 lint errors → 0. Added test-file override to `eslint.config.js` relaxing `no-unsafe-*` rules for tests (where they fire on `require()`, mocks, and `any`-typed fixtures with little benefit). Source code keeps full strict-type-checked + stylistic-type-checked. One real source fix in `Scanner.ts:373` (type-narrow `unknown` to `string` before use). CI lint enforcement re-enabled. ## [2.3.0] - 2026-04-26 ### Added - **`ferret mcp audit`**: scores MCP servers in `.mcp.json` configurations on security posture. Returns trust score (0–100), trust level (HIGH/MEDIUM/LOW/CRITICAL), and the specific flags that reduced the score (insecure transport, unpinned `npx` packages, dangerous args, suspicious names). Exits non-zero when CRITICAL trust servers are found (configurable via `--fail-on`). - **MCP trust summary in all reporters**: Console, HTML, SARIF, and CSV reporters surface MCP trust state. ### Security - **`redact: true` by default**: `DEFAULT_CONFIG.redact` flipped from `false` to `true` — secrets found during a scan are now redacted in all output formats (console, CI logs, SARIF, HTML, CSV) without requiring any opt-in. - **ReDoS prevention enforced in custom rules**: `customRules.ts` now compiles all user-supplied patterns via `compileSafePattern` (previously used raw `new RegExp()`), closing the path by which a malicious `.ferret/rules.yml` could hang a CI build; validation step likewise uses `compileSafePattern` to reject unsafe patterns before load. - **RE2 regex engine**: replaces native JS regex for AST/correlation analysis to eliminate ReDoS class entirely. - **Quarantine path traversal CVE**: hardened path validation in remediation Quarantine. - **Dependency vulnerabilities patched**: `npm audit fix` resolves all 7 vulnerabilities (1 critical Handlebars JS injection, 3 high ReDoS in minimatch/picomatch/flatted, 3 moderate). ### Changed - **Windows platform support**: Removed `"os": ["!win32"]` exclusion. Platform guards already in place in `Quarantine.ts` and `gitHooks.ts`. - **Coverage**: Global ~90% line coverage, ~78% branch coverage; per-module thresholds for `Scanner`, `WatchMode`, `capabilityMapping`, all four reporters. ### Fixed - **`categories: []` no longer disables all rules**: a critical bug where passing an empty array silently produced zero rules (now correctly falls back to default category set). - **SECURITY.md accuracy**: Supported version table now shows `2.x` current, `1.x` end-of-life. ### Tests - **1733 tests** at v2.3.0 release (grew to 1921 in v2.4.0). ### CI/CD - CodeQL, Dependabot, SARIF upload, and npm provenance all wired into release workflow. ## [2.2.0] - 2026-04-23 ### Security - **Bounded content cache**: Replaced unbounded `Map` with `BoundedContentCache` (256 MB aggregate cap, 10,000 entry limit, 1 MB per-file cap with LRU eviction) to prevent OOM on large repos - **Quarantine hardening**: Quarantine directory created with mode `0700` (owner-only) on POSIX; permissions verified after creation with a warning if loose; disk-space pre-checked via `statfsSync` before any quarantine operation - **BUILTIN_FIXES startup validation**: All 9 built-in remediation patterns validated by `compileSafePattern` at module load time — a bad pattern fails fast at startup rather than at first use - **Hybrid AST deadline**: `analyzeFile` now enforces both a per-code-block cap (default 500 ms, `maxBlockMs`) and a file-scoped total cap (default 2 s, `maxMs`). A single hostile markdown block can no longer starve all subsequent blocks of their analysis budget - **ReDoS prevention hardened**: `compileSafePattern` updated to screen alternation-inside-quantified-groups patterns; `globToRegex` escapes all regex metacharacters and anchors patterns; all correlation and AST pattern execution runs through `runBounded` - **`statfsSync` bigint safety**: Explicit `Number()` coercion in `hasSufficientDiskSpace` guards against future `{ bigint: true }` call-sites - **`ignoreComments` regex fix**: Alternation order corrected (longest-first: `ignore-next-line`, `ignore-line`, `ignore`) so `ferret-ignore-next-line` is no longer mis-parsed as `ferret-ignore` ### Added - **JSON schema sync**: `src/schemas/ferret-config.schema.json` now generated from the runtime zod schema via `npm run schema:generate`; CI enforces drift detection with `npm run schema:check` - **Coverage thresholds**: Per-module Jest coverage thresholds for `safeRegex`, `glob`, `contentCache`, `Fixer`, `Quarantine`, `AstAnalyzer`, all four reporters, `WatchMode`, and `policyEnforcement` — silent regressions now fail CI - **CI benchmark regression detection**: `scripts/bench-compare.mjs` compares benchmark results against the cached main-branch baseline and fails PRs that regress by >20% ### Tests - **673 tests** across 39 test suites (was 244 tests) - New unit tests: `AstAnalyzer`, `ConsoleReporter`, `HtmlReporter`, `SarifReporter`, `WatchMode`, `contentCache`, `safeRegex`, `glob`, `Fixer`, `Quarantine`, `ignoreComments`, `mcpValidator`, `policyEnforcement`, `cliOptions` - New integration tests: `remediation` (scan→fix→rescan, quarantine→restore, dry-run, backup round-trip) and `cli` (subprocess exit-code contract for `--version`, `--help`, scan, SARIF output) - HtmlReporter XSS escape verified: `