@blamejs/blamejs-shop 0.3.15 → 0.3.17
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/lib/admin.js +136 -3
- package/lib/asset-manifest.json +1 -1
- package/lib/catalog.js +61 -2
- package/lib/search-facets.js +15 -6
- package/lib/storefront.js +158 -9
- package/lib/vendor/MANIFEST.json +2 -2
- package/lib/vendor/blamejs/CHANGELOG.md +2 -0
- package/lib/vendor/blamejs/api-snapshot.json +2 -2
- package/lib/vendor/blamejs/lib/a2a-tasks.js +6 -6
- package/lib/vendor/blamejs/lib/ai-input.js +1 -1
- package/lib/vendor/blamejs/lib/auth/acr-vocabulary.js +1 -1
- package/lib/vendor/blamejs/lib/auth/ciba.js +3 -3
- package/lib/vendor/blamejs/lib/auth/oauth.js +1 -1
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +1 -1
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +1 -1
- package/lib/vendor/blamejs/lib/breach-deadline.js +3 -3
- package/lib/vendor/blamejs/lib/calendar.js +2 -2
- package/lib/vendor/blamejs/lib/content-credentials.js +3 -3
- package/lib/vendor/blamejs/lib/ddl-change-control.js +2 -2
- package/lib/vendor/blamejs/lib/did.js +2 -2
- package/lib/vendor/blamejs/lib/dsr.js +4 -4
- package/lib/vendor/blamejs/lib/external-db.js +1 -1
- package/lib/vendor/blamejs/lib/guard-cidr.js +1 -1
- package/lib/vendor/blamejs/lib/guard-image.js +1 -1
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +1 -1
- package/lib/vendor/blamejs/lib/guard-time.js +1 -1
- package/lib/vendor/blamejs/lib/guard-xml.js +1 -1
- package/lib/vendor/blamejs/lib/http-client-cache.js +1 -1
- package/lib/vendor/blamejs/lib/iab-tcf.js +4 -4
- package/lib/vendor/blamejs/lib/json-schema.js +1 -1
- package/lib/vendor/blamejs/lib/jtd.js +1 -1
- package/lib/vendor/blamejs/lib/mail-auth.js +1 -1
- package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +1 -1
- package/lib/vendor/blamejs/lib/mail-server-mx.js +1 -1
- package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +1 -1
- package/lib/vendor/blamejs/lib/mail-server-submission.js +1 -1
- package/lib/vendor/blamejs/lib/mcp.js +7 -7
- package/lib/vendor/blamejs/lib/mdoc.js +1 -1
- package/lib/vendor/blamejs/lib/metrics.js +2 -2
- package/lib/vendor/blamejs/lib/middleware/compose-pipeline.js +1 -1
- package/lib/vendor/blamejs/lib/network-dnssec.js +2 -2
- package/lib/vendor/blamejs/lib/network-smtp-policy.js +1 -1
- package/lib/vendor/blamejs/lib/network-tls.js +1 -1
- package/lib/vendor/blamejs/lib/network-tsig.js +3 -3
- package/lib/vendor/blamejs/lib/rfc3339.js +2 -2
- package/lib/vendor/blamejs/lib/safe-decompress.js +1 -1
- package/lib/vendor/blamejs/lib/standard-webhooks.js +3 -3
- package/lib/vendor/blamejs/lib/stream-throttle.js +2 -2
- package/lib/vendor/blamejs/lib/structured-fields.js +1 -1
- package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +1 -1
- package/lib/vendor/blamejs/lib/web-push-vapid.js +1 -1
- package/lib/vendor/blamejs/lib/webhook.js +1 -1
- package/lib/vendor/blamejs/lib/websocket.js +1 -1
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.14.8.json +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +221 -35
- package/package.json +1 -1
|
@@ -41,7 +41,7 @@ var TsigError = defineClass("TsigError", { alwaysPermanent: true });
|
|
|
41
41
|
|
|
42
42
|
var TYPE_TSIG = 250; // IANA RR type TSIG
|
|
43
43
|
var CLASS_ANY = 255; // TSIG RRs use CLASS ANY
|
|
44
|
-
var DEFAULT_FUDGE = 300;
|
|
44
|
+
var DEFAULT_FUDGE = 300;
|
|
45
45
|
|
|
46
46
|
// Algorithm name → Node hash. The strong HMAC-SHA-2 family is the safe set;
|
|
47
47
|
// HMAC-MD5 and HMAC-SHA-1 are refused unless allowLegacy (kept only for
|
|
@@ -225,7 +225,7 @@ function sign(message, opts) {
|
|
|
225
225
|
var secret = _secretBuf(opts.secret);
|
|
226
226
|
var fudge = opts.fudge == null ? DEFAULT_FUDGE : opts.fudge;
|
|
227
227
|
if (typeof fudge !== "number" || !isFinite(fudge) || fudge < 0 || fudge > 0xffff) throw new TsigError("tsig/bad-opt", "tsig.sign: fudge must be 0..65535 seconds"); // 16-bit fudge field
|
|
228
|
-
var time = opts.time == null ? Math.floor(Date.now() / 1000) : opts.time;
|
|
228
|
+
var time = opts.time == null ? Math.floor(Date.now() / 1000) : opts.time;
|
|
229
229
|
if (typeof time !== "number" || !isFinite(time) || time < 0) throw new TsigError("tsig/bad-opt", "tsig.sign: time must be a non-negative Unix-seconds number");
|
|
230
230
|
var error = opts.error == null ? 0 : opts.error;
|
|
231
231
|
var otherData = Buffer.isBuffer(opts.otherData) ? opts.otherData : Buffer.alloc(0);
|
|
@@ -379,7 +379,7 @@ function verify(message, opts) {
|
|
|
379
379
|
macValid = timingSafeEqual(rr.mac, expected.slice(0, rr.mac.length));
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
var now = opts.now == null ? Math.floor(Date.now() / 1000) : opts.now;
|
|
382
|
+
var now = opts.now == null ? Math.floor(Date.now() / 1000) : opts.now;
|
|
383
383
|
var timeValid = Math.abs(now - rr.timeSigned) <= rr.fudge;
|
|
384
384
|
|
|
385
385
|
var reason = null;
|
|
@@ -25,11 +25,11 @@ function isValidDateTime(s) {
|
|
|
25
25
|
if (!m) return false;
|
|
26
26
|
var mo = +m[2], d = +m[3], h = +m[4], mi = +m[5], se = +m[6];
|
|
27
27
|
if (mo < 1 || mo > 12 || d < 1 || d > 31 || h > 23 || mi > 59 || se > 60) return false; // allow:raw-time-literal — RFC 3339 field ranges (60 = leap second)
|
|
28
|
-
var days = [31, ((+m[1] % 4 === 0 && +m[1] % 100 !== 0) || +m[1] % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
28
|
+
var days = [31, ((+m[1] % 4 === 0 && +m[1] % 100 !== 0) || +m[1] % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
29
29
|
if (d > days[mo - 1]) return false;
|
|
30
30
|
var tz = m[8];
|
|
31
31
|
if (tz !== "Z" && tz !== "z") {
|
|
32
|
-
if (+tz.slice(1, 3) > 23 || +tz.slice(4, 6) > 59) return false;
|
|
32
|
+
if (+tz.slice(1, 3) > 23 || +tz.slice(4, 6) > 59) return false;
|
|
33
33
|
}
|
|
34
34
|
return true;
|
|
35
35
|
}
|
|
@@ -105,7 +105,7 @@ var _algorithms = {
|
|
|
105
105
|
// classic bomb shapes (1000:1) while leaving headroom for legitimate
|
|
106
106
|
// text / JSON / XML payloads (which compress 20-50:1 commonly). Per
|
|
107
107
|
// RFC 8460 §5.2 community guidance for TLS-RPT report decompression.
|
|
108
|
-
var DEFAULT_MAX_RATIO = 50;
|
|
108
|
+
var DEFAULT_MAX_RATIO = 50;
|
|
109
109
|
|
|
110
110
|
// Default input cap when operator omits opts.maxCompressedBytes —
|
|
111
111
|
// 4 MiB matches the TLS-RPT receive surface and is a reasonable
|
|
@@ -32,7 +32,7 @@ var { defineClass } = require("./framework-error");
|
|
|
32
32
|
|
|
33
33
|
var StandardWebhooksError = defineClass("StandardWebhooksError", { alwaysPermanent: true });
|
|
34
34
|
|
|
35
|
-
var DEFAULT_TOLERANCE_SEC = 300;
|
|
35
|
+
var DEFAULT_TOLERANCE_SEC = 300;
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* @primitive b.standardWebhooks.sign
|
|
@@ -70,7 +70,7 @@ function sign(opts) {
|
|
|
70
70
|
var id = opts.id || ("msg_" + bCrypto.generateToken(32)); // 32-char id token
|
|
71
71
|
var timestamp = typeof opts.timestamp === "number"
|
|
72
72
|
? opts.timestamp
|
|
73
|
-
: Math.floor(Date.now() / 1000);
|
|
73
|
+
: Math.floor(Date.now() / 1000);
|
|
74
74
|
if (timestamp <= 0 || !isFinite(timestamp)) {
|
|
75
75
|
throw new StandardWebhooksError("standard-webhooks/bad-timestamp",
|
|
76
76
|
"sign: timestamp must be a positive finite integer");
|
|
@@ -148,7 +148,7 @@ function verify(opts) {
|
|
|
148
148
|
numericBounds.requirePositiveFiniteIntIfPresent(opts.toleranceSec, "toleranceSec",
|
|
149
149
|
StandardWebhooksError, "standard-webhooks/bad-tolerance");
|
|
150
150
|
var tolerance = typeof opts.toleranceSec === "number" ? opts.toleranceSec : DEFAULT_TOLERANCE_SEC;
|
|
151
|
-
var nowSec = Math.floor(Date.now() / 1000);
|
|
151
|
+
var nowSec = Math.floor(Date.now() / 1000);
|
|
152
152
|
if (Math.abs(nowSec - ts) > tolerance) {
|
|
153
153
|
throw new StandardWebhooksError("standard-webhooks/timestamp-skew",
|
|
154
154
|
"verify: timestamp skew " + Math.abs(nowSec - ts) + "s exceeds tolerance " + tolerance + "s");
|
|
@@ -71,9 +71,9 @@ var StreamThrottleError = defineClass("StreamThrottleError", { alwaysPermanent:
|
|
|
71
71
|
// (bytes/sec ↔ wait-ms). This is a unit-conversion constant, not a
|
|
72
72
|
// memory cap or protocol-byte literal; the framework's C.TIME / C.BYTES
|
|
73
73
|
// helpers don't apply.
|
|
74
|
-
var MS_PER_SECOND = 1000;
|
|
74
|
+
var MS_PER_SECOND = 1000;
|
|
75
75
|
var NS_PER_MS = 1e6; // ns/ms unit conversion
|
|
76
|
-
var MS_PER_SECOND_HRTIME = 1000;
|
|
76
|
+
var MS_PER_SECOND_HRTIME = 1000;
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* @primitive b.streamThrottle.create
|
|
@@ -542,7 +542,7 @@ function parse(input, type, opts) {
|
|
|
542
542
|
|
|
543
543
|
function _serDecimal(v, E) {
|
|
544
544
|
if (!isFinite(v)) throw E("structured-fields/serialize", "cannot serialize a non-finite decimal");
|
|
545
|
-
var n = Math.round(v * 1000) / 1000; // allow:raw-time-literal — RFC 8941
|
|
545
|
+
var n = Math.round(v * 1000) / 1000; // allow:raw-time-literal — RFC 8941 4.1.5 decimal-scale 10^3 rounding; coincidental * 1000, not a duration, C.TIME N/A
|
|
546
546
|
if (Math.abs(Math.trunc(n)).toString().length > 12) throw E("structured-fields/serialize", "decimal integer part exceeds 12 digits"); // §4.1.5 cap
|
|
547
547
|
var s = n.toString();
|
|
548
548
|
if (s.indexOf(".") === -1) s += ".0"; // a Decimal must carry a fractional part
|
|
@@ -81,7 +81,7 @@ var SealPemFileError = defineClass("SealPemFileError", { alwaysPermanent: true }
|
|
|
81
81
|
// doesn't sneak past the watcher. Operators with extremely-quiet
|
|
82
82
|
// renewal cycles can override via opts.pollInterval; the cost of
|
|
83
83
|
// 500ms polling on an idle PEM file is ~2 stat() syscalls/sec.
|
|
84
|
-
var DEFAULT_POLL_MS = 500;
|
|
84
|
+
var DEFAULT_POLL_MS = 500;
|
|
85
85
|
|
|
86
86
|
// PEM files are tiny — 4 KiB for an ECDSA key, ~8 KiB for a 4096-bit
|
|
87
87
|
// RSA key, ~64 KiB for a long cert chain. Cap at 1 MiB so an operator
|
|
@@ -134,7 +134,7 @@ function buildVapidAuthHeader(opts) {
|
|
|
134
134
|
"buildVapidAuthHeader: subscription.endpoint is not a parseable URL");
|
|
135
135
|
}
|
|
136
136
|
var aud = endpointUrl.origin;
|
|
137
|
-
var now = Math.floor(Date.now() / 1000);
|
|
137
|
+
var now = Math.floor(Date.now() / 1000);
|
|
138
138
|
// Inline JWT sign with ES256 — VAPID strictly mandates ECDSA-P256
|
|
139
139
|
// (RFC 8292 §3.1). The framework jwt.sign is PQC-first and refuses
|
|
140
140
|
// ES256 by design; VAPID is a wire-protocol constraint outside
|
|
@@ -955,7 +955,7 @@ function sign(input) {
|
|
|
955
955
|
}
|
|
956
956
|
ts = Math.floor(input.timestamp);
|
|
957
957
|
} else {
|
|
958
|
-
ts = Math.floor(Date.now() / 1000);
|
|
958
|
+
ts = Math.floor(Date.now() / 1000);
|
|
959
959
|
}
|
|
960
960
|
var hex = _hmacSha256Hex(secretBytes, ts + "." + bodyStr);
|
|
961
961
|
return "t=" + ts + ",v1=" + hex;
|
|
@@ -191,7 +191,7 @@ var CLOSE_GRACE_MS = C.TIME.seconds(2);
|
|
|
191
191
|
function _isValidCloseCode(code) {
|
|
192
192
|
if (code === 1004 || code === 1005 || code === 1006 || code === 1015) return false; // RFC 6455 §7.4.2 reserved codes
|
|
193
193
|
if (code >= 1000 && code <= 1011) return true; // allow:raw-time-literal — code is a numeric, not seconds
|
|
194
|
-
if (code >= 3000 && code <= 4999) return true; // allow:raw-time-literal — code
|
|
194
|
+
if (code >= 3000 && code <= 4999) return true; // allow:raw-time-literal — WebSocket close-code range bound (RFC 6455 7.4.2); coincidental multiple-of-60, C.TIME N/A
|
|
195
195
|
return false;
|
|
196
196
|
}
|
|
197
197
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../scripts/release-notes-schema.json",
|
|
3
|
+
"version": "0.14.8",
|
|
4
|
+
"date": "2026-05-30",
|
|
5
|
+
"headline": "Source-comment and codebase-check hygiene, plus a new require-block alignment check; no API or behaviour changes",
|
|
6
|
+
"summary": "Internal lint and comment cleanup with no operator-facing surface change. Several codebase-check comments and one stub helper name that described behaviour the check no longer has are corrected; an unused lint-suppression class and a set of stale duplicate-cluster qualifiers (functions that were since renamed or extracted) are pruned or re-pointed. Fifty-nine `// allow:` markers that named the byte-size or time-literal check on values those checks no longer flag are removed, and twenty self-negating rationales on markers the time check genuinely fires on are rewritten to say why the value coincidentally matches. A new codebase check holds top-of-file require blocks to consistent `=` column alignment, with the files that currently carry drift listed as a migration allowlist. No exported API, error code, wire format, or runtime behaviour changes.",
|
|
7
|
+
"sections": [
|
|
8
|
+
{
|
|
9
|
+
"heading": "Changed",
|
|
10
|
+
"items": [
|
|
11
|
+
{
|
|
12
|
+
"title": "Lint-suppression and codebase-check comment cleanup",
|
|
13
|
+
"body": "Corrected codebase-check comments that overstated their check's scope (a duplicate-code threshold described as three files when the advisory threshold is two, a narrowed byte-literal check carrying its pre-narrowing description, and a deferred-scan helper named as though it enforced a guarantee it does not yet provide). Removed an unused lint-suppression class and its one dead in-code marker, and pruned or re-pointed stale duplicate-cluster qualifiers that named functions since renamed or extracted into shared helpers. Removed fifty-nine `// allow:` markers that suppressed nothing, and rewrote twenty self-negating marker rationales (which read \"not seconds\" while sitting on a value the time check fires on) to explain the coincidental match. Source-comment and test hygiene only."
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"heading": "Detectors",
|
|
19
|
+
"items": [
|
|
20
|
+
{
|
|
21
|
+
"title": "Require-block `=` alignment check",
|
|
22
|
+
"body": "A new codebase check flags a top-of-file require block that mixes its `=` column alignment — a fittable line whose `=` drifts off the column the rest of the block shares. Compact single-space blocks are exempt (only blocks that declare alignment intent are checked), as are destructures and long names physically too wide to reach the column, and blank- or comment-separated tiers align independently. The files that currently carry such drift are an explicit migration allowlist, reflowed over time; new code is held to the rule."
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -314,7 +314,6 @@ var VALID_ALLOW_CLASSES = {
|
|
|
314
314
|
"math-random-noncrypto": 1,
|
|
315
315
|
"no-number-money-arithmetic": 1,
|
|
316
316
|
"numeric-opt-Infinity": 1,
|
|
317
|
-
"numeric-opt-no-bounds-check": 1,
|
|
318
317
|
"primitive-unreachable": 1,
|
|
319
318
|
"process-exit": 1,
|
|
320
319
|
"raw-byte-literal": 1,
|
|
@@ -460,14 +459,10 @@ function testNoRawByteLiterals() {
|
|
|
460
459
|
// those are protocol constants, decoded to integers in the
|
|
461
460
|
// PQC_GROUPS table and elsewhere. (0x11EC = 4588 not multiple
|
|
462
461
|
// of 8, 0x11ED = 4589 not multiple of 8 — none trip anyway.)
|
|
463
|
-
//
|
|
464
|
-
// multiple-of-8
|
|
465
|
-
//
|
|
466
|
-
//
|
|
467
|
-
// or names them as protocol constants).
|
|
468
|
-
// No skip — flag every multiple of 8 strictly. The fundamental
|
|
469
|
-
// fix path is to use C.BYTES helpers or rename + comment as a
|
|
470
|
-
// protocol constant where 8-multiple is coincidence.
|
|
462
|
+
// After the `* 1024` gate above, this multiple-of-8 scan only
|
|
463
|
+
// runs on byte-scale arithmetic lines, so a multiple-of-8
|
|
464
|
+
// operand here is genuine byte math — the fix path is to route
|
|
465
|
+
// it through the C.BYTES helpers.
|
|
471
466
|
hit = true;
|
|
472
467
|
break;
|
|
473
468
|
}
|
|
@@ -1048,6 +1043,213 @@ function testNoInternalBindingNameInProse() {
|
|
|
1048
1043
|
bad);
|
|
1049
1044
|
}
|
|
1050
1045
|
|
|
1046
|
+
// ---- Pattern: require-block `=` column alignment ----
|
|
1047
|
+
//
|
|
1048
|
+
// class: require-block-misaligned
|
|
1049
|
+
//
|
|
1050
|
+
// Within a contiguous top-of-file run of `var <name> = require(...)` /
|
|
1051
|
+
// `var { ... } = require(...)` lines, the `=` signs share a column WHEN
|
|
1052
|
+
// the block is written in the aligned style. Two styles ship in lib/
|
|
1053
|
+
// and BOTH are legitimate:
|
|
1054
|
+
//
|
|
1055
|
+
// compact (single space — never flagged):
|
|
1056
|
+
// var C = require("./constants");
|
|
1057
|
+
// var audit = require("./audit");
|
|
1058
|
+
//
|
|
1059
|
+
// aligned (padded to a shared column):
|
|
1060
|
+
// var lazyRequire = require("./lazy-require");
|
|
1061
|
+
// var C = require("./constants");
|
|
1062
|
+
//
|
|
1063
|
+
// The detector fires only on a sub-block that DECLARES alignment intent
|
|
1064
|
+
// (at least one require line carries 2+ spaces before its `=`) and only
|
|
1065
|
+
// on a line that COULD reach the block's shared column but doesn't.
|
|
1066
|
+
//
|
|
1067
|
+
// The shared column is the MODAL `=` column — the column the majority
|
|
1068
|
+
// of the block's require lines actually use. A line is a violation when
|
|
1069
|
+
// its `=` is off the modal column AND its left-hand side is short enough
|
|
1070
|
+
// to reach it: `lhsLen + 1 <= modalCol`. A `var { LongDestructure } =`
|
|
1071
|
+
// or a long plain name whose LHS is as wide as (or wider than) the modal
|
|
1072
|
+
// column can only ever get a single space — it physically can't align,
|
|
1073
|
+
// so that overshoot is house style, not drift, and is EXEMPT. (This is
|
|
1074
|
+
// the classic `var guardAgentRegistry = ...` / `var { defineClass } = ...`
|
|
1075
|
+
// shape: unfittable, correctly ignored. The target is the fittable line
|
|
1076
|
+
// that drifts one column anyway — the stray extra space, or the short
|
|
1077
|
+
// `var C =` that didn't get padded to the column.)
|
|
1078
|
+
//
|
|
1079
|
+
// Sub-blocks align INDEPENDENTLY. A blank line OR an interior `//` /
|
|
1080
|
+
// `/* */` comment divider between require lines splits the run: a tier
|
|
1081
|
+
// of node: builtins or a lazyRequire tier separated from the relative-
|
|
1082
|
+
// require tier by an explanatory comment is its own group, checked on
|
|
1083
|
+
// its own column. Only the leading top-of-file require region is
|
|
1084
|
+
// inspected — array/object element order, later constant blocks, and
|
|
1085
|
+
// every non-require `=` are out of scope.
|
|
1086
|
+
//
|
|
1087
|
+
// REQUIRE_ALIGN_ALLOWLIST below is the migration backlog: files that
|
|
1088
|
+
// align today but carry a fittable drifted line. As each is reflowed to
|
|
1089
|
+
// a uniform column (or to compact single-space), drop its entry.
|
|
1090
|
+
var REQUIRE_ALIGN_ALLOWLIST = [
|
|
1091
|
+
"lib/ai-pref.js",
|
|
1092
|
+
"lib/archive.js",
|
|
1093
|
+
"lib/auth/oid4vci.js",
|
|
1094
|
+
"lib/cdn-cache-control.js",
|
|
1095
|
+
"lib/compliance-ai-act-logging.js",
|
|
1096
|
+
"lib/compliance-ai-act.js",
|
|
1097
|
+
"lib/db-file-lifecycle.js",
|
|
1098
|
+
"lib/flag-providers.js",
|
|
1099
|
+
"lib/http-client-cookie-jar.js",
|
|
1100
|
+
"lib/http-client.js",
|
|
1101
|
+
"lib/local-db-thin.js",
|
|
1102
|
+
"lib/log-stream-syslog.js",
|
|
1103
|
+
"lib/mail-require-tls.js",
|
|
1104
|
+
"lib/mail-scan.js",
|
|
1105
|
+
"lib/mail-server-imap.js",
|
|
1106
|
+
"lib/mail-server-mx.js",
|
|
1107
|
+
"lib/mail-server-submission.js",
|
|
1108
|
+
"lib/mail-srs.js",
|
|
1109
|
+
"lib/metrics.js",
|
|
1110
|
+
"lib/middleware/body-parser.js",
|
|
1111
|
+
"lib/middleware/tus-upload.js",
|
|
1112
|
+
"lib/pqc-agent.js",
|
|
1113
|
+
"lib/public-suffix.js",
|
|
1114
|
+
"lib/queue.js",
|
|
1115
|
+
"lib/router.js",
|
|
1116
|
+
"lib/scheduler.js",
|
|
1117
|
+
"lib/session-device-binding.js",
|
|
1118
|
+
"lib/test-harness.js",
|
|
1119
|
+
"lib/websocket.js",
|
|
1120
|
+
];
|
|
1121
|
+
|
|
1122
|
+
// A top-of-file require-assignment line. Plain identifier OR a `{ ... }`
|
|
1123
|
+
// destructure on the left; require / lazyRequire on the right.
|
|
1124
|
+
var _REQUIRE_ALIGN_LINE = /^\s*var\s+(?:\{[^}]*\}|[A-Za-z_$][\w$]*)\s*=\s*(?:require|lazyRequire)\s*\(/;
|
|
1125
|
+
|
|
1126
|
+
// For a require-assignment line: lhsLen (length of the LHS up to and
|
|
1127
|
+
// including its last non-space token char), col (the `=` column = lhsLen
|
|
1128
|
+
// + the padding spaces), and pad (the padding-space count). Returns null
|
|
1129
|
+
// for any line that is not a require-assignment.
|
|
1130
|
+
function _requireEqInfo(line) {
|
|
1131
|
+
var m = line.match(/^(.*?[^\s=!<>+\-*/%&|^])(\s*)=\s*(?:require|lazyRequire)\s*\(/);
|
|
1132
|
+
if (!m) return null;
|
|
1133
|
+
return { lhsLen: m[1].length, col: m[1].length + m[2].length, pad: m[2].length };
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// The modal `=` column of a sub-block: the column the most require lines
|
|
1137
|
+
// land on. Ties break to the wider column (the deliberately-padded one,
|
|
1138
|
+
// not a short outlier), so a block of two aligned lines plus a stray
|
|
1139
|
+
// short `var C =` resolves to the aligned column.
|
|
1140
|
+
function _requireModalColumn(infos) {
|
|
1141
|
+
var counts = {};
|
|
1142
|
+
var best = null;
|
|
1143
|
+
var bestN = -1;
|
|
1144
|
+
for (var i = 0; i < infos.length; i++) {
|
|
1145
|
+
var c = infos[i].info.col;
|
|
1146
|
+
counts[c] = (counts[c] || 0) + 1;
|
|
1147
|
+
if (counts[c] > bestN || (counts[c] === bestN && c > best)) {
|
|
1148
|
+
bestN = counts[c];
|
|
1149
|
+
best = c;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return best;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
function testRequireBlockAlignment() {
|
|
1156
|
+
// class: require-block-misaligned
|
|
1157
|
+
var files = _libFiles();
|
|
1158
|
+
var bad = [];
|
|
1159
|
+
|
|
1160
|
+
for (var fi = 0; fi < files.length; fi++) {
|
|
1161
|
+
var rel = _relPath(files[fi]);
|
|
1162
|
+
if (REQUIRE_ALIGN_ALLOWLIST.indexOf(rel) !== -1) continue;
|
|
1163
|
+
var content;
|
|
1164
|
+
try { content = fs.readFileSync(files[fi], "utf8"); }
|
|
1165
|
+
catch (_e) { continue; }
|
|
1166
|
+
var lines = content.split(/\r?\n/);
|
|
1167
|
+
|
|
1168
|
+
// Locate the first leading require line. Skip the shebang,
|
|
1169
|
+
// "use strict", and the top docblock / comments + blanks. Bail the
|
|
1170
|
+
// moment real code appears before any require (no leading region).
|
|
1171
|
+
var firstReq = -1;
|
|
1172
|
+
for (var j = 0; j < lines.length; j++) {
|
|
1173
|
+
if (_REQUIRE_ALIGN_LINE.test(lines[j])) { firstReq = j; break; }
|
|
1174
|
+
var t = lines[j].trim();
|
|
1175
|
+
if (t === "") continue;
|
|
1176
|
+
if (/^("use strict"|'use strict');?$/.test(t)) continue;
|
|
1177
|
+
if (/^#!/.test(t)) continue;
|
|
1178
|
+
if (/^\/\//.test(t)) continue;
|
|
1179
|
+
if (/^\/\*/.test(t) || /^\*/.test(t) || /\*\//.test(t)) continue;
|
|
1180
|
+
break;
|
|
1181
|
+
}
|
|
1182
|
+
if (firstReq === -1) continue;
|
|
1183
|
+
|
|
1184
|
+
// Collect the contiguous leading require region: require lines,
|
|
1185
|
+
// blank lines (sub-block separators), and interior comment lines
|
|
1186
|
+
// (also sub-block separators). Stop at the first real-code line
|
|
1187
|
+
// that is not a require.
|
|
1188
|
+
var region = [];
|
|
1189
|
+
var inBlockComment = false;
|
|
1190
|
+
for (var k = firstReq; k < lines.length; k++) {
|
|
1191
|
+
var line = lines[k];
|
|
1192
|
+
var tt = line.trim();
|
|
1193
|
+
if (inBlockComment) {
|
|
1194
|
+
region.push({ n: k + 1, line: line, kind: "comment" });
|
|
1195
|
+
if (/\*\//.test(tt)) inBlockComment = false;
|
|
1196
|
+
continue;
|
|
1197
|
+
}
|
|
1198
|
+
if (_REQUIRE_ALIGN_LINE.test(line)) { region.push({ n: k + 1, line: line, kind: "req" }); continue; }
|
|
1199
|
+
if (tt === "") { region.push({ n: k + 1, line: line, kind: "blank" }); continue; }
|
|
1200
|
+
if (/^\/\//.test(tt)) { region.push({ n: k + 1, line: line, kind: "comment" }); continue; }
|
|
1201
|
+
if (/^\/\*/.test(tt)) {
|
|
1202
|
+
region.push({ n: k + 1, line: line, kind: "comment" });
|
|
1203
|
+
if (!/\*\//.test(tt)) inBlockComment = true;
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
break;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// Split the region into sub-blocks separated by a blank line OR a
|
|
1210
|
+
// comment divider; each aligns independently. For every sub-block of
|
|
1211
|
+
// >=3 require lines that shows alignment intent (some line padded
|
|
1212
|
+
// with 2+ spaces before `=`), flag every line whose `=` is off the
|
|
1213
|
+
// modal column yet whose LHS is short enough to reach it.
|
|
1214
|
+
var sub = [];
|
|
1215
|
+
var flush = function () {
|
|
1216
|
+
var infos = [];
|
|
1217
|
+
for (var s = 0; s < sub.length; s++) {
|
|
1218
|
+
if (sub[s].kind !== "req") continue;
|
|
1219
|
+
var info = _requireEqInfo(sub[s].line);
|
|
1220
|
+
if (info) infos.push({ entry: sub[s], info: info });
|
|
1221
|
+
}
|
|
1222
|
+
sub = [];
|
|
1223
|
+
if (infos.length < 3) return;
|
|
1224
|
+
var anyPadded = infos.some(function (e) { return e.info.pad >= 2; });
|
|
1225
|
+
if (!anyPadded) return; // compact single-space style — not aligned, not flagged
|
|
1226
|
+
var modalCol = _requireModalColumn(infos);
|
|
1227
|
+
for (var z = 0; z < infos.length; z++) {
|
|
1228
|
+
var e = infos[z];
|
|
1229
|
+
if (e.info.col === modalCol) continue; // already on the column — fine
|
|
1230
|
+
if (e.info.lhsLen + 1 > modalCol) continue; // LHS too wide to reach the column — house-style overshoot, exempt
|
|
1231
|
+
bad.push({
|
|
1232
|
+
file: rel,
|
|
1233
|
+
line: e.entry.n,
|
|
1234
|
+
content: e.entry.line.trim() +
|
|
1235
|
+
" — `=` off the require-block column (align every `=` in this " +
|
|
1236
|
+
"sub-block to one column, or reflow the group to compact single-space)",
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
for (var r = 0; r < region.length; r++) {
|
|
1241
|
+
if (region[r].kind === "blank" || region[r].kind === "comment") { flush(); continue; }
|
|
1242
|
+
sub.push(region[r]);
|
|
1243
|
+
}
|
|
1244
|
+
flush();
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
_report("top-of-file require blocks keep their `=` signs column-aligned " +
|
|
1248
|
+
"(per blank-or-comment-separated sub-block; compact single-space " +
|
|
1249
|
+
"blocks and unfittable-LHS lines exempt)",
|
|
1250
|
+
bad);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1051
1253
|
function testRequireBindingConsistency() {
|
|
1052
1254
|
// class: require-binding-name
|
|
1053
1255
|
// For each module imported via the plain `var <name> = require("<module>")`
|
|
@@ -2104,15 +2306,16 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
2104
2306
|
// SHINGLE_SIZES — token-window sizes scanned. Smaller catches
|
|
2105
2307
|
// finer-grain idiom (3-call chains); larger catches whole
|
|
2106
2308
|
// function bodies. Multi-pass produces both.
|
|
2107
|
-
// MIN_DISTINCT_FILES —
|
|
2108
|
-
//
|
|
2309
|
+
// MIN_DISTINCT_FILES — min files for a shingle to enter the
|
|
2310
|
+
// advisory inventory (2+). The hard-fail gate is the separate
|
|
2311
|
+
// STRONG_MIN_FILES = 3 (a shape in 3+ files fails the build).
|
|
2109
2312
|
// MIN_DISTINCT_TOKENS — skip shingles whose tokens are mostly
|
|
2110
2313
|
// punctuation / repeated closer chars.
|
|
2111
2314
|
// MAX_REPORTED_PER_LENGTH — cap to keep the report scannable;
|
|
2112
2315
|
// biggest-N hits are surfaced first because they represent the
|
|
2113
2316
|
// largest primitive opportunities.
|
|
2114
2317
|
var SHINGLE_SIZES = [60, 50, 40, 30, 22, 16, 12, 8];
|
|
2115
|
-
var MIN_DISTINCT_FILES = 2; //
|
|
2318
|
+
var MIN_DISTINCT_FILES = 2; // 2+ files → advisory inventory (STRONG_MIN_FILES = 3 hard-fails)
|
|
2116
2319
|
var MIN_DISTINCT_TOKENS = 5;
|
|
2117
2320
|
var _MAX_REPORTED_PER_LENGTH = 5000;
|
|
2118
2321
|
|
|
@@ -2590,7 +2793,6 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
2590
2793
|
mode: "family-subset",
|
|
2591
2794
|
files: [
|
|
2592
2795
|
"lib/ai-disclosure.js:chatbot",
|
|
2593
|
-
"lib/auth/dpop.js:_canonicalJwk",
|
|
2594
2796
|
"lib/auth/sd-jwt-vc-holder.js:store",
|
|
2595
2797
|
"lib/compliance-sanctions.js:screen",
|
|
2596
2798
|
"lib/dora.js:_validateReportInput",
|
|
@@ -2992,7 +3194,6 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
2992
3194
|
files: [
|
|
2993
3195
|
"lib/guard-email.js:_detectAddressIssues",
|
|
2994
3196
|
"lib/middleware/scim-server.js:_parseQuery",
|
|
2995
|
-
"lib/self-update.js:_splitSemver",
|
|
2996
3197
|
],
|
|
2997
3198
|
reason: "Three unrelated string-parser primitives that incidentally share a 50-token charCodeAt-driven scan shingle. _detectAddressIssues walks RFC 5322 addr-spec bytes; _parseQuery walks SCIM filter tokens (RFC 7644 §3.4.2.2); _splitSemver walks SemVer 2.0.0 §2 version-core + pre-release + build identifiers. Each owns a domain-specific error class. Consolidation would couple RFC 5322 / RFC 7644 / SemVer parsing into one primitive none of them want.",
|
|
2998
3199
|
},
|
|
@@ -3081,7 +3282,6 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
3081
3282
|
"lib/mail-dkim.js:bootstrap",
|
|
3082
3283
|
"lib/mail-dkim.js:dualSigner",
|
|
3083
3284
|
"lib/mail-mdn.js:_generateBoundary",
|
|
3084
|
-
"lib/mail-mdn.js:_validateOpts",
|
|
3085
3285
|
"lib/mail-mdn.js:build",
|
|
3086
3286
|
"lib/self-update.js:poll",
|
|
3087
3287
|
"lib/watcher.js:_detectAutoMode",
|
|
@@ -3409,7 +3609,6 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
3409
3609
|
// the RFC 8620 / RFC 8621 wire-protocol shape.
|
|
3410
3610
|
"lib/guard-jmap.js:<top>",
|
|
3411
3611
|
"lib/guard-jmap.js:validate",
|
|
3412
|
-
"lib/guard-jmap.js:_resolveProfile",
|
|
3413
3612
|
"lib/guard-jmap.js:compliancePosture",
|
|
3414
3613
|
],
|
|
3415
3614
|
reason: "Guard-family scaffolding required by `b.gateContract` — every guard ships PROFILES (strict/balanced/permissive) + COMPLIANCE_POSTURES (hipaa/pci-dss/gdpr/soc2) + _resolveProfile dispatcher + a top-level @module JSDoc block. Each member's profile body / posture vocab / validate() body is domain-distinct; the surrounding skeleton is the family contract. Consolidation would erase the per-guard validation rules and break the `b.guardAll` registration pattern.",
|
|
@@ -3551,7 +3750,6 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
3551
3750
|
"lib/agent-tenant.js:_checkDestroyPreconditions",
|
|
3552
3751
|
"lib/agent-idempotency.js:_put",
|
|
3553
3752
|
"lib/auth/dpop.js:verify",
|
|
3554
|
-
"lib/auth/dpop.js:_canonicalJwk",
|
|
3555
3753
|
"lib/auth/sd-jwt-vc-holder.js:store",
|
|
3556
3754
|
"lib/backup/index.js:scheduleTest",
|
|
3557
3755
|
"lib/break-glass.js:_validatePolicySet",
|
|
@@ -3632,7 +3830,6 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
3632
3830
|
// jwk canonicalization). Distinct error classes.
|
|
3633
3831
|
mode: "family-subset",
|
|
3634
3832
|
files: [
|
|
3635
|
-
"lib/auth/dpop.js:_canonicalJwk",
|
|
3636
3833
|
"lib/auth/dpop.js:verify",
|
|
3637
3834
|
"lib/auth/sd-jwt-vc-holder.js:store",
|
|
3638
3835
|
"lib/compliance-sanctions.js:screen",
|
|
@@ -3727,16 +3924,6 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
3727
3924
|
],
|
|
3728
3925
|
reason: "Comma-separated header value parser walking pieces and splitting on `=` per piece. Each enforces a different grammar (RFC 9213 directive list, RFC 9111 Cache-Control directives, Sec-CH-UA brand-member params, RFC 7240/tus.io upload metadata, RFC 9110 quality-list / Accept-* header). Consolidating would couple unrelated header families.",
|
|
3729
3926
|
},
|
|
3730
|
-
{
|
|
3731
|
-
mode: "family-subset",
|
|
3732
|
-
files: [
|
|
3733
|
-
"lib/cdn-cache-control.js:_splitTopLevelCommas",
|
|
3734
|
-
"lib/client-hints.js:_splitTopLevelSemis",
|
|
3735
|
-
"lib/http-client-cache.js:_splitTopLevelCommas",
|
|
3736
|
-
"lib/http-message-signature.js:_splitTopLevelSemis",
|
|
3737
|
-
],
|
|
3738
|
-
reason: "Quote-aware top-level structured-fields splitter — walks a string respecting RFC 8941 §3.3.3 quoted-string state with backslash-escape so `,` (cdn-cache-control / http-client-cache) or `;` (client-hints brand-member params / http-message-signature Signature-Input params) inside quoted-string values doesn't split mid-value. Same shape replicated across four parsers because they each split on a different delimiter for a different RFC; consolidation candidate via a shared `b.structuredFields.splitTopLevel(s, sep)` helper but the per-file copy is intentional pending the extraction (operator-grep finds the splitter inside the file that uses it).",
|
|
3739
|
-
},
|
|
3740
3927
|
{
|
|
3741
3928
|
mode: "family-subset",
|
|
3742
3929
|
files: [
|
|
@@ -4129,7 +4316,6 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
4129
4316
|
{
|
|
4130
4317
|
mode: "family-subset",
|
|
4131
4318
|
files: [
|
|
4132
|
-
"lib/auth/dpop.js:_canonicalJwk",
|
|
4133
4319
|
"lib/compliance-sanctions.js:_emitAudit",
|
|
4134
4320
|
"lib/compliance-sanctions.js:_emitMetric",
|
|
4135
4321
|
"lib/compliance-sanctions.js:create",
|
|
@@ -4403,7 +4589,7 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
4403
4589
|
"lib/auth/fido-mds3.js:_b64urlDecode",
|
|
4404
4590
|
"lib/auth/fido-mds3.js:_parseJws",
|
|
4405
4591
|
"lib/auth/fido-mds3.js:_verifyJws",
|
|
4406
|
-
"lib/auth/fido-mds3.js:
|
|
4592
|
+
"lib/auth/fido-mds3.js:_verifyJws",
|
|
4407
4593
|
"lib/auth/fido-mds3.js:_verifyParamsForAlg",
|
|
4408
4594
|
"lib/auth/fido-mds3.js:fetch",
|
|
4409
4595
|
"lib/auth/fido-mds3.js:verifyAuthenticator",
|
|
@@ -4620,7 +4806,7 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
4620
4806
|
files: [
|
|
4621
4807
|
"lib/guard-list-id.js:_refuse",
|
|
4622
4808
|
"lib/guard-list-unsubscribe.js:_verdict",
|
|
4623
|
-
"lib/guard-smtp-command.js:
|
|
4809
|
+
"lib/guard-smtp-command.js:_parseAuthCommandSyntax",
|
|
4624
4810
|
"lib/safe-dns.js:_decodeOpt",
|
|
4625
4811
|
],
|
|
4626
4812
|
reason: "Three independently-domain'd helpers (List-Unsubscribe verdict assembly / SMTP AUTH mech validation / DNS OPT pseudo-RR decode) share local-var declaration + return-shape token sequence. Bodies are domain-distinct (different field names, different error checks). Consolidation would couple unrelated parsers.",
|
|
@@ -5029,7 +5215,6 @@ async function testNoDuplicateCodeBlocks() {
|
|
|
5029
5215
|
},
|
|
5030
5216
|
{
|
|
5031
5217
|
files: [
|
|
5032
|
-
"lib/auth/dpop.js:_canonicalJwk",
|
|
5033
5218
|
"lib/auth/sd-jwt-vc-holder.js:store",
|
|
5034
5219
|
"lib/compliance-sanctions.js:screen",
|
|
5035
5220
|
"lib/dora.js:_validateReportInput",
|
|
@@ -5743,7 +5928,7 @@ function testNoDeniedVendors() {
|
|
|
5743
5928
|
|
|
5744
5929
|
// ---- Pattern 42: state-stamps in user-facing docs (smoke test the wiki) ----
|
|
5745
5930
|
|
|
5746
|
-
function
|
|
5931
|
+
function testStateStampScanningDeferred() {
|
|
5747
5932
|
// feedback_no_state_stamps_in_docs.md — version numbers / test counts
|
|
5748
5933
|
// in README / SECURITY / CONTRIBUTING etc. rot the moment the next
|
|
5749
5934
|
// release ships. We can't easily grep these via the lib walker, but
|
|
@@ -7974,7 +8159,7 @@ var KNOWN_ANTIPATTERNS = [
|
|
|
7974
8159
|
requires: /_checkDualControlGate|_resolveDualControlGate|dualControlGrant/,
|
|
7975
8160
|
skipCommentLines: true,
|
|
7976
8161
|
allowlist: [],
|
|
7977
|
-
reason: "v0.14.7 — audit-chain purge is irreversible and tamper-evidence-destroying. The discipline: the chain may only be truncated under a two-authorizer dual-control grant. db.js defines the gate (_checkDualControlGate) and audit-tools.js purge() resolves+enforces it (_resolveDualControlGate) before calling db().purgeAuditChain
|
|
8162
|
+
reason: "v0.14.7 — audit-chain purge is irreversible and tamper-evidence-destroying. The discipline: the chain may only be truncated under a two-authorizer dual-control grant. db.js defines the gate (_checkDualControlGate) and audit-tools.js purge() resolves+enforces it (_resolveDualControlGate) before calling db().purgeAuditChain — the only live call site, which satisfies the companion check (db.js's own purgeAuditChain references are its definition + JSDoc, not call sites, so it never matches). A future file that calls purgeAuditChain without naming the gate would let a single operator delete evidence — exactly the shape this blocks.",
|
|
7978
8163
|
},
|
|
7979
8164
|
|
|
7980
8165
|
{
|
|
@@ -10788,6 +10973,7 @@ async function run() {
|
|
|
10788
10973
|
testNoTierTerminologyInLib();
|
|
10789
10974
|
testNoInlineRequires();
|
|
10790
10975
|
testRequireBindingConsistency();
|
|
10976
|
+
testRequireBlockAlignment();
|
|
10791
10977
|
testNodeBuiltinPrefixConsistency();
|
|
10792
10978
|
testNoInternalBindingNameInProse();
|
|
10793
10979
|
testNoDynamicRequires();
|
|
@@ -10827,7 +11013,7 @@ async function run() {
|
|
|
10827
11013
|
testNoHandrolledUrlBuild();
|
|
10828
11014
|
testNoHandrolledRetryLoop();
|
|
10829
11015
|
await testNoDuplicateCodeBlocks();
|
|
10830
|
-
|
|
11016
|
+
testStateStampScanningDeferred();
|
|
10831
11017
|
testNoLegacyUrlFormat();
|
|
10832
11018
|
testNoRawHeadersDistinct();
|
|
10833
11019
|
testNoDenseWildcardRunsInLib();
|
package/package.json
CHANGED