first commit
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# `privacy/assert-no-leak.ts`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`assertNoLeak` is the runtime sentinel that scans an outgoing `Request`
|
||||
URL and body byte-by-byte for three classes of forbidden content: the
|
||||
full SHA-1 digest of the password being checked, the password itself,
|
||||
and the configured email. A hit on any of the three throws
|
||||
`HibpError(HIBP.MALFORMED_RESPONSE)` carrying a `leakClass` tag that
|
||||
identifies what was leaked — without ever echoing the leaked bytes into
|
||||
the error message.
|
||||
|
||||
## Features
|
||||
|
||||
- Three independent leak classes — `sha1`, `password`, `email` — each
|
||||
optional so callers pay only for the checks they configure.
|
||||
- Whitelists the legitimate 5-hex prefix in the range URL so the
|
||||
k-anonymity protocol does not trip the check.
|
||||
- Treats both the literal email and its URL-encoded form as body leaks,
|
||||
but allows the documented `/api/v3/breachedaccount/{encoded}` and
|
||||
`/api/v3/pasteaccount/{encoded}` URL shapes.
|
||||
- Tolerates requests whose body cannot be cloned (some `Request`
|
||||
subclasses) — failure to read the body is silent rather than fatal.
|
||||
- No allocations beyond what `Request.clone().text()` requires.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { assertNoLeak } from "@suaveplan/hibp";
|
||||
|
||||
await assertNoLeak(req, { password: "hunter2" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { assertNoLeak, hashSha1Prefix } from "@suaveplan/hibp";
|
||||
|
||||
const { full } = hashSha1Prefix(password);
|
||||
await assertNoLeak(req, { fullSha1: full, password, email });
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `LeakCandidate` — `{ fullSha1?, password?, email? }`.
|
||||
- `LeakClass` — `"sha1" | "password" | "email"`.
|
||||
- `assertNoLeak(request, candidates): Promise<void>` — throws on hit.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The helper is intentionally async even though the URL check is
|
||||
synchronous: reading the body requires `Request.clone().text()`, which
|
||||
returns a `Promise<string>`. The check uses `Array.prototype.includes`
|
||||
on the strings rather than per-byte iteration because JS strings already
|
||||
expose `indexOf` in O(n), and the simpler API has fewer ways to go
|
||||
wrong. The 5-hex prefix is stripped before the SHA-1 search to avoid
|
||||
flagging the legitimate k-anonymity payload. Production deployments opt
|
||||
in via `HibpClient` `assertOutgoingRequestSafe: true`; tests use it
|
||||
unconditionally as a regression sentinel.
|
||||
Reference in New Issue
Block a user