spamscanner 7.0.10 → 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.
@@ -1624,6 +1624,286 @@ var init_homograph = __esm({
1624
1624
  }
1625
1625
  });
1626
1626
 
1627
+ // src/entities.js
1628
+ var NAMED, NAMED_REFERENCES;
1629
+ var init_entities = __esm({
1630
+ "src/entities.js"() {
1631
+ NAMED = [
1632
+ "AElig=c6,AMP=26,Aacute=c1,Abreve=102,Acirc=c2,Acy=410,Afr=1d504,Agrave=c0,Alpha=391,Amacr=100,And=2a",
1633
+ "53,Aogon=104,Aopf=1d538,ApplyFunction=2061,Aring=c5,Ascr=1d49c,Assign=2254,Atilde=c3,Auml=c4,Backsla",
1634
+ "sh=2216,Barv=2ae7,Barwed=2306,Bcy=411,Because=2235,Bernoullis=212c,Beta=392,Bfr=1d505,Bopf=1d539,Bre",
1635
+ "ve=2d8,Bscr=212c,Bumpeq=224e,CHcy=427,COPY=a9,Cacute=106,Cap=22d2,CapitalDifferentialD=2145,Cayleys=",
1636
+ "212d,Ccaron=10c,Ccedil=c7,Ccirc=108,Cconint=2230,Cdot=10a,Cedilla=b8,CenterDot=b7,Cfr=212d,Chi=3a7,C",
1637
+ "ircleDot=2299,CircleMinus=2296,CirclePlus=2295,CircleTimes=2297,ClockwiseContourIntegral=2232,CloseC",
1638
+ "urlyDoubleQuote=201d,CloseCurlyQuote=2019,Colon=2237,Colone=2a74,Congruent=2261,Conint=222f,ContourI",
1639
+ "ntegral=222e,Copf=2102,Coproduct=2210,CounterClockwiseContourIntegral=2233,Cross=2a2f,Cscr=1d49e,Cup",
1640
+ "=22d3,CupCap=224d,DD=2145,DDotrahd=2911,DJcy=402,DScy=405,DZcy=40f,Dagger=2021,Darr=21a1,Dashv=2ae4,",
1641
+ "Dcaron=10e,Dcy=414,Del=2207,Delta=394,Dfr=1d507,DiacriticalAcute=b4,DiacriticalDot=2d9,DiacriticalDo",
1642
+ "ubleAcute=2dd,DiacriticalGrave=60,DiacriticalTilde=2dc,Diamond=22c4,DifferentialD=2146,Dopf=1d53b,Do",
1643
+ "t=a8,DotDot=20dc,DotEqual=2250,DoubleContourIntegral=222f,DoubleDot=a8,DoubleDownArrow=21d3,DoubleLe",
1644
+ "ftArrow=21d0,DoubleLeftRightArrow=21d4,DoubleLeftTee=2ae4,DoubleLongLeftArrow=27f8,DoubleLongLeftRig",
1645
+ "htArrow=27fa,DoubleLongRightArrow=27f9,DoubleRightArrow=21d2,DoubleRightTee=22a8,DoubleUpArrow=21d1,",
1646
+ "DoubleUpDownArrow=21d5,DoubleVerticalBar=2225,DownArrow=2193,DownArrowBar=2913,DownArrowUpArrow=21f5",
1647
+ ",DownBreve=311,DownLeftRightVector=2950,DownLeftTeeVector=295e,DownLeftVector=21bd,DownLeftVectorBar",
1648
+ "=2956,DownRightTeeVector=295f,DownRightVector=21c1,DownRightVectorBar=2957,DownTee=22a4,DownTeeArrow",
1649
+ "=21a7,Downarrow=21d3,Dscr=1d49f,Dstrok=110,ENG=14a,ETH=d0,Eacute=c9,Ecaron=11a,Ecirc=ca,Ecy=42d,Edot",
1650
+ "=116,Efr=1d508,Egrave=c8,Element=2208,Emacr=112,EmptySmallSquare=25fb,EmptyVerySmallSquare=25ab,Eogo",
1651
+ "n=118,Eopf=1d53c,Epsilon=395,Equal=2a75,EqualTilde=2242,Equilibrium=21cc,Escr=2130,Esim=2a73,Eta=397",
1652
+ ",Euml=cb,Exists=2203,ExponentialE=2147,Fcy=424,Ffr=1d509,FilledSmallSquare=25fc,FilledVerySmallSquar",
1653
+ "e=25aa,Fopf=1d53d,ForAll=2200,Fouriertrf=2131,Fscr=2131,GJcy=403,GT=3e,Gamma=393,Gammad=3dc,Gbreve=1",
1654
+ "1e,Gcedil=122,Gcirc=11c,Gcy=413,Gdot=120,Gfr=1d50a,Gg=22d9,Gopf=1d53e,GreaterEqual=2265,GreaterEqual",
1655
+ "Less=22db,GreaterFullEqual=2267,GreaterGreater=2aa2,GreaterLess=2277,GreaterSlantEqual=2a7e,GreaterT",
1656
+ "ilde=2273,Gscr=1d4a2,Gt=226b,HARDcy=42a,Hacek=2c7,Hat=5e,Hcirc=124,Hfr=210c,HilbertSpace=210b,Hopf=2",
1657
+ "10d,HorizontalLine=2500,Hscr=210b,Hstrok=126,HumpDownHump=224e,HumpEqual=224f,IEcy=415,IJlig=132,IOc",
1658
+ "y=401,Iacute=cd,Icirc=ce,Icy=418,Idot=130,Ifr=2111,Igrave=cc,Im=2111,Imacr=12a,ImaginaryI=2148,Impli",
1659
+ "es=21d2,Int=222c,Integral=222b,Intersection=22c2,InvisibleComma=2063,InvisibleTimes=2062,Iogon=12e,I",
1660
+ "opf=1d540,Iota=399,Iscr=2110,Itilde=128,Iukcy=406,Iuml=cf,Jcirc=134,Jcy=419,Jfr=1d50d,Jopf=1d541,Jsc",
1661
+ "r=1d4a5,Jsercy=408,Jukcy=404,KHcy=425,KJcy=40c,Kappa=39a,Kcedil=136,Kcy=41a,Kfr=1d50e,Kopf=1d542,Ksc",
1662
+ "r=1d4a6,LJcy=409,LT=3c,Lacute=139,Lambda=39b,Lang=27ea,Laplacetrf=2112,Larr=219e,Lcaron=13d,Lcedil=1",
1663
+ "3b,Lcy=41b,LeftAngleBracket=27e8,LeftArrow=2190,LeftArrowBar=21e4,LeftArrowRightArrow=21c6,LeftCeili",
1664
+ "ng=2308,LeftDoubleBracket=27e6,LeftDownTeeVector=2961,LeftDownVector=21c3,LeftDownVectorBar=2959,Lef",
1665
+ "tFloor=230a,LeftRightArrow=2194,LeftRightVector=294e,LeftTee=22a3,LeftTeeArrow=21a4,LeftTeeVector=29",
1666
+ "5a,LeftTriangle=22b2,LeftTriangleBar=29cf,LeftTriangleEqual=22b4,LeftUpDownVector=2951,LeftUpTeeVect",
1667
+ "or=2960,LeftUpVector=21bf,LeftUpVectorBar=2958,LeftVector=21bc,LeftVectorBar=2952,Leftarrow=21d0,Lef",
1668
+ "trightarrow=21d4,LessEqualGreater=22da,LessFullEqual=2266,LessGreater=2276,LessLess=2aa1,LessSlantEq",
1669
+ "ual=2a7d,LessTilde=2272,Lfr=1d50f,Ll=22d8,Lleftarrow=21da,Lmidot=13f,LongLeftArrow=27f5,LongLeftRigh",
1670
+ "tArrow=27f7,LongRightArrow=27f6,Longleftarrow=27f8,Longleftrightarrow=27fa,Longrightarrow=27f9,Lopf=",
1671
+ "1d543,LowerLeftArrow=2199,LowerRightArrow=2198,Lscr=2112,Lsh=21b0,Lstrok=141,Lt=226a,Map=2905,Mcy=41",
1672
+ "c,MediumSpace=205f,Mellintrf=2133,Mfr=1d510,MinusPlus=2213,Mopf=1d544,Mscr=2133,Mu=39c,NJcy=40a,Nacu",
1673
+ "te=143,Ncaron=147,Ncedil=145,Ncy=41d,NegativeMediumSpace=200b,NegativeThickSpace=200b,NegativeThinSp",
1674
+ "ace=200b,NegativeVeryThinSpace=200b,NestedGreaterGreater=226b,NestedLessLess=226a,NewLine=a,Nfr=1d51",
1675
+ "1,NoBreak=2060,NonBreakingSpace=a0,Nopf=2115,Not=2aec,NotCongruent=2262,NotCupCap=226d,NotDoubleVert",
1676
+ "icalBar=2226,NotElement=2209,NotEqual=2260,NotEqualTilde=2242.338,NotExists=2204,NotGreater=226f,Not",
1677
+ "GreaterEqual=2271,NotGreaterFullEqual=2267.338,NotGreaterGreater=226b.338,NotGreaterLess=2279,NotGre",
1678
+ "aterSlantEqual=2a7e.338,NotGreaterTilde=2275,NotHumpDownHump=224e.338,NotHumpEqual=224f.338,NotLeftT",
1679
+ "riangle=22ea,NotLeftTriangleBar=29cf.338,NotLeftTriangleEqual=22ec,NotLess=226e,NotLessEqual=2270,No",
1680
+ "tLessGreater=2278,NotLessLess=226a.338,NotLessSlantEqual=2a7d.338,NotLessTilde=2274,NotNestedGreater",
1681
+ "Greater=2aa2.338,NotNestedLessLess=2aa1.338,NotPrecedes=2280,NotPrecedesEqual=2aaf.338,NotPrecedesSl",
1682
+ "antEqual=22e0,NotReverseElement=220c,NotRightTriangle=22eb,NotRightTriangleBar=29d0.338,NotRightTria",
1683
+ "ngleEqual=22ed,NotSquareSubset=228f.338,NotSquareSubsetEqual=22e2,NotSquareSuperset=2290.338,NotSqua",
1684
+ "reSupersetEqual=22e3,NotSubset=2282.20d2,NotSubsetEqual=2288,NotSucceeds=2281,NotSucceedsEqual=2ab0.",
1685
+ "338,NotSucceedsSlantEqual=22e1,NotSucceedsTilde=227f.338,NotSuperset=2283.20d2,NotSupersetEqual=2289",
1686
+ ",NotTilde=2241,NotTildeEqual=2244,NotTildeFullEqual=2247,NotTildeTilde=2249,NotVerticalBar=2224,Nscr",
1687
+ "=1d4a9,Ntilde=d1,Nu=39d,OElig=152,Oacute=d3,Ocirc=d4,Ocy=41e,Odblac=150,Ofr=1d512,Ograve=d2,Omacr=14",
1688
+ "c,Omega=3a9,Omicron=39f,Oopf=1d546,OpenCurlyDoubleQuote=201c,OpenCurlyQuote=2018,Or=2a54,Oscr=1d4aa,",
1689
+ "Oslash=d8,Otilde=d5,Otimes=2a37,Ouml=d6,OverBar=203e,OverBrace=23de,OverBracket=23b4,OverParenthesis",
1690
+ "=23dc,PartialD=2202,Pcy=41f,Pfr=1d513,Phi=3a6,Pi=3a0,PlusMinus=b1,Poincareplane=210c,Popf=2119,Pr=2a",
1691
+ "bb,Precedes=227a,PrecedesEqual=2aaf,PrecedesSlantEqual=227c,PrecedesTilde=227e,Prime=2033,Product=22",
1692
+ "0f,Proportion=2237,Proportional=221d,Pscr=1d4ab,Psi=3a8,QUOT=22,Qfr=1d514,Qopf=211a,Qscr=1d4ac,RBarr",
1693
+ "=2910,REG=ae,Racute=154,Rang=27eb,Rarr=21a0,Rarrtl=2916,Rcaron=158,Rcedil=156,Rcy=420,Re=211c,Revers",
1694
+ "eElement=220b,ReverseEquilibrium=21cb,ReverseUpEquilibrium=296f,Rfr=211c,Rho=3a1,RightAngleBracket=2",
1695
+ "7e9,RightArrow=2192,RightArrowBar=21e5,RightArrowLeftArrow=21c4,RightCeiling=2309,RightDoubleBracket",
1696
+ "=27e7,RightDownTeeVector=295d,RightDownVector=21c2,RightDownVectorBar=2955,RightFloor=230b,RightTee=",
1697
+ "22a2,RightTeeArrow=21a6,RightTeeVector=295b,RightTriangle=22b3,RightTriangleBar=29d0,RightTriangleEq",
1698
+ "ual=22b5,RightUpDownVector=294f,RightUpTeeVector=295c,RightUpVector=21be,RightUpVectorBar=2954,Right",
1699
+ "Vector=21c0,RightVectorBar=2953,Rightarrow=21d2,Ropf=211d,RoundImplies=2970,Rrightarrow=21db,Rscr=21",
1700
+ "1b,Rsh=21b1,RuleDelayed=29f4,SHCHcy=429,SHcy=428,SOFTcy=42c,Sacute=15a,Sc=2abc,Scaron=160,Scedil=15e",
1701
+ ",Scirc=15c,Scy=421,Sfr=1d516,ShortDownArrow=2193,ShortLeftArrow=2190,ShortRightArrow=2192,ShortUpArr",
1702
+ "ow=2191,Sigma=3a3,SmallCircle=2218,Sopf=1d54a,Sqrt=221a,Square=25a1,SquareIntersection=2293,SquareSu",
1703
+ "bset=228f,SquareSubsetEqual=2291,SquareSuperset=2290,SquareSupersetEqual=2292,SquareUnion=2294,Sscr=",
1704
+ "1d4ae,Star=22c6,Sub=22d0,Subset=22d0,SubsetEqual=2286,Succeeds=227b,SucceedsEqual=2ab0,SucceedsSlant",
1705
+ "Equal=227d,SucceedsTilde=227f,SuchThat=220b,Sum=2211,Sup=22d1,Superset=2283,SupersetEqual=2287,Supse",
1706
+ "t=22d1,THORN=de,TRADE=2122,TSHcy=40b,TScy=426,Tab=9,Tau=3a4,Tcaron=164,Tcedil=162,Tcy=422,Tfr=1d517,",
1707
+ "Therefore=2234,Theta=398,ThickSpace=205f.200a,ThinSpace=2009,Tilde=223c,TildeEqual=2243,TildeFullEqu",
1708
+ "al=2245,TildeTilde=2248,Topf=1d54b,TripleDot=20db,Tscr=1d4af,Tstrok=166,Uacute=da,Uarr=219f,Uarrocir",
1709
+ "=2949,Ubrcy=40e,Ubreve=16c,Ucirc=db,Ucy=423,Udblac=170,Ufr=1d518,Ugrave=d9,Umacr=16a,UnderBar=5f,Und",
1710
+ "erBrace=23df,UnderBracket=23b5,UnderParenthesis=23dd,Union=22c3,UnionPlus=228e,Uogon=172,Uopf=1d54c,",
1711
+ "UpArrow=2191,UpArrowBar=2912,UpArrowDownArrow=21c5,UpDownArrow=2195,UpEquilibrium=296e,UpTee=22a5,Up",
1712
+ "TeeArrow=21a5,Uparrow=21d1,Updownarrow=21d5,UpperLeftArrow=2196,UpperRightArrow=2197,Upsi=3d2,Upsilo",
1713
+ "n=3a5,Uring=16e,Uscr=1d4b0,Utilde=168,Uuml=dc,VDash=22ab,Vbar=2aeb,Vcy=412,Vdash=22a9,Vdashl=2ae6,Ve",
1714
+ "e=22c1,Verbar=2016,Vert=2016,VerticalBar=2223,VerticalLine=7c,VerticalSeparator=2758,VerticalTilde=2",
1715
+ "240,VeryThinSpace=200a,Vfr=1d519,Vopf=1d54d,Vscr=1d4b1,Vvdash=22aa,Wcirc=174,Wedge=22c0,Wfr=1d51a,Wo",
1716
+ "pf=1d54e,Wscr=1d4b2,Xfr=1d51b,Xi=39e,Xopf=1d54f,Xscr=1d4b3,YAcy=42f,YIcy=407,YUcy=42e,Yacute=dd,Ycir",
1717
+ "c=176,Ycy=42b,Yfr=1d51c,Yopf=1d550,Yscr=1d4b4,Yuml=178,ZHcy=416,Zacute=179,Zcaron=17d,Zcy=417,Zdot=1",
1718
+ "7b,ZeroWidthSpace=200b,Zeta=396,Zfr=2128,Zopf=2124,Zscr=1d4b5,aacute=e1,abreve=103,ac=223e,acE=223e.",
1719
+ "333,acd=223f,acirc=e2,acute=b4,acy=430,aelig=e6,af=2061,afr=1d51e,agrave=e0,alefsym=2135,aleph=2135,",
1720
+ "alpha=3b1,amacr=101,amalg=2a3f,amp=26,and=2227,andand=2a55,andd=2a5c,andslope=2a58,andv=2a5a,ang=222",
1721
+ "0,ange=29a4,angle=2220,angmsd=2221,angmsdaa=29a8,angmsdab=29a9,angmsdac=29aa,angmsdad=29ab,angmsdae=",
1722
+ "29ac,angmsdaf=29ad,angmsdag=29ae,angmsdah=29af,angrt=221f,angrtvb=22be,angrtvbd=299d,angsph=2222,ang",
1723
+ "st=c5,angzarr=237c,aogon=105,aopf=1d552,ap=2248,apE=2a70,apacir=2a6f,ape=224a,apid=224b,apos=27,appr",
1724
+ "ox=2248,approxeq=224a,aring=e5,ascr=1d4b6,ast=2a,asymp=2248,asympeq=224d,atilde=e3,auml=e4,awconint=",
1725
+ "2233,awint=2a11,bNot=2aed,backcong=224c,backepsilon=3f6,backprime=2035,backsim=223d,backsimeq=22cd,b",
1726
+ "arvee=22bd,barwed=2305,barwedge=2305,bbrk=23b5,bbrktbrk=23b6,bcong=224c,bcy=431,bdquo=201e,becaus=22",
1727
+ "35,because=2235,bemptyv=29b0,bepsi=3f6,bernou=212c,beta=3b2,beth=2136,between=226c,bfr=1d51f,bigcap=",
1728
+ "22c2,bigcirc=25ef,bigcup=22c3,bigodot=2a00,bigoplus=2a01,bigotimes=2a02,bigsqcup=2a06,bigstar=2605,b",
1729
+ "igtriangledown=25bd,bigtriangleup=25b3,biguplus=2a04,bigvee=22c1,bigwedge=22c0,bkarow=290d,blackloze",
1730
+ "nge=29eb,blacksquare=25aa,blacktriangle=25b4,blacktriangledown=25be,blacktriangleleft=25c2,blacktria",
1731
+ "ngleright=25b8,blank=2423,blk12=2592,blk14=2591,blk34=2593,block=2588,bne=3d.20e5,bnequiv=2261.20e5,",
1732
+ "bnot=2310,bopf=1d553,bot=22a5,bottom=22a5,bowtie=22c8,boxDL=2557,boxDR=2554,boxDl=2556,boxDr=2553,bo",
1733
+ "xH=2550,boxHD=2566,boxHU=2569,boxHd=2564,boxHu=2567,boxUL=255d,boxUR=255a,boxUl=255c,boxUr=2559,boxV",
1734
+ "=2551,boxVH=256c,boxVL=2563,boxVR=2560,boxVh=256b,boxVl=2562,boxVr=255f,boxbox=29c9,boxdL=2555,boxdR",
1735
+ "=2552,boxdl=2510,boxdr=250c,boxh=2500,boxhD=2565,boxhU=2568,boxhd=252c,boxhu=2534,boxminus=229f,boxp",
1736
+ "lus=229e,boxtimes=22a0,boxuL=255b,boxuR=2558,boxul=2518,boxur=2514,boxv=2502,boxvH=256a,boxvL=2561,b",
1737
+ "oxvR=255e,boxvh=253c,boxvl=2524,boxvr=251c,bprime=2035,breve=2d8,brvbar=a6,bscr=1d4b7,bsemi=204f,bsi",
1738
+ "m=223d,bsime=22cd,bsol=5c,bsolb=29c5,bsolhsub=27c8,bull=2022,bullet=2022,bump=224e,bumpE=2aae,bumpe=",
1739
+ "224f,bumpeq=224f,cacute=107,cap=2229,capand=2a44,capbrcup=2a49,capcap=2a4b,capcup=2a47,capdot=2a40,c",
1740
+ "aps=2229.fe00,caret=2041,caron=2c7,ccaps=2a4d,ccaron=10d,ccedil=e7,ccirc=109,ccups=2a4c,ccupssm=2a50",
1741
+ ",cdot=10b,cedil=b8,cemptyv=29b2,cent=a2,centerdot=b7,cfr=1d520,chcy=447,check=2713,checkmark=2713,ch",
1742
+ "i=3c7,cir=25cb,cirE=29c3,circ=2c6,circeq=2257,circlearrowleft=21ba,circlearrowright=21bb,circledR=ae",
1743
+ ",circledS=24c8,circledast=229b,circledcirc=229a,circleddash=229d,cire=2257,cirfnint=2a10,cirmid=2aef",
1744
+ ",cirscir=29c2,clubs=2663,clubsuit=2663,colon=3a,colone=2254,coloneq=2254,comma=2c,commat=40,comp=220",
1745
+ "1,compfn=2218,complement=2201,complexes=2102,cong=2245,congdot=2a6d,conint=222e,copf=1d554,coprod=22",
1746
+ "10,copy=a9,copysr=2117,crarr=21b5,cross=2717,cscr=1d4b8,csub=2acf,csube=2ad1,csup=2ad0,csupe=2ad2,ct",
1747
+ "dot=22ef,cudarrl=2938,cudarrr=2935,cuepr=22de,cuesc=22df,cularr=21b6,cularrp=293d,cup=222a,cupbrcap=",
1748
+ "2a48,cupcap=2a46,cupcup=2a4a,cupdot=228d,cupor=2a45,cups=222a.fe00,curarr=21b7,curarrm=293c,curlyeqp",
1749
+ "rec=22de,curlyeqsucc=22df,curlyvee=22ce,curlywedge=22cf,curren=a4,curvearrowleft=21b6,curvearrowrigh",
1750
+ "t=21b7,cuvee=22ce,cuwed=22cf,cwconint=2232,cwint=2231,cylcty=232d,dArr=21d3,dHar=2965,dagger=2020,da",
1751
+ "leth=2138,darr=2193,dash=2010,dashv=22a3,dbkarow=290f,dblac=2dd,dcaron=10f,dcy=434,dd=2146,ddagger=2",
1752
+ "021,ddarr=21ca,ddotseq=2a77,deg=b0,delta=3b4,demptyv=29b1,dfisht=297f,dfr=1d521,dharl=21c3,dharr=21c",
1753
+ "2,diam=22c4,diamond=22c4,diamondsuit=2666,diams=2666,die=a8,digamma=3dd,disin=22f2,div=f7,divide=f7,",
1754
+ "divideontimes=22c7,divonx=22c7,djcy=452,dlcorn=231e,dlcrop=230d,dollar=24,dopf=1d555,dot=2d9,doteq=2",
1755
+ "250,doteqdot=2251,dotminus=2238,dotplus=2214,dotsquare=22a1,doublebarwedge=2306,downarrow=2193,downd",
1756
+ "ownarrows=21ca,downharpoonleft=21c3,downharpoonright=21c2,drbkarow=2910,drcorn=231f,drcrop=230c,dscr",
1757
+ "=1d4b9,dscy=455,dsol=29f6,dstrok=111,dtdot=22f1,dtri=25bf,dtrif=25be,duarr=21f5,duhar=296f,dwangle=2",
1758
+ "9a6,dzcy=45f,dzigrarr=27ff,eDDot=2a77,eDot=2251,eacute=e9,easter=2a6e,ecaron=11b,ecir=2256,ecirc=ea,",
1759
+ "ecolon=2255,ecy=44d,edot=117,ee=2147,efDot=2252,efr=1d522,eg=2a9a,egrave=e8,egs=2a96,egsdot=2a98,el=",
1760
+ "2a99,elinters=23e7,ell=2113,els=2a95,elsdot=2a97,emacr=113,empty=2205,emptyset=2205,emptyv=2205,emsp",
1761
+ "=2003,emsp13=2004,emsp14=2005,eng=14b,ensp=2002,eogon=119,eopf=1d556,epar=22d5,eparsl=29e3,eplus=2a7",
1762
+ "1,epsi=3b5,epsilon=3b5,epsiv=3f5,eqcirc=2256,eqcolon=2255,eqsim=2242,eqslantgtr=2a96,eqslantless=2a9",
1763
+ "5,equals=3d,equest=225f,equiv=2261,equivDD=2a78,eqvparsl=29e5,erDot=2253,erarr=2971,escr=212f,esdot=",
1764
+ "2250,esim=2242,eta=3b7,eth=f0,euml=eb,euro=20ac,excl=21,exist=2203,expectation=2130,exponentiale=214",
1765
+ "7,fallingdotseq=2252,fcy=444,female=2640,ffilig=fb03,fflig=fb00,ffllig=fb04,ffr=1d523,filig=fb01,fjl",
1766
+ "ig=66.6a,flat=266d,fllig=fb02,fltns=25b1,fnof=192,fopf=1d557,forall=2200,fork=22d4,forkv=2ad9,fparti",
1767
+ "nt=2a0d,frac12=bd,frac13=2153,frac14=bc,frac15=2155,frac16=2159,frac18=215b,frac23=2154,frac25=2156,",
1768
+ "frac34=be,frac35=2157,frac38=215c,frac45=2158,frac56=215a,frac58=215d,frac78=215e,frasl=2044,frown=2",
1769
+ "322,fscr=1d4bb,gE=2267,gEl=2a8c,gacute=1f5,gamma=3b3,gammad=3dd,gap=2a86,gbreve=11f,gcirc=11d,gcy=43",
1770
+ "3,gdot=121,ge=2265,gel=22db,geq=2265,geqq=2267,geqslant=2a7e,ges=2a7e,gescc=2aa9,gesdot=2a80,gesdoto",
1771
+ "=2a82,gesdotol=2a84,gesl=22db.fe00,gesles=2a94,gfr=1d524,gg=226b,ggg=22d9,gimel=2137,gjcy=453,gl=227",
1772
+ "7,glE=2a92,gla=2aa5,glj=2aa4,gnE=2269,gnap=2a8a,gnapprox=2a8a,gne=2a88,gneq=2a88,gneqq=2269,gnsim=22",
1773
+ "e7,gopf=1d558,grave=60,gscr=210a,gsim=2273,gsime=2a8e,gsiml=2a90,gt=3e,gtcc=2aa7,gtcir=2a7a,gtdot=22",
1774
+ "d7,gtlPar=2995,gtquest=2a7c,gtrapprox=2a86,gtrarr=2978,gtrdot=22d7,gtreqless=22db,gtreqqless=2a8c,gt",
1775
+ "rless=2277,gtrsim=2273,gvertneqq=2269.fe00,gvnE=2269.fe00,hArr=21d4,hairsp=200a,half=bd,hamilt=210b,",
1776
+ "hardcy=44a,harr=2194,harrcir=2948,harrw=21ad,hbar=210f,hcirc=125,hearts=2665,heartsuit=2665,hellip=2",
1777
+ "026,hercon=22b9,hfr=1d525,hksearow=2925,hkswarow=2926,hoarr=21ff,homtht=223b,hookleftarrow=21a9,hook",
1778
+ "rightarrow=21aa,hopf=1d559,horbar=2015,hscr=1d4bd,hslash=210f,hstrok=127,hybull=2043,hyphen=2010,iac",
1779
+ "ute=ed,ic=2063,icirc=ee,icy=438,iecy=435,iexcl=a1,iff=21d4,ifr=1d526,igrave=ec,ii=2148,iiiint=2a0c,i",
1780
+ "iint=222d,iinfin=29dc,iiota=2129,ijlig=133,imacr=12b,image=2111,imagline=2110,imagpart=2111,imath=13",
1781
+ "1,imof=22b7,imped=1b5,in=2208,incare=2105,infin=221e,infintie=29dd,inodot=131,int=222b,intcal=22ba,i",
1782
+ "ntegers=2124,intercal=22ba,intlarhk=2a17,intprod=2a3c,iocy=451,iogon=12f,iopf=1d55a,iota=3b9,iprod=2",
1783
+ "a3c,iquest=bf,iscr=1d4be,isin=2208,isinE=22f9,isindot=22f5,isins=22f4,isinsv=22f3,isinv=2208,it=2062",
1784
+ ",itilde=129,iukcy=456,iuml=ef,jcirc=135,jcy=439,jfr=1d527,jmath=237,jopf=1d55b,jscr=1d4bf,jsercy=458",
1785
+ ",jukcy=454,kappa=3ba,kappav=3f0,kcedil=137,kcy=43a,kfr=1d528,kgreen=138,khcy=445,kjcy=45c,kopf=1d55c",
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=27e8,langd=2991,langle=27e8,lap=2a85,laquo=ab,larr=2190,larrb=21",
1788
+ "e4,larrbfs=291f,larrfs=291d,larrhk=21a9,larrlp=21ab,larrpl=2939,larrsim=2973,larrtl=21a2,lat=2aab,la",
1789
+ "tail=2919,late=2aad,lates=2aad.fe00,lbarr=290c,lbbrk=2772,lbrace=7b,lbrack=5b,lbrke=298b,lbrksld=298",
1790
+ "f,lbrkslu=298d,lcaron=13e,lcedil=13c,lceil=2308,lcub=7b,lcy=43b,ldca=2936,ldquo=201c,ldquor=201e,ldr",
1791
+ "dhar=2967,ldrushar=294b,ldsh=21b2,le=2264,leftarrow=2190,leftarrowtail=21a2,leftharpoondown=21bd,lef",
1792
+ "tharpoonup=21bc,leftleftarrows=21c7,leftrightarrow=2194,leftrightarrows=21c6,leftrightharpoons=21cb,",
1793
+ "leftrightsquigarrow=21ad,leftthreetimes=22cb,leg=22da,leq=2264,leqq=2266,leqslant=2a7d,les=2a7d,lesc",
1794
+ "c=2aa8,lesdot=2a7f,lesdoto=2a81,lesdotor=2a83,lesg=22da.fe00,lesges=2a93,lessapprox=2a85,lessdot=22d",
1795
+ "6,lesseqgtr=22da,lesseqqgtr=2a8b,lessgtr=2276,lesssim=2272,lfisht=297c,lfloor=230a,lfr=1d529,lg=2276",
1796
+ ",lgE=2a91,lhard=21bd,lharu=21bc,lharul=296a,lhblk=2584,ljcy=459,ll=226a,llarr=21c7,llcorner=231e,llh",
1797
+ "ard=296b,lltri=25fa,lmidot=140,lmoust=23b0,lmoustache=23b0,lnE=2268,lnap=2a89,lnapprox=2a89,lne=2a87",
1798
+ ",lneq=2a87,lneqq=2268,lnsim=22e6,loang=27ec,loarr=21fd,lobrk=27e6,longleftarrow=27f5,longleftrightar",
1799
+ "row=27f7,longmapsto=27fc,longrightarrow=27f6,looparrowleft=21ab,looparrowright=21ac,lopar=2985,lopf=",
1800
+ "1d55d,loplus=2a2d,lotimes=2a34,lowast=2217,lowbar=5f,loz=25ca,lozenge=25ca,lozf=29eb,lpar=28,lparlt=",
1801
+ "2993,lrarr=21c6,lrcorner=231f,lrhar=21cb,lrhard=296d,lrm=200e,lrtri=22bf,lsaquo=2039,lscr=1d4c1,lsh=",
1802
+ "21b0,lsim=2272,lsime=2a8d,lsimg=2a8f,lsqb=5b,lsquo=2018,lsquor=201a,lstrok=142,lt=3c,ltcc=2aa6,ltcir",
1803
+ "=2a79,ltdot=22d6,lthree=22cb,ltimes=22c9,ltlarr=2976,ltquest=2a7b,ltrPar=2996,ltri=25c3,ltrie=22b4,l",
1804
+ "trif=25c2,lurdshar=294a,luruhar=2966,lvertneqq=2268.fe00,lvnE=2268.fe00,mDDot=223a,macr=af,male=2642",
1805
+ ",malt=2720,maltese=2720,map=21a6,mapsto=21a6,mapstodown=21a7,mapstoleft=21a4,mapstoup=21a5,marker=25",
1806
+ "ae,mcomma=2a29,mcy=43c,mdash=2014,measuredangle=2221,mfr=1d52a,mho=2127,micro=b5,mid=2223,midast=2a,",
1807
+ "midcir=2af0,middot=b7,minus=2212,minusb=229f,minusd=2238,minusdu=2a2a,mlcp=2adb,mldr=2026,mnplus=221",
1808
+ "3,models=22a7,mopf=1d55e,mp=2213,mscr=1d4c2,mstpos=223e,mu=3bc,multimap=22b8,mumap=22b8,nGg=22d9.338",
1809
+ ",nGt=226b.20d2,nGtv=226b.338,nLeftarrow=21cd,nLeftrightarrow=21ce,nLl=22d8.338,nLt=226a.20d2,nLtv=22",
1810
+ "6a.338,nRightarrow=21cf,nVDash=22af,nVdash=22ae,nabla=2207,nacute=144,nang=2220.20d2,nap=2249,napE=2",
1811
+ "a70.338,napid=224b.338,napos=149,napprox=2249,natur=266e,natural=266e,naturals=2115,nbsp=a0,nbump=22",
1812
+ "4e.338,nbumpe=224f.338,ncap=2a43,ncaron=148,ncedil=146,ncong=2247,ncongdot=2a6d.338,ncup=2a42,ncy=43",
1813
+ "d,ndash=2013,ne=2260,neArr=21d7,nearhk=2924,nearr=2197,nearrow=2197,nedot=2250.338,nequiv=2262,nesea",
1814
+ "r=2928,nesim=2242.338,nexist=2204,nexists=2204,nfr=1d52b,ngE=2267.338,nge=2271,ngeq=2271,ngeqq=2267.",
1815
+ "338,ngeqslant=2a7e.338,nges=2a7e.338,ngsim=2275,ngt=226f,ngtr=226f,nhArr=21ce,nharr=21ae,nhpar=2af2,",
1816
+ "ni=220b,nis=22fc,nisd=22fa,niv=220b,njcy=45a,nlArr=21cd,nlE=2266.338,nlarr=219a,nldr=2025,nle=2270,n",
1817
+ "leftarrow=219a,nleftrightarrow=21ae,nleq=2270,nleqq=2266.338,nleqslant=2a7d.338,nles=2a7d.338,nless=",
1818
+ "226e,nlsim=2274,nlt=226e,nltri=22ea,nltrie=22ec,nmid=2224,nopf=1d55f,not=ac,notin=2209,notinE=22f9.3",
1819
+ "38,notindot=22f5.338,notinva=2209,notinvb=22f7,notinvc=22f6,notni=220c,notniva=220c,notnivb=22fe,not",
1820
+ "nivc=22fd,npar=2226,nparallel=2226,nparsl=2afd.20e5,npart=2202.338,npolint=2a14,npr=2280,nprcue=22e0",
1821
+ ",npre=2aaf.338,nprec=2280,npreceq=2aaf.338,nrArr=21cf,nrarr=219b,nrarrc=2933.338,nrarrw=219d.338,nri",
1822
+ "ghtarrow=219b,nrtri=22eb,nrtrie=22ed,nsc=2281,nsccue=22e1,nsce=2ab0.338,nscr=1d4c3,nshortmid=2224,ns",
1823
+ "hortparallel=2226,nsim=2241,nsime=2244,nsimeq=2244,nsmid=2224,nspar=2226,nsqsube=22e2,nsqsupe=22e3,n",
1824
+ "sub=2284,nsubE=2ac5.338,nsube=2288,nsubset=2282.20d2,nsubseteq=2288,nsubseteqq=2ac5.338,nsucc=2281,n",
1825
+ "succeq=2ab0.338,nsup=2285,nsupE=2ac6.338,nsupe=2289,nsupset=2283.20d2,nsupseteq=2289,nsupseteqq=2ac6",
1826
+ ".338,ntgl=2279,ntilde=f1,ntlg=2278,ntriangleleft=22ea,ntrianglelefteq=22ec,ntriangleright=22eb,ntria",
1827
+ "nglerighteq=22ed,nu=3bd,num=23,numero=2116,numsp=2007,nvDash=22ad,nvHarr=2904,nvap=224d.20d2,nvdash=",
1828
+ "22ac,nvge=2265.20d2,nvgt=3e.20d2,nvinfin=29de,nvlArr=2902,nvle=2264.20d2,nvlt=3c.20d2,nvltrie=22b4.2",
1829
+ "0d2,nvrArr=2903,nvrtrie=22b5.20d2,nvsim=223c.20d2,nwArr=21d6,nwarhk=2923,nwarr=2196,nwarrow=2196,nwn",
1830
+ "ear=2927,oS=24c8,oacute=f3,oast=229b,ocir=229a,ocirc=f4,ocy=43e,odash=229d,odblac=151,odiv=2a38,odot",
1831
+ "=2299,odsold=29bc,oelig=153,ofcir=29bf,ofr=1d52c,ogon=2db,ograve=f2,ogt=29c1,ohbar=29b5,ohm=3a9,oint",
1832
+ "=222e,olarr=21ba,olcir=29be,olcross=29bb,oline=203e,olt=29c0,omacr=14d,omega=3c9,omicron=3bf,omid=29",
1833
+ "b6,ominus=2296,oopf=1d560,opar=29b7,operp=29b9,oplus=2295,or=2228,orarr=21bb,ord=2a5d,order=2134,ord",
1834
+ "erof=2134,ordf=aa,ordm=ba,origof=22b6,oror=2a56,orslope=2a57,orv=2a5b,oscr=2134,oslash=f8,osol=2298,",
1835
+ "otilde=f5,otimes=2297,otimesas=2a36,ouml=f6,ovbar=233d,par=2225,para=b6,parallel=2225,parsim=2af3,pa",
1836
+ "rsl=2afd,part=2202,pcy=43f,percnt=25,period=2e,permil=2030,perp=22a5,pertenk=2031,pfr=1d52d,phi=3c6,",
1837
+ "phiv=3d5,phmmat=2133,phone=260e,pi=3c0,pitchfork=22d4,piv=3d6,planck=210f,planckh=210e,plankv=210f,p",
1838
+ "lus=2b,plusacir=2a23,plusb=229e,pluscir=2a22,plusdo=2214,plusdu=2a25,pluse=2a72,plusmn=b1,plussim=2a",
1839
+ "26,plustwo=2a27,pm=b1,pointint=2a15,popf=1d561,pound=a3,pr=227a,prE=2ab3,prap=2ab7,prcue=227c,pre=2a",
1840
+ "af,prec=227a,precapprox=2ab7,preccurlyeq=227c,preceq=2aaf,precnapprox=2ab9,precneqq=2ab5,precnsim=22",
1841
+ "e8,precsim=227e,prime=2032,primes=2119,prnE=2ab5,prnap=2ab9,prnsim=22e8,prod=220f,profalar=232e,prof",
1842
+ "line=2312,profsurf=2313,prop=221d,propto=221d,prsim=227e,prurel=22b0,pscr=1d4c5,psi=3c8,puncsp=2008,",
1843
+ "qfr=1d52e,qint=2a0c,qopf=1d562,qprime=2057,qscr=1d4c6,quaternions=210d,quatint=2a16,quest=3f,questeq",
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=27e9,rangd=2992,range=29a5,rangle=27e9,raquo=bb,rarr=2192,rarrap=2975,rarrb=2",
1846
+ "1e5,rarrbfs=2920,rarrc=2933,rarrfs=291e,rarrhk=21aa,rarrlp=21ac,rarrpl=2945,rarrsim=2974,rarrtl=21a3",
1847
+ ",rarrw=219d,ratail=291a,ratio=2236,rationals=211a,rbarr=290d,rbbrk=2773,rbrace=7d,rbrack=5d,rbrke=29",
1848
+ "8c,rbrksld=298e,rbrkslu=2990,rcaron=159,rcedil=157,rceil=2309,rcub=7d,rcy=440,rdca=2937,rdldhar=2969",
1849
+ ",rdquo=201d,rdquor=201d,rdsh=21b3,real=211c,realine=211b,realpart=211c,reals=211d,rect=25ad,reg=ae,r",
1850
+ "fisht=297d,rfloor=230b,rfr=1d52f,rhard=21c1,rharu=21c0,rharul=296c,rho=3c1,rhov=3f1,rightarrow=2192,",
1851
+ "rightarrowtail=21a3,rightharpoondown=21c1,rightharpoonup=21c0,rightleftarrows=21c4,rightleftharpoons",
1852
+ "=21cc,rightrightarrows=21c9,rightsquigarrow=219d,rightthreetimes=22cc,ring=2da,risingdotseq=2253,rla",
1853
+ "rr=21c4,rlhar=21cc,rlm=200f,rmoust=23b1,rmoustache=23b1,rnmid=2aee,roang=27ed,roarr=21fe,robrk=27e7,",
1854
+ "ropar=2986,ropf=1d563,roplus=2a2e,rotimes=2a35,rpar=29,rpargt=2994,rppolint=2a12,rrarr=21c9,rsaquo=2",
1855
+ "03a,rscr=1d4c7,rsh=21b1,rsqb=5d,rsquo=2019,rsquor=2019,rthree=22cc,rtimes=22ca,rtri=25b9,rtrie=22b5,",
1856
+ "rtrif=25b8,rtriltri=29ce,ruluhar=2968,rx=211e,sacute=15b,sbquo=201a,sc=227b,scE=2ab4,scap=2ab8,scaro",
1857
+ "n=161,sccue=227d,sce=2ab0,scedil=15f,scirc=15d,scnE=2ab6,scnap=2aba,scnsim=22e9,scpolint=2a13,scsim=",
1858
+ "227f,scy=441,sdot=22c5,sdotb=22a1,sdote=2a66,seArr=21d8,searhk=2925,searr=2198,searrow=2198,sect=a7,",
1859
+ "semi=3b,seswar=2929,setminus=2216,setmn=2216,sext=2736,sfr=1d530,sfrown=2322,sharp=266f,shchcy=449,s",
1860
+ "hcy=448,shortmid=2223,shortparallel=2225,shy=ad,sigma=3c3,sigmaf=3c2,sigmav=3c2,sim=223c,simdot=2a6a",
1861
+ ",sime=2243,simeq=2243,simg=2a9e,simgE=2aa0,siml=2a9d,simlE=2a9f,simne=2246,simplus=2a24,simrarr=2972",
1862
+ ",slarr=2190,smallsetminus=2216,smashp=2a33,smeparsl=29e4,smid=2223,smile=2323,smt=2aaa,smte=2aac,smt",
1863
+ "es=2aac.fe00,softcy=44c,sol=2f,solb=29c4,solbar=233f,sopf=1d564,spades=2660,spadesuit=2660,spar=2225",
1864
+ ",sqcap=2293,sqcaps=2293.fe00,sqcup=2294,sqcups=2294.fe00,sqsub=228f,sqsube=2291,sqsubset=228f,sqsubs",
1865
+ "eteq=2291,sqsup=2290,sqsupe=2292,sqsupset=2290,sqsupseteq=2292,squ=25a1,square=25a1,squarf=25aa,squf",
1866
+ "=25aa,srarr=2192,sscr=1d4c8,ssetmn=2216,ssmile=2323,sstarf=22c6,star=2606,starf=2605,straightepsilon",
1867
+ "=3f5,straightphi=3d5,strns=af,sub=2282,subE=2ac5,subdot=2abd,sube=2286,subedot=2ac3,submult=2ac1,sub",
1868
+ "nE=2acb,subne=228a,subplus=2abf,subrarr=2979,subset=2282,subseteq=2286,subseteqq=2ac5,subsetneq=228a",
1869
+ ",subsetneqq=2acb,subsim=2ac7,subsub=2ad5,subsup=2ad3,succ=227b,succapprox=2ab8,succcurlyeq=227d,succ",
1870
+ "eq=2ab0,succnapprox=2aba,succneqq=2ab6,succnsim=22e9,succsim=227f,sum=2211,sung=266a,sup=2283,sup1=b",
1871
+ "9,sup2=b2,sup3=b3,supE=2ac6,supdot=2abe,supdsub=2ad8,supe=2287,supedot=2ac4,suphsol=27c9,suphsub=2ad",
1872
+ "7,suplarr=297b,supmult=2ac2,supnE=2acc,supne=228b,supplus=2ac0,supset=2283,supseteq=2287,supseteqq=2",
1873
+ "ac6,supsetneq=228b,supsetneqq=2acc,supsim=2ac8,supsub=2ad4,supsup=2ad6,swArr=21d9,swarhk=2926,swarr=",
1874
+ "2199,swarrow=2199,swnwar=292a,szlig=df,target=2316,tau=3c4,tbrk=23b4,tcaron=165,tcedil=163,tcy=442,t",
1875
+ "dot=20db,telrec=2315,tfr=1d531,there4=2234,therefore=2234,theta=3b8,thetasym=3d1,thetav=3d1,thickapp",
1876
+ "rox=2248,thicksim=223c,thinsp=2009,thkap=2248,thksim=223c,thorn=fe,tilde=2dc,times=d7,timesb=22a0,ti",
1877
+ "mesbar=2a31,timesd=2a30,tint=222d,toea=2928,top=22a4,topbot=2336,topcir=2af1,topf=1d565,topfork=2ada",
1878
+ ",tosa=2929,tprime=2034,trade=2122,triangle=25b5,triangledown=25bf,triangleleft=25c3,trianglelefteq=2",
1879
+ "2b4,triangleq=225c,triangleright=25b9,trianglerighteq=22b5,tridot=25ec,trie=225c,triminus=2a3a,tripl",
1880
+ "us=2a39,trisb=29cd,tritime=2a3b,trpezium=23e2,tscr=1d4c9,tscy=446,tshcy=45b,tstrok=167,twixt=226c,tw",
1881
+ "oheadleftarrow=219e,twoheadrightarrow=21a0,uArr=21d1,uHar=2963,uacute=fa,uarr=2191,ubrcy=45e,ubreve=",
1882
+ "16d,ucirc=fb,ucy=443,udarr=21c5,udblac=171,udhar=296e,ufisht=297e,ufr=1d532,ugrave=f9,uharl=21bf,uha",
1883
+ "rr=21be,uhblk=2580,ulcorn=231c,ulcorner=231c,ulcrop=230f,ultri=25f8,umacr=16b,uml=a8,uogon=173,uopf=",
1884
+ "1d566,uparrow=2191,updownarrow=2195,upharpoonleft=21bf,upharpoonright=21be,uplus=228e,upsi=3c5,upsih",
1885
+ "=3d2,upsilon=3c5,upuparrows=21c8,urcorn=231d,urcorner=231d,urcrop=230e,uring=16f,urtri=25f9,uscr=1d4",
1886
+ "ca,utdot=22f0,utilde=169,utri=25b5,utrif=25b4,uuarr=21c8,uuml=fc,uwangle=29a7,vArr=21d5,vBar=2ae8,vB",
1887
+ "arv=2ae9,vDash=22a8,vangrt=299c,varepsilon=3f5,varkappa=3f0,varnothing=2205,varphi=3d5,varpi=3d6,var",
1888
+ "propto=221d,varr=2195,varrho=3f1,varsigma=3c2,varsubsetneq=228a.fe00,varsubsetneqq=2acb.fe00,varsups",
1889
+ "etneq=228b.fe00,varsupsetneqq=2acc.fe00,vartheta=3d1,vartriangleleft=22b2,vartriangleright=22b3,vcy=",
1890
+ "432,vdash=22a2,vee=2228,veebar=22bb,veeeq=225a,vellip=22ee,verbar=7c,vert=7c,vfr=1d533,vltri=22b2,vn",
1891
+ "sub=2282.20d2,vnsup=2283.20d2,vopf=1d567,vprop=221d,vrtri=22b3,vscr=1d4cb,vsubnE=2acb.fe00,vsubne=22",
1892
+ "8a.fe00,vsupnE=2acc.fe00,vsupne=228b.fe00,vzigzag=299a,wcirc=175,wedbar=2a5f,wedge=2227,wedgeq=2259,",
1893
+ "weierp=2118,wfr=1d534,wopf=1d568,wp=2118,wr=2240,wreath=2240,wscr=1d4cc,xcap=22c2,xcirc=25ef,xcup=22",
1894
+ "c3,xdtri=25bd,xfr=1d535,xhArr=27fa,xharr=27f7,xi=3be,xlArr=27f8,xlarr=27f5,xmap=27fc,xnis=22fb,xodot",
1895
+ "=2a00,xopf=1d569,xoplus=2a01,xotime=2a02,xrArr=27f9,xrarr=27f6,xscr=1d4cd,xsqcup=2a06,xuplus=2a04,xu",
1896
+ "tri=25b3,xvee=22c1,xwedge=22c0,yacute=fd,yacy=44f,ycirc=177,ycy=44b,yen=a5,yfr=1d536,yicy=457,yopf=1",
1897
+ "d56a,yscr=1d4ce,yucy=44e,yuml=ff,zacute=17a,zcaron=17e,zcy=437,zdot=17c,zeetrf=2128,zeta=3b6,zfr=1d5",
1898
+ "37,zhcy=436,zigrarr=21dd,zopf=1d56b,zscr=1d4cf,zwj=200d,zwnj=200c"
1899
+ ].join("");
1900
+ NAMED_REFERENCES = new Map(NAMED.split(",").map((entry) => {
1901
+ const [name, codes] = entry.split("=");
1902
+ return [name, String.fromCodePoint(...codes.split(".").map((code) => Number.parseInt(code, 16)))];
1903
+ }));
1904
+ }
1905
+ });
1906
+
1627
1907
  // src/html.js
