release: WRNexusJS 0.7.0
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { SecurityError } from "./errors.ts";
|
||||
import { assertSafeObject, isDangerousObjectKey } from "./object.ts";
|
||||
|
||||
export interface SecureSerializeOptions {
|
||||
maxDepth?: number;
|
||||
maxKeys?: number;
|
||||
maxBytes?: number;
|
||||
redact?: RegExp | string[];
|
||||
redactedValue?: string;
|
||||
}
|
||||
|
||||
const DEFAULT_SECRET =
|
||||
/(?:password|passwd|secret|token|api[-_]?key|private[-_]?key|otp|authorization|cookie)/i;
|
||||
|
||||
function shouldRedact(key: string, rule: SecureSerializeOptions["redact"]): boolean {
|
||||
if (Array.isArray(rule)) return rule.includes(key);
|
||||
return (rule ?? DEFAULT_SECRET).test(key);
|
||||
}
|
||||
|
||||
export function secureJsonStringify(value: unknown, options: SecureSerializeOptions = {}): string {
|
||||
assertSafeObject(value, { maxDepth: options.maxDepth, maxKeys: options.maxKeys });
|
||||
const redactedValue = options.redactedValue ?? "[REDACTED]";
|
||||
|
||||
const json = JSON.stringify(value, function secureReplacer(key, current) {
|
||||
if (isDangerousObjectKey(key)) {
|
||||
throw new SecurityError(
|
||||
"WRN-SEC-PROTOTYPE-POLLUTION",
|
||||
`Dangerous key '${key}' is not allowed.`,
|
||||
);
|
||||
}
|
||||
if (key && shouldRedact(key, options.redact)) return redactedValue;
|
||||
if (typeof current === "bigint") return current.toString();
|
||||
if (typeof current === "function" || typeof current === "symbol") {
|
||||
throw new SecurityError(
|
||||
"WRN-SEC-SERIALIZE-TYPE",
|
||||
`Cannot serialize ${typeof current} values.`,
|
||||
);
|
||||
}
|
||||
return current;
|
||||
});
|
||||
|
||||
if (json === undefined) {
|
||||
throw new SecurityError("WRN-SEC-SERIALIZE-EMPTY", "Value cannot be serialized.");
|
||||
}
|
||||
|
||||
const safe = json
|
||||
.replace(/&/g, "\\u0026")
|
||||
.replace(/</g, "\\u003c")
|
||||
.replace(/>/g, "\\u003e")
|
||||
.replace(/\u2028/g, "\\u2028")
|
||||
.replace(/\u2029/g, "\\u2029");
|
||||
const bytes = new TextEncoder().encode(safe).byteLength;
|
||||
const maxBytes = options.maxBytes ?? 256 * 1024;
|
||||
if (bytes > maxBytes) {
|
||||
throw new SecurityError(
|
||||
"WRN-SEC-SERIALIZE-SIZE",
|
||||
`Serialized payload exceeds ${maxBytes} bytes.`,
|
||||
413,
|
||||
);
|
||||
}
|
||||
return safe;
|
||||
}
|
||||
|
||||
export function serializeForHtml(value: unknown, options: SecureSerializeOptions = {}): string {
|
||||
return secureJsonStringify(value, options);
|
||||
}
|
||||
Reference in New Issue
Block a user