@daloyjs/core 0.14.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Password hashing helpers with no knobs.
3
+ *
4
+ * Daloy's secure-by-default initiative ships exactly one correct way to
5
+ * hash a password. There is no algorithm switch, no cost-factor argument,
6
+ * no salt management — the API is two functions: {@link passwordHash} and
7
+ * {@link passwordVerify}.
8
+ *
9
+ * The implementation uses Node's built-in `crypto.scrypt` with parameters
10
+ * aligned with the OWASP Password Storage Cheat Sheet for scrypt
11
+ * (`N = 2^17`, `r = 8`, `p = 1`, 32-byte key, 16-byte salt). scrypt is
12
+ * memory-hard like Argon2 and ships in Node core, so this helper has no
13
+ * runtime dependencies. The output is a self-describing PHC-style string
14
+ * — verifying a hash never requires re-supplying the parameters.
15
+ *
16
+ * Argon2id is the preferred OWASP recommendation but requires a native
17
+ * binding that Daloy explicitly refuses to ship in `dependencies`. Apps
18
+ * that need Argon2id should pull `argon2` themselves; this helper
19
+ * intentionally uses scrypt to keep the install surface zero.
20
+ *
21
+ * Subpath: `@daloyjs/core/hashing`.
22
+ *
23
+ * @since 0.15.0
24
+ */
25
+ /**
26
+ * Hash a password and return a self-describing PHC-style string. The
27
+ * returned string encodes the algorithm, parameters, salt, and digest so
28
+ * {@link passwordVerify} needs nothing else.
29
+ *
30
+ * Format: `$scrypt$N=131072,r=8,p=1$<salt-base64>$<hash-base64>`
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import { passwordHash, passwordVerify } from "@daloyjs/core/hashing";
35
+ *
36
+ * const hash = await passwordHash("hunter2");
37
+ * await passwordVerify("hunter2", hash); // true
38
+ * await passwordVerify("wrong", hash); // false
39
+ * ```
40
+ *
41
+ * @param password - The plaintext password. UTF-8 encoded internally.
42
+ * @returns A PHC-style hash string safe to store in a database column.
43
+ * @throws {TypeError} When `password` is empty.
44
+ * @since 0.15.0
45
+ */
46
+ export declare function passwordHash(password: string): Promise<string>;
47
+ /**
48
+ * Verify a plaintext password against a PHC-style hash produced by
49
+ * {@link passwordHash} in constant time. Returns `false` (never throws)
50
+ * for any malformed input so the caller cannot distinguish "bad hash
51
+ * format" from "wrong password" through exception side channels.
52
+ *
53
+ * @param password - The plaintext password to check.
54
+ * @param storedHash - The PHC-style hash returned from {@link passwordHash}.
55
+ * @returns `true` when the password matches, `false` otherwise.
56
+ * @since 0.15.0
57
+ */
58
+ export declare function passwordVerify(password: string, storedHash: string): Promise<boolean>;
59
+ //# sourceMappingURL=hashing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashing.d.ts","sourceRoot":"","sources":["../src/hashing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA2CH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOpE;AAgCD;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAuBlB"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Password hashing helpers with no knobs.
3
+ *
4
+ * Daloy's secure-by-default initiative ships exactly one correct way to
5
+ * hash a password. There is no algorithm switch, no cost-factor argument,
6
+ * no salt management — the API is two functions: {@link passwordHash} and
7
+ * {@link passwordVerify}.
8
+ *
9
+ * The implementation uses Node's built-in `crypto.scrypt` with parameters
10
+ * aligned with the OWASP Password Storage Cheat Sheet for scrypt
11
+ * (`N = 2^17`, `r = 8`, `p = 1`, 32-byte key, 16-byte salt). scrypt is
12
+ * memory-hard like Argon2 and ships in Node core, so this helper has no
13
+ * runtime dependencies. The output is a self-describing PHC-style string
14
+ * — verifying a hash never requires re-supplying the parameters.
15
+ *
16
+ * Argon2id is the preferred OWASP recommendation but requires a native
17
+ * binding that Daloy explicitly refuses to ship in `dependencies`. Apps
18
+ * that need Argon2id should pull `argon2` themselves; this helper
19
+ * intentionally uses scrypt to keep the install surface zero.
20
+ *
21
+ * Subpath: `@daloyjs/core/hashing`.
22
+ *
23
+ * @since 0.15.0
24
+ */
25
+ import { randomBytes, scrypt as scryptCb, timingSafeEqual as nodeTimingSafeEqual } from "node:crypto";
26
+ // OWASP-aligned scrypt parameters (Password Storage Cheat Sheet, 2024).
27
+ const SCRYPT_N = 1 << 17; // 131072
28
+ const SCRYPT_R = 8;
29
+ const SCRYPT_P = 1;
30
+ const SCRYPT_KEYLEN = 32;
31
+ const SCRYPT_SALTLEN = 16;
32
+ // Generous max — covers OWASP's recommended `N = 2^17` at `r = 8`, `p = 1`.
33
+ const SCRYPT_MAXMEM = 192 * 1024 * 1024;
34
+ function scryptAsync(password, salt, keylen) {
35
+ return new Promise((resolve, reject) => {
36
+ scryptCb(password, salt, keylen, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P, maxmem: SCRYPT_MAXMEM }, (err, key) => {
37
+ if (err)
38
+ reject(err);
39
+ else
40
+ resolve(key);
41
+ });
42
+ });
43
+ }
44
+ function toBase64(b) {
45
+ return b.toString("base64").replace(/=+$/, "");
46
+ }
47
+ function fromBase64(s) {
48
+ if (s.length === 0 || s.length % 4 === 1)
49
+ return null;
50
+ if (!/^[A-Za-z0-9+/]+$/.test(s))
51
+ return null;
52
+ try {
53
+ const pad = s.length % 4 === 0 ? "" : "=".repeat(4 - (s.length % 4));
54
+ return Buffer.from(s + pad, "base64");
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ /**
61
+ * Hash a password and return a self-describing PHC-style string. The
62
+ * returned string encodes the algorithm, parameters, salt, and digest so
63
+ * {@link passwordVerify} needs nothing else.
64
+ *
65
+ * Format: `$scrypt$N=131072,r=8,p=1$<salt-base64>$<hash-base64>`
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * import { passwordHash, passwordVerify } from "@daloyjs/core/hashing";
70
+ *
71
+ * const hash = await passwordHash("hunter2");
72
+ * await passwordVerify("hunter2", hash); // true
73
+ * await passwordVerify("wrong", hash); // false
74
+ * ```
75
+ *
76
+ * @param password - The plaintext password. UTF-8 encoded internally.
77
+ * @returns A PHC-style hash string safe to store in a database column.
78
+ * @throws {TypeError} When `password` is empty.
79
+ * @since 0.15.0
80
+ */
81
+ export async function passwordHash(password) {
82
+ if (typeof password !== "string" || password.length === 0) {
83
+ throw new TypeError("password must be a non-empty string");
84
+ }
85
+ const salt = randomBytes(SCRYPT_SALTLEN);
86
+ const key = await scryptAsync(Buffer.from(password, "utf8"), salt, SCRYPT_KEYLEN);
87
+ return `$scrypt$N=${SCRYPT_N},r=${SCRYPT_R},p=${SCRYPT_P}$${toBase64(salt)}$${toBase64(key)}`;
88
+ }
89
+ function parsePhc(s) {
90
+ // $scrypt$N=...,r=...,p=...$salt$hash
91
+ if (typeof s !== "string" || s.length < 32)
92
+ return null;
93
+ const parts = s.split("$");
94
+ if (parts.length !== 5)
95
+ return null;
96
+ if (parts[0] !== "" || parts[1] !== "scrypt")
97
+ return null;
98
+ const paramSection = parts[2] ?? "";
99
+ const paramMatch = /^N=(\d+),r=(\d+),p=(\d+)$/.exec(paramSection);
100
+ if (!paramMatch)
101
+ return null;
102
+ const N = Number(paramMatch[1]);
103
+ const r = Number(paramMatch[2]);
104
+ const p = Number(paramMatch[3]);
105
+ if (!Number.isInteger(N) || N <= 1 || (N & (N - 1)) !== 0)
106
+ return null;
107
+ if (!Number.isInteger(r) || r < 1)
108
+ return null;
109
+ if (!Number.isInteger(p) || p < 1)
110
+ return null;
111
+ if (N !== SCRYPT_N || r !== SCRYPT_R || p !== SCRYPT_P)
112
+ return null;
113
+ const salt = fromBase64(parts[3] ?? "");
114
+ const hash = fromBase64(parts[4] ?? "");
115
+ if (!salt || !hash || salt.length !== SCRYPT_SALTLEN || hash.length !== SCRYPT_KEYLEN)
116
+ return null;
117
+ return { N, r, p, salt, hash };
118
+ }
119
+ /**
120
+ * Verify a plaintext password against a PHC-style hash produced by
121
+ * {@link passwordHash} in constant time. Returns `false` (never throws)
122
+ * for any malformed input so the caller cannot distinguish "bad hash
123
+ * format" from "wrong password" through exception side channels.
124
+ *
125
+ * @param password - The plaintext password to check.
126
+ * @param storedHash - The PHC-style hash returned from {@link passwordHash}.
127
+ * @returns `true` when the password matches, `false` otherwise.
128
+ * @since 0.15.0
129
+ */
130
+ export async function passwordVerify(password, storedHash) {
131
+ if (typeof password !== "string" || password.length === 0)
132
+ return false;
133
+ const parsed = parsePhc(storedHash);
134
+ if (!parsed)
135
+ return false;
136
+ let derived;
137
+ try {
138
+ derived = await new Promise((resolve, reject) => {
139
+ scryptCb(Buffer.from(password, "utf8"), parsed.salt, parsed.hash.length, { N: parsed.N, r: parsed.r, p: parsed.p, maxmem: SCRYPT_MAXMEM }, (err, key) => {
140
+ if (err)
141
+ reject(err);
142
+ else
143
+ resolve(key);
144
+ });
145
+ });
146
+ }
147
+ catch {
148
+ return false;
149
+ }
150
+ if (derived.length !== parsed.hash.length)
151
+ return false;
152
+ return nodeTimingSafeEqual(derived, parsed.hash);
153
+ }
154
+ //# sourceMappingURL=hashing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashing.js","sourceRoot":"","sources":["../src/hashing.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -7,11 +7,12 @@ export { HttpError, BadRequestError, ValidationError, NotFoundError, Unauthorize
7
7
  export type { ProblemDetails, ProblemRenderOptions } from "./errors.js";
8
8
  export type { StandardSchemaV1 } from "./schema.js";
9
9
  export { validate, isStandardSchema } from "./schema.js";
10
- export { readBodyLimited, safeJsonParse, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, } from "./security.js";
10
+ export { readBodyLimited, safeJsonParse, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, assertNoDuplicateSingletonHeaders, SMUGGLING_SINGLETON_HEADERS, verifyWebhookSignature, signWebhookPayload, } from "./security.js";
11
+ export type { WebhookHmacAlgorithm } from "./security.js";
11
12
  export { requestId, secureHeaders, cors, rateLimit, timing, bearerAuth, basicAuth, csrf, } from "./middleware.js";
12
13
  export type { RequestIdOptions, SecureHeadersOptions, CspDirectivesOptions, CorsOptions, RateLimitOptions, RateLimitStore, CsrfOptions, CsrfCookieOptions, BasicAuthOptions, } from "./middleware.js";
13
- export { createLogger, noopLogger } from "./logger.js";
14
- export type { Logger, LogLevel, ConsoleLoggerOptions } from "./logger.js";
14
+ export { createLogger, noopLogger, DEFAULT_REDACT_KEYS } from "./logger.js";
15
+ export type { Logger, LogLevel, ConsoleLoggerOptions, LoggerRedactionOptions, } from "./logger.js";
15
16
  export type { ScalarJsonPrimitive, ScalarJsonValue, ScalarReferenceConfiguration, ScalarTheme, } from "./docs.js";
16
17
  export { formatStartupBanner, printStartupBanner } from "./banner.js";
17
18
  export type { StartupBannerLink, StartupBannerOptions } from "./banner.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -3,9 +3,9 @@ export { createApp } from "./app.js";
3
3
  export { _resetPackageJsonCacheForTests } from "./app.js";
4
4
  export { HttpError, BadRequestError, ValidationError, NotFoundError, UnauthorizedError, ForbiddenError, MethodNotAllowedError, PayloadTooLargeError, UnsupportedMediaTypeError, TooManyRequestsError, RequestTimeoutError, InternalError, } from "./errors.js";
5
5
  export { validate, isStandardSchema } from "./schema.js";
6
- export { readBodyLimited, safeJsonParse, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, } from "./security.js";
6
+ export { readBodyLimited, safeJsonParse, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, assertNoDuplicateSingletonHeaders, SMUGGLING_SINGLETON_HEADERS, verifyWebhookSignature, signWebhookPayload, } from "./security.js";
7
7
  export { requestId, secureHeaders, cors, rateLimit, timing, bearerAuth, basicAuth, csrf, } from "./middleware.js";
8
- export { createLogger, noopLogger } from "./logger.js";
8
+ export { createLogger, noopLogger, DEFAULT_REDACT_KEYS } from "./logger.js";
9
9
  export { formatStartupBanner, printStartupBanner } from "./banner.js";
10
10
  export { sseStream, sseResponse, ndjsonStream, ndjsonResponse, } from "./streaming.js";
11
11
  export { httpBearerScheme, httpBasicScheme, apiKeyScheme, oauth2Scheme, openIdConnectScheme, } from "./security-schemes.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,8BAA8B,EAAE,MAAM,UAAU,CAAC;AAgC1D,OAAO,EACL,SAAS,EACT,eAAe,EACf,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EACL,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,QAAQ,GACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,SAAS,EACT,aAAa,EACb,IAAI,EACJ,SAAS,EACT,MAAM,EACN,UAAU,EACV,SAAS,EACT,IAAI,GACL,MAAM,iBAAiB,CAAC;AAazB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAUvD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGtE,OAAO,EACL,SAAS,EACT,WAAW,EACX,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AASxB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAqB/B,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAOvE,OAAO,EACL,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAUtB,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AASxB,OAAO,EACL,WAAW,EACX,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AAUtB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,OAAO,EACP,cAAc,EACd,SAAS,EACT,aAAa,EACb,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,2BAA2B,EAC3B,eAAe,EACf,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,SAAS,EACT,gBAAgB,GACjB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,8BAA8B,EAAE,MAAM,UAAU,CAAC;AAgC1D,OAAO,EACL,SAAS,EACT,eAAe,EACf,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EACL,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,QAAQ,EACR,iCAAiC,EACjC,2BAA2B,EAC3B,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,SAAS,EACT,aAAa,EACb,IAAI,EACJ,SAAS,EACT,MAAM,EACN,UAAU,EACV,SAAS,EACT,IAAI,GACL,MAAM,iBAAiB,CAAC;AAazB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAe5E,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGtE,OAAO,EACL,SAAS,EACT,WAAW,EACX,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AASxB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAqB/B,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAOvE,OAAO,EACL,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAUtB,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AASxB,OAAO,EACL,WAAW,EACX,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AAUtB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,OAAO,EACP,cAAc,EACd,SAAS,EACT,aAAa,EACb,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,2BAA2B,EAC3B,eAAe,EACf,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,SAAS,EACT,gBAAgB,GACjB,MAAM,gBAAgB,CAAC"}
package/dist/logger.d.ts CHANGED
@@ -15,12 +15,64 @@ export interface Logger {
15
15
  fatal(obj: object | string, msg?: string): void;
16
16
  child(bindings: Record<string, unknown>): Logger;
17
17
  }
18
+ /**
19
+ * Redaction configuration for {@link createLogger}. Keys are matched
20
+ * case-insensitively at any depth. Pass `false` to disable the safe
21
+ * defaults. When omitted, the secure-by-default set
22
+ * ({@link DEFAULT_REDACT_KEYS}) is used and string values shaped like a JWT
23
+ * (`eyJ...`) are also replaced.
24
+ *
25
+ * @since 0.15.0
26
+ */
27
+ export interface LoggerRedactionOptions {
28
+ /** Additional case-insensitive keys to redact. Merged with the defaults unless `useDefaults` is `false`. */
29
+ keys?: readonly string[];
30
+ /** Replacement string. Default: `"[REDACTED]"`. */
31
+ censor?: string;
32
+ /** Include the {@link DEFAULT_REDACT_KEYS} list. Default: `true`. */
33
+ useDefaults?: boolean;
34
+ /** Replace string values shaped like a JWT (`eyJ...`) regardless of key. Default: `true`. */
35
+ redactJwtLikeStrings?: boolean;
36
+ /** Maximum recursion depth when walking nested objects. Default: 6. */
37
+ maxDepth?: number;
38
+ }
39
+ /**
40
+ * Default set of header / field names redacted from every structured log
41
+ * record. These are the keys most commonly observed leaking credentials
42
+ * into log aggregators in real-world incidents. Matched case-insensitively.
43
+ *
44
+ * @since 0.15.0
45
+ */
46
+ export declare const DEFAULT_REDACT_KEYS: readonly string[];
18
47
  export interface ConsoleLoggerOptions {
19
48
  level?: LogLevel;
20
49
  bindings?: Record<string, unknown>;
21
50
  /** Where to write. Defaults to process.stdout.write or console.log. */
22
51
  write?: (line: string) => void;
52
+ /**
53
+ * Redact sensitive fields from log records before serialization. Pass
54
+ * `false` to disable the default redaction; pass an options object to
55
+ * extend it. Default: on, with {@link DEFAULT_REDACT_KEYS}.
56
+ *
57
+ * @since 0.15.0
58
+ */
59
+ redact?: LoggerRedactionOptions | false;
60
+ }
61
+ interface ResolvedRedaction {
62
+ keys: Set<string>;
63
+ censor: string;
64
+ redactJwt: boolean;
65
+ maxDepth: number;
23
66
  }
67
+ /**
68
+ * Walk `record` in place, replacing any value whose key (case-insensitive)
69
+ * matches `cfg.keys` and any string value shaped like a JWT (when
70
+ * `cfg.redactJwt` is on) with `cfg.censor`. Exported for direct use by
71
+ * custom logger implementations that want the same defaults.
72
+ *
73
+ * @since 0.15.0
74
+ */
75
+ export declare function redactRecord(record: Record<string, unknown>, cfg: ResolvedRedaction): Record<string, unknown>;
24
76
  /**
25
77
  * Build a structured JSON logger writing one record per line to stdout (or
26
78
  * any sink you supply). Records always include `level`, `time`, and the
@@ -49,4 +101,5 @@ export declare function createLogger(opts?: ConsoleLoggerOptions): Logger;
49
101
  * @since 0.1.0
50
102
  */
51
103
  export declare const noopLogger: Logger;
104
+ export {};
52
105
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE/E,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;CAClD;AAWD,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,uEAAuE;IACvE,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,oBAAyB,GAAG,MAAM,CA6CpE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,EAAE,MAWxB,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE/E,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;CAClD;AAWD;;;;;;;;GAQG;AACH,MAAM,WAAW,sBAAsB;IACrC,4GAA4G;IAC5G,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6FAA6F;IAC7F,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAe/C,CAAC;AAEH,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,uEAAuE;IACvE,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,sBAAsB,GAAG,KAAK,CAAC;CACzC;AAID,UAAU,iBAAiB;IACzB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAmBD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,GAAG,EAAE,iBAAiB,GACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzB;AAuCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,oBAAyB,GAAG,MAAM,CAoDpE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,EAAE,MAWxB,CAAC"}
package/dist/logger.js CHANGED
@@ -12,6 +12,97 @@ const LEVELS = {
12
12
  error: 50,
13
13
  fatal: 60,
14
14
  };
15
+ /**
16
+ * Default set of header / field names redacted from every structured log
17
+ * record. These are the keys most commonly observed leaking credentials
18
+ * into log aggregators in real-world incidents. Matched case-insensitively.
19
+ *
20
+ * @since 0.15.0
21
+ */
22
+ export const DEFAULT_REDACT_KEYS = Object.freeze([
23
+ "authorization",
24
+ "cookie",
25
+ "set-cookie",
26
+ "x-api-key",
27
+ "api-key",
28
+ "apikey",
29
+ "password",
30
+ "passwd",
31
+ "secret",
32
+ "token",
33
+ "access_token",
34
+ "refresh_token",
35
+ "id_token",
36
+ "client_secret",
37
+ ]);
38
+ const JWT_LIKE_RE = /^eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
39
+ function resolveRedaction(opt) {
40
+ if (opt === false)
41
+ return null;
42
+ const cfg = opt ?? {};
43
+ const useDefaults = cfg.useDefaults ?? true;
44
+ const keys = new Set();
45
+ if (useDefaults)
46
+ for (const k of DEFAULT_REDACT_KEYS)
47
+ keys.add(k.toLowerCase());
48
+ if (cfg.keys)
49
+ for (const k of cfg.keys)
50
+ keys.add(k.toLowerCase());
51
+ return {
52
+ keys,
53
+ censor: cfg.censor ?? "[REDACTED]",
54
+ redactJwt: cfg.redactJwtLikeStrings ?? true,
55
+ maxDepth: cfg.maxDepth ?? 6,
56
+ };
57
+ }
58
+ /**
59
+ * Walk `record` in place, replacing any value whose key (case-insensitive)
60
+ * matches `cfg.keys` and any string value shaped like a JWT (when
61
+ * `cfg.redactJwt` is on) with `cfg.censor`. Exported for direct use by
62
+ * custom logger implementations that want the same defaults.
63
+ *
64
+ * @since 0.15.0
65
+ */
66
+ export function redactRecord(record, cfg) {
67
+ walkRedact(record, cfg, 0, new WeakSet());
68
+ return record;
69
+ }
70
+ function walkRedact(node, cfg, depth, seen) {
71
+ if (depth > cfg.maxDepth)
72
+ return;
73
+ if (node === null || typeof node !== "object")
74
+ return;
75
+ if (seen.has(node))
76
+ return;
77
+ seen.add(node);
78
+ if (Array.isArray(node)) {
79
+ for (let i = 0; i < node.length; i++) {
80
+ const v = node[i];
81
+ if (cfg.redactJwt && typeof v === "string" && JWT_LIKE_RE.test(v)) {
82
+ node[i] = cfg.censor;
83
+ }
84
+ else {
85
+ walkRedact(v, cfg, depth + 1, seen);
86
+ }
87
+ }
88
+ return;
89
+ }
90
+ const obj = node;
91
+ for (const key of Object.keys(obj)) {
92
+ const lower = key.toLowerCase();
93
+ if (cfg.keys.has(lower)) {
94
+ obj[key] = cfg.censor;
95
+ continue;
96
+ }
97
+ const v = obj[key];
98
+ if (cfg.redactJwt && typeof v === "string" && JWT_LIKE_RE.test(v)) {
99
+ obj[key] = cfg.censor;
100
+ }
101
+ else {
102
+ walkRedact(v, cfg, depth + 1, seen);
103
+ }
104
+ }
105
+ }
15
106
  /**
16
107
  * Build a structured JSON logger writing one record per line to stdout (or
17
108
  * any sink you supply). Records always include `level`, `time`, and the
@@ -35,6 +126,7 @@ export function createLogger(opts = {}) {
35
126
  const level = opts.level ?? "info";
36
127
  const threshold = LEVELS[level];
37
128
  const bindings = opts.bindings ?? {};
129
+ const redaction = resolveRedaction(opts.redact);
38
130
  const write = opts.write ??
39
131
  (typeof process !== "undefined" && process.stdout?.write
40
132
  ? (line) => {
@@ -57,6 +149,8 @@ export function createLogger(opts = {}) {
57
149
  if (msg !== undefined)
58
150
  base.msg = msg;
59
151
  }
152
+ if (redaction)
153
+ redactRecord(base, redaction);
60
154
  try {
61
155
  write(JSON.stringify(base));
62
156
  }
@@ -73,7 +167,12 @@ export function createLogger(opts = {}) {
73
167
  error: (o, m) => emit("error", o, m),
74
168
  fatal: (o, m) => emit("fatal", o, m),
75
169
  child(extra) {
76
- return createLogger({ level, bindings: { ...bindings, ...extra }, write });
170
+ return createLogger({
171
+ level,
172
+ bindings: { ...bindings, ...extra },
173
+ write,
174
+ redact: opts.redact,
175
+ });
77
176
  },
78
177
  };
79
178
  return logger;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -87,4 +87,90 @@ export declare function timingSafeEqual(a: string, b: string): boolean;
87
87
  * @since 0.1.0
88
88
  */
89
89
  export declare function randomId(): string;
90
+ /**
91
+ * Header names that MUST appear at most once on a request per RFC 7230.
92
+ * Duplicate values for these headers are a classic HTTP-request-smuggling
93
+ * vector — front-end and back-end proxies can disagree on which value
94
+ * "wins", letting an attacker desync the connection.
95
+ *
96
+ * Exported for adapter authors that need the same set.
97
+ *
98
+ * @since 0.15.0
99
+ */
100
+ export declare const SMUGGLING_SINGLETON_HEADERS: readonly string[];
101
+ /**
102
+ * Reject requests that carry more than one value for any header in
103
+ * {@link SMUGGLING_SINGLETON_HEADERS}. Because the standard `Headers`
104
+ * collection normalizes duplicates to a comma-joined string for these
105
+ * fields, the check is "value contains a comma at the top level".
106
+ *
107
+ * Throws {@link BadRequestError} so the framework returns a structured
108
+ * `400 problem+json` instead of forwarding a smuggling-class request.
109
+ *
110
+ * @since 0.15.0
111
+ */
112
+ export declare function assertNoDuplicateSingletonHeaders(headers: Headers): void;
113
+ /**
114
+ * Supported HMAC digest algorithms for {@link verifyWebhookSignature} and
115
+ * {@link signWebhookPayload}. SHA-1 is deliberately excluded — it is
116
+ * cryptographically broken for adversarial collision resistance and
117
+ * unnecessary for new webhook integrations.
118
+ *
119
+ * @since 0.15.0
120
+ */
121
+ export type WebhookHmacAlgorithm = "sha256" | "sha384" | "sha512";
122
+ /**
123
+ * Verify an HMAC signature for a webhook payload in constant time.
124
+ *
125
+ * Accepts the four common signature shapes seen in the wild:
126
+ *
127
+ * - Raw hex (`"a1b2..."`)
128
+ * - Raw base64 / base64url (`"oRY..."`, with or without padding)
129
+ * - Matching GitHub-style prefixed (`"sha256=..."`, `"sha384=..."`, `"sha512=..."`)
130
+ * - A `Uint8Array` of raw signature bytes
131
+ *
132
+ * Returns `false` for every malformed input — never throws on bad
133
+ * signatures so the caller cannot distinguish "wrong format" from "wrong
134
+ * key" through exception side channels.
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * import { verifyWebhookSignature } from "@daloyjs/core";
139
+ *
140
+ * app.route({
141
+ * method: "POST",
142
+ * path: "/webhook",
143
+ * handler: async ({ request }) => {
144
+ * const raw = new Uint8Array(await request.arrayBuffer());
145
+ * const sig = request.headers.get("x-hub-signature-256") ?? "";
146
+ * if (!await verifyWebhookSignature({
147
+ * payload: raw, signature: sig, secret: process.env.WEBHOOK_SECRET!,
148
+ * })) {
149
+ * return { status: 401 as const, body: { ok: false } };
150
+ * }
151
+ * // safe to parse
152
+ * },
153
+ * });
154
+ * ```
155
+ *
156
+ * @since 0.15.0
157
+ */
158
+ export declare function verifyWebhookSignature(opts: {
159
+ payload: Uint8Array | string;
160
+ signature: string | Uint8Array;
161
+ secret: string | Uint8Array;
162
+ algorithm?: WebhookHmacAlgorithm;
163
+ }): Promise<boolean>;
164
+ /**
165
+ * Compute an HMAC signature over `payload` and return it hex-encoded. The
166
+ * companion of {@link verifyWebhookSignature}; useful for tests, for
167
+ * outbound webhook senders, and for diffing implementations.
168
+ *
169
+ * @since 0.15.0
170
+ */
171
+ export declare function signWebhookPayload(opts: {
172
+ payload: Uint8Array | string;
173
+ secret: string | Uint8Array;
174
+ algorithm?: WebhookHmacAlgorithm;
175
+ }): Promise<string>;
90
176
  //# sourceMappingURL=security.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,UAAU,CAAC,CAsCrB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUnD;AAID;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAO7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAUjC"}
1
+ {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,UAAU,CAAC,CAsCrB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUnD;AAID;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAO7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAUjC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,EAAE,SAAS,MAAM,EAGvD,CAAC;AAEH;;;;;;;;;;GAUG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAOxE;AAqFD;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,OAAO,EAAE,UAAU,GAAG,MAAM,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5B,SAAS,CAAC,EAAE,oBAAoB,CAAC;CAClC,GAAG,OAAO,CAAC,OAAO,CAAC,CA8BnB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,OAAO,EAAE,UAAU,GAAG,MAAM,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5B,SAAS,CAAC,EAAE,oBAAoB,CAAC;CAClC,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBlB"}