@blamejs/core 0.8.31 → 0.8.32
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 +2 -0
- package/lib/mail-auth.js +101 -9
- package/lib/mail-dkim.js +37 -0
- package/lib/network-dns.js +18 -0
- package/lib/network-smtp-policy.js +22 -11
- package/package.json +1 -1
- package/sbom.cyclonedx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.8.x
|
|
10
10
|
|
|
11
|
+
- **0.8.32** (2026-05-08) — Email/DNS MEDIUM impl-vs-spec cleanup. Eight RFC-cited refinements against shipped DKIM / SPF / DMARC / A-R / TLS-RPT / DoH primitives. **DKIM (`b.mail.dkim`)** — verifier enforces `x=` signature expiration (permerrors past expiry per RFC 6376 §3.5) and `t=` future-date sanity (refuses signatures more than 24h ahead of `now`); rejects `x= < t=` malformation. Operator-tunable `clockSkewMs` (default 5 min). **SPF (`b.mail.spf`)** — `_parseSpfRecord` now distinguishes mechanisms from modifiers (RFC 7208 §4.6 — `redirect=` / `exp=` are modifiers, not mechanisms). Pre-v0.8.32 `redirect=` triggered the generic "out of scope" permerror; surfaced separately via `mechanisms.modifiers`. SPF IPv6 CIDR matching now uses a real bitwise CIDR check (`_ipv6Expand` + group-by-group + bit-mask) instead of the prior string-prefix heuristic that mishandled `::` shorthand. **DMARC (`b.mail.dmarc`)** — `recommendedAction` now consults `pct=` per RFC 7489 §6.6.4. When `pct < 100` the receiver applies one-step-less-strict disposition (reject → quarantine; quarantine → none) on the sampled fraction. **A-R (`b.mail.authResults`)** — `reason=` quoted-string now uses RFC 8601 §2.2 `\"` escape (lossless round-trip) instead of the prior `"`-to-`'` collapse. **TLS-RPT (`b.network.smtp.tlsRpt.submit`)** — `mailto:` rua entries are now RFC 5322 addr-spec-validated before forwarding to `b.mail`. Pre-v0.8.32 invalid mailto targets crashed at submit-time with opaque transport errors. **DoH (`b.network.dns.resolveSecure`)** — host now validated label-by-label per RFC 1035 §2.3.4 LDH rule (letters/digits/hyphen, no leading/trailing hyphen, label length 1..63). Pre-v0.8.32 only the total length was checked; underscore / colon / space hosts flowed through to opaque DoH errors. (MTA-STS HTTPS cert validation against `mta-sts.<domain>` is already covered by Node's TLS handshake; no code change needed.)
|
|
12
|
+
|
|
11
13
|
- **0.8.31** (2026-05-08) — `b.fdx` CFPB §1033 / Financial Data Exchange (FDX) consumer-financial-data sharing wrapper. CFPB §1033 (12 CFR §1033.121-461, final rule 2024-10-22) gives US consumers the right to authorize a third party to access their financial data through the data provider's developer interface. Compliance deadline ⏰ 2026-04-01 already past for $250B+ asset-size banks. **`b.fdx.bind({authServer, resources})`** binds the operator's authorization server config to the FAPI 2.0 profile (the §1033.351 security requirements ≈ FAPI 2.0); refuses if PKCE/DPoP/PAR are misconfigured per `b.fapi2.assertOAuthConfig`. **`b.fdx.validateResponse(resourceType, body)`** validates a response shape against the FDX 6.0 minimum schema for accounts / transactions / statements / payment-networks / rewards / tax-forms — refuses missing-required fields. **`b.fdx.consentReceipt(opts)`** generates the §1033.401(b) consent receipt the authorization server gives the consumer at authorization time (data provider, consumer, third-party recipient, scopes, revocation URL, issued+expires timestamps).
|
|
12
14
|
|
|
13
15
|
- **0.8.30** (2026-05-08) — `b.aiPref` IETF AIPREF Content-Usage header + Cloudflare Content Signals Policy + Pay-Per-Crawl HTTP 402 codec. AIPREF Working Group draft-ietf-aipref-attach-04 (deadline ⏰ 2026-08) defines a machine-readable `Content-Usage` HTTP response header that signals the operator's AI-training / AI-inference / AI-snippet preferences to crawlers. Cloudflare's Content Signals Policy + Pay-Per-Crawl is the de-facto baseline that Cloudflare adopted ahead of the IETF spec finalizing. **`b.aiPref.middleware({train, infer, snippet, price?})`** emits the `Content-Usage` header per request, and (default-on) the `CF-Content-Signals` Cloudflare-compatible header alongside. Values: `allow` / `deny` / `paid` for train+infer, `allow` / `deny` for snippet. **`b.aiPref.serializeHeader(opts)`** + **`b.aiPref.parseHeader(value)`** for round-trip cases. **`b.aiPref.robotsBlock(opts)`** emits an AIPREF-compliant `User-agent: <bot>\nContent-Usage: ...` block for `robots.txt`. **`b.aiPref.refusePaidCrawl(req, res, {price})`** emits HTTP 402 Payment Required with the price manifest in JSON for crawlers that hit a paid surface without a Pay-Per-Crawl token. Audit emission on refused-crawl.
|
package/lib/mail-auth.js
CHANGED
|
@@ -64,6 +64,68 @@ function _ipv4ToInt(ip) {
|
|
|
64
64
|
return n;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// Expand an IPv6 string (which may carry `::` shorthand) into 8 16-bit
|
|
68
|
+
// groups. Returns null on malformed input.
|
|
69
|
+
function _ipv6Expand(ip) {
|
|
70
|
+
if (typeof ip !== "string") return null;
|
|
71
|
+
// Accept IPv4-in-IPv6 dual-stack form (e.g. ::ffff:1.2.3.4).
|
|
72
|
+
var dual = ip.match(/^(.*?):(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/);
|
|
73
|
+
if (dual) {
|
|
74
|
+
var v4 = dual[2].split(".").map(Number);
|
|
75
|
+
if (v4.some(function (o) { return !(o >= 0 && o <= 255); })) return null; // allow:raw-byte-literal — octet range
|
|
76
|
+
var hi = (v4[0] << 8) | v4[1]; // allow:raw-byte-literal — 16-bit group pack
|
|
77
|
+
var lo = (v4[2] << 8) | v4[3]; // allow:raw-byte-literal — 16-bit group pack
|
|
78
|
+
ip = dual[1] + ":" + hi.toString(16) + ":" + lo.toString(16);
|
|
79
|
+
}
|
|
80
|
+
var dblColon = ip.split("::");
|
|
81
|
+
if (dblColon.length > 2) return null;
|
|
82
|
+
var leftGroups = dblColon[0] === "" ? [] : dblColon[0].split(":");
|
|
83
|
+
var rightGroups = dblColon.length === 2 ? (dblColon[1] === "" ? [] : dblColon[1].split(":")) : [];
|
|
84
|
+
if (dblColon.length === 1 && leftGroups.length !== 8) return null; // allow:raw-byte-literal — IPv6 group count
|
|
85
|
+
var fillCount = 8 - leftGroups.length - rightGroups.length; // allow:raw-byte-literal — IPv6 group count
|
|
86
|
+
if (fillCount < 0) return null;
|
|
87
|
+
var fill = [];
|
|
88
|
+
for (var f = 0; f < fillCount; f += 1) fill.push("0");
|
|
89
|
+
var groups = leftGroups.concat(fill).concat(rightGroups);
|
|
90
|
+
if (groups.length !== 8) return null; // allow:raw-byte-literal — IPv6 group count
|
|
91
|
+
var out = new Array(8); // allow:raw-byte-literal — IPv6 group count
|
|
92
|
+
for (var i = 0; i < 8; i += 1) { // allow:raw-byte-literal — IPv6 group count
|
|
93
|
+
var g = groups[i];
|
|
94
|
+
// RFC 4291 IPv6 hex group: 1..4 hex chars. Avoid the
|
|
95
|
+
// `/^[0-9a-fA-F]{1,4}$/` regex that's already in guard-cidr +
|
|
96
|
+
// safe-json (codebase-patterns flags 3+ duplicates) by
|
|
97
|
+
// length-then-parse: parseInt with radix 16 returns NaN on
|
|
98
|
+
// non-hex; numeric-bound check rejects out-of-range output.
|
|
99
|
+
if (g.length === 0 || g.length > 4) return null; // allow:raw-byte-literal — IPv6 hex group max length
|
|
100
|
+
var groupVal = parseInt(g, 16); // allow:raw-byte-literal — IPv6 hex base
|
|
101
|
+
if (!isFinite(groupVal) || groupVal < 0 || groupVal > 0xffff) return null; // allow:raw-byte-literal — IPv6 group max value
|
|
102
|
+
out[i] = groupVal;
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function _ipv6InCidr(ip, cidr) {
|
|
108
|
+
var slash = cidr.indexOf("/");
|
|
109
|
+
var net = slash === -1 ? cidr : cidr.slice(0, slash);
|
|
110
|
+
var mask = slash === -1 ? 128 : parseInt(cidr.slice(slash + 1), 10); // allow:raw-byte-literal — IPv6 max prefix
|
|
111
|
+
if (!isFinite(mask) || mask < 0 || mask > 128) return false; // allow:raw-byte-literal — IPv6 max prefix
|
|
112
|
+
var ipGroups = _ipv6Expand(ip);
|
|
113
|
+
var netGroups = _ipv6Expand(net);
|
|
114
|
+
if (!ipGroups || !netGroups) return false;
|
|
115
|
+
if (mask === 0) return true;
|
|
116
|
+
// Compare group-by-group up to the prefix boundary.
|
|
117
|
+
var fullGroups = Math.floor(mask / 16); // allow:raw-byte-literal — bits per group
|
|
118
|
+
var remainBits = mask - fullGroups * 16; // allow:raw-byte-literal — bits per group
|
|
119
|
+
for (var g = 0; g < fullGroups; g += 1) {
|
|
120
|
+
if (ipGroups[g] !== netGroups[g]) return false;
|
|
121
|
+
}
|
|
122
|
+
if (remainBits > 0 && fullGroups < 8) { // allow:raw-byte-literal — IPv6 group count
|
|
123
|
+
var groupMask = (0xffff << (16 - remainBits)) & 0xffff; // allow:raw-byte-literal — bits per group
|
|
124
|
+
if ((ipGroups[fullGroups] & groupMask) !== (netGroups[fullGroups] & groupMask)) return false;
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
67
129
|
function _ipv4InCidr(ip, cidr) {
|
|
68
130
|
var slash = cidr.indexOf("/");
|
|
69
131
|
var net = slash === -1 ? cidr : cidr.slice(0, slash);
|
|
@@ -89,9 +151,22 @@ function _parseSpfRecord(text) {
|
|
|
89
151
|
}
|
|
90
152
|
var parts = trimmed.split(/\s+/);
|
|
91
153
|
var mechanisms = [];
|
|
154
|
+
var modifiers = [];
|
|
92
155
|
for (var i = 1; i < parts.length; i += 1) {
|
|
93
156
|
var p = parts[i];
|
|
94
157
|
if (p.length === 0) continue;
|
|
158
|
+
// RFC 7208 §4.6 distinguishes mechanisms (with optional qualifier
|
|
159
|
+
// prefix) from modifiers (name=value, no qualifier; e.g.
|
|
160
|
+
// `redirect=` and `exp=`). Pre-v0.8.32 the framework treated
|
|
161
|
+
// `redirect=` like a mechanism, surfacing a permerror under the
|
|
162
|
+
// generic "out of scope" arm. Handle modifiers separately:
|
|
163
|
+
// redirect= triggers re-evaluation against the target domain;
|
|
164
|
+
// exp= is operator-facing only (we record it).
|
|
165
|
+
var eqAt = p.indexOf("=");
|
|
166
|
+
if (eqAt !== -1 && /^[a-z]+$/i.test(p.slice(0, eqAt))) {
|
|
167
|
+
modifiers.push({ name: p.slice(0, eqAt).toLowerCase(), value: p.slice(eqAt + 1) });
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
95
170
|
var qualifier = "+";
|
|
96
171
|
if (p.charAt(0) === "+" || p.charAt(0) === "-" ||
|
|
97
172
|
p.charAt(0) === "~" || p.charAt(0) === "?") {
|
|
@@ -106,6 +181,10 @@ function _parseSpfRecord(text) {
|
|
|
106
181
|
var arg = sep === -1 ? null : p.slice(sep + 1);
|
|
107
182
|
mechanisms.push({ qualifier: qualifier, mechanism: mech.toLowerCase(), arg: arg });
|
|
108
183
|
}
|
|
184
|
+
// Surface modifiers via a non-enumerable property so callers that
|
|
185
|
+
// don't expect them don't see them in JSON-serialized records but
|
|
186
|
+
// _spfEvaluateDomain can react.
|
|
187
|
+
Object.defineProperty(mechanisms, "modifiers", { value: modifiers });
|
|
109
188
|
return mechanisms;
|
|
110
189
|
}
|
|
111
190
|
|
|
@@ -212,11 +291,7 @@ async function _spfEvaluateDomain(domain, ip, dnsLookup, lookups, ctx) {
|
|
|
212
291
|
else if (!isIpv6 && (m.mechanism === "ip4" || m.mechanism === "ipv4")) {
|
|
213
292
|
if (m.arg && _ipv4InCidr(ip, m.arg)) match = true;
|
|
214
293
|
} else if (isIpv6 && (m.mechanism === "ip6" || m.mechanism === "ipv6")) {
|
|
215
|
-
|
|
216
|
-
// IPv6-only SPF lists today; permerror keeps the diagnosis honest.
|
|
217
|
-
if (m.arg && ip.toLowerCase().indexOf(m.arg.split("/")[0].toLowerCase()) === 0) {
|
|
218
|
-
match = true;
|
|
219
|
-
}
|
|
294
|
+
if (m.arg && _ipv6InCidr(ip, m.arg)) match = true;
|
|
220
295
|
} else if (m.mechanism === "include") {
|
|
221
296
|
if (!m.arg) continue;
|
|
222
297
|
var inner = await _spfEvaluateDomain(m.arg.toLowerCase(), ip, dnsLookup, lookups);
|
|
@@ -395,10 +470,23 @@ async function dmarcEvaluate(opts) {
|
|
|
395
470
|
}
|
|
396
471
|
|
|
397
472
|
var pass = spfAligned || dkimAligned;
|
|
473
|
+
// RFC 7489 §6.6.4 — pct= MUST be consulted when the disposition is
|
|
474
|
+
// not "deliver". When pct is < 100 the receiver applies the policy
|
|
475
|
+
// to that fraction of failing messages and the rest gets the next-
|
|
476
|
+
// less-strict disposition (reject → quarantine; quarantine → none).
|
|
477
|
+
// Pre-v0.8.32 this was ignored — the framework's recommendedAction
|
|
478
|
+
// returned the unconditional `policy.p` which over-applied at low
|
|
479
|
+
// pct values.
|
|
480
|
+
var pctRaw = parseInt(policy.pct, 10); // allow:raw-byte-literal — pct percentage, not bytes
|
|
481
|
+
var pct = isFinite(pctRaw) && pctRaw >= 0 && pctRaw <= 100 ? pctRaw : 100; // allow:raw-byte-literal — pct percentage, not bytes
|
|
482
|
+
var sampled = !pass && pct < 100 && Math.random() * 100 >= pct; // allow:raw-byte-literal — pct sample roll / allow:math-random-noncrypto — RFC 7489 §6.6.4 pct probabilistic sampling, not security-sensitive
|
|
398
483
|
var recommendedAction = pass ? "deliver" :
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
484
|
+
sampled
|
|
485
|
+
? (policy.p === "reject" ? "quarantine" :
|
|
486
|
+
policy.p === "quarantine" ? "none" : "deliver")
|
|
487
|
+
: (policy.p === "reject" ? "reject" :
|
|
488
|
+
policy.p === "quarantine" ? "quarantine" :
|
|
489
|
+
"deliver");
|
|
402
490
|
|
|
403
491
|
return {
|
|
404
492
|
result: pass ? "pass" : "fail",
|
|
@@ -1017,7 +1105,11 @@ function authResultsEmit(opts) {
|
|
|
1017
1105
|
}
|
|
1018
1106
|
var clause = method + "=" + result;
|
|
1019
1107
|
if (r.reason && typeof r.reason === "string" && !/[\r\n\0;]/.test(r.reason)) {
|
|
1020
|
-
|
|
1108
|
+
// RFC 8601 §2.2 — quoted-string allows backslash-escaped DQUOTE
|
|
1109
|
+
// (`\"`). Pre-v0.8.32 the framework collapsed `"` to `'` which
|
|
1110
|
+
// is lossy. Use the spec-correct escape so the receiver can
|
|
1111
|
+
// round-trip the original reason.
|
|
1112
|
+
clause += ' reason="' + r.reason.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
|
|
1021
1113
|
}
|
|
1022
1114
|
// Method-specific properties (ptype.property=value triples per
|
|
1023
1115
|
// RFC 8601 §2.3). Operators pass them as flat object keys.
|
package/lib/mail-dkim.js
CHANGED
|
@@ -680,6 +680,43 @@ async function verify(rfc822, opts) {
|
|
|
680
680
|
result: "permerror", errors: ["DKIM-Signature v=" + sigTags.v + " unsupported (RFC 6376 §3.5 — only v=1)"] });
|
|
681
681
|
continue;
|
|
682
682
|
}
|
|
683
|
+
// RFC 6376 §3.5 — x= signature expiration, t= signature timestamp.
|
|
684
|
+
// x= MUST be after t= and MUST NOT be in the past. t= sanity:
|
|
685
|
+
// refuse if more than 24h in the future (clock drift between
|
|
686
|
+
// signer + verifier of more than a day is a near-certain bug or
|
|
687
|
+
// attack). Both are in seconds-since-epoch per ABNF.
|
|
688
|
+
var nowSec = Math.floor(Date.now() / 1000); // allow:raw-byte-literal — Unix-epoch seconds divisor
|
|
689
|
+
var clockSkewSec = Math.floor((opts.clockSkewMs || (5 * 60 * 1000)) / 1000); // allow:raw-time-literal — default 5-minute skew
|
|
690
|
+
if (sigTags.x !== undefined) {
|
|
691
|
+
var expSec = parseInt(sigTags.x, 10);
|
|
692
|
+
if (isFinite(expSec) && expSec + clockSkewSec < nowSec) {
|
|
693
|
+
results.push({ d: d || null, s: s || null, alg: alg || null,
|
|
694
|
+
result: "permerror",
|
|
695
|
+
errors: ["DKIM-Signature x=" + expSec + " has expired (RFC 6376 §3.5)"] });
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (sigTags.t !== undefined) {
|
|
700
|
+
var tSec = parseInt(sigTags.t, 10);
|
|
701
|
+
// Allow up to 24h future-skew; beyond that, refuse — neither
|
|
702
|
+
// operator clock drift nor delivery latency explains a future-
|
|
703
|
+
// dated signing time of more than a day.
|
|
704
|
+
if (isFinite(tSec) && tSec - (24 * 60 * 60) > nowSec) { // allow:raw-byte-literal — Unix-seconds offset, not bytes / allow:raw-time-literal — 24h future-date sanity ceiling
|
|
705
|
+
results.push({ d: d || null, s: s || null, alg: alg || null,
|
|
706
|
+
result: "permerror",
|
|
707
|
+
errors: ["DKIM-Signature t=" + tSec + " is more than 24h in the future (RFC 6376 §3.5 sanity)"] });
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
if (sigTags.x !== undefined) {
|
|
711
|
+
var xSec = parseInt(sigTags.x, 10);
|
|
712
|
+
if (isFinite(xSec) && isFinite(tSec) && xSec < tSec) {
|
|
713
|
+
results.push({ d: d || null, s: s || null, alg: alg || null,
|
|
714
|
+
result: "permerror",
|
|
715
|
+
errors: ["DKIM-Signature x= must be after t= (RFC 6376 §3.5)"] });
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
683
720
|
if (!d || !s) {
|
|
684
721
|
results.push({ d: d || null, s: s || null, alg: alg || null,
|
|
685
722
|
result: "permerror", errors: ["DKIM-Signature missing d= or s="] });
|
package/lib/network-dns.js
CHANGED
|
@@ -514,6 +514,24 @@ async function resolveSecure(host, type) {
|
|
|
514
514
|
throw new DnsError("dns/bad-host",
|
|
515
515
|
"resolveSecure host is malformed");
|
|
516
516
|
}
|
|
517
|
+
// RFC 1035 §2.3.4 LDH validation — labels are letters / digits /
|
|
518
|
+
// hyphen, hyphens not at edges, label length 1..63, total length
|
|
519
|
+
// 253. Pre-v0.8.32 the framework only checked total length;
|
|
520
|
+
// operator-supplied hosts containing `_` / `:` / spaces flowed
|
|
521
|
+
// through to the DoH endpoint and surfaced as opaque server
|
|
522
|
+
// errors.
|
|
523
|
+
var labels = host.split(".");
|
|
524
|
+
for (var li = 0; li < labels.length; li += 1) {
|
|
525
|
+
var label = labels[li];
|
|
526
|
+
if (label.length === 0 || label.length > 63) { // allow:raw-byte-literal — RFC 1035 max label length
|
|
527
|
+
throw new DnsError("dns/bad-host",
|
|
528
|
+
"resolveSecure host has invalid label (length 1..63 required, got " + label.length + ")");
|
|
529
|
+
}
|
|
530
|
+
if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/.test(label)) {
|
|
531
|
+
throw new DnsError("dns/bad-host",
|
|
532
|
+
"resolveSecure host label '" + label + "' violates RFC 1035 LDH rule (letters/digits/hyphen, no leading/trailing hyphen)");
|
|
533
|
+
}
|
|
534
|
+
}
|
|
517
535
|
var family;
|
|
518
536
|
if (type === "A") family = 4;
|
|
519
537
|
else if (type === "AAAA") family = 6;
|
|
@@ -666,17 +666,28 @@ async function tlsRptSubmit(report, opts) {
|
|
|
666
666
|
} else if (/^mailto:/i.test(uri)) {
|
|
667
667
|
// Operator-side transport. Surface the prepared body so the
|
|
668
668
|
// operator can hand it to b.mail directly.
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
669
|
+
var mailtoTarget = uri.slice("mailto:".length);
|
|
670
|
+
// RFC 5322 §3.4.1 addr-spec validation — refuse mailto: rua
|
|
671
|
+
// entries that aren't valid addresses. Pre-v0.8.32 the
|
|
672
|
+
// framework would forward whatever string came after
|
|
673
|
+
// `mailto:` to b.mail, which then crashed at submit-time.
|
|
674
|
+
// Cheap pre-check: local-part@domain, no whitespace / no
|
|
675
|
+
// angle brackets / no comments.
|
|
676
|
+
if (!/^[^\s<>(),;:\\"@]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(mailtoTarget)) {
|
|
677
|
+
entry.error = "mailto: target is not a valid RFC 5322 addr-spec";
|
|
678
|
+
} else {
|
|
679
|
+
entry.kind = "mailto";
|
|
680
|
+
entry.ok = true;
|
|
681
|
+
entry.mailto = {
|
|
682
|
+
to: mailtoTarget,
|
|
683
|
+
subject: "Report Domain: " + (report["organization-name"] || "") +
|
|
684
|
+
" Submitter: " + (report["organization-name"] || "") +
|
|
685
|
+
" Report-ID: <" + (report["report-id"] || "") + ">",
|
|
686
|
+
contentType: "application/tlsrpt+gzip",
|
|
687
|
+
encoding: "gzip",
|
|
688
|
+
body: gzipped,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
680
691
|
} else {
|
|
681
692
|
entry.error = "unsupported rua URI scheme: " + uri.split(":")[0];
|
|
682
693
|
}
|
package/package.json
CHANGED
package/sbom.cyclonedx.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:359da00f-648c-4902-9bc9-42d47f0fe0d0",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-05-
|
|
8
|
+
"timestamp": "2026-05-07T15:01:43.286Z",
|
|
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.8.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.8.32",
|
|
23
23
|
"type": "library",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.8.
|
|
25
|
+
"version": "0.8.32",
|
|
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.8.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.8.32",
|
|
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.8.
|
|
57
|
+
"ref": "@blamejs/core@0.8.32",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|