Filter runs a pipeline of transformers before dictionary matching. Each one returns the new text plus an index map back to your original string, so offsets in matches and replaceWith refer to the text you passed in.
| Transformer | Catches | Example |
|---|---|---|
| html | tags, comments, named, numeric, hex and double-encoded entities, soft hyphens | f<b>u</b>ck, f&#117;ck |
| separators | . , / | _ - between letters, single dots, rotating separators |
f.u.c.k, b.itch, f.u_c-k |
| masking | one mask character per hidden letter | f*ck, sh#t, b*****d, @sshole |
| stretch | repeated letters, with legitimate double letters kept | aaaasshole |
| leetspeak | |_|, |<, /\, and |, 1, ! read as l |
f|_|ck, a$$ho|e |
| whitelist | built-in safe words and names; hits inside the protected word are dropped | cl@ss, Moby Dick, cum laude |
new Filter({ obfuscation: 'standard' }); // default
new Filter({ obfuscation: 'off' }); // plain matching only
new Filter({
obfuscation: {
level: 'basic', // off | basic | standard | strict
separators: { chars: '.,/|_-', minTwoPieceLength: 4 },
masking: { chars: '*#@$%_?!', minVisible: 1 },
html: { decodeDepth: 2 },
builtinWhitelist: true,
},
whitelist: ['shit happens'], // extra safe phrases
});| Level | Switches on |
|---|---|
off |
nothing (behaves like earlier versions) |
basic |
html, stretch, built-in whitelist |
standard (default) |
basic, separators, masking, l reading of |/1/! |
strict |
standard, spaces allowed after one other separator, shorter two-fragment joins |
Defaults can be overridden from the environment: GLIN_HTML_DECODE_DEPTH, GLIN_SEPARATOR_MIN_JOIN, GLIN_MASK_MIN_VISIBLE.
- A mask only resolves when a dictionary word of the same length fits it, so
****,p@sswordand markdown**bold**stay clean. - Separator joins must start and end on a fragment boundary and include a single-letter fragment, so
end.fuck,e.g.,B.S.andwell-knownproduce nothing new. - Stretched tokens must contain the word's own letter runs, only longer, so
asnever becomesass. - The whitelist drops a hit only when it lies fully inside the protected word of an entry (
cumincum laude,DickinMoby Dick). Other profane words around it are still flagged, soFuck Dickreportsfuck. A capitalized word beforeDicksuch asYouorBigdoes not make it a name. The dictionary itself is untouched. - A spelled-out chain joins only at a word end:
a s s i g n m e n tandc.o.c.k.p.i.tstay clean,f.u.c.k.s.h.i.tis still found. wordBoundaries: false(also the default whenallowObfuscatedMatch: true) keeps the earlier fuzzy scan. Theobfuscationoptions and the built-in whitelist apply only with word boundaries on. The scan still honorsignoreWordsand your ownwhitelistphrases (matched on word edges), but not the built-in list.replaceWithmasks by the offsets of the reported hits, so a whitelisted name in the same text stays readable.enableContextAwarescans the same views as the plain path; it only adds the context scoring ofContextAnalyzer(JS).
Two-word splits such as fu ck or sh it are not joined (they collide with ordinary text like pen is). Names other than Dick, and idioms outside the built-in list, need whitelist.