first commit
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# `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: `assess` accepts an alternative table via options
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user