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

hppx

v0.1.10

Published

Superior HTTP Parameter Pollution protection middleware with modern TypeScript, robust sanitizer, and extensive tests.

Readme

hppx

Superior HTTP Parameter Pollution protection middleware for Node.js/Express, written in TypeScript. It sanitizes req.query, req.body, and req.params, blocks prototype-pollution keys, supports nested whitelists, multiple merge strategies, and plays nicely with stacked middlewares.

License: MIT npm version TypeScript Node.js Zero Dependencies


Features

  • Zero runtime dependencies — minimal attack surface and bundle size
  • Multiple merge strategieskeepFirst, keepLast (default), combine
  • Enhanced security:
    • Blocks dangerous keys: __proto__, prototype, constructor
    • Prevents null-byte injection in keys
    • Rejects malformed keys (dot/bracket-only patterns)
    • Validates key lengths to prevent DoS attacks
    • Limits array sizes to prevent memory exhaustion
  • Flexible whitelisting — nested whitelist with dot-notation and leaf matching
  • Pollution tracking — records polluted parameters on the request (queryPolluted, bodyPolluted, paramsPolluted)
  • Multi-middleware support — works with multiple middlewares on different routes (whitelists applied incrementally)
  • DoS protectionmaxDepth, maxKeys, maxArrayLength, maxKeyLength
  • Performance optimized — path caching and Set-based lookups for fast whitelist checks
  • Fully typed API — TypeScript-first with comprehensive type definitions for both ESM and CommonJS

Installation

npm install hppx

Quick Start

ESM (ES Modules)

import express from "express";
import hppx from "hppx";

const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(
  hppx({
    whitelist: ["tags", "user.roles", "ids"],
    mergeStrategy: "keepLast",
    sources: ["query", "body"],
  }),
);

app.get("/search", (req, res) => {
  res.json({
    query: req.query,
    queryPolluted: req.queryPolluted ?? {},
    body: req.body ?? {},
    bodyPolluted: req.bodyPolluted ?? {},
  });
});

CommonJS

const express = require("express");
const hppx = require("hppx");

const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(
  hppx({
    whitelist: ["tags", "user.roles", "ids"],
    mergeStrategy: "keepLast",
    sources: ["query", "body"],
  }),
);

app.get("/search", (req, res) => {
  res.json({
    query: req.query,
    queryPolluted: req.queryPolluted ?? {},
    body: req.body ?? {},
    bodyPolluted: req.bodyPolluted ?? {},
  });
});

API

Default Export: hppx(options?: HppxOptions)

Creates an Express-compatible middleware. Applies sanitization to each selected source and exposes *.Polluted objects on the request.

Note: Invalid options throw a TypeError at middleware creation time, not at request time. This ensures misconfiguration is caught early.

Options

Whitelist & Strategy:

| Option | Type | Default | Description | | --------------- | ---------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | whitelist | string[] \| string | [] | Keys allowed to remain as arrays. Supports dot-notation ("user.tags") and leaf matching ("tags" matches any path ending in tags). | | mergeStrategy | 'keepFirst' \| 'keepLast' \| 'combine' | 'keepLast' | How to reduce duplicate/array parameters when not whitelisted. keepFirst takes the first value, keepLast takes the last, combine flattens all values into a single array. |

Source Selection:

| Option | Type | Default | Description | | ---------------------- | -------------------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | sources | Array<'query' \| 'body' \| 'params'> | ['query', 'body', 'params'] | Which request parts to sanitize. | | checkBodyContentType | 'urlencoded' \| 'any' \| 'none' | 'urlencoded' | When to process req.body. urlencoded only processes URL-encoded bodies, any processes all content types, none skips body processing entirely. | | excludePaths | string[] | [] | Paths to exclude from sanitization. Supports * wildcard suffix (e.g., "/assets*"). |

Security Limits (DoS Protection):

| Option | Type | Default | Range | Description | | ---------------- | -------- | ------- | -------- | ------------------------------------------------------------------------------------- | | maxDepth | number | 20 | 1 - 100 | Maximum object nesting depth. Exceeding this throws an error passed to next(). | | maxKeys | number | 5000 | >= 1 | Maximum number of keys to process. Exceeding this throws an error passed to next(). | | maxArrayLength | number | 1000 | >= 1 | Maximum array length. Arrays are truncated before processing. | | maxKeyLength | number | 200 | 1 - 1000 | Maximum key string length. Longer keys are silently dropped. |

Behavior & Callbacks:

| Option | Type | Default | Description | | --------------------- | --------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | trimValues | boolean | false | Trim whitespace from string values. | | preserveNull | boolean | true | Preserve null values in the output. | | strict | boolean | false | Immediately respond with HTTP 400 when pollution is detected. Response includes error, message, pollutedParameters, and code ("HPP_DETECTED") fields. | | onPollutionDetected | (req, info) => void | — | Callback fired on pollution detection. Called once per polluted source (e.g., fires twice if both query and body are polluted). info contains { source: RequestSource, pollutedKeys: string[] }. | | logger | (err: Error \| unknown) => void | — | Custom logger for errors and pollution warnings. Receives string for pollution warnings and Error for caught errors. Falls back to console.warn/console.error if the logger throws. | | logPollution | boolean | true | Enable automatic logging when pollution is detected. |


