Repository navigation
v3.39.0: fewer false positives, Effect schema option, banned-import allowNames
Features
-
banned-importrules acceptallowNames. A rule can ban a package and keep some of its exports, for example{ "kind": "banned-import", "specifiers": ["three"], "allowNames": ["Vector3", "Quaternion"] }. Each entry is an exact imported name, anddefaultallows a default import. A namespace import, a star re-export and a side-effect import always report. A finding of such a rule hasmatchedset to<specifier>: <binding>, for examplethree: Mesh. AddingallowNameschanges the baseline keys and finding ids of that rule, so re-save the baseline in the same commit. Thanks @Smrtnyk for the report. (#4072) -
An explicit flag now says when its rule is off in config. Before,
fallow dead-code --unused-depswithunused-dependenciesatoffprintedNo issues found. Now fallow prints a note on stderr that the flag did not check the rule. JSON gets the newworkspace_diagnosticskindrequested-rule-off, withruleandflag. SARIF, Markdown, MCP, the GitHub Action and the GitLab template also show the note. The exit code does not change, so set the rule to "error" to fail on a finding. Thanks @Smrtnyk for the report. (#4067) -
The MCP tool
inspect_targetreturns thecloudsection and the compact text. Setcloud: trueon a file target to run the same path asfallow inspect --file <path> --cloud. The CLI reads the key fromFALLOW_API_KEYin the server environment, then the keychain, then the credentials file. The key is never a tool parameter. A status other thanokdoes not fail the call. Setformat: "compact"to get the plain text offallow inspect --format compactinstead of the JSON bundle. (#4001) -
fallow securityreports more sinks. These shapes are now candidates when the value is not a literal:- iframe
srcdocasdangerous-html, through assignment,setAttribute, JSXsrcDoc, Vue:srcdoc, Sveltesrcdocand Angular[srcdoc]; - an assignment to
src,text,textContentorinnerTexton a local bound todocument.createElement("script")ascode-injection, with the calleeHTMLScriptElement.<property>. The same property on another element stays quiet; - a non-literal argument to
Range.createContextualFragment()asdangerous-html.
- iframe
-
The review brief reports security sink substitution. A change that removes a security sink and adds a different sink of the same class in the same file gives a
security-check-removedsignal. The signal appears inweakening[]offallow audit --briefandfallow review. The classes are code execution, such asnew Functionandvm.runInNewContext, and HTML, such asinnerHTMLanddangerouslySetInnerHTML. A sink that only moves in the file, or goes away, gives no signal. Test files do not take part. -
A
graphql-configplugin keeps GraphQL schema and operation files used. The plugin reads theschemaanddocumentsglobs ofgraphql.config.*,.graphqlrc,.graphqlrc.*or thegraphqlkey inpackage.json. It marks the matching.graphql,.gqland.graphqlsfiles as used, and the config file too. A glob is relative to the directory of the config file. A URL, a negated glob, and a glob for source files get no credit. -
fallow auditshows the reason of an addedfallow-ignorecomment. Anadded-suppressionfinding now has asuppressionobject withkinds,reasonandsecurity_without_reason. Write the reason after--, for example// fallow-ignore-next-line security-sink -- the HTML is a constant template. When the comment hidessecurity-sinkorsecurity-client-server-leakwithout a reason,security_without_reasonistrue. The signal does not change the severity, the verdict or the exit code. To find existing security suppressions without a reason, runfallow suppressions --format jsonand selectreason_present: false. -
effect.schemaExportsAsTypesreports unused Effect schemas as unused types. With{ "effect": { "schemaExportsAsTypes": true } }, fallow reports an unused schema export underunused-types, notunused-exports. A schema is a class that extends aSchemaclass factory, or a subclass of such a class. A const whose value is aSchemaconstructor, member or pipe chain is also a schema.Schemamust be an import fromeffect,effect/Schemaor@effect/schema.fallow fixdoes not change these exports. The option is off by default. Older versions reject theeffectkey, so pair it withminimumVersion.
Changed
-
Runtime coverage totals are null for a partial capture. A Fallow Cloud partial capture (
capturePolicy: positive_only) records only the functions that ran. Nowsummary.functions_tracked,summary.functions_unhitandsummary.coverage_percentare null for such a capture.summary.functions_untrackednow also counts the functions without a call. The newsummary.capture_policyfield ispositive_only. The human output no longer prints an unhit count for a partial capture. A full capture gives the same numbers as before. See the upgrade notes for the schema versions. -
The secret-leak
client-server-leakfinding now hascategory: "client-server-leak". Every security finding now names a category, so you can group candidates oncategoryalone. The rule id, thefinding_idand the SARIF fingerprint do not change. -
Production mode leaves out user support entries. In a production run, a file that only a user
framework[]entry with an explicit"entryPointRole": "support"matches is now like a test file. It is not an entry point, and its imports credit nothing. An export, a file or a dependency that only a dev tool script uses is now reported. A file that a runtime root also seeds stays a runtime root. (#4068)
Bug fixes
-
fallow dead-codeno longer reportsgenerateStaticParamsin a Next.js metadata route as an unused export. Fallow now credits it on every metadata route, such as anopengraph-image,twitter-image,icon,apple-iconorsitemaproute under a dynamic segment. Fallow also credits the route segment config exports, such asdynamicandrevalidate.dynamicParamsand unrelated helper exports stay reportable. Thanks @Vladimir-Pirotskiy for the patch. (#4038) -
fallow mutationkeeps therootof the selected Vitest config. Before, the companion replaced the configrootwith the project root, so a config with its ownrootcould list no test files. The run then stopped withtest-files-not-discovered. Now the companion starts Vitest from the project root without the override. A config withoutrootbehaves as before. (#4039) -
fallow mutationkeeps the test error of a test that fails in a dry run. Before,baseline_failures[].messagehad only a fixed reason or no message, so the assertion and the stack were lost. Now the Vitest, Node test and Bun test companions sendfailure_kindandmessagefor each failed dry-run test. The human footer shows the reason on theMessage:line and the error on theError:line. The run stayspartial. (#4030) -
fallow mutationfinds the runtime onPATHand skips files that cannot run. A name without a path separator, such asnodein--runtime node, is now aPATHlookup, as in a shell. A path with a separator, such as./bin/node, stays relative to the working directory. On Unix, a file without an execute bit early onPATHno longer hides a realnodeorbunlater onPATH. -
fallow dead-codecredits alias replacement files in more config shapes. Fallow reported some stub files as unused files, or, withincludeEntryExports, reported their exports as unused. Fallow now credits the replacement file in these cases:- a tsconfig
pathsorbaseUrlmaps the alias key to another existing file; - a top-level
constorletholds the replacement aspath.resolve(...),path.join(...),fileURLToPath(...)ornew URL(...).pathname.
This applies to Vite, Vitest and webpack aliases. Vitest
projectsentries andvitest.workspacearrays also read these constants. Thanks @Smrtnyk for the report of the tsconfig case. (#4035) - a tsconfig
-
fallow dead-codeno longer reports the default export of a local OxlintjsPluginsdirectory whosemainpoints at build output. Fallow now maps a built file, such as./dist/src/index.js, back to its source file, such assrc/index.ts. TherootDirandoutDiroptions oftsconfig.jsonapply. Other exports of the source file stay reportable. Thanks @Smrtnyk for the report. (#4034) -
fallow dead-codecredits a member access through a local that holds the result of a method called onthis. Code such asconst old = this.current(); old?.deactivate?.();now creditsTool.deactivate. The fix also coversthis.current?.(), a#-private method, and a method after the call site. A cast, a call with arguments, and a call in a plainfunctiongive no credit. Thanks @Smrtnyk for the report. (#4033) -
fallow dead-codereports a module that wraps a same-named import as a duplicate export again. A module that importsencodeunder an alias and declares its ownencodeis a wrapper. Since 3.35.0 theduplicate-exportsrule dropped such a module. An overlay module that also declaresexport * fromthe same module stays dropped. To keep a wrapper on purpose, add// fallow-ignore-next-line duplicate-exportabove its export, or add the name toignoreExports. (#4024) -
absent-component-propsreports in two more cases. A method signature such asonClose(): voidin a props type now counts as a prop, so the component no longer abstains. An overloaded method is optional only when every overload is optional. A DOM query,innerHTMLor an unsafe Lit directive in the calling module now makes only Lit elements and imported Svelte components abstain. (#4022, #4023) -
Fallow Cloud keychain calls have time limits, and two logins no longer lose a key. The keychain read of
fallow inspect --cloudnow has a time limit of 1 second. At the limit, fallow reads the credentials file and reportsno_keywhen no key is stored. Infallow cloud loginandfallow cloud logout, each keychain call has a limit of 60 seconds. Fallow now holds a lock oncredentials.lockwhile it changes the credentials file. Two logins at the same time for two API URLs then keep both keys. When the credentials file does not parse, the command stops with a message that names the file, and fallow does not change the file. (#4003, #4004) -
Large or deep inputs no longer stall fallow or exhaust memory.
- Fallow now expands at most 64 directory arguments of
node --test,bun testordeno testfor each command in a package script. A chain of command wrappers stops after 8 wrappers. - Fallow now rejects a CODEOWNERS pattern or a config glob that is longer than 512 bytes, with an error that names the limit.
- Fallow now treats a collection value for a pnpm override in
pnpm-workspace.yamlas a missing value. Fallow reports it as misconfigured, as for npmoverridesand bunresolutions.
- Fallow now expands at most 64 directory arguments of
-
fallow type-aware statusnames the tsrs backend in its human output. WithFALLOW_TYPE_AWARE_BACKEND=tsrs, the line now saystsrs <version>, notTypeScript <version>. The line for the default backend and the JSON output do not change. Thanks @Smrtnyk for the report. (#4066) -
fallow dead-codecredits a package or file that code loads through an inlinecreateRequire(import.meta.url)('pkg')call. Fallow now reads every spelling of arequirefunction based on the file itself. Examples are.resolveon the call, a binding with any name, and an aliasedcreateRequireimport. These forms now give the same findings as the namedrequireform. A call with another base, such ascreateRequire(process.cwd()), credits nothing. Thanks @Smrtnyk for the report. (#4062) -
fallow baselines pruneno longer removes valid entries of a baseline saved in another mode. Prune now stops on such a file withreason_code: "scope-mismatch", exits 2, and prints the command to use.baselines prunenow accepts--productionand honors--include-entry-exports. Prune also stops on a file with no record of a mode that the run uses. To clear that refusal, review the findings and save the file again in that mode. (#4070) -
--fail-on-stale-baselinenow judges a dead-code baseline only when it is saved in the same mode as the run. A same-mode run with a stale entry can now exit 1 where it exited 0. To clear it, run the prune command that the failure prints and commit the result. A run in another mode now exits 0, and the gate status isskipped.change_scopedandscope_reasonsnow name what stops the run from judging the file. Thanks @Smrtnyk for the report. (#4070) -
An
overridesrule that cannot change a result now reports a diagnostic. The newoverride-rule-ineffectiveentry inworkspace_diagnostics[]hasrule,filesandreason.reason: project-levelcovers project-level rules, such asdev-dependencies-in-production.reason: base-rule-offcovers an override for a rule that is off at top level. Findings and exit codes do not change. The old stderr warnings forduplicate-exportsandre-export-cyclemoved to this entry. Thanks @Smrtnyk for the report. (#4065) -
The suppression footer no longer prints
0 stale suppressions, and the status line names every finding type. The suppression footer now names a stale count only when the count is above zero. Thanks @Smrtnyk for the report. (#4063) The status line and the--group-byheaders now name every finding type that the run counts, for example⚠ 1 unused catalog entry (0.02s). Before, some runs printed an empty label. JSON output does not change. -
banned-callrules andboundaries.calls.forbiddennow matchnewexpressions. ADatepattern now reportsnew Date()too, withmatchedorcalleeset tonew Date. Findings for the call form keep their id, fingerprint and baseline key. A saved baseline does not cover the new findings. To preview them, runfallow rule-pack test. To accept them, set the rule severity towarnor save the baseline again.banned-effectdoes not change. Thanks @Smrtnyk for the report. (#4071) -
Config load now warns about a plugin that can never activate. A
framework[]entry or a plugin file with neitherdetectionnorenablersnow gives a warning on stderr. The warning tells you to add one of them, then runfallow plugin-check. Fallow prints the warning also with--quiet,--ciorFALLOW_QUIET. Findings, stdout and exit codes do not change. Thanks @Smrtnyk for the report. (#4068) -
fallow dead-code --helpnow names every kind that--unused-depsselects. The help now lists unused, dev and optional dependencies, plus type-only, test-only and dev-in-production dependencies. Findings, JSON output and exit codes do not change. Thanks @Smrtnyk for the report. (#4064) -
Findings in
.vue,.svelte,.astroand.mdxfiles now report the line of the call. Before,banned-call,banned-effect,boundaries.calls.forbidden,unprovided-injectandmisplaced-directivefindings had a wrong line and column. Afallow-ignore-next-linecomment above the call now applies. In these files, taint trace sources now have the correct line, and anext/dynamicimport withssr: falsecounts as client-only forclient-server-leak. Policy baseline keys do not change, but line-based CodeClimate fingerprints can change. -
ignore-dependencies-entry-redundantno longer tells you to remove an entry that a--productionrun needs. A normal run no longer reports anignoreDependenciesentry as redundant when the entry hides atype-only-dependenciesorunused-dependenciesfinding of a--productionrun. Findings and exit codes do not change. -
fallow securityno longer misses sinks behind evasion syntax, on complex objects, or from namedvmimports. Examples of new candidates:(0, eval)(code),globalThis.eval(code),const e = eval; e(code),Function(code)andsetTimeout("run(" + id + ")");el["inner" + "HTML"] = html, and a JSX spread that includesdangerouslySetInnerHTML;innerHTMLonthis.node,el!,(el as T), an optional chain or a call result;runInNewContext,runInThisContextandrunInContextfrom a named import ofvmornode:vm, or fromrequire("vm");new Script(code)andcompileFunctionfrom a named import ofvmor fromrequire("vm");new Function(...names, body), andel.setAttribute("src", url)on a<script>element.
fallow security blind-spotsnow lists an assignment whose property name joins a string with a dynamic part, such asel[prefix + "HTML"] = html. -
fallow securityreports fewer source-free candidates in library, build and CLI code. On eight public projects, mostpath-traversal,dynamic-regexandheader-injectioncandidates had no untrusted input near them.RegExp(x),res.setHeaderandres.writeHeadnow need a value that traces to untrusted input.path.joinandpath.resolvenow need untrusted input in the argument, the same module, or every direct importer.req.url,req.originalUrlandreq.headersare now untrusted input.process.env,import.meta.env, test files, story files and tooling-config files no longer start an untrusted-source trace. -
fallow dead-codecredits moreoverridemembers of a class with a package base call. A class such asclass LookupError extends Schema.TaggedError<LookupError>()("LookupError", { ...sharedFields })no longer reportsoverride get message()as unused. The same applies when the call takes a module-scopeconstwith an object literal initializer. A mixin call that takes a project class still gives no credit. -
A
/// <reference types="pkg" />directive now uses the packagepkg. Fallow records the directive as a type-only import, so the rules forimport typeapply. In production mode, a package independenciesthat only such directives use is reported as a type-only dependency. Fallow ignores a directive after code, atypesvalue that starts with.or/, andtypes="node". -
The pnpm plugin activates from more files. The plugin now activates from
pnpm-workspace.yaml,pnpm-lock.yaml, a pnpmfile, and apackageManagerfield that names pnpm.fallow dead-codeno longer reports.pnpmfile.cjsor.pnpmfile.mjsas unused. The plugin also marks the files that thepnpmfilesetting names as used. -
A React Native platform file that imports from its base file no longer reports a re-export self-loop. When a platform extension makes a specifier resolve to the importing file, fallow resolves it again without platform extensions.
export type { ButtonProps } from "./Button"inButton.ios.tsxthen resolves toButton.tsx. A specifier that names the platform, such as./Button.ios, keeps its target. -
fallow dead-codecredits a class member that a runtime check reads by name. Code such as"displayDetail" in causeorReflect.has(cause, "displayDetail")now credits the member. The credit applies to classes in the same module and its direct import neighbors. A dynamic key,Object.hasOwn,hasOwnPropertyand a check in an unreachable file give no credit. -
fallow migratetranslates more settings. The migration now translates settings in aworkspacesmap, with the workspace path in front of each pattern. It removes the trailing!production marker from entry patterns, so the entry is no longer lost. It warns about a section that mixes marked and unmarked patterns, and about a negated entry pattern. It now also translates the rootincludeEntryExportsandignoreIssuessettings. -
The VS Code extension uses the project
fallow-type-awarecompanion. When the extension runs Fallow from the projectnode_modules, it uses thefallow-type-awarepackage of the same install if the two versions are equal. This works with the pnpm store layout too. AFALLOW_TYPE_AWARE_BINthat you set still wins. (#4069)
Security
fallow-similar-codeno longer shipszip8.6.0. The sidecar now usescandle-core,candle-nnandcandle-transformers0.11.1, and the lockfile selectszip9.3.0. This removes RUSTSEC-2026-0429 and the allowlist entries for RUSTSEC-2026-0367, RUSTSEC-2026-0368 and RUSTSEC-2026-0369. The sidecar never opened a ZIP archive. It also moves fromtokenizers0.22.2 to 0.23.2. The embedding semantics stay at v2, so the vector cache stays valid.
Upgrade notes
-
Partial-capture coverage totals are nullable, so some schema versions change.
fallow healthmoves to 13,fallow auditto 14, the bare combined output to 15,fallow audit --briefto 14, andfallow coverage analyzeto"3". Update scripts that check these versions or readsummary.coverage_percentas a number. -
Adding
allowNamesto abanned-importrule changes its baseline keys and finding ids. Re-save the baseline in the same commit.--fail-on-baseline-growthreports the new keys. (#4072) -
banned-callandboundaries.calls.forbiddencan report newnewexpression findings. A rule with severityerrorcan now fail the gate. Runfallow rule-pack testto preview them, then set the rule towarnor save the baseline again. (#4071) -
duplicate-exportsreports wrapper modules again. This rule iserrorby default, so CI can show new findings. Add afallow-ignore-next-line duplicate-exportcomment or anignoreExportsentry for an intended wrapper. (#4024) -
fallow baselines prunecan exit 2 withreason_code: "scope-mismatch".prune --checkcan exit 2 when the config sets production mode and the baseline file has no record of that mode. Review the findings and save the baseline again in that mode. (#4070) -
--fail-on-stale-baselinecan change its exit code. A same-mode run with a stale entry can exit 1, and a run in another mode now exits 0 with the gateskipped. Run the prune command that the failure prints and commit the result. (#4070) -
New
workspace_diagnosticskinds appear in JSON.requested-rule-offhasruleandflag, andoverride-rule-ineffectivehasrule,filesandreason. The stderr warnings forduplicate-exportsandre-export-cyclemoved tooverride-rule-ineffective. Update scripts that parse these entries or the stderr text. (#4067, #4065) -
Security findings change. Every security finding now has a
category, andclient-server-leakusescategory: "client-server-leak".fallow securityreports new sink candidates and drops many source-free candidates. Expect newcode-injectionanddangerous-htmlcandidates, and fewerpath-traversal,dynamic-regexandheader-injectioncandidates. Code that logs or copiesreq.url,req.originalUrlorreq.headerscan show a newsecret-pii-logormass-assignmentcandidate. -
Line-based CodeClimate fingerprints can change for findings in
.vue,.svelte,.astroand.mdxfiles. Policy baseline keys do not change. -
A CODEOWNERS pattern or a config glob longer than 512 bytes is now an error. Fallow rejects such a pattern with an error that names the limit.
-
The first run after the upgrade parses every file again. The extraction cache version changes, and the graph cache rebuilds. No action is needed.
-
To use
effect.schemaExportsAsTypes, pair it withminimumVersion. Older versions reject theeffectkey. -
A production run can report new dead code. A project that declares tool scripts as user entries with
"entryPointRole": "support"can see newunused-export,unused-fileandunused-dependencyfindings with--production. The productionhealthvital signs that come from dead code follow the new result. (#4068)
Full Changelog: v3.38.1...v3.39.0