@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/restore-rollback.js
CHANGED
|
@@ -283,10 +283,16 @@ function list(opts) {
|
|
|
283
283
|
var p = nodePath.join(rollbackRoot, name);
|
|
284
284
|
var markerPath = p + ".marker.json";
|
|
285
285
|
var marker = null;
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
286
|
+
// Capped fd-bound read (no existsSync check-then-read window): the fs read is
|
|
287
|
+
// now bounded to 64 KiB too, so a tampered multi-GB marker.json is refused
|
|
288
|
+
// BEFORE allocation (the parse-only cap let readFileSync slurp the whole file
|
|
289
|
+
// first). refuseSymlink: the marker lives under the operator's rollbackRoot,
|
|
290
|
+
// never a secret-mount. Any failure → marker:null, the best-effort behavior.
|
|
291
|
+
try {
|
|
292
|
+
marker = safeJson.parse(
|
|
293
|
+
atomicFile.fdSafeReadSync(markerPath, { maxBytes: C.BYTES.kib(64), encoding: "utf8", refuseSymlink: true }),
|
|
294
|
+
{ maxBytes: C.BYTES.kib(64) });
|
|
295
|
+
} catch (_e) { marker = null; }
|
|
290
296
|
var stat;
|
|
291
297
|
try { stat = nodeFs.statSync(p); } catch (_e) { continue; }
|
|
292
298
|
out.push({
|
package/lib/restore.js
CHANGED
|
@@ -59,9 +59,8 @@ var bCrypto = require("./crypto");
|
|
|
59
59
|
var numericChecks = require("./numeric-checks");
|
|
60
60
|
var restoreBundle = require("./restore-bundle");
|
|
61
61
|
var restoreRollback = require("./restore-rollback");
|
|
62
|
-
var lazyRequire = require("./lazy-require");
|
|
63
62
|
var validateOpts = require("./validate-opts");
|
|
64
|
-
var
|
|
63
|
+
var auditEmit = require("./audit-emit");
|
|
65
64
|
var { FrameworkError } = require("./framework-error");
|
|
66
65
|
|
|
67
66
|
class RestoreError extends FrameworkError {
|
|
@@ -74,17 +73,9 @@ class RestoreError extends FrameworkError {
|
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
function _validateStorage(storage) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
var required = ["readBundle", "listBundles", "hasBundle"];
|
|
82
|
-
for (var i = 0; i < required.length; i++) {
|
|
83
|
-
if (typeof storage[required[i]] !== "function") {
|
|
84
|
-
throw new RestoreError("restore/bad-storage",
|
|
85
|
-
"storage backend missing method '" + required[i] + "'");
|
|
86
|
-
}
|
|
87
|
-
}
|
|
76
|
+
validateOpts.requireMethods(storage,
|
|
77
|
+
["readBundle", "listBundles", "hasBundle"],
|
|
78
|
+
"storage backend", RestoreError, "restore/bad-storage");
|
|
88
79
|
}
|
|
89
80
|
|
|
90
81
|
function create(opts) {
|
|
@@ -92,6 +83,7 @@ function create(opts) {
|
|
|
92
83
|
validateOpts(opts, [
|
|
93
84
|
"dataDir", "storage", "passphrase", "rollbackRoot", "audit",
|
|
94
85
|
"maxPulledBytes", "maxPulledFiles",
|
|
86
|
+
"requireSignature", "expectedFingerprint", "verifySignature",
|
|
95
87
|
], "restore");
|
|
96
88
|
validateOpts.requireNonEmptyString(opts.dataDir, "create: opts.dataDir", RestoreError, "restore/no-datadir");
|
|
97
89
|
_validateStorage(opts.storage);
|
|
@@ -105,6 +97,19 @@ function create(opts) {
|
|
|
105
97
|
var passphrase = opts.passphrase;
|
|
106
98
|
var rollbackRoot = opts.rollbackRoot || (dataDir + ".rollbacks");
|
|
107
99
|
var auditOn = opts.audit !== false;
|
|
100
|
+
// Manifest-signature policy. The framework signs bundles best-effort (an
|
|
101
|
+
// unsigned bundle is the documented CLI / standalone / worker case), so the
|
|
102
|
+
// non-opt-in integrity default is the per-blob AEAD path-binding below
|
|
103
|
+
// (which defeats the blob-remap attack on EVERY bundle, signed or not);
|
|
104
|
+
// requireSignature is the additional provenance policy operators under
|
|
105
|
+
// HIPAA/PCI opt into to mandate a verified signer. expectedFingerprint pins
|
|
106
|
+
// a signer; verifySignature:false allows a cold/cross-org restore. All
|
|
107
|
+
// three are threaded into restoreBundle.extract on every run — previously
|
|
108
|
+
// omitted entirely, so a present signature was never even verified and a
|
|
109
|
+
// requireSignature policy could not be enforced (CWE-347).
|
|
110
|
+
var requireSignature = opts.requireSignature === true;
|
|
111
|
+
var expectedFingerprint = opts.expectedFingerprint;
|
|
112
|
+
var verifySignature = opts.verifySignature;
|
|
108
113
|
|
|
109
114
|
// Preflight footprint caps. Defended against storage that returns a
|
|
110
115
|
// tampered or oversized bundle: we cap both the storage-reported size
|
|
@@ -152,15 +157,7 @@ function create(opts) {
|
|
|
152
157
|
return { totalBytes: totalBytes, fileCount: fileCount };
|
|
153
158
|
}
|
|
154
159
|
|
|
155
|
-
|
|
156
|
-
if (!auditOn) return;
|
|
157
|
-
audit().safeEmit({
|
|
158
|
-
action: action,
|
|
159
|
-
outcome: outcome,
|
|
160
|
-
metadata: info || {},
|
|
161
|
-
reason: info && info.reason ? info.reason : null,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
160
|
+
var _emitAudit = auditEmit.gatedReasonEmitter({ audit: auditOn });
|
|
164
161
|
|
|
165
162
|
async function list() { return await storage.listBundles(); }
|
|
166
163
|
|
|
@@ -288,6 +285,9 @@ function create(opts) {
|
|
|
288
285
|
passphrase: passphrase,
|
|
289
286
|
filter: runOpts.filter,
|
|
290
287
|
progressCallback: runOpts.progressCallback,
|
|
288
|
+
requireSignature: requireSignature,
|
|
289
|
+
expectedFingerprint: expectedFingerprint,
|
|
290
|
+
verifySignature: verifySignature,
|
|
291
291
|
});
|
|
292
292
|
} catch (e) {
|
|
293
293
|
_cleanupTmp();
|
|
@@ -300,6 +300,8 @@ function create(opts) {
|
|
|
300
300
|
else if (code === "restore-bundle/missing-manifest") mappedCode = "restore/missing-manifest";
|
|
301
301
|
else if (code === "restore-bundle/missing-blob") mappedCode = "restore/missing-blob";
|
|
302
302
|
else if (code === "restore-bundle/size-mismatch") mappedCode = "restore/size-mismatch";
|
|
303
|
+
else if (code === "restore-bundle/missing-signature") mappedCode = "restore/missing-signature";
|
|
304
|
+
else if (code === "restore-bundle/bad-signature") mappedCode = "restore/bad-signature";
|
|
303
305
|
_emitAudit("restore.failure",
|
|
304
306
|
{ bundleId: bundleId, reason: (e && e.message) || String(e) }, "failure");
|
|
305
307
|
throw new RestoreError(mappedCode,
|
package/lib/retention.js
CHANGED
|
@@ -47,6 +47,7 @@ var lazyRequire = require("./lazy-require");
|
|
|
47
47
|
var validateOpts = require("./validate-opts");
|
|
48
48
|
var safeSql = require("./safe-sql");
|
|
49
49
|
var sql = require("./sql");
|
|
50
|
+
var numericBounds = require("./numeric-bounds");
|
|
50
51
|
var { defineClass } = require("./framework-error");
|
|
51
52
|
|
|
52
53
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
@@ -116,11 +117,8 @@ function _validateRule(rule) {
|
|
|
116
117
|
throw _err("BAD_RULE",
|
|
117
118
|
"rule.action string must be 'erase' / 'delete' / 'soft-delete', got " + JSON.stringify(action));
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
rule.batchSize <= 0 || Math.floor(rule.batchSize) !== rule.batchSize)) {
|
|
122
|
-
throw _err("BAD_RULE", "rule.batchSize must be a positive integer");
|
|
123
|
-
}
|
|
120
|
+
numericBounds.requirePositiveFiniteIntIfPresent(rule.batchSize,
|
|
121
|
+
"rule.batchSize", RetentionError, "BAD_RULE");
|
|
124
122
|
if (rule.softDeleteField !== undefined &&
|
|
125
123
|
(typeof rule.softDeleteField !== "string" || rule.softDeleteField.length === 0)) {
|
|
126
124
|
throw _err("BAD_RULE", "rule.softDeleteField must be a non-empty string");
|
|
@@ -433,17 +431,29 @@ function create(opts) {
|
|
|
433
431
|
|
|
434
432
|
try {
|
|
435
433
|
var moreRows = true;
|
|
434
|
+
// Keyset pagination cursor. Without it the loop re-selects the SAME batch
|
|
435
|
+
// forever whenever a full batch of rows is NOT removed from the candidate
|
|
436
|
+
// set by its action — legal-hold-skipped, "warn"-stage, errored, and
|
|
437
|
+
// (critically) EVERY row under dryRun mutate nothing, so a LIMIT-from-the-
|
|
438
|
+
// top query returns the identical rows each pass and `rows.length ===
|
|
439
|
+
// batchSize` never goes false. Ordering by _id and advancing past the last
|
|
440
|
+
// row seen guarantees forward progress regardless of whether a row was
|
|
441
|
+
// actioned. (Deleted / anonymized rows are also simply skipped past.)
|
|
442
|
+
var lastId = null;
|
|
436
443
|
while (moreRows) {
|
|
437
444
|
var rows;
|
|
438
445
|
// The candidate WHERE-clause: age + not-already-erased + not-on-legal-hold +
|
|
439
|
-
// (when soft-delete is configured) not-already-soft-deleted.
|
|
440
|
-
// through b.sql so the operator-supplied table / ageField /
|
|
441
|
-
// identifiers are quoted by construction and every value
|
|
442
|
-
// placeholder (the '' empty-string compare included — no
|
|
446
|
+
// (when soft-delete is configured) not-already-soft-deleted + keyset cursor.
|
|
447
|
+
// Built through b.sql so the operator-supplied table / ageField /
|
|
448
|
+
// softDeleteField identifiers are quoted by construction and every value
|
|
449
|
+
// binds as a placeholder (the '' empty-string compare included — no
|
|
450
|
+
// embedded literal).
|
|
443
451
|
function _candidateBase() {
|
|
444
452
|
var qb = sql.select(rule.table, SQL_OPTS)
|
|
445
453
|
.where(rule.ageField, "<=", cutoff);
|
|
446
454
|
if (rule.softDeleteField) qb.whereNull(rule.softDeleteField);
|
|
455
|
+
if (lastId !== null) qb.where("_id", ">", lastId);
|
|
456
|
+
qb.orderBy("_id", "asc");
|
|
447
457
|
return qb;
|
|
448
458
|
}
|
|
449
459
|
var selStmt;
|
|
@@ -506,6 +516,10 @@ function create(opts) {
|
|
|
506
516
|
reason: (e && e.message) || String(e) });
|
|
507
517
|
}
|
|
508
518
|
}
|
|
519
|
+
// Advance the keyset cursor past this batch so already-seen rows
|
|
520
|
+
// (including skipped / warned / errored / dry-run rows that stay in the
|
|
521
|
+
// candidate set) are never re-selected — the loop-termination guarantee.
|
|
522
|
+
lastId = rows[rows.length - 1]._id;
|
|
509
523
|
if (rows.length < rule.batchSize) moreRows = false;
|
|
510
524
|
}
|
|
511
525
|
} catch (e) {
|
package/lib/router.js
CHANGED
|
@@ -36,6 +36,7 @@ var http = require("node:http");
|
|
|
36
36
|
var http2 = require("node:http2");
|
|
37
37
|
var nodeFs = require("node:fs");
|
|
38
38
|
var nodePath = require("node:path");
|
|
39
|
+
var atomicFile = require("./atomic-file");
|
|
39
40
|
var C = require("./constants");
|
|
40
41
|
var requestHelpers = require("./request-helpers");
|
|
41
42
|
var lazyRequire = require("./lazy-require");
|
|
@@ -147,7 +148,11 @@ function _makeSchemaValidator(spec) {
|
|
|
147
148
|
if (!qq.ok) return _writeValidationError(req, res, "query", qq.errors);
|
|
148
149
|
req.query = qq.value;
|
|
149
150
|
}
|
|
150
|
-
if (spec.body
|
|
151
|
+
if (spec.body) {
|
|
152
|
+
// Validate even when req.body is undefined (no body parsed / empty POST):
|
|
153
|
+
// a required body schema must report the violation, not be silently
|
|
154
|
+
// skipped. safeParse(undefined) fails a non-optional schema and passes an
|
|
155
|
+
// optional()/default() one. Mirrors the always-run params/query checks.
|
|
151
156
|
var bb = spec.body.safeParse(req.body);
|
|
152
157
|
if (!bb.ok) return _writeValidationError(req, res, "body", bb.errors);
|
|
153
158
|
req.body = bb.value;
|
|
@@ -1425,11 +1430,19 @@ function serveStatic(dir) {
|
|
|
1425
1430
|
// that shares the root's name as a prefix — e.g. root `/srv/public`
|
|
1426
1431
|
// vs `/srv/public-evil` — cannot satisfy the containment check.
|
|
1427
1432
|
if (filePath !== root && !filePath.startsWith(root + nodePath.sep)) return next();
|
|
1428
|
-
|
|
1433
|
+
// Open the (lexically-confined) path ONCE with O_NOFOLLOW and bind existence,
|
|
1434
|
+
// stat, and streaming to that single fd. The previous existsSync → statSync →
|
|
1435
|
+
// statSync → createReadStream sequence was a triple check-then-read TOCTOU
|
|
1436
|
+
// (CWE-367) on a request-derived pathname, and the plain createReadStream
|
|
1437
|
+
// would follow a symlink swapped in after the lexical check (CWE-22).
|
|
1438
|
+
// Opening before writeHead lets a refusal still fall through to next().
|
|
1439
|
+
var fd;
|
|
1440
|
+
try { fd = atomicFile.openNoFollowSync(filePath); } catch (_e) { return next(); }
|
|
1441
|
+
var stat = nodeFs.fstatSync(fd);
|
|
1442
|
+
if (stat.isDirectory()) { nodeFs.closeSync(fd); return next(); }
|
|
1429
1443
|
|
|
1430
1444
|
var ext = nodePath.extname(filePath).toLowerCase();
|
|
1431
1445
|
var mime = MIME_TYPES[ext] || "application/octet-stream";
|
|
1432
|
-
var stat = nodeFs.statSync(filePath);
|
|
1433
1446
|
var hasVersion = req.url && req.url.includes("?v=");
|
|
1434
1447
|
var cacheControl = hasVersion
|
|
1435
1448
|
? "public, max-age=31536000, immutable"
|
|
@@ -1439,7 +1452,7 @@ function serveStatic(dir) {
|
|
|
1439
1452
|
"Content-Length": stat.size,
|
|
1440
1453
|
"Cache-Control": cacheControl,
|
|
1441
1454
|
});
|
|
1442
|
-
nodeFs.createReadStream(filePath).pipe(res);
|
|
1455
|
+
nodeFs.createReadStream(filePath, { fd: fd }).pipe(res);
|
|
1443
1456
|
};
|
|
1444
1457
|
}
|
|
1445
1458
|
|