@blamejs/core 0.18.49 → 0.18.51
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 +82 -0
- package/NOTICE +1 -1
- package/README.md +1 -1
- package/lib/ai-input.js +25 -3
- package/lib/db-query.js +32 -4
- package/lib/gate-contract.js +11 -2
- package/lib/guard-filename.js +6 -2
- package/lib/guard-jwt.js +2 -2
- package/lib/json-path.js +54 -4
- package/lib/json-schema.js +212 -10
- package/lib/mail-bimi.js +279 -6
- package/lib/mail-scan.js +21 -1
- package/lib/mail-server-submission.js +8 -1
- package/lib/mail-sieve.js +61 -19
- package/lib/markup-tokenizer.js +88 -0
- package/lib/vendor/MANIFEST.json +12 -12
- package/lib/vendor/blamejs-pki.cjs +835 -41
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/mail-sieve.js
CHANGED
|
@@ -43,9 +43,10 @@
|
|
|
43
43
|
* parse time (require 'comparator-NAME' not in KNOWN_CAPABILITIES).
|
|
44
44
|
*
|
|
45
45
|
* Match-type wildcards: `:matches` uses `*` (any sequence) and `?`
|
|
46
|
-
* (one byte), per RFC 5228 §2.7.1.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
* (one byte), per RFC 5228 §2.7.1. The pattern is matched directly
|
|
47
|
+
* against the value rather than translated into a regular
|
|
48
|
+
* expression, so a script author cannot spend the delivery thread's
|
|
49
|
+
* CPU by writing a pattern that a backtracking engine explores.
|
|
49
50
|
*
|
|
50
51
|
* The interpreter does NOT execute multi-script chains, sieve
|
|
51
52
|
* `include`s (RFC 6609), `notify` actions (RFC 5435), or `vacation`
|
|
@@ -63,7 +64,6 @@ var safeSieve = require("./safe-sieve");
|
|
|
63
64
|
var { defineClass } = require("./framework-error");
|
|
64
65
|
var numericBounds = require("./numeric-bounds");
|
|
65
66
|
var validateOpts = require("./validate-opts");
|
|
66
|
-
var codepointClass = require("./codepoint-class");
|
|
67
67
|
|
|
68
68
|
var MailSieveError = defineClass("MailSieveError", { alwaysPermanent: true });
|
|
69
69
|
|
|
@@ -124,22 +124,64 @@ function _envelopeAddresses(env, key) {
|
|
|
124
124
|
|
|
125
125
|
// ---- match-type ---------------------------------------------------------
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
// RFC 5228 §2.7.1 — `*` matches any sequence, `?` matches exactly one. Matched
|
|
128
|
+
// directly rather than translated into a regular expression.
|
|
129
|
+
//
|
|
130
|
+
// The translation is the obvious implementation and it is a denial of service.
|
|
131
|
+
// `*` becomes `.*`, so a pattern with N stars hands a backtracking engine N
|
|
132
|
+
// independent `.*` groups, and on a subject that never supplies the trailing
|
|
133
|
+
// literal the engine tries every way of dividing the subject between them: the
|
|
134
|
+
// cost is polynomial in the subject length with degree N. Three stars measured
|
|
135
|
+
// x7.7 per doubling of the subject, and ten stars — which a mailbox owner can
|
|
136
|
+
// type without meaning anything by it — did not finish on a 64-character
|
|
137
|
+
// Subject. A Sieve script is USER-authored in most deployments, and the gas
|
|
138
|
+
// budget does not help: gas counts operations, and one match is one operation
|
|
139
|
+
// however long the engine spends inside it.
|
|
140
|
+
//
|
|
141
|
+
// This walks the two strings with a single remembered wildcard position to fall
|
|
142
|
+
// back to. It is not linear: `*` followed by a long literal, matched against a
|
|
143
|
+
// subject that keeps almost matching it, re-walks the literal from each new
|
|
144
|
+
// alignment, so the worst case is the product of the two lengths. Measured at
|
|
145
|
+
// 3.9ms for a 500-character pattern against 2,000 characters, 11.4ms at
|
|
146
|
+
// 1,000 x 4,000, and 52.2ms at 2,000 x 8,000 — the quadratic shape, and a
|
|
147
|
+
// bound that both the script size and the header size already cap.
|
|
148
|
+
//
|
|
149
|
+
// What it is NOT is a function of how many wildcards the pattern contains,
|
|
150
|
+
// which is the property that mattered: the regex translation was polynomial
|
|
151
|
+
// with degree equal to the wildcard count, so a tenth `*` cost another factor
|
|
152
|
+
// of the subject length and a short Subject stopped returning at all.
|
|
153
|
+
// `i;ascii-casemap` folds US-ASCII A-Z to a-z and leaves every other character
|
|
154
|
+
// alone (RFC 4790 §9.2), which is also the only fold that preserves length.
|
|
155
|
+
// `String.prototype.toLowerCase` does not: `"İ".toLowerCase()` is two
|
|
156
|
+
// UTF-16 units, so folding the whole subject up front would shift every
|
|
157
|
+
// position after it and leave `?` — which matches exactly one character —
|
|
158
|
+
// facing two.
|
|
159
|
+
function _asciiLower(ch) {
|
|
160
|
+
var c = ch.charCodeAt(0);
|
|
161
|
+
return (c >= 0x41 && c <= 0x5A) ? String.fromCharCode(c + 0x20) : ch;
|
|
129
162
|
}
|
|
130
163
|
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
var
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
164
|
+
function _wildcardMatches(pattern, subject, caseInsensitive) {
|
|
165
|
+
var pat = pattern, sub = subject;
|
|
166
|
+
var p = 0, s = 0;
|
|
167
|
+
var starP = -1, starS = 0;
|
|
168
|
+
function same(a, bChar) {
|
|
169
|
+
return caseInsensitive ? _asciiLower(a) === _asciiLower(bChar) : a === bChar;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
while (s < sub.length) {
|
|
173
|
+
if (p < pat.length && (pat[p] === "?" || same(pat[p], sub[s]))) { p += 1; s += 1; continue; }
|
|
174
|
+
if (p < pat.length && pat[p] === "*") { starP = p; starS = s; p += 1; continue; }
|
|
175
|
+
// No match here. If a `*` came earlier, give it one more character and
|
|
176
|
+
// resume from just after it; otherwise the subject cannot match.
|
|
177
|
+
if (starP === -1) return false;
|
|
178
|
+
starS += 1;
|
|
179
|
+
s = starS;
|
|
180
|
+
p = starP + 1;
|
|
181
|
+
}
|
|
182
|
+
// Trailing stars can absorb the empty remainder; anything else cannot.
|
|
183
|
+
while (p < pat.length && pat[p] === "*") p += 1;
|
|
184
|
+
return p === pat.length;
|
|
143
185
|
}
|
|
144
186
|
|
|
145
187
|
function _matches(haystack, needle, matchType, comparator) {
|
|
@@ -155,7 +197,7 @@ function _matches(haystack, needle, matchType, comparator) {
|
|
|
155
197
|
: haystack.indexOf(needle) !== -1;
|
|
156
198
|
}
|
|
157
199
|
if (matchType === "matches") {
|
|
158
|
-
return
|
|
200
|
+
return _wildcardMatches(needle, haystack, ci);
|
|
159
201
|
}
|
|
160
202
|
throw new MailSieveError("mail-sieve/bad-match-type",
|
|
161
203
|
"unknown match-type: " + matchType);
|
package/lib/markup-tokenizer.js
CHANGED
|
@@ -61,6 +61,92 @@ function splitTagNameAttrs(inner, tailChars) {
|
|
|
61
61
|
var HTML_TAG_NAME_TAIL = codepointClass.ASCII_ALNUM + ":-";
|
|
62
62
|
var XML_TAG_NAME_TAIL = codepointClass.ASCII_ALNUM + ":-_";
|
|
63
63
|
|
|
64
|
+
// The five entities XML predefines. Everything else in an attribute value is
|
|
65
|
+
// either a numeric character reference or a reference to an entity a DTD
|
|
66
|
+
// declared, which nothing here expands: an attribute value is compared, not
|
|
67
|
+
// executed, and expanding declared entities is the door XXE comes through.
|
|
68
|
+
var XML_PREDEFINED_REFS = {
|
|
69
|
+
amp: "&", lt: "<", gt: ">", quot: "\"", apos: "'",
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
var DEC_DIGITS_RE = /^[0-9]+$/;
|
|
73
|
+
var HEX_DIGITS_RE = /^[0-9A-Fa-f]+$/;
|
|
74
|
+
|
|
75
|
+
// U+10FFFF is six hex digits and 1114111 is seven decimal ones.
|
|
76
|
+
var MAX_CHAR_REF_DIGITS = 7; // digit count, not bytes
|
|
77
|
+
|
|
78
|
+
// decodeCharRefs(s) — an attribute value with its numeric character references
|
|
79
|
+
// and predefined entities resolved, so a value can be COMPARED against what it
|
|
80
|
+
// denotes rather than against one spelling of it. `svg` and `svg` are the
|
|
81
|
+
// same namespace to an XML processor, and a scanner that compares the lexical
|
|
82
|
+
// form disagrees with it.
|
|
83
|
+
//
|
|
84
|
+
// An unrecognized or malformed reference is left exactly as written rather than
|
|
85
|
+
// dropped, so nothing a caller then matches on can be manufactured by deleting
|
|
86
|
+
// characters. Returns the input unchanged when it holds no `&` at all.
|
|
87
|
+
function decodeCharRefs(s) {
|
|
88
|
+
if (s.indexOf("&") === -1) return s;
|
|
89
|
+
var out = "";
|
|
90
|
+
var i = 0;
|
|
91
|
+
while (i < s.length) {
|
|
92
|
+
var amp = s.indexOf("&", i);
|
|
93
|
+
if (amp === -1) { out += s.slice(i); break; }
|
|
94
|
+
out += s.slice(i, amp);
|
|
95
|
+
var semi = s.indexOf(";", amp + 1);
|
|
96
|
+
if (semi === -1) { out += s.slice(amp); break; }
|
|
97
|
+
var body = s.slice(amp + 1, semi);
|
|
98
|
+
var decoded = null;
|
|
99
|
+
if (body.charAt(0) === "#") {
|
|
100
|
+
var hex = body.charAt(1) === "x" || body.charAt(1) === "X";
|
|
101
|
+
var digits = hex ? body.slice(2) : body.slice(1);
|
|
102
|
+
// XML puts no limit on leading zeros, so what gets bounded is the
|
|
103
|
+
// SIGNIFICANT digits, not the lexical run: a cap on the written form
|
|
104
|
+
// refuses `h`, which an XML processor resolves normally.
|
|
105
|
+
// Skipping the zeros is a plain index walk, so the unbounded part of the
|
|
106
|
+
// input never reaches a pattern. The largest code point is six hex digits
|
|
107
|
+
// or seven decimal ones, and a refused reference is left exactly as
|
|
108
|
+
// written, so bounding it cannot manufacture a match.
|
|
109
|
+
var firstSignificant = 0;
|
|
110
|
+
while (firstSignificant < digits.length - 1 &&
|
|
111
|
+
digits.charAt(firstSignificant) === "0") firstSignificant += 1;
|
|
112
|
+
var significant = digits.slice(firstSignificant);
|
|
113
|
+
var wellFormed = significant.length > 0 &&
|
|
114
|
+
significant.length <= MAX_CHAR_REF_DIGITS &&
|
|
115
|
+
(hex ? HEX_DIGITS_RE.test(significant) : DEC_DIGITS_RE.test(significant));
|
|
116
|
+
if (wellFormed) {
|
|
117
|
+
var cp = parseInt(significant, hex ? 16 : 10);
|
|
118
|
+
// A lone surrogate is not a character; neither is anything past the
|
|
119
|
+
// last plane, nor U+0000, which XML's Char production excludes. Each
|
|
120
|
+
// would either throw out of fromCodePoint or put a value in the string
|
|
121
|
+
// that no document can contain.
|
|
122
|
+
if (cp > 0 && cp <= 0x10FFFF && !(cp >= 0xD800 && cp <= 0xDFFF)) {
|
|
123
|
+
decoded = String.fromCodePoint(cp);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} else if (Object.prototype.hasOwnProperty.call(XML_PREDEFINED_REFS, body)) {
|
|
127
|
+
decoded = XML_PREDEFINED_REFS[body];
|
|
128
|
+
}
|
|
129
|
+
out += decoded === null ? s.slice(amp, semi + 1) : decoded;
|
|
130
|
+
i = semi + 1;
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// xmlCommentEnd(s, lt) — the same question for an XML document, where the
|
|
136
|
+
// answer is different. XML 1.0 §2.5 gives a comment exactly one terminator,
|
|
137
|
+
// "-->": there is no "--!>" and no abrupt "<!-->" / "<!--->" close. So the two
|
|
138
|
+
// grammars disagree in BOTH directions, and using the HTML reader on XML is a
|
|
139
|
+
// parser differential either way — it ends a comment early and reads the text
|
|
140
|
+
// after it as markup, and it ends one that XML says is still open.
|
|
141
|
+
//
|
|
142
|
+
// This module already names both tag-name grammars for the same reason; a
|
|
143
|
+
// caller picks the one its document is written in rather than restating it.
|
|
144
|
+
// Returns -1 when the comment is unterminated, as its HTML sibling does.
|
|
145
|
+
function xmlCommentEnd(s, lt) {
|
|
146
|
+
var end = s.indexOf("-->", lt + 4);
|
|
147
|
+
return end === -1 ? -1 : end + 3;
|
|
148
|
+
}
|
|
149
|
+
|
|
64
150
|
// htmlCommentEnd(s, lt) — given that an HTML comment opens at index `lt`
|
|
65
151
|
// (s.startsWith("<!--", lt)), return the index ONE PAST the comment's
|
|
66
152
|
// terminator per the WHATWG HTML tokenizer, not just the legacy "-->" form.
|
|
@@ -233,6 +319,8 @@ module.exports = {
|
|
|
233
319
|
scanToTagEnd: scanToTagEnd,
|
|
234
320
|
splitTagNameAttrs: splitTagNameAttrs,
|
|
235
321
|
htmlCommentEnd: htmlCommentEnd,
|
|
322
|
+
xmlCommentEnd: xmlCommentEnd,
|
|
323
|
+
decodeCharRefs: decodeCharRefs,
|
|
236
324
|
HTML_TAG_NAME_TAIL: HTML_TAG_NAME_TAIL,
|
|
237
325
|
XML_TAG_NAME_TAIL: XML_TAG_NAME_TAIL,
|
|
238
326
|
isMarkupSpace: isMarkupSpace,
|
package/lib/vendor/MANIFEST.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"server": "sha256:f3325f480cb8eb814fcb0baaa19336cbbf2b993f48624c6aa9600ffd69d0be5e",
|
|
22
22
|
"browser": "sha256:0ffd91540bcb586a29b56e52ee1c29df69097b50776beb4036a07558f7a4e12e"
|
|
23
23
|
},
|
|
24
|
-
"refreshedAt": "2026-08-
|
|
24
|
+
"refreshedAt": "2026-08-23T07:32:38.917Z"
|
|
25
25
|
},
|
|
26
26
|
"@noble/hashes": {
|
|
27
27
|
"version": "2.3.0",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"hashes": {
|
|
49
49
|
"browser": "sha256:dfe4b7ae3c9880e388c8da4b68f44742b229b53afacd1e674179527e33da62b0"
|
|
50
50
|
},
|
|
51
|
-
"refreshedAt": "2026-08-
|
|
51
|
+
"refreshedAt": "2026-08-23T07:32:38.917Z"
|
|
52
52
|
},
|
|
53
53
|
"@noble/curves": {
|
|
54
54
|
"version": "2.3.0",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"hashes": {
|
|
71
71
|
"server": "sha256:b5fe88d1ea780d0581dee6145d666f89d46fc9531b5db35db2e5b16627840890"
|
|
72
72
|
},
|
|
73
|
-
"refreshedAt": "2026-08-
|
|
73
|
+
"refreshedAt": "2026-08-23T07:32:38.917Z",
|
|
74
74
|
"components": {
|
|
75
75
|
"@noble/hashes": {
|
|
76
76
|
"url": "https://github.com/paulmillr/noble-hashes",
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"server": "sha256:fab7ebe5737793862c473444f4ee5912f79dd1edec86683acbb4eecbca0f5892",
|
|
115
115
|
"browser": "sha256:cae1d5bbdc7184b202b6ca68df6e1db7b0d0f668c77809ded189ca7f271accc9"
|
|
116
116
|
},
|
|
117
|
-
"refreshedAt": "2026-08-
|
|
117
|
+
"refreshedAt": "2026-08-23T07:32:38.917Z",
|
|
118
118
|
"components": {
|
|
119
119
|
"@noble/hashes": {
|
|
120
120
|
"url": "https://github.com/paulmillr/noble-hashes",
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
},
|
|
149
149
|
"runtime_artifact": "lib/vendor/common-passwords-top-10000.data.js",
|
|
150
150
|
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
151
|
-
"refreshedAt": "2026-08-
|
|
151
|
+
"refreshedAt": "2026-08-23T07:32:38.917Z"
|
|
152
152
|
},
|
|
153
153
|
"bimi-trust-anchors": {
|
|
154
154
|
"version": "operator-managed",
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
},
|
|
174
174
|
"runtime_artifact": "lib/vendor/bimi-trust-anchors.data.js",
|
|
175
175
|
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
176
|
-
"refreshedAt": "2026-08-
|
|
176
|
+
"refreshedAt": "2026-08-23T07:32:38.917Z"
|
|
177
177
|
},
|
|
178
178
|
"publicsuffix-list": {
|
|
179
179
|
"version": "master",
|
|
@@ -193,10 +193,10 @@
|
|
|
193
193
|
},
|
|
194
194
|
"runtime_artifact": "lib/vendor/public-suffix-list.data.js",
|
|
195
195
|
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
196
|
-
"refreshedAt": "2026-08-
|
|
196
|
+
"refreshedAt": "2026-08-23T07:32:38.917Z"
|
|
197
197
|
},
|
|
198
198
|
"@blamejs/pki": {
|
|
199
|
-
"version": "0.5.
|
|
199
|
+
"version": "0.5.28",
|
|
200
200
|
"license": "Apache-2.0",
|
|
201
201
|
"author": "blamejs",
|
|
202
202
|
"source": "https://github.com/blamejs/pki",
|
|
@@ -216,12 +216,12 @@
|
|
|
216
216
|
"server": "lib/vendor/blamejs-pki.cjs"
|
|
217
217
|
},
|
|
218
218
|
"bundler": "esbuild --format=cjs --platform=node --external:crypto --external:node:crypto",
|
|
219
|
-
"bundledAt": "2026-08-
|
|
220
|
-
"cpe": "cpe:2.3:a:blamejs:pki:0.5.
|
|
219
|
+
"bundledAt": "2026-08-23T00:00:00Z",
|
|
220
|
+
"cpe": "cpe:2.3:a:blamejs:pki:0.5.28:*:*:*:*:node.js:*:*",
|
|
221
221
|
"hashes": {
|
|
222
|
-
"server": "sha256:
|
|
222
|
+
"server": "sha256:a11e84272034dc4065dac44f55b722b3b4e1803ddeb27b09cbe6d8644225209c"
|
|
223
223
|
},
|
|
224
|
-
"refreshedAt": "2026-08-
|
|
224
|
+
"refreshedAt": "2026-08-23T07:32:38.917Z"
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
}
|