@blamejs/core 0.9.42 → 0.9.45
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/CHANGELOG.md +3 -0
- package/index.js +2 -0
- package/lib/agent-tenant.js +168 -0
- package/lib/argon2-builtin.js +8 -1
- package/lib/auth/dpop.js +2 -7
- package/lib/auth/jwt.js +3 -7
- package/lib/auth/oauth.js +4 -8
- package/lib/auth/status-list.js +3 -8
- package/lib/crypto.js +61 -0
- package/lib/middleware/compose-pipeline.js +355 -0
- package/lib/middleware/index.js +2 -0
- package/lib/network-dns-resolver.js +2 -1
- package/lib/network-dns.js +4 -3
- package/lib/object-store/gcs.js +2 -6
- package/lib/pagination.js +2 -7
- package/lib/storage.js +417 -0
- package/lib/test-harness.js +275 -0
- package/lib/watcher.js +155 -3
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module b.middleware.composePipeline
|
|
4
|
+
* @nav Middleware
|
|
5
|
+
* @title Compose Pipeline
|
|
6
|
+
* @order 550
|
|
7
|
+
*
|
|
8
|
+
* @intro
|
|
9
|
+
* Order-aware middleware composer. Replaces the per-project pattern
|
|
10
|
+
* of N separate `app.use(mw)` calls — where mount order silently
|
|
11
|
+
* matters (apiEncrypt must precede body-parser; body-parser must
|
|
12
|
+
* precede idempotency-key + csrf; csrf must precede require-auth) —
|
|
13
|
+
* with a single declarative pipeline that documents the order +
|
|
14
|
+
* detects conflicts at registration time.
|
|
15
|
+
*
|
|
16
|
+
* ## What this primitive owns
|
|
17
|
+
*
|
|
18
|
+
* - **Single mount point**: one `app.use(pipeline)` instead of N.
|
|
19
|
+
* - **Order documented in code**: the entry array IS the order;
|
|
20
|
+
* reading the registration tells the reviewer the canonical
|
|
21
|
+
* order without grepping `app.use` calls.
|
|
22
|
+
* - **Conflict detection at registration**: duplicate names refused;
|
|
23
|
+
* duplicate explicit positions refused; non-monotonic positions
|
|
24
|
+
* refused (a later entry with a smaller position is a
|
|
25
|
+
* mis-registration).
|
|
26
|
+
* - **Canonical-position warnings**: when an entry's `name` matches
|
|
27
|
+
* a known framework primitive's recommended position
|
|
28
|
+
* (`apiEncrypt` → 10, `bodyParser` → 20, `csrf` → 30,
|
|
29
|
+
* `idempotency` → 30, `rateLimit` → 40, `requireAuth` → 50,
|
|
30
|
+
* `handler` → 60, `errorHandler` → 90), the composer emits an
|
|
31
|
+
* `system.middleware.compose.canonical_mismatch` audit at warning when
|
|
32
|
+
* the operator-supplied order deviates. Refusal is opt-in via
|
|
33
|
+
* `opts.strict: true`; default is warn-and-continue so operators
|
|
34
|
+
* with intentional non-canonical ordering aren't blocked.
|
|
35
|
+
*
|
|
36
|
+
* ## What this primitive does NOT own
|
|
37
|
+
*
|
|
38
|
+
* - **The middlewares themselves** — the composer is a sequencer,
|
|
39
|
+
* not a registry. Each middleware retains its own
|
|
40
|
+
* `b.middleware.X(opts)` factory + behavior.
|
|
41
|
+
* - **Async-context propagation** — async middleware works (the
|
|
42
|
+
* composer awaits the previous `next()` via Promise wrap), but
|
|
43
|
+
* primitives that need `AsyncLocalStorage` should attach it at
|
|
44
|
+
* the middleware itself, not the composer.
|
|
45
|
+
* - **Error handling** — the composer dispatches through `next(err)`
|
|
46
|
+
* in the standard way; operators register a tail error-handler
|
|
47
|
+
* (`name: "errorHandler"`) for the canonical position 90 slot.
|
|
48
|
+
*
|
|
49
|
+
* ## Audit
|
|
50
|
+
*
|
|
51
|
+
* Each composed pipeline is registered at boot time with a unique
|
|
52
|
+
* `pipelineId` (sha3-512 of the sorted entry names) and emits a
|
|
53
|
+
* `system.middleware.compose.pipeline_built` audit with the entry list
|
|
54
|
+
* and canonical-mismatch flags. Per-request dispatch is NOT
|
|
55
|
+
* audited (would blow up the audit pipeline volume) — composers
|
|
56
|
+
* that need per-request observability compose `b.observability`
|
|
57
|
+
* inside their own middleware.
|
|
58
|
+
*
|
|
59
|
+
* @card
|
|
60
|
+
* Order-aware middleware composer. Single mount point replacing N app.use calls, with conflict detection at registration + canonical-position warnings for framework middlewares. Operator's pipeline order documented in code; the entry array IS the order.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
var bCrypto = require("../crypto");
|
|
64
|
+
var { defineClass } = require("../framework-error");
|
|
65
|
+
var lazyRequire = require("../lazy-require");
|
|
66
|
+
var validateOpts = require("../validate-opts");
|
|
67
|
+
|
|
68
|
+
var audit = lazyRequire(function () { return require("../audit"); });
|
|
69
|
+
|
|
70
|
+
var ComposePipelineError = defineClass("ComposePipelineError", { alwaysPermanent: true });
|
|
71
|
+
|
|
72
|
+
// Canonical positions for framework middlewares. The composer
|
|
73
|
+
// surfaces mismatches as warnings; refusal is opt-in via
|
|
74
|
+
// `opts.strict: true`. Operators with intentional non-canonical
|
|
75
|
+
// ordering aren't blocked.
|
|
76
|
+
//
|
|
77
|
+
// Position groupings (relative — exact numbers can drift, the
|
|
78
|
+
// classes are what matter):
|
|
79
|
+
// < 10 : request-id, connection-tracking (must be earliest)
|
|
80
|
+
// 10-19 : api-encrypt (must decrypt before any read)
|
|
81
|
+
// 20-29 : body-parser (must precede idempotency, csrf, validation)
|
|
82
|
+
// 30-39 : csrf, idempotency, header policy (need body parsed)
|
|
83
|
+
// 40-49 : rate-limit, bot-guard (after auth context if any)
|
|
84
|
+
// 50-59 : require-auth, ACL (after body + csrf)
|
|
85
|
+
// 60-89 : application handlers
|
|
86
|
+
// >= 90 : error-handler (must be last; trailing catch)
|
|
87
|
+
var CANONICAL_POSITIONS = Object.freeze({
|
|
88
|
+
requestId: 5, // allow:raw-byte-literal — canonical position bucket
|
|
89
|
+
apiEncrypt: 10, // allow:raw-byte-literal — canonical position bucket
|
|
90
|
+
bodyParser: 20, // allow:raw-byte-literal — canonical position bucket
|
|
91
|
+
cspNonce: 22, // allow:raw-byte-literal — canonical position bucket
|
|
92
|
+
securityHeaders: 25, // allow:raw-byte-literal — canonical position bucket
|
|
93
|
+
csrf: 30, // allow:raw-byte-literal — canonical position bucket
|
|
94
|
+
idempotency: 30, // allow:raw-byte-literal — canonical position bucket (same as csrf)
|
|
95
|
+
fetchMetadata: 32, // allow:raw-byte-literal — canonical position bucket
|
|
96
|
+
rateLimit: 40, // allow:raw-byte-literal — canonical position bucket
|
|
97
|
+
botGuard: 42, // allow:raw-byte-literal — canonical position bucket
|
|
98
|
+
requireAuth: 50, // allow:raw-byte-literal — canonical position bucket
|
|
99
|
+
attachUser: 52, // allow:raw-byte-literal — canonical position bucket
|
|
100
|
+
handler: 60, // allow:raw-byte-literal — canonical position bucket // allow:raw-time-literal — pipeline position int, not seconds
|
|
101
|
+
errorHandler: 90, // allow:raw-byte-literal — canonical position bucket
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @primitive b.middleware.composePipeline
|
|
106
|
+
* @signature b.middleware.composePipeline(entries, opts?)
|
|
107
|
+
* @since 0.9.44
|
|
108
|
+
* @status stable
|
|
109
|
+
* @related b.middleware.requestId, b.middleware.requireAuth, b.middleware.idempotencyKey
|
|
110
|
+
*
|
|
111
|
+
* Compose an ordered middleware pipeline into a single Express-shaped
|
|
112
|
+
* middleware. Each `entries[i]` is `{ name: string, mw: function,
|
|
113
|
+
* position?: number }`. Returns the composed `(req, res, next) =>
|
|
114
|
+
* void` middleware. Throws at registration time on duplicate names,
|
|
115
|
+
* duplicate positions, non-monotonic positions, or (with strict)
|
|
116
|
+
* canonical-position mismatches.
|
|
117
|
+
*
|
|
118
|
+
* @opts
|
|
119
|
+
* strict: boolean, // refuse on canonical-position mismatch (default false: warn-and-continue)
|
|
120
|
+
* name: string, // optional pipeline name for audit
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* var pipeline = b.middleware.composePipeline([
|
|
124
|
+
* { name: "apiEncrypt", mw: apiEncryptMw },
|
|
125
|
+
* { name: "bodyParser", mw: bodyParserMw },
|
|
126
|
+
* { name: "csrf", mw: csrfMw },
|
|
127
|
+
* { name: "idempotency", mw: idempotencyMw, position: 35 },
|
|
128
|
+
* { name: "requireAuth", mw: requireAuthMw },
|
|
129
|
+
* ]);
|
|
130
|
+
* app.use(pipeline);
|
|
131
|
+
*/
|
|
132
|
+
function composePipeline(entries, opts) {
|
|
133
|
+
opts = opts || {};
|
|
134
|
+
validateOpts.optionalBoolean(opts.strict, "composePipeline.strict",
|
|
135
|
+
ComposePipelineError, "compose-pipeline/bad-strict");
|
|
136
|
+
|
|
137
|
+
if (!Array.isArray(entries)) {
|
|
138
|
+
throw new ComposePipelineError("compose-pipeline/bad-entries",
|
|
139
|
+
"composePipeline: entries must be an array of { name, mw, position? } objects");
|
|
140
|
+
}
|
|
141
|
+
if (entries.length === 0) {
|
|
142
|
+
throw new ComposePipelineError("compose-pipeline/bad-entries",
|
|
143
|
+
"composePipeline: entries must contain at least one middleware");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
var seenNames = Object.create(null);
|
|
147
|
+
var seenPositions = Object.create(null);
|
|
148
|
+
var canonicalMismatches = [];
|
|
149
|
+
var resolved = [];
|
|
150
|
+
|
|
151
|
+
for (var i = 0; i < entries.length; i += 1) {
|
|
152
|
+
var e = entries[i];
|
|
153
|
+
if (!e || typeof e !== "object") {
|
|
154
|
+
throw new ComposePipelineError("compose-pipeline/bad-entry",
|
|
155
|
+
"composePipeline: entry at index " + i + " must be an object");
|
|
156
|
+
}
|
|
157
|
+
if (typeof e.name !== "string" || e.name.length === 0 || e.name.length > 64) { // allow:raw-byte-literal — middleware-name cap
|
|
158
|
+
throw new ComposePipelineError("compose-pipeline/bad-entry",
|
|
159
|
+
"composePipeline: entries[" + i + "].name must be a non-empty string ≤ 64 bytes");
|
|
160
|
+
}
|
|
161
|
+
if (typeof e.mw !== "function") {
|
|
162
|
+
throw new ComposePipelineError("compose-pipeline/bad-entry",
|
|
163
|
+
"composePipeline: entries[" + i + "].mw must be a function (got " + typeof e.mw + ")");
|
|
164
|
+
}
|
|
165
|
+
if (seenNames[e.name]) {
|
|
166
|
+
throw new ComposePipelineError("compose-pipeline/duplicate-name",
|
|
167
|
+
"composePipeline: duplicate entry name '" + e.name + "' at index " + i);
|
|
168
|
+
}
|
|
169
|
+
seenNames[e.name] = true;
|
|
170
|
+
|
|
171
|
+
var position;
|
|
172
|
+
if (e.position !== undefined) {
|
|
173
|
+
if (typeof e.position !== "number" || !Number.isFinite(e.position) || e.position < 0) {
|
|
174
|
+
throw new ComposePipelineError("compose-pipeline/bad-position",
|
|
175
|
+
"composePipeline: entries[" + i + "].position must be a non-negative finite number");
|
|
176
|
+
}
|
|
177
|
+
position = e.position;
|
|
178
|
+
} else if (Object.prototype.hasOwnProperty.call(CANONICAL_POSITIONS, e.name)) {
|
|
179
|
+
// Operator-supplied name matches a canonical framework
|
|
180
|
+
// middleware — use the canonical position by default. Operator
|
|
181
|
+
// can still override by passing an explicit position.
|
|
182
|
+
position = CANONICAL_POSITIONS[e.name];
|
|
183
|
+
} else {
|
|
184
|
+
// Operator-defined middleware without explicit position —
|
|
185
|
+
// synthesize a position from the array index times 100 so
|
|
186
|
+
// operators that don't care about explicit ordering get a
|
|
187
|
+
// natural sequential flow. Use 100 so canonical positions
|
|
188
|
+
// (5..90) can interleave without colliding when an operator
|
|
189
|
+
// mixes named + unnamed entries.
|
|
190
|
+
position = (i + 1) * 100; // allow:raw-byte-literal — index→position scale; canonical-pos ceiling is 90
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (Object.prototype.hasOwnProperty.call(seenPositions, position)) {
|
|
194
|
+
// Same explicit position from two entries — refuse (would make
|
|
195
|
+
// dispatch order undefined). Two canonical entries CAN land at
|
|
196
|
+
// the same position (csrf + idempotency are both 30); allowed
|
|
197
|
+
// only when operator didn't supply an explicit position for
|
|
198
|
+
// either. Surface this via a less-severe error code so
|
|
199
|
+
// operators with intentional ties can override with explicit
|
|
200
|
+
// distinct positions.
|
|
201
|
+
var prevName = seenPositions[position];
|
|
202
|
+
var bothExplicit = entries[_findIndex(resolved, prevName)] &&
|
|
203
|
+
entries[_findIndex(resolved, prevName)].position !== undefined &&
|
|
204
|
+
e.position !== undefined;
|
|
205
|
+
if (bothExplicit) {
|
|
206
|
+
throw new ComposePipelineError("compose-pipeline/duplicate-position",
|
|
207
|
+
"composePipeline: entries[" + i + "].position=" + position +
|
|
208
|
+
" collides with '" + prevName + "'; supply explicit distinct positions to disambiguate");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
seenPositions[position] = e.name;
|
|
212
|
+
|
|
213
|
+
if (resolved.length > 0 && position < resolved[resolved.length - 1].position) {
|
|
214
|
+
throw new ComposePipelineError("compose-pipeline/non-monotonic",
|
|
215
|
+
"composePipeline: entries[" + i + "] ('" + e.name + "', position=" + position +
|
|
216
|
+
") declared before entries with higher position; entries must be in non-decreasing position order");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (Object.prototype.hasOwnProperty.call(CANONICAL_POSITIONS, e.name) &&
|
|
220
|
+
e.position !== undefined && e.position !== CANONICAL_POSITIONS[e.name]) {
|
|
221
|
+
canonicalMismatches.push({
|
|
222
|
+
name: e.name,
|
|
223
|
+
suppliedPosition: e.position,
|
|
224
|
+
canonicalPosition: CANONICAL_POSITIONS[e.name],
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
resolved.push({ name: e.name, mw: e.mw, position: position });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (canonicalMismatches.length > 0) {
|
|
232
|
+
if (opts.strict === true) {
|
|
233
|
+
throw new ComposePipelineError("compose-pipeline/canonical-mismatch",
|
|
234
|
+
"composePipeline: strict=true; " + canonicalMismatches.length +
|
|
235
|
+
" canonical-position mismatch(es): " +
|
|
236
|
+
canonicalMismatches.map(function (m) {
|
|
237
|
+
return m.name + " supplied=" + m.suppliedPosition + " canonical=" + m.canonicalPosition;
|
|
238
|
+
}).join(", "));
|
|
239
|
+
}
|
|
240
|
+
_emitAudit("system.middleware.compose.canonical_mismatch", {
|
|
241
|
+
pipelineName: opts.name || null,
|
|
242
|
+
mismatches: canonicalMismatches,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
var pipelineId = bCrypto.namespaceHash("system.middleware.compose.pipeline",
|
|
247
|
+
resolved.map(function (r) { return r.name; }).join("\n"));
|
|
248
|
+
|
|
249
|
+
_emitAudit("system.middleware.compose.pipeline_built", {
|
|
250
|
+
pipelineId: pipelineId,
|
|
251
|
+
pipelineName: opts.name || null,
|
|
252
|
+
entryCount: resolved.length,
|
|
253
|
+
entries: resolved.map(function (r) { return { name: r.name, position: r.position }; }),
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Composed middleware — sequentially invokes each entry's mw via
|
|
257
|
+
// next() chaining. Standard Express idiom: each mw receives
|
|
258
|
+
// (req, res, next) and either calls next() to continue or
|
|
259
|
+
// next(err) to bail out to the error-handler.
|
|
260
|
+
//
|
|
261
|
+
// composedPipeline returns a Promise that resolves AFTER `finalNext`
|
|
262
|
+
// has been called by the chain. The framework router awaits this
|
|
263
|
+
// promise; without it, async middleware (bodyParser / apiEncrypt
|
|
264
|
+
// reading the request stream) leave the router with `next` still
|
|
265
|
+
// false when composedPipeline returns synchronously, and the
|
|
266
|
+
// router exits the request before the chain has actually advanced.
|
|
267
|
+
//
|
|
268
|
+
// Middleware do NOT await next() — that's the Express contract.
|
|
269
|
+
// So we can't rely on `await entry.mw(...)` to wait for the rest
|
|
270
|
+
// of the chain. Instead, the outer Promise resolves only when the
|
|
271
|
+
// chain reaches its end via finalNext, regardless of how many
|
|
272
|
+
// hops of async middleware have happened along the way.
|
|
273
|
+
//
|
|
274
|
+
// When a middleware calls `next(err)`, the chain skips non-error
|
|
275
|
+
// middleware (3-arg) and dispatches the error to the first 4-arg
|
|
276
|
+
// entry (`(err, req, res, next)` — Express's error-handler shape).
|
|
277
|
+
// If no error-handler entry is found, `finalNext(err)` carries the
|
|
278
|
+
// error up to the framework router.
|
|
279
|
+
return function composedPipeline(req, res, finalNext) {
|
|
280
|
+
return new Promise(function (resolve, reject) {
|
|
281
|
+
var idx = 0;
|
|
282
|
+
var finished = false;
|
|
283
|
+
function _finishOnce(err) {
|
|
284
|
+
if (finished) return;
|
|
285
|
+
finished = true;
|
|
286
|
+
try { finalNext(err); }
|
|
287
|
+
catch (finalErr) { return reject(finalErr); }
|
|
288
|
+
resolve();
|
|
289
|
+
}
|
|
290
|
+
async function dispatch(err) {
|
|
291
|
+
if (finished) return;
|
|
292
|
+
if (idx >= resolved.length) return _finishOnce(err);
|
|
293
|
+
var entry = resolved[idx];
|
|
294
|
+
idx += 1;
|
|
295
|
+
// 4-arg entries are error handlers (Express convention).
|
|
296
|
+
// Regular entries run on the success path; error entries on
|
|
297
|
+
// the error path. Skip entries that don't match the current
|
|
298
|
+
// path until one matches OR the chain ends.
|
|
299
|
+
var isErrorHandler = entry.mw.length === 4;
|
|
300
|
+
if (err && !isErrorHandler) return dispatch(err);
|
|
301
|
+
if (!err && isErrorHandler) return dispatch();
|
|
302
|
+
// Track whether the middleware called its next() argument.
|
|
303
|
+
// If a 4-arg error handler runs to completion without calling
|
|
304
|
+
// next, the error is considered handled and the chain ends
|
|
305
|
+
// cleanly (Express convention). Same applies to a 3-arg
|
|
306
|
+
// middleware that doesn't call next — the chain stops, but
|
|
307
|
+
// we don't invoke finalNext to avoid the router proceeding
|
|
308
|
+
// to the route handler after a middleware decided to halt.
|
|
309
|
+
var advanced = false;
|
|
310
|
+
function _next(passErr) {
|
|
311
|
+
advanced = true;
|
|
312
|
+
return dispatch(passErr);
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
if (err) {
|
|
316
|
+
// Error handler: (err, req, res, next)
|
|
317
|
+
await entry.mw(err, req, res, _next);
|
|
318
|
+
if (!advanced) _finishOnce();
|
|
319
|
+
} else {
|
|
320
|
+
// Regular middleware: (req, res, next)
|
|
321
|
+
await entry.mw(req, res, _next);
|
|
322
|
+
// 3-arg middleware that doesn't call next — chain halts.
|
|
323
|
+
// The middleware presumably wrote the response itself.
|
|
324
|
+
}
|
|
325
|
+
} catch (syncErr) {
|
|
326
|
+
// Synchronous throw OR rejected promise — route through
|
|
327
|
+
// the error path so a downstream error-handler can format
|
|
328
|
+
// the response.
|
|
329
|
+
dispatch(syncErr).catch(reject);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
dispatch().catch(reject);
|
|
333
|
+
});
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
composePipeline.CANONICAL_POSITIONS = CANONICAL_POSITIONS;
|
|
338
|
+
composePipeline.ComposePipelineError = ComposePipelineError;
|
|
339
|
+
|
|
340
|
+
function _emitAudit(action, metadata) {
|
|
341
|
+
try {
|
|
342
|
+
if (audit && typeof audit().safeEmit === "function") {
|
|
343
|
+
audit().safeEmit({ action: action, outcome: "success", metadata: metadata });
|
|
344
|
+
}
|
|
345
|
+
} catch (_e) { /* drop-silent — audit failure must not break pipeline registration */ }
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function _findIndex(arr, name) {
|
|
349
|
+
for (var i = 0; i < arr.length; i += 1) {
|
|
350
|
+
if (arr[i].name === name) return i;
|
|
351
|
+
}
|
|
352
|
+
return -1;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
module.exports = composePipeline;
|
package/lib/middleware/index.js
CHANGED
|
@@ -68,6 +68,7 @@ var protectedResourceMetadata = require("./protected-resource-metadata");
|
|
|
68
68
|
var scimServer = require("./scim-server");
|
|
69
69
|
var idempotencyKey = require("./idempotency-key");
|
|
70
70
|
var noCache = require("./no-cache");
|
|
71
|
+
var composePipeline = require("./compose-pipeline");
|
|
71
72
|
|
|
72
73
|
module.exports = {
|
|
73
74
|
requestId: requestId.create,
|
|
@@ -126,6 +127,7 @@ module.exports = {
|
|
|
126
127
|
IdempotencyError: idempotencyKey.IdempotencyError,
|
|
127
128
|
}),
|
|
128
129
|
noCache: noCache.create,
|
|
130
|
+
composePipeline: composePipeline,
|
|
129
131
|
|
|
130
132
|
// Module exports for advanced use (constants, raw factory access)
|
|
131
133
|
_modules: {
|
|
@@ -107,6 +107,7 @@
|
|
|
107
107
|
var C = require("./constants");
|
|
108
108
|
var https = require("node:https");
|
|
109
109
|
var nodeCrypto = require("node:crypto");
|
|
110
|
+
var bCrypto = require("./crypto");
|
|
110
111
|
var { defineClass } = require("./framework-error");
|
|
111
112
|
var networkDns = require("./network-dns");
|
|
112
113
|
var safeDns = require("./safe-dns");
|
|
@@ -413,7 +414,7 @@ async function _wireLookup(name, qtype) {
|
|
|
413
414
|
var url = networkDns._getDohUrlForTest ? networkDns._getDohUrlForTest() : "https://cloudflare-dns.com/dns-query";
|
|
414
415
|
// Encode a wire-format query for the target qtype.
|
|
415
416
|
var qbuf = _encodeWireQuery(name, qtype);
|
|
416
|
-
var b64 =
|
|
417
|
+
var b64 = bCrypto.toBase64Url(qbuf);
|
|
417
418
|
var getUrl = url + (url.indexOf("?") === -1 ? "?" : "&") + "dns=" + b64;
|
|
418
419
|
var u = safeUrl.parse(getUrl, { allowedProtocols: safeUrl.ALLOW_HTTP_TLS });
|
|
419
420
|
return new Promise(function (resolve, reject) {
|
package/lib/network-dns.js
CHANGED
|
@@ -8,6 +8,7 @@ var nodeTls = require("node:tls");
|
|
|
8
8
|
var dnsPromises = dns.promises;
|
|
9
9
|
|
|
10
10
|
var C = require("./constants");
|
|
11
|
+
var bCrypto = require("./crypto");
|
|
11
12
|
var lazyRequire = require("./lazy-require");
|
|
12
13
|
var safeBuffer = require("./safe-buffer");
|
|
13
14
|
var safeUrl = require("./safe-url");
|
|
@@ -368,7 +369,7 @@ var DOH_GET_URL_MAX_BYTES = 2048;
|
|
|
368
369
|
async function _dohLookup(host, family) {
|
|
369
370
|
var qtype = family === 6 ? 28 : 1;
|
|
370
371
|
var enc = _encodeDnsQuery(host, qtype);
|
|
371
|
-
var b64 = enc.buf
|
|
372
|
+
var b64 = bCrypto.toBase64Url(enc.buf);
|
|
372
373
|
var getUrl = STATE.doh.url + (STATE.doh.url.indexOf("?") === -1 ? "?" : "&") + "dns=" + b64;
|
|
373
374
|
var forcedMethod = STATE.doh.method;
|
|
374
375
|
var usePost = forcedMethod === "POST" || (!forcedMethod && getUrl.length > DOH_GET_URL_MAX_BYTES);
|
|
@@ -437,7 +438,7 @@ async function _dohLookup(host, family) {
|
|
|
437
438
|
async function _dohLookupSecure(host, family) {
|
|
438
439
|
var qtype = family === 6 ? 28 : 1; // allow:raw-byte-literal — DNS QTYPE values for A / AAAA
|
|
439
440
|
var enc = _encodeDnsQuery(host, qtype);
|
|
440
|
-
var b64 = enc.buf
|
|
441
|
+
var b64 = bCrypto.toBase64Url(enc.buf);
|
|
441
442
|
var getUrl = STATE.doh.url + (STATE.doh.url.indexOf("?") === -1 ? "?" : "&") + "dns=" + b64;
|
|
442
443
|
var forcedMethod = STATE.doh.method;
|
|
443
444
|
var usePost = forcedMethod === "POST" || (!forcedMethod && getUrl.length > DOH_GET_URL_MAX_BYTES);
|
|
@@ -792,7 +793,7 @@ function _decodeDnsAnswerRaw(buf) {
|
|
|
792
793
|
|
|
793
794
|
async function _dohRawQuery(host, qtype) {
|
|
794
795
|
var enc = _encodeDnsQuery(host, qtype);
|
|
795
|
-
var b64 = enc.buf
|
|
796
|
+
var b64 = bCrypto.toBase64Url(enc.buf);
|
|
796
797
|
var getUrl = STATE.doh.url + (STATE.doh.url.indexOf("?") === -1 ? "?" : "&") + "dns=" + b64;
|
|
797
798
|
var forcedMethod = STATE.doh.method;
|
|
798
799
|
var usePost = forcedMethod === "POST" || (!forcedMethod && getUrl.length > DOH_GET_URL_MAX_BYTES);
|
package/lib/object-store/gcs.js
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
var nodeFs = require("node:fs");
|
|
26
26
|
var nodeCrypto = require("node:crypto");
|
|
27
27
|
var { Readable } = require("node:stream");
|
|
28
|
+
var bCrypto = require("../crypto");
|
|
28
29
|
var safeJson = require("../safe-json");
|
|
29
30
|
var C = require("../constants");
|
|
30
31
|
var numericBounds = require("../numeric-bounds");
|
|
@@ -88,12 +89,7 @@ var _httpRequest = sharedRequest;
|
|
|
88
89
|
|
|
89
90
|
// ---- JWT signing for service-account auth ----
|
|
90
91
|
|
|
91
|
-
function _base64UrlEncode(buf) {
|
|
92
|
-
return Buffer.from(buf).toString("base64")
|
|
93
|
-
.replace(/=+$/g, "")
|
|
94
|
-
.replace(/\+/g, "-")
|
|
95
|
-
.replace(/\//g, "_");
|
|
96
|
-
}
|
|
92
|
+
function _base64UrlEncode(buf) { return bCrypto.toBase64Url(buf); }
|
|
97
93
|
|
|
98
94
|
function _signJwt(serviceAccount, scope, audience) {
|
|
99
95
|
var nowSec = Math.floor(Date.now() / C.TIME.seconds(1));
|
package/lib/pagination.js
CHANGED
|
@@ -83,16 +83,11 @@ function _toBuf(secret) {
|
|
|
83
83
|
"secret must be a Buffer or non-empty string");
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function _b64urlEncode(buf) {
|
|
87
|
-
var b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
|
|
88
|
-
return b.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
89
|
-
}
|
|
86
|
+
function _b64urlEncode(buf) { return bCrypto.toBase64Url(buf); }
|
|
90
87
|
|
|
91
88
|
function _b64urlDecode(s) {
|
|
92
89
|
if (typeof s !== "string") throw new PaginationError("pagination/bad-cursor", "cursor must be a string");
|
|
93
|
-
|
|
94
|
-
var padded = pad ? s + "=".repeat(4 - pad) : s;
|
|
95
|
-
return Buffer.from(padded.replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
90
|
+
return bCrypto.fromBase64Url(s);
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
function _tag(secretBuf, stateJson) {
|