@blamejs/core 0.15.12 → 0.15.14
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 +4 -0
- package/index.js +2 -0
- package/lib/a2a-tasks.js +45 -29
- package/lib/acme.js +6 -5
- package/lib/agent-event-bus.js +18 -4
- package/lib/agent-idempotency.js +7 -6
- package/lib/agent-orchestrator.js +2 -5
- package/lib/agent-saga.js +3 -5
- package/lib/agent-snapshot.js +32 -2
- package/lib/agent-tenant.js +2 -5
- package/lib/ai-adverse-decision.js +2 -15
- package/lib/ai-aedt-bias-audit.js +2 -1
- package/lib/ai-capability.js +1 -6
- package/lib/ai-content-detect.js +1 -3
- package/lib/ai-dp.js +1 -5
- package/lib/ai-frontier-protocol.js +1 -1
- package/lib/ai-input.js +2 -2
- package/lib/ai-model-manifest.js +1 -1
- package/lib/ai-output.js +16 -7
- package/lib/ai-pref.js +3 -8
- package/lib/ai-quota.js +3 -14
- package/lib/api-key.js +37 -28
- package/lib/api-snapshot.js +4 -1
- package/lib/app-shutdown.js +7 -1
- package/lib/archive-adapters.js +2 -4
- package/lib/archive-entry-policy.js +32 -0
- package/lib/archive-gz.js +9 -0
- package/lib/archive-read.js +14 -24
- package/lib/archive-tar-read.js +56 -24
- package/lib/archive.js +6 -12
- package/lib/arg-parser.js +7 -6
- package/lib/asn1-der.js +70 -22
- package/lib/asyncapi-traits.js +2 -6
- package/lib/atomic-file.js +312 -33
- package/lib/audit-chain.js +183 -54
- package/lib/audit-daily-review.js +36 -16
- package/lib/audit-emit.js +82 -0
- package/lib/audit-sign.js +258 -0
- package/lib/audit-tools.js +108 -23
- package/lib/audit.js +91 -2
- package/lib/auth/access-lock.js +7 -29
- package/lib/auth/bot-challenge.js +1 -1
- package/lib/auth/ciba.js +43 -4
- package/lib/auth/dpop.js +29 -36
- package/lib/auth/fido-mds3.js +14 -16
- package/lib/auth/jwt-external.js +26 -8
- package/lib/auth/jwt.js +15 -17
- package/lib/auth/lockout.js +24 -27
- package/lib/auth/oauth.js +67 -45
- package/lib/auth/oid4vci.js +55 -32
- package/lib/auth/oid4vp.js +3 -2
- package/lib/auth/openid-federation.js +6 -6
- package/lib/auth/passkey.js +4 -4
- package/lib/auth/password.js +8 -2
- package/lib/auth/saml.js +70 -40
- package/lib/auth/sd-jwt-vc-holder.js +2 -14
- package/lib/auth/sd-jwt-vc-issuer.js +2 -14
- package/lib/auth/sd-jwt-vc.js +25 -5
- package/lib/auth/status-list.js +21 -9
- package/lib/auth/step-up.js +15 -13
- package/lib/auth-bot-challenge.js +27 -39
- package/lib/backup/bundle.js +18 -20
- package/lib/backup/crypto.js +23 -8
- package/lib/backup/index.js +55 -67
- package/lib/backup/manifest.js +7 -1
- package/lib/bounded-map.js +112 -1
- package/lib/breach-deadline.js +1 -11
- package/lib/break-glass.js +41 -22
- package/lib/cache.js +7 -18
- package/lib/cbor.js +36 -12
- package/lib/cdn-cache-control.js +15 -12
- package/lib/cert.js +8 -13
- package/lib/chain-writer.js +162 -47
- package/lib/cli.js +10 -2
- package/lib/client-hints.js +7 -9
- package/lib/cloud-events.js +43 -33
- package/lib/cluster-storage.js +9 -41
- package/lib/cms-codec.js +2 -1
- package/lib/codepoint-class.js +84 -1
- package/lib/compliance-ai-act-logging.js +1 -6
- package/lib/compliance-ai-act-transparency.js +2 -4
- package/lib/compliance-eaa.js +2 -12
- package/lib/compliance-sanctions-fetcher.js +21 -28
- package/lib/compliance-sanctions.js +11 -21
- package/lib/compliance.js +3 -10
- package/lib/config-drift.js +24 -20
- package/lib/content-credentials.js +11 -8
- package/lib/content-digest.js +21 -12
- package/lib/cookies.js +15 -13
- package/lib/cose.js +39 -11
- package/lib/cra-report.js +1 -11
- package/lib/crdt.js +2 -1
- package/lib/crypto-field.js +34 -33
- package/lib/crypto.js +138 -7
- package/lib/csp.js +239 -3
- package/lib/daemon.js +46 -42
- package/lib/data-act.js +46 -13
- package/lib/db-file-lifecycle.js +14 -6
- package/lib/db-query.js +75 -49
- package/lib/db.js +66 -27
- package/lib/dbsc.js +5 -6
- package/lib/ddl-change-control.js +18 -14
- package/lib/deprecate.js +4 -5
- package/lib/did.js +6 -2
- package/lib/dora.js +43 -13
- package/lib/dr-runbook.js +1 -1
- package/lib/dsa.js +2 -1
- package/lib/dsr.js +30 -20
- package/lib/early-hints.js +19 -0
- package/lib/eat.js +5 -1
- package/lib/external-db-migrate.js +21 -22
- package/lib/external-db.js +74 -30
- package/lib/fda-21cfr11.js +42 -13
- package/lib/fdx.js +22 -18
- package/lib/file-upload.js +80 -66
- package/lib/flag-evaluation-context.js +5 -8
- package/lib/flag-providers.js +6 -2
- package/lib/forms.js +1 -1
- package/lib/framework-error.js +12 -0
- package/lib/framework-schema.js +19 -0
- package/lib/fsm.js +7 -12
- package/lib/gate-contract.js +911 -39
- package/lib/gdpr-ropa.js +22 -22
- package/lib/graphql-federation.js +18 -5
- package/lib/guard-agent-registry.js +2 -1
- package/lib/guard-all.js +3 -2
- package/lib/guard-archive.js +9 -30
- package/lib/guard-auth.js +23 -80
- package/lib/guard-cidr.js +20 -96
- package/lib/guard-csv.js +135 -196
- package/lib/guard-domain.js +23 -106
- package/lib/guard-dsn.js +17 -14
- package/lib/guard-email.js +46 -53
- package/lib/guard-envelope.js +2 -2
- package/lib/guard-event-bus-topic.js +2 -1
- package/lib/guard-filename.js +12 -60
- package/lib/guard-graphql.js +28 -75
- package/lib/guard-html-wcag.js +15 -2
- package/lib/guard-html.js +74 -128
- package/lib/guard-idempotency-key.js +2 -1
- package/lib/guard-image.js +280 -77
- package/lib/guard-imap-command.js +9 -10
- package/lib/guard-jmap.js +1 -1
- package/lib/guard-json.js +101 -109
- package/lib/guard-jsonpath.js +20 -88
- package/lib/guard-jwt.js +32 -114
- package/lib/guard-list-id.js +2 -7
- package/lib/guard-list-unsubscribe.js +2 -7
- package/lib/guard-mail-compose.js +5 -6
- package/lib/guard-mail-move.js +3 -2
- package/lib/guard-mail-query.js +5 -3
- package/lib/guard-mail-sieve.js +6 -7
- package/lib/guard-managesieve-command.js +6 -5
- package/lib/guard-markdown.js +76 -110
- package/lib/guard-message-id.js +5 -6
- package/lib/guard-mime.js +20 -99
- package/lib/guard-oauth.js +19 -73
- package/lib/guard-pdf.js +65 -72
- package/lib/guard-pop3-command.js +13 -14
- package/lib/guard-posture-chain.js +2 -1
- package/lib/guard-regex.js +24 -99
- package/lib/guard-saga-config.js +2 -1
- package/lib/guard-shell.js +22 -87
- package/lib/guard-smtp-command.js +9 -12
- package/lib/guard-sql.js +15 -13
- package/lib/guard-stream-args.js +2 -1
- package/lib/guard-svg.js +103 -149
- package/lib/guard-template.js +23 -80
- package/lib/guard-tenant-id.js +2 -1
- package/lib/guard-text.js +592 -0
- package/lib/guard-time.js +27 -95
- package/lib/guard-uuid.js +21 -93
- package/lib/guard-xml.js +76 -106
- package/lib/guard-yaml.js +24 -60
- package/lib/html-balance.js +7 -3
- package/lib/http-client-cache.js +5 -12
- package/lib/http-client-cookie-jar.js +35 -16
- package/lib/http-client.js +233 -74
- package/lib/http-message-signature.js +3 -2
- package/lib/i18n-messageformat.js +5 -7
- package/lib/i18n.js +83 -26
- package/lib/iab-tcf.js +3 -2
- package/lib/importmap-integrity.js +41 -1
- package/lib/inbox.js +8 -8
- package/lib/incident-report.js +12 -27
- package/lib/ip-utils.js +49 -6
- package/lib/jobs.js +3 -2
- package/lib/json-patch.js +1 -1
- package/lib/json-path.js +24 -3
- package/lib/jtd.js +2 -2
- package/lib/keychain.js +6 -18
- package/lib/legal-hold.js +30 -23
- package/lib/log-stream-cloudwatch.js +44 -112
- package/lib/log-stream-otlp-grpc.js +17 -14
- package/lib/log-stream-otlp.js +16 -80
- package/lib/log-stream-webhook.js +20 -92
- package/lib/log.js +2 -2
- package/lib/mail-agent.js +2 -2
- package/lib/mail-arc-sign.js +8 -3
- package/lib/mail-arf.js +4 -3
- package/lib/mail-auth.js +43 -69
- package/lib/mail-bimi.js +35 -55
- package/lib/mail-bounce.js +3 -3
- package/lib/mail-crypto-pgp.js +3 -2
- package/lib/mail-crypto-smime.js +71 -6
- package/lib/mail-dav.js +8 -35
- package/lib/mail-deploy.js +15 -14
- package/lib/mail-dkim.js +19 -38
- package/lib/mail-greylist.js +17 -30
- package/lib/mail-helo.js +4 -7
- package/lib/mail-journal.js +13 -9
- package/lib/mail-mdn.js +12 -7
- package/lib/mail-rbl.js +7 -12
- package/lib/mail-scan.js +5 -7
- package/lib/mail-send-deliver.js +33 -20
- package/lib/mail-server-imap.js +32 -87
- package/lib/mail-server-jmap.js +39 -52
- package/lib/mail-server-managesieve.js +29 -83
- package/lib/mail-server-mx.js +33 -74
- package/lib/mail-server-net.js +177 -0
- package/lib/mail-server-pop3.js +30 -83
- package/lib/mail-server-rate-limit.js +30 -6
- package/lib/mail-server-registry.js +1 -1
- package/lib/mail-server-submission.js +34 -73
- package/lib/mail-server-tls.js +98 -2
- package/lib/mail-spam-score.js +9 -12
- package/lib/mail-store-fts.js +1 -1
- package/lib/mail-store.js +3 -2
- package/lib/mail.js +28 -13
- package/lib/markup-escape.js +31 -0
- package/lib/markup-tokenizer.js +78 -0
- package/lib/mcp-tool-registry.js +26 -23
- package/lib/mcp.js +7 -5
- package/lib/mdoc.js +36 -14
- package/lib/metrics.js +46 -33
- package/lib/middleware/age-gate.js +3 -23
- package/lib/middleware/api-encrypt.js +13 -24
- package/lib/middleware/assetlinks.js +2 -7
- package/lib/middleware/bearer-auth.js +2 -1
- package/lib/middleware/body-parser.js +41 -52
- package/lib/middleware/bot-disclose.js +7 -10
- package/lib/middleware/bot-guard.js +28 -28
- package/lib/middleware/clear-site-data.js +5 -1
- package/lib/middleware/compression.js +9 -0
- package/lib/middleware/cors.js +32 -23
- package/lib/middleware/csrf-protect.js +73 -23
- package/lib/middleware/daily-byte-quota.js +12 -30
- package/lib/middleware/deny-response.js +19 -1
- package/lib/middleware/fetch-metadata.js +35 -4
- package/lib/middleware/idempotency-key.js +4 -20
- package/lib/middleware/network-allowlist.js +61 -30
- package/lib/middleware/rate-limit.js +45 -44
- package/lib/middleware/scim-server.js +5 -3
- package/lib/middleware/security-headers.js +33 -7
- package/lib/middleware/security-txt.js +2 -6
- package/lib/middleware/speculation-rules.js +6 -3
- package/lib/middleware/tus-upload.js +14 -29
- package/lib/middleware/web-app-manifest.js +2 -7
- package/lib/migrations.js +4 -13
- package/lib/mime-parse.js +34 -17
- package/lib/module-loader.js +44 -0
- package/lib/money.js +105 -0
- package/lib/mtls-ca.js +125 -41
- package/lib/network-byte-quota.js +4 -18
- package/lib/network-dane.js +8 -6
- package/lib/network-dns-resolver.js +98 -7
- package/lib/network-dns.js +9 -2
- package/lib/network-dnssec.js +18 -17
- package/lib/network-heartbeat.js +3 -2
- package/lib/network-smtp-policy.js +52 -46
- package/lib/network-tls.js +67 -47
- package/lib/network-tsig.js +2 -2
- package/lib/nis2-report.js +2 -12
- package/lib/nist-crosswalk.js +1 -1
- package/lib/nonce-store.js +81 -3
- package/lib/ntp-check.js +28 -0
- package/lib/numeric-bounds.js +31 -8
- package/lib/object-store/azure-blob-bucket-ops.js +2 -6
- package/lib/object-store/azure-blob.js +6 -47
- package/lib/object-store/gcs-bucket-ops.js +4 -2
- package/lib/object-store/gcs.js +14 -75
- package/lib/object-store/http-put.js +2 -7
- package/lib/object-store/http-request.js +157 -0
- package/lib/object-store/local.js +37 -17
- package/lib/object-store/sigv4-bucket-ops.js +2 -1
- package/lib/object-store/sigv4.js +10 -79
- package/lib/observability-otlp-exporter.js +29 -29
- package/lib/observability.js +95 -0
- package/lib/openapi-paths-builder.js +7 -3
- package/lib/otel-export.js +7 -10
- package/lib/outbox.js +54 -41
- package/lib/pagination.js +11 -15
- package/lib/parsers/safe-env.js +5 -4
- package/lib/parsers/safe-ini.js +10 -4
- package/lib/parsers/safe-toml.js +11 -9
- package/lib/parsers/safe-xml.js +3 -3
- package/lib/parsers/safe-yaml.js +28 -9
- package/lib/pick.js +63 -5
- package/lib/pipl-cn.js +69 -59
- package/lib/privacy-pass.js +8 -6
- package/lib/problem-details.js +2 -5
- package/lib/pubsub.js +4 -8
- package/lib/queue-local.js +10 -3
- package/lib/redact.js +9 -4
- package/lib/render.js +4 -2
- package/lib/request-helpers.js +334 -29
- package/lib/resource-access-lock.js +3 -3
- package/lib/restore-bundle.js +46 -18
- package/lib/restore-rollback.js +10 -4
- package/lib/restore.js +24 -22
- package/lib/retention.js +23 -9
- package/lib/router.js +17 -4
- package/lib/safe-async.js +457 -0
- package/lib/safe-buffer.js +137 -6
- package/lib/safe-decompress.js +2 -1
- package/lib/safe-dns.js +2 -1
- package/lib/safe-ical.js +14 -28
- package/lib/safe-icap.js +1 -1
- package/lib/safe-json.js +51 -8
- package/lib/safe-jsonpath.js +6 -5
- package/lib/safe-mime.js +25 -29
- package/lib/safe-redirect.js +2 -5
- package/lib/safe-schema.js +7 -6
- package/lib/safe-sieve.js +1 -1
- package/lib/safe-sql.js +126 -0
- package/lib/safe-vcard.js +13 -27
- package/lib/sandbox-worker.js +15 -2
- package/lib/sandbox.js +4 -3
- package/lib/scheduler.js +22 -13
- package/lib/sec-cyber.js +82 -37
- package/lib/seeders.js +4 -11
- package/lib/self-update-standalone-verifier.js +16 -0
- package/lib/self-update.js +92 -69
- package/lib/session-device-binding.js +112 -66
- package/lib/session.js +219 -7
- package/lib/sql.js +228 -81
- package/lib/sse.js +6 -10
- package/lib/static.js +201 -111
- package/lib/storage.js +4 -2
- package/lib/structured-fields.js +275 -16
- package/lib/template.js +7 -5
- package/lib/tenant-quota.js +53 -38
- package/lib/tsa.js +16 -17
- package/lib/validate-opts.js +154 -8
- package/lib/vault/index.js +5 -0
- package/lib/vault/passphrase-ops.js +22 -26
- package/lib/vault/passphrase-source.js +8 -3
- package/lib/vault/rotate.js +13 -18
- package/lib/vault/seal-pem-file.js +34 -49
- package/lib/vault-aad.js +3 -2
- package/lib/vc.js +3 -8
- package/lib/vendor/MANIFEST.json +10 -10
- package/lib/vendor/public-suffix-list.dat +23 -10
- package/lib/vendor/public-suffix-list.data.js +5498 -5494
- package/lib/vex.js +35 -13
- package/lib/web-push-vapid.js +23 -20
- package/lib/webhook-dispatcher.js +706 -0
- package/lib/webhook.js +59 -19
- package/lib/websocket-channels.js +9 -7
- package/lib/websocket.js +8 -4
- package/lib/worm.js +3 -4
- package/lib/ws-client.js +65 -56
- package/lib/x509-chain.js +44 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/static.js
CHANGED
|
@@ -42,6 +42,7 @@ var nodeFs = require("node:fs");
|
|
|
42
42
|
var fsp = require("node:fs/promises");
|
|
43
43
|
var nodeCrypto = require("node:crypto");
|
|
44
44
|
var nodePath = require("node:path");
|
|
45
|
+
var atomicFile = require("./atomic-file");
|
|
45
46
|
var C = require("./constants");
|
|
46
47
|
var gateContract = require("./gate-contract");
|
|
47
48
|
var lazyRequire = require("./lazy-require");
|
|
@@ -240,10 +241,16 @@ async function _readMeta(root, candidate) {
|
|
|
240
241
|
var sri = nodeCrypto.createHash("sha384");
|
|
241
242
|
var sha3 = nodeCrypto.createHash("sha3-512");
|
|
242
243
|
await new Promise(function (resolve, reject) {
|
|
243
|
-
// The path
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
|
|
244
|
+
// The path is the confined output of `_assertInsideRoot(root, candidate)`
|
|
245
|
+
// above (lexical resolve + root-prefix containment). Open it O_NOFOLLOW and
|
|
246
|
+
// stream from that fd: lexical confinement still leaves a window where a
|
|
247
|
+
// symlink swapped in at the final component after the check would be
|
|
248
|
+
// followed by a plain createReadStream (CWE-22 / CWE-367); O_NOFOLLOW refuses
|
|
249
|
+
// it. The open throws synchronously on a symlink (ELOOP) / missing file —
|
|
250
|
+
// reject the hash promise so the caller falls back exactly as on a read error.
|
|
251
|
+
var s;
|
|
252
|
+
try { s = nodeFs.createReadStream(absPath, { fd: atomicFile.openNoFollowSync(absPath) }); }
|
|
253
|
+
catch (e) { reject(e); return; }
|
|
247
254
|
s.on("data", function (chunk) { sri.update(chunk); sha3.update(chunk); });
|
|
248
255
|
s.on("end", resolve);
|
|
249
256
|
s.on("error", reject);
|
|
@@ -399,7 +406,7 @@ function _shouldForceAttachment(contentType, ext, contentSafetyMap, allowSvgRend
|
|
|
399
406
|
return true;
|
|
400
407
|
}
|
|
401
408
|
if (bare.indexOf("text/") === 0) return false;
|
|
402
|
-
if (SAFE_RENDER_RASTER_MIMES
|
|
409
|
+
if (Object.prototype.hasOwnProperty.call(SAFE_RENDER_RASTER_MIMES, bare)) return false;
|
|
403
410
|
if (bare === "image/svg+xml") {
|
|
404
411
|
if (!allowSvgRender) return true;
|
|
405
412
|
if (!contentSafetyMap || typeof contentSafetyMap !== "object") return true;
|
|
@@ -489,84 +496,147 @@ function _httpDate(date) {
|
|
|
489
496
|
}
|
|
490
497
|
|
|
491
498
|
function _validateCreateOpts(opts) {
|
|
492
|
-
|
|
493
|
-
|
|
499
|
+
// Declarative per-field validation. shape() runs requireObject(opts)
|
|
500
|
+
// itself (same "opts must be an object" / BAD_OPT contract the inline
|
|
501
|
+
// requireObject produced), then dispatches each field. A field whose
|
|
502
|
+
// check is one of the rule tokens maps to the token directly; a field
|
|
503
|
+
// with a bespoke message (mountPath / hashedPathPattern), a
|
|
504
|
+
// duck-typed-handle shape (permissions / cache / fileType / retention /
|
|
505
|
+
// revokeStore — each carrying its own operator-facing description), a
|
|
506
|
+
// numeric bound, or an audit/observability shape uses the
|
|
507
|
+
// validator-function hatch so the exact thrown code + message + label
|
|
508
|
+
// are preserved. Cross-field business logic (root existence,
|
|
509
|
+
// allowedFileTypes↔fileType wiring, the contentSafety map, the
|
|
510
|
+
// mountType enum, the quota↔cache requirement) stays below shape.
|
|
511
|
+
validateOpts.shape(opts, {
|
|
512
|
+
root: "required-string",
|
|
513
|
+
mountPath: function (value, _label, errorClass, code) {
|
|
514
|
+
// empty string is operator-permissible: "no mount, root is request URL"
|
|
515
|
+
if (typeof value === "string" && value.length === 0) return;
|
|
516
|
+
if (value !== undefined && value !== null && typeof value !== "string") {
|
|
517
|
+
throw errorClass.factory(code, "staticServe.create: mountPath must be a string");
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
hashedPathPattern: function (value, _label, errorClass, code) {
|
|
521
|
+
if (value !== undefined && value !== null && !(value instanceof RegExp)) {
|
|
522
|
+
throw errorClass.factory(code, "staticServe.create: hashedPathPattern must be a RegExp");
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
// indexFile === null is the operator's "disable" sentinel; the helper
|
|
526
|
+
// returns null/undefined unchanged so we keep that semantic.
|
|
527
|
+
indexFile: "optional-string",
|
|
528
|
+
defaultMaxAge: function (value, label, errorClass, code) {
|
|
529
|
+
numericBounds.requireNonNegativeFiniteIntIfPresent(value, label, errorClass, code);
|
|
530
|
+
},
|
|
531
|
+
contentTypes: "optional-plain-object",
|
|
532
|
+
// contentSafety — extension-keyed gate map. undefined → the framework
|
|
533
|
+
// wires b.guardAll.byExtension({ profile: "strict" }) so every shipped
|
|
534
|
+
// guard is ON by default; null is the explicit opt-out (audited at
|
|
535
|
+
// create() time); a plain { ext: gate } object validates each value is a
|
|
536
|
+
// gate (a .check fn). The label / code / message match the prior inline
|
|
537
|
+
// check exactly so a test asserting them still holds.
|
|
538
|
+
contentSafety: function (value, _label, errorClass, code) {
|
|
539
|
+
if (value === undefined || value === null) return;
|
|
540
|
+
validateOpts.optionalPlainObject(value,
|
|
541
|
+
"staticServe.create: contentSafety", errorClass, code,
|
|
542
|
+
"must be a plain { ext: gate } object, null to opt out, or " +
|
|
543
|
+
"undefined for the default-on b.guardAll wiring");
|
|
544
|
+
var safetyKeys = Object.keys(value);
|
|
545
|
+
for (var sk = 0; sk < safetyKeys.length; sk++) {
|
|
546
|
+
var ext = safetyKeys[sk];
|
|
547
|
+
var g = value[ext];
|
|
548
|
+
if (!g || typeof g.check !== "function") {
|
|
549
|
+
throw errorClass.factory(code,
|
|
550
|
+
"staticServe.create: contentSafety[" + JSON.stringify(ext) +
|
|
551
|
+
"] must be a gate (b.guardCsv.gate / b.guardHtml.gate / etc.)");
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
permissions: function (value, label, errorClass, code) {
|
|
556
|
+
validateOpts.optionalObjectWithMethod(value, "check", label, errorClass, code,
|
|
557
|
+
"must be a b.permissions instance (check fn)");
|
|
558
|
+
},
|
|
559
|
+
cache: function (value, label, errorClass, code) {
|
|
560
|
+
validateOpts.optionalObjectWithMethod(value, "get", label, errorClass, code,
|
|
561
|
+
"must be a b.cache instance (used for cluster-shared bandwidth + concurrency tracking)");
|
|
562
|
+
},
|
|
563
|
+
fileType: function (value, label, errorClass, code) {
|
|
564
|
+
validateOpts.optionalObjectWithMethod(value, "detect", label, errorClass, code,
|
|
565
|
+
"must be a b.fileType instance (magic-byte MIME detection)");
|
|
566
|
+
},
|
|
567
|
+
retention: function (value, label, errorClass, code) {
|
|
568
|
+
validateOpts.optionalObjectWithMethod(value, "isServable", label, errorClass, code,
|
|
569
|
+
"must expose isServable(absPath, ctx) → boolean (compliance retention check)");
|
|
570
|
+
},
|
|
571
|
+
revokeStore: function (value, label, errorClass, code) {
|
|
572
|
+
validateOpts.optionalObjectWithMethod(value, "isRevoked", label, errorClass, code,
|
|
573
|
+
"must expose isRevoked(key) and revoke(key) for force-revoke support");
|
|
574
|
+
},
|
|
575
|
+
allowedFileTypes: "optional-string-array",
|
|
576
|
+
audit: function (value, _label, errorClass, _code) {
|
|
577
|
+
validateOpts.auditShape(value, "staticServe.create", errorClass);
|
|
578
|
+
},
|
|
579
|
+
observability: function (value, _label, errorClass, _code) {
|
|
580
|
+
validateOpts.observabilityShape(value, "staticServe.create", errorClass);
|
|
581
|
+
},
|
|
582
|
+
onServe: "optional-function",
|
|
583
|
+
onError: "optional-function",
|
|
584
|
+
acceptRanges: "optional-boolean",
|
|
585
|
+
auditSuccess: "optional-boolean",
|
|
586
|
+
auditFailures: "optional-boolean",
|
|
587
|
+
safeAttachmentForRiskyMimes: "optional-boolean",
|
|
588
|
+
forceAttachmentForNonText: "optional-boolean",
|
|
589
|
+
safeRenderSvg: "optional-boolean",
|
|
590
|
+
safeRenderPdf: "optional-boolean",
|
|
591
|
+
maxBytesPerActorPerWindowMs: function (value, label, errorClass, code) {
|
|
592
|
+
numericBounds.requireNonNegativeFiniteIntIfPresent(value, label, errorClass, code);
|
|
593
|
+
},
|
|
594
|
+
maxBytesAllActorsPerWindowMs: function (value, label, errorClass, code) {
|
|
595
|
+
numericBounds.requireNonNegativeFiniteIntIfPresent(value, label, errorClass, code);
|
|
596
|
+
},
|
|
597
|
+
bandwidthWindowMs: function (value, label, errorClass, code) {
|
|
598
|
+
numericBounds.requirePositiveFiniteIntIfPresent(value, label, errorClass, code);
|
|
599
|
+
},
|
|
600
|
+
maxConcurrentDownloadsPerActor: function (value, label, errorClass, code) {
|
|
601
|
+
numericBounds.requireNonNegativeFiniteIntIfPresent(value, label, errorClass, code);
|
|
602
|
+
},
|
|
603
|
+
maxIdleMs: function (value, label, errorClass, code) {
|
|
604
|
+
numericBounds.requirePositiveFiniteIntIfPresent(value, label, errorClass, code);
|
|
605
|
+
},
|
|
606
|
+
// maxRangeBytes — per-range byte cap (slowloris-range defense). A
|
|
607
|
+
// positive finite integer caps Range requests; Infinity is the
|
|
608
|
+
// documented opt-out, so the numericBounds finite-int helpers (which
|
|
609
|
+
// reject Infinity) can't be used directly here.
|
|
610
|
+
maxRangeBytes: function (value, label, errorClass, code) {
|
|
611
|
+
if (value === undefined || value === null || value === Infinity) return;
|
|
612
|
+
numericBounds.requirePositiveFiniteInt(value, label, errorClass, code);
|
|
613
|
+
},
|
|
614
|
+
}, "staticServe.create", StaticServeError, "BAD_OPT", {
|
|
615
|
+
// contentSafetyDisabledReason (audit-row string) and mountType (curated
|
|
616
|
+
// | user-content enum) carry dedicated cross-field validation below
|
|
617
|
+
// shape — the audit reason read and the enum throw whose exact message a
|
|
618
|
+
// test asserts. Listing them here keeps that bespoke logic the single
|
|
619
|
+
// validator rather than re-checking the same key inside shape.
|
|
620
|
+
allow: ["contentSafetyDisabledReason", "mountType"],
|
|
621
|
+
});
|
|
622
|
+
|
|
494
623
|
if (!nodeFs.existsSync(opts.root)) {
|
|
495
624
|
throw _err("BAD_OPT", "staticServe.create: root does not exist: " + opts.root);
|
|
496
625
|
}
|
|
497
|
-
if (typeof opts.mountPath === "string" && opts.mountPath.length === 0) {
|
|
498
|
-
// empty string is operator-permissible: "no mount, root is request URL"
|
|
499
|
-
} else if (opts.mountPath !== undefined && opts.mountPath !== null &&
|
|
500
|
-
typeof opts.mountPath !== "string") {
|
|
501
|
-
throw _err("BAD_OPT", "staticServe.create: mountPath must be a string");
|
|
502
|
-
}
|
|
503
|
-
if (opts.hashedPathPattern !== undefined && opts.hashedPathPattern !== null &&
|
|
504
|
-
!(opts.hashedPathPattern instanceof RegExp)) {
|
|
505
|
-
throw _err("BAD_OPT", "staticServe.create: hashedPathPattern must be a RegExp");
|
|
506
|
-
}
|
|
507
|
-
// indexFile === null is the operator's "disable" sentinel; the helper
|
|
508
|
-
// returns null/undefined unchanged so we keep that semantic.
|
|
509
|
-
validateOpts.optionalNonEmptyString(opts.indexFile,
|
|
510
|
-
"staticServe.create: indexFile", StaticServeError, "BAD_OPT");
|
|
511
|
-
numericBounds.requireNonNegativeFiniteIntIfPresent(opts.defaultMaxAge,
|
|
512
|
-
"staticServe.create: defaultMaxAge", StaticServeError, "BAD_OPT");
|
|
513
|
-
validateOpts.optionalPlainObject(opts.contentTypes,
|
|
514
|
-
"staticServe.create: contentTypes", StaticServeError, "BAD_OPT");
|
|
515
|
-
validateOpts.optionalObjectWithMethod(opts.permissions, "check",
|
|
516
|
-
"staticServe.create: permissions", StaticServeError, "BAD_OPT",
|
|
517
|
-
"must be a b.permissions instance (check fn)");
|
|
518
|
-
validateOpts.optionalObjectWithMethod(opts.cache, "get",
|
|
519
|
-
"staticServe.create: cache", StaticServeError, "BAD_OPT",
|
|
520
|
-
"must be a b.cache instance (used for cluster-shared bandwidth + concurrency tracking)");
|
|
521
|
-
validateOpts.optionalObjectWithMethod(opts.fileType, "detect",
|
|
522
|
-
"staticServe.create: fileType", StaticServeError, "BAD_OPT",
|
|
523
|
-
"must be a b.fileType instance (magic-byte MIME detection)");
|
|
524
|
-
validateOpts.optionalObjectWithMethod(opts.retention, "isServable",
|
|
525
|
-
"staticServe.create: retention", StaticServeError, "BAD_OPT",
|
|
526
|
-
"must expose isServable(absPath, ctx) → boolean (compliance retention check)");
|
|
527
|
-
validateOpts.optionalObjectWithMethod(opts.revokeStore, "isRevoked",
|
|
528
|
-
"staticServe.create: revokeStore", StaticServeError, "BAD_OPT",
|
|
529
|
-
"must expose isRevoked(key) and revoke(key) for force-revoke support");
|
|
530
|
-
validateOpts.optionalNonEmptyStringArray(opts.allowedFileTypes,
|
|
531
|
-
"staticServe.create: allowedFileTypes", StaticServeError, "BAD_OPT");
|
|
532
626
|
if (Array.isArray(opts.allowedFileTypes) && opts.allowedFileTypes.length > 0 &&
|
|
533
627
|
(!opts.fileType || typeof opts.fileType.detect !== "function")) {
|
|
534
628
|
throw _err("BAD_OPT",
|
|
535
629
|
"staticServe.create: allowedFileTypes is set but fileType primitive is not wired " +
|
|
536
630
|
"(pass fileType: b.fileType so the framework can sniff magic bytes before serving)");
|
|
537
631
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
// contentSafety
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
// Explicit opt-out: contentSafety: null (audited at create() time so
|
|
546
|
-
// a security review can reconstruct which deploys disabled the
|
|
547
|
-
// default-on protection).
|
|
632
|
+
// contentSafety — extension-keyed gate map; validated in the shape above
|
|
633
|
+
// (plain { ext: gate } object / null opt-out / undefined default-on).
|
|
634
|
+
// When undefined, the framework wires b.guardAll.byExtension({ profile:
|
|
635
|
+
// "strict" }) automatically so every shipped guard is ON by default;
|
|
636
|
+
// contentSafety: null is the explicit opt-out, audited at create() time so
|
|
637
|
+
// a security review can reconstruct which deploys disabled the default-on
|
|
638
|
+
// protection.
|
|
548
639
|
// Example: contentSafety: { ".csv": b.guardCsv.gate({ profile: "strict" }) }
|
|
549
|
-
if (opts.contentSafety !== undefined && opts.contentSafety !== null) {
|
|
550
|
-
validateOpts.optionalPlainObject(opts.contentSafety,
|
|
551
|
-
"staticServe.create: contentSafety", StaticServeError, "BAD_OPT",
|
|
552
|
-
"must be a plain { ext: gate } object, null to opt out, or " +
|
|
553
|
-
"undefined for the default-on b.guardAll wiring");
|
|
554
|
-
var safetyKeys = Object.keys(opts.contentSafety);
|
|
555
|
-
for (var sk = 0; sk < safetyKeys.length; sk++) {
|
|
556
|
-
var ext = safetyKeys[sk];
|
|
557
|
-
var g = opts.contentSafety[ext];
|
|
558
|
-
if (!g || typeof g.check !== "function") {
|
|
559
|
-
throw _err("BAD_OPT",
|
|
560
|
-
"staticServe.create: contentSafety[" + JSON.stringify(ext) +
|
|
561
|
-
"] must be a gate (b.guardCsv.gate / b.guardHtml.gate / etc.)");
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
validateOpts.optionalBoolean(opts.acceptRanges, "staticServe.create: acceptRanges", StaticServeError);
|
|
566
|
-
validateOpts.optionalBoolean(opts.auditSuccess, "staticServe.create: auditSuccess", StaticServeError);
|
|
567
|
-
validateOpts.optionalBoolean(opts.auditFailures, "staticServe.create: auditFailures", StaticServeError);
|
|
568
|
-
validateOpts.optionalBoolean(opts.safeAttachmentForRiskyMimes,
|
|
569
|
-
"staticServe.create: safeAttachmentForRiskyMimes", StaticServeError);
|
|
570
640
|
// mountType — config-time enum. A typo ("usercontent", "uploads")
|
|
571
641
|
// would silently fall back to the curated default and serve untrusted
|
|
572
642
|
// HTML inline, so THROW at boot rather than mis-type the mount.
|
|
@@ -576,22 +646,6 @@ function _validateCreateOpts(opts) {
|
|
|
576
646
|
"staticServe.create: mountType must be 'curated' (default) or " +
|
|
577
647
|
"'user-content'; got " + JSON.stringify(opts.mountType));
|
|
578
648
|
}
|
|
579
|
-
validateOpts.optionalBoolean(opts.forceAttachmentForNonText,
|
|
580
|
-
"staticServe.create: forceAttachmentForNonText", StaticServeError);
|
|
581
|
-
validateOpts.optionalBoolean(opts.safeRenderSvg,
|
|
582
|
-
"staticServe.create: safeRenderSvg", StaticServeError);
|
|
583
|
-
validateOpts.optionalBoolean(opts.safeRenderPdf,
|
|
584
|
-
"staticServe.create: safeRenderPdf", StaticServeError);
|
|
585
|
-
numericBounds.requireNonNegativeFiniteIntIfPresent(opts.maxBytesPerActorPerWindowMs,
|
|
586
|
-
"staticServe.create: maxBytesPerActorPerWindowMs", StaticServeError, "BAD_OPT");
|
|
587
|
-
numericBounds.requireNonNegativeFiniteIntIfPresent(opts.maxBytesAllActorsPerWindowMs,
|
|
588
|
-
"staticServe.create: maxBytesAllActorsPerWindowMs", StaticServeError, "BAD_OPT");
|
|
589
|
-
numericBounds.requirePositiveFiniteIntIfPresent(opts.bandwidthWindowMs,
|
|
590
|
-
"staticServe.create: bandwidthWindowMs", StaticServeError, "BAD_OPT");
|
|
591
|
-
numericBounds.requireNonNegativeFiniteIntIfPresent(opts.maxConcurrentDownloadsPerActor,
|
|
592
|
-
"staticServe.create: maxConcurrentDownloadsPerActor", StaticServeError, "BAD_OPT");
|
|
593
|
-
numericBounds.requirePositiveFiniteIntIfPresent(opts.maxIdleMs,
|
|
594
|
-
"staticServe.create: maxIdleMs", StaticServeError, "BAD_OPT");
|
|
595
649
|
// Quotas require a cache for cluster-shared coordination.
|
|
596
650
|
if ((opts.maxBytesPerActorPerWindowMs > 0 ||
|
|
597
651
|
opts.maxBytesAllActorsPerWindowMs > 0 ||
|
|
@@ -701,20 +755,11 @@ async function integrity(absPath) {
|
|
|
701
755
|
|
|
702
756
|
function create(opts) {
|
|
703
757
|
opts = opts || {};
|
|
704
|
-
// The
|
|
705
|
-
//
|
|
706
|
-
//
|
|
707
|
-
validateOpts(opts, [
|
|
708
|
-
|
|
709
|
-
"indexFile", "defaultMaxAge", "contentTypes",
|
|
710
|
-
"permissions", "cache", "fileType", "retention", "revokeStore",
|
|
711
|
-
"allowedFileTypes", "audit", "observability", "onServe", "onError",
|
|
712
|
-
"acceptRanges", "auditSuccess", "auditFailures",
|
|
713
|
-
"maxBytesPerActorPerWindowMs", "maxBytesAllActorsPerWindowMs",
|
|
714
|
-
"bandwidthWindowMs", "maxConcurrentDownloadsPerActor", "maxIdleMs",
|
|
715
|
-
"contentSafety", "contentSafetyDisabledReason",
|
|
716
|
-
"mountType", "forceAttachmentForNonText", "safeRenderSvg", "safeRenderPdf",
|
|
717
|
-
], "staticServe.create");
|
|
758
|
+
// The exhaustive shape() in _validateCreateOpts is the authoritative
|
|
759
|
+
// unknown-key gate: any opt not declared in its schema (nor listed in
|
|
760
|
+
// its options.allow pass-through) is rejected as an unknown opt, so the
|
|
761
|
+
// typo-catching the v0.6.x `validateOpts(opts, [...allowed])` call gave
|
|
762
|
+
// is now subsumed by the per-field validation below.
|
|
718
763
|
_validateCreateOpts(opts);
|
|
719
764
|
var cfg = validateOpts.applyDefaults(opts, DEFAULTS);
|
|
720
765
|
var root = nodePath.resolve(opts.root);
|
|
@@ -846,10 +891,21 @@ function create(opts) {
|
|
|
846
891
|
// `_assertInsideRoot`, not the request-derived candidate.
|
|
847
892
|
var confined = _assertInsideRoot(root, absPath);
|
|
848
893
|
if (!confined) return { ok: false, reason: "read-failed" };
|
|
894
|
+
// Only the first 64 KiB is ever inspected, so read JUST that prefix from an
|
|
895
|
+
// O_NOFOLLOW fd: the previous fsp.readFile slurped the WHOLE file into memory
|
|
896
|
+
// just to sniff its header — a request-reachable OOM on a user-content mount
|
|
897
|
+
// — and followed a post-confinement symlink swap. A PREFIX read (not a
|
|
898
|
+
// maxBytes refusal) is required so a large but allowed file still serves.
|
|
849
899
|
var sample;
|
|
850
|
-
try {
|
|
851
|
-
|
|
852
|
-
|
|
900
|
+
try {
|
|
901
|
+
var sniffFd = atomicFile.openNoFollowSync(confined);
|
|
902
|
+
try {
|
|
903
|
+
var sniffBuf = Buffer.alloc(C.BYTES.kib(64));
|
|
904
|
+
var sniffN = nodeFs.readSync(sniffFd, sniffBuf, 0, sniffBuf.length, 0);
|
|
905
|
+
sample = sniffBuf.slice(0, sniffN);
|
|
906
|
+
} finally { nodeFs.closeSync(sniffFd); }
|
|
907
|
+
} catch (_e) { return { ok: false, reason: "read-failed" }; }
|
|
908
|
+
var detected = fileType.detect(sample) || {};
|
|
853
909
|
if (!detected.mime) return { ok: false, reason: "indeterminate" };
|
|
854
910
|
if (allowedFileTypes.indexOf(detected.mime) === -1) {
|
|
855
911
|
return { ok: false, reason: "not-allowed", detected: detected.mime };
|
|
@@ -1013,6 +1069,23 @@ function create(opts) {
|
|
|
1013
1069
|
// creation can ever ride this code path.
|
|
1014
1070
|
gateHandle = await fsp.open(gateConfined, gateOpenFlags, 0o600);
|
|
1015
1071
|
var gateStat = await gateHandle.stat();
|
|
1072
|
+
// Cap before Buffer.alloc(gateStat.size): the gate buffers the WHOLE
|
|
1073
|
+
// file to inspect it, so a multi-GiB file on a user-content mount is a
|
|
1074
|
+
// request-reachable OOM lever. A file too large for the gate to vet is
|
|
1075
|
+
// refused (the gate's own "refuse" disposition) rather than buffered.
|
|
1076
|
+
if (gateStat.size > C.BYTES.mib(16)) {
|
|
1077
|
+
stats.failures += 1;
|
|
1078
|
+
_emitObs("staticServe.content_safety_refused", 1, { route: urlPath });
|
|
1079
|
+
try { await gateHandle.close(); } catch (_ce) { /* close best-effort */ }
|
|
1080
|
+
if (auditFailures) {
|
|
1081
|
+
emitAudit("staticServe.serve.failure", Object.assign({
|
|
1082
|
+
outcome: "failure", reason: "content_safety_too_large",
|
|
1083
|
+
resource: urlPath, ext: ext, sizeBytes: gateStat.size,
|
|
1084
|
+
}, actorCtx));
|
|
1085
|
+
}
|
|
1086
|
+
return writeErr(res, HTTP.UNSUPPORTED_MEDIA_TYPE,
|
|
1087
|
+
"content_safety_refused", "Unsupported Media Type");
|
|
1088
|
+
}
|
|
1016
1089
|
gateBuf = Buffer.alloc(gateStat.size);
|
|
1017
1090
|
var gateRead = 0;
|
|
1018
1091
|
while (gateRead < gateStat.size) {
|
|
@@ -1097,8 +1170,11 @@ function create(opts) {
|
|
|
1097
1170
|
"precondition_failed", "Precondition Failed");
|
|
1098
1171
|
}
|
|
1099
1172
|
|
|
1100
|
-
// Conditional: If-Modified-Since (304)
|
|
1101
|
-
|
|
1173
|
+
// Conditional: If-Modified-Since (304) — RFC 7232 §6: evaluated ONLY when
|
|
1174
|
+
// If-None-Match is absent. Otherwise a changed-ETag resource rewritten
|
|
1175
|
+
// within the same wall-clock second (mtime compared at second granularity)
|
|
1176
|
+
// would be falsely reported 304 despite the content hash differing.
|
|
1177
|
+
var ifModSince = !ifNone && headersIn["if-modified-since"];
|
|
1102
1178
|
if (ifModSince) {
|
|
1103
1179
|
var ims = Date.parse(ifModSince);
|
|
1104
1180
|
if (isFinite(ims) && Math.floor(meta.mtimeMs / C.TIME.seconds(1)) <= Math.floor(ims / C.TIME.seconds(1))) {
|
|
@@ -1113,8 +1189,9 @@ function create(opts) {
|
|
|
1113
1189
|
}
|
|
1114
1190
|
}
|
|
1115
1191
|
|
|
1116
|
-
// Conditional: If-Unmodified-Since (412)
|
|
1117
|
-
|
|
1192
|
+
// Conditional: If-Unmodified-Since (412) — RFC 7232 §6: evaluated ONLY
|
|
1193
|
+
// when If-Match is absent.
|
|
1194
|
+
var ifUnmodSince = !ifMatch && headersIn["if-unmodified-since"];
|
|
1118
1195
|
if (ifUnmodSince) {
|
|
1119
1196
|
var ius = Date.parse(ifUnmodSince);
|
|
1120
1197
|
if (isFinite(ius) && Math.floor(meta.mtimeMs / C.TIME.seconds(1)) > Math.floor(ius / C.TIME.seconds(1))) {
|
|
@@ -1310,7 +1387,20 @@ function create(opts) {
|
|
|
1310
1387
|
}
|
|
1311
1388
|
|
|
1312
1389
|
var streamOpts = range ? { start: range.start, end: range.end } : {};
|
|
1313
|
-
|
|
1390
|
+
// Open streamTarget (the re-confined _assertInsideRoot output) O_NOFOLLOW and
|
|
1391
|
+
// stream from that fd, so a symlink swapped in at the final component after
|
|
1392
|
+
// confinement is refused (ELOOP) rather than followed (CWE-22 / CWE-367).
|
|
1393
|
+
// Headers are already committed here, so a sync refusal can't re-issue a 404
|
|
1394
|
+
// — abort the connection instead of serving the swapped target.
|
|
1395
|
+
var fileStream;
|
|
1396
|
+
try {
|
|
1397
|
+
fileStream = nodeFs.createReadStream(streamTarget,
|
|
1398
|
+
Object.assign({ fd: atomicFile.openNoFollowSync(streamTarget) }, streamOpts));
|
|
1399
|
+
} catch (_openErr) {
|
|
1400
|
+
releaseSlot();
|
|
1401
|
+
try { res.destroy(); } catch (_d) { /* already torn down */ }
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1314
1404
|
|
|
1315
1405
|
// Idle timeout — close the connection if the client stalls. Pattern is
|
|
1316
1406
|
// a deadline-style debounce (clearTimeout + setTimeout) tied directly
|
package/lib/storage.js
CHANGED
|
@@ -38,10 +38,12 @@
|
|
|
38
38
|
* Filesystem-and-cloud-backed object storage with sealed per-file encryption keys, classification routing, and residency enforcement.
|
|
39
39
|
*/
|
|
40
40
|
var C = require("./constants");
|
|
41
|
+
var safeBuffer = require("./safe-buffer");
|
|
41
42
|
var { generateBytes, encryptPacked, decryptPacked } = require("./crypto");
|
|
42
43
|
var objectStore = require("./object-store");
|
|
43
44
|
var lazyRequire = require("./lazy-require");
|
|
44
45
|
var numericBounds = require("./numeric-bounds");
|
|
46
|
+
var codepointClass = require("./codepoint-class");
|
|
45
47
|
var canonicalJson = require("./canonical-json");
|
|
46
48
|
var { StorageError } = require("./framework-error");
|
|
47
49
|
|
|
@@ -950,7 +952,7 @@ function _validateAssemblyId(id) {
|
|
|
950
952
|
for (var i = 0; i < id.length; i += 1) {
|
|
951
953
|
var c = id.charCodeAt(i);
|
|
952
954
|
// Refuse: C0 (0x00-0x1F), DEL (0x7F), slash, backslash, dot-prefix
|
|
953
|
-
if (c
|
|
955
|
+
if (codepointClass.isForbiddenControlChar(c, { forbidTab: true }) || c === 0x2F || c === 0x5C) {
|
|
954
956
|
throw _err("INVALID_ARGUMENT",
|
|
955
957
|
"chunkScratch: assemblyId carries forbidden character at byte " + i, true);
|
|
956
958
|
}
|
|
@@ -1083,7 +1085,7 @@ function chunkScratch(opts) {
|
|
|
1083
1085
|
if (!Buffer.isBuffer(args.data)) {
|
|
1084
1086
|
throw _err("INVALID_ARGUMENT", "chunkScratch.saveChunk: data must be a Buffer", true);
|
|
1085
1087
|
}
|
|
1086
|
-
if (args.data
|
|
1088
|
+
if (safeBuffer.byteLengthOf(args.data) > maxChunkBytes) {
|
|
1087
1089
|
throw _err("INVALID_ARGUMENT",
|
|
1088
1090
|
"chunkScratch.saveChunk: chunk exceeds maxChunkBytes (" + args.data.length + " > " + maxChunkBytes + ")", true);
|
|
1089
1091
|
}
|