1628
1908
  function fromCode(code) {
1629
1909
  if (code === 0 || code > 1114111 || code >= 55296 && code <= 57343) {
@@ -1640,8 +1920,8 @@ function decodeEntities(value, { attribute = false } = {}) {
1640
1920
  if (hex !== void 0 || decimal !== void 0) {
1641
1921
  return fromCode(hex === void 0 ? Number.parseInt(decimal, 10) : Number.parseInt(hex, 16));
1642
1922
  }
1643
- if (semicolon && (ENTITIES.has(name) || LONGER_NAMES.has(name))) {
1644
- return ENTITIES.get(name) ?? match;
1923
+ if (semicolon && NAMED_REFERENCES.has(name)) {
1924
+ return NAMED_REFERENCES.get(name);
1645
1925
  }
1646
1926
  let legacy;
1647
1927
  for (let length = Math.min(name.length, LEGACY_LONGEST); length >= 2 && legacy === void 0; length--) {
@@ -1655,7 +1935,7 @@ function decodeEntities(value, { attribute = false } = {}) {
1655
1935
  if (attribute && /[\da-zA-Z=]/.test(next)) {
1656
1936
  return match;
1657
1937
  }
1658
- return ENTITIES.get(legacy) + rest;
1938
+ return NAMED_REFERENCES.get(legacy) + rest;
1659
1939
  });
1660
1940
  }
1661
1941
  function readTag(html2, start) {
@@ -2296,106 +2576,14 @@ function visibleText(html2) {
2296
2576
  }
2297
2577
  return { text: parts.join(""), hidden };
2298
2578
  }
2299
- var LATIN1, ENTITIES, LEGACY, LEGACY_LONGEST, LONGER_NAMES, WINDOWS_1252, REFERENCE, UNSHOWN, RAW_SHOWN, RAW_END, BREAKS, SPECIAL, SCOPE, FORMATTING, LINE, OWN_PLACE, VOID, CLOSES_PARAGRAPH, PARAGRAPH, IMPLIED, TABLE_PARTS, TABLE_BOXES, HEADINGS, IMPLIED_END, PARTS_OF_TABLE, TABLE_STRUCTURE, IN_TABLE, MAX_FORMATTING, MARKERS, FOSTERED_MARKERS, NOT_REOPENING, LEAVES_FOREIGN, HOLDS_HTML, SVG_UNSHOWN, MAX_OPEN, MAX_ATTRIBUTES, MAX_STYLE, COMMENT_END, WHITESPACE2, NO_WORD_ATTRIBUTES, FIELDS, FIELD_HELP, HINTS, MAX_HELP, LABEL_END, isHelp, MAX_VALUE, LENGTH, UNITS, SIZE_KEYWORDS, ZERO_NUMBER, DISPLAY_INLINE, DISPLAY_BLOCK, isZero, declarationCache, ROOT, DOCTYPE, QUIRKS_PUBLIC, QUIRKS_IF_NO_SYSTEM;
2579
+ var LATIN1, LEGACY, LEGACY_LONGEST, WINDOWS_1252, REFERENCE, UNSHOWN, RAW_SHOWN, RAW_END, BREAKS, SPECIAL, SCOPE, FORMATTING, LINE, OWN_PLACE, VOID, CLOSES_PARAGRAPH, PARAGRAPH, IMPLIED, TABLE_PARTS, TABLE_BOXES, HEADINGS, IMPLIED_END, PARTS_OF_TABLE, TABLE_STRUCTURE, IN_TABLE, MAX_FORMATTING, MARKERS, FOSTERED_MARKERS, NOT_REOPENING, LEAVES_FOREIGN, HOLDS_HTML, SVG_UNSHOWN, MAX_OPEN, MAX_ATTRIBUTES, MAX_STYLE, COMMENT_END, WHITESPACE2, NO_WORD_ATTRIBUTES, FIELDS, FIELD_HELP, HINTS, MAX_HELP, LABEL_END, isHelp, MAX_VALUE, LENGTH, UNITS, SIZE_KEYWORDS, ZERO_NUMBER, DISPLAY_INLINE, DISPLAY_BLOCK, isZero, declarationCache, ROOT, DOCTYPE, QUIRKS_PUBLIC, QUIRKS_IF_NO_SYSTEM;
2300
2580
  var init_html = __esm({
2301
2581
  "src/html.js"() {
2582
+ init_entities();
2302
2583
  init_linear();
2303
2584
  LATIN1 = "nbsp iexcl cent pound curren yen brvbar sect uml copy ordf laquo not shy reg macr deg plusmn sup2 sup3 acute micro para middot cedil sup1 ordm raquo frac14 frac12 frac34 iquest Agrave Aacute Acirc Atilde Auml Aring AElig Ccedil Egrave Eacute Ecirc Euml Igrave Iacute Icirc Iuml ETH Ntilde Ograve Oacute Ocirc Otilde Ouml times Oslash Ugrave Uacute Ucirc Uuml Yacute THORN szlig agrave aacute acirc atilde auml aring aelig ccedil egrave eacute ecirc euml igrave iacute icirc iuml eth ntilde ograve oacute ocirc otilde ouml divide oslash ugrave uacute ucirc uuml yacute thorn yuml".split(" ");
2304
- ENTITIES = new Map([
2305
- ...LATIN1.map((name, index) => [name, String.fromCodePoint(160 + index)]),
2306
- ["amp", "&"],
2307
- ["lt", "<"],
2308
- ["gt", ">"],
2309
- ["quot", '"'],
2310
- ["apos", "'"],
2311
- ["AMP", "&"],
2312
- ["LT", "<"],
2313
- ["GT", ">"],
2314
- ["QUOT", '"'],
2315
- ["COPY", "\xA9"],
2316
- ["REG", "\xAE"],
2317
- ["OElig", "\u0152"],
2318
- ["oelig", "\u0153"],
2319
- ["Scaron", "\u0160"],
2320
- ["scaron", "\u0161"],
2321
- ["Yuml", "\u0178"],
2322
- ["fnof", "\u0192"],
2323
- ["circ", "\u02C6"],
2324
- ["tilde", "\u02DC"],
2325
- ["ensp", "\u2002"],
2326
- ["emsp", "\u2003"],
2327
- ["thinsp", "\u2009"],
2328
- ["zwnj", "\u200C"],
2329
- ["zwj", "\u200D"],
2330
- ["lrm", "\u200E"],
2331
- ["rlm", "\u200F"],
2332
- ["ndash", "\u2013"],
2333
- ["mdash", "\u2014"],
2334
- ["lsquo", "\u2018"],
2335
- ["rsquo", "\u2019"],
2336
- ["sbquo", "\u201A"],
2337
- ["ldquo", "\u201C"],
2338
- ["rdquo", "\u201D"],
2339
- ["bdquo", "\u201E"],
2340
- ["dagger", "\u2020"],
2341
- ["Dagger", "\u2021"],
2342
- ["bull", "\u2022"],
2343
- ["hellip", "\u2026"],
2344
- ["permil", "\u2030"],
2345
- ["prime", "\u2032"],
2346
- ["Prime", "\u2033"],
2347
- ["lsaquo", "\u2039"],
2348
- ["rsaquo", "\u203A"],
2349
- ["euro", "\u20AC"],
2350
- ["trade", "\u2122"],
2351
- ["larr", "\u2190"],
2352
- ["uarr", "\u2191"],
2353
- ["rarr", "\u2192"],
2354
- ["darr", "\u2193"],
2355
- ["hearts", "\u2665"],
2356
- ["check", "\u2713"],
2357
- // The punctuation of a web address, as browsers read these names
2358
- // ("https://bank.example&commat;evil.example/" goes to evil.example)
2359
- ["Tab", " "],
2360
- ["NewLine", "\n"],
2361
- ["excl", "!"],
2362
- ["num", "#"],
2363
- ["dollar", "$"],
2364
- ["percnt", "%"],
2365
- ["lpar", "("],
2366
- ["rpar", ")"],
2367
- ["ast", "*"],
2368
- ["midast", "*"],
2369
- ["plus", "+"],
2370
- ["comma", ","],
2371
- ["period", "."],
2372
- ["sol", "/"],
2373
- ["colon", ":"],
2374
- ["semi", ";"],
2375
- ["equals", "="],
2376
- ["quest", "?"],
2377
- ["commat", "@"],
2378
- ["lsqb", "["],
2379
- ["lbrack", "["],
2380
- ["bsol", "\\"],
2381
- ["rsqb", "]"],
2382
- ["rbrack", "]"],
2383
- ["Hat", "^"],
2384
- ["lowbar", "_"],
2385
- ["UnderBar", "_"],
2386
- ["grave", "`"],
2387
- ["DiacriticalGrave", "`"],
2388
- ["lcub", "{"],
2389
- ["lbrace", "{"],
2390
- ["verbar", "|"],
2391
- ["vert", "|"],
2392
- ["VerticalLine", "|"],
2393
- ["rcub", "}"],
2394
- ["rbrace", "}"]
2395
- ]);
2396
2585
  LEGACY = /* @__PURE__ */ new Set([...LATIN1, "amp", "lt", "gt", "quot", "AMP", "LT", "GT", "QUOT", "COPY", "REG"]);
2397
2586
  LEGACY_LONGEST = Math.max(...[...LEGACY].map((name) => name.length));
2398
- LONGER_NAMES = new Set("centerdot copysr divideontimes gtcc gtcir gtdot gtlPar gtquest gtrapprox gtrarr gtrdot gtreqless gtreqqless gtrless gtrsim ltcc ltcir ltdot lthree ltimes ltlarr ltquest ltrPar ltri ltrie ltrif notin notinE notindot notinva notinvb notinvc notni notniva notnivb notnivc parallel timesb timesbar timesd".split(" "));
2399
2587
  WINDOWS_1252 = [8364, 129, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 141, 381, 143, 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250, 339, 157, 382, 376];
2400
2588
  REFERENCE = /&(?:#([xX])([\da-fA-F]+)|#(\d+)|([a-zA-Z][a-zA-Z\d]{0,31}))(;?)/g;
2401
2589
  UNSHOWN = /* @__PURE__ */ new Set(["script", "style", "title", "noembed", "noframes", "iframe"]);
@@ -2509,6 +2697,31 @@ function continuesName(text, index) {
2509
2697
  }
2510
2698
  return (text[index - 1] === "." || text[index - 1] === "-") && NAME_END.test(text.slice(Math.max(0, index - 3), index - 1));
2511
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
+ }
2512
2725
  function isClickTracker(url) {
2513
2726
  const host = url.hostname.toLowerCase();
2514
2727
  return CLICK_TRACKERS.some((domain) => isUnder(host, domain)) || host === "substack.com" && url.pathname.startsWith("/redirect/");
@@ -2535,7 +2748,7 @@ function gatewayTarget(url) {
2535
2748
  function unwrapLink(address2) {
2536
2749
  let url;
2537
2750
  try {
2538
- url = new URL(address2);
2751
+ url = boundedUrl(address2);
2539
2752
  } catch {
2540
2753
  return null;
2541
2754
  }
@@ -2546,7 +2759,7 @@ function unwrapLink(address2) {
2546
2759
  }
2547
2760
  let next;
2548
2761
  try {
2549
- next = new URL(target);
2762
+ next = boundedUrl(target);
2550
2763
  } catch {
2551
2764
  break;
2552
2765
  }
@@ -2790,7 +3003,7 @@ function endsInsideTag(html2, end, link) {
2790
3003
  const joined = trimEndOf(link.replace(/^https?:\/\//i, "") + after, ".-");
2791
3004
  return joined !== host && (!isHost(host) || isHost(joined) && registrableDomain(joined) !== registrableDomain(host));
2792
3005
  }
2793
- function extractLinks(text, html2, { maxLength = 5e5, bareHosts = false } = {}) {
3006
+ function extractLinks(text, html2, { maxLength = 5e5, bareHosts: bareHosts2 = false } = {}) {
2794
3007
  text = typeof text === "string" ? foldToAscii(text) : text;
2795
3008
  html2 = typeof html2 === "string" ? foldToAscii(html2) : html2;
2796
3009
  const links = [];
@@ -2808,7 +3021,7 @@ function extractLinks(text, html2, { maxLength = 5e5, bareHosts = false } = {})
2808
3021
  }
2809
3022
  let url;
2810
3023
  try {
2811
- url = new URL(href);
3024
+ url = boundedUrl(href);
2812
3025
  } catch {
2813
3026
  return;
2814
3027
  }
@@ -2857,7 +3070,7 @@ ${shown || ""}`;
2857
3070
  }
2858
3071
  add(link);
2859
3072
  }
2860
- if (bareHosts && index < 2) {
3073
+ if (bareHosts2 && index < 2) {
2861
3074
  bareLinkHosts(read, (host) => {
2862
3075
  bareFound.push(host);
2863
3076
  });
@@ -3121,7 +3334,7 @@ function isDeceptiveLink(link, { from } = {}) {
3121
3334
  }
3122
3335
  let shownUrl;
3123
3336
  try {
3124
- shownUrl = new URL(/^https?:\/\//i.test(link.text) ? link.text : `http://${link.text}`);
3337
+ shownUrl = boundedUrl(/^https?:\/\//i.test(link.text) ? link.text : `http://${link.text}`);
3125
3338
  } catch {
3126
3339
  return false;
3127
3340
  }
@@ -3479,7 +3692,7 @@ ${body}`);
3479
3692
  hidden: visible.hidden
3480
3693
  };
3481
3694
  }
3482
- 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;
3483
3696
  var init_tokenizer = __esm({
3484
3697
  "src/tokenizer.js"() {
3485
3698
  import_tldts2 = require("tldts");
@@ -3585,6 +3798,9 @@ var init_tokenizer = __esm({
3585
3798
  FILE_TLDS = /* @__PURE__ */ new Set(["ai", "app", "md", "mov", "pm", "ps", "py", "rs", "sh", "so", "zip"]);
3586
3799
  LINK_FILE_TLDS = /* @__PURE__ */ new Set(["md", "mov", "pm", "ps", "py", "rs", "sh", "so", "zip"]);
3587
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;
3588
3804
  isUnder = (host, domain) => host === domain || host.endsWith(`.${domain}`);
3589
3805
  MAX_GATEWAYS = 16;
3590
3806
  BRAND_NAMES = new Set(BRANDS);
@@ -4423,7 +4639,8 @@ async function parseWithin(raw, parse = parseMessage, deadline = Date.now() + PA
4423
4639
  }
4424
4640
  }
4425
4641
  }
4426
- async function expandAttachments(attachments = [], errors = [], parse = parseMessage, unopened = []) {
4642
+ async function expandAttachments(attachments = [], errors = [], parse = parseMessage, gets = {}) {
4643
+ const { unopened = [], mails = [] } = Array.isArray(gets) ? { unopened: gets } : gets ?? {};
4427
4644
  const all = [];
4428
4645
  const deadline = Date.now() + PARSE_TIMEOUT;
4429
4646
  let budget = MAX_NESTED_BYTES;
@@ -4472,6 +4689,7 @@ async function expandAttachments(attachments = [], errors = [], parse = parseMes
4472
4689
  if (inner.error) {
4473
4690
  errors.push(inner.error);
4474
4691
  }
4692
+ mails.push(inner.mail);
4475
4693
  await visit(inner.mail.attachments, prefix ? `${prefix}/${name}` : name || "attached message", depth + 1);
4476
4694
  }
4477
4695
  }
@@ -4571,6 +4789,10 @@ var init_parse = __esm({
4571
4789
  });
4572
4790
 
4573
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
+ }
4574
4796
  function lookupName(host) {
4575
4797
  if (typeof host !== "string") {
4576
4798
  return null;
@@ -4581,6 +4803,7 @@ function lookupName(host) {
4581
4803
  }
4582
4804
  let name = host.slice(0, end).toLowerCase();
4583
4805
  if (/[\u0080-\u{10FFFF}]/u.test(name)) {
4806
+ name = name.slice(lastLabels(name));
4584
4807
  name = (0, import_node_url2.domainToASCII)(name) || name;
4585
4808
  }
4586
4809
  if ((0, import_node_net.isIP)(name.replaceAll(/^\[|]$/g, "")) !== 0) {
@@ -4680,7 +4903,7 @@ function isListedAnswer(address2, zone) {
4680
4903
  }
4681
4904
  return true;
4682
4905
  }
4683
- 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;
4684
4907
  var init_dnsbl = __esm({
4685
4908
  "src/dnsbl.js"() {
4686
4909
  import_node_dns = __toESM(require("node:dns"), 1);
@@ -4688,6 +4911,7 @@ var init_dnsbl = __esm({
4688
4911
  import_node_url2 = require("node:url");
4689
4912
  import_tldts3 = require("tldts");
4690
4913
  LOOKUP_LABEL = /^[a-z\d_-]{1,63}$/;
4914
+ LABEL_SEPARATOR = /[.\u3002\uFF0E\uFF61]/g;
4691
4915
  CLOUDFLARE_MALWARE = ["1.1.1.2", "1.0.0.2"];
4692
4916
  CLOUDFLARE_FAMILY = ["1.1.1.3", "1.0.0.3"];
4693
4917
  DnsChecker = class {
@@ -4927,7 +5151,7 @@ function asciiHostname(value) {
4927
5151
  return null;
4928
5152
  }
4929
5153
  const lower = value.normalize("NFKC").toLowerCase().replace(/\.$/, "");
4930
- if (lower === "" || /[\0-,/:-^`{-\u007F]/.test(lower)) {
5154
+ if (lower === "" || lower.length > 1024 || /[\0-,/:-^`{-\u007F]/.test(lower)) {
4931
5155
  return null;
4932
5156
  }
4933
5157
  const ascii = (0, import_node_url3.domainToASCII)(lower);
@@ -5000,8 +5224,8 @@ function trustReputation(result, trusted = []) {
5000
5224
  for (const value of result.checkedValues.filter((item) => allowed.has(item))) {
5001
5225
  const signers = allowed.get(value);
5002
5226
  const counts = (entry) => Boolean(entry) && (signers === null || signers.some((signer) => vouches(normalizeEntry(entry), signer)));
5003
- const detail = result.details[value];
5004
- 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);
5005
5229
  if (counts(listed) && !out.isAllowlisted) {
5006
5230
  out.isAllowlisted = true;
5007
5231
  out.allowlistValue = listed;
@@ -6015,6 +6239,997 @@ var init_classifier = __esm({
6015
6239
  }
6016
6240
  });
6017
6241
 
6242
+ // src/links.js
6243
+ function isHostCode2(code) {
6244
+ return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 || code === 45 || code === 46;
6245
+ }
6246
+ function isSpace(code) {
6247
+ return code === 32 || code >= 9 && code <= 13;
6248
+ }
6249
+ function urlOf(value) {
6250
+ let href = value.replaceAll(/[\t\n\r]/g, "");
6251
+ let start = 0;
6252
+ let end = href.length;
6253
+ while (start < end && href.codePointAt(start) <= 32) {
6254
+ start++;
6255
+ }
6256
+ while (end > start && href.codePointAt(end - 1) <= 32) {
6257
+ end--;
6258
+ }
6259
+ while (end > start && TRAILING.has(href[end - 1])) {
6260
+ end--;
6261
+ }
6262
+ href = href.slice(start, end);
6263
+ if (/^[\\/]{2}/.test(href)) {
6264
+ href = `https://${href.slice(2)}`;
6265
+ } else if (/^www\./i.test(href)) {
6266
+ href = `http://${href}`;
6267
+ } else if (!/^https?:/i.test(href)) {
6268
+ return null;
6269
+ }
6270
+ try {
6271
+ return boundedUrl(href);
6272
+ } catch {
6273
+ return null;
6274
+ }
6275
+ }
6276
+ function hostOfUrl(url) {
6277
+ let host = url.hostname.toLowerCase();
6278
+ let dots = host.length;
6279
+ while (dots > 0 && host[dots - 1] === ".") {
6280
+ dots--;
6281
+ }
6282
+ host = host.slice(0, dots);
6283
+ if (host.startsWith("[") && host.endsWith("]")) {
6284
+ host = host.slice(1, -1);
6285
+ }
6286
+ if (!host || NAMESPACES.has(host)) {
6287
+ return null;
6288
+ }
6289
+ return (0, import_node_net4.isIP)(host) ? host : lookupName(host);
6290
+ }
6291
+ function hostsOf(value) {
6292
+ const url = urlOf(value);
6293
+ if (!url) {
6294
+ return [];
6295
+ }
6296
+ const host = hostOfUrl(url);
6297
+ const hosts = host ? [host] : [];
6298
+ const target = unwrapLink(url.href);
6299
+ if (target && target.href !== url.href) {
6300
+ const targetHost = hostOfUrl(target);
6301
+ if (targetHost && targetHost !== host) {
6302
+ hosts.push(targetHost);
6303
+ }
6304
+ const further = unwrapLink(target.href);
6305
+ if (further && further.href !== target.href) {
6306
+ hosts.unresolved = true;
6307
+ }
6308
+ }
6309
+ return hosts;
6310
+ }
6311
+ function* attributesOf(html2) {
6312
+ TAG_START.lastIndex = 0;
6313
+ let match;
6314
+ while ((match = TAG_START.exec(html2)) !== null) {
6315
+ let i = match.index + match[0].length;
6316
+ while (i < html2.length && html2[i] !== ">") {
6317
+ if (isSpace(html2.codePointAt(i)) || html2[i] === "/") {
6318
+ i++;
6319
+ continue;
6320
+ }
6321
+ let j = i;
6322
+ while (j < html2.length && html2[j] !== "=" && html2[j] !== ">" && html2[j] !== "/" && !isSpace(html2.codePointAt(j))) {
6323
+ j++;
6324
+ }
6325
+ const name = html2.slice(i, j).toLowerCase();
6326
+ if (j === i) {
6327
+ j++;
6328
+ }
6329
+ while (j < html2.length && isSpace(html2.codePointAt(j))) {
6330
+ j++;
6331
+ }
6332
+ if (html2[j] !== "=") {
6333
+ i = j;
6334
+ continue;
6335
+ }
6336
+ j++;
6337
+ while (j < html2.length && isSpace(html2.codePointAt(j))) {
6338
+ j++;
6339
+ }
6340
+ let value;
6341
+ if (html2[j] === '"' || html2[j] === "'") {
6342
+ const close = html2.indexOf(html2[j], j + 1);
6343
+ const stop = close === -1 ? html2.length : close;
6344
+ value = html2.slice(j + 1, stop);
6345
+ j = stop + 1;
6346
+ } else {
6347
+ const start = j;
6348
+ while (j < html2.length && html2[j] !== ">" && !isSpace(html2.codePointAt(j))) {
6349
+ j++;
6350
+ }
6351
+ value = html2.slice(start, j);
6352
+ }
6353
+ if (name) {
6354
+ yield [name, value];
6355
+ }
6356
+ i = j;
6357
+ }
6358
+ TAG_START.lastIndex = Math.max(i, match.index + 1);
6359
+ }
6360
+ }
6361
+ function linksOf(name, value, { add, embed, cut }) {
6362
+ if (value.length > MAX_VALUE2) {
6363
+ cut();
6364
+ value = value.slice(0, MAX_VALUE2);
6365
+ }
6366
+ const decoded = decodeEntities(value, { attribute: true });
6367
+ switch (name) {
6368
+ case "srcdoc": {
6369
+ embed(decoded);
6370
+ break;
6371
+ }
6372
+ case "style": {
6373
+ for (const [, link] of decoded.matchAll(CSS_URL)) {
6374
+ add(link);
6375
+ }
6376
+ break;
6377
+ }
6378
+ case "srcset": {
6379
+ for (const candidate of decoded.split(",")) {
6380
+ add(candidate.trim().split(/\s/, 1)[0]);
6381
+ }
6382
+ break;
6383
+ }
6384
+ // (several, separated by spaces)
6385
+ case "ping": {
6386
+ for (const link of decoded.split(/\s+/)) {
6387
+ add(link);
6388
+ }
6389
+ break;
6390
+ }
6391
+ // (only the address of a refresh)
6392
+ case "content": {
6393
+ const match = /(?:^|[\s;,])url\s*=\s*["']?([^\s"';]+)/i.exec(decoded.slice(0, 4096));
6394
+ if (match) {
6395
+ add(match[1]);
6396
+ }
6397
+ break;
6398
+ }
6399
+ default: {
6400
+ add(decoded);
6401
+ }
6402
+ }
6403
+ }
6404
+ function lenientLinks(html2, done, reader) {
6405
+ LENIENT.lastIndex = 0;
6406
+ let match;
6407
+ while (!done() && (match = LENIENT.exec(html2)) !== null) {
6408
+ const name = match[1].toLowerCase();
6409
+ let i = LENIENT.lastIndex;
6410
+ let end = i;
6411
+ if (html2[i] === '"' || html2[i] === "'") {
6412
+ const quote = html2[i];
6413
+ i++;
6414
+ end = i;
6415
+ while (end < html2.length && html2[end] !== quote && html2[end] !== "<" && html2[end] !== ">") {
6416
+ end++;
6417
+ }
6418
+ if (end < html2.length && html2[end] !== quote) {
6419
+ const close = html2.indexOf(quote, end);
6420
+ if (close !== -1 && close - i <= MAX_LENIENT_VALUE && !reader.spend(close - i)) {
6421
+ linksOf(name, html2.slice(i, close), reader);
6422
+ }
6423
+ }
6424
+ } else {
6425
+ while (end < html2.length && !isSpace(html2.codePointAt(end)) && !UNQUOTED_END.has(html2[end])) {
6426
+ end++;
6427
+ }
6428
+ }
6429
+ if (reader.spend(end - i)) {
6430
+ break;
6431
+ }
6432
+ linksOf(name, html2.slice(i, end), reader);
6433
+ }
6434
+ }
6435
+ function endsLink2(text, i) {
6436
+ const code = text.codePointAt(i);
6437
+ if (code < 128) {
6438
+ return isSpace(code) || code === 34 || code === 39 || code === 60 || code === 62 || code === 96;
6439
+ }
6440
+ return /\s/u.test(text[i]);
6441
+ }
6442
+ function linkReader(text) {
6443
+ let read = 0;
6444
+ let space = -1;
6445
+ let scheme = -1;
6446
+ return (start) => {
6447
+ for (; read < start; read++) {
6448
+ if (endsLink2(text, read)) {
6449
+ space = read;
6450
+ } else if (text[read] === "/" && text[read - 1] === "/" && text[read - 2] === ":") {
6451
+ scheme = read;
6452
+ }
6453
+ }
6454
+ return scheme > space;
6455
+ };
6456
+ }
6457
+ function isSchemeless(text, start) {
6458
+ if (start < 2 || text[start - 2] !== "/") {
6459
+ return false;
6460
+ }
6461
+ if (start === 2) {
6462
+ return true;
6463
+ }
6464
+ const before = text[start - 3];
6465
+ return !(before === ":" || before === "/" || isHostCode2(text.codePointAt(start - 3)));
6466
+ }
6467
+ function bareName(name) {
6468
+ const labels = name.split(".");
6469
+ const tld = labels.pop();
6470
+ if (!/^(?:[a-z]{2,24}|xn--[a-z\d-]{1,59})$/i.test(tld)) {
6471
+ return null;
6472
+ }
6473
+ const names = [];
6474
+ while (labels.length > 0 && names.length < 11) {
6475
+ const label = labels.pop();
6476
+ if (!/^[a-z\d-]{1,63}$/i.test(label)) {
6477
+ break;
6478
+ }
6479
+ names.unshift(label);
6480
+ }
6481
+ if (names.length === 0) {
6482
+ return null;
6483
+ }
6484
+ names[0] = names[0].replace(/^-+/, "");
6485
+ if (!names[0]) {
6486
+ return null;
6487
+ }
6488
+ return `${names.join(".")}.${tld}`.toLowerCase();
6489
+ }
6490
+ function isRealName(host) {
6491
+ const parsed = (0, import_tldts4.parse)(host);
6492
+ return Boolean(parsed.isIcann && parsed.domain);
6493
+ }
6494
+ function bareHosts(text, add, { done, seen, from = 0, cutEnd = false }) {
6495
+ let previous = -1;
6496
+ const inLink = linkReader(text);
6497
+ for (let slash = text.indexOf("/"); slash !== -1; slash = text.indexOf("/", slash + 1)) {
6498
+ if (done()) {
6499
+ return;
6500
+ }
6501
+ const floor = Math.max(previous + 1, slash - MAX_HOST);
6502
+ previous = slash;
6503
+ let end = slash;
6504
+ while (end > floor && text.codePointAt(end - 1) >= 48 && text.codePointAt(end - 1) <= 57) {
6505
+ end--;
6506
+ }
6507
+ end = end > floor && text[end - 1] === ":" ? end - 1 : slash;
6508
+ let start = end;
6509
+ while (start > floor && isHostCode2(text.codePointAt(start - 1))) {
6510
+ start--;
6511
+ }
6512
+ if (start === end || start < from || cutEnd && slash === text.length - 1) {
6513
+ continue;
6514
+ }
6515
+ if (start > 0 && (IN_WORD2.test(text[start - 1]) || text[start - 1] === "/" && !isSchemeless(text, start)) || inLink(start)) {
6516
+ continue;
6517
+ }
6518
+ while (end > start && text[end - 1] === ".") {
6519
+ end--;
6520
+ }
6521
+ const host = bareName(text.slice(start, end));
6522
+ if (!host || seen.has(host)) {
6523
+ continue;
6524
+ }
6525
+ if (seen.size < MAX_SEEN) {
6526
+ seen.add(host);
6527
+ }
6528
+ if (isRealName(host)) {
6529
+ add(`http://${host}/`);
6530
+ }
6531
+ }
6532
+ }
6533
+ function looseHosts(text, add, { from = 0, cutEnd = false, seen, done }) {
6534
+ const inLink = linkReader(text);
6535
+ const { length } = text;
6536
+ let i = 0;
6537
+ while (i < length) {
6538
+ if (!isHostCode2(text.codePointAt(i))) {
6539
+ i++;
6540
+ continue;
6541
+ }
6542
+ const start = i;
6543
+ while (i < length && isHostCode2(text.codePointAt(i))) {
6544
+ i++;
6545
+ }
6546
+ let end = i;
6547
+ if (end - start > MAX_HOST || start < from || cutEnd && end === length) {
6548
+ continue;
6549
+ }
6550
+ while (end > start && text[end - 1] === ".") {
6551
+ end--;
6552
+ }
6553
+ const before = start > 0 ? text[start - 1] : "";
6554
+ const after = end < length ? text[end] : "";
6555
+ if (after === "/" || after === "@" || before === "@") {
6556
+ continue;
6557
+ }
6558
+ if (before && (IN_WORD2.test(before) || before === "/" || before === "\\" || before === "&" || before === "#")) {
6559
+ continue;
6560
+ }
6561
+ if (after && after !== "." && IN_WORD2.test(after)) {
6562
+ continue;
6563
+ }
6564
+ const name = text.slice(start, end);
6565
+ if (!name.includes(".") || inLink(start)) {
6566
+ continue;
6567
+ }
6568
+ const lower = name.toLowerCase();
6569
+ if (seen.has(lower)) {
6570
+ continue;
6571
+ }
6572
+ if (seen.size < MAX_SEEN) {
6573
+ seen.add(lower);
6574
+ }
6575
+ if (done()) {
6576
+ return;
6577
+ }
6578
+ const host = bareName(name);
6579
+ if (host && isRealName(host)) {
6580
+ add(host);
6581
+ }
6582
+ }
6583
+ }
6584
+ function* chunksOf(html2) {
6585
+ let start = 0;
6586
+ let cutStart = false;
6587
+ while (start < html2.length) {
6588
+ const end = Math.min(html2.length, start + DECODE_CHUNK);
6589
+ if (end < html2.length) {
6590
+ let cut = end;
6591
+ while (cut > end - 65536 && cut > start) {
6592
+ if (html2[cut] === "<" || isSpace(html2.codePointAt(cut))) {
6593
+ break;
6594
+ }
6595
+ cut--;
6596
+ }
6597
+ if (cut > start && cut > end - 65536) {
6598
+ yield { text: html2.slice(start, cut), cutStart, cutEnd: false };
6599
+ start = cut;
6600
+ cutStart = false;
6601
+ continue;
6602
+ }
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;
6614
+ cutStart = true;
6615
+ continue;
6616
+ }
6617
+ yield { text: html2.slice(start, end), cutStart, cutEnd: false };
6618
+ start = end;
6619
+ }
6620
+ }
6621
+ function withoutNul(text) {
6622
+ return text.includes("\0") ? `${text}
6623
+ ${text.replaceAll("\0", "")}` : text;
6624
+ }
6625
+ function hexValue(code) {
6626
+ if (code >= 48 && code <= 57) {
6627
+ return code - 48;
6628
+ }
6629
+ if (code >= 65 && code <= 70) {
6630
+ return code - 55;
6631
+ }
6632
+ if (code >= 97 && code <= 102) {
6633
+ return code - 87;
6634
+ }
6635
+ return -1;
6636
+ }
6637
+ function percentDecoded(text) {
6638
+ const input = import_node_buffer7.Buffer.from(text);
6639
+ const output = import_node_buffer7.Buffer.allocUnsafe(input.length);
6640
+ let length = 0;
6641
+ for (let i = 0; i < input.length; i++) {
6642
+ const high = input[i] === 37 ? hexValue(input[i + 1]) : -1;
6643
+ const low = high === -1 ? -1 : hexValue(input[i + 2]);
6644
+ if (low === -1) {
6645
+ output[length++] = input[i];
6646
+ continue;
6647
+ }
6648
+ output[length++] = high * 16 + low;
6649
+ i += 2;
6650
+ }
6651
+ return output.subarray(0, length);
6652
+ }
6653
+ function swapped(bytes) {
6654
+ return import_node_buffer7.Buffer.from(bytes.subarray(0, bytes.length - bytes.length % 2)).swap16().toString("utf16le");
6655
+ }
6656
+ function decodeText(content, charset = "") {
6657
+ if (content[0] === 255 && content[1] === 254) {
6658
+ return content.subarray(2).toString("utf16le");
6659
+ }
6660
+ if (content[0] === 254 && content[1] === 255) {
6661
+ return swapped(content.subarray(2));
6662
+ }
6663
+ if (UTF16LE.has(charset)) {
6664
+ return content.toString("utf16le");
6665
+ }
6666
+ return UTF16BE.has(charset) ? swapped(content) : content.toString("utf8");
6667
+ }
6668
+ function dataDocument(value) {
6669
+ const match = DATA_DOCUMENT.exec(value.slice(0, 1024));
6670
+ if (!match) {
6671
+ return null;
6672
+ }
6673
+ const bytes = percentDecoded(value.slice(match[0].length));
6674
+ const content = /;\s*base64\s*$/i.test(match[1]) ? import_node_buffer7.Buffer.from(bytes.toString("latin1"), "base64") : bytes;
6675
+ const charset = /;\s*charset\s*=\s*"?([^;"\s]+)/i.exec(match[1]);
6676
+ return decodeText(content, charset ? charset[1].toLowerCase() : "");
6677
+ }
6678
+ function readEscapes(text, lenient = false) {
6679
+ let index = text.indexOf("\\");
6680
+ if (index === -1) {
6681
+ return text;
6682
+ }
6683
+ let out = "";
6684
+ let parts = [];
6685
+ let start = 0;
6686
+ while (index !== -1 && index + 1 < text.length) {
6687
+ const next = text[index + 1];
6688
+ if (lenient || "nN,;\\".includes(next)) {
6689
+ parts.push(text.slice(start, index), next === "n" || next === "N" ? "\n" : next);
6690
+ start = index + 2;
6691
+ index = text.indexOf("\\", start);
6692
+ if (parts.length >= 65536) {
6693
+ out += parts.join("");
6694
+ parts = [];
6695
+ }
6696
+ } else {
6697
+ index = text.indexOf("\\", index + 1);
6698
+ }
6699
+ }
6700
+ parts.push(text.slice(start));
6701
+ return out + parts.join("");
6702
+ }
6703
+ function valueColon(line) {
6704
+ let colon = line.indexOf(":");
6705
+ let from = 0;
6706
+ for (; ; ) {
6707
+ const quote = line.indexOf('"', from);
6708
+ if (quote === -1 || colon === -1 || colon < quote) {
6709
+ return colon;
6710
+ }
6711
+ const end = line.indexOf('"', quote + 1);
6712
+ if (end === -1) {
6713
+ return -1;
6714
+ }
6715
+ from = end + 1;
6716
+ if (colon < from) {
6717
+ colon = line.indexOf(":", from);
6718
+ }
6719
+ }
6720
+ }
6721
+ function trimSpaceEnd(text) {
6722
+ let end = text.length;
6723
+ while (end > 0 && (text[end - 1] === " " || text[end - 1] === " ")) {
6724
+ end--;
6725
+ }
6726
+ return end === text.length ? text : text.slice(0, end);
6727
+ }
6728
+ function decodeQuotedPrintable(value, charset) {
6729
+ let decoder;
6730
+ try {
6731
+ decoder = new TextDecoder(charset || "utf8");
6732
+ } catch {
6733
+ decoder = new TextDecoder("utf8");
6734
+ }
6735
+ if (!/[\u0080-\u{10FFFF}]/u.test(value)) {
6736
+ const bytes = import_node_buffer7.Buffer.from(value, "latin1");
6737
+ let length = 0;
6738
+ for (let index = 0; index < bytes.length; index++) {
6739
+ const hex = bytes[index] === 61 ? value.slice(index + 1, index + 3) : "";
6740
+ if (/^[\da-f]{2}$/i.test(hex)) {
6741
+ bytes[length++] = Number.parseInt(hex, 16);
6742
+ index += 2;
6743
+ } else {
6744
+ bytes[length++] = bytes[index];
6745
+ }
6746
+ }
6747
+ return decoder.decode(bytes.subarray(0, length));
6748
+ }
6749
+ return value.replaceAll(/(?:=[\da-f]{2})+/gi, (run) => decoder.decode(import_node_buffer7.Buffer.from(run.replaceAll("=", ""), "hex")));
6750
+ }
6751
+ function quotedPrintableValues(text) {
6752
+ if (!/quoted-printable/i.test(text)) {
6753
+ return text;
6754
+ }
6755
+ const lines2 = text.split(LINE_BREAK);
6756
+ const out = [];
6757
+ for (let i = 0; i < lines2.length; i++) {
6758
+ const line = lines2[i];
6759
+ const colon = valueColon(line);
6760
+ const parameters = colon === -1 ? "" : line.slice(0, colon);
6761
+ if (colon === -1 || !QUOTED_PRINTABLE.test(parameters)) {
6762
+ out.push(line);
6763
+ continue;
6764
+ }
6765
+ const charset = /;\s*charset\s*=\s*"?([\w.:-]+)/i.exec(parameters)?.[1];
6766
+ const parts = [trimSpaceEnd(line.slice(colon + 1))];
6767
+ while (parts.at(-1).endsWith("=") && i + 1 < lines2.length) {
6768
+ parts[parts.length - 1] = parts.at(-1).slice(0, -1);
6769
+ parts.push(trimSpaceEnd(lines2[++i]));
6770
+ }
6771
+ out.push(line.slice(0, colon + 1) + decodeQuotedPrintable(parts.join(""), charset));
6772
+ }
6773
+ return out.join("\n");
6774
+ }
6775
+ function shownText(text) {
6776
+ const unfolded = text.replaceAll(FOLD, "");
6777
+ const shown = /* @__PURE__ */ new Set([text, readEscapes(unfolded)]);
6778
+ const decoded = quotedPrintableValues(unfolded);
6779
+ if (decoded !== unfolded) {
6780
+ shown.add(readEscapes(decoded));
6781
+ if (unfolded !== text) {
6782
+ shown.add(quotedPrintableValues(text));
6783
+ }
6784
+ }
6785
+ if (/\\[^nN,;\\]/.test(decoded)) {
6786
+ shown.add(readEscapes(decoded, true));
6787
+ }
6788
+ return [...shown].reverse().join("\n");
6789
+ }
6790
+ function htmlDescriptions(text) {
6791
+ if (!/[<&]|altrep/i.test(text)) {
6792
+ return "";
6793
+ }
6794
+ const html2 = [];
6795
+ for (const line of text.replaceAll(FOLD, "").split(LINE_BREAK)) {
6796
+ const colon = valueColon(line);
6797
+ if (colon === -1) {
6798
+ continue;
6799
+ }
6800
+ const altrep = /;\s*altrep\s*=\s*"([^"]*)"/i.exec(line.slice(0, colon))?.[1];
6801
+ if (altrep) {
6802
+ html2.push(`<iframe src="${altrep.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;")}"></iframe>`);
6803
+ }
6804
+ if (!HTML_PROPERTIES.test(line)) {
6805
+ continue;
6806
+ }
6807
+ const value = readEscapes(line.slice(colon + 1));
6808
+ if (/[<&]/.test(value)) {
6809
+ html2.push(value);
6810
+ }
6811
+ }
6812
+ return html2.join("\n");
6813
+ }
6814
+ function withoutBinaryValues(text) {
6815
+ if (!/base64|binary|encoding\s*=\s*"?b|;\s*b\s*[;:]/i.test(text)) {
6816
+ return text;
6817
+ }
6818
+ const lines2 = text.split(LINE_BREAK);
6819
+ const out = [];
6820
+ for (let i = 0; i < lines2.length; i++) {
6821
+ const line = lines2[i];
6822
+ const colon = valueColon(line);
6823
+ if (colon === -1 || !BINARY.test(line.slice(0, colon))) {
6824
+ out.push(line);
6825
+ continue;
6826
+ }
6827
+ const value = [line.slice(colon + 1)];
6828
+ let next = i + 1;
6829
+ while (next < lines2.length && (lines2[next].startsWith(" ") || lines2[next].startsWith(" "))) {
6830
+ value.push(lines2[next++]);
6831
+ }
6832
+ if (!value.every((part) => /^[\w+/=\s]*$/.test(part))) {
6833
+ for (let index = i; index < next; index++) {
6834
+ out.push(lines2[index]);
6835
+ }
6836
+ i = next - 1;
6837
+ continue;
6838
+ }
6839
+ while (next < lines2.length && lines2[next] !== "" && /^[\w+/=]+$/.test(lines2[next])) {
6840
+ value.push(lines2[next++]);
6841
+ }
6842
+ if (next < lines2.length && lines2[next] === "") {
6843
+ next++;
6844
+ }
6845
+ i = next - 1;
6846
+ const name = line.slice(0, colon).split(";")[0].split(".").pop().trim().toLowerCase();
6847
+ out.push(MEDIA_PROPERTIES.has(name) ? line.slice(0, colon + 1) : line.slice(0, colon + 1) + import_node_buffer7.Buffer.from(value.join("").replaceAll(/\s/g, ""), "base64").toString("utf8"));
6848
+ }
6849
+ return out.join("\n");
6850
+ }
6851
+ function extensionOf2(filename) {
6852
+ let end = filename.length;
6853
+ while (end > 0 && (filename[end - 1] === "." || filename[end - 1] === " ")) {
6854
+ end--;
6855
+ }
6856
+ const name = filename.slice(0, end).trim().toLowerCase();
6857
+ const index = name.lastIndexOf(".");
6858
+ return index !== -1 && index < name.length - 1 ? name.slice(index + 1) : null;
6859
+ }
6860
+ function extensionsOf(attachment) {
6861
+ const names = [];
6862
+ if (typeof attachment.filename === "string") {
6863
+ names.push(attachment.filename);
6864
+ }
6865
+ const { headers } = attachment;
6866
+ if (headers && typeof headers.get === "function") {
6867
+ for (const [field, parameter] of [["content-type", "name"], ["content-disposition", "filename"]]) {
6868
+ const value = headers.get(field)?.params?.[parameter];
6869
+ if (typeof value === "string" && value !== "") {
6870
+ names.push(value);
6871
+ }
6872
+ }
6873
+ }
6874
+ return new Set(names.flatMap((name) => [extensionOf2(name), extensionOf(name)]).filter(Boolean));
6875
+ }
6876
+ function charsetOf(attachment) {
6877
+ const { headers } = attachment;
6878
+ const charset = headers && typeof headers.get === "function" ? headers.get("content-type")?.params?.charset : null;
6879
+ return typeof charset === "string" ? charset.toLowerCase() : "";
6880
+ }
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 = [];
6899
+ let bytes = 0;
6900
+ let opened = 0;
6901
+ for (const attachment of attachments) {
6902
+ const kind = kindOf(attachment);
6903
+ if (!kind) {
6904
+ continue;
6905
+ }
6906
+ if (!kind.isText && ++opened > MAX_ATTACHED || bytes + attachment.content.length > MAX_ATTACHED_BYTES) {
6907
+ state.tooComplex = true;
6908
+ break;
6909
+ }
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;
6917
+ }
6918
+ const text = decodeText(attachment.content, charsetOf(attachment));
6919
+ if (isHtml) {
6920
+ yield { html: text };
6921
+ continue;
6922
+ }
6923
+ if (!isCard) {
6924
+ yield { text };
6925
+ continue;
6926
+ }
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;
6940
+ }
6941
+ const html2 = htmlDescriptions(readable);
6942
+ if (html2 !== "") {
6943
+ yield { html: html2 };
6944
+ }
6945
+ }
6946
+ }
6947
+ function carriedLinkHosts(mail, {
6948
+ mails = [],
6949
+ attachments = [],
6950
+ maxHosts = 250,
6951
+ hostsInText = false
6952
+ } = {}) {
6953
+ const hosts = /* @__PURE__ */ new Set();
6954
+ const loose = /* @__PURE__ */ new Set();
6955
+ const seen = /* @__PURE__ */ new Set();
6956
+ const seenBare = /* @__PURE__ */ new Set();
6957
+ const seenLoose = /* @__PURE__ */ new Set();
6958
+ let more = false;
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;
6972
+ const addLoose = (host) => {
6973
+ if (!looseDone()) {
6974
+ loose.add(host);
6975
+ }
6976
+ };
6977
+ const queue = [mail, ...mails].map((document) => ({ document, depth: 0 }));
6978
+ const carried = documentsOf(attachments, state, late);
6979
+ let depth = 0;
6980
+ let embedded = 0;
6981
+ let embeddedLength = 0;
6982
+ const embeddedSeen = /* @__PURE__ */ new Set();
6983
+ const embed = (html2) => {
6984
+ if (html2 === "" || embeddedSeen.has(html2)) {
6985
+ return;
6986
+ }
6987
+ if (depth >= MAX_EMBED_DEPTH || embedded >= MAX_EMBEDDED || embeddedLength + html2.length > MAX_EMBEDDED_LENGTH) {
6988
+ state.tooComplex = true;
6989
+ return;
6990
+ }
6991
+ embeddedSeen.add(html2);
6992
+ embedded++;
6993
+ embeddedLength += html2.length;
6994
+ queue.push({ document: { html: html2 }, depth: depth + 1 });
6995
+ };
6996
+ const add = (value) => {
6997
+ if (full || typeof value !== "string" || value === "" || seen.has(value)) {
6998
+ return;
6999
+ }
7000
+ if (seen.size < MAX_SEEN) {
7001
+ seen.add(value);
7002
+ }
7003
+ if (/^\s*data:/i.test(value)) {
7004
+ const html2 = dataDocument(value);
7005
+ if (html2 !== null) {
7006
+ embed(html2);
7007
+ }
7008
+ return;
7009
+ }
7010
+ const found = hostsOf(value);
7011
+ if (found.unresolved) {
7012
+ state.tooComplex = true;
7013
+ }
7014
+ for (const host of found) {
7015
+ if (hosts.has(host)) {
7016
+ continue;
7017
+ }
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;
7027
+ return;
7028
+ }
7029
+ }
7030
+ };
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 } = {}) => {
7049
+ for (const match of source.matchAll(URL_START)) {
7050
+ if (done()) {
7051
+ break;
7052
+ }
7053
+ if (cutStart && match.index === 0) {
7054
+ continue;
7055
+ }
7056
+ if (cutEnd && match.index + match[0].length === source.length) {
7057
+ if (exact && source.length - match.index < CHUNK_OVERLAP) {
7058
+ continue;
7059
+ }
7060
+ const value = match[0];
7061
+ const at = /^www\./i.test(value) ? 0 : value.indexOf("//") + 2;
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;
7070
+ }
7071
+ continue;
7072
+ }
7073
+ add(match[0]);
7074
+ }
7075
+ const from = cutStart ? 1 : 0;
7076
+ bareHosts(source, add, {
7077
+ done,
7078
+ seen: seenBare,
7079
+ from,
7080
+ cutEnd
7081
+ });
7082
+ if (hostsInText && !looseDone()) {
7083
+ looseHosts(source, addLoose, {
7084
+ from,
7085
+ cutEnd,
7086
+ seen: seenLoose,
7087
+ done: looseDone
7088
+ });
7089
+ }
7090
+ };
7091
+ for (let index = 0; ; index++) {
7092
+ if (full || late()) {
7093
+ break;
7094
+ }
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];
7104
+ const html2 = withoutNul(typeof document?.html === "string" ? document.html : "");
7105
+ const text = withoutNul(typeof document?.text === "string" ? document.text : "");
7106
+ for (const [name, value] of attributesOf(html2)) {
7107
+ if (done()) {
7108
+ break;
7109
+ }
7110
+ if (!LINK_ATTRIBUTES.has(name)) {
7111
+ continue;
7112
+ }
7113
+ if (spend(value.length)) {
7114
+ break;
7115
+ }
7116
+ linksOf(name, value, reader);
7117
+ }
7118
+ lenientLinks(html2, done, reader);
7119
+ for (const pattern of [CSS_URL, CSS_IMPORT]) {
7120
+ for (const [, link] of html2.matchAll(pattern)) {
7121
+ if (done() || spend(link.length)) {
7122
+ break;
7123
+ }
7124
+ if (link.length > MAX_VALUE2) {
7125
+ state.tooComplex = true;
7126
+ }
7127
+ add(decodeEntities(link.slice(0, MAX_VALUE2), { attribute: true }));
7128
+ }
7129
+ }
7130
+ for (const chunk2 of chunksOf(text)) {
7131
+ if (full || late()) {
7132
+ break;
7133
+ }
7134
+ readText(chunk2.text, { ...chunk2, exact: true });
7135
+ }
7136
+ for (const chunk2 of chunksOf(html2)) {
7137
+ if (full || late()) {
7138
+ break;
7139
+ }
7140
+ const decoded = decodeEntities(chunk2.text);
7141
+ readText(decoded, { ...chunk2, exact: decoded.endsWith(chunk2.text.slice(-CHUNK_OVERLAP)) });
7142
+ }
7143
+ }
7144
+ if (!more && !state.incomplete) {
7145
+ for (const name of loose) {
7146
+ if (hosts.size >= maxHosts) {
7147
+ break;
7148
+ }
7149
+ const [host] = hostsOf(`http://${name}/`);
7150
+ if (host) {
7151
+ hosts.add(host);
7152
+ }
7153
+ }
7154
+ }
7155
+ return {
7156
+ hosts: [...hosts],
7157
+ more,
7158
+ tooComplex: state.tooComplex,
7159
+ incomplete: state.incomplete
7160
+ };
7161
+ }
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;
7163
+ var init_links = __esm({
7164
+ "src/links.js"() {
7165
+ import_node_buffer7 = require("node:buffer");
7166
+ import_node_net4 = require("node:net");
7167
+ import_tldts4 = require("tldts");
7168
+ init_attachments();
7169
+ init_dnsbl();
7170
+ init_html();
7171
+ init_tokenizer();
7172
+ LINK_ATTRIBUTES = /* @__PURE__ */ new Set([
7173
+ "href",
7174
+ "src",
7175
+ "srcset",
7176
+ "background",
7177
+ "action",
7178
+ "formaction",
7179
+ "poster",
7180
+ "cite",
7181
+ "longdesc",
7182
+ "xlink:href",
7183
+ "style",
7184
+ "data",
7185
+ "ping",
7186
+ // (of a <meta> refresh: "0;url=https://example.com/")
7187
+ "content",
7188
+ // (a document of its own, see linksOf)
7189
+ "srcdoc"
7190
+ ]);
7191
+ TAG_START = /<[a-z][^\s/>]*/gi;
7192
+ URL_START = /\b(?:https?:\/\/|www\.)[^\s<>"'`]+/gi;
7193
+ CSS_URL = /url\(\s*["']?([^()"']*)/gi;
7194
+ CSS_IMPORT = /@import\s+["']([^"'<>]*)/gi;
7195
+ LENIENT = /[\s"'/<](href|src|srcset|background|action|formaction|poster|cite|longdesc|xlink:href|style|data|ping|content|srcdoc)\s*=\s*/gi;
7196
+ IN_WORD2 = /[\p{L}\p{N}\p{M}_%]/u;
7197
+ NAMESPACES = /* @__PURE__ */ new Set(["www.w3.org", "w3.org"]);
7198
+ MAX_HOST = 760;
7199
+ MAX_SEEN = 1e5;
7200
+ TRAILING = /* @__PURE__ */ new Set([")", ".", ",", ";", ":", "!", "?", "'", '"', "]"]);
7201
+ DECODE_CHUNK = 1024 * 1024;
7202
+ CHUNK_OVERLAP = 4096;
7203
+ MAX_EMBED_DEPTH = 3;
7204
+ MAX_EMBEDDED = 1e3;
7205
+ MAX_EMBEDDED_LENGTH = 10 * 1024 * 1024;
7206
+ MAX_ATTACHED = 100;
7207
+ MAX_ATTACHED_BYTES = 50 * 1024 * 1024;
7208
+ MAX_READ_TIME = 5e3;
7209
+ MAX_CARD_BYTES = 4 * 1024 * 1024;
7210
+ MAX_SHOWN_BYTES = 1024 * 1024;
7211
+ MAX_VALUE2 = 1024 * 1024;
7212
+ LONG_VALUE = 64 * 1024;
7213
+ MAX_LENIENT_VALUE = 4096;
7214
+ UNQUOTED_END = /* @__PURE__ */ new Set(["<", ">", '"', "'", "`", "="]);
7215
+ UTF16LE = /* @__PURE__ */ new Set(["csunicode", "iso-10646-ucs-2", "ucs-2", "unicode", "unicodefeff", "utf-16", "utf-16le"]);
7216
+ UTF16BE = /* @__PURE__ */ new Set(["unicodefffe", "utf-16be"]);
7217
+ DATA_DOCUMENT = /^\s*data:\s*(?:text\/html|application\/xhtml\+xml|image\/svg\+xml|text\/xml|application\/xml)\b([^,]*),/i;
7218
+ LINE_BREAK = /\r\n?|\n/;
7219
+ FOLD = /(?:\r\n?|\n)[ \t]/g;
7220
+ QUOTED_PRINTABLE = /;\s*(?:encoding\s*=\s*)?"?quoted-printable"?\s*(?:;|$)/i;
7221
+ MEDIA_PROPERTIES = /* @__PURE__ */ new Set(["photo", "logo", "sound", "key", "attach"]);
7222
+ HTML_PROPERTIES = /^(?:x-alt-desc|styled-description|description|comment)[;:]/i;
7223
+ BINARY = /;\s*(?:(?:encoding\s*=\s*)?"?(?:b|base64)"?|value\s*=\s*"?binary"?)\s*(?:;|$)/i;
7224
+ HTML_TYPES = /* @__PURE__ */ new Set(["text/html", "application/xhtml+xml", "image/svg+xml", "text/xml", "application/xml"]);
7225
+ HTML_EXTENSIONS = /* @__PURE__ */ new Set(["html", "htm", "shtml", "xhtml", "xht", "svg", "xml"]);
7226
+ CARD_TYPES = /* @__PURE__ */ new Set(["text/calendar", "text/x-vcalendar", "application/ics", "text/vcard", "text/x-vcard", "text/directory"]);
7227
+ CARD_EXTENSIONS = /* @__PURE__ */ new Set(["ics", "ical", "ifb", "vcs", "vcf", "vcard"]);
7228
+ MESSAGE_TYPES = /* @__PURE__ */ new Set(["message/rfc822", "message/global"]);
7229
+ LATE_EVERY = 1024;
7230
+ }
7231
+ });
7232
+
6018
7233
  // src/is-arbitrary.js
6019
7234
  function unquoted(text) {
6020
7235
  let start = 0;
@@ -6079,13 +7294,13 @@ function joinsDisplay(brand, display, name) {
6079
7294
  return false;
6080
7295
  }
6081
7296
  function isOfficial({ brand, official }, domain, display, shared = true) {
6082
- const parsed = (0, import_tldts4.parse)(domain);
7297
+ const parsed = (0, import_tldts5.parse)(domain);
6083
7298
  const name = parsed.domainWithoutSuffix ?? "";
6084
7299
  const compact = brand.replaceAll(" ", "");
6085
7300
  if (parsed.publicSuffix === compact || compact.length >= 5 && joinsDisplay(brand, display, name) && !JOINED_WORDS.some((word) => name.slice(compact.length).includes(word))) {
6086
7301
  return true;
6087
7302
  }
6088
- return official.some((entry) => entry instanceof RegExp ? entry.test(registrableDomain(domain)) : entry.includes(".") ? (shared || (0, import_tldts4.parse)(entry).domain !== null) && (domain === entry || domain.endsWith(`.${entry}`)) : name === entry);
7303
+ return official.some((entry) => entry instanceof RegExp ? entry.test(registrableDomain(domain)) : entry.includes(".") ? (shared || (0, import_tldts5.parse)(entry).domain !== null) && (domain === entry || domain.endsWith(`.${entry}`)) : name === entry);
6089
7304
  }
6090
7305
  function headerString(mail, name) {
6091
7306
  const value = mail.headers?.get?.(name);
@@ -6187,7 +7402,7 @@ ${withoutHelp(markup)}`) || isInjection(readable) || attributeText(markup).some(
6187
7402
  }
6188
7403
  } else if (claimed && isWholeName(name, claimed)) {
6189
7404
  const domain = claimed[1];
6190
- const brand = MAILBOXES.has((0, import_tldts4.parse)(domain).domainWithoutSuffix) ? null : DISPLAY_BRANDS.find((entry) => isOfficial(entry, domain, "", false) && !isOfficial(entry, session.originalFromAddressDomain, ""));
7405
+ const brand = MAILBOXES.has((0, import_tldts5.parse)(domain).domainWithoutSuffix) ? null : DISPLAY_BRANDS.find((entry) => isOfficial(entry, domain, "", false) && !isOfficial(entry, session.originalFromAddressDomain, ""));
6191
7406
  if (brand) {
6192
7407
  hit("FROM_NAME_BRAND", 2, `Display name is an address of "${brand.brand}" but the message is from ${fromRoot}`, "PHISHING");
6193
7408
  }
@@ -6220,10 +7435,10 @@ ${withoutHelp(markup)}`) || isInjection(readable) || attributeText(markup).some(
6220
7435
  category
6221
7436
  };
6222
7437
  }
6223
- var import_tldts4, GTUBE, BLOCKED_SUBJECT, HACKED_SUBJECT, COIN, CODE, CURRENCY, MONEY, CRYPTOCURRENCY, AMOUNT_DEMAND, AMOUNT_GIVEN, HARM_VERB, HARMED, RECORDING, THREAT, HOURS, COUNT, DEADLINE, isPaymentDemand, BITCOIN_ADDRESS, IGNORE_INSTRUCTIONS, AI_ACTION, AI_OBJECT, AI_ACTION_OBJECT, AI_NEAR, VERDICT, VERDICT_WORD, VERDICT_ORDER, VERDICT_CLAIM, CLASSIFY, SYSTEM_PROMPT, YOU_ARE, QUOTE, CONDITION, DISCUSSED, DISCUSSED_SHOWN, DISCUSSED_NEAR, isDiscussed, isInjection, QUOTE_OR_SPACE, SHOWN_NEAR, isOrder, PAYPAL_DOMAIN, PAYPAL_SPAM_TYPE_IDS, PAYPAL_OWN_REQUEST, PAYPAL_INVOICE_SUBJECT, MS_SPAM_VERDICT, MS_SPAM_CATEGORY, APPLE_NEXT, APPLE_FILLER, APPLE_STRONG, APPLE_BUSINESS, APPLE_ALONE, APPLE_BESIDE, APPLE_PART, NEXT_WORD, MAX_AFTER, APPLE, DISPLAY_BRANDS, ADDRESSES, LIST_HOSTS, VIA_LIST, MAILBOXES, BEFORE_ADDRESS, AFTER_ADDRESS, isWholeName, JOINED_WORDS, MAX_DISPLAY;
7438
+ var import_tldts5, GTUBE, BLOCKED_SUBJECT, HACKED_SUBJECT, COIN, CODE, CURRENCY, MONEY, CRYPTOCURRENCY, AMOUNT_DEMAND, AMOUNT_GIVEN, HARM_VERB, HARMED, RECORDING, THREAT, HOURS, COUNT, DEADLINE, isPaymentDemand, BITCOIN_ADDRESS, IGNORE_INSTRUCTIONS, AI_ACTION, AI_OBJECT, AI_ACTION_OBJECT, AI_NEAR, VERDICT, VERDICT_WORD, VERDICT_ORDER, VERDICT_CLAIM, CLASSIFY, SYSTEM_PROMPT, YOU_ARE, QUOTE, CONDITION, DISCUSSED, DISCUSSED_SHOWN, DISCUSSED_NEAR, isDiscussed, isInjection, QUOTE_OR_SPACE, SHOWN_NEAR, isOrder, PAYPAL_DOMAIN, PAYPAL_SPAM_TYPE_IDS, PAYPAL_OWN_REQUEST, PAYPAL_INVOICE_SUBJECT, MS_SPAM_VERDICT, MS_SPAM_CATEGORY, APPLE_NEXT, APPLE_FILLER, APPLE_STRONG, APPLE_BUSINESS, APPLE_ALONE, APPLE_BESIDE, APPLE_PART, NEXT_WORD, MAX_AFTER, APPLE, DISPLAY_BRANDS, ADDRESSES, LIST_HOSTS, VIA_LIST, MAILBOXES, BEFORE_ADDRESS, AFTER_ADDRESS, isWholeName, JOINED_WORDS, MAX_DISPLAY;
6224
7439
  var init_is_arbitrary = __esm({
6225
7440
  "src/is-arbitrary.js"() {
6226
- import_tldts4 = require("tldts");
7441
+ import_tldts5 = require("tldts");
6227
7442
  init_homograph();
6228
7443
  init_html();
6229
7444
  init_linear();
@@ -6474,7 +7689,7 @@ function authHeaders(config) {
6474
7689
  case "basic": {
6475
7690
  const user = config.username ?? "";
6476
7691
  const pass = config.password ?? config.apiKey ?? "";
6477
- headers.authorization = `Basic ${import_node_buffer7.Buffer.from(`${user}:${pass}`).toString("base64")}`;
7692
+ headers.authorization = `Basic ${import_node_buffer8.Buffer.from(`${user}:${pass}`).toString("base64")}`;
6478
7693
  break;
6479
7694
  }
6480
7695
  case "x-api-key": {
@@ -6921,10 +8136,10 @@ function replyText(api, reply2) {
6921
8136
  }
6922
8137
  return reply2?.choices?.[0]?.message?.content || null;
6923
8138
  }
6924
- var import_node_buffer7, import_node_crypto2, import_node_os, import_node_process, PROVIDERS, DECISION_APIS, TOP_LOGPROBS, VERDICTS, VERDICT_CRITERIA, SIGNS, GUIDE, DEFAULT_SYSTEM_PROMPT, DECISION_SYSTEM_PROMPT, DECISION_INSTRUCTIONS, LOCALHOST, PRIVATE_HOST, REDACTIONS, THINK_OPEN, THINK_CLOSE, ANSWER_FIRST, LOCAL_CHARACTER, BREAKS2, VERDICT_WORD2, NOT, DENIED, JOINED, SPAM_LABEL, HAM_WORDS, HAM_LABEL, NOT_HAM_LABEL, UNWANTED, MIN_VERDICT_MASS, percent, MODES, LLMClassifier;
8139
+ var import_node_buffer8, import_node_crypto2, import_node_os, import_node_process, PROVIDERS, DECISION_APIS, TOP_LOGPROBS, VERDICTS, VERDICT_CRITERIA, SIGNS, GUIDE, DEFAULT_SYSTEM_PROMPT, DECISION_SYSTEM_PROMPT, DECISION_INSTRUCTIONS, LOCALHOST, PRIVATE_HOST, REDACTIONS, THINK_OPEN, THINK_CLOSE, ANSWER_FIRST, LOCAL_CHARACTER, BREAKS2, VERDICT_WORD2, NOT, DENIED, JOINED, SPAM_LABEL, HAM_WORDS, HAM_LABEL, NOT_HAM_LABEL, UNWANTED, MIN_VERDICT_MASS, percent, MODES, LLMClassifier;
6925
8140
  var init_llm = __esm({
6926
8141
  "src/llm.js"() {
6927
- import_node_buffer7 = require("node:buffer");
8142
+ import_node_buffer8 = require("node:buffer");
6928
8143
  import_node_crypto2 = require("node:crypto");
6929
8144
  import_node_os = __toESM(require("node:os"), 1);
6930
8145
  import_node_process = __toESM(require("node:process"), 1);
@@ -7676,7 +8891,7 @@ var init_package = __esm({
7676
8891
  package_default = {
7677
8892
  name: "spamscanner",
7678
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.",
7679
- version: "7.0.10",
8894
+ version: "7.0.12",
7680
8895
  author: "Forward Email (https://forwardemail.net)",
7681
8896
  bin: {
7682
8897
  spamscanner: "./dist/esm/cli.js"
@@ -7865,10 +9080,10 @@ function splitMessage(raw) {
7865
9080
  const match = /^\r?\n|\r?\n\r?\n/.exec(text);
7866
9081
  if (!match) {
7867
9082
  const newline2 = text.includes("\n") && !text.includes("\r\n") ? "\n" : "\r\n";
7868
- return { header: text.replace(/\r?\n$/, ""), body: import_node_buffer8.Buffer.alloc(0), newline: newline2 };
9083
+ return { header: text.replace(/\r?\n$/, ""), body: import_node_buffer9.Buffer.alloc(0), newline: newline2 };
7869
9084
  }
7870
9085
  const newline = match[0].startsWith("\r") ? "\r\n" : "\n";
7871
- return { header: text.slice(0, match.index), body: raw.subarray(import_node_buffer8.Buffer.byteLength(text.slice(0, match.index + match[0].length), "latin1")), newline };
9086
+ return { header: text.slice(0, match.index), body: raw.subarray(import_node_buffer9.Buffer.byteLength(text.slice(0, match.index + match[0].length), "latin1")), newline };
7872
9087
  }
7873
9088
  function subjectTagText(tag, spaceAfter = false) {
7874
9089
  const line = String(tag).replaceAll(new RegExp("\\p{Cc}+", "gu"), " ").trim();
@@ -7878,14 +9093,14 @@ function subjectTagText(tag, spaceAfter = false) {
7878
9093
  const words2 = [];
7879
9094
  let chunk2 = "";
7880
9095
  for (const character of spaceAfter ? `${line} ` : line) {
7881
- if (import_node_buffer8.Buffer.byteLength(chunk2 + character, "utf8") > ENCODED_BYTES) {
9096
+ if (import_node_buffer9.Buffer.byteLength(chunk2 + character, "utf8") > ENCODED_BYTES) {
7882
9097
  words2.push(chunk2);
7883
9098
  chunk2 = "";
7884
9099
  }
7885
9100
  chunk2 += character;
7886
9101
  }
7887
9102
  words2.push(chunk2);
7888
- return words2.map((word) => `=?UTF-8?B?${import_node_buffer8.Buffer.from(word, "utf8").toString("base64")}?=`).join(" ");
9103
+ return words2.map((word) => `=?UTF-8?B?${import_node_buffer9.Buffer.from(word, "utf8").toString("base64")}?=`).join(" ");
7889
9104
  }
7890
9105
  function taggedSubject(tag, current, newline = "\r\n") {
7891
9106
  const unfolded = current.replaceAll(/\r?\n/g, "");
@@ -7916,7 +9131,7 @@ function taggedSubject(tag, current, newline = "\r\n") {
7916
9131
  return lines2.join(newline).slice("Subject: ".length);
7917
9132
  }
7918
9133
  function rewriteMessage(source, headers = [], options = {}) {
7919
- const raw = import_node_buffer8.Buffer.isBuffer(source) ? source : import_node_buffer8.Buffer.from(String(source), "utf8");
9134
+ const raw = import_node_buffer9.Buffer.isBuffer(source) ? source : import_node_buffer9.Buffer.from(String(source), "utf8");
7920
9135
  const { header, body, newline } = splitMessage(raw);
7921
9136
  const remove = options.remove ?? /^x-spam-/i;
7922
9137
  const lines2 = header === "" ? [] : header.split(/\r?\n/);
@@ -7941,15 +9156,18 @@ function rewriteMessage(source, headers = [], options = {}) {
7941
9156
  }
7942
9157
  }
7943
9158
  }
7944
- const added = headers.map(([name, value]) => foldHeader(name, import_node_buffer8.Buffer.from(String(value), "utf8").toString("latin1"), newline));
7945
- const mbox = kept.length > 0 && kept[0][0].startsWith("From ") ? kept.shift() : [];
9159
+ const added = headers.map(([name, value]) => foldHeader(name, import_node_buffer9.Buffer.from(String(value), "utf8").toString("latin1"), newline));
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
+ }
7946
9164
  const head = [...mbox, ...added, ...kept.map((field) => field.join(newline))].join(newline);
7947
- return import_node_buffer8.Buffer.concat([import_node_buffer8.Buffer.from(head === "" && header === "" ? newline : `${head}${newline}${newline}`, "latin1"), body]);
9165
+ return import_node_buffer9.Buffer.concat([import_node_buffer9.Buffer.from(head === "" ? newline : `${head}${newline}${newline}`, "latin1"), body]);
7948
9166
  }
7949
- var import_node_buffer8, ENCODED_BYTES, PLAIN_WORD, ENCODED_LINE, MAX_LINE2, ENCODED_WORD, ENCODED_START;
9167
+ var import_node_buffer9, ENCODED_BYTES, PLAIN_WORD, ENCODED_LINE, MAX_LINE2, ENCODED_WORD, ENCODED_START;
7950
9168
  var init_headers = __esm({
7951
9169
  "src/headers.js"() {
7952
- import_node_buffer8 = require("node:buffer");
9170
+ import_node_buffer9 = require("node:buffer");
7953
9171
  ENCODED_BYTES = 39;
7954
9172
  PLAIN_WORD = 64;
7955
9173
  ENCODED_LINE = 76;
@@ -8125,7 +9343,7 @@ async function* readJsonl(file, options = {}) {
8125
9343
  }
8126
9344
  async function* walk(directory) {
8127
9345
  const entries = await import_promises.default.readdir(directory, { withFileTypes: true });
8128
- entries.sort((a, b) => import_node_buffer9.Buffer.compare(import_node_buffer9.Buffer.from(a.name), import_node_buffer9.Buffer.from(b.name)));
9346
+ entries.sort((a, b) => import_node_buffer10.Buffer.compare(import_node_buffer10.Buffer.from(a.name), import_node_buffer10.Buffer.from(b.name)));
8129
9347
  for (const entry of entries) {
8130
9348
  if (SKIP.has(entry.name) || entry.name.startsWith(".")) {
8131
9349
  continue;
@@ -8150,7 +9368,7 @@ async function* readMessages(source) {
8150
9368
  const handle = await import_promises.default.open(file);
8151
9369
  let head;
8152
9370
  try {
8153
- const { buffer, bytesRead } = await handle.read(import_node_buffer9.Buffer.alloc(64), 0, 64, 0);
9371
+ const { buffer, bytesRead } = await handle.read(import_node_buffer10.Buffer.alloc(64), 0, 64, 0);
8154
9372
  head = buffer.subarray(0, bytesRead).toString("latin1");
8155
9373
  } finally {
8156
9374
  await handle.close();
@@ -8162,10 +9380,10 @@ async function* readMessages(source) {
8162
9380
  }
8163
9381
  }
8164
9382
  }
8165
- var import_node_buffer9, import_node_fs3, import_promises, import_node_path2, import_node_readline, import_node_zlib, SKIP, SPAM_LABELS, HAM_LABELS, MAX_RECORD_LINES, MAX_RECORD_LENGTH, TEXT_NAMES, LABEL_NAMES, JSONL_LABEL_NAMES, JSONL_TEXT_NAMES;
9383
+ var import_node_buffer10, import_node_fs3, import_promises, import_node_path2, import_node_readline, import_node_zlib, SKIP, SPAM_LABELS, HAM_LABELS, MAX_RECORD_LINES, MAX_RECORD_LENGTH, TEXT_NAMES, LABEL_NAMES, JSONL_LABEL_NAMES, JSONL_TEXT_NAMES;
8166
9384
  var init_sources = __esm({
8167
9385
  "src/sources.js"() {
8168
- import_node_buffer9 = require("node:buffer");
9386
+ import_node_buffer10 = require("node:buffer");
8169
9387
  import_node_fs3 = require("node:fs");
8170
9388
  import_promises = __toESM(require("node:fs/promises"), 1);
8171
9389
  import_node_path2 = __toESM(require("node:path"), 1);
@@ -8306,7 +9524,12 @@ var init_train = __esm({
8306
9524
 
8307
9525
  // src/server.js
8308
9526
  function serializeResult(result, options = {}) {
8309
- const { mail, tokens, ...rest } = result;
9527
+ const {
9528
+ mail,
9529
+ tokens,
9530
+ attached,
9531
+ ...rest
9532
+ } = result;
8310
9533
  const output = {
8311
9534
  ...rest,
8312
9535
  results: { ...result.results }
@@ -8339,8 +9562,8 @@ function sessionFromQuery(query) {
8339
9562
  return session;
8340
9563
  }
8341
9564
  function sameToken(given, expected) {
8342
- const a = import_node_buffer10.Buffer.from(String(given));
8343
- const b = import_node_buffer10.Buffer.from(String(expected));
9565
+ const a = import_node_buffer11.Buffer.from(String(given));
9566
+ const b = import_node_buffer11.Buffer.from(String(expected));
8344
9567
  return a.length === b.length && (0, import_node_crypto5.timingSafeEqual)(a, b);
8345
9568
  }
8346
9569
  function readBody(request, limit) {
@@ -8360,7 +9583,7 @@ function readBody(request, limit) {
8360
9583
  reject(error);
8361
9584
  return;
8362
9585
  }
8363
- resolve(import_node_buffer10.Buffer.concat(chunks));
9586
+ resolve(import_node_buffer11.Buffer.concat(chunks));
8364
9587
  });
8365
9588
  request.on("error", reject);
8366
9589
  });
@@ -8375,7 +9598,7 @@ function createHttpServer(scanner, options = {}) {
8375
9598
  let active = 0;
8376
9599
  const server = import_node_http2.default.createServer(async (request, response) => {
8377
9600
  const send = (status, body, headers = {}) => {
8378
- const isBuffer = import_node_buffer10.Buffer.isBuffer(body);
9601
+ const isBuffer = import_node_buffer11.Buffer.isBuffer(body);
8379
9602
  response.writeHead(status, { "content-type": isBuffer ? "message/rfc822" : "application/json; charset=utf-8", "cache-control": "no-store", ...headers });
8380
9603
  response.end(isBuffer ? body : `${JSON.stringify(body)}
8381
9604
  `);
@@ -8448,14 +9671,14 @@ function createTcpServer(scanner, options = {}) {
8448
9671
  timeout = 6e4,
8449
9672
  maxConnections = MAX_CONNECTIONS
8450
9673
  } = options;
8451
- const server = import_node_net4.default.createServer({ allowHalfOpen: true }, (socket) => {
9674
+ const server = import_node_net5.default.createServer({ allowHalfOpen: true }, (socket) => {
8452
9675
  const chunks = [];
8453
9676
  let size = 0;
8454
9677
  socket.setTimeout(timeout, () => socket.destroy());
8455
9678
  const onEnd = async () => {
8456
9679
  socket.setTimeout(0);
8457
9680
  try {
8458
- const result = await scanner.scan(import_node_buffer10.Buffer.concat(chunks));
9681
+ const result = await scanner.scan(import_node_buffer11.Buffer.concat(chunks));
8459
9682
  socket.end(json ? `${JSON.stringify(serializeResult(result))}
8460
9683
  ` : `${result.isSpam ? "SPAM" : "HAM"} ${result.score.toFixed(1)}/${result.threshold.toFixed(1)} ${result.tests.map((test) => test.name).join(",")}
8461
9684
  `);
@@ -8484,13 +9707,13 @@ function createTcpServer(scanner, options = {}) {
8484
9707
  server.maxConnections = maxConnections;
8485
9708
  return server;
8486
9709
  }
8487
- var import_node_buffer10, import_node_crypto5, import_node_http2, import_node_net4, MAX_MESSAGE_SIZE, MAX_CONNECTIONS;
9710
+ var import_node_buffer11, import_node_crypto5, import_node_http2, import_node_net5, MAX_MESSAGE_SIZE, MAX_CONNECTIONS;
8488
9711
  var init_server = __esm({
8489
9712
  "src/server.js"() {
8490
- import_node_buffer10 = require("node:buffer");
9713
+ import_node_buffer11 = require("node:buffer");
8491
9714
  import_node_crypto5 = require("node:crypto");
8492
9715
  import_node_http2 = __toESM(require("node:http"), 1);
8493
- import_node_net4 = __toESM(require("node:net"), 1);
9716
+ import_node_net5 = __toESM(require("node:net"), 1);
8494
9717
  init_headers();
8495
9718
  init_version();
8496
9719
  MAX_MESSAGE_SIZE = 25 * 1024 * 1024;
@@ -8499,12 +9722,12 @@ var init_server = __esm({
8499
9722
  });
8500
9723
 
8501
9724
  // src/milter.js
8502
- function packet(command, data2 = import_node_buffer11.Buffer.alloc(0)) {
8503
- const payload = import_node_buffer11.Buffer.isBuffer(data2) ? data2 : import_node_buffer11.Buffer.from(data2, "latin1");
8504
- const header = import_node_buffer11.Buffer.alloc(5);
9725
+ function packet(command, data2 = import_node_buffer12.Buffer.alloc(0)) {
9726
+ const payload = import_node_buffer12.Buffer.isBuffer(data2) ? data2 : import_node_buffer12.Buffer.from(data2, "latin1");
9727
+ const header = import_node_buffer12.Buffer.alloc(5);
8505
9728
  header.writeUInt32BE(payload.length + 1);
8506
9729
  header.write(command, 4, "latin1");
8507
- return import_node_buffer11.Buffer.concat([header, payload]);
9730
+ return import_node_buffer12.Buffer.concat([header, payload]);
8508
9731
  }
8509
9732
  function cstrings(data2) {
8510
9733
  const parts = data2.toString("latin1").split("\0");
@@ -8514,19 +9737,19 @@ function cstrings(data2) {
8514
9737
  return parts;
8515
9738
  }
8516
9739
  function cstring(value) {
8517
- return import_node_buffer11.Buffer.from(`${value}\0`, "latin1");
9740
+ return import_node_buffer12.Buffer.from(`${value}\0`, "latin1");
8518
9741
  }
8519
9742
  function address(value) {
8520
9743
  const text = String(value ?? "").trim();
8521
9744
  const match = /^<?([^<>]*)>?$/.exec(text);
8522
9745
  return match ? match[1] : text;
8523
9746
  }
8524
- var import_node_buffer11, import_node_events, import_node_net5, COMMANDS, RESPONSES, ACTIONS, PROTOCOL, MAX_PACKET, MilterSession, MilterServer;
9747
+ var import_node_buffer12, import_node_events, import_node_net6, COMMANDS, RESPONSES, ACTIONS, PROTOCOL, MAX_PACKET, MilterSession, MilterServer;
8525
9748
  var init_milter = __esm({
8526
9749
  "src/milter.js"() {
8527
- import_node_buffer11 = require("node:buffer");
9750
+ import_node_buffer12 = require("node:buffer");
8528
9751
  import_node_events = require("node:events");
8529
- import_node_net5 = __toESM(require("node:net"), 1);
9752
+ import_node_net6 = __toESM(require("node:net"), 1);
8530
9753
  init_headers();
8531
9754
  init_auth();
8532
9755
  init_server();
@@ -8644,7 +9867,7 @@ var init_milter = __esm({
8644
9867
  if (this.buffered < this.needed) {
8645
9868
  return;
8646
9869
  }
8647
- let buffer = this.chunks.length === 1 ? this.chunks[0] : import_node_buffer11.Buffer.concat(this.chunks);
9870
+ let buffer = this.chunks.length === 1 ? this.chunks[0] : import_node_buffer12.Buffer.concat(this.chunks);
8648
9871
  this.needed = 4;
8649
9872
  while (buffer.length >= 4) {
8650
9873
  const length = buffer.readUInt32BE(0);
@@ -8689,7 +9912,7 @@ var init_milter = __esm({
8689
9912
  const protocol = data2.readUInt32BE(8);
8690
9913
  const wanted = ACTIONS.ADDHDRS | ACTIONS.CHGHDRS | ACTIONS.QUARANTINE;
8691
9914
  this.leadingSpace = Boolean(protocol & PROTOCOL.HDR_LEADSPC);
8692
- const reply2 = import_node_buffer11.Buffer.alloc(12);
9915
+ const reply2 = import_node_buffer12.Buffer.alloc(12);
8693
9916
  reply2.writeUInt32BE(Math.min(version, 6), 0);
8694
9917
  reply2.writeUInt32BE(actions & wanted, 4);
8695
9918
  reply2.writeUInt32BE(protocol & (PROTOCOL.HDR_LEADSPC | PROTOCOL.NOUNKNOWN), 8);
@@ -8754,7 +9977,7 @@ var init_milter = __esm({
8754
9977
  if (this.truncated || this.size + data2.length > options.maxSize) {
8755
9978
  this.truncated = true;
8756
9979
  } else {
8757
- this.body.push(import_node_buffer11.Buffer.from(data2));
9980
+ this.body.push(import_node_buffer12.Buffer.from(data2));
8758
9981
  this.size += data2.length;
8759
9982
  }
8760
9983
  this.send(RESPONSES.CONTINUE);
@@ -8789,7 +10012,7 @@ var init_milter = __esm({
8789
10012
  /** @returns {Buffer} */
8790
10013
  rawMessage() {
8791
10014
  const head = this.headers.map(([name, value]) => `${name}:${this.leadingSpace || value.startsWith(" ") || value.startsWith(" ") ? "" : " "}${value}`).join("\r\n");
8792
- return import_node_buffer11.Buffer.concat([import_node_buffer11.Buffer.from(this.headers.length > 0 ? `${head}\r
10015
+ return import_node_buffer12.Buffer.concat([import_node_buffer12.Buffer.from(this.headers.length > 0 ? `${head}\r
8793
10016
  \r
8794
10017
  ` : "\r\n", "latin1"), ...this.body]);
8795
10018
  }
@@ -8800,9 +10023,9 @@ var init_milter = __esm({
8800
10023
  const key = name.toLowerCase();
8801
10024
  counts.set(key, (counts.get(key) || 0) + 1);
8802
10025
  if (key.startsWith("x-spam-")) {
8803
- const index = import_node_buffer11.Buffer.alloc(4);
10026
+ const index = import_node_buffer12.Buffer.alloc(4);
8804
10027
  index.writeUInt32BE(counts.get(key));
8805
- this.send(RESPONSES.CHGHEADER, import_node_buffer11.Buffer.concat([index, cstring(name), cstring("")]));
10028
+ this.send(RESPONSES.CHGHEADER, import_node_buffer12.Buffer.concat([index, cstring(name), cstring("")]));
8806
10029
  }
8807
10030
  }
8808
10031
  }
@@ -8825,7 +10048,7 @@ var init_milter = __esm({
8825
10048
  }
8826
10049
  this.server.report(error);
8827
10050
  this.removeForgedHeaders();
8828
- this.send(RESPONSES.ADDHEADER, import_node_buffer11.Buffer.concat([cstring("X-Spam-Virus-Scan"), cstring(`${this.leadingSpace ? " " : ""}failed`)]));
10051
+ this.send(RESPONSES.ADDHEADER, import_node_buffer12.Buffer.concat([cstring("X-Spam-Virus-Scan"), cstring(`${this.leadingSpace ? " " : ""}failed`)]));
8829
10052
  this.send(RESPONSES.CONTINUE);
8830
10053
  this.resetMessage();
8831
10054
  return;
@@ -8846,15 +10069,15 @@ var init_milter = __esm({
8846
10069
  headers.unshift(["Authentication-Results", formatAuthResultsHeader(result.results.authentication, options.hostname)]);
8847
10070
  }
8848
10071
  for (const [name, value] of headers) {
8849
- this.send(RESPONSES.ADDHEADER, import_node_buffer11.Buffer.concat([cstring(name), cstring(`${this.leadingSpace ? " " : ""}${value}`.replaceAll("\r\n", "\n"))]));
10072
+ this.send(RESPONSES.ADDHEADER, import_node_buffer12.Buffer.concat([cstring(name), cstring(`${this.leadingSpace ? " " : ""}${value}`.replaceAll("\r\n", "\n"))]));
8850
10073
  }
8851
10074
  if (result.isSpam && options.subjectTag) {
8852
10075
  const subject = this.headers.find(([name]) => name.toLowerCase() === "subject");
8853
10076
  const value = taggedSubject(options.subjectTag, subject ? subject[1].trim() : "", "\n");
8854
10077
  if (value !== null) {
8855
- const index = import_node_buffer11.Buffer.alloc(4);
10078
+ const index = import_node_buffer12.Buffer.alloc(4);
8856
10079
  index.writeUInt32BE(1);
8857
- this.send(RESPONSES.CHGHEADER, import_node_buffer11.Buffer.concat([index, cstring("Subject"), cstring(`${this.leadingSpace ? " " : ""}${value}`.replaceAll("\r\n", "\n"))]));
10080
+ this.send(RESPONSES.CHGHEADER, import_node_buffer12.Buffer.concat([index, cstring("Subject"), cstring(`${this.leadingSpace ? " " : ""}${value}`.replaceAll("\r\n", "\n"))]));
8858
10081
  }
8859
10082
  }
8860
10083
  if (result.action === "reject" && options.reject) {
@@ -8905,7 +10128,7 @@ var init_milter = __esm({
8905
10128
  throw new RangeError(`rejectCode must be a 4xx or 5xx SMTP code, not ${this.options.rejectCode}`);
8906
10129
  }
8907
10130
  this.sockets = /* @__PURE__ */ new Set();
8908
- this.server = import_node_net5.default.createServer((socket) => {
10131
+ this.server = import_node_net6.default.createServer((socket) => {
8909
10132
  this.sockets.add(socket);
8910
10133
  socket.on("close", () => this.sockets.delete(socket));
8911
10134
  return new MilterSession(socket, this);
@@ -8981,7 +10204,7 @@ function reply(code, text, headers = [], body = null) {
8981
10204
  lines2.push(`Content-length: ${body.length}`);
8982
10205
  }
8983
10206
  lines2.push(...headers.map(([name, value]) => `${name}: ${value}`), "", "");
8984
- return body === null ? import_node_buffer12.Buffer.from(lines2.join("\r\n")) : import_node_buffer12.Buffer.concat([import_node_buffer12.Buffer.from(lines2.join("\r\n")), body]);
10207
+ return body === null ? import_node_buffer13.Buffer.from(lines2.join("\r\n")) : import_node_buffer13.Buffer.concat([import_node_buffer13.Buffer.from(lines2.join("\r\n")), body]);
8985
10208
  }
8986
10209
  function report(result) {
8987
10210
  const lines2 = [
@@ -8991,7 +10214,7 @@ function report(result) {
8991
10214
  "",
8992
10215
  " pts rule name description",
8993
10216
  "---- ---------------------- --------------------------------------------------",
8994
- ...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)}`)
8995
10218
  ];
8996
10219
  return `${lines2.join("\r\n")}\r
8997
10220
  `;
@@ -9026,11 +10249,11 @@ async function answer(scanner, request, message, options) {
9026
10249
  return reply(EX_OK, "EX_OK", [verdict]);
9027
10250
  }
9028
10251
  case "SYMBOLS": {
9029
- return reply(EX_OK, "EX_OK", [verdict], import_node_buffer12.Buffer.from(result.tests.map((test) => test.name).join(",")));
10252
+ return reply(EX_OK, "EX_OK", [verdict], import_node_buffer13.Buffer.from(result.tests.map((test) => test.name).join(",")));
9030
10253
  }
9031
10254
  case "REPORT":
9032
10255
  case "REPORT_IFSPAM": {
9033
- return reply(EX_OK, "EX_OK", [verdict], command === "REPORT" || result.isSpam ? import_node_buffer12.Buffer.from(report(result)) : import_node_buffer12.Buffer.alloc(0));
10256
+ return reply(EX_OK, "EX_OK", [verdict], command === "REPORT" || result.isSpam ? import_node_buffer13.Buffer.from(report(result)) : import_node_buffer13.Buffer.alloc(0));
9034
10257
  }
9035
10258
  default: {
9036
10259
  const rewritten = rewriteMessage(message, spamHeaders(result, { version: VERSION }), { subjectTag: result.isSpam ? options.subjectTag : null });
@@ -9038,7 +10261,7 @@ async function answer(scanner, request, message, options) {
9038
10261
  return reply(EX_OK, "EX_OK", [verdict], rewritten);
9039
10262
  }
9040
10263
  const { header, newline } = splitMessage(rewritten);
9041
- return reply(EX_OK, "EX_OK", [verdict], import_node_buffer12.Buffer.from(`${header}${newline}${newline}`, "latin1"));
10264
+ return reply(EX_OK, "EX_OK", [verdict], import_node_buffer13.Buffer.from(`${header}${newline}${newline}`, "latin1"));
9042
10265
  }
9043
10266
  }
9044
10267
  }
@@ -9052,8 +10275,8 @@ function createSpamdServer(scanner, options = {}) {
9052
10275
  maxConnections: MAX_CONNECTIONS,
9053
10276
  ...options
9054
10277
  };
9055
- const server = import_node_net6.default.createServer({ allowHalfOpen: true }, (socket) => {
9056
- let head = import_node_buffer12.Buffer.alloc(0);
10278
+ const server = import_node_net7.default.createServer({ allowHalfOpen: true }, (socket) => {
10279
+ let head = import_node_buffer13.Buffer.alloc(0);
9057
10280
  const chunks = [];
9058
10281
  let size = 0;
9059
10282
  let request = null;
@@ -9079,7 +10302,7 @@ function createSpamdServer(scanner, options = {}) {
9079
10302
  return;
9080
10303
  }
9081
10304
  if (!request) {
9082
- head = import_node_buffer12.Buffer.concat([head, chunk2]);
10305
+ head = import_node_buffer13.Buffer.concat([head, chunk2]);
9083
10306
  const end = head.indexOf("\r\n\r\n");
9084
10307
  if (end === -1) {
9085
10308
  if (head.length > 64 * 1024) {
@@ -9114,7 +10337,7 @@ function createSpamdServer(scanner, options = {}) {
9114
10337
  chunks.push(chunk2);
9115
10338
  size += chunk2.length;
9116
10339
  if (request.length === null ? ["PING", "SKIP"].includes(request.command) : size >= request.length) {
9117
- finish(import_node_buffer12.Buffer.concat(chunks).subarray(0, request.length ?? 0));
10340
+ finish(import_node_buffer13.Buffer.concat(chunks).subarray(0, request.length ?? 0));
9118
10341
  } else if (size > settings.maxSize) {
9119
10342
  fail(EX_DATAERR, `Message larger than ${settings.maxSize} bytes`);
9120
10343
  }
@@ -9126,7 +10349,7 @@ function createSpamdServer(scanner, options = {}) {
9126
10349
  if (!request) {
9127
10350
  fail(EX_PROTOCOL, "Incomplete request");
9128
10351
  } else if (request.length === null) {
9129
- finish(import_node_buffer12.Buffer.concat(chunks));
10352
+ finish(import_node_buffer13.Buffer.concat(chunks));
9130
10353
  } else {
9131
10354
  fail(EX_PROTOCOL, `Message shorter than its Content-length of ${request.length} bytes`);
9132
10355
  }
@@ -9137,11 +10360,11 @@ function createSpamdServer(scanner, options = {}) {
9137
10360
  server.maxConnections = settings.maxConnections;
9138
10361
  return server;
9139
10362
  }
9140
- var import_node_buffer12, import_node_net6, 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;
9141
10364
  var init_spamd = __esm({
9142
10365
  "src/spamd.js"() {
9143
- import_node_buffer12 = require("node:buffer");
9144
- import_node_net6 = __toESM(require("node:net"), 1);
10366
+ import_node_buffer13 = require("node:buffer");
10367
+ import_node_net7 = __toESM(require("node:net"), 1);
9145
10368
  init_headers();
9146
10369
  init_server();
9147
10370
  init_version();
@@ -9152,6 +10375,7 @@ var init_spamd = __esm({
9152
10375
  EX_PROTOCOL = 76;
9153
10376
  EX_NOPERM = 77;
9154
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, " ");
9155
10379
  }
9156
10380
  });
9157
10381
 
@@ -9353,21 +10577,21 @@ async function withTimeout(promise, ms, fallback) {
9353
10577
  }
9354
10578
  }
9355
10579
  async function toBuffer(source) {
9356
- if (import_node_buffer13.Buffer.isBuffer(source)) {
10580
+ if (import_node_buffer14.Buffer.isBuffer(source)) {
9357
10581
  return source;
9358
10582
  }
9359
10583
  if (typeof source === "string") {
9360
- return import_node_buffer13.Buffer.from(source, "utf8");
10584
+ return import_node_buffer14.Buffer.from(source, "utf8");
9361
10585
  }
9362
10586
  if (source instanceof Uint8Array) {
9363
- return import_node_buffer13.Buffer.from(source);
10587
+ return import_node_buffer14.Buffer.from(source);
9364
10588
  }
9365
10589
  if (source && typeof source[Symbol.asyncIterator] === "function") {
9366
10590
  const chunks = [];
9367
10591
  for await (const chunk2 of source) {
9368
- chunks.push(import_node_buffer13.Buffer.isBuffer(chunk2) ? chunk2 : import_node_buffer13.Buffer.from(chunk2));
10592
+ chunks.push(import_node_buffer14.Buffer.isBuffer(chunk2) ? chunk2 : import_node_buffer14.Buffer.from(chunk2));
9369
10593
  }
9370
- return import_node_buffer13.Buffer.concat(chunks);
10594
+ return import_node_buffer14.Buffer.concat(chunks);
9371
10595
  }
9372
10596
  throw new TypeError("A message must be a Buffer, a string, a Uint8Array or a readable stream");
9373
10597
  }
@@ -9442,12 +10666,12 @@ function fromDomain(mail) {
9442
10666
  function withHeader(raw) {
9443
10667
  const first = raw.subarray(0, 1e3).toString("latin1");
9444
10668
  const headerless = raw.length > 0 && !/^(?:[!-9;-~]+[ \t]*:|From |[\r\n])/.test(first);
9445
- return headerless ? import_node_buffer13.Buffer.concat([import_node_buffer13.Buffer.from("\r\n"), raw]) : raw;
10669
+ return headerless ? import_node_buffer14.Buffer.concat([import_node_buffer14.Buffer.from("\r\n"), raw]) : raw;
9446
10670
  }
9447
- var import_node_buffer13, import_promises2, import_node_util2, debug2, DEFAULTS2, NESTED, MAX_LOOKALIKE_HOSTS, MAX_DECEPTIVE_LINKS, CLAMAV_CONCURRENCY, CLAMAV_TIMEOUT, CLAMAV_LIMITS, MAX_NSFW_IMAGES, MAX_REPUTATION_CACHES, SpamScanner, src_default;
10671
+ var import_node_buffer14, import_promises2, import_node_util2, debug2, DEFAULTS2, NESTED, MAX_LOOKALIKE_HOSTS, MAX_DECEPTIVE_LINKS, CLAMAV_CONCURRENCY, CLAMAV_TIMEOUT, CLAMAV_LIMITS, MAX_NSFW_IMAGES, MAX_REPUTATION_CACHES, SpamScanner, src_default;
9448
10672
  var init_src = __esm({
9449
10673
  "src/index.js"() {
9450
- import_node_buffer13 = require("node:buffer");
10674
+ import_node_buffer14 = require("node:buffer");
9451
10675
  import_promises2 = require("node:fs/promises");
9452
10676
  import_node_util2 = require("node:util");
9453
10677
  init_attachments();
@@ -9456,6 +10680,7 @@ var init_src = __esm({
9456
10680
  init_classifier();
9457
10681
  init_dnsbl();
9458
10682
  init_html();
10683
+ init_links();
9459
10684
  init_homograph();
9460
10685
  init_is_arbitrary();
9461
10686
  init_language();
@@ -9498,6 +10723,19 @@ var init_src = __esm({
9498
10723
  cloudflare: true,
9499
10724
  adult: true,
9500
10725
  maxHosts: 25,
10726
+ // Also read the links of the whole message and of what it carries
10727
+ // (attached messages, HTML, SVG and XML files, text files, invitations
10728
+ // and contact cards, documents in iframes and data: URLs) when link
10729
+ // hosts are looked up, for at most five seconds: off by default, as it
10730
+ // reads all of it, up to 50 MB of attachments.
10731
+ attachments: false,
10732
+ // Milliseconds the link lookups may take in all (default: timeout).
10733
+ timeout: void 0,
10734
+ // Also look up host names written in the text without a link or a path
10735
+ // ("evil.example"), which mail clients make links of, as far as there is
10736
+ // room: off by default, as a quoted reply or a signature may name any
10737
+ // site (they are checked for lookalikes either way).
10738
+ hostsInText: false,
9501
10739
  // Characters of text read for links (twice as many of HTML): more than
9502
10740
  // for the classifier (maxLength), as a link anywhere in a message may be
9503
10741
  // followed; a longer message sets results.linksTruncated.
@@ -9699,7 +10937,10 @@ var init_src = __esm({
9699
10937
  async scanFile(file, options) {
9700
10938
  return this.scan(await (0, import_promises2.readFile)(file), options);
9701
10939
  }
9702
- async checkLinks(links, config, hosts = [], from = "") {
10940
+ // (`from`: the sender's domain; `carried`: the link hosts of the whole
10941
+ // message and of what it carries, see carriedLinkHosts)
10942
+ async checkLinks(links, config, hosts = [], options = {}) {
10943
+ const { from = "", carried = null } = typeof options === "string" ? { from: options } : options ?? {};
9703
10944
  const phishing = [];
9704
10945
  const homographs = [];
9705
10946
  const seenHosts = /* @__PURE__ */ new Set();
@@ -9755,18 +10996,23 @@ var init_src = __esm({
9755
10996
  for (const host of hosts) {
9756
10997
  lookalike(host, { source: "text" });
9757
10998
  }
9758
- const ordered = [...new Set([...found.filter((link) => link.text), ...found.filter((link) => !link.text), ...bare].map((link) => lookupName(destination(link))).filter(Boolean))];
10999
+ const carriedHosts = (carried?.hosts ?? []).map((host) => lookupName(host)).filter(Boolean);
11000
+ for (const host of carriedHosts) {
11001
+ seenHosts.add(host);
11002
+ }
11003
+ const own3 = [...found.filter((link) => link.text), ...found.filter((link) => !link.text), ...bare].map((link) => lookupName(destination(link))).filter(Boolean);
9759
11004
  const firsts = /* @__PURE__ */ new Map();
9760
- for (const host of ordered) {
11005
+ for (const host of [...own3, ...carriedHosts]) {
9761
11006
  const domain = registrableDomain(host);
9762
11007
  if (!firsts.has(domain)) {
9763
11008
  firsts.set(domain, host);
9764
11009
  }
9765
11010
  }
9766
- const checked = [.../* @__PURE__ */ new Set([...firsts.values(), ...ordered])].slice(0, config.phishing.maxHosts);
11011
+ const checked = [.../* @__PURE__ */ new Set([...firsts.values(), ...own3, ...carriedHosts])].slice(0, config.phishing.maxHosts);
9767
11012
  const domains = [...new Set(checked.map((host) => registrableDomain(host)))];
9768
11013
  const blocked = [];
9769
11014
  let answered = 0;
11015
+ const linkTimeout = Number(config.phishing.timeout) > 0 ? Number(config.phishing.timeout) : config.timeout;
9770
11016
  const cloudflare = async () => {
9771
11017
  await withTimeout(Promise.all(checked.map(async (host) => {
9772
11018
  const found2 = await this.dns.checkCloudflare([host], { adult: config.phishing.adult });
@@ -9774,12 +11020,12 @@ var init_src = __esm({
9774
11020
  if (!found2.unanswered?.length) {
9775
11021
  answered++;
9776
11022
  }
9777
- })), config.timeout, null);
11023
+ })), linkTimeout, null);
9778
11024
  return { found: [...blocked], unanswered: checked.length - answered };
9779
11025
  };
9780
11026
  const [cloudflareResult, listed] = await Promise.all([
9781
11027
  config.phishing.cloudflare && checked.length > 0 ? cloudflare() : { found: [], unanswered: 0 },
9782
- config.dnsbl.domain?.length > 0 && domains.length > 0 ? withTimeout(this.dns.checkDomains(domains, config.dnsbl.domain), config.timeout, []) : []
11028
+ config.dnsbl.domain?.length > 0 && domains.length > 0 ? withTimeout(this.dns.checkDomains(domains, config.dnsbl.domain), linkTimeout, []) : []
9783
11029
  ]);
9784
11030
  for (const { host, category } of cloudflareResult.found) {
9785
11031
  phishing.push(finding(category === "malware" ? { type: "malicious_domain", domain: host, message: `Link hostname ${host} is blocked by Cloudflare's malware filtering as phishing or malware` } : { type: "adult_domain", domain: host, message: `Link hostname ${host} was detected by Cloudflare's family filtering to contain adult-related content` }));
@@ -9795,7 +11041,8 @@ var init_src = __esm({
9795
11041
  return {
9796
11042
  phishing,
9797
11043
  homographs,
9798
- uncheckedHosts: seenHosts.size - checked.length,
11044
+ // (at least one when the message has more hosts than were read)
11045
+ uncheckedHosts: Math.max(seenHosts.size - checked.length, carried?.more ? 1 : 0),
9799
11046
  timedOutHosts: cloudflareResult.unanswered
9800
11047
  };
9801
11048
  }
@@ -9967,10 +11214,17 @@ ${visibleText(html2.slice(0, config.maxLength * 2)).text}`, { maxLength: config.
9967
11214
  styled: plainObfuscation.styled || shownObfuscation.styled
9968
11215
  };
9969
11216
  const unopened = [];
9970
- const attached = await expandAttachments(mail.attachments, parseErrors, void 0, unopened);
11217
+ const opened = [];
11218
+ const attached = await expandAttachments(mail.attachments, parseErrors, void 0, { unopened, mails: opened });
11219
+ const carried = config.phishing.attachments === true && (config.phishing.cloudflare || config.dnsbl.domain?.length > 0) ? carriedLinkHosts(mail, {
11220
+ mails: opened,
11221
+ attachments: attached,
11222
+ maxHosts: config.phishing.maxHosts,
11223
+ hostsInText: config.phishing.hostsInText === true
11224
+ }) : null;
9971
11225
  const attachments = config.attachments === false ? [] : inspectAttachments(attached).filter((item) => config.macros !== false || !["macro", "pdf_active", "rtf_object"].includes(item.type)).map((item) => finding(item));
9972
11226
  const [links, authentication, checkedReputation, dnsbl, virusScan, toxicity, nsfw] = await Promise.all([
9973
- this.checkLinks(checkedLinks, config, extracted.hosts, fromDomain(mail)),
11227
+ this.checkLinks(checkedLinks, config, extracted.hosts, { from: fromDomain(mail), carried }),
9974
11228
  config.authenticationResults && typeof config.authenticationResults === "object" ? normalizeAuthOutput(config.authenticationResults) : config.authentication && session.remoteAddress ? withTimeout(authenticate(raw, {
9975
11229
  timeout: config.timeout,
9976
11230
  ...typeof config.authentication === "object" ? config.authentication : {},
@@ -10098,6 +11352,14 @@ ${visibleText(html2.slice(0, config.maxLength * 2)).text}`, { maxLength: config.
10098
11352
  // Whether the message has more text than phishing.maxLength, whose
10099
11353
  // links were not read.
10100
11354
  linksTruncated,
11355
+ // Whether a link host of the message or of what it carries was
11356
+ // never read whole (it runs past where long text is cut to be
11357
+ // read, it goes through more security gateways than are
11358
+ // unwrapped, or it is in more documents than are read), and whether
11359
+ // an attachment was left unread for time: in either case not every
11360
+ // link was checked (see phishing.attachments).
11361
+ linksTooComplex: Boolean(carried?.tooComplex),
11362
+ linksIncomplete: Boolean(carried?.incomplete),
10101
11363
  idnHomographAttack: {
10102
11364
  detected: links.homographs.length > 0,
10103
11365
  domains: links.homographs,
@@ -10109,6 +11371,9 @@ ${visibleText(html2.slice(0, config.maxLength * 2)).text}`, { maxLength: config.
10109
11371
  language: extracted.language,
10110
11372
  tokens: extracted.words,
10111
11373
  mail,
11374
+ // (the attachments of the message and of its attached messages, as
11375
+ // read, an attached message's named after it)
11376
+ attached,
10112
11377
  version: VERSION,
10113
11378
  metrics: { totalTime: time }
10114
11379
  };