first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -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.