Add optional LLM tokenizer-based false positive filtering with per-detector opt-out support#4935
Add optional LLM tokenizer-based false positive filtering with per-detector opt-out support#4935MuneebUllahKhan222 wants to merge 2 commits intotrufflesecurity:mainfrom
Conversation
| } else { | ||
| ctx.Logger().Info("Filtered out result with low token-to-character ratio", "detector", res.DetectorType.String(), "ratio", ratio, "result", string(res.Raw)) | ||
| return false | ||
| } |
There was a problem hiding this comment.
Inverted boolean logic filters valid secrets, keeps false positives
High Severity
IsTokenizerFalsePositive returns true when ratio > 0.39 and false when ratio ≤ 0.39, but according to the PR description, ratio > 0.39 means the result is valid (should be kept) and ratio ≤ 0.39 means it's a false positive (should be filtered). In notifierWorker, IsTokenizerFalsePositive == true causes the result to be skipped. This means valid secrets get discarded and actual false positives are retained — the exact opposite of the intended behavior.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit af6a7ba. Configure here.
| maxDepth = 5 * 2 | ||
| maxSize = 2 << 30 // 2 GB | ||
| maxTimeout = time.Duration(60) * time.Second | ||
| maxTimeout = time.Duration(100) * time.Second |
There was a problem hiding this comment.
Unrelated archive timeout increase shipped with PR
Low Severity
maxTimeout was changed from 60 to 100 seconds for archive processing. This is unrelated to the tokenizer-based filtering feature described in the PR and changes behavior for all users — archive extraction now waits 67% longer before timing out. This looks like it was accidentally included in the commit.
Reviewed by Cursor Bugbot for commit af6a7ba. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
Reviewed by Cursor Bugbot for commit 54a5243. Configure here.
| if ok { | ||
| return func(ctx context.Context, res Result) bool { | ||
| return checker.IsTokenizerFpDisabled() | ||
| } |
There was a problem hiding this comment.
Opt-out detectors have all results incorrectly filtered
High Severity
GetTokenizerFalsePositiveCheck returns a function that directly returns checker.IsTokenizerFpDisabled() for detectors implementing the opt-out interface. Since Postgres and MongoDB return true from IsTokenizerFpDisabled(), this marks every unverified result from these detectors as a tokenizer false positive, causing all their results to be filtered out — the exact opposite of opting out. The returned function for opted-out detectors needs to return false (not a false positive) to prevent filtering.
Reviewed by Cursor Bugbot for commit 54a5243. Configure here.
| } | ||
|
|
||
| func getTokens(text, encoding string) []int { | ||
| tke, err := tiktoken.GetEncoding(encoding) |
There was a problem hiding this comment.
Probably wanna grab it offline: https://github.com/betterleaks/betterleaks/blob/c65402b457ee3d5eca13d050805a8e77da285175/detect/detect.go#L174-L176
gzip it for minimizing binary size (saves a couple MBs) https://github.com/betterleaks/betterleaks/tree/main/detect/assets
Cool idea ;)
There was a problem hiding this comment.
Yeah thats the intent for it later on.


Description
This PR introduces LLM tokenizer-based filtering as an optional mechanism to reduce false positives during scans.
A new CLI flag
--filter-tokenizeenables this feature, allowing users to run scans with tokenizer-based heuristics applied to unverified results.What’s Changed
1. CLI Flag for Tokenizer Filtering
--- filter-tokenize
2. Tokenizer-Based Heuristic
Uses an LLM tokenizer (e.g.,
cl100k_base) to compute a token-to-character ratio.How it works
An LLM tokenizer breaks a string into smaller units called tokens.
These tokens are not just words—they can be:
"connection")"connect","ion")"x7","Ab")The tokenizer is trained on large amounts of natural language and code, so it tries to split text into meaningful or commonly seen patterns.
Step-by-step
Intuition
Natural language / structured text
Random or high-entropy strings
Threshold
Example (conceptual)
getUserAccountQWbToc7xu15O5oDfWhat this helps filter out
Important Note
This is a heuristic, not a guarantee:
3. Per-Detector Opt-Out Mechanism
4. Initial Detector Support
Implemented opt-out for:
Reason:
Checklist:
make test-community)?make lintthis requires golangci-lint)?Note
Medium Risk
Changes result filtering behavior and adds a new heuristic dependency, which could hide legitimate unverified findings when enabled or impact scan performance.
Overview
Introduces an optional tokenizer-based false-positive filter for unverified findings, enabled via the new CLI flag
--filter-tokenizeand wired throughengine.Config/Engineto suppress results flagged by this heuristic.Adds tokenizer FP plumbing (
ResultWithMetadata.IsTokenizerFalsePositive,TokenizerFalsePositiveCheckeropt-out) and implements the heuristic usingtiktoken-go(token-to-character ratio) with initial opt-outs for the MongoDB and Postgres detectors. Also extendsFalsePositiveInfoprotobuf withlow_token_char_ratio, and logs a newtotal_results_foundmetric derived fromEngine.NumFoundResults().Reviewed by Cursor Bugbot for commit 54a5243. Bugbot is set up for automated code reviews on this repo. Configure here.