@blamejs/core 0.6.13 → 0.6.21
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 +8 -0
- package/NOTICE +16 -0
- package/README.md +27 -18
- package/index.js +12 -0
- package/lib/archive.js +8 -7
- package/lib/audit.js +4 -0
- package/lib/auth/password.js +449 -4
- package/lib/bundler.js +8 -8
- package/lib/cache.js +105 -20
- package/lib/cli.js +598 -4
- package/lib/config-drift.js +309 -0
- package/lib/crypto-field.js +37 -0
- package/lib/crypto.js +8 -0
- package/lib/db-query.js +21 -2
- package/lib/db.js +32 -2
- package/lib/dual-control.js +475 -0
- package/lib/file-type.js +265 -0
- package/lib/framework-schema.js +38 -6
- package/lib/http-client-cookie-jar.js +117 -17
- package/lib/http-client.js +81 -3
- package/lib/internal-sha1-hibp.js +34 -0
- package/lib/mail.js +5 -4
- package/lib/middleware/csp-nonce.js +7 -4
- package/lib/middleware/index.js +2 -0
- package/lib/middleware/network-allowlist.js +199 -0
- package/lib/network-dns.js +564 -0
- package/lib/network-heartbeat.js +290 -0
- package/lib/network-nts.js +552 -0
- package/lib/network-proxy.js +246 -0
- package/lib/network-tls.js +326 -0
- package/lib/network.js +233 -0
- package/lib/ntp-check.js +50 -4
- package/lib/object-store/azure-blob.js +16 -42
- package/lib/pagination.js +136 -76
- package/lib/parsers/index.js +16 -2
- package/lib/parsers/safe-ini.js +273 -0
- package/lib/permissions.js +223 -9
- package/lib/pqc-agent.js +4 -4
- package/lib/retention.js +439 -0
- package/lib/security-assert.js +368 -0
- package/lib/session.js +138 -8
- package/lib/ssrf-guard.js +9 -0
- package/lib/vault/index.js +3 -3
- package/lib/vendor/MANIFEST.json +12 -0
- package/lib/vendor/common-passwords-top-10000.txt +10000 -0
- package/package.json +3 -2
- package/sbom.cyclonedx.json +61 -0
package/lib/cache.js
CHANGED
|
@@ -74,11 +74,6 @@
|
|
|
74
74
|
*
|
|
75
75
|
* What is NOT in the box:
|
|
76
76
|
*
|
|
77
|
-
* - Tag invalidation on the cluster backend — invalidating tagged
|
|
78
|
-
* entries across cluster nodes ties to a future distributed-pubsub
|
|
79
|
-
* slice. invalidateTag against a cluster-backend cache throws
|
|
80
|
-
* NOT_SUPPORTED today; operators wanting cluster-scope tag wipe
|
|
81
|
-
* run their own DELETE against _blamejs_cache.
|
|
82
77
|
* - maxBytes on the cluster backend — per-row size accounting against
|
|
83
78
|
* a shared table would mean an aggregate query on every set. The
|
|
84
79
|
* operator controls cluster-table size with their own pruning if
|
|
@@ -509,28 +504,98 @@ function _clusterBackend(cfg) {
|
|
|
509
504
|
catch (_e) { return undefined; }
|
|
510
505
|
}
|
|
511
506
|
|
|
512
|
-
async function set(key, value, expiresAt) {
|
|
507
|
+
async function set(key, value, expiresAt, meta) {
|
|
513
508
|
var json = JSON.stringify(value);
|
|
514
509
|
var storedExpires = (expiresAt === Infinity) ? Number.MAX_SAFE_INTEGER : expiresAt;
|
|
515
510
|
var now = clock();
|
|
511
|
+
var ck = _composedKey(key);
|
|
516
512
|
// SQLite + Postgres both honor ON CONFLICT (cacheKey) DO UPDATE.
|
|
517
513
|
await clusterStorage.execute(
|
|
518
514
|
"INSERT INTO _blamejs_cache (cacheKey, valueJson, expiresAt, updatedAt) " +
|
|
519
515
|
"VALUES (?, ?, ?, ?) " +
|
|
520
516
|
"ON CONFLICT (cacheKey) DO UPDATE SET " +
|
|
521
517
|
"valueJson = ?, expiresAt = ?, updatedAt = ?",
|
|
522
|
-
[
|
|
518
|
+
[ck, json, storedExpires, now, json, storedExpires, now]
|
|
519
|
+
);
|
|
520
|
+
// Tag handling: drop any prior tags for this key (tags can change
|
|
521
|
+
// across sets), then INSERT the new ones. The PRIMARY KEY on
|
|
522
|
+
// (cacheKey, tag) makes the INSERT idempotent if duplicate tags
|
|
523
|
+
// sneak in.
|
|
524
|
+
var tags = meta && Array.isArray(meta.tags) ? meta.tags : null;
|
|
525
|
+
await clusterStorage.execute(
|
|
526
|
+
"DELETE FROM _blamejs_cache_tags WHERE cacheKey = ?",
|
|
527
|
+
[ck]
|
|
523
528
|
);
|
|
529
|
+
if (tags && tags.length > 0) {
|
|
530
|
+
for (var i = 0; i < tags.length; i++) {
|
|
531
|
+
await clusterStorage.execute(
|
|
532
|
+
"INSERT INTO _blamejs_cache_tags (cacheKey, tag) VALUES (?, ?) " +
|
|
533
|
+
"ON CONFLICT (cacheKey, tag) DO NOTHING",
|
|
534
|
+
[ck, tags[i]]
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
524
538
|
}
|
|
525
539
|
|
|
526
540
|
async function del(key) {
|
|
541
|
+
var ck = _composedKey(key);
|
|
527
542
|
var result = await clusterStorage.execute(
|
|
528
543
|
"DELETE FROM _blamejs_cache WHERE cacheKey = ?",
|
|
529
|
-
[
|
|
544
|
+
[ck]
|
|
530
545
|
);
|
|
546
|
+
// Drop any matching tag rows. Best-effort: a stale tag row pointing
|
|
547
|
+
// at a non-existent cacheKey is dropped on the next invalidateTag
|
|
548
|
+
// sweep (by the JOIN-shape DELETE) anyway.
|
|
549
|
+
await clusterStorage.execute(
|
|
550
|
+
"DELETE FROM _blamejs_cache_tags WHERE cacheKey = ?",
|
|
551
|
+
[ck]
|
|
552
|
+
).catch(function () { /* best-effort */ });
|
|
531
553
|
return !!(result && result.rowCount && result.rowCount > 0);
|
|
532
554
|
}
|
|
533
555
|
|
|
556
|
+
async function invalidateTag(tag) {
|
|
557
|
+
// Find every cacheKey carrying the tag (namespace-scoped via the LIKE
|
|
558
|
+
// on the composed key), delete from the cache table + the junction.
|
|
559
|
+
var like = namespace + ":%";
|
|
560
|
+
var keysResult = await clusterStorage.execute(
|
|
561
|
+
"SELECT cacheKey FROM _blamejs_cache_tags WHERE tag = ? AND cacheKey LIKE ?",
|
|
562
|
+
[tag, like]
|
|
563
|
+
);
|
|
564
|
+
var keys = (keysResult && keysResult.rows) || [];
|
|
565
|
+
if (keys.length === 0) {
|
|
566
|
+
// Nothing to invalidate; still drop any orphan tag rows for
|
|
567
|
+
// this tag scoped to our namespace.
|
|
568
|
+
await clusterStorage.execute(
|
|
569
|
+
"DELETE FROM _blamejs_cache_tags WHERE tag = ? AND cacheKey LIKE ?",
|
|
570
|
+
[tag, like]
|
|
571
|
+
);
|
|
572
|
+
return 0;
|
|
573
|
+
}
|
|
574
|
+
var purged = 0;
|
|
575
|
+
for (var i = 0; i < keys.length; i++) {
|
|
576
|
+
var ck = keys[i].cacheKey;
|
|
577
|
+
var r = await clusterStorage.execute(
|
|
578
|
+
"DELETE FROM _blamejs_cache WHERE cacheKey = ?",
|
|
579
|
+
[ck]
|
|
580
|
+
);
|
|
581
|
+
if (r && r.rowCount > 0) purged += r.rowCount;
|
|
582
|
+
await clusterStorage.execute(
|
|
583
|
+
"DELETE FROM _blamejs_cache_tags WHERE cacheKey = ?",
|
|
584
|
+
[ck]
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
return purged;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
async function getTags(key) {
|
|
591
|
+
var result = await clusterStorage.execute(
|
|
592
|
+
"SELECT tag FROM _blamejs_cache_tags WHERE cacheKey = ?",
|
|
593
|
+
[_composedKey(key)]
|
|
594
|
+
);
|
|
595
|
+
if (!result || !result.rows) return [];
|
|
596
|
+
return result.rows.map(function (r) { return r.tag; });
|
|
597
|
+
}
|
|
598
|
+
|
|
534
599
|
async function has(key) {
|
|
535
600
|
// Existence check without recency bump — cluster backend doesn't
|
|
536
601
|
// track LRU at all, so "without bumping" is automatic. Honors
|
|
@@ -551,6 +616,11 @@ function _clusterBackend(cfg) {
|
|
|
551
616
|
"DELETE FROM _blamejs_cache WHERE cacheKey LIKE ?",
|
|
552
617
|
[like]
|
|
553
618
|
);
|
|
619
|
+
// Drop matching tag rows in the same namespace.
|
|
620
|
+
await clusterStorage.execute(
|
|
621
|
+
"DELETE FROM _blamejs_cache_tags WHERE cacheKey LIKE ?",
|
|
622
|
+
[like]
|
|
623
|
+
).catch(function () { /* best-effort */ });
|
|
554
624
|
return (result && result.rowCount) || 0;
|
|
555
625
|
}
|
|
556
626
|
|
|
@@ -568,10 +638,24 @@ function _clusterBackend(cfg) {
|
|
|
568
638
|
async function _sweep() {
|
|
569
639
|
var now = clock();
|
|
570
640
|
var like = namespace + ":%";
|
|
641
|
+
// Capture the to-be-purged keys first so we can drop matching tag
|
|
642
|
+
// rows in the same sweep — keeps the junction table free of orphans
|
|
643
|
+
// pointing at expired cacheKeys.
|
|
644
|
+
var expiredResult = await clusterStorage.execute(
|
|
645
|
+
"SELECT cacheKey FROM _blamejs_cache WHERE cacheKey LIKE ? AND expiresAt <= ?",
|
|
646
|
+
[like, now]
|
|
647
|
+
);
|
|
648
|
+
var expiredKeys = (expiredResult && expiredResult.rows) || [];
|
|
571
649
|
await clusterStorage.execute(
|
|
572
650
|
"DELETE FROM _blamejs_cache WHERE cacheKey LIKE ? AND expiresAt <= ?",
|
|
573
651
|
[like, now]
|
|
574
652
|
);
|
|
653
|
+
for (var i = 0; i < expiredKeys.length; i++) {
|
|
654
|
+
await clusterStorage.execute(
|
|
655
|
+
"DELETE FROM _blamejs_cache_tags WHERE cacheKey = ?",
|
|
656
|
+
[expiredKeys[i].cacheKey]
|
|
657
|
+
).catch(function () { /* best-effort */ });
|
|
658
|
+
}
|
|
575
659
|
}
|
|
576
660
|
|
|
577
661
|
function _startSweep(intervalMs) {
|
|
@@ -583,15 +667,17 @@ function _clusterBackend(cfg) {
|
|
|
583
667
|
}
|
|
584
668
|
|
|
585
669
|
return {
|
|
586
|
-
name:
|
|
587
|
-
get:
|
|
588
|
-
set:
|
|
589
|
-
del:
|
|
590
|
-
has:
|
|
591
|
-
clear:
|
|
592
|
-
size:
|
|
593
|
-
close:
|
|
594
|
-
|
|
670
|
+
name: "cluster",
|
|
671
|
+
get: get,
|
|
672
|
+
set: set,
|
|
673
|
+
del: del,
|
|
674
|
+
has: has,
|
|
675
|
+
clear: clear,
|
|
676
|
+
size: size,
|
|
677
|
+
close: close,
|
|
678
|
+
invalidateTag: invalidateTag,
|
|
679
|
+
getTags: getTags,
|
|
680
|
+
_startSweep: _startSweep,
|
|
595
681
|
};
|
|
596
682
|
}
|
|
597
683
|
|
|
@@ -865,9 +951,8 @@ function create(opts) {
|
|
|
865
951
|
if (typeof backend.invalidateTag !== "function") {
|
|
866
952
|
throw _err("NOT_SUPPORTED",
|
|
867
953
|
"cache.invalidateTag: backend '" + (backend.name || "custom") +
|
|
868
|
-
"' does not
|
|
869
|
-
"
|
|
870
|
-
"the distributed-invalidation slice.");
|
|
954
|
+
"' does not implement invalidateTag. Operator-supplied custom backends " +
|
|
955
|
+
"must export invalidateTag(tag) → number to participate in tag-based wipes.");
|
|
871
956
|
}
|
|
872
957
|
var purged;
|
|
873
958
|
try { purged = await backend.invalidateTag(tag); }
|