spamscanner 7.0.11 → 7.0.12
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/dist/cjs/arf.cjs.map +1 -1
- package/dist/cjs/index.cjs +245 -129
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/esm/arf.js.map +1 -1
- package/dist/esm/cli.js +249 -132
- package/dist/esm/cli.js.map +3 -3
- package/dist/esm/index.js +241 -125
- package/dist/esm/index.js.map +3 -3
- package/dist/types/links.d.ts +5 -3
- package/dist/types/tokenizer.d.ts +9 -0
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -1784,7 +1784,7 @@ var init_entities = __esm({
|
|
|
1784
1784
|
",itilde=129,iukcy=456,iuml=ef,jcirc=135,jcy=439,jfr=1d527,jmath=237,jopf=1d55b,jscr=1d4bf,jsercy=458",
|
|
1785
1785
|
",jukcy=454,kappa=3ba,kappav=3f0,kcedil=137,kcy=43a,kfr=1d528,kgreen=138,khcy=445,kjcy=45c,kopf=1d55c",
|
|
1786
1786
|
",kscr=1d4c0,lAarr=21da,lArr=21d0,lAtail=291b,lBarr=290e,lE=2266,lEg=2a8b,lHar=2962,lacute=13a,laempt",
|
|
1787
|
-
"yv=29b4,lagran=2112,lambda=3bb,lang=
|
|
1787
|
+
"yv=29b4,lagran=2112,lambda=3bb,lang=27e8,langd=2991,langle=27e8,lap=2a85,laquo=ab,larr=2190,larrb=21",
|
|
1788
1788
|
"e4,larrbfs=291f,larrfs=291d,larrhk=21a9,larrlp=21ab,larrpl=2939,larrsim=2973,larrtl=21a2,lat=2aab,la",
|
|
1789
1789
|
"tail=2919,late=2aad,lates=2aad.fe00,lbarr=290c,lbbrk=2772,lbrace=7b,lbrack=5b,lbrke=298b,lbrksld=298",
|
|
1790
1790
|
"f,lbrkslu=298d,lcaron=13e,lcedil=13c,lceil=2308,lcub=7b,lcy=43b,ldca=2936,ldquo=201c,ldquor=201e,ldr",
|
|
@@ -1842,7 +1842,7 @@ var init_entities = __esm({
|
|
|
1842
1842
|
"line=2312,profsurf=2313,prop=221d,propto=221d,prsim=227e,prurel=22b0,pscr=1d4c5,psi=3c8,puncsp=2008,",
|
|
1843
1843
|
"qfr=1d52e,qint=2a0c,qopf=1d562,qprime=2057,qscr=1d4c6,quaternions=210d,quatint=2a16,quest=3f,questeq",
|
|
1844
1844
|
"=225f,quot=22,rAarr=21db,rArr=21d2,rAtail=291c,rBarr=290f,rHar=2964,race=223d.331,racute=155,radic=2",
|
|
1845
|
-
"21a,raemptyv=29b3,rang=
|
|
1845
|
+
"21a,raemptyv=29b3,rang=27e9,rangd=2992,range=29a5,rangle=27e9,raquo=bb,rarr=2192,rarrap=2975,rarrb=2",
|
|
1846
1846
|
"1e5,rarrbfs=2920,rarrc=2933,rarrfs=291e,rarrhk=21aa,rarrlp=21ac,rarrpl=2945,rarrsim=2974,rarrtl=21a3",
|
|
1847
1847
|
",rarrw=219d,ratail=291a,ratio=2236,rationals=211a,rbarr=290d,rbbrk=2773,rbrace=7d,rbrack=5d,rbrke=29",
|
|
1848
1848
|
"8c,rbrksld=298e,rbrkslu=2990,rcaron=159,rcedil=157,rceil=2309,rcub=7d,rcy=440,rdca=2937,rdldhar=2969",
|
|
@@ -2697,6 +2697,31 @@ function continuesName(text, index) {
|
|
|
2697
2697
|
}
|
|
2698
2698
|
return (text[index - 1] === "." || text[index - 1] === "-") && NAME_END.test(text.slice(Math.max(0, index - 3), index - 1));
|
|
2699
2699
|
}
|
|
2700
|
+
function boundedUrl(address2) {
|
|
2701
|
+
const text = String(address2).replaceAll(/[\t\n\r]/g, "");
|
|
2702
|
+
const colon = text.indexOf(":");
|
|
2703
|
+
let start = colon === -1 ? 0 : colon + 1;
|
|
2704
|
+
while (start < text.length && (text[start] === "/" || text[start] === "\\")) {
|
|
2705
|
+
start++;
|
|
2706
|
+
}
|
|
2707
|
+
AUTHORITY_END.lastIndex = start;
|
|
2708
|
+
const end = AUTHORITY_END.exec(text)?.index ?? text.length;
|
|
2709
|
+
const file = /^[\0- ]*file:/i.test(text);
|
|
2710
|
+
const at = file ? start : Math.max(start, text.lastIndexOf("@", end - 1) + 1);
|
|
2711
|
+
const bracket = text.lastIndexOf("]", end - 1);
|
|
2712
|
+
const port = text.lastIndexOf(":", end - 1);
|
|
2713
|
+
let host = text.slice(at, !file && port > Math.max(at - 1, bracket) ? port : end);
|
|
2714
|
+
if (host.length > MAX_URL_HOST && host.includes("%")) {
|
|
2715
|
+
try {
|
|
2716
|
+
host = decodeURIComponent(host);
|
|
2717
|
+
} catch {
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
if (host.length > MAX_URL_HOST && host.replaceAll(IGNORED_IN_HOST, "").length > MAX_URL_HOST) {
|
|
2721
|
+
throw new TypeError("Invalid URL: host too long");
|
|
2722
|
+
}
|
|
2723
|
+
return new URL(text);
|
|
2724
|
+
}
|
|
2700
2725
|
function isClickTracker(url) {
|
|
2701
2726
|
const host = url.hostname.toLowerCase();
|
|
2702
2727
|
return CLICK_TRACKERS.some((domain) => isUnder(host, domain)) || host === "substack.com" && url.pathname.startsWith("/redirect/");
|
|
@@ -2723,7 +2748,7 @@ function gatewayTarget(url) {
|
|
|
2723
2748
|
function unwrapLink(address2) {
|
|
2724
2749
|
let url;
|
|
2725
2750
|
try {
|
|
2726
|
-
url =
|
|
2751
|
+
url = boundedUrl(address2);
|
|
2727
2752
|
} catch {
|
|
2728
2753
|
return null;
|
|
2729
2754
|
}
|
|
@@ -2734,7 +2759,7 @@ function unwrapLink(address2) {
|
|
|
2734
2759
|
}
|
|
2735
2760
|
let next;
|
|
2736
2761
|
try {
|
|
2737
|
-
next =
|
|
2762
|
+
next = boundedUrl(target);
|
|
2738
2763
|
} catch {
|
|
2739
2764
|
break;
|
|
2740
2765
|
}
|
|
@@ -2996,7 +3021,7 @@ function extractLinks(text, html2, { maxLength = 5e5, bareHosts: bareHosts2 = fa
|
|
|
2996
3021
|
}
|
|
2997
3022
|
let url;
|
|
2998
3023
|
try {
|
|
2999
|
-
url =
|
|
3024
|
+
url = boundedUrl(href);
|
|
3000
3025
|
} catch {
|
|
3001
3026
|
return;
|
|
3002
3027
|
}
|
|
@@ -3309,7 +3334,7 @@ function isDeceptiveLink(link, { from } = {}) {
|
|
|
3309
3334
|
}
|
|
3310
3335
|
let shownUrl;
|
|
3311
3336
|
try {
|
|
3312
|
-
shownUrl =
|
|
3337
|
+
shownUrl = boundedUrl(/^https?:\/\//i.test(link.text) ? link.text : `http://${link.text}`);
|
|
3313
3338
|
} catch {
|
|
3314
3339
|
return false;
|
|
3315
3340
|
}
|
|
@@ -3667,7 +3692,7 @@ ${body}`);
|
|
|
3667
3692
|
hidden: visible.hidden
|
|
3668
3693
|
};
|
|
3669
3694
|
}
|
|
3670
|
-
var import_tldts2, INVISIBLE, OBFUSCATING, JOINERS, INVISIBLE_OR_JOINER, ZERO_WIDTH_SPACE, ZERO_WIDTH_JOINERS, LATIN_END, LATIN_START, ZWSP_SCRIPTS, ZWSP_SCRIPT_END, ZWSP_SCRIPT_START, SOFT_HYPHEN, BIDI, RIGHT_TO_LEFT, LETTER_END, LETTER_START, DIGIT_END, DIGIT_START, STYLED_LETTERS, WIDE_LETTERS, CJK, LOOKALIKES, UNIT_LETTERS, UNIT_WORD, LEET, LEET_DIGITS, UNSPACED, URL_PATTERN, EMAIL_SOURCE, EMAIL_PATTERN, PHONE_PATTERN, IP_PATTERN, MONEY_PATTERN, BITCOIN_PATTERN, CARD_PATTERN, ANCHOR_START, MAX_LINK_TEXT, MAX_URL, SHOWN_URL, IMG_PIXEL, IMG, BARE_HOST, MAX_BARE_HOSTS, NAME_END, FILE_TLDS, LINK_FILE_TLDS, CLICK_TRACKERS, isUnder, MAX_GATEWAYS, BRAND_NAMES, SHORTENERS, FREEMAIL, segmenters, LATIN_LOOKALIKES, LATIN, CYRILLIC_OR_GREEK, INLINE_TAG, HOST_CHAR, MAX_SHOWN_HTML, IN_WORD, MAX_BARE_NAME, LINKED_BRANDS, SIGN_IN_PART, signInPath, SENDING_DOMAINS, ADDRESS_SOURCE, NAMED_ADDRESS, MIN_SHOWN_WORDS, PART_WORD;
|
|
3695
|
+
var import_tldts2, INVISIBLE, OBFUSCATING, JOINERS, INVISIBLE_OR_JOINER, ZERO_WIDTH_SPACE, ZERO_WIDTH_JOINERS, LATIN_END, LATIN_START, ZWSP_SCRIPTS, ZWSP_SCRIPT_END, ZWSP_SCRIPT_START, SOFT_HYPHEN, BIDI, RIGHT_TO_LEFT, LETTER_END, LETTER_START, DIGIT_END, DIGIT_START, STYLED_LETTERS, WIDE_LETTERS, CJK, LOOKALIKES, UNIT_LETTERS, UNIT_WORD, LEET, LEET_DIGITS, UNSPACED, URL_PATTERN, EMAIL_SOURCE, EMAIL_PATTERN, PHONE_PATTERN, IP_PATTERN, MONEY_PATTERN, BITCOIN_PATTERN, CARD_PATTERN, ANCHOR_START, MAX_LINK_TEXT, MAX_URL, SHOWN_URL, IMG_PIXEL, IMG, BARE_HOST, MAX_BARE_HOSTS, NAME_END, FILE_TLDS, LINK_FILE_TLDS, CLICK_TRACKERS, MAX_URL_HOST, AUTHORITY_END, IGNORED_IN_HOST, isUnder, MAX_GATEWAYS, BRAND_NAMES, SHORTENERS, FREEMAIL, segmenters, LATIN_LOOKALIKES, LATIN, CYRILLIC_OR_GREEK, INLINE_TAG, HOST_CHAR, MAX_SHOWN_HTML, IN_WORD, MAX_BARE_NAME, LINKED_BRANDS, SIGN_IN_PART, signInPath, SENDING_DOMAINS, ADDRESS_SOURCE, NAMED_ADDRESS, MIN_SHOWN_WORDS, PART_WORD;
|
|
3671
3696
|
var init_tokenizer = __esm({
|
|
3672
3697
|
"src/tokenizer.js"() {
|
|
3673
3698
|
import_tldts2 = require("tldts");
|
|
@@ -3773,6 +3798,9 @@ var init_tokenizer = __esm({
|
|
|
3773
3798
|
FILE_TLDS = /* @__PURE__ */ new Set(["ai", "app", "md", "mov", "pm", "ps", "py", "rs", "sh", "so", "zip"]);
|
|
3774
3799
|
LINK_FILE_TLDS = /* @__PURE__ */ new Set(["md", "mov", "pm", "ps", "py", "rs", "sh", "so", "zip"]);
|
|
3775
3800
|
CLICK_TRACKERS = ["sendgrid.net", "list-manage.com", "mandrillapp.com", "awstrack.me", "pstmrk.it", "hubspotlinks.com", "rs6.net", "klclick.com", "mjt.lu", "exacttarget.com", "convertkit-mail.com", "convertkit-mail2.com", "kit-mail3.com", "sendibt2.com", "sendibt3.com", "createsend1.com", "cmail20.com", "clicks.mlsend.com", "acemlnb.com", "link.mail.beehiiv.com", "bhclick.com", "safelinks.protection.outlook.com", "urldefense.com", "urldefense.proofpoint.com", "mimecast.com", "mimecastprotect.com", "cudasvc.com", "secure-web.cisco.com"];
|
|
3801
|
+
MAX_URL_HOST = 4096;
|
|
3802
|
+
AUTHORITY_END = /[/\\?#]/g;
|
|
3803
|
+
IGNORED_IN_HOST = /[\u00AD\u034F\u180B-\u180D\u180F\u200B\u2060\u2064\uFE00-\uFE0F\uFEFF]|\uD82F[\uDCA0-\uDCA3]|\uDB40[\uDD00-\uDDEF]/g;
|
|
3776
3804
|
isUnder = (host, domain) => host === domain || host.endsWith(`.${domain}`);
|
|
3777
3805
|
MAX_GATEWAYS = 16;
|
|
3778
3806
|
BRAND_NAMES = new Set(BRANDS);
|
|
@@ -4612,7 +4640,7 @@ async function parseWithin(raw, parse = parseMessage, deadline = Date.now() + PA
|
|
|
4612
4640
|
}
|
|
4613
4641
|
}
|
|
4614
4642
|
async function expandAttachments(attachments = [], errors = [], parse = parseMessage, gets = {}) {
|
|
4615
|
-
const { unopened = [], mails = [] } = Array.isArray(gets) ? { unopened: gets } : gets;
|
|
4643
|
+
const { unopened = [], mails = [] } = Array.isArray(gets) ? { unopened: gets } : gets ?? {};
|
|
4616
4644
|
const all = [];
|
|
4617
4645
|
const deadline = Date.now() + PARSE_TIMEOUT;
|
|
4618
4646
|
let budget = MAX_NESTED_BYTES;
|
|
@@ -4761,6 +4789,10 @@ var init_parse = __esm({
|
|
|
4761
4789
|
});
|
|
4762
4790
|
|
|
4763
4791
|
// src/dnsbl.js
|
|
4792
|
+
function lastLabels(name) {
|
|
4793
|
+
const separators = [...name.matchAll(LABEL_SEPARATOR)];
|
|
4794
|
+
return separators.length < 128 ? 0 : separators.at(-128).index + 1;
|
|
4795
|
+
}
|
|
4764
4796
|
function lookupName(host) {
|
|
4765
4797
|
if (typeof host !== "string") {
|
|
4766
4798
|
return null;
|
|
@@ -4771,6 +4803,7 @@ function lookupName(host) {
|
|
|
4771
4803
|
}
|
|
4772
4804
|
let name = host.slice(0, end).toLowerCase();
|
|
4773
4805
|
if (/[\u0080-\u{10FFFF}]/u.test(name)) {
|
|
4806
|
+
name = name.slice(lastLabels(name));
|
|
4774
4807
|
name = (0, import_node_url2.domainToASCII)(name) || name;
|
|
4775
4808
|
}
|
|
4776
4809
|
if ((0, import_node_net.isIP)(name.replaceAll(/^\[|]$/g, "")) !== 0) {
|
|
@@ -4870,7 +4903,7 @@ function isListedAnswer(address2, zone) {
|
|
|
4870
4903
|
}
|
|
4871
4904
|
return true;
|
|
4872
4905
|
}
|
|
4873
|
-
var import_node_dns, import_node_net, import_node_url2, import_tldts3, LOOKUP_LABEL, CLOUDFLARE_MALWARE, CLOUDFLARE_FAMILY, DnsChecker;
|
|
4906
|
+
var import_node_dns, import_node_net, import_node_url2, import_tldts3, LOOKUP_LABEL, LABEL_SEPARATOR, CLOUDFLARE_MALWARE, CLOUDFLARE_FAMILY, DnsChecker;
|
|
4874
4907
|
var init_dnsbl = __esm({
|
|
4875
4908
|
"src/dnsbl.js"() {
|
|
4876
4909
|
import_node_dns = __toESM(require("node:dns"), 1);
|
|
@@ -4878,6 +4911,7 @@ var init_dnsbl = __esm({
|
|
|
4878
4911
|
import_node_url2 = require("node:url");
|
|
4879
4912
|
import_tldts3 = require("tldts");
|
|
4880
4913
|
LOOKUP_LABEL = /^[a-z\d_-]{1,63}$/;
|
|
4914
|
+
LABEL_SEPARATOR = /[.\u3002\uFF0E\uFF61]/g;
|
|
4881
4915
|
CLOUDFLARE_MALWARE = ["1.1.1.2", "1.0.0.2"];
|
|
4882
4916
|
CLOUDFLARE_FAMILY = ["1.1.1.3", "1.0.0.3"];
|
|
4883
4917
|
DnsChecker = class {
|
|
@@ -5117,7 +5151,7 @@ function asciiHostname(value) {
|
|
|
5117
5151
|
return null;
|
|
5118
5152
|
}
|
|
5119
5153
|
const lower = value.normalize("NFKC").toLowerCase().replace(/\.$/, "");
|
|
5120
|
-
if (lower === "" || /[\0-,/:-^`{-\u007F]/.test(lower)) {
|
|
5154
|
+
if (lower === "" || lower.length > 1024 || /[\0-,/:-^`{-\u007F]/.test(lower)) {
|
|
5121
5155
|
return null;
|
|
5122
5156
|
}
|
|
5123
5157
|
const ascii = (0, import_node_url3.domainToASCII)(lower);
|
|
@@ -5190,8 +5224,8 @@ function trustReputation(result, trusted = []) {
|
|
|
5190
5224
|
for (const value of result.checkedValues.filter((item) => allowed.has(item))) {
|
|
5191
5225
|
const signers = allowed.get(value);
|
|
5192
5226
|
const counts = (entry) => Boolean(entry) && (signers === null || signers.some((signer) => vouches(normalizeEntry(entry), signer)));
|
|
5193
|
-
const detail = result.details[value];
|
|
5194
|
-
const listed = result.allowlisted[value] || (detail?.isAllowlisted ? detail.allowlistValue : null);
|
|
5227
|
+
const detail = Object.hasOwn(result.details, value) ? result.details[value] : void 0;
|
|
5228
|
+
const listed = Object.hasOwn(result.allowlisted, value) && result.allowlisted[value] || (detail?.isAllowlisted ? detail.allowlistValue : null);
|
|
5195
5229
|
if (counts(listed) && !out.isAllowlisted) {
|
|
5196
5230
|
out.isAllowlisted = true;
|
|
5197
5231
|
out.allowlistValue = listed;
|
|
@@ -6234,7 +6268,7 @@ function urlOf(value) {
|
|
|
6234
6268
|
return null;
|
|
6235
6269
|
}
|
|
6236
6270
|
try {
|
|
6237
|
-
return
|
|
6271
|
+
return boundedUrl(href);
|
|
6238
6272
|
} catch {
|
|
6239
6273
|
return null;
|
|
6240
6274
|
}
|
|
@@ -6324,7 +6358,11 @@ function* attributesOf(html2) {
|
|
|
6324
6358
|
TAG_START.lastIndex = Math.max(i, match.index + 1);
|
|
6325
6359
|
}
|
|
6326
6360
|
}
|
|
6327
|
-
function linksOf(name, value, add, embed) {
|
|
6361
|
+
function linksOf(name, value, { add, embed, cut }) {
|
|
6362
|
+
if (value.length > MAX_VALUE2) {
|
|
6363
|
+
cut();
|
|
6364
|
+
value = value.slice(0, MAX_VALUE2);
|
|
6365
|
+
}
|
|
6328
6366
|
const decoded = decodeEntities(value, { attribute: true });
|
|
6329
6367
|
switch (name) {
|
|
6330
6368
|
case "srcdoc": {
|
|
@@ -6363,7 +6401,7 @@ function linksOf(name, value, add, embed) {
|
|
|
6363
6401
|
}
|
|
6364
6402
|
}
|
|
6365
6403
|
}
|
|
6366
|
-
function lenientLinks(html2,
|
|
6404
|
+
function lenientLinks(html2, done, reader) {
|
|
6367
6405
|
LENIENT.lastIndex = 0;
|
|
6368
6406
|
let match;
|
|
6369
6407
|
while (!done() && (match = LENIENT.exec(html2)) !== null) {
|
|
@@ -6379,8 +6417,8 @@ function lenientLinks(html2, add, done, embed) {
|
|
|
6379
6417
|
}
|
|
6380
6418
|
if (end < html2.length && html2[end] !== quote) {
|
|
6381
6419
|
const close = html2.indexOf(quote, end);
|
|
6382
|
-
if (close !== -1 && close - i <= MAX_LENIENT_VALUE) {
|
|
6383
|
-
linksOf(name, html2.slice(i, close),
|
|
6420
|
+
if (close !== -1 && close - i <= MAX_LENIENT_VALUE && !reader.spend(close - i)) {
|
|
6421
|
+
linksOf(name, html2.slice(i, close), reader);
|
|
6384
6422
|
}
|
|
6385
6423
|
}
|
|
6386
6424
|
} else {
|
|
@@ -6388,7 +6426,10 @@ function lenientLinks(html2, add, done, embed) {
|
|
|
6388
6426
|
end++;
|
|
6389
6427
|
}
|
|
6390
6428
|
}
|
|
6391
|
-
|
|
6429
|
+
if (reader.spend(end - i)) {
|
|
6430
|
+
break;
|
|
6431
|
+
}
|
|
6432
|
+
linksOf(name, html2.slice(i, end), reader);
|
|
6392
6433
|
}
|
|
6393
6434
|
}
|
|
6394
6435
|
function endsLink2(text, i) {
|
|
@@ -6559,8 +6600,17 @@ function* chunksOf(html2) {
|
|
|
6559
6600
|
cutStart = false;
|
|
6560
6601
|
continue;
|
|
6561
6602
|
}
|
|
6562
|
-
|
|
6563
|
-
start
|
|
6603
|
+
let hard = end;
|
|
6604
|
+
while (hard > start + CHUNK_OVERLAP + 1 && /[\dA-Za-z#]/.test(html2[hard - 1])) {
|
|
6605
|
+
hard--;
|
|
6606
|
+
}
|
|
6607
|
+
if (hard < end && html2[hard - 1] === "&" && hard - 1 > start + CHUNK_OVERLAP) {
|
|
6608
|
+
hard--;
|
|
6609
|
+
} else {
|
|
6610
|
+
hard = end;
|
|
6611
|
+
}
|
|
6612
|
+
yield { text: html2.slice(start, hard), cutStart, cutEnd: true };
|
|
6613
|
+
start = hard - CHUNK_OVERLAP;
|
|
6564
6614
|
cutStart = true;
|
|
6565
6615
|
continue;
|
|
6566
6616
|
}
|
|
@@ -6722,27 +6772,14 @@ function quotedPrintableValues(text) {
|
|
|
6722
6772
|
}
|
|
6723
6773
|
return out.join("\n");
|
|
6724
6774
|
}
|
|
6725
|
-
function
|
|
6726
|
-
const unfolded = text.replaceAll(FOLD, "");
|
|
6727
|
-
if (!/quoted-printable/i.test(unfolded)) {
|
|
6728
|
-
return false;
|
|
6729
|
-
}
|
|
6730
|
-
return unfolded.split(LINE_BREAK).some((line) => {
|
|
6731
|
-
const colon = valueColon(line);
|
|
6732
|
-
return colon !== -1 && QUOTED_PRINTABLE.test(line.slice(0, colon));
|
|
6733
|
-
});
|
|
6734
|
-
}
|
|
6735
|
-
function shownText(text, full = true) {
|
|
6775
|
+
function shownText(text) {
|
|
6736
6776
|
const unfolded = text.replaceAll(FOLD, "");
|
|
6737
6777
|
const shown = /* @__PURE__ */ new Set([text, readEscapes(unfolded)]);
|
|
6738
|
-
|
|
6739
|
-
if (
|
|
6740
|
-
decoded
|
|
6741
|
-
if (
|
|
6742
|
-
shown.add(
|
|
6743
|
-
if (unfolded !== text) {
|
|
6744
|
-
shown.add(quotedPrintableValues(text));
|
|
6745
|
-
}
|
|
6778
|
+
const decoded = quotedPrintableValues(unfolded);
|
|
6779
|
+
if (decoded !== unfolded) {
|
|
6780
|
+
shown.add(readEscapes(decoded));
|
|
6781
|
+
if (unfolded !== text) {
|
|
6782
|
+
shown.add(quotedPrintableValues(text));
|
|
6746
6783
|
}
|
|
6747
6784
|
}
|
|
6748
6785
|
if (/\\[^nN,;\\]/.test(decoded)) {
|
|
@@ -6841,58 +6878,71 @@ function charsetOf(attachment) {
|
|
|
6841
6878
|
const charset = headers && typeof headers.get === "function" ? headers.get("content-type")?.params?.charset : null;
|
|
6842
6879
|
return typeof charset === "string" ? charset.toLowerCase() : "";
|
|
6843
6880
|
}
|
|
6844
|
-
function
|
|
6845
|
-
const
|
|
6881
|
+
function kindOf(attachment) {
|
|
6882
|
+
const { content } = attachment ?? {};
|
|
6883
|
+
if (!import_node_buffer7.Buffer.isBuffer(content) || content.length === 0) {
|
|
6884
|
+
return null;
|
|
6885
|
+
}
|
|
6886
|
+
const type = typeof attachment.contentType === "string" ? attachment.contentType.toLowerCase() : "";
|
|
6887
|
+
const extensions = extensionsOf(attachment);
|
|
6888
|
+
const has = (set) => [...extensions].some((name) => set.has(name));
|
|
6889
|
+
if (MESSAGE_TYPES.has(type) || extensions.has("eml") || extensions.has("u8msg")) {
|
|
6890
|
+
return null;
|
|
6891
|
+
}
|
|
6892
|
+
const isHtml = HTML_TYPES.has(type) || has(HTML_EXTENSIONS);
|
|
6893
|
+
const isCard = !isHtml && (CARD_TYPES.has(type) || has(CARD_EXTENSIONS));
|
|
6894
|
+
const isText = isCard || !isHtml && type === "text/plain";
|
|
6895
|
+
return isHtml || isText ? { isHtml, isText, isCard } : null;
|
|
6896
|
+
}
|
|
6897
|
+
function* documentsOf(attachments, state, late) {
|
|
6898
|
+
const read = [];
|
|
6846
6899
|
let bytes = 0;
|
|
6847
6900
|
let opened = 0;
|
|
6848
6901
|
for (const attachment of attachments) {
|
|
6849
|
-
const
|
|
6850
|
-
if (!
|
|
6851
|
-
continue;
|
|
6852
|
-
}
|
|
6853
|
-
const type = typeof attachment.contentType === "string" ? attachment.contentType.toLowerCase() : "";
|
|
6854
|
-
const extensions = extensionsOf(attachment);
|
|
6855
|
-
const has = (set) => [...extensions].some((name) => set.has(name));
|
|
6856
|
-
if (MESSAGE_TYPES.has(type) || extensions.has("eml") || extensions.has("u8msg")) {
|
|
6857
|
-
continue;
|
|
6858
|
-
}
|
|
6859
|
-
const isHtml = HTML_TYPES.has(type) || has(HTML_EXTENSIONS);
|
|
6860
|
-
const isCard = !isHtml && (CARD_TYPES.has(type) || has(CARD_EXTENSIONS));
|
|
6861
|
-
const isText = isCard || !isHtml && type === "text/plain";
|
|
6862
|
-
if (!isHtml && !isText) {
|
|
6902
|
+
const kind = kindOf(attachment);
|
|
6903
|
+
if (!kind) {
|
|
6863
6904
|
continue;
|
|
6864
6905
|
}
|
|
6865
|
-
if (!isText && ++opened > MAX_ATTACHED || bytes + content.length > MAX_ATTACHED_BYTES) {
|
|
6866
|
-
|
|
6867
|
-
|
|
6906
|
+
if (!kind.isText && ++opened > MAX_ATTACHED || bytes + attachment.content.length > MAX_ATTACHED_BYTES) {
|
|
6907
|
+
state.tooComplex = true;
|
|
6908
|
+
break;
|
|
6868
6909
|
}
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6910
|
+
bytes += attachment.content.length;
|
|
6911
|
+
read.push({ attachment, kind });
|
|
6912
|
+
}
|
|
6913
|
+
for (const { attachment, kind: { isHtml, isCard } } of read) {
|
|
6914
|
+
if (late()) {
|
|
6915
|
+
state.incomplete = true;
|
|
6916
|
+
return;
|
|
6872
6917
|
}
|
|
6873
|
-
|
|
6874
|
-
const text = decodeText(content, charsetOf(attachment));
|
|
6918
|
+
const text = decodeText(attachment.content, charsetOf(attachment));
|
|
6875
6919
|
if (isHtml) {
|
|
6876
|
-
|
|
6920
|
+
yield { html: text };
|
|
6877
6921
|
continue;
|
|
6878
6922
|
}
|
|
6879
6923
|
if (!isCard) {
|
|
6880
|
-
|
|
6924
|
+
yield { text };
|
|
6881
6925
|
continue;
|
|
6882
6926
|
}
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6927
|
+
let readable = withoutBinaryValues(text.length > MAX_CARD_BYTES ? text.slice(0, MAX_CARD_BYTES) : text);
|
|
6928
|
+
if (text.length > MAX_CARD_BYTES || readable.length > MAX_SHOWN_BYTES) {
|
|
6929
|
+
state.tooComplex = true;
|
|
6930
|
+
readable = readable.slice(0, MAX_SHOWN_BYTES);
|
|
6931
|
+
}
|
|
6932
|
+
if (late()) {
|
|
6933
|
+
state.incomplete = true;
|
|
6934
|
+
return;
|
|
6935
|
+
}
|
|
6936
|
+
yield { text: shownText(readable) };
|
|
6937
|
+
if (late()) {
|
|
6938
|
+
state.incomplete = true;
|
|
6939
|
+
return;
|
|
6888
6940
|
}
|
|
6889
|
-
documents.push({ text: shownText(readable, full) });
|
|
6890
6941
|
const html2 = htmlDescriptions(readable);
|
|
6891
6942
|
if (html2 !== "") {
|
|
6892
|
-
|
|
6943
|
+
yield { html: html2 };
|
|
6893
6944
|
}
|
|
6894
6945
|
}
|
|
6895
|
-
return documents;
|
|
6896
6946
|
}
|
|
6897
6947
|
function carriedLinkHosts(mail, {
|
|
6898
6948
|
mails = [],
|
|
@@ -6906,40 +6956,45 @@ function carriedLinkHosts(mail, {
|
|
|
6906
6956
|
const seenBare = /* @__PURE__ */ new Set();
|
|
6907
6957
|
const seenLoose = /* @__PURE__ */ new Set();
|
|
6908
6958
|
let more = false;
|
|
6909
|
-
let
|
|
6910
|
-
const
|
|
6911
|
-
const
|
|
6959
|
+
let full = false;
|
|
6960
|
+
const domains = /* @__PURE__ */ new Set();
|
|
6961
|
+
const state = { tooComplex: false, incomplete: false };
|
|
6962
|
+
const until = Date.now() + MAX_READ_TIME;
|
|
6963
|
+
const late = () => {
|
|
6964
|
+
if (!state.incomplete && Date.now() > until) {
|
|
6965
|
+
state.incomplete = true;
|
|
6966
|
+
}
|
|
6967
|
+
return state.incomplete;
|
|
6968
|
+
};
|
|
6969
|
+
let calls = 0;
|
|
6970
|
+
const done = () => full || state.incomplete || ++calls % LATE_EVERY === 0 && late();
|
|
6971
|
+
const looseDone = () => done() || loose.size > maxHosts;
|
|
6912
6972
|
const addLoose = (host) => {
|
|
6913
6973
|
if (!looseDone()) {
|
|
6914
6974
|
loose.add(host);
|
|
6915
6975
|
}
|
|
6916
6976
|
};
|
|
6917
|
-
const
|
|
6918
|
-
|
|
6919
|
-
const late = () => {
|
|
6920
|
-
if (!incomplete && Date.now() > until) {
|
|
6921
|
-
incomplete = true;
|
|
6922
|
-
}
|
|
6923
|
-
return incomplete;
|
|
6924
|
-
};
|
|
6925
|
-
const carried = documentsOf(attachments, until);
|
|
6926
|
-
tooComplex = Boolean(carried.tooComplex);
|
|
6927
|
-
incomplete = Boolean(carried.incomplete);
|
|
6928
|
-
const queue = [mail, ...mails, ...carried].map((document) => ({ document, depth: 0 }));
|
|
6977
|
+
const queue = [mail, ...mails].map((document) => ({ document, depth: 0 }));
|
|
6978
|
+
const carried = documentsOf(attachments, state, late);
|
|
6929
6979
|
let depth = 0;
|
|
6930
6980
|
let embedded = 0;
|
|
6931
6981
|
let embeddedLength = 0;
|
|
6982
|
+
const embeddedSeen = /* @__PURE__ */ new Set();
|
|
6932
6983
|
const embed = (html2) => {
|
|
6984
|
+
if (html2 === "" || embeddedSeen.has(html2)) {
|
|
6985
|
+
return;
|
|
6986
|
+
}
|
|
6933
6987
|
if (depth >= MAX_EMBED_DEPTH || embedded >= MAX_EMBEDDED || embeddedLength + html2.length > MAX_EMBEDDED_LENGTH) {
|
|
6934
|
-
tooComplex = true;
|
|
6988
|
+
state.tooComplex = true;
|
|
6935
6989
|
return;
|
|
6936
6990
|
}
|
|
6991
|
+
embeddedSeen.add(html2);
|
|
6937
6992
|
embedded++;
|
|
6938
6993
|
embeddedLength += html2.length;
|
|
6939
6994
|
queue.push({ document: { html: html2 }, depth: depth + 1 });
|
|
6940
6995
|
};
|
|
6941
6996
|
const add = (value) => {
|
|
6942
|
-
if (
|
|
6997
|
+
if (full || typeof value !== "string" || value === "" || seen.has(value)) {
|
|
6943
6998
|
return;
|
|
6944
6999
|
}
|
|
6945
7000
|
if (seen.size < MAX_SEEN) {
|
|
@@ -6954,34 +7009,64 @@ function carriedLinkHosts(mail, {
|
|
|
6954
7009
|
}
|
|
6955
7010
|
const found = hostsOf(value);
|
|
6956
7011
|
if (found.unresolved) {
|
|
6957
|
-
tooComplex = true;
|
|
7012
|
+
state.tooComplex = true;
|
|
6958
7013
|
}
|
|
6959
7014
|
for (const host of found) {
|
|
6960
7015
|
if (hosts.has(host)) {
|
|
6961
7016
|
continue;
|
|
6962
7017
|
}
|
|
6963
|
-
|
|
6964
|
-
|
|
7018
|
+
const domain = registrableDomain(host);
|
|
7019
|
+
if (hosts.size < maxHosts || !domains.has(domain) && domains.size < maxHosts) {
|
|
7020
|
+
hosts.add(host);
|
|
7021
|
+
domains.add(domain);
|
|
7022
|
+
continue;
|
|
7023
|
+
}
|
|
7024
|
+
more = true;
|
|
7025
|
+
if (domains.size >= maxHosts) {
|
|
7026
|
+
full = true;
|
|
6965
7027
|
return;
|
|
6966
7028
|
}
|
|
6967
|
-
hosts.add(host);
|
|
6968
7029
|
}
|
|
6969
7030
|
};
|
|
6970
|
-
|
|
7031
|
+
let spent = 0;
|
|
7032
|
+
const spend = (length) => {
|
|
7033
|
+
spent += length;
|
|
7034
|
+
if (spent < LONG_VALUE) {
|
|
7035
|
+
return false;
|
|
7036
|
+
}
|
|
7037
|
+
spent = 0;
|
|
7038
|
+
return late();
|
|
7039
|
+
};
|
|
7040
|
+
const reader = {
|
|
7041
|
+
add,
|
|
7042
|
+
embed,
|
|
7043
|
+
spend,
|
|
7044
|
+
cut() {
|
|
7045
|
+
state.tooComplex = true;
|
|
7046
|
+
}
|
|
7047
|
+
};
|
|
7048
|
+
const readText = (source, { cutStart = false, cutEnd = false, exact = false } = {}) => {
|
|
6971
7049
|
for (const match of source.matchAll(URL_START)) {
|
|
6972
|
-
if (
|
|
7050
|
+
if (done()) {
|
|
6973
7051
|
break;
|
|
6974
7052
|
}
|
|
6975
7053
|
if (cutStart && match.index === 0) {
|
|
6976
7054
|
continue;
|
|
6977
7055
|
}
|
|
6978
7056
|
if (cutEnd && match.index + match[0].length === source.length) {
|
|
7057
|
+
if (exact && source.length - match.index < CHUNK_OVERLAP) {
|
|
7058
|
+
continue;
|
|
7059
|
+
}
|
|
6979
7060
|
const value = match[0];
|
|
6980
7061
|
const at = /^www\./i.test(value) ? 0 : value.indexOf("//") + 2;
|
|
6981
|
-
if (
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
7062
|
+
if (!/[/?#\\]/.test(value.slice(at))) {
|
|
7063
|
+
state.tooComplex = true;
|
|
7064
|
+
continue;
|
|
7065
|
+
}
|
|
7066
|
+
add(value);
|
|
7067
|
+
const url = urlOf(value);
|
|
7068
|
+
if (url && isClickTracker(url)) {
|
|
7069
|
+
state.tooComplex = true;
|
|
6985
7070
|
}
|
|
6986
7071
|
continue;
|
|
6987
7072
|
}
|
|
@@ -7003,42 +7088,60 @@ function carriedLinkHosts(mail, {
|
|
|
7003
7088
|
});
|
|
7004
7089
|
}
|
|
7005
7090
|
};
|
|
7006
|
-
for (
|
|
7007
|
-
if (
|
|
7091
|
+
for (let index = 0; ; index++) {
|
|
7092
|
+
if (full || late()) {
|
|
7008
7093
|
break;
|
|
7009
7094
|
}
|
|
7010
|
-
(
|
|
7011
|
-
|
|
7095
|
+
if (index >= queue.length) {
|
|
7096
|
+
const next = carried.next();
|
|
7097
|
+
if (next.done) {
|
|
7098
|
+
break;
|
|
7099
|
+
}
|
|
7100
|
+
queue.push({ document: next.value, depth: 0 });
|
|
7101
|
+
}
|
|
7102
|
+
({ depth } = queue[index]);
|
|
7103
|
+
const { document } = queue[index];
|
|
7012
7104
|
const html2 = withoutNul(typeof document?.html === "string" ? document.html : "");
|
|
7013
7105
|
const text = withoutNul(typeof document?.text === "string" ? document.text : "");
|
|
7014
7106
|
for (const [name, value] of attributesOf(html2)) {
|
|
7015
|
-
if (
|
|
7107
|
+
if (done()) {
|
|
7016
7108
|
break;
|
|
7017
7109
|
}
|
|
7018
|
-
if (LINK_ATTRIBUTES.has(name)) {
|
|
7019
|
-
|
|
7110
|
+
if (!LINK_ATTRIBUTES.has(name)) {
|
|
7111
|
+
continue;
|
|
7020
7112
|
}
|
|
7113
|
+
if (spend(value.length)) {
|
|
7114
|
+
break;
|
|
7115
|
+
}
|
|
7116
|
+
linksOf(name, value, reader);
|
|
7021
7117
|
}
|
|
7022
|
-
lenientLinks(html2,
|
|
7118
|
+
lenientLinks(html2, done, reader);
|
|
7023
7119
|
for (const pattern of [CSS_URL, CSS_IMPORT]) {
|
|
7024
7120
|
for (const [, link] of html2.matchAll(pattern)) {
|
|
7025
|
-
if (
|
|
7121
|
+
if (done() || spend(link.length)) {
|
|
7026
7122
|
break;
|
|
7027
7123
|
}
|
|
7028
|
-
|
|
7124
|
+
if (link.length > MAX_VALUE2) {
|
|
7125
|
+
state.tooComplex = true;
|
|
7126
|
+
}
|
|
7127
|
+
add(decodeEntities(link.slice(0, MAX_VALUE2), { attribute: true }));
|
|
7029
7128
|
}
|
|
7030
7129
|
}
|
|
7031
|
-
|
|
7032
|
-
|
|
7130
|
+
for (const chunk2 of chunksOf(text)) {
|
|
7131
|
+
if (full || late()) {
|
|
7132
|
+
break;
|
|
7133
|
+
}
|
|
7134
|
+
readText(chunk2.text, { ...chunk2, exact: true });
|
|
7033
7135
|
}
|
|
7034
7136
|
for (const chunk2 of chunksOf(html2)) {
|
|
7035
|
-
if (
|
|
7137
|
+
if (full || late()) {
|
|
7036
7138
|
break;
|
|
7037
7139
|
}
|
|
7038
|
-
|
|
7140
|
+
const decoded = decodeEntities(chunk2.text);
|
|
7141
|
+
readText(decoded, { ...chunk2, exact: decoded.endsWith(chunk2.text.slice(-CHUNK_OVERLAP)) });
|
|
7039
7142
|
}
|
|
7040
7143
|
}
|
|
7041
|
-
if (!more && !incomplete) {
|
|
7144
|
+
if (!more && !state.incomplete) {
|
|
7042
7145
|
for (const name of loose) {
|
|
7043
7146
|
if (hosts.size >= maxHosts) {
|
|
7044
7147
|
break;
|
|
@@ -7052,11 +7155,11 @@ function carriedLinkHosts(mail, {
|
|
|
7052
7155
|
return {
|
|
7053
7156
|
hosts: [...hosts],
|
|
7054
7157
|
more,
|
|
7055
|
-
tooComplex,
|
|
7056
|
-
incomplete
|
|
7158
|
+
tooComplex: state.tooComplex,
|
|
7159
|
+
incomplete: state.incomplete
|
|
7057
7160
|
};
|
|
7058
7161
|
}
|
|
7059
|
-
var import_node_buffer7, import_node_net4, import_tldts4, LINK_ATTRIBUTES, TAG_START, URL_START, CSS_URL, CSS_IMPORT, LENIENT, IN_WORD2, NAMESPACES, MAX_HOST, MAX_SEEN, TRAILING, DECODE_CHUNK, CHUNK_OVERLAP, MAX_EMBED_DEPTH, MAX_EMBEDDED, MAX_EMBEDDED_LENGTH, MAX_ATTACHED, MAX_ATTACHED_BYTES, MAX_READ_TIME, MAX_SHOWN_BYTES, MAX_LENIENT_VALUE, UNQUOTED_END, UTF16LE, UTF16BE, DATA_DOCUMENT, LINE_BREAK, FOLD, QUOTED_PRINTABLE, MEDIA_PROPERTIES, HTML_PROPERTIES, BINARY, HTML_TYPES, HTML_EXTENSIONS, CARD_TYPES, CARD_EXTENSIONS, MESSAGE_TYPES;
|
|
7162
|
+
var import_node_buffer7, import_node_net4, import_tldts4, LINK_ATTRIBUTES, TAG_START, URL_START, CSS_URL, CSS_IMPORT, LENIENT, IN_WORD2, NAMESPACES, MAX_HOST, MAX_SEEN, TRAILING, DECODE_CHUNK, CHUNK_OVERLAP, MAX_EMBED_DEPTH, MAX_EMBEDDED, MAX_EMBEDDED_LENGTH, MAX_ATTACHED, MAX_ATTACHED_BYTES, MAX_READ_TIME, MAX_CARD_BYTES, MAX_SHOWN_BYTES, MAX_VALUE2, LONG_VALUE, MAX_LENIENT_VALUE, UNQUOTED_END, UTF16LE, UTF16BE, DATA_DOCUMENT, LINE_BREAK, FOLD, QUOTED_PRINTABLE, MEDIA_PROPERTIES, HTML_PROPERTIES, BINARY, HTML_TYPES, HTML_EXTENSIONS, CARD_TYPES, CARD_EXTENSIONS, MESSAGE_TYPES, LATE_EVERY;
|
|
7060
7163
|
var init_links = __esm({
|
|
7061
7164
|
"src/links.js"() {
|
|
7062
7165
|
import_node_buffer7 = require("node:buffer");
|
|
@@ -7103,7 +7206,10 @@ var init_links = __esm({
|
|
|
7103
7206
|
MAX_ATTACHED = 100;
|
|
7104
7207
|
MAX_ATTACHED_BYTES = 50 * 1024 * 1024;
|
|
7105
7208
|
MAX_READ_TIME = 5e3;
|
|
7106
|
-
|
|
7209
|
+
MAX_CARD_BYTES = 4 * 1024 * 1024;
|
|
7210
|
+
MAX_SHOWN_BYTES = 1024 * 1024;
|
|
7211
|
+
MAX_VALUE2 = 1024 * 1024;
|
|
7212
|
+
LONG_VALUE = 64 * 1024;
|
|
7107
7213
|
MAX_LENIENT_VALUE = 4096;
|
|
7108
7214
|
UNQUOTED_END = /* @__PURE__ */ new Set(["<", ">", '"', "'", "`", "="]);
|
|
7109
7215
|
UTF16LE = /* @__PURE__ */ new Set(["csunicode", "iso-10646-ucs-2", "ucs-2", "unicode", "unicodefeff", "utf-16", "utf-16le"]);
|
|
@@ -7120,6 +7226,7 @@ var init_links = __esm({
|
|
|
7120
7226
|
CARD_TYPES = /* @__PURE__ */ new Set(["text/calendar", "text/x-vcalendar", "application/ics", "text/vcard", "text/x-vcard", "text/directory"]);
|
|
7121
7227
|
CARD_EXTENSIONS = /* @__PURE__ */ new Set(["ics", "ical", "ifb", "vcs", "vcf", "vcard"]);
|
|
7122
7228
|
MESSAGE_TYPES = /* @__PURE__ */ new Set(["message/rfc822", "message/global"]);
|
|
7229
|
+
LATE_EVERY = 1024;
|
|
7123
7230
|
}
|
|
7124
7231
|
});
|
|
7125
7232
|
|
|
@@ -8784,7 +8891,7 @@ var init_package = __esm({
|
|
|
8784
8891
|
package_default = {
|
|
8785
8892
|
name: "spamscanner",
|
|
8786
8893
|
description: "Spam, phishing and malware filter for email in any language: a trainable classifier, link and attachment checks, SPF/DKIM/DMARC, and an optional AI second opinion from any LLM, including local Ollama models. Works as a library, CLI, Postfix milter or HTTP API.",
|
|
8787
|
-
version: "7.0.
|
|
8894
|
+
version: "7.0.12",
|
|
8788
8895
|
author: "Forward Email (https://forwardemail.net)",
|
|
8789
8896
|
bin: {
|
|
8790
8897
|
spamscanner: "./dist/esm/cli.js"
|
|
@@ -9050,9 +9157,12 @@ function rewriteMessage(source, headers = [], options = {}) {
|
|
|
9050
9157
|
}
|
|
9051
9158
|
}
|
|
9052
9159
|
const added = headers.map(([name, value]) => foldHeader(name, import_node_buffer9.Buffer.from(String(value), "utf8").toString("latin1"), newline));
|
|
9053
|
-
const mbox = kept.length > 0 && kept[0][0].startsWith("From ") ? kept.shift() : [];
|
|
9160
|
+
const mbox = kept.length > 0 && kept[0][0].startsWith("From ") ? kept.shift().slice(0, 1) : [];
|
|
9161
|
+
if (kept.length > 0 && /^[ \t]/.test(kept[0][0])) {
|
|
9162
|
+
kept.shift();
|
|
9163
|
+
}
|
|
9054
9164
|
const head = [...mbox, ...added, ...kept.map((field) => field.join(newline))].join(newline);
|
|
9055
|
-
return import_node_buffer9.Buffer.concat([import_node_buffer9.Buffer.from(head === ""
|
|
9165
|
+
return import_node_buffer9.Buffer.concat([import_node_buffer9.Buffer.from(head === "" ? newline : `${head}${newline}${newline}`, "latin1"), body]);
|
|
9056
9166
|
}
|
|
9057
9167
|
var import_node_buffer9, ENCODED_BYTES, PLAIN_WORD, ENCODED_LINE, MAX_LINE2, ENCODED_WORD, ENCODED_START;
|
|
9058
9168
|
var init_headers = __esm({
|
|
@@ -9414,7 +9524,12 @@ var init_train = __esm({
|
|
|
9414
9524
|
|
|
9415
9525
|
// src/server.js
|
|
9416
9526
|
function serializeResult(result, options = {}) {
|
|
9417
|
-
const {
|
|
9527
|
+
const {
|
|
9528
|
+
mail,
|
|
9529
|
+
tokens,
|
|
9530
|
+
attached,
|
|
9531
|
+
...rest
|
|
9532
|
+
} = result;
|
|
9418
9533
|
const output = {
|
|
9419
9534
|
...rest,
|
|
9420
9535
|
results: { ...result.results }
|
|
@@ -10099,7 +10214,7 @@ function report(result) {
|
|
|
10099
10214
|
"",
|
|
10100
10215
|
" pts rule name description",
|
|
10101
10216
|
"---- ---------------------- --------------------------------------------------",
|
|
10102
|
-
...result.tests.map((test) => `${test.score.toFixed(1).padStart(4)} ${test.name.padEnd(22)} ${test.description}`)
|
|
10217
|
+
...result.tests.map((test) => `${test.score.toFixed(1).padStart(4)} ${oneLine2(test.name).padEnd(22)} ${oneLine2(test.description)}`)
|
|
10103
10218
|
];
|
|
10104
10219
|
return `${lines2.join("\r\n")}\r
|
|
10105
10220
|
`;
|
|
@@ -10245,7 +10360,7 @@ function createSpamdServer(scanner, options = {}) {
|
|
|
10245
10360
|
server.maxConnections = settings.maxConnections;
|
|
10246
10361
|
return server;
|
|
10247
10362
|
}
|
|
10248
|
-
var import_node_buffer13, import_node_net7, EX_OK, EX_USAGE, EX_DATAERR, EX_SOFTWARE, EX_PROTOCOL, EX_NOPERM, COMMANDS2;
|
|
10363
|
+
var import_node_buffer13, import_node_net7, EX_OK, EX_USAGE, EX_DATAERR, EX_SOFTWARE, EX_PROTOCOL, EX_NOPERM, COMMANDS2, oneLine2;
|
|
10249
10364
|
var init_spamd = __esm({
|
|
10250
10365
|
"src/spamd.js"() {
|
|
10251
10366
|
import_node_buffer13 = require("node:buffer");
|
|
@@ -10260,6 +10375,7 @@ var init_spamd = __esm({
|
|
|
10260
10375
|
EX_PROTOCOL = 76;
|
|
10261
10376
|
EX_NOPERM = 77;
|
|
10262
10377
|
COMMANDS2 = /* @__PURE__ */ new Set(["CHECK", "SYMBOLS", "REPORT", "REPORT_IFSPAM", "PROCESS", "HEADERS", "PING", "SKIP", "TELL"]);
|
|
10378
|
+
oneLine2 = (text) => String(text ?? "").replaceAll(/[\p{Cc}\u061C\u200E\u200F\u2028\u2029\u202A-\u202E\u2066-\u2069]+/gu, " ");
|
|
10263
10379
|
}
|
|
10264
10380
|
});
|
|
10265
10381
|
|
|
@@ -10824,7 +10940,7 @@ var init_src = __esm({
|
|
|
10824
10940
|
// (`from`: the sender's domain; `carried`: the link hosts of the whole
|
|
10825
10941
|
// message and of what it carries, see carriedLinkHosts)
|
|
10826
10942
|
async checkLinks(links, config, hosts = [], options = {}) {
|
|
10827
|
-
const { from = "", carried = null } = typeof options === "string" ? { from: options } : options;
|
|
10943
|
+
const { from = "", carried = null } = typeof options === "string" ? { from: options } : options ?? {};
|
|
10828
10944
|
const phishing = [];
|
|
10829
10945
|
const homographs = [];
|
|
10830
10946
|
const seenHosts = /* @__PURE__ */ new Set();
|
|
@@ -10884,15 +11000,15 @@ var init_src = __esm({
|
|
|
10884
11000
|
for (const host of carriedHosts) {
|
|
10885
11001
|
seenHosts.add(host);
|
|
10886
11002
|
}
|
|
10887
|
-
const
|
|
11003
|
+
const own3 = [...found.filter((link) => link.text), ...found.filter((link) => !link.text), ...bare].map((link) => lookupName(destination(link))).filter(Boolean);
|
|
10888
11004
|
const firsts = /* @__PURE__ */ new Map();
|
|
10889
|
-
for (const host of
|
|
11005
|
+
for (const host of [...own3, ...carriedHosts]) {
|
|
10890
11006
|
const domain = registrableDomain(host);
|
|
10891
11007
|
if (!firsts.has(domain)) {
|
|
10892
11008
|
firsts.set(domain, host);
|
|
10893
11009
|
}
|
|
10894
11010
|
}
|
|
10895
|
-
const checked = [.../* @__PURE__ */ new Set([...firsts.values(), ...
|
|
11011
|
+
const checked = [.../* @__PURE__ */ new Set([...firsts.values(), ...own3, ...carriedHosts])].slice(0, config.phishing.maxHosts);
|
|
10896
11012
|
const domains = [...new Set(checked.map((host) => registrableDomain(host)))];
|
|
10897
11013
|
const blocked = [];
|
|
10898
11014
|
let answered = 0;
|