Named Export: sanitize(input, options?)

function sanitize<T extends Record<string, unknown>>(input: T, options?: SanitizeOptions): T;

Sanitize a plain object using the same rules as the middleware. Returns only the cleaned object (polluted data is not returned — use the middleware if you need req.queryPolluted etc.).

ESM:

import { sanitize } from "hppx";

const clean = sanitize(payload, {
  whitelist: ["user.tags"],
  mergeStrategy: "keepFirst",
});

CommonJS:

const { sanitize } = require("hppx");

const clean = sanitize(payload, {
  whitelist: ["user.tags"],
  mergeStrategy: "keepFirst",
});

Exported Types

All types are available for both ESM and CommonJS consumers:

import type {
  RequestSource, // "query" | "body" | "params"
  MergeStrategy, // "keepFirst" | "keepLast" | "combine"
  SanitizeOptions, // Options for sanitize()
  HppxOptions, // Full middleware options (extends SanitizeOptions)
  SanitizedResult, // { cleaned, pollutedTree, pollutedKeys }
} from "hppx";

Exported Constants

import { DANGEROUS_KEYS, DEFAULT_SOURCES, DEFAULT_STRATEGY } from "hppx";

DANGEROUS_KEYS; // Set<string> — {"__proto__", "prototype", "constructor"}
DEFAULT_SOURCES; // ["query", "body", "params"]
DEFAULT_STRATEGY; // "keepLast"

Advanced Usage

Strict Mode (Respond 400 on Pollution)

app.use(hppx({ strict: true }));

// Polluted requests receive:
// {
//   "error": "Bad Request",
//   "message": "HTTP Parameter Pollution detected",
//   "pollutedParameters": ["query.x"],
//   "code": "HPP_DETECTED"
// }

Process JSON Bodies Too

app.use(express.json());
app.use(hppx({ checkBodyContentType: "any" }));

Exclude Specific Paths

app.use(hppx({ excludePaths: ["/public", "/assets*"] }));

Custom Logging

// Use your application's logger
app.use(
  hppx({
    logger: (msg) => {
      if (typeof msg === "string") {
        myLogger.warn(msg); // Pollution warnings
      } else {
        myLogger.error(msg); // Errors
      }
    },
  }),
);

// Disable automatic pollution logging
app.use(hppx({ logPollution: false }));

Multi-Middleware Stacking

hppx supports incremental whitelisting across multiple middleware instances. Each subsequent middleware applies its own whitelist to the already-collected polluted data:

// Global middleware — whitelist "a"
app.use(hppx({ whitelist: ["a"] }));

// Route-level middleware — additionally whitelist "b" and "c"
const router = express.Router();
router.use(hppx({ whitelist: ["b", "c"] }));

// On this route, "a", "b", and "c" are all allowed as arrays
router.get("/data", (req, res) => {
  res.json({ query: req.query });
});

app.use("/api", router);

Pollution Detection Callback

app.use(
  hppx({
    onPollutionDetected: (req, info) => {
      // Called once per polluted source (query, body, params)
      securityLogger.warn("HPP detected", {
        source: info.source,
        pollutedKeys: info.pollutedKeys,
      });
    },
  }),
);

Security

What hppx Protects Against

| Threat | Protection | | ------------------------ | ---------------------------------------------------------------------------------- | | Parameter pollution | Duplicate parameters are reduced to a single value via the chosen merge strategy | | Prototype pollution | __proto__, constructor, prototype keys are blocked at every processing level | | DoS via deep nesting | maxDepth limit throws error on excessive nesting | | DoS via key flooding | maxKeys limit throws error when key count is exceeded | | DoS via large arrays | maxArrayLength truncates arrays before processing | | DoS via long keys | maxKeyLength silently drops excessively long keys | | Null-byte injection | Keys containing \u0000 are silently dropped | | Malformed keys | Keys consisting only of dots/brackets (e.g., "...", "[[") are dropped |

Production Configuration

app.use(
  hppx({
    maxDepth: 10,
    maxKeys: 1000,
    maxArrayLength: 100,
    maxKeyLength: 100,
    strict: true,
    onPollutionDetected: (req, info) => {
      securityLogger.warn("HPP detected", {
        ip: req.ip,
        path: req.path,
        source: info.source,
        pollutedKeys: info.pollutedKeys,
      });
    },
  }),
);

What hppx Does NOT Protect Against

hppx is not a complete security solution. You still need:

  • SQL injection protection — use parameterized queries
  • XSS protection — sanitize output, use CSP headers
  • CSRF protection — use CSRF tokens
  • Authentication/Authorization — validate user permissions
  • Rate limiting — prevent brute-force attacks
  • Input validation — use schema validation libraries (Joi, Yup, Zod) alongside hppx

License

MIT License - see LICENSE file for details.

Links