@blamejs/core 0.18.50 → 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 +54 -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-schema.js +130 -13
- package/lib/mail-bimi.js +279 -6
- 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-bimi.js
CHANGED
|
@@ -54,6 +54,7 @@ var nodeCrypto = require("node:crypto");
|
|
|
54
54
|
|
|
55
55
|
var asn1 = require("./asn1-der");
|
|
56
56
|
var C = require("./constants");
|
|
57
|
+
var codepointClass = require("./codepoint-class");
|
|
57
58
|
var pick = require("./pick");
|
|
58
59
|
var httpClient = require("./http-client");
|
|
59
60
|
var lazyRequire = require("./lazy-require");
|
|
@@ -87,6 +88,57 @@ var BIMI_RECORD_MAX_BYTES = C.BYTES.kib(2);
|
|
|
87
88
|
// refused at validate-time before any tokenization.
|
|
88
89
|
var TINY_PS_MAX_BYTES = C.BYTES.kib(32);
|
|
89
90
|
|
|
91
|
+
// The SVG namespace, which a root element carrying a prefix must bind that
|
|
92
|
+
// prefix to before the document is treated as a logo.
|
|
93
|
+
var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
94
|
+
|
|
95
|
+
// An XML namespace prefix is an NCName: XML 1.0's Name grammar without the
|
|
96
|
+
// colon. These are the 5th-edition NameStartChar ranges and the characters
|
|
97
|
+
// NameChar adds once a name has started, written as CODE POINTS rather than as
|
|
98
|
+
// a regular-expression character class for two reasons. Most of the boundaries
|
|
99
|
+
// do not render, so a literal class would put invisible characters in this
|
|
100
|
+
// file; and the grammar reaches past the basic plane, where a class over UTF-16
|
|
101
|
+
// code units cannot follow it.
|
|
102
|
+
//
|
|
103
|
+
// "Non-ASCII" is not the rule, and treating it as one is what this replaces.
|
|
104
|
+
// NameStartChar deliberately omits the C1 controls, most punctuation and the
|
|
105
|
+
// surrogate block, so a prefix built from those is not a name however faithfully
|
|
106
|
+
// the document then declares it.
|
|
107
|
+
var NCNAME_START_RANGES = [
|
|
108
|
+
[0x41, 0x5A], 0x5F, [0x61, 0x7A],
|
|
109
|
+
[0xC0, 0xD6], [0xD8, 0xF6], [0xF8, 0x2FF],
|
|
110
|
+
[0x370, 0x37D], [0x37F, 0x1FFF],
|
|
111
|
+
[0x200C, 0x200D], [0x2070, 0x218F], [0x2C00, 0x2FEF],
|
|
112
|
+
[0x3001, 0xD7FF], [0xF900, 0xFDCF], [0xFDF0, 0xFFFD],
|
|
113
|
+
[0x10000, 0xEFFFF],
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
var NCNAME_TAIL_RANGES = [
|
|
117
|
+
0x2D, 0x2E, [0x30, 0x39], 0xB7, [0x300, 0x36F], [0x203F, 0x2040],
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
// Stepped by CODE POINT, so an astral name character is read as the single
|
|
121
|
+
// character it is, and a lone surrogate is read as itself — which is in no
|
|
122
|
+
// range, so it is refused.
|
|
123
|
+
function _isNcName(s) {
|
|
124
|
+
if (s.length === 0) return false;
|
|
125
|
+
var started = false;
|
|
126
|
+
for (var i = 0; i < s.length; ) {
|
|
127
|
+
var cp = s.codePointAt(i);
|
|
128
|
+
i += cp > 0xFFFF ? 2 : 1;
|
|
129
|
+
var ok = codepointClass.inRanges(cp, NCNAME_START_RANGES) ||
|
|
130
|
+
(started && codepointClass.inRanges(cp, NCNAME_TAIL_RANGES));
|
|
131
|
+
if (!ok) return false;
|
|
132
|
+
started = true;
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Superseded by _isNcName above. Not re-typed on the way out: two of this
|
|
138
|
+
// class's bounds are characters that do not render, so what follows is left
|
|
139
|
+
// exactly as it was rather than reconstructed from a reading of it.
|
|
140
|
+
// var NC_NAME_RE = /^[A-Za-z_-][A-Za-z0-9._--]*$/;
|
|
141
|
+
|
|
90
142
|
// VMC / CMC fetch cap. Production VMCs are typically ~10-20 KiB;
|
|
91
143
|
// 256 KiB is a generous ceiling that still bounds the download against
|
|
92
144
|
// pathological responses. Operators with a stricter posture pass
|
|
@@ -571,7 +623,13 @@ function _tokenizeTinyPsSvg(s) {
|
|
|
571
623
|
// values are still accepted (the SVG profile is permissive on quoting).
|
|
572
624
|
function _parseTinyPsAttrs(src) {
|
|
573
625
|
var attrs = {};
|
|
574
|
-
|
|
626
|
+
// The lookbehind is what keeps this linear, and it is also what XML means: an
|
|
627
|
+
// attribute name starts after something that is not a name character. Without
|
|
628
|
+
// it the name can begin at every offset inside a long run of name characters,
|
|
629
|
+
// consuming the run each time before `\s*=\s*` fails — 397ms on a 32 KiB
|
|
630
|
+
// input against 0.6ms for a well-formed SVG of the same size. The bytes come
|
|
631
|
+
// from a logo fetched at a URL in the sender's own DNS record.
|
|
632
|
+
var re = /(?<![A-Za-z0-9:._-])([A-Za-z_:][A-Za-z0-9:._-]*)\s*=\s*("([^"]*)"|'([^']*)'|([^\s>]+))/g;
|
|
575
633
|
var m;
|
|
576
634
|
while ((m = re.exec(src)) !== null) {
|
|
577
635
|
var name = m[1];
|
|
@@ -1002,17 +1060,232 @@ function _extractBimiCertPolicy(cert) {
|
|
|
1002
1060
|
return rv;
|
|
1003
1061
|
}
|
|
1004
1062
|
|
|
1063
|
+
// Is this leaf an SVG document — that is, does its root element come next once
|
|
1064
|
+
// everything XML permits ahead of a root has been stepped over?
|
|
1065
|
+
//
|
|
1066
|
+
// A prefix window cannot answer that. XML puts no length on the prologue, so
|
|
1067
|
+
// whichever number is chosen a legal document can put its root past it; the
|
|
1068
|
+
// first attempt here read 64 characters and dropped any logo carrying the usual
|
|
1069
|
+
// SVG 1.1 DOCTYPE, whose root sits at 154. What XML does bound is the KIND of
|
|
1070
|
+
// thing allowed before the root: whitespace (markup whitespace covers the
|
|
1071
|
+
// byte-order mark too), the declaration, processing instructions, comments and
|
|
1072
|
+
// a DOCTYPE. Stepping over those is both more permissive than any window and
|
|
1073
|
+
// stricter than a substring search, which called a document an SVG because a
|
|
1074
|
+
// comment mentioned one.
|
|
1075
|
+
//
|
|
1076
|
+
// Every branch advances the cursor past a complete construct, so the walk is
|
|
1077
|
+
// linear in the leaf and an unterminated construct ends it rather than
|
|
1078
|
+
// restarting a scan.
|
|
1079
|
+
// One past the `>` that closes a DOCTYPE declaration opening at `at`, or -1 if
|
|
1080
|
+
// it never closes.
|
|
1081
|
+
//
|
|
1082
|
+
// Every character the declaration can end on — `>`, and the `[`/`]` of the
|
|
1083
|
+
// internal subset — is also a character it may legally CONTAIN, inside a quoted
|
|
1084
|
+
// external identifier, a quoted entity value, or a comment. So none of them can
|
|
1085
|
+
// be located by searching for the next occurrence:
|
|
1086
|
+
//
|
|
1087
|
+
// <!DOCTYPE svg SYSTEM "urn:logo>v1"> the `>` is in the URI
|
|
1088
|
+
// <!DOCTYPE svg SYSTEM "urn:logo[v1"> the `[` opens no subset
|
|
1089
|
+
// <!DOCTYPE svg [ <!ENTITY x "a]b"> ]> the `]` is in the value
|
|
1090
|
+
//
|
|
1091
|
+
// Each of those was a separate defect while the ends were found with separate
|
|
1092
|
+
// searches. They are one defect: the declaration ends at the first `>` that is
|
|
1093
|
+
// not inside a quoted literal, a comment, or the subset. Reading it once, with
|
|
1094
|
+
// that rule, answers all of them and the ones not listed. Every branch advances,
|
|
1095
|
+
// so the walk is linear in the declaration.
|
|
1096
|
+
// One past a quoted literal opening at `j`; 0 when none opens there, -1 when
|
|
1097
|
+
// one opens and never closes. Same three-valued answer for the two helpers
|
|
1098
|
+
// below, so a caller reads them the same way.
|
|
1099
|
+
function _skipQuotedLiteral(text, j) {
|
|
1100
|
+
var ch = text.charAt(j);
|
|
1101
|
+
if (ch !== "\"" && ch !== "'") return 0;
|
|
1102
|
+
var end = text.indexOf(ch, j + 1);
|
|
1103
|
+
return end === -1 ? -1 : end + 1;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// One past a comment or a processing instruction opening at `j`. These are the
|
|
1107
|
+
// two constructs that may appear almost anywhere in a prologue and may contain
|
|
1108
|
+
// anything, so every scan that looks for a delimiter has to step over them —
|
|
1109
|
+
// and each must step over the SAME set. Teaching one scanner about processing
|
|
1110
|
+
// instructions and not its neighbour is what produced this function: the
|
|
1111
|
+
// declaration knew about them and the internal subset did not, so a legal
|
|
1112
|
+
// `<?meta value]?>` inside a subset ended it early.
|
|
1113
|
+
function _skipCommentOrPi(text, j) {
|
|
1114
|
+
if (text.startsWith("<!--", j)) {
|
|
1115
|
+
// XML rules, not HTML: a certificate payload is an XML document, and the
|
|
1116
|
+
// HTML reader closes a comment at `--!>` and abruptly at `<!-->`, neither
|
|
1117
|
+
// of which XML has. Reading one grammar with the other's rules disagrees
|
|
1118
|
+
// about where the document begins — it would take the text after an early
|
|
1119
|
+
// close for a root element, and would end a comment XML says is open.
|
|
1120
|
+
var endComment = markupTokenizer.xmlCommentEnd(text, j);
|
|
1121
|
+
return endComment === -1 ? -1 : endComment;
|
|
1122
|
+
}
|
|
1123
|
+
if (text.startsWith("<?", j)) {
|
|
1124
|
+
var endPi = text.indexOf("?>", j + 2);
|
|
1125
|
+
return endPi === -1 ? -1 : endPi + 2;
|
|
1126
|
+
}
|
|
1127
|
+
return 0;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
function _endOfDoctype(text, at) {
|
|
1131
|
+
var j = at + 9; // past "<!DOCTYPE"
|
|
1132
|
+
while (j < text.length) {
|
|
1133
|
+
if (text.charAt(j) === ">") return j + 1;
|
|
1134
|
+
if (text.charAt(j) === "[") {
|
|
1135
|
+
var endSubset = _endOfInternalSubset(text, j);
|
|
1136
|
+
if (endSubset === -1) return -1;
|
|
1137
|
+
j = endSubset + 1;
|
|
1138
|
+
continue;
|
|
1139
|
+
}
|
|
1140
|
+
var skipped = _skipQuotedLiteral(text, j);
|
|
1141
|
+
if (skipped === 0) skipped = _skipCommentOrPi(text, j);
|
|
1142
|
+
if (skipped === -1) return -1;
|
|
1143
|
+
j = skipped === 0 ? j + 1 : skipped;
|
|
1144
|
+
}
|
|
1145
|
+
return -1;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// The index of the `]` that closes a DOCTYPE's internal subset, given the index
|
|
1149
|
+
// of its `[`, or -1 if it never closes. Same rule as the declaration around it:
|
|
1150
|
+
// quoted runs and comments are stepped over rather than searched through.
|
|
1151
|
+
function _endOfInternalSubset(text, at) {
|
|
1152
|
+
var j = at + 1;
|
|
1153
|
+
while (j < text.length) {
|
|
1154
|
+
if (text.charAt(j) === "]") return j;
|
|
1155
|
+
var skipped = _skipQuotedLiteral(text, j);
|
|
1156
|
+
if (skipped === 0) skipped = _skipCommentOrPi(text, j);
|
|
1157
|
+
if (skipped === -1) return -1;
|
|
1158
|
+
j = skipped === 0 ? j + 1 : skipped;
|
|
1159
|
+
}
|
|
1160
|
+
return -1;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// The characters that can END a tag name: whitespace before the attributes, the
|
|
1164
|
+
// slash of an empty element, or the tag's own close. A closed set, unlike the
|
|
1165
|
+
// set of characters that may CONTINUE an XML name.
|
|
1166
|
+
function _endsTagName(text, at) {
|
|
1167
|
+
var ch = text.charAt(at);
|
|
1168
|
+
return ch === ">" || ch === "/" || markupTokenizer.isMarkupSpace(text.charCodeAt(at));
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// Does the text between the root's name and its `>` read as a start tag?
|
|
1172
|
+
//
|
|
1173
|
+
// The one structural rule worth testing here is where a `/` may appear: XML
|
|
1174
|
+
// allows it in exactly one position, against the `>`, closing an empty element.
|
|
1175
|
+
// Anywhere else outside a quoted value the text is not a start tag — both
|
|
1176
|
+
// `<x:svg xmlns:x="…"/garbage>` and `<x:svg xmlns:x="…"/ >` have a closing
|
|
1177
|
+
// bracket and a perfectly good namespace declaration, and neither is one.
|
|
1178
|
+
//
|
|
1179
|
+
// Quotes are tracked because a `/` inside an attribute value is data: every
|
|
1180
|
+
// namespace URI here contains several.
|
|
1181
|
+
//
|
|
1182
|
+
// This is deliberately NOT a full attribute-grammar check. The function it
|
|
1183
|
+
// serves decides WHICH ASN.1 leaf holds the logo; whether the document is
|
|
1184
|
+
// conformant is validateTinyPsSvg's question, and duplicating that here would
|
|
1185
|
+
// be a second, weaker parser drifting out of step with the real one.
|
|
1186
|
+
function _tagBodyIsWellFormed(text, from, tagEnd) {
|
|
1187
|
+
var quote = "";
|
|
1188
|
+
for (var i = from; i < tagEnd; i += 1) {
|
|
1189
|
+
var ch = text.charAt(i);
|
|
1190
|
+
if (quote) { if (ch === quote) quote = ""; continue; }
|
|
1191
|
+
if (ch === "\"" || ch === "'") { quote = ch; continue; }
|
|
1192
|
+
if (ch !== "/") continue;
|
|
1193
|
+
// A slash outside a quoted value closes an empty element, and XML's
|
|
1194
|
+
// EmptyElemTag is `S? '/>'` — the space may come BEFORE the slash and not
|
|
1195
|
+
// after it, so the slash has to sit against the bracket.
|
|
1196
|
+
return i + 1 === tagEnd;
|
|
1197
|
+
}
|
|
1198
|
+
// An unterminated quote means the tag never really ended either.
|
|
1199
|
+
return quote === "";
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
function _svgRootFollowsPrologue(text) {
|
|
1203
|
+
var i = 0;
|
|
1204
|
+
for (;;) {
|
|
1205
|
+
i = markupTokenizer.skipMarkupSpace(text, i);
|
|
1206
|
+
|
|
1207
|
+
// Comments and processing instructions, through the same definitions the
|
|
1208
|
+
// two DOCTYPE scanners use. The XML declaration is a processing instruction
|
|
1209
|
+
// as far as finding its end goes.
|
|
1210
|
+
var aside = _skipCommentOrPi(text, i);
|
|
1211
|
+
if (aside === -1) return false;
|
|
1212
|
+
if (aside !== 0) { i = aside; continue; }
|
|
1213
|
+
|
|
1214
|
+
if (text.startsWith("<!DOCTYPE", i) || text.startsWith("<!doctype", i)) {
|
|
1215
|
+
var endDoctype = _endOfDoctype(text, i);
|
|
1216
|
+
if (endDoctype === -1) return false;
|
|
1217
|
+
i = endDoctype;
|
|
1218
|
+
continue;
|
|
1219
|
+
}
|
|
1220
|
+
break;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
if (text.charAt(i) !== "<") return false;
|
|
1224
|
+
|
|
1225
|
+
// Read the whole root name and compare its LOCAL part.
|
|
1226
|
+
//
|
|
1227
|
+
// Two things this gets right that a `startsWith("<svg")` does not. `<svgfoo`
|
|
1228
|
+
// and `<svg.foo` are different elements — and asking instead "does a name
|
|
1229
|
+
// character follow?" is only as good as the character list, since XML's name
|
|
1230
|
+
// grammar runs well past the ASCII set any such list holds, `.` alone being
|
|
1231
|
+
// enough to let `<svg.foo` through. And an SVG may bind its own namespace to
|
|
1232
|
+
// a prefix and write the root `<svg:svg>`, which is the same element.
|
|
1233
|
+
//
|
|
1234
|
+
// What is deliberately NOT done is resolving the prefix to a namespace URI. A
|
|
1235
|
+
// logo that omits `xmlns` altogether is common and was accepted before, so
|
|
1236
|
+
// requiring the binding here would reject real marks; this step is best-effort
|
|
1237
|
+
// detection deciding which leaf to hand back, not validation.
|
|
1238
|
+
var nameEnd = i + 1;
|
|
1239
|
+
while (nameEnd < text.length && !_endsTagName(text, nameEnd)) nameEnd += 1;
|
|
1240
|
+
if (nameEnd >= text.length) return false; // the name never ends: truncated
|
|
1241
|
+
// A qualified name is `prefix:local`, one colon, two parts.
|
|
1242
|
+
// The start tag has to CLOSE. A tag name ends at the whitespace before the
|
|
1243
|
+
// attributes, so a document whose bytes stop mid-tag still has a complete
|
|
1244
|
+
// name — and, if it got as far as a namespace declaration, a perfectly good
|
|
1245
|
+
// binding. That is a truncated document rather than a logo. scanToTagEnd
|
|
1246
|
+
// reports the end of the input when it finds no `>`.
|
|
1247
|
+
var tagEnd = markupTokenizer.scanToTagEnd(text, nameEnd, text.length);
|
|
1248
|
+
if (tagEnd >= text.length) return false;
|
|
1249
|
+
if (!_tagBodyIsWellFormed(text, nameEnd, tagEnd)) return false;
|
|
1250
|
+
|
|
1251
|
+
var parts = text.slice(i + 1, nameEnd).split(":");
|
|
1252
|
+
if (parts.length > 2 || parts[parts.length - 1] !== "svg") return false;
|
|
1253
|
+
// A prefix is an NCName, so a digit or a punctuation character cannot begin
|
|
1254
|
+
// one. Without this a malformed name that declares a matching attribute —
|
|
1255
|
+
// `<0ns:svg xmlns:0ns="...">` — would satisfy the binding check below, since
|
|
1256
|
+
// attribute parsing is as permissive about names as this scan is.
|
|
1257
|
+
if (parts.length === 2 && !_isNcName(parts[0])) return false;
|
|
1258
|
+
// An unprefixed root is accepted without an `xmlns`: logos omit it, and the
|
|
1259
|
+
// scanner this replaces accepted them.
|
|
1260
|
+
if (parts.length === 1) return true;
|
|
1261
|
+
|
|
1262
|
+
// A prefix, though, means nothing until the root binds it. `<x:svg>` where
|
|
1263
|
+
// `x` is another vocabulary is not an SVG, and one declared nowhere names
|
|
1264
|
+
// nothing — so requiring the binding is what separates a namespaced root from
|
|
1265
|
+
// arbitrary XML whose local name happens to read `svg`. It also settles the
|
|
1266
|
+
// malformed names for free: `<a::svg` has too many parts, and `<a<:svg` and
|
|
1267
|
+
// `<0ns:svg` declare no matching `xmlns:` attribute.
|
|
1268
|
+
var attrs = markupTokenizer.parseAttrs(text.slice(nameEnd, tagEnd));
|
|
1269
|
+
var wanted = "xmlns:" + parts[0];
|
|
1270
|
+
for (var a = 0; a < attrs.length; a += 1) {
|
|
1271
|
+
// The value is compared for what it DENOTES: an XML processor resolves
|
|
1272
|
+
// `svg` and `svg` to the same namespace, so comparing the lexical form
|
|
1273
|
+
// would reject a root whose namespace is the SVG one.
|
|
1274
|
+
if (attrs[a].name === wanted) {
|
|
1275
|
+
return markupTokenizer.decodeCharRefs(attrs[a].value) === SVG_NAMESPACE;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
return false;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1005
1281
|
function _scanForEmbeddedSvg(node, depthBudget) {
|
|
1006
1282
|
if (!node) return null;
|
|
1007
1283
|
if (depthBudget < 0) return null;
|
|
1008
1284
|
|
|
1009
1285
|
if (!node.constructed) {
|
|
1010
1286
|
if (!node.value || node.value.length < 4) return null;
|
|
1011
|
-
var
|
|
1012
|
-
|
|
1013
|
-
return node.value.toString("utf8");
|
|
1014
|
-
}
|
|
1015
|
-
return null;
|
|
1287
|
+
var text = node.value.toString("utf8");
|
|
1288
|
+
return _svgRootFollowsPrologue(text) ? text : null;
|
|
1016
1289
|
}
|
|
1017
1290
|
|
|
1018
1291
|
var children;
|
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
|
}
|