spamscanner 7.0.2 → 7.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/arf.cjs +62 -21
- package/dist/cjs/arf.cjs.map +4 -4
- package/dist/cjs/index.cjs +5470 -3425
- package/dist/cjs/index.cjs.map +4 -4
- package/dist/esm/arf.js +61 -20
- package/dist/esm/arf.js.map +4 -4
- package/dist/esm/cli.js +7110 -5028
- package/dist/esm/cli.js.map +4 -4
- package/dist/esm/index.js +5501 -3465
- package/dist/esm/index.js.map +4 -4
- package/dist/types/arf.d.cts +11 -0
- package/dist/types/arf.d.ts +15 -12
- package/dist/types/attachments.d.ts +13 -1
- package/dist/types/auth.d.ts +41 -4
- package/dist/types/clamav.d.ts +1 -1
- package/dist/types/classifier.d.ts +10 -1
- package/dist/types/dnsbl.d.ts +37 -5
- package/dist/types/headers.d.ts +30 -1
- package/dist/types/homograph.d.ts +18 -1
- package/dist/types/html.d.ts +29 -0
- package/dist/types/index.d.cts +22 -0
- package/dist/types/index.d.ts +360 -21
- package/dist/types/is-arbitrary.d.ts +1 -1
- package/dist/types/language.d.ts +3 -1
- package/dist/types/legacy.d.ts +24 -0
- package/dist/types/linear.d.ts +15 -2
- package/dist/types/llm.d.ts +13 -2
- package/dist/types/milter.d.ts +12 -5
- package/dist/types/model.d.ts +15 -6
- package/dist/types/parse.d.ts +46 -0
- package/dist/types/reputation.d.ts +84 -3
- package/dist/types/score.d.ts +4 -1
- package/dist/types/server.d.ts +7 -1
- package/dist/types/sources.d.ts +10 -5
- package/dist/types/spamd.d.ts +5 -0
- package/dist/types/tokenizer.d.ts +50 -23
- package/package.json +21 -8
- package/dist/standalone/cli.cjs +0 -702
- package/dist/types/cli.d.ts +0 -40
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type * as m from './arf.js' with {'resolution-mode': 'import'};
|
|
2
|
+
export declare const parseReportFields: typeof m.parseReportFields;
|
|
3
|
+
export declare const findPart: typeof m.findPart;
|
|
4
|
+
export declare const isArfMessage: typeof m.isArfMessage;
|
|
5
|
+
export declare const parse: typeof m.parse;
|
|
6
|
+
export declare const tryParse: typeof m.tryParse;
|
|
7
|
+
export declare const create: typeof m.create;
|
|
8
|
+
export declare const VALID_FEEDBACK_TYPES: typeof m.VALID_FEEDBACK_TYPES;
|
|
9
|
+
export declare const ArfParser: typeof m.ArfParser;
|
|
10
|
+
declare const _default: typeof m.default;
|
|
11
|
+
export {_default as default};
|
package/dist/types/arf.d.ts
CHANGED
|
@@ -32,8 +32,9 @@ export function isArfMessage(parsed: object): boolean;
|
|
|
32
32
|
* @returns {Promise<object>} feedbackType, userAgent, version, arrivalDate,
|
|
33
33
|
* sourceIp, originalMailFrom, originalRcptTo, reportingMta,
|
|
34
34
|
* originalEnvelopeId, authenticationResults, reportedDomain, reportedUri,
|
|
35
|
-
* incidents, humanReadable, originalMessage, originalHeaders (
|
|
36
|
-
*
|
|
35
|
+
* incidents, humanReadable, originalMessage, originalHeaders (an object of
|
|
36
|
+
* lowercase header name to value, or null when the report has no original
|
|
37
|
+
* message) and rawFeedbackReport
|
|
37
38
|
* @throws {Error} when the message is not a valid ARF report
|
|
38
39
|
*/
|
|
39
40
|
export function parse(source: Buffer | string): Promise<object>;
|
|
@@ -44,36 +45,38 @@ export function parse(source: Buffer | string): Promise<object>;
|
|
|
44
45
|
*/
|
|
45
46
|
export function tryParse(source: Buffer | string): Promise<object | null>;
|
|
46
47
|
/**
|
|
47
|
-
* Write an ARF report.
|
|
48
|
+
* Write an ARF report. (A string message gives a string report, and a
|
|
49
|
+
* Buffer a Buffer, its bytes kept as they are, whatever their character set.)
|
|
50
|
+
* @template {string|Buffer} M
|
|
48
51
|
* @param {object} options
|
|
49
52
|
* @param {string} options.feedbackType - abuse, fraud, virus, other, not-spam, ...
|
|
50
53
|
* @param {string} options.userAgent
|
|
51
54
|
* @param {string} options.from
|
|
52
55
|
* @param {string} options.to
|
|
53
|
-
* @param {
|
|
56
|
+
* @param {M} options.originalMessage
|
|
54
57
|
* @param {string} [options.humanReadable]
|
|
55
58
|
* @param {string} [options.subject]
|
|
56
59
|
* @param {string} [options.sourceIp]
|
|
57
60
|
* @param {string} [options.originalMailFrom]
|
|
58
|
-
* @param {string[]} [options.originalRcptTo]
|
|
59
|
-
* @param {Date} [options.arrivalDate]
|
|
61
|
+
* @param {string|string[]} [options.originalRcptTo]
|
|
62
|
+
* @param {Date|string|number} [options.arrivalDate]
|
|
60
63
|
* @param {string} [options.reportingMta]
|
|
61
|
-
* @returns {string}
|
|
64
|
+
* @returns {M extends Buffer ? Buffer : string} the report
|
|
62
65
|
*/
|
|
63
|
-
export function create(options?: {
|
|
66
|
+
export function create<M extends string | Buffer>(options?: {
|
|
64
67
|
feedbackType: string;
|
|
65
68
|
userAgent: string;
|
|
66
69
|
from: string;
|
|
67
70
|
to: string;
|
|
68
|
-
originalMessage:
|
|
71
|
+
originalMessage: M;
|
|
69
72
|
humanReadable?: string;
|
|
70
73
|
subject?: string;
|
|
71
74
|
sourceIp?: string;
|
|
72
75
|
originalMailFrom?: string;
|
|
73
|
-
originalRcptTo?: string[];
|
|
74
|
-
arrivalDate?: Date;
|
|
76
|
+
originalRcptTo?: string | string[];
|
|
77
|
+
arrivalDate?: Date | string | number;
|
|
75
78
|
reportingMta?: string;
|
|
76
|
-
}): string;
|
|
79
|
+
}): M extends Buffer ? Buffer : string;
|
|
77
80
|
/**
|
|
78
81
|
* Feedback types from RFC 5965, RFC 6591 (auth-failure) and RFC 7489 (dmarc).
|
|
79
82
|
*/
|
|
@@ -39,11 +39,15 @@ export function zipEntries(buffer: Buffer, limit?: number): Array<{
|
|
|
39
39
|
* - disguised_executable: content is a program but the name says otherwise
|
|
40
40
|
* - double_extension: "invoice.pdf.exe"
|
|
41
41
|
* - rtl_override: a right-to-left override hides the real extension (a file named "invoice", U+202E, "fdp.exe" shows as "invoiceexe.pdf")
|
|
42
|
+
* - risky_attachment: a type that is risky by name but not a program
|
|
43
|
+
* (a certificate, an Internet shortcut, a theme; see RISKY_EXTENSIONS)
|
|
42
44
|
* - executable_in_archive: a ZIP holds a program, read without unpacking
|
|
43
45
|
* (extension: the first program's, extensions: every program's)
|
|
46
|
+
* - script_in_archive: a ZIP holds scripts (JavaScript, Python, shell, Perl)
|
|
47
|
+
* but no other programs
|
|
44
48
|
* - encrypted_archive: a password-protected ZIP, which virus scanners cannot open
|
|
45
49
|
* - macro: an Office document with a VBA project
|
|
46
|
-
* - pdf_active: a PDF with JavaScript
|
|
50
|
+
* - pdf_active: a PDF with JavaScript or launch actions
|
|
47
51
|
* - rtf_object: an RTF document with an embedded OLE object
|
|
48
52
|
* - html_attachment: an HTML or SVG file, the usual carrier of fake login pages
|
|
49
53
|
*
|
|
@@ -69,4 +73,12 @@ export function inspectAttachments(attachments?: Array<{
|
|
|
69
73
|
* campaigns.
|
|
70
74
|
*/
|
|
71
75
|
export const EXECUTABLE_EXTENSIONS: Set<string>;
|
|
76
|
+
/**
|
|
77
|
+
* File extensions Outlook blocks that are risky by name rather than programs:
|
|
78
|
+
* certificates (installing one trusts whoever holds its key), Internet
|
|
79
|
+
* shortcuts, Windows themes, and data files people send every day (MATLAB
|
|
80
|
+
* data, Outlook data, temporary files). Reported as risky_attachment, with
|
|
81
|
+
* few points.
|
|
82
|
+
*/
|
|
83
|
+
export const RISKY_EXTENSIONS: Set<string>;
|
|
72
84
|
import { Buffer } from 'node:buffer';
|
package/dist/types/auth.d.ts
CHANGED
|
@@ -11,8 +11,11 @@ export function createResolver(timeout?: number, servers?: string[]): (name: str
|
|
|
11
11
|
/**
|
|
12
12
|
* Overall DKIM result from mailauth's per-signature results: "pass" when any
|
|
13
13
|
* signature passes, "fail" when signatures exist and none passes, "none"
|
|
14
|
-
* without signatures. `aligned` is the
|
|
15
|
-
* aligned with
|
|
14
|
+
* without signatures. `aligned` is the signing domain of a passing signature
|
|
15
|
+
* aligned with the From domain (relaxed: they share their registrable domain),
|
|
16
|
+
* in ASCII, if any. A signature whose body length (l=) is shorter than the
|
|
17
|
+
* body (mailauth's `status.underSized`) is aligned with nothing: it signs only
|
|
18
|
+
* the start of the body, and anyone can append to it.
|
|
16
19
|
* @param {object} dkim
|
|
17
20
|
* @returns {{status: {result: string, comment: string}, results: object[], aligned: string|null}}
|
|
18
21
|
*/
|
|
@@ -26,11 +29,29 @@ export function summarizeDkim(dkim: object): {
|
|
|
26
29
|
};
|
|
27
30
|
/**
|
|
28
31
|
* Turn mailauth's output into this module's result shape, filling in "none"
|
|
29
|
-
* for anything mailauth left out
|
|
32
|
+
* for anything mailauth left out, and checking what mailauth does not:
|
|
33
|
+
*
|
|
34
|
+
* - the policy (`p`, `sp`) and alignment modes (`adkim`, `aspf`) of the
|
|
35
|
+
* DMARC record are read without spaces and in any case, as RFC 7489 reads
|
|
36
|
+
* them ("p= None" is none, "adkim=S" strict), and strict alignment is
|
|
37
|
+
* enforced: under it only a signature or SPF pass of the From domain itself
|
|
38
|
+
* aligns, and DMARC fails without one;
|
|
39
|
+
* - a message without exactly one From address, whose DMARC mailauth does
|
|
40
|
+
* not check, fails DMARC, as RFC 7489 (6.6.1) allows;
|
|
41
|
+
* - `arc.sealer` is the domain of the newest ARC seal of a passing chain,
|
|
42
|
+
* when its ARC-Message-Signature is of the same domain, and `arc.trusted`
|
|
43
|
+
* whether it is one of `trustedArcSealers` (or under one) and the results
|
|
44
|
+
* it sealed show the message passed DMARC, or DKIM or SPF aligned with the
|
|
45
|
+
* From domain, when it got it.
|
|
30
46
|
* @param {object} output
|
|
47
|
+
* @param {object} [options]
|
|
48
|
+
* @param {string[]} [options.trustedArcSealers] - forwarders whose ARC seal
|
|
49
|
+
* is believed
|
|
31
50
|
* @returns {object}
|
|
32
51
|
*/
|
|
33
|
-
export function normalizeAuthOutput(output?: object
|
|
52
|
+
export function normalizeAuthOutput(output?: object, options?: {
|
|
53
|
+
trustedArcSealers?: string[];
|
|
54
|
+
}): object;
|
|
34
55
|
/**
|
|
35
56
|
* Check SPF, DKIM, DMARC, ARC and BIMI for a message, using mailauth.
|
|
36
57
|
*
|
|
@@ -46,6 +67,10 @@ export function normalizeAuthOutput(output?: object): object;
|
|
|
46
67
|
* @param {Function} [options.resolver] - custom DNS resolver (name, type)
|
|
47
68
|
* @param {number} [options.timeout] - DNS timeout in milliseconds
|
|
48
69
|
* @param {string[]} [options.dnsServers] - name servers to use
|
|
70
|
+
* @param {string[]} [options.trustedArcSealers] - forwarders (such as a
|
|
71
|
+
* mailing list's domain) whose ARC seal is believed: a message they passed
|
|
72
|
+
* on, which passed when they got it, gets no points for failing SPF, DKIM
|
|
73
|
+
* and DMARC (see normalizeAuthOutput)
|
|
49
74
|
* @returns {Promise<object>} dkim, spf, dmarc, arc, bimi, receivedChain and
|
|
50
75
|
* headers (Received-SPF and Authentication-Results, ready to prepend)
|
|
51
76
|
*/
|
|
@@ -57,9 +82,20 @@ export function authenticate(message: Buffer | string, options?: {
|
|
|
57
82
|
resolver?: Function;
|
|
58
83
|
timeout?: number;
|
|
59
84
|
dnsServers?: string[];
|
|
85
|
+
trustedArcSealers?: string[];
|
|
60
86
|
}): Promise<object>;
|
|
61
87
|
/**
|
|
62
88
|
* Score authentication results: failures add points, passes take some away.
|
|
89
|
+
*
|
|
90
|
+
* A DMARC failure scores `dmarcFail` when the domain's policy is quarantine
|
|
91
|
+
* or reject (or not known), and SPF failing as well adds nothing more: DMARC
|
|
92
|
+
* already counts it. When the policy is none it scores `dmarcFailNone`, on
|
|
93
|
+
* top of SPF's points, so a domain with policy none is never easier to
|
|
94
|
+
* spoof than one without DMARC. A DKIM signature of only part of the body
|
|
95
|
+
* (l=) earns no DKIM_PASS, nor a DMARC pass that rests on one alone
|
|
96
|
+
* DMARC_PASS. An ARC seal counts only from a trusted forwarder (`arc.trusted`,
|
|
97
|
+
* see authenticate's trustedArcSealers): its pass gets ARC_PASS, and no
|
|
98
|
+
* points for failing SPF, DKIM or DMARC, as forwarding is what broke them.
|
|
63
99
|
* @param {object} auth - from authenticate
|
|
64
100
|
* @param {object} [weights] - overrides for DEFAULT_AUTH_WEIGHTS
|
|
65
101
|
* @returns {{score: number, tests: Array<{name: string, score: number}>}}
|
|
@@ -93,6 +129,7 @@ export namespace DEFAULT_AUTH_WEIGHTS {
|
|
|
93
129
|
let spfSoftfail: number;
|
|
94
130
|
let dmarcPass: number;
|
|
95
131
|
let dmarcFail: number;
|
|
132
|
+
let dmarcFailNone: number;
|
|
96
133
|
let arcPass: number;
|
|
97
134
|
let arcFail: number;
|
|
98
135
|
}
|
package/dist/types/clamav.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export function clamdAddress(options?: object): {
|
|
|
17
17
|
* @param {string} [options.socket] - clamd's Unix socket
|
|
18
18
|
* @param {string} [options.host] - or its TCP host
|
|
19
19
|
* @param {number} [options.port] - and port (3310)
|
|
20
|
-
* @param {number} [options.timeout] - milliseconds
|
|
20
|
+
* @param {number} [options.timeout] - milliseconds for the whole scan
|
|
21
21
|
* @param {number} [options.chunkSize]
|
|
22
22
|
* @returns {Promise<{infected: boolean, viruses: string[], reply: string}>}
|
|
23
23
|
*/
|
|
@@ -27,6 +27,9 @@ export const MODEL_VERSION: 2;
|
|
|
27
27
|
export class Classifier {
|
|
28
28
|
/**
|
|
29
29
|
* Load a model written by toJSON. Options passed here override the model's.
|
|
30
|
+
* A model is data that may come from anywhere, so everything in it is
|
|
31
|
+
* checked: a damaged one throws a TypeError starting "Corrupt classifier
|
|
32
|
+
* model".
|
|
30
33
|
* @param {object|string} json
|
|
31
34
|
* @param {object} [options]
|
|
32
35
|
* @returns {Classifier}
|
|
@@ -67,7 +70,7 @@ export class Classifier {
|
|
|
67
70
|
/**
|
|
68
71
|
* Score a message.
|
|
69
72
|
* @param {string[]} features
|
|
70
|
-
* @returns {{probability: number, category: 'spam'|'ham'|'unsure', spam: number, ham: number, clues: Array<{feature: string, probability: number}
|
|
73
|
+
* @returns {{probability: number, category: 'spam'|'ham'|'unsure', spam: number, ham: number, clues: Array<{feature: string, probability: number}>, coverage: {feature: string|null, spam: number, ham: number, confidence: number}}}
|
|
71
74
|
*/
|
|
72
75
|
classify(features: string[]): {
|
|
73
76
|
probability: number;
|
|
@@ -78,6 +81,12 @@ export class Classifier {
|
|
|
78
81
|
feature: string;
|
|
79
82
|
probability: number;
|
|
80
83
|
}>;
|
|
84
|
+
coverage: {
|
|
85
|
+
feature: string | null;
|
|
86
|
+
spam: number;
|
|
87
|
+
ham: number;
|
|
88
|
+
confidence: number;
|
|
89
|
+
};
|
|
81
90
|
};
|
|
82
91
|
/**
|
|
83
92
|
* How well the training data covers the language of a message, from the
|
package/dist/types/dnsbl.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
+
export function isLookupName(name: any): boolean;
|
|
2
|
+
/**
|
|
3
|
+
* An IP address as blocklists and allowlists know it: without an IPv6 zone
|
|
4
|
+
* ("fe80::1%eth0"), and an IPv4 address mapped into IPv6 ("::ffff:192.0.2.1",
|
|
5
|
+
* as a dual-stack server reports IPv4 clients) as the IPv4 address, and any
|
|
6
|
+
* other IPv6 address in its canonical form (RFC 5952: "2001:db8::1", and an
|
|
7
|
+
* IPv4 address translated into IPv6 or under the NAT64 prefix in dotted form:
|
|
8
|
+
* "::ffff:0:192.0.2.1", "64:ff9b::192.0.2.1"). Anything
|
|
9
|
+
* that is not an IP address is returned as it is.
|
|
10
|
+
* @param {string} ip
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
export function normalizeIp(ip: string): string;
|
|
1
14
|
/**
|
|
2
15
|
* The DNS name to query for an IP address in a blocklist zone: reversed
|
|
3
16
|
* octets for IPv4 ("4.3.2.1.zen.spamhaus.org"), reversed nibbles for IPv6.
|
|
17
|
+
* An IPv4 address mapped into IPv6 is looked up as IPv4.
|
|
4
18
|
* @param {string} ip
|
|
5
19
|
* @param {string} zone
|
|
6
20
|
* @returns {string|null}
|
|
@@ -8,8 +22,12 @@
|
|
|
8
22
|
export function reverseName(ip: string, zone: string): string | null;
|
|
9
23
|
/**
|
|
10
24
|
* Whether an answer means "listed": an address in 127.0.0.0/8 that is not one
|
|
11
|
-
* of the error codes lists return when queries are refused
|
|
12
|
-
* Spamhaus, 127.
|
|
25
|
+
* of the error codes lists return when queries are refused or wrong: at
|
|
26
|
+
* Spamhaus, 127.255.255.x (127.255.255.252 for a misspelled zone,
|
|
27
|
+
* .254 for queries through a public resolver, .255 for too many queries) and
|
|
28
|
+
* 127.0.1.255 (an address looked up in the DBL), 127.0.0.255 anywhere, and
|
|
29
|
+
* 127.0.0.1 at SURBL and URIBL, which answer it to resolvers they block
|
|
30
|
+
* (SpamAssassin's SURBL_BLOCKED and URIBL_BLOCKED).
|
|
13
31
|
* @param {string} address
|
|
14
32
|
* @param {string} zone
|
|
15
33
|
* @returns {boolean}
|
|
@@ -69,6 +87,16 @@ export class DnsChecker {
|
|
|
69
87
|
* @returns {Promise<string[]>}
|
|
70
88
|
*/
|
|
71
89
|
resolve4(name: string, servers?: string[] | null): Promise<string[]>;
|
|
90
|
+
/**
|
|
91
|
+
* A records for a name, an empty list when the name does not exist or has
|
|
92
|
+
* no A records, and null when no answer came: the lookup timed out or
|
|
93
|
+
* failed (SERVFAIL, refused, unreachable). Only answers are cached, so a
|
|
94
|
+
* failed lookup is tried again for the next message.
|
|
95
|
+
* @param {string} name
|
|
96
|
+
* @param {string[]|null} [servers]
|
|
97
|
+
* @returns {Promise<string[]|null>}
|
|
98
|
+
*/
|
|
99
|
+
answer(name: string, servers?: string[] | null): Promise<string[] | null>;
|
|
72
100
|
/**
|
|
73
101
|
* Look an IP address up in IP blocklists.
|
|
74
102
|
* @param {string} ip
|
|
@@ -94,13 +122,15 @@ export class DnsChecker {
|
|
|
94
122
|
/**
|
|
95
123
|
* Ask Cloudflare's filtering resolvers about host names. A name blocked by
|
|
96
124
|
* the malware resolver is "malware"; one blocked only by the family
|
|
97
|
-
* resolver is "adult".
|
|
125
|
+
* resolver is "adult". The hosts that got no answer (from the malware
|
|
126
|
+
* resolver, or from the family resolver when asked) are listed in the
|
|
127
|
+
* result's (non-enumerable) `unanswered` property.
|
|
98
128
|
* @param {string[]} hosts
|
|
99
129
|
* @param {object} [options]
|
|
100
130
|
* @param {boolean} [options.adult] - also ask the family resolver
|
|
101
131
|
* @param {string[]} [options.malwareServers]
|
|
102
132
|
* @param {string[]} [options.familyServers]
|
|
103
|
-
* @returns {Promise<Array<{host: string, category: 'malware'|'adult'}
|
|
133
|
+
* @returns {Promise<Array<{host: string, category: 'malware'|'adult'}> & {unanswered: string[]}>}
|
|
104
134
|
*/
|
|
105
135
|
checkCloudflare(hosts?: string[], options?: {
|
|
106
136
|
adult?: boolean;
|
|
@@ -109,5 +139,7 @@ export class DnsChecker {
|
|
|
109
139
|
}): Promise<Array<{
|
|
110
140
|
host: string;
|
|
111
141
|
category: "malware" | "adult";
|
|
112
|
-
}
|
|
142
|
+
}> & {
|
|
143
|
+
unanswered: string[];
|
|
144
|
+
}>;
|
|
113
145
|
}
|
package/dist/types/headers.d.ts
CHANGED
|
@@ -16,8 +16,9 @@ export function foldHeader(name: string, value: string, newline?: string): strin
|
|
|
16
16
|
* - X-Spam-Level: one star per point (Sieve: header :contains "X-Spam-Level" "*****")
|
|
17
17
|
* - X-Spam-Status: Yes or No, with score, threshold, tests and version
|
|
18
18
|
* - X-Spam-Action: accept, tag or reject
|
|
19
|
+
* - X-Spam-Virus-Scan: failed, when ClamAV could not scan an attachment
|
|
19
20
|
*
|
|
20
|
-
* @param {{score: number, threshold: number, isSpam: boolean, action: string, tests: Array<{name: string}>}} result
|
|
21
|
+
* @param {{score: number, threshold: number, isSpam: boolean, action: string, tests: Array<{name: string}>, results?: {virusScanErrors?: Array<object>}}} result
|
|
21
22
|
* @param {object} [options]
|
|
22
23
|
* @param {string} [options.version]
|
|
23
24
|
* @returns {Array<[string, string]>} header name and value pairs
|
|
@@ -30,6 +31,9 @@ export function spamHeaders(result: {
|
|
|
30
31
|
tests: Array<{
|
|
31
32
|
name: string;
|
|
32
33
|
}>;
|
|
34
|
+
results?: {
|
|
35
|
+
virusScanErrors?: Array<object>;
|
|
36
|
+
};
|
|
33
37
|
}, options?: {
|
|
34
38
|
version?: string;
|
|
35
39
|
}): Array<[string, string]>;
|
|
@@ -43,6 +47,31 @@ export function splitMessage(raw: Buffer): {
|
|
|
43
47
|
body: Buffer;
|
|
44
48
|
newline: string;
|
|
45
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* A subject tag as it can go in a header: on one line, and, when it is not
|
|
52
|
+
* printable ASCII, holds "=?" (which would read as the start of an encoded
|
|
53
|
+
* word) or a word too long to fold, as encoded words (RFC 2047) of at most
|
|
54
|
+
* 64 characters, split only between whole characters and separated by
|
|
55
|
+
* spaces, so "[垃圾]" survives and a tag cannot add header lines. A tag empty
|
|
56
|
+
* after cleanup is "".
|
|
57
|
+
* @param {string} tag
|
|
58
|
+
* @param {boolean} [spaceAfter] - for a tag before an encoded word: the space
|
|
59
|
+
* after an encoded tag goes inside it, since the space between two encoded
|
|
60
|
+
* words is not shown
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
export function subjectTagText(tag: string, spaceAfter?: boolean): string;
|
|
64
|
+
/**
|
|
65
|
+
* A Subject header's value with a tag before it (see subjectTagText), folded
|
|
66
|
+
* between the tag's words so a line with an encoded word stays within 76
|
|
67
|
+
* characters, and before the subject when the line would be too long, or
|
|
68
|
+
* null when the tag is empty or the subject already starts with it.
|
|
69
|
+
* @param {string} tag
|
|
70
|
+
* @param {string} current - the subject's value, as written, perhaps folded
|
|
71
|
+
* @param {string} [newline]
|
|
72
|
+
* @returns {string|null} the value, after "Subject: "
|
|
73
|
+
*/
|
|
74
|
+
export function taggedSubject(tag: string, current: string, newline?: string): string | null;
|
|
46
75
|
/**
|
|
47
76
|
* Add headers to a raw message and optionally tag its subject.
|
|
48
77
|
*
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a name is a brand with one letter left out inside it, or with one
|
|
3
|
+
* of its letters doubled.
|
|
4
|
+
* @param {string} name
|
|
5
|
+
* @param {string} brand
|
|
6
|
+
* @returns {boolean}
|
|
7
|
+
*/
|
|
8
|
+
export function isDroppedOrDoubled(name: string, brand: string): boolean;
|
|
1
9
|
/**
|
|
2
10
|
* Map every lookalike character of a domain label to the Latin letter it
|
|
3
11
|
* imitates (Cyrillic "рауpаl" becomes "paypal").
|
|
@@ -24,6 +32,14 @@ export function editDistance(a: string, b: string, max?: number): number;
|
|
|
24
32
|
* @returns {string|null}
|
|
25
33
|
*/
|
|
26
34
|
export function brandWithPhishingWord(name: string, brands: Set<string>): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Whether two names differ only by two neighbouring letters swapped (not
|
|
37
|
+
* when they are the same).
|
|
38
|
+
* @param {string} a
|
|
39
|
+
* @param {string} b
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
42
|
+
export function isSwap(a: string, b: string): boolean;
|
|
27
43
|
/**
|
|
28
44
|
* Brands most often imitated in phishing: banks, payment services, mail and
|
|
29
45
|
* cloud providers, shops, couriers and tax offices. A domain whose name is a
|
|
@@ -31,6 +47,7 @@ export function brandWithPhishingWord(name: string, brands: Set<string>): string
|
|
|
31
47
|
* never flagged.
|
|
32
48
|
*/
|
|
33
49
|
export const BRANDS: string[];
|
|
50
|
+
export const PHISHING_WORDS: string[];
|
|
34
51
|
/**
|
|
35
52
|
* Detects domains made to look like well-known brands: lookalike letters from
|
|
36
53
|
* other scripts (Cyrillic "а" for Latin "a"), mixed scripts in one label,
|
|
@@ -79,7 +96,7 @@ export default class HomographDetector {
|
|
|
79
96
|
recommendations: string[];
|
|
80
97
|
confidence: number;
|
|
81
98
|
};
|
|
82
|
-
analyse(input: any): {
|
|
99
|
+
analyse(input: any, written?: any): {
|
|
83
100
|
domain: any;
|
|
84
101
|
unicode: any;
|
|
85
102
|
isIDN: boolean;
|
package/dist/types/html.d.ts
CHANGED
|
@@ -12,6 +12,35 @@
|
|
|
12
12
|
export function decodeEntities(value: string, { attribute }?: {
|
|
13
13
|
attribute?: boolean;
|
|
14
14
|
}): string;
|
|
15
|
+
/**
|
|
16
|
+
* The values of the attributes of an HTML document that may hold words (see
|
|
17
|
+
* NO_WORD_ATTRIBUTES), with their character references decoded, where the
|
|
18
|
+
* tag that holds each starts and ends, and whether it is a form's help text
|
|
19
|
+
* (see FIELD_HELP), in order: not
|
|
20
|
+
* those in comments or in the raw text of scripts and styles, and none after
|
|
21
|
+
* a tag the input ends inside, as browsers read them. In time linear in the
|
|
22
|
+
* input.
|
|
23
|
+
* @param {string} html
|
|
24
|
+
* @returns {Array<{text: string, start: number, end: number, help: boolean}>}
|
|
25
|
+
*/
|
|
26
|
+
export function attributeText(html: string): Array<{
|
|
27
|
+
text: string;
|
|
28
|
+
start: number;
|
|
29
|
+
end: number;
|
|
30
|
+
help: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Where a form's help text is in an HTML document (see FIELD_HELP): the
|
|
34
|
+
* values of those attributes and the text of each <label> of at most
|
|
35
|
+
* MAX_HELP characters, as places in the input, in order of where they start
|
|
36
|
+
* (a label's text may hold another's). In time linear in the input.
|
|
37
|
+
* @param {string} html
|
|
38
|
+
* @returns {Array<{from: number, to: number}>}
|
|
39
|
+
*/
|
|
40
|
+
export function formHelp(html: string): Array<{
|
|
41
|
+
from: number;
|
|
42
|
+
to: number;
|
|
43
|
+
}>;
|
|
15
44
|
/**
|
|
16
45
|
* The text of HTML as a mail client shows it, read in linear time: without
|
|
17
46
|
* tags, comments, scripts, styles and the title, with character references
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as m from './index.js' with {'resolution-mode': 'import'};
|
|
2
|
+
type SpamScanner = m.SpamScanner;
|
|
3
|
+
declare const SpamScanner: typeof m.SpamScanner & Omit<typeof m, 'default'> & {default: typeof m.SpamScanner};
|
|
4
|
+
declare namespace SpamScanner {
|
|
5
|
+
export type SpamScanner = m.SpamScanner;
|
|
6
|
+
export type Classifier = m.Classifier;
|
|
7
|
+
export type MilterServer = m.MilterServer;
|
|
8
|
+
export type SmtpSession = m.SmtpSession;
|
|
9
|
+
export type ReputationOptions = m.ReputationOptions;
|
|
10
|
+
export type LegacyOptions = m.LegacyOptions;
|
|
11
|
+
export type SpamScannerOptions = m.SpamScannerOptions;
|
|
12
|
+
export type ScannerSettings = m.ScannerSettings;
|
|
13
|
+
export type ScanOptions = m.ScanOptions;
|
|
14
|
+
export type ScanTest = m.ScanTest;
|
|
15
|
+
export type ScanFinding = m.ScanFinding;
|
|
16
|
+
export type ReputationAnswer = m.ReputationAnswer;
|
|
17
|
+
export type ReputationResult = m.ReputationResult;
|
|
18
|
+
export type ScanDetails = m.ScanDetails;
|
|
19
|
+
export type ScanResult = m.ScanResult;
|
|
20
|
+
export type LLMClassifier = m.LLMClassifier;
|
|
21
|
+
}
|
|
22
|
+
export = SpamScanner;
|