npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hazo_secure

v1.0.1

Published

Security & compliance primitives — SSRF-safe fetch, rate limiting, field-level encryption, GDPR orchestration. Four subpath modules, pick the ones you need.

Readme

hazo_secure

Security & compliance primitives for Node.js / Next.js apps. Four independent subpath modules — import only the ones you need.

| Subpath | Purpose | |---|---| | hazo_secure/fetch | SSRF-safe safeFetch() — domain allowlist, private-CIDR blocker, redirect controls | | hazo_secure/ratelimit | Sliding-window + token-bucket limiter with pluggable store (in-memory default, hazo_connect adapter) | | hazo_secure/crypto | AES-256-GCM field-level encryption with key versioning and AAD support | | hazo_secure/gdpr | Exporter / anonymiser registry + lifecycle orchestrator. Uses hazo_jobs for async export, hazo_files for ZIP delivery | | hazo_secure/csrf | Double-submit CSRF protection for Next.js routes — token generation, cookie header, constant-time verification |

Install

npm install hazo_secure

hazo_logs is a required peer dep. hazo_connect, hazo_jobs, and hazo_files are optional peers — hazo_connect is needed for ConnectRateLimitStore; hazo_jobs and hazo_files are only needed for hazo_secure/gdpr.

Quick start

SSRF-safe outbound fetch

import { safeFetch } from "hazo_secure/fetch";

const res = await safeFetch("https://nominatim.openstreetmap.org/search", {
  policy: {
    allowedHosts: ["nominatim.openstreetmap.org"],
    blockPrivateIps: true,
    maxRedirects: 3,
    timeoutMs: 5000,
  },
});

Rate limit a Next.js route

import { checkRateLimit } from "hazo_secure/ratelimit";

export async function POST(req: Request) {
  const decision = await checkRateLimit(req, { windowMs: 60_000, max: 30 });
  if (!decision.allowed) return new Response("Too Many Requests", { status: 429 });
  // ...
}

Encrypt a PII field

import { encryptField, decryptField } from "hazo_secure/crypto";

const stored = await encryptField(plaintext, { keys, aad: `user:${userId}` });
const recovered = await decryptField(stored, { keys });

Register GDPR exporters

import { createGdprRegistry } from "hazo_secure/gdpr";

const gdpr = createGdprRegistry();

gdpr.registerExporter({
  domain: "persons",
  async *export({ userId }) {
    for (const row of await db.personsByUser(userId)) {
      yield { filename: `persons/${row.id}.json`, content: JSON.stringify(row, null, 2) };
    }
  },
});

New in 1.0.0

hazo_secure/ratelimit — ConnectRateLimitStore (token bucket)

import { ConnectRateLimitStore } from "hazo_secure/ratelimit";

const store = new ConnectRateLimitStore({ getHazoConnect: () => db });
const limiter = createRateLimiter({ store, windowMs: 60_000, max: 30 });

Stores token-bucket state in hazo_rl_buckets (see migrations/001_hazo_rl_buckets.sql). Run the migration before use. Requires hazo_connect peer.

hazo_secure/fetch — undici dispatcher + validateConnectIp

import { safeFetch, createSecureDispatcher, validateConnectIp } from "hazo_secure/fetch";

// Use standalone to close the DNS-rebinding gap at socket creation time
const dispatcher = createSecureDispatcher();
const res = await safeFetch(url, { policy, deps: { fetchImpl: (u, i) => fetch(u, { ...i, dispatcher }) } });

// Or validate resolved IPs yourself
validateConnectIp("169.254.169.254"); // throws — blocked CIDR

Node.js only — undici is a Node dep. Edge runtime gets pre-flight checks only.

hazo_secure/crypto — HttpKeyProvider

import { HttpKeyProvider } from "hazo_secure/crypto";

const provider = new HttpKeyProvider({
  endpoint: "https://kms.internal/keys",
  headers: { Authorization: `Bearer ${token}` },
  ttlMs: 30_000,           // in-process cache TTL (default: 30 s)
});
provider.clearCache();     // force next fetch to re-fetch from endpoint

Cloud-agnostic — works with any HTTPS key endpoint that returns { keyId, key: "<base64>" }. Caches keys in-process to avoid latency on every encrypt/decrypt call.

hazo_secure/gdpr — registry enhancements

import { createGdprRegistry, GdprRegistryError } from "hazo_secure/gdpr";

const gdpr = createGdprRegistry({ auditIntent: logAuditEntry });

// Validate registry completeness
const issues = await gdpr.validateRegistry({ requiredDomains: ["persons", "payments"] });

// Export user data (streams records from all registered exporters)
for await (const file of gdpr.exportUser({ userId })) {
  console.log(file.filename, file.content);
}

// Dry-run export without side effects
const files = await gdpr.dryRunExport({ userId });

// Anonymise user across all registered domains
await gdpr.anonymiseUser({ userId });

GdprRegistryError is a typed error class — catch and instanceof-check it.


Runtime Compatibility

| Subpath | Edge runtime | Node.js (server) | Notes | |---|---|---|---| | hazo_secure | ✅ | ✅ | Types only, no runtime weight | | hazo_secure/csrf | ✅ | ✅ | Web Crypto API only | | hazo_secure/ratelimit | ✅ (MemoryRateLimitStore) | ✅ | ConnectRateLimitStore is Node-only (hazo_connect) | | hazo_secure/fetch | ⚠️ pre-flight only | ✅ full protection | Edge: host/IP checks only; Node: + undici connect-time validation | | hazo_secure/crypto | ❌ | ✅ | Uses node:crypto | | hazo_secure/gdpr | ❌ | ✅ | Async iterables + optional hazo_connect |

Node.js ≥ 18 required for all Node-only subpaths.


Design

Each subpath is independent — no cross-imports between /fetch, /ratelimit, /crypto, /gdpr. The gdpr module consumes the others when a project wires them together, but the package itself doesn't entangle them.

See CLAUDE.md and AGENTS.md for architecture details.

License

MIT