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

@boldsec/next

v0.12.0

Published

BoLD live authorization monitoring for Next.js App Router: wrap a route, watch it for cross-user access (BOLA/IDOR), function-level (BFLA), mass-assignment (BOPLA), and tenant-isolation risks. Metadata only, fail-safe.

Readme

@boldsec/next

Live BOLA/IDOR monitoring for Next.js (App Router). Wrap an API route; BoLD watches it and raises an alarm the moment one user reaches another user's object — in real time, from your app's actual traffic.

  • Metadata only. BoLD receives who made the request (an opaque, non-reversible handle — never the token), the endpoint shape, the object id, the status, and the object's declared owner if the response exposed one. The response body is read once to extract that single owner field and then discarded. No body, no headers, no credentials ever leave your app.
  • Fail-safe. Shipping the metadata is fire-and-forget. If BoLD is slow or down, your response is returned untouched and on time. BoLD can never break or slow the app it watches.
  • Never alters your response. Your handler runs exactly as before; its response is returned byte-for-byte.

Install

npm install @boldsec/next

Configure (once)

Add your keys (from the BoLD Live page, shown once when you connect the app):

# .env.local
BOLD_INGEST_URL=https://<your-bold>/api/live/ingest
BOLD_INGEST_KEY=blk_...
# optional — owner-field names to look for in responses (default covers common ones)
BOLD_OWNER_FIELDS=ownerId,owner_id,user_id

Use

Wrap the route handlers you want watched:

// app/api/invoices/[id]/route.ts
import { withBold } from "@boldsec/next";

export const GET = withBold(
  async (req, ctx) => {
    const invoice = await getInvoice(ctx.params.id);
    return Response.json(invoice); // { id, ownerId, ... }
  },
  // STRONGLY RECOMMENDED — tell BoLD who the caller is, in the SAME namespace as your owner field:
  { resolveCallerId: (req) => getUserIdFromSession(req) }, // e.g. "usr_101"
);

Works for GET / PUT / PATCH / POST / DELETE handlers. Redeploy, and your app's real traffic is watched automatically — catches appear in your BoLD Findings with a live timestamp.

Many routes? Wrap them all at once

Editing 100 routes by hand is a lot of work. The codemod wraps every App Router route handler for you, and shows you the change before writing anything:

npx @boldsec/next init           # DRY RUN: previews every change, writes nothing
npx @boldsec/next init --apply   # writes the changes — then review with: git diff
  • Dry run by default. You always see exactly what would change first. --apply writes; you review the git diff and commit.
  • Runs entirely on your machine. It never sends your code anywhere.
  • Idempotent. Re-run any time — already-wrapped handlers are left untouched.
  • Loud, never silent. Any handler it can't safely auto-wrap (e.g. a re-export) is listed for you to wrap by hand, so a route is never quietly left unwatched.
  • It inserts a TODO(BoLD) where your resolveCallerId goes — fill in the one line for your auth stack (search TODO(BoLD)), since only your app knows who the caller is.

Point at a non-default app directory with --dir src/app.

App shapes it reads

  • REST object routes — string ids (/api/invoices/inv_8101), numeric ids (/api/orders/4021), UUIDs.
  • Nested / oddly-named owner fields — the owner is found wherever it nests (bounded depth) as long as the field name is in your owner-field list (e.g. add holderId for data.account.holderId).
  • GraphQL — wrap your app/api/graphql/route.ts the same way. BoLD parses the operation and its id (from variables or an inline literal) and reads the owner from data.<field>. Single-object queries and mutations are watched; batched/list queries, or an id BoLD can't resolve, are skipped (it never guesses — a shape it can't read is held for review, never reported clean).

Mass assignment (BOPLA)

Mass assignment is when a normal user sets a field on a write that they must not: PATCH /users/me with { "role": "admin" }, or { "owner_id": "...", "balance": 999999 }. On a write, BoLD reads the request body locally, in your app and reports only the field NAMES that match a built-in wordlist of high-signal privilege / ownership / trust / financial fields (role, is_admin, owner_id, verified, balance, banned, ...).

  • Names only, never a value. BoLD sees that role was in the write body, never what it was set to. And a body key that is not a known-sensitive name is dropped, so your app's own field names (your schema) never leave the app.

  • Zero-config leads. With no setup, a wordlist hit on a write raises a loud needs-review lead in your Findings ("a write set role on this route -- declare it or dismiss it"). It is never auto-reported as CONFIRMED from live traffic.

  • Declare your app's protected fields to raise leads on names outside the wordlist, and to let the one-click active check confirm a real mass assignment on a seeded test object:

    export const PATCH = withBold(handler, {
      resolveCallerId: (req) => getUserIdFromSession(req),
      sensitiveFields: ["clearance", "commission_rate"], // your app-specific write-protected fields
    });

    Or set BOLD_SENSITIVE_FIELDS (comma-separated) once for every route.

To keep the noise budget low, genuinely ambiguous, commonly user-settable names (status, active, plan, tier, visibility, ...) are not in the default wordlist -- add them with sensitiveFields if your app treats them as protected. Mass assignment has no passive CONFIRMED: a live hit is always a needs-review lead, and the active seeded check is the only confirmer. (Field extraction is REST-JSON, top-level keys; GraphQL mutations and non-JSON bodies do not raise a passive lead -- absence of a lead is never an all-clear.)

Why resolveCallerId matters

Your app already authenticated the caller, so it knows their id (e.g. usr_101). Passing it lets BoLD compare the caller against the object's owner directly: if they match, it's the owner's own access and BoLD stays silent — no false alarm. Without it, BoLD falls back to a non-reversible hash of the auth header, which can't be matched against your owner ids, so a user reading their own object may be flagged. Always provide resolveCallerId in production.

Org / team apps: resolveCallerScopes

If your app is scoped by an org, team, tenant, or workspace (a nested route like /orgs/{id}/items/{id}, or an owner field like tenantId), a teammate reading a colleague's item is legitimate — the object is owned by the scope, not one person. BoLD recognises this: it never raises a CONFIRMED problem on a scoped read it can't place the caller in (it holds it for review instead). To judge these precisely — a real member stays silent, a genuine outsider is confirmed — pass the scopes the caller belongs to:

export const GET = withBold(handler, {
  resolveCallerId: (req) => getUserId(req),        // e.g. "usr_101"
  resolveCallerScopes: (req) => getUserOrgIds(req), // e.g. ["org_42", "org_7"]
});

With it, a caller who is in the object's scope is treated as an authorized member (never flagged), and one who is not is judged as a cross-scope access. Without it, scoped reads are safely held for review — never a false alarm, just not auto-resolved.

If your scoping shape is unusual and BoLD can't auto-detect it from the URL or owner field, name it explicitly with resolveObjectScope: (req) => "team_9". Combined with resolveCallerScopes, that makes the member-vs-outsider guarantee airtight for any app shape.

Mark privileged routes (BFLA)

Set privileged: true on any admin or elevated-access route. BoLD then knows to verify that a non-privileged user cannot reach it (function-level authorization / BFLA). The SDK includes route_declared_privileged: true in the metadata event for that route; absent or false leaves the event byte-for-byte unchanged.

export const DELETE = withBold(handler, { privileged: true });

How it confirms a violation

BoLD confirms a BOLA when a request returns an object whose owner field identifies a different user than the caller. If a response doesn't expose an owner field, BoLD reports needs review rather than guessing — it never raises a false alarm.