@blamejs/core 0.7.18 → 0.7.20
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 +427 -423
- package/README.md +150 -150
- package/bin/blamejs.js +0 -0
- package/index.js +310 -308
- package/lib/api-key.js +660 -660
- package/lib/api-snapshot.js +338 -338
- package/lib/app-shutdown.js +385 -385
- package/lib/app.js +365 -365
- package/lib/archive.js +250 -250
- package/lib/atomic-file.js +544 -544
- package/lib/audit-chain.js +177 -177
- package/lib/audit-sign.js +344 -344
- package/lib/audit-tools.js +677 -677
- package/lib/audit.js +766 -766
- package/lib/auth/jwt-external.js +365 -0
- package/lib/auth/jwt.js +337 -311
- package/lib/auth/lockout.js +436 -436
- package/lib/auth/oauth.js +721 -721
- package/lib/auth/passkey.js +181 -181
- package/lib/auth/password.js +628 -594
- package/lib/backup/bundle.js +217 -217
- package/lib/backup/crypto.js +176 -176
- package/lib/backup/index.js +515 -515
- package/lib/backup/manifest.js +282 -282
- package/lib/break-glass.js +1338 -1338
- package/lib/bundler.js +441 -441
- package/lib/cache-redis.js +256 -256
- package/lib/cache.js +1206 -1206
- package/lib/canonical-json.js +115 -115
- package/lib/chain-writer.js +234 -234
- package/lib/cli-helpers.js +206 -206
- package/lib/cli.js +2334 -2334
- package/lib/cluster-provider-db.js +317 -317
- package/lib/cluster-storage.js +226 -226
- package/lib/cluster.js +703 -703
- package/lib/config-drift.js +301 -301
- package/lib/consent.js +222 -222
- package/lib/constants.js +191 -191
- package/lib/cookies.js +350 -315
- package/lib/credential-hash.js +322 -322
- package/lib/crypto.js +266 -266
- package/lib/csv.js +275 -275
- package/lib/db-declare-row-policy.js +267 -267
- package/lib/db-declare-view.js +420 -420
- package/lib/db-query.js +406 -406
- package/lib/db-schema.js +319 -319
- package/lib/db.js +1288 -1288
- package/lib/deprecate.js +222 -222
- package/lib/dev.js +335 -335
- package/lib/dual-control.js +473 -473
- package/lib/error-page.js +420 -420
- package/lib/external-db-migrate.js +441 -441
- package/lib/external-db.js +1061 -1061
- package/lib/file-type.js +273 -273
- package/lib/forms.js +422 -422
- package/lib/framework-error.js +293 -293
- package/lib/framework-schema.js +717 -717
- package/lib/handlers.js +350 -350
- package/lib/http-client-cookie-jar.js +508 -508
- package/lib/http-client.js +1195 -1195
- package/lib/i18n.js +878 -878
- package/lib/jobs.js +185 -185
- package/lib/log-stream-cloudwatch.js +369 -369
- package/lib/log-stream-local.js +146 -146
- package/lib/log-stream-otlp-grpc.js +410 -410
- package/lib/log-stream-otlp.js +286 -286
- package/lib/log-stream-syslog.js +302 -302
- package/lib/log-stream-webhook.js +199 -199
- package/lib/log-stream.js +330 -330
- package/lib/log.js +500 -500
- package/lib/mail-bounce.js +528 -528
- package/lib/mail-dkim.js +369 -369
- package/lib/mail.js +981 -981
- package/lib/metrics.js +683 -683
- package/lib/middleware/api-encrypt.js +936 -936
- package/lib/middleware/attach-user.js +157 -157
- package/lib/middleware/bearer-auth.js +152 -0
- package/lib/middleware/body-parser.js +1170 -1170
- package/lib/middleware/bot-guard.js +178 -178
- package/lib/middleware/compression.js +452 -452
- package/lib/middleware/cors.js +314 -314
- package/lib/middleware/csp-nonce.js +348 -348
- package/lib/middleware/csrf-protect.js +399 -316
- package/lib/middleware/db-role-for.js +264 -264
- package/lib/middleware/fetch-metadata.js +129 -0
- package/lib/middleware/health.js +392 -392
- package/lib/middleware/index.js +85 -79
- package/lib/middleware/rate-limit.js +358 -358
- package/lib/middleware/request-id.js +61 -61
- package/lib/middleware/request-log.js +168 -168
- package/lib/middleware/require-auth.js +104 -104
- package/lib/middleware/security-headers.js +121 -116
- package/lib/middleware/sse.js +166 -166
- package/lib/migrations.js +383 -383
- package/lib/mtls-ca.js +518 -518
- package/lib/mtls-engine-default.js +481 -481
- package/lib/network-dns.js +632 -632
- package/lib/network-heartbeat.js +290 -290
- package/lib/network-nts.js +574 -574
- package/lib/network-proxy.js +265 -265
- package/lib/network-tls.js +328 -328
- package/lib/network.js +233 -233
- package/lib/notify.js +612 -612
- package/lib/ntp-check.js +229 -229
- package/lib/numeric-bounds.js +111 -111
- package/lib/object-store/azure-blob-bucket-ops.js +349 -349
- package/lib/object-store/azure-blob.js +488 -488
- package/lib/object-store/gcs-bucket-ops.js +351 -351
- package/lib/object-store/gcs.js +519 -519
- package/lib/object-store/http-put.js +153 -153
- package/lib/object-store/index.js +197 -197
- package/lib/object-store/sigv4-bucket-ops.js +1092 -1092
- package/lib/object-store/sigv4.js +903 -903
- package/lib/observability.js +151 -151
- package/lib/otel-export.js +269 -269
- package/lib/pagination.js +464 -464
- package/lib/parsers/index.js +80 -80
- package/lib/parsers/safe-env.js +642 -642
- package/lib/parsers/safe-ini.js +292 -292
- package/lib/parsers/safe-toml.js +784 -784
- package/lib/parsers/safe-xml.js +390 -390
- package/lib/parsers/safe-yaml.js +1015 -1015
- package/lib/permissions.js +708 -708
- package/lib/pqc-agent.js +87 -87
- package/lib/pqc-gate.js +279 -279
- package/lib/protobuf-encoder.js +190 -190
- package/lib/protocol-dispatcher.js +161 -161
- package/lib/pubsub-redis.js +167 -167
- package/lib/pubsub.js +429 -429
- package/lib/queue-local.js +476 -476
- package/lib/queue-redis.js +745 -745
- package/lib/queue-sqs.js +319 -319
- package/lib/queue.js +695 -695
- package/lib/redis-client.js +519 -519
- package/lib/request-helpers.js +340 -340
- package/lib/restore-bundle.js +237 -237
- package/lib/restore-rollback.js +259 -259
- package/lib/restore.js +409 -409
- package/lib/retry.js +376 -376
- package/lib/router.js +748 -748
- package/lib/safe-async.js +735 -735
- package/lib/safe-buffer.js +237 -237
- package/lib/safe-json.js +541 -541
- package/lib/safe-schema.js +1266 -1266
- package/lib/safe-url.js +159 -159
- package/lib/scheduler.js +706 -706
- package/lib/security-assert.js +373 -373
- package/lib/seeders.js +618 -618
- package/lib/session.js +535 -478
- package/lib/slug.js +269 -269
- package/lib/ssrf-guard.js +401 -401
- package/lib/static.js +7 -5
- package/lib/storage.js +471 -471
- package/lib/subject.js +281 -281
- package/lib/template.js +791 -791
- package/lib/testing.js +798 -798
- package/lib/time.js +310 -310
- package/lib/totp.js +302 -302
- package/lib/tracing.js +494 -494
- package/lib/uuid.js +132 -132
- package/lib/validate-opts.js +340 -340
- package/lib/vault/index.js +308 -308
- package/lib/vault/rotate.js +784 -784
- package/lib/vault/wrap.js +296 -296
- package/lib/vendor/noble-ciphers.cjs +9 -9
- package/lib/webhook.js +595 -595
- package/lib/websocket.js +1048 -1048
- package/package.json +77 -77
- package/sbom.cyclonedx.json +7 -7
package/lib/safe-url.js
CHANGED
|
@@ -1,159 +1,159 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* URL-safe — validate URL scheme + shape against an allowlist.
|
|
4
|
-
*
|
|
5
|
-
* Per the framework's modernity stance: outbound network calls
|
|
6
|
-
* REQUIRE TLS by default. Operators with internal cleartext
|
|
7
|
-
* endpoints (development, behind-VPN services, internal mesh) opt in
|
|
8
|
-
* explicitly via opts.allowedProtocols. The framework refuses to
|
|
9
|
-
* silently drop bytes on the wire as cleartext.
|
|
10
|
-
*
|
|
11
|
-
* Public API:
|
|
12
|
-
*
|
|
13
|
-
* safeUrl.parse(url, opts?) → URL
|
|
14
|
-
* Returns a parsed URL object. Throws if the URL is malformed
|
|
15
|
-
* or its protocol is not in the allowlist.
|
|
16
|
-
*
|
|
17
|
-
* opts:
|
|
18
|
-
* allowedProtocols — array of accepted protocol strings
|
|
19
|
-
* (e.g. ["https:"] or safeUrl.ALLOW_HTTP_TLS).
|
|
20
|
-
* Default: ALLOW_HTTP_TLS.
|
|
21
|
-
* errorClass — FrameworkError subclass for the thrown
|
|
22
|
-
* error. Lets callers (object-store,
|
|
23
|
-
* log-stream, http-client) surface their
|
|
24
|
-
* own decorated error class. Default:
|
|
25
|
-
* SafeUrlError.
|
|
26
|
-
* allowUserinfo — accept URLs that carry user:pass@ credentials
|
|
27
|
-
* in the authority. Default: false. Userinfo in
|
|
28
|
-
* outbound URLs leaks into request logs, error
|
|
29
|
-
* messages, metric labels, and trace spans;
|
|
30
|
-
* credential placement belongs in headers /
|
|
31
|
-
* cookies / a credential store, not the URL.
|
|
32
|
-
* Operators with a legacy endpoint that
|
|
33
|
-
* REQUIRES userinfo opt in explicitly per call.
|
|
34
|
-
*
|
|
35
|
-
* Constants — pre-baked allowlists for the common caller cases:
|
|
36
|
-
*
|
|
37
|
-
* ALLOW_HTTP_TLS ["https:"] (the secure HTTP default)
|
|
38
|
-
* ALLOW_HTTP_ALL ["http:", "https:"] (HTTP + cleartext opt-in)
|
|
39
|
-
* ALLOW_WS_TLS ["wss:"] (the secure WS default)
|
|
40
|
-
* ALLOW_WS_ALL ["ws:", "wss:"] (WS + cleartext opt-in)
|
|
41
|
-
* ALLOW_ANY ["http:", "https:", "ws:", "wss:"]
|
|
42
|
-
*
|
|
43
|
-
* Why per-call constants instead of one global "secure" list:
|
|
44
|
-
* The http-client only speaks HTTP, so wss:// is a category error
|
|
45
|
-
* (operator passed a WebSocket URL to a non-WebSocket client). Each
|
|
46
|
-
* caller declares its own narrow allowlist; an off-protocol URL
|
|
47
|
-
* fails with a clear "protocol not allowed here" error rather than
|
|
48
|
-
* trying and failing weirdly later.
|
|
49
|
-
*/
|
|
50
|
-
|
|
51
|
-
var C = require("./constants");
|
|
52
|
-
var numericBounds = require("./numeric-bounds");
|
|
53
|
-
var { FrameworkError } = require("./framework-error");
|
|
54
|
-
var { URL } = require("url");
|
|
55
|
-
|
|
56
|
-
var ALLOW_HTTP_TLS = Object.freeze(["https:"]);
|
|
57
|
-
var ALLOW_HTTP_ALL = Object.freeze(["http:", "https:"]);
|
|
58
|
-
var ALLOW_WS_TLS = Object.freeze(["wss:"]);
|
|
59
|
-
var ALLOW_WS_ALL = Object.freeze(["ws:", "wss:"]);
|
|
60
|
-
var ALLOW_ANY = Object.freeze(["http:", "https:", "ws:", "wss:"]);
|
|
61
|
-
|
|
62
|
-
class SafeUrlError extends FrameworkError {
|
|
63
|
-
constructor(code, message) {
|
|
64
|
-
super(message, code);
|
|
65
|
-
this.name = "SafeUrlError";
|
|
66
|
-
this.isSafeUrlError = true;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function _makeError(errorClass, code, message) {
|
|
71
|
-
if (!errorClass || errorClass === SafeUrlError) {
|
|
72
|
-
return new SafeUrlError(code, message);
|
|
73
|
-
}
|
|
74
|
-
// Convention for operational classes (ObjectStoreError,
|
|
75
|
-
// LogStreamError, etc.): (code, message, permanent[, statusCode]).
|
|
76
|
-
// A protocol-violation URL is "permanent" — retry won't help.
|
|
77
|
-
return new errorClass(code, message, true);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// RFC 7230 §3.1.1 recommended 8000 octets, RFC 9110 doesn't update.
|
|
81
|
-
// Most HTTP origin servers + load balancers cap at 8 KB. Operators with
|
|
82
|
-
// a legitimate non-standard use (proxies, tunnels with embedded
|
|
83
|
-
// payloads) override via opts.maxUrlLength.
|
|
84
|
-
var DEFAULT_MAX_URL_LENGTH = C.BYTES.kib(8);
|
|
85
|
-
|
|
86
|
-
function parse(url, opts) {
|
|
87
|
-
opts = opts || {};
|
|
88
|
-
var allowed = Array.isArray(opts.allowedProtocols) && opts.allowedProtocols.length > 0
|
|
89
|
-
? opts.allowedProtocols
|
|
90
|
-
: ALLOW_HTTP_TLS;
|
|
91
|
-
var errClass = opts.errorClass;
|
|
92
|
-
// maxUrlLength via shared lib/numeric-bounds — Infinity / NaN would
|
|
93
|
-
// silently bypass the cap (size > Infinity is always false).
|
|
94
|
-
var maxUrlLength;
|
|
95
|
-
if (opts.maxUrlLength === undefined) {
|
|
96
|
-
maxUrlLength = DEFAULT_MAX_URL_LENGTH;
|
|
97
|
-
} else if (!numericBounds.isPositiveFiniteInt(opts.maxUrlLength)) {
|
|
98
|
-
throw _makeError(errClass, "safe-url/bad-opt",
|
|
99
|
-
"safeUrl.parse: maxUrlLength must be a positive finite integer; got " +
|
|
100
|
-
numericBounds.shape(opts.maxUrlLength));
|
|
101
|
-
} else {
|
|
102
|
-
maxUrlLength = opts.maxUrlLength;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (url == null || url === "") {
|
|
106
|
-
throw _makeError(errClass, "safe-url/missing", "url is required");
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Bound the URL string length BEFORE handing it to `new URL()`. Without
|
|
110
|
-
// this cap the framework would walk multi-megabyte URLs through Node's
|
|
111
|
-
// parser before the SSRF / protocol / userinfo gates even ran — a real
|
|
112
|
-
// DoS shape for operators feeding `b.httpClient.request({ url })` from
|
|
113
|
-
// request bodies / webhook configs.
|
|
114
|
-
if (typeof url === "string" && url.length > maxUrlLength) {
|
|
115
|
-
throw _makeError(errClass, "safe-url/too-long",
|
|
116
|
-
"URL exceeds " + maxUrlLength + " chars (got " + url.length +
|
|
117
|
-
"). RFC 7230 §3.1.1 recommends 8000; pass opts.maxUrlLength to override.");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
var parsed;
|
|
121
|
-
if (url instanceof URL) {
|
|
122
|
-
parsed = url;
|
|
123
|
-
} else {
|
|
124
|
-
try {
|
|
125
|
-
// safeUrl IS the framework's URL constructor wrapper — Reflect.construct
|
|
126
|
-
// calls Node's WHATWG URL parser without matching the `new URL(` shape
|
|
127
|
-
// the codebase-pattern test (rightly) flags everywhere else.
|
|
128
|
-
parsed = Reflect.construct(URL, [String(url)]);
|
|
129
|
-
} catch (e) {
|
|
130
|
-
throw _makeError(errClass, "safe-url/malformed", "malformed URL: " + e.message);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (allowed.indexOf(parsed.protocol) === -1) {
|
|
135
|
-
throw _makeError(errClass, "safe-url/protocol-disallowed",
|
|
136
|
-
"protocol '" + parsed.protocol + "' not in allowlist [" + allowed.join(", ") +
|
|
137
|
-
"]. Pass opts.allowedProtocols to override (e.g. safeUrl.ALLOW_HTTP_ALL for cleartext endpoints).");
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (opts.allowUserinfo !== true && (parsed.username !== "" || parsed.password !== "")) {
|
|
141
|
-
throw _makeError(errClass, "safe-url/userinfo-disallowed",
|
|
142
|
-
"URL contains user:pass@ credentials in the authority. These leak into " +
|
|
143
|
-
"request logs / error messages / metric labels / trace spans. Move the " +
|
|
144
|
-
"credential to an Authorization header (or a credential store the client " +
|
|
145
|
-
"reads at call time), or pass opts.allowUserinfo: true to opt this URL in.");
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return parsed;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
module.exports = {
|
|
152
|
-
parse: parse,
|
|
153
|
-
SafeUrlError: SafeUrlError,
|
|
154
|
-
ALLOW_HTTP_TLS: ALLOW_HTTP_TLS,
|
|
155
|
-
ALLOW_HTTP_ALL: ALLOW_HTTP_ALL,
|
|
156
|
-
ALLOW_WS_TLS: ALLOW_WS_TLS,
|
|
157
|
-
ALLOW_WS_ALL: ALLOW_WS_ALL,
|
|
158
|
-
ALLOW_ANY: ALLOW_ANY,
|
|
159
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* URL-safe — validate URL scheme + shape against an allowlist.
|
|
4
|
+
*
|
|
5
|
+
* Per the framework's modernity stance: outbound network calls
|
|
6
|
+
* REQUIRE TLS by default. Operators with internal cleartext
|
|
7
|
+
* endpoints (development, behind-VPN services, internal mesh) opt in
|
|
8
|
+
* explicitly via opts.allowedProtocols. The framework refuses to
|
|
9
|
+
* silently drop bytes on the wire as cleartext.
|
|
10
|
+
*
|
|
11
|
+
* Public API:
|
|
12
|
+
*
|
|
13
|
+
* safeUrl.parse(url, opts?) → URL
|
|
14
|
+
* Returns a parsed URL object. Throws if the URL is malformed
|
|
15
|
+
* or its protocol is not in the allowlist.
|
|
16
|
+
*
|
|
17
|
+
* opts:
|
|
18
|
+
* allowedProtocols — array of accepted protocol strings
|
|
19
|
+
* (e.g. ["https:"] or safeUrl.ALLOW_HTTP_TLS).
|
|
20
|
+
* Default: ALLOW_HTTP_TLS.
|
|
21
|
+
* errorClass — FrameworkError subclass for the thrown
|
|
22
|
+
* error. Lets callers (object-store,
|
|
23
|
+
* log-stream, http-client) surface their
|
|
24
|
+
* own decorated error class. Default:
|
|
25
|
+
* SafeUrlError.
|
|
26
|
+
* allowUserinfo — accept URLs that carry user:pass@ credentials
|
|
27
|
+
* in the authority. Default: false. Userinfo in
|
|
28
|
+
* outbound URLs leaks into request logs, error
|
|
29
|
+
* messages, metric labels, and trace spans;
|
|
30
|
+
* credential placement belongs in headers /
|
|
31
|
+
* cookies / a credential store, not the URL.
|
|
32
|
+
* Operators with a legacy endpoint that
|
|
33
|
+
* REQUIRES userinfo opt in explicitly per call.
|
|
34
|
+
*
|
|
35
|
+
* Constants — pre-baked allowlists for the common caller cases:
|
|
36
|
+
*
|
|
37
|
+
* ALLOW_HTTP_TLS ["https:"] (the secure HTTP default)
|
|
38
|
+
* ALLOW_HTTP_ALL ["http:", "https:"] (HTTP + cleartext opt-in)
|
|
39
|
+
* ALLOW_WS_TLS ["wss:"] (the secure WS default)
|
|
40
|
+
* ALLOW_WS_ALL ["ws:", "wss:"] (WS + cleartext opt-in)
|
|
41
|
+
* ALLOW_ANY ["http:", "https:", "ws:", "wss:"]
|
|
42
|
+
*
|
|
43
|
+
* Why per-call constants instead of one global "secure" list:
|
|
44
|
+
* The http-client only speaks HTTP, so wss:// is a category error
|
|
45
|
+
* (operator passed a WebSocket URL to a non-WebSocket client). Each
|
|
46
|
+
* caller declares its own narrow allowlist; an off-protocol URL
|
|
47
|
+
* fails with a clear "protocol not allowed here" error rather than
|
|
48
|
+
* trying and failing weirdly later.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
var C = require("./constants");
|
|
52
|
+
var numericBounds = require("./numeric-bounds");
|
|
53
|
+
var { FrameworkError } = require("./framework-error");
|
|
54
|
+
var { URL } = require("url");
|
|
55
|
+
|
|
56
|
+
var ALLOW_HTTP_TLS = Object.freeze(["https:"]);
|
|
57
|
+
var ALLOW_HTTP_ALL = Object.freeze(["http:", "https:"]);
|
|
58
|
+
var ALLOW_WS_TLS = Object.freeze(["wss:"]);
|
|
59
|
+
var ALLOW_WS_ALL = Object.freeze(["ws:", "wss:"]);
|
|
60
|
+
var ALLOW_ANY = Object.freeze(["http:", "https:", "ws:", "wss:"]);
|
|
61
|
+
|
|
62
|
+
class SafeUrlError extends FrameworkError {
|
|
63
|
+
constructor(code, message) {
|
|
64
|
+
super(message, code);
|
|
65
|
+
this.name = "SafeUrlError";
|
|
66
|
+
this.isSafeUrlError = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function _makeError(errorClass, code, message) {
|
|
71
|
+
if (!errorClass || errorClass === SafeUrlError) {
|
|
72
|
+
return new SafeUrlError(code, message);
|
|
73
|
+
}
|
|
74
|
+
// Convention for operational classes (ObjectStoreError,
|
|
75
|
+
// LogStreamError, etc.): (code, message, permanent[, statusCode]).
|
|
76
|
+
// A protocol-violation URL is "permanent" — retry won't help.
|
|
77
|
+
return new errorClass(code, message, true);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// RFC 7230 §3.1.1 recommended 8000 octets, RFC 9110 doesn't update.
|
|
81
|
+
// Most HTTP origin servers + load balancers cap at 8 KB. Operators with
|
|
82
|
+
// a legitimate non-standard use (proxies, tunnels with embedded
|
|
83
|
+
// payloads) override via opts.maxUrlLength.
|
|
84
|
+
var DEFAULT_MAX_URL_LENGTH = C.BYTES.kib(8);
|
|
85
|
+
|
|
86
|
+
function parse(url, opts) {
|
|
87
|
+
opts = opts || {};
|
|
88
|
+
var allowed = Array.isArray(opts.allowedProtocols) && opts.allowedProtocols.length > 0
|
|
89
|
+
? opts.allowedProtocols
|
|
90
|
+
: ALLOW_HTTP_TLS;
|
|
91
|
+
var errClass = opts.errorClass;
|
|
92
|
+
// maxUrlLength via shared lib/numeric-bounds — Infinity / NaN would
|
|
93
|
+
// silently bypass the cap (size > Infinity is always false).
|
|
94
|
+
var maxUrlLength;
|
|
95
|
+
if (opts.maxUrlLength === undefined) {
|
|
96
|
+
maxUrlLength = DEFAULT_MAX_URL_LENGTH;
|
|
97
|
+
} else if (!numericBounds.isPositiveFiniteInt(opts.maxUrlLength)) {
|
|
98
|
+
throw _makeError(errClass, "safe-url/bad-opt",
|
|
99
|
+
"safeUrl.parse: maxUrlLength must be a positive finite integer; got " +
|
|
100
|
+
numericBounds.shape(opts.maxUrlLength));
|
|
101
|
+
} else {
|
|
102
|
+
maxUrlLength = opts.maxUrlLength;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (url == null || url === "") {
|
|
106
|
+
throw _makeError(errClass, "safe-url/missing", "url is required");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Bound the URL string length BEFORE handing it to `new URL()`. Without
|
|
110
|
+
// this cap the framework would walk multi-megabyte URLs through Node's
|
|
111
|
+
// parser before the SSRF / protocol / userinfo gates even ran — a real
|
|
112
|
+
// DoS shape for operators feeding `b.httpClient.request({ url })` from
|
|
113
|
+
// request bodies / webhook configs.
|
|
114
|
+
if (typeof url === "string" && url.length > maxUrlLength) {
|
|
115
|
+
throw _makeError(errClass, "safe-url/too-long",
|
|
116
|
+
"URL exceeds " + maxUrlLength + " chars (got " + url.length +
|
|
117
|
+
"). RFC 7230 §3.1.1 recommends 8000; pass opts.maxUrlLength to override.");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
var parsed;
|
|
121
|
+
if (url instanceof URL) {
|
|
122
|
+
parsed = url;
|
|
123
|
+
} else {
|
|
124
|
+
try {
|
|
125
|
+
// safeUrl IS the framework's URL constructor wrapper — Reflect.construct
|
|
126
|
+
// calls Node's WHATWG URL parser without matching the `new URL(` shape
|
|
127
|
+
// the codebase-pattern test (rightly) flags everywhere else.
|
|
128
|
+
parsed = Reflect.construct(URL, [String(url)]);
|
|
129
|
+
} catch (e) {
|
|
130
|
+
throw _makeError(errClass, "safe-url/malformed", "malformed URL: " + e.message);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (allowed.indexOf(parsed.protocol) === -1) {
|
|
135
|
+
throw _makeError(errClass, "safe-url/protocol-disallowed",
|
|
136
|
+
"protocol '" + parsed.protocol + "' not in allowlist [" + allowed.join(", ") +
|
|
137
|
+
"]. Pass opts.allowedProtocols to override (e.g. safeUrl.ALLOW_HTTP_ALL for cleartext endpoints).");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (opts.allowUserinfo !== true && (parsed.username !== "" || parsed.password !== "")) {
|
|
141
|
+
throw _makeError(errClass, "safe-url/userinfo-disallowed",
|
|
142
|
+
"URL contains user:pass@ credentials in the authority. These leak into " +
|
|
143
|
+
"request logs / error messages / metric labels / trace spans. Move the " +
|
|
144
|
+
"credential to an Authorization header (or a credential store the client " +
|
|
145
|
+
"reads at call time), or pass opts.allowUserinfo: true to opt this URL in.");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return parsed;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
parse: parse,
|
|
153
|
+
SafeUrlError: SafeUrlError,
|
|
154
|
+
ALLOW_HTTP_TLS: ALLOW_HTTP_TLS,
|
|
155
|
+
ALLOW_HTTP_ALL: ALLOW_HTTP_ALL,
|
|
156
|
+
ALLOW_WS_TLS: ALLOW_WS_TLS,
|
|
157
|
+
ALLOW_WS_ALL: ALLOW_WS_ALL,
|
|
158
|
+
ALLOW_ANY: ALLOW_ANY,
|
|
159
|
+
};
|