first commit
This commit is contained in:
77
docs/security-stack/security/hibp/src/range/range.md
Normal file
77
docs/security-stack/security/hibp/src/range/range.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# `range/range.ts`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`RangeClient` is the heart of the package — the k-anonymity Pwned
|
||||
Passwords client. `checkPassword(password, opts?)` hashes the input,
|
||||
slices the 5-hex prefix, dispatches `GET https://api.pwnedpasswords.com/range/<prefix>`
|
||||
with the mandatory `Add-Padding: true` header, scans the streamed
|
||||
response for a matching 35-char suffix, and returns
|
||||
`{ pwned, count }`. It owns the strict / off framing, the NTLM mode
|
||||
opt-in, the differential-privacy decoy fan-out, the AbortSignal
|
||||
threading, and the OTel emission.
|
||||
|
||||
## Features
|
||||
|
||||
- Mandatory `Add-Padding: true` on every fetch — including every
|
||||
differential-privacy decoy.
|
||||
- SHA-1 (default) and NTLM (caller-injected adapter) hash modes.
|
||||
- Constant-time suffix compare on every parsed line.
|
||||
- Streaming line parser (no `Response.text()` call ever).
|
||||
- Differential-privacy decoy fan-out when `extraPrefixes > 0` — real
|
||||
prefix's response is the only one consulted.
|
||||
- AbortSignal threaded into fetch and the streaming parser.
|
||||
- OTel histograms for duration; counters for outcome, errors,
|
||||
throttling.
|
||||
- Optional `assertOutgoingRequestSafe: true` runtime privacy gate.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { RangeClient } from "@suaveplan/hibp";
|
||||
|
||||
const client = new RangeClient();
|
||||
const { pwned, count } = await client.checkPassword("hunter2");
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { RangeClient } from "@suaveplan/hibp";
|
||||
|
||||
const client = new RangeClient({
|
||||
extraPrefixes: 3,
|
||||
rng: myRng,
|
||||
assertOutgoingRequestSafe: true,
|
||||
strict: true,
|
||||
});
|
||||
|
||||
const ac = new AbortController();
|
||||
setTimeout(() => ac.abort(), 5_000);
|
||||
const result = await client.checkPassword("hunter2", { signal: ac.signal });
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `CheckPasswordOptions` — `mode?`, `signal?`, `extraPrefixes?`.
|
||||
- `RangeClientOptions` — `fetchFn`, `clock`, `rng`, `strict`,
|
||||
`ntlmHash`, `extraPrefixes`, `assertOutgoingRequestSafe`.
|
||||
- `new RangeClient(options?)`.
|
||||
- `client.checkPassword(password, opts?): Promise<PwnedResult>`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The class is stateless beyond its constructor options — every public
|
||||
method takes the password and a per-call options bag, so a single
|
||||
`RangeClient` instance is safe to share across a process. Error paths
|
||||
funnel through the metrics emission so dashboards see counters even
|
||||
for failures the caller catches and retries. The `assertOutgoingRequestSafe`
|
||||
gate constructs a `Request` to feed into `assertNoLeak`, then re-issues
|
||||
the actual fetch with the same `RequestInit` so the privacy check
|
||||
exercises the real outgoing bytes. The NTLM adapter is required at
|
||||
runtime if `mode: "ntlm"` is ever passed; a missing adapter surfaces as
|
||||
`HIBP.MALFORMED_RESPONSE` because the caller has misconfigured the
|
||||
client rather than the server having misbehaved.
|
||||
Reference in New Issue
Block a user