Pick

Build a new object holding only the keys you named. Assigning a request body onto a record takes every key the request happened to carry, which is how a form that edits a display name also sets isAdmin. Naming the keys inverts that: what was not named is not there.

The keys that move or read a prototype, __proto__, constructor and prototype, are dropped whether or not they were named, so an allow-list cannot be written that lets one through. The result has a null prototype internally and is copied onto a plain object, so nothing inherited comes with it.

Nesting is by [name, [keys]], and the same rules apply at every level. An array is filtered element by element, so a body an attacker sent as a list is no way around the allow-list, and the shape of the object does not matter: a class instance and an object from another realm are filtered the same way a literal is.

b.pick.registerPoisonedKeys(keys) #

stable0.15.13

Add key names to the set every caller treats as unsafe. A deployment whose object layer gives another name the power __proto__ has adds it here once, and every b.pick call and every assertSafeKey check refuses it from then on.

The set only grows: there is no removal, so a name registered as unsafe cannot be made safe again by later code.

var b = require("@blamejs/core");
b.pick.registerPoisonedKeys(["$where"]);
b.pick.isPoisonedKey("$where");   // → true

b.pick.isPoisonedKey(key) #

stable0.15.13

Is this key one of the names that must never be assigned from untrusted input? True for __proto__, constructor and prototype, and for anything b.pick.registerPoisonedKeys has added. False for a value that is not a string.

var b = require("@blamejs/core");
b.pick.isPoisonedKey("__proto__");   // → true
b.pick.isPoisonedKey("name");        // → false

b.pick.movesThePrototype(key) #

stable0.20.32

Is this the one key whose assignment replaces an object's prototype? True only for __proto__.

constructor and prototype are unsafe to copy and do not by themselves move a prototype, so a caller that has to tell the two apart, to report which happened, asks this rather than isPoisonedKey.

var b = require("@blamejs/core");
b.pick.movesThePrototype("__proto__");     // → true
b.pick.movesThePrototype("constructor");   // → false

b.pick.assertSafeKey(key, onPoisoned) #

stable0.15.13

Call onPoisoned(key) when the key is unsafe, and answer undefined when it is not. The caller decides what happens, because a parser building a value wants to skip the key while an options reader wants to throw.

onPoisoned must be a function; anything else throws a TypeError here, rather than leaving an unsafe key unhandled because the handler was missing.

var b = require("@blamejs/core");
var refused = [];
b.pick.assertSafeKey("__proto__", function (k) { refused.push(k); });
b.pick.assertSafeKey("name", function (k) { refused.push(k); });
refused;
// → ["__proto__"]

b.pick(input, allowList, opts?) #

stable0.7.21soc2gdpr
{
  onUnknown: string,   // "drop" (default) or "throw"
}

Answer a new object carrying only the allowed keys of input. An entry in allowList is a key name, or [name, [keys]] to allow a nested object and name what may come out of it.

A key that is not on the list is dropped, or throws when onUnknown: "throw", which is the choice between accepting a request that carried more than it should and telling the caller it did.

__proto__, constructor and prototype never come through, and naming one on the allow-list does not change that.

An array is filtered element by element against the same list. Anything that is not an object comes back as it is, so a caller can hand the result of a parse straight in whatever it turned out to be.

Every object is filtered, whatever its prototype: a class instance keeps only the allowed own keys and brings none of its prototype, and an object built in another realm, by a node:vm context for instance, is treated as the object it is rather than passed through.

var b = require("@blamejs/core");
b.pick({ name: "ada", isAdmin: true }, ["name"]);
// → { name: "ada" }

b.pick({ name: "ada", profile: { bio: "x", role: "root" } },
       ["name", ["profile", ["bio"]]]);
// → { name: "ada", profile: { bio: "x" } }

Last updated 2026-10-12T14:21:56.879Z by seeder.