spamscanner 7.0.11 → 7.0.13
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 +274 -117
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/esm/arf.js.map +1 -1
- package/dist/esm/cli.js +278 -120
- package/dist/esm/cli.js.map +3 -3
- package/dist/esm/index.js +270 -113
- 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,23 @@ function* attributesOf(html2) {
|
|
|
6324
6358
|
TAG_START.lastIndex = Math.max(i, match.index + 1);
|
|
6325
6359
|
}
|
|
6326
6360
|
}
|
|
6327
|
-
function
|
|
6361
|
+
function isCut(value) {
|
|
6362
|
+
if (value.length <= MAX_VALUE2) {
|
|
6363
|
+
return false;
|
|
6364
|
+
}
|
|
6365
|
+
let references = 0;
|
|
6366
|
+
for (let index = value.indexOf("&"); index !== -1; index = value.indexOf("&", index + 1)) {
|
|
6367
|
+
if (++references > MAX_REFERENCES) {
|
|
6368
|
+
return true;
|
|
6369
|
+
}
|
|
6370
|
+
}
|
|
6371
|
+
return false;
|
|
6372
|
+
}
|
|
6373
|
+
function linksOf(name, value, { add, embed, cut }) {
|
|
6374
|
+
if (isCut(value)) {
|
|
6375
|
+
cut();
|
|
6376
|
+
value = value.slice(0, MAX_VALUE2);
|
|
6377
|
+
}
|
|
6328
6378
|
const decoded = decodeEntities(value, { attribute: true });
|
|
6329
6379
|
switch (name) {
|
|
6330
6380
|
case "srcdoc": {
|
|
@@ -6363,7 +6413,7 @@ function linksOf(name, value, add, embed) {
|
|
|
6363
6413
|
}
|
|
6364
6414
|
}
|
|
6365
6415
|
}
|
|
6366
|
-
function lenientLinks(html2,
|
|
6416
|
+
function lenientLinks(html2, done, reader) {
|
|
6367
6417
|
LENIENT.lastIndex = 0;
|
|
6368
6418
|
let match;
|
|
6369
6419
|
while (!done() && (match = LENIENT.exec(html2)) !== null) {
|
|
@@ -6379,8 +6429,8 @@ function lenientLinks(html2, add, done, embed) {
|
|
|
6379
6429
|
}
|
|
6380
6430
|
if (end < html2.length && html2[end] !== quote) {
|
|
6381
6431
|
const close = html2.indexOf(quote, end);
|
|
6382
|
-
if (close !== -1 && close - i <= MAX_LENIENT_VALUE) {
|
|
6383
|
-
linksOf(name, html2.slice(i, close),
|
|
6432
|
+
if (close !== -1 && close - i <= MAX_LENIENT_VALUE && !reader.spend(close - i)) {
|
|
6433
|
+
linksOf(name, html2.slice(i, close), reader);
|
|
6384
6434
|
}
|
|
6385
6435
|
}
|
|
6386
6436
|
} else {
|
|
@@ -6388,7 +6438,10 @@ function lenientLinks(html2, add, done, embed) {
|
|
|
6388
6438
|
end++;
|
|
6389
6439
|
}
|
|
6390
6440
|
}
|
|
6391
|
-
|
|
6441
|
+
if (reader.spend(end - i)) {
|
|
6442
|
+
break;
|
|
6443
|
+
}
|
|
6444
|
+
linksOf(name, html2.slice(i, end), reader);
|
|
6392
6445
|
}
|
|
6393
6446
|
}
|
|
6394
6447
|
function endsLink2(text, i) {
|
|
@@ -6559,8 +6612,17 @@ function* chunksOf(html2) {
|
|
|
6559
6612
|
cutStart = false;
|
|
6560
6613
|
continue;
|
|
6561
6614
|
}
|
|
6562
|
-
|
|
6563
|
-
start
|
|
6615
|
+
let hard = end;
|
|
6616
|
+
while (hard > start + CHUNK_OVERLAP + 1 && /[\dA-Za-z#]/.test(html2[hard - 1])) {
|
|
6617
|
+
hard--;
|
|
6618
|
+
}
|
|
6619
|
+
if (hard < end && html2[hard - 1] === "&" && hard - 1 > start + CHUNK_OVERLAP) {
|
|
6620
|
+
hard--;
|
|
6621
|
+
} else {
|
|
6622
|
+
hard = end;
|
|
6623
|
+
}
|
|
6624
|
+
yield { text: html2.slice(start, hard), cutStart, cutEnd: true };
|
|
6625
|
+
start = hard - CHUNK_OVERLAP;
|
|
6564
6626
|
cutStart = true;
|
|
6565
6627
|
continue;
|
|
6566
6628
|
}
|
|
@@ -6774,40 +6836,50 @@ function htmlDescriptions(text) {
|
|
|
6774
6836
|
}
|
|
6775
6837
|
return html2.join("\n");
|
|
6776
6838
|
}
|
|
6777
|
-
function withoutBinaryValues(text) {
|
|
6839
|
+
function withoutBinaryValues(text, limit = Number.POSITIVE_INFINITY) {
|
|
6778
6840
|
if (!/base64|binary|encoding\s*=\s*"?b|;\s*b\s*[;:]/i.test(text)) {
|
|
6779
6841
|
return text;
|
|
6780
6842
|
}
|
|
6781
6843
|
const lines2 = text.split(LINE_BREAK);
|
|
6782
6844
|
const out = [];
|
|
6845
|
+
let length = 0;
|
|
6846
|
+
const push = (line) => {
|
|
6847
|
+
out.push(line);
|
|
6848
|
+
length += line.length + 1;
|
|
6849
|
+
};
|
|
6783
6850
|
for (let i = 0; i < lines2.length; i++) {
|
|
6851
|
+
if (length > limit) {
|
|
6852
|
+
break;
|
|
6853
|
+
}
|
|
6784
6854
|
const line = lines2[i];
|
|
6785
6855
|
const colon = valueColon(line);
|
|
6786
6856
|
if (colon === -1 || !BINARY.test(line.slice(0, colon))) {
|
|
6787
|
-
|
|
6857
|
+
push(line);
|
|
6788
6858
|
continue;
|
|
6789
6859
|
}
|
|
6790
|
-
|
|
6860
|
+
let base64 = /^[\w+/=\s]*$/.test(line.slice(colon + 1));
|
|
6791
6861
|
let next = i + 1;
|
|
6792
6862
|
while (next < lines2.length && (lines2[next].startsWith(" ") || lines2[next].startsWith(" "))) {
|
|
6793
|
-
|
|
6863
|
+
base64 &&= /^[\w+/=\s]*$/.test(lines2[next]);
|
|
6864
|
+
next++;
|
|
6794
6865
|
}
|
|
6795
|
-
if (!
|
|
6866
|
+
if (!base64) {
|
|
6796
6867
|
for (let index = i; index < next; index++) {
|
|
6797
|
-
|
|
6868
|
+
push(lines2[index]);
|
|
6798
6869
|
}
|
|
6799
6870
|
i = next - 1;
|
|
6800
6871
|
continue;
|
|
6801
6872
|
}
|
|
6802
6873
|
while (next < lines2.length && lines2[next] !== "" && /^[\w+/=]+$/.test(lines2[next])) {
|
|
6803
|
-
|
|
6874
|
+
next++;
|
|
6804
6875
|
}
|
|
6876
|
+
const end = next;
|
|
6805
6877
|
if (next < lines2.length && lines2[next] === "") {
|
|
6806
6878
|
next++;
|
|
6807
6879
|
}
|
|
6808
|
-
i = next - 1;
|
|
6809
6880
|
const name = line.slice(0, colon).split(";")[0].split(".").pop().trim().toLowerCase();
|
|
6810
|
-
|
|
6881
|
+
push(MEDIA_PROPERTIES.has(name) ? line.slice(0, colon + 1) : line.slice(0, colon + 1) + import_node_buffer7.Buffer.from([line.slice(colon + 1), ...lines2.slice(i + 1, end)].join("").replaceAll(/\s/g, ""), "base64").toString("utf8"));
|
|
6882
|
+
i = next - 1;
|
|
6811
6883
|
}
|
|
6812
6884
|
return out.join("\n");
|
|
6813
6885
|
}
|
|
@@ -6841,58 +6913,75 @@ function charsetOf(attachment) {
|
|
|
6841
6913
|
const charset = headers && typeof headers.get === "function" ? headers.get("content-type")?.params?.charset : null;
|
|
6842
6914
|
return typeof charset === "string" ? charset.toLowerCase() : "";
|
|
6843
6915
|
}
|
|
6844
|
-
function
|
|
6845
|
-
const
|
|
6916
|
+
function kindOf(attachment) {
|
|
6917
|
+
const { content } = attachment ?? {};
|
|
6918
|
+
if (!import_node_buffer7.Buffer.isBuffer(content) || content.length === 0) {
|
|
6919
|
+
return null;
|
|
6920
|
+
}
|
|
6921
|
+
const type = typeof attachment.contentType === "string" ? attachment.contentType.toLowerCase() : "";
|
|
6922
|
+
const extensions = extensionsOf(attachment);
|
|
6923
|
+
const has = (set) => [...extensions].some((name) => set.has(name));
|
|
6924
|
+
if (MESSAGE_TYPES.has(type) || extensions.has("eml") || extensions.has("u8msg")) {
|
|
6925
|
+
return null;
|
|
6926
|
+
}
|
|
6927
|
+
const isHtml = HTML_TYPES.has(type) || has(HTML_EXTENSIONS);
|
|
6928
|
+
const isCard = !isHtml && (CARD_TYPES.has(type) || has(CARD_EXTENSIONS));
|
|
6929
|
+
const isText = isCard || !isHtml && type === "text/plain";
|
|
6930
|
+
return isHtml || isText ? { isHtml, isText, isCard } : null;
|
|
6931
|
+
}
|
|
6932
|
+
function* documentsOf(attachments, state, late) {
|
|
6933
|
+
const read = [];
|
|
6846
6934
|
let bytes = 0;
|
|
6847
6935
|
let opened = 0;
|
|
6848
6936
|
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) {
|
|
6937
|
+
const kind = kindOf(attachment);
|
|
6938
|
+
if (!kind) {
|
|
6863
6939
|
continue;
|
|
6864
6940
|
}
|
|
6865
|
-
if (!isText && ++opened > MAX_ATTACHED || bytes + content.length > MAX_ATTACHED_BYTES) {
|
|
6866
|
-
|
|
6867
|
-
|
|
6941
|
+
if (!kind.isText && ++opened > MAX_ATTACHED || bytes + attachment.content.length > MAX_ATTACHED_BYTES) {
|
|
6942
|
+
state.tooComplex = true;
|
|
6943
|
+
break;
|
|
6868
6944
|
}
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6945
|
+
bytes += attachment.content.length;
|
|
6946
|
+
read.push({ attachment, kind });
|
|
6947
|
+
}
|
|
6948
|
+
for (const { attachment, kind: { isHtml, isCard } } of read) {
|
|
6949
|
+
if (late()) {
|
|
6950
|
+
state.incomplete = true;
|
|
6951
|
+
return;
|
|
6872
6952
|
}
|
|
6873
|
-
|
|
6874
|
-
const text = decodeText(content, charsetOf(attachment));
|
|
6953
|
+
const text = decodeText(attachment.content, charsetOf(attachment));
|
|
6875
6954
|
if (isHtml) {
|
|
6876
|
-
|
|
6955
|
+
yield { html: text };
|
|
6877
6956
|
continue;
|
|
6878
6957
|
}
|
|
6879
6958
|
if (!isCard) {
|
|
6880
|
-
|
|
6959
|
+
yield { text };
|
|
6881
6960
|
continue;
|
|
6882
6961
|
}
|
|
6883
|
-
|
|
6962
|
+
let readable = withoutBinaryValues(text, MAX_CARD_TEXT);
|
|
6963
|
+
if (readable.length > MAX_CARD_TEXT) {
|
|
6964
|
+
state.tooComplex = true;
|
|
6965
|
+
readable = readable.slice(0, MAX_CARD_TEXT);
|
|
6966
|
+
}
|
|
6884
6967
|
const full = readable.length <= MAX_SHOWN_BYTES;
|
|
6885
6968
|
if (!full && hasQuotedPrintable(readable)) {
|
|
6886
|
-
|
|
6887
|
-
|
|
6969
|
+
state.tooComplex = true;
|
|
6970
|
+
}
|
|
6971
|
+
if (late()) {
|
|
6972
|
+
state.incomplete = true;
|
|
6973
|
+
return;
|
|
6974
|
+
}
|
|
6975
|
+
yield { text: shownText(readable, full) };
|
|
6976
|
+
if (late()) {
|
|
6977
|
+
state.incomplete = true;
|
|
6978
|
+
return;
|
|
6888
6979
|
}
|
|
6889
|
-
documents.push({ text: shownText(readable, full) });
|
|
6890
6980
|
const html2 = htmlDescriptions(readable);
|
|
6891
6981
|
if (html2 !== "") {
|
|
6892
|
-
|
|
6982
|
+
yield { html: html2 };
|
|
6893
6983
|
}
|
|
6894
6984
|
}
|
|
6895
|
-
return documents;
|
|
6896
6985
|
}
|
|
6897
6986
|
function carriedLinkHosts(mail, {
|
|
6898
6987
|
mails = [],
|
|
@@ -6906,40 +6995,45 @@ function carriedLinkHosts(mail, {
|
|
|
6906
6995
|
const seenBare = /* @__PURE__ */ new Set();
|
|
6907
6996
|
const seenLoose = /* @__PURE__ */ new Set();
|
|
6908
6997
|
let more = false;
|
|
6909
|
-
let
|
|
6910
|
-
const
|
|
6911
|
-
const
|
|
6998
|
+
let full = false;
|
|
6999
|
+
const domains = /* @__PURE__ */ new Set();
|
|
7000
|
+
const state = { tooComplex: false, incomplete: false };
|
|
7001
|
+
const until = Date.now() + MAX_READ_TIME;
|
|
7002
|
+
const late = () => {
|
|
7003
|
+
if (!state.incomplete && Date.now() > until) {
|
|
7004
|
+
state.incomplete = true;
|
|
7005
|
+
}
|
|
7006
|
+
return state.incomplete;
|
|
7007
|
+
};
|
|
7008
|
+
let calls = 0;
|
|
7009
|
+
const done = () => full || state.incomplete || ++calls % LATE_EVERY === 0 && late();
|
|
7010
|
+
const looseDone = () => done() || loose.size > maxHosts;
|
|
6912
7011
|
const addLoose = (host) => {
|
|
6913
7012
|
if (!looseDone()) {
|
|
6914
7013
|
loose.add(host);
|
|
6915
7014
|
}
|
|
6916
7015
|
};
|
|
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 }));
|
|
7016
|
+
const queue = [mail, ...mails].map((document) => ({ document, depth: 0 }));
|
|
7017
|
+
const carried = documentsOf(attachments, state, late);
|
|
6929
7018
|
let depth = 0;
|
|
6930
7019
|
let embedded = 0;
|
|
6931
7020
|
let embeddedLength = 0;
|
|
7021
|
+
const embeddedSeen = /* @__PURE__ */ new Set();
|
|
6932
7022
|
const embed = (html2) => {
|
|
7023
|
+
if (html2 === "" || embeddedSeen.has(html2)) {
|
|
7024
|
+
return;
|
|
7025
|
+
}
|
|
6933
7026
|
if (depth >= MAX_EMBED_DEPTH || embedded >= MAX_EMBEDDED || embeddedLength + html2.length > MAX_EMBEDDED_LENGTH) {
|
|
6934
|
-
tooComplex = true;
|
|
7027
|
+
state.tooComplex = true;
|
|
6935
7028
|
return;
|
|
6936
7029
|
}
|
|
7030
|
+
embeddedSeen.add(html2);
|
|
6937
7031
|
embedded++;
|
|
6938
7032
|
embeddedLength += html2.length;
|
|
6939
7033
|
queue.push({ document: { html: html2 }, depth: depth + 1 });
|
|
6940
7034
|
};
|
|
6941
7035
|
const add = (value) => {
|
|
6942
|
-
if (
|
|
7036
|
+
if (full || typeof value !== "string" || value === "" || seen.has(value)) {
|
|
6943
7037
|
return;
|
|
6944
7038
|
}
|
|
6945
7039
|
if (seen.size < MAX_SEEN) {
|
|
@@ -6954,34 +7048,64 @@ function carriedLinkHosts(mail, {
|
|
|
6954
7048
|
}
|
|
6955
7049
|
const found = hostsOf(value);
|
|
6956
7050
|
if (found.unresolved) {
|
|
6957
|
-
tooComplex = true;
|
|
7051
|
+
state.tooComplex = true;
|
|
6958
7052
|
}
|
|
6959
7053
|
for (const host of found) {
|
|
6960
7054
|
if (hosts.has(host)) {
|
|
6961
7055
|
continue;
|
|
6962
7056
|
}
|
|
6963
|
-
|
|
6964
|
-
|
|
7057
|
+
const domain = registrableDomain(host);
|
|
7058
|
+
if (hosts.size < maxHosts || !domains.has(domain) && domains.size < maxHosts) {
|
|
7059
|
+
hosts.add(host);
|
|
7060
|
+
domains.add(domain);
|
|
7061
|
+
continue;
|
|
7062
|
+
}
|
|
7063
|
+
more = true;
|
|
7064
|
+
if (domains.size >= maxHosts) {
|
|
7065
|
+
full = true;
|
|
6965
7066
|
return;
|
|
6966
7067
|
}
|
|
6967
|
-
hosts.add(host);
|
|
6968
7068
|
}
|
|
6969
7069
|
};
|
|
6970
|
-
|
|
7070
|
+
let spent = 0;
|
|
7071
|
+
const spend = (length) => {
|
|
7072
|
+
spent += length;
|
|
7073
|
+
if (spent < LONG_VALUE) {
|
|
7074
|
+
return false;
|
|
7075
|
+
}
|
|
7076
|
+
spent = 0;
|
|
7077
|
+
return late();
|
|
7078
|
+
};
|
|
7079
|
+
const reader = {
|
|
7080
|
+
add,
|
|
7081
|
+
embed,
|
|
7082
|
+
spend,
|
|
7083
|
+
cut() {
|
|
7084
|
+
state.tooComplex = true;
|
|
7085
|
+
}
|
|
7086
|
+
};
|
|
7087
|
+
const readText = (source, { cutStart = false, cutEnd = false, exact = false } = {}) => {
|
|
6971
7088
|
for (const match of source.matchAll(URL_START)) {
|
|
6972
|
-
if (
|
|
7089
|
+
if (done()) {
|
|
6973
7090
|
break;
|
|
6974
7091
|
}
|
|
6975
7092
|
if (cutStart && match.index === 0) {
|
|
6976
7093
|
continue;
|
|
6977
7094
|
}
|
|
6978
7095
|
if (cutEnd && match.index + match[0].length === source.length) {
|
|
7096
|
+
if (exact && source.length - match.index < CHUNK_OVERLAP) {
|
|
7097
|
+
continue;
|
|
7098
|
+
}
|
|
6979
7099
|
const value = match[0];
|
|
6980
7100
|
const at = /^www\./i.test(value) ? 0 : value.indexOf("//") + 2;
|
|
6981
|
-
if (
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
7101
|
+
if (!/[/?#\\]/.test(value.slice(at))) {
|
|
7102
|
+
state.tooComplex = true;
|
|
7103
|
+
continue;
|
|
7104
|
+
}
|
|
7105
|
+
add(value);
|
|
7106
|
+
const url = urlOf(value);
|
|
7107
|
+
if (url && isClickTracker(url)) {
|
|
7108
|
+
state.tooComplex = true;
|
|
6985
7109
|
}
|
|
6986
7110
|
continue;
|
|
6987
7111
|
}
|
|
@@ -7003,42 +7127,61 @@ function carriedLinkHosts(mail, {
|
|
|
7003
7127
|
});
|
|
7004
7128
|
}
|
|
7005
7129
|
};
|
|
7006
|
-
for (
|
|
7007
|
-
if (
|
|
7130
|
+
for (let index = 0; ; index++) {
|
|
7131
|
+
if (full || late()) {
|
|
7008
7132
|
break;
|
|
7009
7133
|
}
|
|
7010
|
-
(
|
|
7011
|
-
|
|
7134
|
+
if (index >= queue.length) {
|
|
7135
|
+
const next = carried.next();
|
|
7136
|
+
if (next.done) {
|
|
7137
|
+
break;
|
|
7138
|
+
}
|
|
7139
|
+
queue.push({ document: next.value, depth: 0 });
|
|
7140
|
+
}
|
|
7141
|
+
({ depth } = queue[index]);
|
|
7142
|
+
const { document } = queue[index];
|
|
7012
7143
|
const html2 = withoutNul(typeof document?.html === "string" ? document.html : "");
|
|
7013
7144
|
const text = withoutNul(typeof document?.text === "string" ? document.text : "");
|
|
7014
7145
|
for (const [name, value] of attributesOf(html2)) {
|
|
7015
|
-
if (
|
|
7146
|
+
if (done()) {
|
|
7016
7147
|
break;
|
|
7017
7148
|
}
|
|
7018
|
-
if (LINK_ATTRIBUTES.has(name)) {
|
|
7019
|
-
|
|
7149
|
+
if (!LINK_ATTRIBUTES.has(name)) {
|
|
7150
|
+
continue;
|
|
7151
|
+
}
|
|
7152
|
+
if (spend(value.length)) {
|
|
7153
|
+
break;
|
|
7020
7154
|
}
|
|
7155
|
+
linksOf(name, value, reader);
|
|
7021
7156
|
}
|
|
7022
|
-
lenientLinks(html2,
|
|
7157
|
+
lenientLinks(html2, done, reader);
|
|
7023
7158
|
for (const pattern of [CSS_URL, CSS_IMPORT]) {
|
|
7024
7159
|
for (const [, link] of html2.matchAll(pattern)) {
|
|
7025
|
-
if (
|
|
7160
|
+
if (done() || spend(link.length)) {
|
|
7026
7161
|
break;
|
|
7027
7162
|
}
|
|
7028
|
-
|
|
7163
|
+
const cut = isCut(link);
|
|
7164
|
+
if (cut) {
|
|
7165
|
+
state.tooComplex = true;
|
|
7166
|
+
}
|
|
7167
|
+
add(decodeEntities(cut ? link.slice(0, MAX_VALUE2) : link, { attribute: true }));
|
|
7029
7168
|
}
|
|
7030
7169
|
}
|
|
7031
|
-
|
|
7032
|
-
|
|
7170
|
+
for (const chunk2 of chunksOf(text)) {
|
|
7171
|
+
if (full || late()) {
|
|
7172
|
+
break;
|
|
7173
|
+
}
|
|
7174
|
+
readText(chunk2.text, { ...chunk2, exact: true });
|
|
7033
7175
|
}
|
|
7034
7176
|
for (const chunk2 of chunksOf(html2)) {
|
|
7035
|
-
if (
|
|
7177
|
+
if (full || late()) {
|
|
7036
7178
|
break;
|
|
7037
7179
|
}
|
|
7038
|
-
|
|
7180
|
+
const decoded = decodeEntities(chunk2.text);
|
|
7181
|
+
readText(decoded, { ...chunk2, exact: decoded.endsWith(chunk2.text.slice(-CHUNK_OVERLAP)) });
|
|
7039
7182
|
}
|
|
7040
7183
|
}
|
|
7041
|
-
if (!more && !incomplete) {
|
|
7184
|
+
if (!more && !state.incomplete) {
|
|
7042
7185
|
for (const name of loose) {
|
|
7043
7186
|
if (hosts.size >= maxHosts) {
|
|
7044
7187
|
break;
|
|
@@ -7052,11 +7195,11 @@ function carriedLinkHosts(mail, {
|
|
|
7052
7195
|
return {
|
|
7053
7196
|
hosts: [...hosts],
|
|
7054
7197
|
more,
|
|
7055
|
-
tooComplex,
|
|
7056
|
-
incomplete
|
|
7198
|
+
tooComplex: state.tooComplex,
|
|
7199
|
+
incomplete: state.incomplete
|
|
7057
7200
|
};
|
|
7058
7201
|
}
|
|
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;
|
|
7202
|
+
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_CARD_TEXT, MAX_VALUE2, MAX_REFERENCES, 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
7203
|
var init_links = __esm({
|
|
7061
7204
|
"src/links.js"() {
|
|
7062
7205
|
import_node_buffer7 = require("node:buffer");
|
|
@@ -7103,7 +7246,11 @@ var init_links = __esm({
|
|
|
7103
7246
|
MAX_ATTACHED = 100;
|
|
7104
7247
|
MAX_ATTACHED_BYTES = 50 * 1024 * 1024;
|
|
7105
7248
|
MAX_READ_TIME = 5e3;
|
|
7106
|
-
MAX_SHOWN_BYTES =
|
|
7249
|
+
MAX_SHOWN_BYTES = 1024 * 1024;
|
|
7250
|
+
MAX_CARD_TEXT = 8 * 1024 * 1024;
|
|
7251
|
+
MAX_VALUE2 = 1024 * 1024;
|
|
7252
|
+
MAX_REFERENCES = 64 * 1024;
|
|
7253
|
+
LONG_VALUE = 64 * 1024;
|
|
7107
7254
|
MAX_LENIENT_VALUE = 4096;
|
|
7108
7255
|
UNQUOTED_END = /* @__PURE__ */ new Set(["<", ">", '"', "'", "`", "="]);
|
|
7109
7256
|
UTF16LE = /* @__PURE__ */ new Set(["csunicode", "iso-10646-ucs-2", "ucs-2", "unicode", "unicodefeff", "utf-16", "utf-16le"]);
|
|
@@ -7120,6 +7267,7 @@ var init_links = __esm({
|
|
|
7120
7267
|
CARD_TYPES = /* @__PURE__ */ new Set(["text/calendar", "text/x-vcalendar", "application/ics", "text/vcard", "text/x-vcard", "text/directory"]);
|
|
7121
7268
|
CARD_EXTENSIONS = /* @__PURE__ */ new Set(["ics", "ical", "ifb", "vcs", "vcf", "vcard"]);
|
|
7122
7269
|
MESSAGE_TYPES = /* @__PURE__ */ new Set(["message/rfc822", "message/global"]);
|
|
7270
|
+
LATE_EVERY = 1024;
|
|
7123
7271
|
}
|
|
7124
7272
|
});
|
|
7125
7273
|
|
|
@@ -8784,7 +8932,7 @@ var init_package = __esm({
|
|
|
8784
8932
|
package_default = {
|
|
8785
8933
|
name: "spamscanner",
|
|
8786
8934
|
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.
|
|
8935
|
+
version: "7.0.13",
|
|
8788
8936
|
author: "Forward Email (https://forwardemail.net)",
|
|
8789
8937
|
bin: {
|
|
8790
8938
|
spamscanner: "./dist/esm/cli.js"
|
|
@@ -9050,9 +9198,12 @@ function rewriteMessage(source, headers = [], options = {}) {
|
|
|
9050
9198
|
}
|
|
9051
9199
|
}
|
|
9052
9200
|
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() : [];
|
|
9201
|
+
const mbox = kept.length > 0 && kept[0][0].startsWith("From ") ? kept.shift().slice(0, 1) : [];
|
|
9202
|
+
if (kept.length > 0 && /^[ \t]/.test(kept[0][0])) {
|
|
9203
|
+
kept.shift();
|
|
9204
|
+
}
|
|
9054
9205
|
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 === ""
|
|
9206
|
+
return import_node_buffer9.Buffer.concat([import_node_buffer9.Buffer.from(head === "" ? newline : `${head}${newline}${newline}`, "latin1"), body]);
|
|
9056
9207
|
}
|
|
9057
9208
|
var import_node_buffer9, ENCODED_BYTES, PLAIN_WORD, ENCODED_LINE, MAX_LINE2, ENCODED_WORD, ENCODED_START;
|
|
9058
9209
|
var init_headers = __esm({
|
|
@@ -9414,7 +9565,12 @@ var init_train = __esm({
|
|
|
9414
9565
|
|
|
9415
9566
|
// src/server.js
|
|
9416
9567
|
function serializeResult(result, options = {}) {
|
|
9417
|
-
const {
|
|
9568
|
+
const {
|
|
9569
|
+
mail,
|
|
9570
|
+
tokens,
|
|
9571
|
+
attached,
|
|
9572
|
+
...rest
|
|
9573
|
+
} = result;
|
|
9418
9574
|
const output = {
|
|
9419
9575
|
...rest,
|
|
9420
9576
|
results: { ...result.results }
|
|
@@ -10099,7 +10255,7 @@ function report(result) {
|
|
|
10099
10255
|
"",
|
|
10100
10256
|
" pts rule name description",
|
|
10101
10257
|
"---- ---------------------- --------------------------------------------------",
|
|
10102
|
-
...result.tests.map((test) => `${test.score.toFixed(1).padStart(4)} ${test.name.padEnd(22)} ${test.description}`)
|
|
10258
|
+
...result.tests.map((test) => `${test.score.toFixed(1).padStart(4)} ${oneLine2(test.name).padEnd(22)} ${oneLine2(test.description)}`)
|
|
10103
10259
|
];
|
|
10104
10260
|
return `${lines2.join("\r\n")}\r
|
|
10105
10261
|
`;
|
|
@@ -10245,7 +10401,7 @@ function createSpamdServer(scanner, options = {}) {
|
|
|
10245
10401
|
server.maxConnections = settings.maxConnections;
|
|
10246
10402
|
return server;
|
|
10247
10403
|
}
|
|
10248
|
-
var import_node_buffer13, import_node_net7, EX_OK, EX_USAGE, EX_DATAERR, EX_SOFTWARE, EX_PROTOCOL, EX_NOPERM, COMMANDS2;
|
|
10404
|
+
var import_node_buffer13, import_node_net7, EX_OK, EX_USAGE, EX_DATAERR, EX_SOFTWARE, EX_PROTOCOL, EX_NOPERM, COMMANDS2, oneLine2;
|
|
10249
10405
|
var init_spamd = __esm({
|
|
10250
10406
|
"src/spamd.js"() {
|
|
10251
10407
|
import_node_buffer13 = require("node:buffer");
|
|
@@ -10260,6 +10416,7 @@ var init_spamd = __esm({
|
|
|
10260
10416
|
EX_PROTOCOL = 76;
|
|
10261
10417
|
EX_NOPERM = 77;
|
|
10262
10418
|
COMMANDS2 = /* @__PURE__ */ new Set(["CHECK", "SYMBOLS", "REPORT", "REPORT_IFSPAM", "PROCESS", "HEADERS", "PING", "SKIP", "TELL"]);
|
|
10419
|
+
oneLine2 = (text) => String(text ?? "").replaceAll(/[\p{Cc}\u061C\u200E\u200F\u2028\u2029\u202A-\u202E\u2066-\u2069]+/gu, " ");
|
|
10263
10420
|
}
|
|
10264
10421
|
});
|
|
10265
10422
|
|
|
@@ -10824,7 +10981,7 @@ var init_src = __esm({
|
|
|
10824
10981
|
// (`from`: the sender's domain; `carried`: the link hosts of the whole
|
|
10825
10982
|
// message and of what it carries, see carriedLinkHosts)
|
|
10826
10983
|
async checkLinks(links, config, hosts = [], options = {}) {
|
|
10827
|
-
const { from = "", carried = null } = typeof options === "string" ? { from: options } : options;
|
|
10984
|
+
const { from = "", carried = null } = typeof options === "string" ? { from: options } : options ?? {};
|
|
10828
10985
|
const phishing = [];
|
|
10829
10986
|
const homographs = [];
|
|
10830
10987
|
const seenHosts = /* @__PURE__ */ new Set();
|
|
@@ -10884,15 +11041,15 @@ var init_src = __esm({
|
|
|
10884
11041
|
for (const host of carriedHosts) {
|
|
10885
11042
|
seenHosts.add(host);
|
|
10886
11043
|
}
|
|
10887
|
-
const
|
|
11044
|
+
const own3 = [...found.filter((link) => link.text), ...found.filter((link) => !link.text), ...bare].map((link) => lookupName(destination(link))).filter(Boolean);
|
|
10888
11045
|
const firsts = /* @__PURE__ */ new Map();
|
|
10889
|
-
for (const host of
|
|
11046
|
+
for (const host of [...own3, ...carriedHosts]) {
|
|
10890
11047
|
const domain = registrableDomain(host);
|
|
10891
11048
|
if (!firsts.has(domain)) {
|
|
10892
11049
|
firsts.set(domain, host);
|
|
10893
11050
|
}
|
|
10894
11051
|
}
|
|
10895
|
-
const checked = [.../* @__PURE__ */ new Set([...firsts.values(), ...
|
|
11052
|
+
const checked = [.../* @__PURE__ */ new Set([...firsts.values(), ...own3, ...carriedHosts])].slice(0, config.phishing.maxHosts);
|
|
10896
11053
|
const domains = [...new Set(checked.map((host) => registrableDomain(host)))];
|
|
10897
11054
|
const blocked = [];
|
|
10898
11055
|
let answered = 0;
|