Files
skills/docs/security-stack/security/hibp/src/privacy/assert-no-leak.md
2026-05-29 08:20:56 +01:00

2.3 KiB

privacy/assert-no-leak.ts

(no submodules)

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

import { assertNoLeak } from "@suaveplan/hibp";

await assertNoLeak(req, { password: "hunter2" });

Usage: Advanced Example

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.