Redact
Operational-log redaction — regex-shape and field-name rules that strip PII / secrets out of every log payload before it reaches a file, debug sink, or external SIEM.
Two complementary signals run on every walk: a sensitive-field name set (case-insensitive substring match against keys like password, api_key, authorization, dpop, client_secret, refresh_token) and a value-shape detector chain (Luhn-validated credit-card numbers, JWS triplets, PEM / OpenSSH private-key blocks, AWS access-key prefixes, vault-sealed ciphertexts, connection-string credential leaks). Field-name hits replace the whole value with the configured marker; value-shape hits replace with a per-detector marker ([REDACTED-CC], [REDACTED-JWT]).
The redactor never mutates the input — every call returns a fresh object. The same payload commonly lands in two paths simultaneously (audit-log seals via vault; operational log redacts here) so in-place mutation would corrupt the sealed-then-archived copy.
classifyDefaults and installOutboundDlp extend the same primitive set into outbound-DLP duty: the classifier produces a verdict ("clean" / "redact" / "refuse") for a request body + headers, and the installer wraps httpClient / mail / webhook instances so refused requests fail with DlpError and redacted ones proceed with sanitized payloads. Posture presets (pci-dss / hipaa / fapi2 / soc2 / gdpr) pick a sensible default classifier.
b.redact.registerFieldRule(name, replacement?) #
Add a field name to the always-redact set. Match is case-insensitive substring, so registering secret also redacts appSecret / customer_secret. The replacement argument is accepted for symmetry with registerValueDetector but ignored — field-name hits always use the redactor's configured marker.
b.redact.registerFieldRule("internal_token");
var out = b.redact.redact({ internal_token: "abc-123" });
// → { internal_token: "[REDACTED]" }
b.redact.registerValueDetector(name, testFn, replacement) #
Register a custom value-shape detector. testFn(value) runs against every string value the redactor walks; truthy result substitutes the replacement (string or function — function receives the matched value and returns the substitution). Custom detectors run AFTER the built-in chain.
// Redact internal employee IDs (shape: EMP-NNNNNN).
b.redact.registerValueDetector("employee-id",
function (v) { return /^EMP-\d{6}$/.test(v); },
"[REDACTED-EMPID]");
var out = b.redact.redact({ note: "owner EMP-123456" });
// → { note: "owner EMP-123456" } — value-shape detectors only fire
// on full-string match; in-string matches need a custom regex
// replacement function.
b.redact.redact(value, opts?) #
{
marker: string, // replacement marker; default "[REDACTED]"
maxDepth: number, // recursion cap; default 50
parentKey: string | null, // seed parent-key for top-level scalars
}
Walk value and return a NEW value with sensitive fields and sensitive-shaped strings replaced by the marker. Handles plain objects, arrays, primitives, Buffers (always replaced — never log raw binary). The original input is never mutated.
var safe = b.redact.redact({
email: "alice@example.com",
password: "hunter2",
card: "4111 1111 1111 1111",
note: "see eyJabcdefghijk.eyJxyz.signature for proof",
});
// → { email: "alice@example.com",
// password: "[REDACTED]",
// card: "[REDACTED-CC]",
// note: "see eyJabcdefghijk.eyJxyz.signature for proof" }
b.redact.classifyDefaults(opts) #
{
patterns: string[], // names from CLASSIFIER_PATTERNS
extra: object, // additional { name: { detect, action, label } }
overrideAction: "refuse" | "redact" | "audit-only",
marker: string, // default "[REDACTED]"
}
Build a classifier function from a list of pattern names. The returned classify({ body, headers, url }) walks the body (object, string, or Buffer), inspects every header value, and returns { verdict, hits, redactedBody }. Verdict precedence is refuse > redact > audit-only > clean.
var classify = b.redact.classifyDefaults({
patterns: ["pan", "ssn", "jwt", "aws-access-key"],
});
var v = classify({
body: { card: "4111111111111111", note: "ok" },
headers: { authorization: "Bearer eyJabc.eyJdef.sig" },
});
// → v.verdict === "refuse" (PAN match defaults to refuse)
b.redact.installOutboundDlp(opts) #
{
httpClient: object, // instance with .request(opts)
mail: object, // instance with .send(message)
webhook: object, // signer instance with .send(input)
classifier: function, // override the default classifier
posture: string, // "pci-dss" | "hipaa" | "fapi2" | "soc2" | "gdpr"
onRefuse: function, // hook fired on refuse verdict
onRedact: function, // hook fired on redact verdict
onScan: function, // hook fired on every classify call
}
Install request-time interceptors on httpClient / mail / webhook instances so every outbound payload runs through a DLP classifier first. Refused requests reject with DlpError; redacted requests proceed with a sanitized body. Idempotent per primitive instance — installing twice on the same client no-ops.
var http = b.httpClient.create({ baseUrl: "https://api.example.com" });
var mail = b.mail.create({ host: "smtp.example.com", port: 587 });
var dlp = b.redact.installOutboundDlp({
httpClient: http,
mail: mail,
posture: "pci-dss",
onRefuse: function (info) { console.warn("DLP refused", info.verdict.hits); },
});
// dlp.installed → { httpClient: true, mail: true, webhook: false }
// dlp.uninstall() restores the original .request / .send methods.
b.redact.isOutboundDlpInstalled() #
Returns true when at least one primitive instance (httpClient / mail / webhook) currently carries an outbound-DLP interceptor. b.compliance.set reads this to decide whether to emit the one-time compliance.posture.outbound_dlp_unwired warning when a posture whose floor implies outbound DLP is pinned without any wiring. Read-only.
b.redact.isOutboundDlpInstalled(); // → false
var dlp = b.redact.installForPosture("hipaa", { httpClient: myHttp });
b.redact.isOutboundDlpInstalled(); // → true
dlp.uninstall();
b.redact.isOutboundDlpInstalled(); // → false
b.redact.installForPosture(posture, primitives) #
Posture-coordinated install — picks the default classifier for posture and wraps the operator-supplied primitives.httpClient / .mail / .webhook so every outbound payload runs through it. A thin convenience over installOutboundDlp; direct callers usually want installOutboundDlp because it accepts the full hook surface.
The operator MUST call this with the primitive instances — pinning a posture via b.compliance.set does NOT auto-install outbound DLP, because the compliance coordinator holds no httpClient / mail / webhook handles. When a posture whose floor implies outbound DLP (hipaa / pci-dss / gdpr / soc2 / fapi-2.0) is pinned without this call, b.compliance.set emits a one-time compliance.posture.outbound_dlp_unwired audit warning so the gap is grep-able in the audit chain.
var dlp = b.redact.installForPosture("hipaa", {
httpClient: myHttp,
mail: myMail,
webhook: myWebhook,
});
// → dlp.installed.httpClient === true
b.redact.redactText(str) #
Scrub credentials EMBEDDED in a free-text string in place, keeping the surrounding prose — for log messages and other operator-facing text where a secret may be interpolated mid-sentence. Unlike redact (structured, whole-value, anchored), this uses word-boundary fragment replacement so "login failed: <jwt> for bob" keeps everything but the jwt. Detects PEM blocks, JWTs, AWS access keys, vault-sealed ciphertext, URL-userinfo passwords (including empty-username forms), bearer tokens, key=secret assignments, SSN/EIN, and Luhn-valid PANs. The high-entropy api-key-shape detector is deliberately excluded (on free text it eats ordinary IDs / hashes / base64). Drop-safe: never throws (it runs on the hot-path log-emit sink); on any error it returns a fully-masked marker rather than the raw input. Every quantifier is length-capped (ReDoS backstop).
b.redact.redactText("token=AKIAIOSFODNN7EXAMPLE ok"); // → "token=[redacted] ok"
Last updated 2026-08-08T16:39:49.652Z by seeder.