@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.
- package/README.md +11 -3
- package/dist/app.d.ts +29 -2
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +37 -11
- package/dist/app.js.map +1 -1
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +28 -3
- package/dist/cli.js.map +1 -1
- package/dist/hashing.d.ts +59 -0
- package/dist/hashing.d.ts.map +1 -0
- package/dist/hashing.js +154 -0
- package/dist/hashing.js.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +53 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +100 -1
- package/dist/logger.js.map +1 -1
- package/dist/security.d.ts +86 -0
- package/dist/security.d.ts.map +1 -1
- package/dist/security.js +185 -0
- package/dist/security.js.map +1 -1
- package/package.json +7 -3
|
@@ -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"}
|
package/dist/hashing.js
ADDED
|
@@ -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";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","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;AAC1D,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,GACd,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,eAAe,EACf,UAAU,EACV,UAAU,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,KAAK,EACL,WAAW,EACX,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,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;AACrB,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EACL,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","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;AAC1D,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,GACd,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,eAAe,EACf,UAAU,EACV,UAAU,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,KAAK,EACL,WAAW,EACX,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,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;AACrB,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,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;AACvB,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,EACL,SAAS,EACT,aAAa,EACb,IAAI,EACJ,SAAS,EACT,MAAM,EACN,UAAU,EACV,SAAS,EACT,IAAI,GACL,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC5E,YAAY,EACV,MAAM,EACN,QAAQ,EACR,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,4BAA4B,EAC5B,WAAW,GACZ,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtE,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAE3E,OAAO,EACL,SAAS,EACT,WAAW,EACX,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,uBAAuB,EACvB,2BAA2B,EAC3B,2BAA2B,EAC3B,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,0BAA0B,EAC1B,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACvE,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,YAAY,EACZ,YAAY,GACb,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,WAAW,EACX,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,WAAW,EACX,uBAAuB,EACvB,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,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;AACxB,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,WAAW,EACX,YAAY,IAAI,qBAAqB,EACrC,eAAe,GAChB,MAAM,gBAAgB,CAAC"}
|
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,
|
|
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
|
package/dist/logger.d.ts.map
CHANGED
|
@@ -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;
|
|
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({
|
|
170
|
+
return createLogger({
|
|
171
|
+
level,
|
|
172
|
+
bindings: { ...bindings, ...extra },
|
|
173
|
+
write,
|
|
174
|
+
redact: opts.redact,
|
|
175
|
+
});
|
|
77
176
|
},
|
|
78
177
|
};
|
|
79
178
|
return logger;
|
package/dist/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,MAAM,MAAM,GAA6B;IACvC,KAAK,EAAE,EAAE;IACT,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,EAAE;IACR,IAAI,EAAE,EAAE;IACR,KAAK,EAAE,EAAE;IACT,KAAK,EAAE,EAAE;CACV,CAAC;
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
|
package/dist/security.d.ts
CHANGED
|
@@ -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
|
package/dist/security.d.ts.map
CHANGED
|
@@ -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"}
|