3.0 KiB
l33t-table — default substitution table
Purpose
Provides the default character → letter substitution table used by the
dictionaryMatch matcher to recognise l33t-encoded variants of dictionary
tokens (e.g. p4ssw0rd for password). The table follows Wheeler §3.1 and
mirrors the dropbox/zxcvbn reference impl's data/l33t_table.coffee. The
companion inverseL33tTable utility flips the table for the matcher's enumeration
pass — given a substitute character, it returns the set of letters that character
could represent.
Features
- Frozen default table covering twelve high-frequency substitutions
- Inverse lookup helper for matcher enumeration
- Pure data; safe to import in any tier (no runtime deps)
- Caller-pluggable:
assessaccepts an alternative table via options
Usage: Basic Example
import { DEFAULT_L33T_TABLE, inverseL33tTable } from "@suaveplan/password-strength/matchers/dictionary";
const inverse = inverseL33tTable(DEFAULT_L33T_TABLE);
console.log(inverse["4"]); // ["a"]
console.log(inverse["1"]); // ["i", "l"]
Usage: Advanced Example
import { DEFAULT_L33T_TABLE, inverseL33tTable, dictionaryMatch } from "@suaveplan/password-strength/matchers/dictionary";
const customTable = {
...DEFAULT_L33T_TABLE,
a: ["4", "@", "/-\\"],
};
const matches = dictionaryMatch("p4ssword", { passwords: new Map([["password", 1]]) }, customTable);
console.log(matches);
API Reference
DEFAULT_L33T_TABLE: L33tTable— frozen default substitution map.inverseL33tTable(table: L33tTable): Record<string, string[]>— return the inverse map of substitute character → array of letters it can stand in for.
Implementation Notes
The table is intentionally conservative: every substitute character maps to
real letters that appear in the breach-corpus literature. Adding more
substitutions inflates the enumeration cost quadratically per password
length, so callers who extend the table should benchmark against
assess_dictionary_heavy. The frozen Object.freeze wrapper prevents
inadvertent mutation by consumers.
Additional Implementation Notes
The implementation follows the canonical zxcvbn algorithm from Wheeler 2016 with
the suaveplan-style additions described in the package proposal: structured error
codes via the defineCodes registry, Zod boundaries at every public entry
point, OTel histograms and counters keyed to low-cardinality attributes, and
a strict input-length guard at 200 characters. Consumers integrate with their
existing observability stack by installing a meter provider before importing
the package, and with their i18n stack via the MessageResolver callback type
declared in @suaveplan/types. The bundled data tables are intentionally
small to keep the package bundle compact; production deployments should swap
in fuller frequency-ranked tables via loadDictionaries. The default English
dictionary set is a frozen DictionarySet that the matcher never mutates;
caller-supplied dictionaries override defaults by name.