spamscanner 7.0.4 → 7.0.5
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 +245 -11
- package/dist/cjs/arf.cjs.map +3 -3
- package/dist/cjs/index.cjs +858 -408
- package/dist/cjs/index.cjs.map +4 -4
- package/dist/esm/arf.js +246 -12
- package/dist/esm/arf.js.map +3 -3
- package/dist/esm/cli.js +993 -444
- package/dist/esm/cli.js.map +4 -4
- package/dist/esm/index.js +835 -383
- package/dist/esm/index.js.map +4 -4
- package/dist/types/arf.d.ts +4 -4
- package/dist/types/attachments.d.ts +3 -1
- package/dist/types/auth.d.ts +32 -2
- package/dist/types/milter.d.ts +7 -1
- package/dist/types/model.d.ts +3 -2
- package/dist/types/parse.d.ts +66 -4
- package/dist/types/reputation.d.ts +2 -1
- package/dist/types/server.d.ts +17 -0
- package/dist/types/spamd.d.ts +2 -0
- package/package.json +1 -1
package/dist/types/arf.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export function findPart(raw: string, boundary: string, types: string[]): {
|
|
|
28
28
|
export function isArfMessage(parsed: object): boolean;
|
|
29
29
|
/**
|
|
30
30
|
* Parse an ARF (RFC 5965) abuse report.
|
|
31
|
-
* @param {Buffer|string} source - the raw report message
|
|
31
|
+
* @param {Buffer|Uint8Array|string} source - the raw report message
|
|
32
32
|
* @returns {Promise<object>} feedbackType, userAgent, version, arrivalDate,
|
|
33
33
|
* sourceIp, originalMailFrom, originalRcptTo, reportingMta,
|
|
34
34
|
* originalEnvelopeId, authenticationResults, reportedDomain, reportedUri,
|
|
@@ -37,13 +37,13 @@ export function isArfMessage(parsed: object): boolean;
|
|
|
37
37
|
* message) and rawFeedbackReport
|
|
38
38
|
* @throws {Error} when the message is not a valid ARF report
|
|
39
39
|
*/
|
|
40
|
-
export function parse(
|
|
40
|
+
export function parse(message: any): Promise<object>;
|
|
41
41
|
/**
|
|
42
42
|
* Parse an ARF report, or return null when the message is not one.
|
|
43
|
-
* @param {Buffer|string} source
|
|
43
|
+
* @param {Buffer|Uint8Array|string} source
|
|
44
44
|
* @returns {Promise<object|null>}
|
|
45
45
|
*/
|
|
46
|
-
export function tryParse(source: Buffer | string): Promise<object | null>;
|
|
46
|
+
export function tryParse(source: Buffer | Uint8Array | string): Promise<object | null>;
|
|
47
47
|
/**
|
|
48
48
|
* Write an ARF report. (A string message gives a string report, and a
|
|
49
49
|
* Buffer a Buffer, its bytes kept as they are, whatever their character set.)
|
|
@@ -21,7 +21,9 @@ export function sniff(buffer: Buffer): {
|
|
|
21
21
|
* is read, as well as the last one found: archive programs differ in which
|
|
22
22
|
* they use, so a directory hidden behind another record is read too. ZIP64
|
|
23
23
|
* archives, and archives after other data (self-extracting archives), are
|
|
24
|
-
* read as well.
|
|
24
|
+
* read as well. Each entry is read once, however many directories overlap
|
|
25
|
+
* it; a name longer than 1 KiB keeps its first and last 512 bytes (with "…"
|
|
26
|
+
* between them), and reading stops after 1 MiB of names.
|
|
25
27
|
* @param {Buffer} buffer
|
|
26
28
|
* @param {number} [limit] - most entries to read
|
|
27
29
|
* @returns {Array<{name: string, encrypted: boolean, size: number}>}
|
package/dist/types/auth.d.ts
CHANGED
|
@@ -8,6 +8,20 @@
|
|
|
8
8
|
* @returns {(name: string, type: string) => Promise<any[]>}
|
|
9
9
|
*/
|
|
10
10
|
export function createResolver(timeout?: number, servers?: string[]): (name: string, type: string) => Promise<any[]>;
|
|
11
|
+
/**
|
|
12
|
+
* A resolver that answers through `resolver` at most `maxQueries` times and
|
|
13
|
+
* until `deadline` (a time in milliseconds, as Date.now() gives it): any
|
|
14
|
+
* lookup after either, or still unanswered at the deadline, fails at once
|
|
15
|
+
* with ETIMEOUT, so a message's checks stop sending queries when its time is
|
|
16
|
+
* up and never send more than `maxQueries`.
|
|
17
|
+
* @param {(name: string, type: string) => Promise<any[]>} resolver
|
|
18
|
+
* @param {{maxQueries?: number, deadline?: number}} [limits]
|
|
19
|
+
* @returns {(name: string, type: string) => Promise<any[]>}
|
|
20
|
+
*/
|
|
21
|
+
export function limitResolver(resolver: (name: string, type: string) => Promise<any[]>, { maxQueries, deadline }?: {
|
|
22
|
+
maxQueries?: number;
|
|
23
|
+
deadline?: number;
|
|
24
|
+
}): (name: string, type: string) => Promise<any[]>;
|
|
11
25
|
/**
|
|
12
26
|
* Overall DKIM result from mailauth's per-signature results: "pass" when any
|
|
13
27
|
* signature passes, "fail" when signatures exist and none passes, "none"
|
|
@@ -57,6 +71,7 @@ export function normalizeAuthOutput(output?: object, options?: {
|
|
|
57
71
|
*
|
|
58
72
|
* Needs the connecting client's IP address: without it nothing is checked and
|
|
59
73
|
* every result is "none". DNS errors give "temperror" results, never a pass.
|
|
74
|
+
* Only the first MAX_DKIM_SIGNATURES DKIM signatures are checked.
|
|
60
75
|
*
|
|
61
76
|
* @param {Buffer|string} message - the raw message
|
|
62
77
|
* @param {object} options
|
|
@@ -65,7 +80,11 @@ export function normalizeAuthOutput(output?: object, options?: {
|
|
|
65
80
|
* @param {string} [options.sender] - envelope sender (MAIL FROM)
|
|
66
81
|
* @param {string} [options.mta] - this server's hostname, for the headers
|
|
67
82
|
* @param {Function} [options.resolver] - custom DNS resolver (name, type)
|
|
68
|
-
* @param {number} [options.timeout] - DNS
|
|
83
|
+
* @param {number} [options.timeout] - milliseconds for a DNS lookup, and for
|
|
84
|
+
* all of the message's: no lookup is sent, nor waited for, after this long
|
|
85
|
+
* (and 100 ms more)
|
|
86
|
+
* @param {number} [options.maxDnsQueries] - DNS lookups allowed for the
|
|
87
|
+
* message (MAX_DNS_QUERIES); any more fail with a temporary error
|
|
69
88
|
* @param {string[]} [options.dnsServers] - name servers to use
|
|
70
89
|
* @param {string[]} [options.trustedArcSealers] - forwarders (such as a
|
|
71
90
|
* mailing list's domain) whose ARC seal is believed: a message they passed
|
|
@@ -81,6 +100,7 @@ export function authenticate(message: Buffer | string, options?: {
|
|
|
81
100
|
mta?: string;
|
|
82
101
|
resolver?: Function;
|
|
83
102
|
timeout?: number;
|
|
103
|
+
maxDnsQueries?: number;
|
|
84
104
|
dnsServers?: string[];
|
|
85
105
|
trustedArcSealers?: string[];
|
|
86
106
|
}): Promise<object>;
|
|
@@ -109,7 +129,8 @@ export function calculateAuthScore(auth: object, weights?: object): {
|
|
|
109
129
|
};
|
|
110
130
|
/**
|
|
111
131
|
* The Authentication-Results header (RFC 8601) for a result, without the
|
|
112
|
-
* header name. mailauth's own headers are used when present
|
|
132
|
+
* header name. mailauth's own headers are used when present, with as many
|
|
133
|
+
* of their results as fit in 8 KiB.
|
|
113
134
|
* @param {object} auth
|
|
114
135
|
* @param {string} [hostname]
|
|
115
136
|
* @returns {string}
|
|
@@ -121,6 +142,15 @@ export function formatAuthResultsHeader(auth: object, hostname?: string): string
|
|
|
121
142
|
* @returns {string}
|
|
122
143
|
*/
|
|
123
144
|
export function summarizeAuth(auth: object): string;
|
|
145
|
+
export { MAX_DKIM_SIGNATURES } from "./parse.js";
|
|
146
|
+
/**
|
|
147
|
+
* DNS lookups allowed for one message: room for SPF at the limits of RFC
|
|
148
|
+
* 7208 (10 lookups, each MX mechanism with up to 10 hosts, A and AAAA, about
|
|
149
|
+
* 220 in all, as mailauth counts them), MAX_DKIM_SIGNATURES keys (see
|
|
150
|
+
* boundedForAuth), the
|
|
151
|
+
* newest ARC seal, DMARC and BIMI.
|
|
152
|
+
*/
|
|
153
|
+
export const MAX_DNS_QUERIES: 300;
|
|
124
154
|
export namespace DEFAULT_AUTH_WEIGHTS {
|
|
125
155
|
let dkimPass: number;
|
|
126
156
|
let dkimFail: number;
|
package/dist/types/milter.d.ts
CHANGED
|
@@ -116,6 +116,8 @@ export class MilterSession {
|
|
|
116
116
|
body: any[];
|
|
117
117
|
size: number;
|
|
118
118
|
truncated: boolean;
|
|
119
|
+
headerSize: number;
|
|
120
|
+
keep(data: any): void;
|
|
119
121
|
send(command: any, data: any): void;
|
|
120
122
|
receive(chunk: any): void;
|
|
121
123
|
process(): Promise<void>;
|
|
@@ -146,8 +148,10 @@ export class MilterServer extends EventEmitter<[never]> {
|
|
|
146
148
|
* @param {boolean} [options.quarantine] - hold spam in the MTA's quarantine
|
|
147
149
|
* @param {string|null} [options.subjectTag] - e.g. "[SPAM]"
|
|
148
150
|
* @param {string} [options.hostname] - this server's name, for Authentication-Results
|
|
149
|
-
* @param {number} [options.maxSize] - body bytes kept for scanning
|
|
151
|
+
* @param {number} [options.maxSize] - body bytes kept for scanning (and the
|
|
152
|
+
* most bytes of envelope and headers: beyond them a message is not scanned)
|
|
150
153
|
* @param {'tempfail'|'accept'} [options.onError] - answer when a scan fails
|
|
154
|
+
* @param {number} [options.maxConnections] - connections kept open at once; more are closed at once
|
|
151
155
|
*/
|
|
152
156
|
constructor(scanner: import("./index.js").SpamScanner, options?: {
|
|
153
157
|
reject?: boolean;
|
|
@@ -158,6 +162,7 @@ export class MilterServer extends EventEmitter<[never]> {
|
|
|
158
162
|
hostname?: string;
|
|
159
163
|
maxSize?: number;
|
|
160
164
|
onError?: "tempfail" | "accept";
|
|
165
|
+
maxConnections?: number;
|
|
161
166
|
});
|
|
162
167
|
scanner: import("./index.js").SpamScanner;
|
|
163
168
|
options: {
|
|
@@ -169,6 +174,7 @@ export class MilterServer extends EventEmitter<[never]> {
|
|
|
169
174
|
hostname: string;
|
|
170
175
|
maxSize: number;
|
|
171
176
|
onError: string;
|
|
177
|
+
maxConnections: number;
|
|
172
178
|
};
|
|
173
179
|
sockets: Set<any>;
|
|
174
180
|
server: net.Server;
|
package/dist/types/model.d.ts
CHANGED
|
@@ -49,8 +49,9 @@ export function loadDefaultModel(options?: object, { file, sea }?: {
|
|
|
49
49
|
export function loadModel(file: string, options?: object): Classifier;
|
|
50
50
|
/**
|
|
51
51
|
* Write a classifier to a file, creating its directory. The model is written
|
|
52
|
-
* to a temporary file next to it first, then renamed, so
|
|
53
|
-
* the file
|
|
52
|
+
* to a new temporary file next to it first, flushed to disk, then renamed, so
|
|
53
|
+
* a process loading the file, or one reading it after a crash, never reads
|
|
54
|
+
* half of it. A file replaced keeps its permissions.
|
|
54
55
|
* @param {Classifier} classifier
|
|
55
56
|
* @param {string} file
|
|
56
57
|
* @param {object} [options] - see Classifier#toJSON
|
package/dist/types/parse.d.ts
CHANGED
|
@@ -1,11 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The message as mailauth is to read it: its address fields bounded as
|
|
3
|
+
* parseMessage bounds them (mailauth reads From and Return-Path with the same
|
|
4
|
+
* parser), and, when the header would take mailauth longer than linear time
|
|
5
|
+
* (it joins folded lines, and finds the fields a DKIM signature names, in
|
|
6
|
+
* time that grows with the square of their number), the header cut down:
|
|
7
|
+
* its folded lines unfolded (which relaxed canonicalization of DKIM does
|
|
8
|
+
* too) and only the fields that say who sent the message and how, as many
|
|
9
|
+
* as smallerHeader keeps, each up to MAX_FIELD characters (address fields as
|
|
10
|
+
* bounded), and at most MAX_SIGNATURES signatures. Only the first
|
|
11
|
+
* MAX_DKIM_SIGNATURES DKIM-Signature fields are ever kept (with their folded
|
|
12
|
+
* lines; the body is untouched). `fromCut` is whether a
|
|
13
|
+
* From field was cut or left out: then mailauth did not read the From
|
|
14
|
+
* address the message gives.
|
|
15
|
+
* @param {Buffer} raw
|
|
16
|
+
* @returns {{raw: Buffer, fromCut: boolean}}
|
|
17
|
+
*/
|
|
18
|
+
export function boundedForAuth(raw: Buffer): {
|
|
19
|
+
raw: Buffer;
|
|
20
|
+
fromCut: boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The simpleParser of mailparser on a message it can finish (see finishable),
|
|
24
|
+
* settled within `timeout`: a parse that has not settled by then is stopped
|
|
25
|
+
* (its parser destroyed) and rejected with an ETIMEDOUT error.
|
|
26
|
+
* @param {Buffer|string} raw
|
|
27
|
+
* @param {object} [options]
|
|
28
|
+
* @param {number} [options.timeout] - milliseconds (PARSE_TIMEOUT)
|
|
29
|
+
* @param {typeof simpleParser} [options.parser] - for tests
|
|
30
|
+
* @returns {Promise<object>} the parsed message
|
|
31
|
+
*/
|
|
32
|
+
export function simpleParse(raw: Buffer | string, { timeout, parser }?: {
|
|
33
|
+
timeout?: number;
|
|
34
|
+
parser?: typeof simpleParser;
|
|
35
|
+
}): Promise<object>;
|
|
1
36
|
/**
|
|
2
37
|
* Parse a message with mailparser, which does not make text of HTML here:
|
|
3
38
|
* `text` is that of the plain text parts only (empty for a message of HTML
|
|
4
39
|
* alone), and the text HTML shows is read by getFeatures.
|
|
5
40
|
* @param {Buffer|string} raw
|
|
41
|
+
* @param {{timeout?: number}} [options] - milliseconds for the parse (PARSE_TIMEOUT)
|
|
6
42
|
* @returns {Promise<object>} the parsed message
|
|
7
43
|
*/
|
|
8
|
-
export function parseMessage(raw: Buffer | string
|
|
44
|
+
export function parseMessage(raw: Buffer | string, options?: {
|
|
45
|
+
timeout?: number;
|
|
46
|
+
}): Promise<object>;
|
|
9
47
|
/**
|
|
10
48
|
* A message mailparser can finish: it never finishes one that ends in the
|
|
11
49
|
* header of an attached message (however that header names or folds its
|
|
@@ -15,6 +53,17 @@ export function parseMessage(raw: Buffer | string): Promise<object>;
|
|
|
15
53
|
* @returns {Buffer}
|
|
16
54
|
*/
|
|
17
55
|
export function finishable(raw: Buffer | string): Buffer;
|
|
56
|
+
/**
|
|
57
|
+
* The message with each header field folded on more than MAX_FOLDS lines
|
|
58
|
+
* unfolded: its line breaks and the white space after each made one space,
|
|
59
|
+
* as mailparser reads a field (libmime's decodeHeader), so what it reads is
|
|
60
|
+
* the same. Header fields are those of the message and, as mailsplit finds
|
|
61
|
+
* them, of each MIME part and each attached message inline; everything else
|
|
62
|
+
* is left as it is. The message itself when nothing is unfolded.
|
|
63
|
+
* @param {Buffer} raw
|
|
64
|
+
* @returns {Buffer}
|
|
65
|
+
*/
|
|
66
|
+
export function unfoldHeaders(raw: Buffer): Buffer;
|
|
18
67
|
/**
|
|
19
68
|
* Parse a message, reading what can be read of one beyond mailparser's
|
|
20
69
|
* limits (more than 1000 MIME parts, a part's header over 1 MB): its parts
|
|
@@ -23,12 +72,16 @@ export function finishable(raw: Buffer | string): Buffer;
|
|
|
23
72
|
* is too large, the message with its header lines cut down, or its first
|
|
24
73
|
* megabyte when the header is that of a later part. `error` is the limit,
|
|
25
74
|
* or null. Other errors are thrown, as earlier versions did (servers defer
|
|
26
|
-
* the message).
|
|
75
|
+
* the message), and so is a parse not finished by `deadline` (ETIMEDOUT).
|
|
27
76
|
* @param {Buffer} raw
|
|
28
|
-
* @param {(raw: Buffer) => Promise<object>} [parse] - for tests
|
|
77
|
+
* @param {(raw: Buffer, options: {timeout: number}) => Promise<object>} [parse] - for tests
|
|
78
|
+
* @param {number} [deadline] - Date.now() milliseconds by which every try
|
|
79
|
+
* ends (PARSE_TIMEOUT from now)
|
|
29
80
|
* @returns {Promise<{mail: object, error: string|null}>}
|
|
30
81
|
*/
|
|
31
|
-
export function parseWithin(raw: Buffer, parse?: (raw: Buffer
|
|
82
|
+
export function parseWithin(raw: Buffer, parse?: (raw: Buffer, options: {
|
|
83
|
+
timeout: number;
|
|
84
|
+
}) => Promise<object>, deadline?: number): Promise<{
|
|
32
85
|
mail: object;
|
|
33
86
|
error: string | null;
|
|
34
87
|
}>;
|
|
@@ -43,4 +96,13 @@ export function parseWithin(raw: Buffer, parse?: (raw: Buffer) => Promise<object
|
|
|
43
96
|
* @returns {Promise<Array<object>>}
|
|
44
97
|
*/
|
|
45
98
|
export function expandAttachments(attachments?: Array<object>, errors?: string[], parse?: (raw: Buffer) => Promise<object>): Promise<Array<object>>;
|
|
99
|
+
/**
|
|
100
|
+
* DKIM-Signature fields mailauth reads of a message (see boundedForAuth): it
|
|
101
|
+
* checks every signature, hashing the body once for each body length (l=)
|
|
102
|
+
* and canonicalization and looking up each signer's key, so hundreds of them
|
|
103
|
+
* would hold up the process and send as many DNS queries. Real mail carries
|
|
104
|
+
* one to three.
|
|
105
|
+
*/
|
|
106
|
+
export const MAX_DKIM_SIGNATURES: 8;
|
|
107
|
+
export const PARSE_TIMEOUT: 60000;
|
|
46
108
|
import { Buffer } from 'node:buffer';
|
|
@@ -71,7 +71,8 @@ export function reputationValues(mail?: object, session?: object): string[];
|
|
|
71
71
|
* domain itself vouches for it. A DKIM signature of part of the body (l=
|
|
72
72
|
* shorter than the body, mailauth's underSized) vouches for nothing, nor
|
|
73
73
|
* does a DMARC pass that rests on one alone, and no signature vouches for a
|
|
74
|
-
* From address when DMARC fails
|
|
74
|
+
* From address when DMARC fails, nor for one of a From field too long to read
|
|
75
|
+
* whole (`mail.from.truncated`).
|
|
75
76
|
* @param {object} mail - parsed message
|
|
76
77
|
* @param {object} [session] - remoteAddress, resolvedClientHostname, envelope
|
|
77
78
|
* @param {object|null} [authentication] - SPF, DKIM and DMARC results
|
package/dist/types/server.d.ts
CHANGED
|
@@ -35,12 +35,14 @@ export function sessionFromQuery(query: URLSearchParams): object;
|
|
|
35
35
|
* @param {string} [options.token] - required as "Authorization: Bearer <token>" when set
|
|
36
36
|
* @param {number} [options.maxSize] - largest message accepted, in bytes
|
|
37
37
|
* @param {string} [options.modelPath] - where /learn saves the classifier
|
|
38
|
+
* @param {number} [options.maxConnections] - connections kept open, and requests handled, at once
|
|
38
39
|
* @returns {http.Server}
|
|
39
40
|
*/
|
|
40
41
|
export function createHttpServer(scanner: import("./index.js").SpamScanner, options?: {
|
|
41
42
|
token?: string;
|
|
42
43
|
maxSize?: number;
|
|
43
44
|
modelPath?: string;
|
|
45
|
+
maxConnections?: number;
|
|
44
46
|
}): http.Server;
|
|
45
47
|
/**
|
|
46
48
|
* A plain TCP server: a client sends a raw message and closes its side; the
|
|
@@ -54,12 +56,27 @@ export function createHttpServer(scanner: import("./index.js").SpamScanner, opti
|
|
|
54
56
|
* @param {boolean} [options.json] - answer in JSON (default true)
|
|
55
57
|
* @param {number} [options.maxSize]
|
|
56
58
|
* @param {number} [options.timeout] - close a connection that sends nothing for this many milliseconds (default 60000)
|
|
59
|
+
* @param {number} [options.maxConnections] - connections kept open at once; more are closed at once
|
|
57
60
|
* @returns {net.Server}
|
|
58
61
|
*/
|
|
59
62
|
export function createTcpServer(scanner: import("./index.js").SpamScanner, options?: {
|
|
60
63
|
json?: boolean;
|
|
61
64
|
maxSize?: number;
|
|
62
65
|
timeout?: number;
|
|
66
|
+
maxConnections?: number;
|
|
63
67
|
}): net.Server;
|
|
68
|
+
/**
|
|
69
|
+
* The largest message the servers and the command line read by default, in
|
|
70
|
+
* bytes (25 MiB): above Postfix's default message_size_limit (10 MB) and the
|
|
71
|
+
* limits of most mail services.
|
|
72
|
+
*/
|
|
73
|
+
export const MAX_MESSAGE_SIZE: number;
|
|
74
|
+
/**
|
|
75
|
+
* The most connections each server keeps open at once by default (and, for
|
|
76
|
+
* the HTTP API, requests it handles at once): each may hold a message of up
|
|
77
|
+
* to MAX_MESSAGE_SIZE bytes while it is scanned. Postfix runs at most 100
|
|
78
|
+
* smtpd and 100 cleanup processes by default, each with one milter connection.
|
|
79
|
+
*/
|
|
80
|
+
export const MAX_CONNECTIONS: 200;
|
|
64
81
|
import http from 'node:http';
|
|
65
82
|
import net from 'node:net';
|
package/dist/types/spamd.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export function answer(scanner: import("./index.js").SpamScanner, request: {
|
|
|
43
43
|
* @param {string} [options.modelPath] - where TELL saves the classifier
|
|
44
44
|
* @param {string|null} [options.subjectTag] - tag for spam in PROCESS and HEADERS answers
|
|
45
45
|
* @param {number} [options.timeout] - close a connection that sends nothing for this many milliseconds (default 60000)
|
|
46
|
+
* @param {number} [options.maxConnections] - connections kept open at once; more are closed at once
|
|
46
47
|
* @returns {net.Server}
|
|
47
48
|
*/
|
|
48
49
|
export function createSpamdServer(scanner: import("./index.js").SpamScanner, options?: {
|
|
@@ -51,6 +52,7 @@ export function createSpamdServer(scanner: import("./index.js").SpamScanner, opt
|
|
|
51
52
|
modelPath?: string;
|
|
52
53
|
subjectTag?: string | null;
|
|
53
54
|
timeout?: number;
|
|
55
|
+
maxConnections?: number;
|
|
54
56
|
}): net.Server;
|
|
55
57
|
export namespace SPAMD_EXIT_CODES {
|
|
56
58
|
export { EX_OK };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spamscanner",
|
|
3
3
|
"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.",
|
|
4
|
-
"version": "7.0.
|
|
4
|
+
"version": "7.0.5",
|
|
5
5
|
"author": "Forward Email (https://forwardemail.net)",
|
|
6
6
|
"bin": {
|
|
7
7
|
"spamscanner": "./dist/esm/cli.js"
|