@blamejs/core 0.18.53 → 0.18.55
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 +228 -0
- package/NOTICE +1 -1
- package/README.md +5 -5
- package/lib/agent-audit.js +27 -2
- package/lib/ai-adverse-decision.js +18 -2
- package/lib/audit-sign.js +24 -5
- package/lib/auth/passkey.js +4 -1
- package/lib/codepoint-class.js +72 -0
- package/lib/cookies.js +7 -10
- package/lib/credential-hash.js +8 -1
- package/lib/crypto.js +7 -5
- package/lib/db-file-lifecycle.js +14 -3
- package/lib/db.js +505 -49
- package/lib/guard-auth.js +34 -11
- package/lib/guard-filename.js +41 -33
- package/lib/guard-html.js +10 -2
- package/lib/guard-list-unsubscribe.js +6 -1
- package/lib/guard-managesieve-command.js +73 -12
- package/lib/guard-regex.js +3 -5
- package/lib/guard-smtp-command.js +20 -4
- package/lib/guard-svg.js +6 -1
- package/lib/guard-yaml.js +60 -15
- package/lib/http-client.js +17 -3
- package/lib/mail-agent.js +29 -13
- package/lib/mail-arc-sign.js +40 -7
- package/lib/mail-auth.js +134 -22
- package/lib/mail-crypto-pgp.js +1 -1
- package/lib/mail-dkim.js +80 -11
- package/lib/mail-helo.js +10 -0
- package/lib/mail-rbl.js +10 -3
- package/lib/mail-send-deliver.js +151 -32
- package/lib/mail-server-imap.js +186 -89
- package/lib/mail-server-jmap.js +31 -4
- package/lib/mail-server-managesieve.js +198 -42
- package/lib/mail-server-mx.js +191 -38
- package/lib/mail-server-net.js +281 -1
- package/lib/mail-server-pop3.js +89 -41
- package/lib/mail-server-rate-limit.js +104 -6
- package/lib/mail-server-submission.js +183 -35
- package/lib/mail-server-tls.js +48 -3
- package/lib/mail-store.js +33 -11
- package/lib/mail.js +355 -17
- package/lib/mcp.js +11 -3
- package/lib/middleware/bearer-auth.js +6 -1
- package/lib/middleware/fetch-metadata.js +5 -1
- package/lib/middleware/headers.js +7 -10
- package/lib/middleware/require-mtls.js +8 -1
- package/lib/network-dns-resolver.js +71 -8
- package/lib/network-dns.js +26 -0
- package/lib/network-smtp-policy.js +42 -10
- package/lib/network-tls.js +18 -0
- package/lib/redact.js +13 -3
- package/lib/retention.js +22 -2
- package/lib/safe-mount-info.js +39 -6
- package/lib/safe-smtp.js +96 -1
- package/lib/safe-url.js +8 -2
- package/lib/self-update.js +4 -1
- package/lib/vendor/MANIFEST.json +12 -12
- package/lib/vendor/blamejs-pki.cjs +672 -75
- package/lib/watcher.js +31 -6
- package/lib/ws-client.js +17 -2
- package/lib/yaml-lex.js +55 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/watcher.js
CHANGED
|
@@ -231,7 +231,7 @@ var AUTO_PROBE_POLL_FSTYPES = new Set([
|
|
|
231
231
|
"vboxsf",
|
|
232
232
|
]);
|
|
233
233
|
|
|
234
|
-
function _detectAutoMode(rootPath) {
|
|
234
|
+
function _detectAutoMode(rootPath, probe) {
|
|
235
235
|
// Sync probe — looks at the kernel's view of the mount carrying the
|
|
236
236
|
// watcher's root and decides whether fs.watch will deliver events.
|
|
237
237
|
// Three signals contribute, in priority order:
|
|
@@ -246,7 +246,21 @@ function _detectAutoMode(rootPath) {
|
|
|
246
246
|
// 3. Otherwise — fs.
|
|
247
247
|
//
|
|
248
248
|
// Returns { mode, reason, fsType, inContainer }.
|
|
249
|
-
|
|
249
|
+
// Injectable so the branches that only exist on a restricted Linux can be
|
|
250
|
+
// driven from any host. A rule about what happens when mountinfo cannot be
|
|
251
|
+
// read is untestable while reaching it requires unmounting /proc.
|
|
252
|
+
var platform = (probe && probe.platform) || process.platform;
|
|
253
|
+
var readEntries = (probe && probe.readEntries) ||
|
|
254
|
+
function () { return safeMountInfo.read(); };
|
|
255
|
+
// Injected for the same reason as readEntries, and for one more: "/.dockerenv"
|
|
256
|
+
// is drive-relative on Windows, so a direct probe there asks about C:\.dockerenv
|
|
257
|
+
// rather than about being in a container. It is only reached below the
|
|
258
|
+
// non-Linux return, but reaching a POSIX path through the injected reader is
|
|
259
|
+
// what keeps that ordering from being the only thing holding it up.
|
|
260
|
+
var exists = (probe && probe.exists) ||
|
|
261
|
+
function (p) { return nodeFs.existsSync(p); };
|
|
262
|
+
|
|
263
|
+
if (platform !== "linux") {
|
|
250
264
|
// macOS + Windows fs.watch backends use FSEvents + ReadDirectoryChangesW
|
|
251
265
|
// respectively, which DO deliver recursive events natively for
|
|
252
266
|
// operator-owned local filesystems. Containerized Linux is the
|
|
@@ -255,7 +269,7 @@ function _detectAutoMode(rootPath) {
|
|
|
255
269
|
}
|
|
256
270
|
|
|
257
271
|
var inContainer = false;
|
|
258
|
-
try { inContainer =
|
|
272
|
+
try { inContainer = exists("/.dockerenv"); }
|
|
259
273
|
catch (_e) { inContainer = false; }
|
|
260
274
|
|
|
261
275
|
// Route through b.safeMountInfo — single canonical parser that
|
|
@@ -263,13 +277,23 @@ function _detectAutoMode(rootPath) {
|
|
|
263
277
|
// mount check below. Pre-v0.11.6 this parsed inline; centralizing
|
|
264
278
|
// it means future container-escape / sealed-store / sandbox call
|
|
265
279
|
// sites inherit the discipline.
|
|
266
|
-
var entries =
|
|
280
|
+
var entries = readEntries();
|
|
281
|
+
// Not knowing which filesystem this is resolves toward polling, not toward
|
|
282
|
+
// fs.watch. On Linux mountinfo is always there, so failing to read it means
|
|
283
|
+
// /proc is unmounted or restricted — which is the containerized case this
|
|
284
|
+
// probe exists for, and the one where inotify is least likely to deliver.
|
|
285
|
+
// The two answers are not symmetric: polling costs stat calls the operator
|
|
286
|
+
// can see, while fs.watch on a filesystem that does not chain inotify drops
|
|
287
|
+
// events silently and nothing downstream can tell that it did.
|
|
267
288
|
if (entries === null || entries.length === 0) {
|
|
268
|
-
return { mode: "
|
|
289
|
+
return { mode: "poll", reason: "no-mountinfo", fsType: null, inContainer: inContainer };
|
|
269
290
|
}
|
|
270
291
|
var bestMatch = safeMountInfo.bestMatch(entries, rootPath);
|
|
271
292
|
if (!bestMatch) {
|
|
272
|
-
|
|
293
|
+
// Same reasoning: "/" matches every path on a healthy Linux, so no match
|
|
294
|
+
// means the mount table did not describe this root and its fstype is
|
|
295
|
+
// unknown rather than known-good.
|
|
296
|
+
return { mode: "poll", reason: "no-mount-match", fsType: null, inContainer: inContainer };
|
|
273
297
|
}
|
|
274
298
|
if (AUTO_PROBE_POLL_FSTYPES.has(bestMatch.fstype)) {
|
|
275
299
|
return {
|
|
@@ -687,4 +711,5 @@ module.exports = {
|
|
|
687
711
|
// create() enforces, instead of pinning hand-copied mirror constants.
|
|
688
712
|
MAX_IGNORE_PATTERN_LEN: MAX_IGNORE_PATTERN_LEN,
|
|
689
713
|
MAX_IGNORE_STAR_COUNT: MAX_IGNORE_STAR_COUNT,
|
|
714
|
+
_detectAutoModeForTest: _detectAutoMode,
|
|
690
715
|
};
|
package/lib/ws-client.js
CHANGED
|
@@ -361,7 +361,12 @@ class WsClient extends EventEmitter {
|
|
|
361
361
|
allowInternal: opts.allowInternal,
|
|
362
362
|
errorClass: WsClientError,
|
|
363
363
|
});
|
|
364
|
-
|
|
364
|
+
// `null` when the gate produced no pin at all, the array when it did —
|
|
365
|
+
// including an EMPTY one. An empty pin means the gate admitted no address,
|
|
366
|
+
// so there is nothing to connect to; treating it as "no pin" would hand the
|
|
367
|
+
// connect back to the ordinary resolver and re-open the rebinding window
|
|
368
|
+
// the pin exists to close.
|
|
369
|
+
this._ssrfPinnedIps = probe && Array.isArray(probe.ips) ? probe.ips : null;
|
|
365
370
|
this._dialParsed = dialParsed;
|
|
366
371
|
this._dialTlsOpts = dialTlsOpts;
|
|
367
372
|
}
|
|
@@ -405,12 +410,22 @@ class WsClient extends EventEmitter {
|
|
|
405
410
|
// private one between the check and the connect.
|
|
406
411
|
var pinnedIps = self._ssrfPinnedIps || null;
|
|
407
412
|
var lookup = null;
|
|
408
|
-
if (pinnedIps
|
|
413
|
+
if (pinnedIps) {
|
|
409
414
|
lookup = function (h, lookupOpts, cb) {
|
|
410
415
|
// Node's lookup callback signatures:
|
|
411
416
|
// (err, address, family) for legacy { all: false } (default)
|
|
412
417
|
// (err, addresses) for { all: true }
|
|
413
418
|
if (typeof lookupOpts === "function") { cb = lookupOpts; lookupOpts = {}; }
|
|
419
|
+
// An empty pin admits no address, so there is nothing to connect to.
|
|
420
|
+
// Answer the lookup with an error, which fails the dial through the
|
|
421
|
+
// socket's ordinary error path — reading `pinnedIps[0].address` here
|
|
422
|
+
// would throw a TypeError out of a lookup callback instead, which is a
|
|
423
|
+
// crash rather than a refusal.
|
|
424
|
+
if (pinnedIps.length === 0) {
|
|
425
|
+
var noneErr = new Error("ws.connect: the SSRF gate admitted no address for " + h);
|
|
426
|
+
noneErr.code = "ENOTFOUND";
|
|
427
|
+
return cb(noneErr);
|
|
428
|
+
}
|
|
414
429
|
var first = pinnedIps[0];
|
|
415
430
|
if (lookupOpts && lookupOpts.all) {
|
|
416
431
|
return cb(null, pinnedIps.map(function (ip) {
|
package/lib/yaml-lex.js
CHANGED
|
@@ -96,7 +96,44 @@ function _isVerbatimLine(line, indent) {
|
|
|
96
96
|
return false;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
// maskNonStructural(text) → the mask alone, which is what most callers want.
|
|
99
100
|
function maskNonStructural(text) {
|
|
101
|
+
return lexLines(text).masked;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// lexLines(text) → { masked, nodeStarts }.
|
|
105
|
+
//
|
|
106
|
+
// `nodeStarts` is a byte per source character, 1 where a NODE may begin: the
|
|
107
|
+
// first content character of a key, a value, or a flow-collection item. It
|
|
108
|
+
// answers the question the mask cannot, because the mask blanks a key's own text
|
|
109
|
+
// along with a comment's, and a screen that deliberately reports shapes which
|
|
110
|
+
// are not valid structure needs them apart.
|
|
111
|
+
//
|
|
112
|
+
// A byte array rather than a sparse one keyed by offset. Sparse indices this
|
|
113
|
+
// large put V8's array into dictionary mode, which measured ~200 bytes for each
|
|
114
|
+
// node in the document: a 1.7 MB file of short keys allocated 71 MB, a 40x
|
|
115
|
+
// amplification an attacker picks by choosing the shape of the input. A byte per
|
|
116
|
+
// character is 1x, flat, and known before the scan starts.
|
|
117
|
+
//
|
|
118
|
+
// The merge-key screen is that kind of screen. `<<:*d` with no space after the
|
|
119
|
+
// colon is not a mapping entry, and is reported anyway, because a parser variant
|
|
120
|
+
// may read it as a merge and that is the shape being smuggled. So its test
|
|
121
|
+
// cannot be "is this well-formed structure?" — it has to be "could a node begin
|
|
122
|
+
// here at all?", which is false inside a comment, a quoted body, a block
|
|
123
|
+
// scalar's body, and a plain scalar's continuation line, and true for both
|
|
124
|
+
// `<<: *b` and `<<:*b`.
|
|
125
|
+
//
|
|
126
|
+
// Enumerating those regions instead was tried and is the wrong shape: a first
|
|
127
|
+
// version excluded block-scalar bodies and quoted bodies and still reported a
|
|
128
|
+
// merge key written in a comment, on a plain scalar's continuation line, and
|
|
129
|
+
// after a document marker. There are more ways to be inside literal text than
|
|
130
|
+
// anyone lists from memory, and the lexer is already tracking the one property
|
|
131
|
+
// that separates them.
|
|
132
|
+
//
|
|
133
|
+
// Both come out of ONE pass. A second function walking the same grammar is how
|
|
134
|
+
// two answers to one question start disagreeing, which is the defect this
|
|
135
|
+
// module was written to end.
|
|
136
|
+
function lexLines(text) {
|
|
100
137
|
var src = String(text == null ? "" : text);
|
|
101
138
|
// Split on the newline ALONE, keeping any `\r` with the line it terminates.
|
|
102
139
|
// `codepointClass.splitLines` strips it, and rejoining those with "\n" drops
|
|
@@ -106,6 +143,13 @@ function maskNonStructural(text) {
|
|
|
106
143
|
// mask, because the locations it reports are confidently wrong.
|
|
107
144
|
var lines = src.split("\n");
|
|
108
145
|
var out = [];
|
|
146
|
+
var nodeStarts = new Uint8Array(src.length);
|
|
147
|
+
// The absolute offset line `li` begins at. Derived at the TOP of each
|
|
148
|
+
// iteration from the PREVIOUS line's raw length, rather than advanced at the
|
|
149
|
+
// bottom, because the loop body leaves by a dozen different `continue`s and
|
|
150
|
+
// any one of them forgetting to advance would silently shift every offset
|
|
151
|
+
// after it.
|
|
152
|
+
var lineBase = 0;
|
|
109
153
|
// A block scalar's body indentation is DETECTED from its first content line,
|
|
110
154
|
// which is what YAML specifies and the only thing that works. Two fixed
|
|
111
155
|
// columns were tried and each failed the other's case: measuring from the
|
|
@@ -149,6 +193,7 @@ function maskNonStructural(text) {
|
|
|
149
193
|
var plainFromDash = false;
|
|
150
194
|
|
|
151
195
|
for (var li = 0; li < lines.length; li += 1) {
|
|
196
|
+
if (li > 0) lineBase += lines[li - 1].length + 1; // + the "\n" that ended it
|
|
152
197
|
var raw = lines[li];
|
|
153
198
|
// The carriage return of a CRLF pair is a line TERMINATOR, not content, so
|
|
154
199
|
// it is held aside and put back verbatim. Letting it into the scan would
|
|
@@ -283,6 +328,14 @@ function maskNonStructural(text) {
|
|
|
283
328
|
var jsonKeyBefore = prevWasJsonKey;
|
|
284
329
|
prevWasJsonKey = false;
|
|
285
330
|
|
|
331
|
+
// The first content character of a node position. Whitespace carries the
|
|
332
|
+
// flag rather than clearing it, so this lands on the character that
|
|
333
|
+
// actually starts the node, and a `#` here opens a comment instead — no
|
|
334
|
+
// node begins inside one.
|
|
335
|
+
if (atNodeStart && !_isPlainSpace(ch) && ch !== "#") {
|
|
336
|
+
nodeStarts[lineBase + i] = 1;
|
|
337
|
+
}
|
|
338
|
+
|
|
286
339
|
// A comment opens on `#` at the start of the content or after whitespace,
|
|
287
340
|
// and runs to the end of the line. Its text is not YAML, whatever it
|
|
288
341
|
// says: `x: 1 # note !bang` names no tag.
|
|
@@ -478,7 +531,7 @@ function maskNonStructural(text) {
|
|
|
478
531
|
}
|
|
479
532
|
out.push(masked + (cr ? "\r" : ""));
|
|
480
533
|
}
|
|
481
|
-
return out.join("\n");
|
|
534
|
+
return { masked: out.join("\n"), nodeStarts: nodeStarts };
|
|
482
535
|
}
|
|
483
536
|
|
|
484
537
|
// A line kept for its structure, with any comment on it masked. Used for the
|
|
@@ -530,4 +583,5 @@ function _blanked(s) {
|
|
|
530
583
|
|
|
531
584
|
module.exports = {
|
|
532
585
|
maskNonStructural: maskNonStructural,
|
|
586
|
+
lexLines: lexLines,
|
|
533
587
|
};
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:e557e6e1-623d-4598-87fa-caac0c468199",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-08-
|
|
8
|
+
"timestamp": "2026-08-27T07:20:33.894Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/core@0.18.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.18.55",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.18.
|
|
25
|
+
"version": "0.18.55",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "The Node framework that owns its stack.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/core@0.18.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.18.55",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/core@0.18.
|
|
57
|
+
"ref": "@blamejs/core@0.18.55",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|