spamscanner 6.0.1 → 6.1.0

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.
@@ -0,0 +1,129 @@
1
+ /**
2
+ * ARF (Abuse Reporting Format) Parser Type Definitions
3
+ *
4
+ * @see https://www.rfc-editor.org/rfc/rfc5965.html
5
+ */
6
+
7
+ /**
8
+ * Valid feedback types as defined in RFC 5965 and extensions
9
+ */
10
+ export declare const VALID_FEEDBACK_TYPES: Set<string>;
11
+
12
+ /**
13
+ * Reporting MTA information
14
+ */
15
+ export interface ReportingMta {
16
+ /** MTA type (dns, smtp, etc.) */
17
+ type: string;
18
+ /** MTA hostname */
19
+ name: string;
20
+ }
21
+
22
+ /**
23
+ * Parsed ARF report result
24
+ */
25
+ export interface ArfResult {
26
+ /** Whether this is a valid ARF message */
27
+ isArf: boolean;
28
+ /** ARF version (usually "1") */
29
+ version: string | null;
30
+ /** Feedback type (abuse, fraud, virus, other, not-spam, auth-failure, dmarc) */
31
+ feedbackType: string | null;
32
+ /** Original feedback type if it was normalized to "other" */
33
+ feedbackTypeOriginal?: string;
34
+ /** User agent that generated the report */
35
+ userAgent: string | null;
36
+ /** Arrival date of the original message */
37
+ arrivalDate: Date | null;
38
+ /** Source IP address of the original message */
39
+ sourceIp: string | null;
40
+ /** Original MAIL FROM address */
41
+ originalMailFrom: string | null;
42
+ /** Original RCPT TO addresses */
43
+ originalRcptTo: string[] | null;
44
+ /** Reporting MTA information */
45
+ reportingMta: ReportingMta | null;
46
+ /** Original envelope ID */
47
+ originalEnvelopeId: string | null;
48
+ /** Authentication results */
49
+ authenticationResults: string[] | null;
50
+ /** Reported domains */
51
+ reportedDomain: string[] | null;
52
+ /** Reported URIs */
53
+ reportedUri: string[] | null;
54
+ /** Number of incidents */
55
+ incidents: number;
56
+ /** Human-readable description */
57
+ humanReadable: string | null;
58
+ /** Original message content */
59
+ originalMessage: string | null;
60
+ /** Parsed headers from original message */
61
+ originalHeaders: Record<string, unknown> | null;
62
+ /** Raw feedback report content */
63
+ rawFeedbackReport: string | null;
64
+ }
65
+
66
+ /**
67
+ * Options for creating an ARF report
68
+ */
69
+ export interface ArfCreateOptions {
70
+ /** Feedback type (abuse, fraud, virus, other) */
71
+ feedbackType: string;
72
+ /** User agent string */
73
+ userAgent: string;
74
+ /** From address for the report */
75
+ from: string;
76
+ /** To address for the report */
77
+ to: string;
78
+ /** Original message content */
79
+ originalMessage: string;
80
+ /** Human-readable description */
81
+ humanReadable?: string;
82
+ /** Source IP of original message */
83
+ sourceIp?: string;
84
+ /** Original MAIL FROM address */
85
+ originalMailFrom?: string;
86
+ /** Original RCPT TO addresses */
87
+ originalRcptTo?: string[];
88
+ /** Arrival date of original message */
89
+ arrivalDate?: Date;
90
+ /** Reporting MTA name */
91
+ reportingMta?: string;
92
+ }
93
+
94
+ /**
95
+ * ARF Parser class for parsing and creating ARF (Abuse Reporting Format) messages
96
+ */
97
+ export declare class ArfParser {
98
+ /**
99
+ * Check if a parsed email message is an ARF report
100
+ * @param parsed - Parsed email message from mailparser
101
+ * @returns True if message is ARF
102
+ */
103
+ static isArfMessage(parsed: unknown): boolean;
104
+
105
+ /**
106
+ * Parse an ARF message
107
+ * @param source - Raw email message as Buffer or string
108
+ * @returns Parsed ARF report
109
+ * @throws Error if not a valid ARF message
110
+ */
111
+ static parse(source: Buffer | string): Promise<ArfResult>;
112
+
113
+ /**
114
+ * Try to parse a message as ARF, return null if not ARF
115
+ * @param source - Raw email message as Buffer or string
116
+ * @returns Parsed ARF report or null if not ARF
117
+ */
118
+ static tryParse(source: Buffer | string): Promise<ArfResult | null>;
119
+
120
+ /**
121
+ * Create an ARF report message
122
+ * @param options - Report options
123
+ * @returns ARF message as string
124
+ * @throws Error if missing required fields
125
+ */
126
+ static create(options: ArfCreateOptions): string;
127
+ }
128
+
129
+ export default ArfParser;
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Email Authentication Module Type Definitions
3
+ */
4
+
5
+ export interface DkimResult {
6
+ results: DkimSignatureResult[];
7
+ status: AuthStatus;
8
+ }
9
+
10
+ export interface DkimSignatureResult {
11
+ signingDomain?: string;
12
+ selector?: string;
13
+ algo?: string;
14
+ format?: string;
15
+ signature?: string;
16
+ bodyHash?: string;
17
+ status: AuthStatus;
18
+ }
19
+
20
+ export interface SpfResult {
21
+ status: AuthStatus;
22
+ domain: string | null;
23
+ explanation?: string | null;
24
+ }
25
+
26
+ export interface DmarcResult {
27
+ status: AuthStatus;
28
+ policy: string | null;
29
+ domain: string | null;
30
+ p?: string | null;
31
+ sp?: string | null;
32
+ pct?: number | null;
33
+ }
34
+
35
+ export interface ArcResult {
36
+ status: AuthStatus;
37
+ chain: ArcChainEntry[];
38
+ i?: number | null;
39
+ }
40
+
41
+ export interface ArcChainEntry {
42
+ i: number;
43
+ cv: string;
44
+ status: AuthStatus;
45
+ }
46
+
47
+ export interface BimiResult {
48
+ status: AuthStatus;
49
+ location: string | null;
50
+ authority: string | null;
51
+ selector?: string | null;
52
+ }
53
+
54
+ export interface AuthStatus {
55
+ result: 'pass' | 'fail' | 'softfail' | 'neutral' | 'none' | 'temperror' | 'permerror';
56
+ comment?: string;
57
+ }
58
+
59
+ export interface AuthResult {
60
+ dkim: DkimResult;
61
+ spf: SpfResult;
62
+ dmarc: DmarcResult;
63
+ arc: ArcResult;
64
+ bimi: BimiResult;
65
+ receivedChain: ReceivedChainEntry[];
66
+ headers: Record<string, string>;
67
+ }
68
+
69
+ export interface ReceivedChainEntry {
70
+ from?: string;
71
+ by?: string;
72
+ with?: string;
73
+ id?: string;
74
+ for?: string;
75
+ date?: string;
76
+ }
77
+
78
+ export interface AuthOptions {
79
+ ip: string;
80
+ helo?: string;
81
+ mta?: string;
82
+ sender?: string;
83
+ resolver?: (name: string, type: string) => Promise<string[]>;
84
+ timeout?: number;
85
+ }
86
+
87
+ export interface AuthScoreWeights {
88
+ dkimPass?: number;
89
+ dkimFail?: number;
90
+ spfPass?: number;
91
+ spfFail?: number;
92
+ spfSoftfail?: number;
93
+ dmarcPass?: number;
94
+ dmarcFail?: number;
95
+ arcPass?: number;
96
+ arcFail?: number;
97
+ }
98
+
99
+ export interface AuthScoreResult {
100
+ score: number;
101
+ tests: string[];
102
+ details: {
103
+ dkim: string;
104
+ spf: string;
105
+ dmarc: string;
106
+ arc: string;
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Authenticate an email message
112
+ */
113
+ export function authenticate(
114
+ message: Buffer | string,
115
+ options?: AuthOptions
116
+ ): Promise<AuthResult>;
117
+
118
+ /**
119
+ * Perform SPF check only
120
+ */
121
+ export function checkSpf(
122
+ ip: string,
123
+ sender: string,
124
+ helo?: string,
125
+ options?: Partial<AuthOptions>
126
+ ): Promise<SpfResult>;
127
+
128
+ /**
129
+ * Verify DKIM signature
130
+ */
131
+ export function verifyDkim(
132
+ message: Buffer | string,
133
+ options?: Partial<AuthOptions>
134
+ ): Promise<DkimResult>;
135
+
136
+ /**
137
+ * Calculate authentication score based on results
138
+ */
139
+ export function calculateAuthScore(
140
+ authResult: AuthResult,
141
+ weights?: AuthScoreWeights
142
+ ): AuthScoreResult;
143
+
144
+ /**
145
+ * Format authentication results as Authentication-Results header
146
+ */
147
+ export function formatAuthResultsHeader(
148
+ authResult: AuthResult,
149
+ hostname?: string
150
+ ): string;
151
+
152
+ /**
153
+ * Create a DNS resolver with timeout support
154
+ */
155
+ export function createResolver(
156
+ timeout?: number
157
+ ): (name: string, type: string) => Promise<string[]>;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Get Attributes Module Type Definitions
3
+ * Based on Forward Email's get-attributes helper
4
+ */
5
+
6
+ import type {AuthenticationResults} from './auth';
7
+
8
+ /**
9
+ * Session information for attribute extraction
10
+ */
11
+ export interface SessionInfo {
12
+ /** Resolved hostname of connecting client */
13
+ resolvedClientHostname?: string;
14
+ /** Root domain of resolved client hostname */
15
+ resolvedRootClientHostname?: string;
16
+ /** IP address of connecting client */
17
+ remoteAddress?: string;
18
+ /** Email address from From header */
19
+ originalFromAddress?: string;
20
+ /** Domain from From header */
21
+ originalFromAddressDomain?: string;
22
+ /** Root domain from From header */
23
+ originalFromAddressRootDomain?: string;
24
+ /** SMTP envelope */
25
+ envelope?: {
26
+ mailFrom?: {address: string};
27
+ rcptTo?: Array<{address: string}>;
28
+ };
29
+ /** Whether DKIM was aligned and passing */
30
+ hadAlignedAndPassingDKIM?: boolean;
31
+ /** SPF result for From header */
32
+ spfFromHeader?: {
33
+ status?: {result: string};
34
+ };
35
+ /** Whether client hostname matches From domain */
36
+ hasSameHostnameAsFrom?: boolean;
37
+ /** Whether sender is allowlisted */
38
+ isAllowlisted?: boolean;
39
+ /** Set of DKIM signing domains */
40
+ signingDomains?: Set<string>;
41
+ /** SPF domain */
42
+ spf?: {
43
+ domain?: string;
44
+ };
45
+ /** Whether message is potential phishing */
46
+ isPotentialPhishing?: boolean;
47
+ }
48
+
49
+ /**
50
+ * Options for attribute extraction
51
+ */
52
+ export interface GetAttributesOptions {
53
+ /** Only return attributes that are verified and aligned */
54
+ isAligned?: boolean;
55
+ /** Authentication results from mailauth */
56
+ authResults?: AuthenticationResults | null;
57
+ }
58
+
59
+ /**
60
+ * Options for extractAttributes convenience function
61
+ */
62
+ export interface ExtractAttributesOptions extends GetAttributesOptions {
63
+ /** Sender IP address */
64
+ senderIp?: string;
65
+ /** Sender hostname */
66
+ senderHostname?: string;
67
+ }
68
+
69
+ /**
70
+ * Result from extractAttributes
71
+ */
72
+ export interface ExtractAttributesResult {
73
+ /** Array of unique attributes to check */
74
+ attributes: string[];
75
+ /** Session information built from parsed email */
76
+ session: SessionInfo;
77
+ }
78
+
79
+ /**
80
+ * Get attributes from an email for reputation checking
81
+ * @param parsed - Parsed email message
82
+ * @param session - Session information
83
+ * @param options - Options
84
+ * @returns Array of unique attributes to check
85
+ */
86
+ export function getAttributes(
87
+ parsed: object,
88
+ session?: SessionInfo,
89
+ options?: GetAttributesOptions,
90
+ ): Promise<string[]>;
91
+
92
+ /**
93
+ * Build session info from parsed email
94
+ * @param parsed - Parsed email
95
+ * @param existingSession - Existing session info to merge
96
+ * @returns Session information
97
+ */
98
+ export function buildSessionFromParsed(
99
+ parsed: object,
100
+ existingSession?: Partial<SessionInfo>,
101
+ ): SessionInfo;
102
+
103
+ /**
104
+ * Extract all checkable attributes from an email
105
+ * @param parsed - Parsed email message
106
+ * @param options - Options
107
+ * @returns Attributes and session info
108
+ */
109
+ export function extractAttributes(
110
+ parsed: object,
111
+ options?: ExtractAttributesOptions,
112
+ ): Promise<ExtractAttributesResult>;
113
+
114
+ /**
115
+ * Check and remove SRS (Sender Rewriting Scheme) encoding from an address
116
+ * @param address - Email address
117
+ * @returns Address with SRS removed
118
+ */
119
+ export function checkSRS(address: string): string;
120
+
121
+ /**
122
+ * Parse host/domain from an email address or domain string
123
+ * @param addressOrDomain - Email address or domain
124
+ * @returns Domain portion
125
+ */
126
+ export function parseHostFromDomainOrAddress(addressOrDomain: string): string;
127
+
128
+ /**
129
+ * Get root domain from a hostname
130
+ * @param hostname - Hostname
131
+ * @returns Root domain
132
+ */
133
+ export function parseRootDomain(hostname: string): string;
134
+
135
+ /**
136
+ * Parse addresses from a header value
137
+ * @param headerValue - Header value
138
+ * @returns Array of email addresses
139
+ */
140
+ export function parseAddresses(headerValue: string | object | unknown[]): string[];
141
+
142
+ /**
143
+ * Get header value from parsed email
144
+ * @param headers - Headers object
145
+ * @param name - Header name
146
+ * @returns Header value or null
147
+ */
148
+ export function getHeaders(headers: object, name: string): string | null;
@@ -1,4 +1,14 @@
1
1
  import type {ParsedMail, Attachment} from 'mailparser';
2
+ import type {AuthResult, AuthOptions, AuthScoreWeights, AuthScoreResult} from './auth.d.ts';
3
+ import type {ReputationResult, ReputationOptions} from './reputation.d.ts';
4
+ import type {SessionInfo, GetAttributesOptions, ExtractAttributesResult} from './get-attributes.d.ts';
5
+ import type {ArbitraryResult, ArbitraryOptions} from './is-arbitrary.d.ts';
6
+
7
+ // Re-export auth and reputation types
8
+ export type {AuthResult, AuthOptions, AuthScoreWeights, AuthScoreResult} from './auth.d.ts';
9
+ export type {ReputationResult, ReputationOptions} from './reputation.d.ts';
10
+ export type {SessionInfo, GetAttributesOptions, ExtractAttributesResult} from './get-attributes.d.ts';
11
+ export type {ArbitraryResult, ArbitraryOptions} from './is-arbitrary.d.ts';
2
12
 
3
13
  /**
4
14
  * ClamScan configuration options
@@ -68,6 +78,20 @@ export type SpamScannerConfig = {
68
78
  strictIdnDetection?: boolean;
69
79
  /** Enable token hashing */
70
80
  hashTokens?: boolean;
81
+ /** Enable email authentication (DKIM/SPF/ARC/DMARC/BIMI) */
82
+ enableAuthentication?: boolean;
83
+ /** Authentication options */
84
+ authOptions?: AuthOptions;
85
+ /** Authentication score weights */
86
+ authScoreWeights?: AuthScoreWeights;
87
+ /** Enable Forward Email reputation checking */
88
+ enableReputation?: boolean;
89
+ /** Reputation API options */
90
+ reputationOptions?: ReputationOptions;
91
+ /** Enable arbitrary spam detection */
92
+ enableArbitraryDetection?: boolean;
93
+ /** Arbitrary spam score threshold */
94
+ arbitraryThreshold?: number;
71
95
  };
72
96
 
73
97
  /**
@@ -135,13 +159,19 @@ export type MacroResult = {
135
159
  };
136
160
 
137
161
  /**
138
- * Arbitrary detection result (e.g., GTUBE)
162
+ * Arbitrary detection result (e.g., GTUBE, spam patterns)
139
163
  */
140
164
  export type ArbitraryResult = {
141
165
  /** Type of detection */
142
166
  type: 'arbitrary';
167
+ /** Subtype of arbitrary detection */
168
+ subtype?: 'gtube' | 'pattern';
143
169
  /** Description of the issue */
144
170
  description: string;
171
+ /** Arbitrary spam score */
172
+ score?: number;
173
+ /** List of reasons why the message was flagged */
174
+ reasons?: string[];
145
175
  };
146
176
 
147
177
  /**
@@ -236,6 +266,29 @@ export type NsfwResult = {
236
266
  description: string;
237
267
  };
238
268
 
269
+ /**
270
+ * All scan results
271
+ */
272
+ /**
273
+ * Extended authentication result with score
274
+ */
275
+ export type AuthenticationResult = AuthResult & {
276
+ /** Authentication score */
277
+ score: AuthScoreResult;
278
+ /** Formatted Authentication-Results header */
279
+ authResultsHeader: string;
280
+ };
281
+
282
+ /**
283
+ * Extended reputation result with details
284
+ */
285
+ export type ExtendedReputationResult = ReputationResult & {
286
+ /** Values that were checked */
287
+ checkedValues: string[];
288
+ /** Detailed results per value */
289
+ details: Record<string, ReputationResult>;
290
+ };
291
+
239
292
  /**
240
293
  * All scan results
241
294
  */
@@ -260,6 +313,10 @@ export type ScanResults = {
260
313
  toxicity: ToxicityResult[];
261
314
  /** NSFW detection results */
262
315
  nsfw: NsfwResult[];
316
+ /** Authentication results (if enabled) */
317
+ authentication?: AuthenticationResult | null;
318
+ /** Reputation results (if enabled) */
319
+ reputation?: ExtendedReputationResult | null;
263
320
  };
264
321
 
265
322
  /**
@@ -432,12 +489,32 @@ declare class SpamScanner {
432
489
  */
433
490
  initializeRegex(): void;
434
491
 
492
+ /**
493
+ * Scan options for per-scan configuration
494
+ */
495
+ scanOptions?: {
496
+ /** Enable authentication for this scan */
497
+ enableAuthentication?: boolean;
498
+ /** Authentication options for this scan */
499
+ authOptions?: AuthOptions;
500
+ /** Enable reputation checking for this scan */
501
+ enableReputation?: boolean;
502
+ /** Reputation options for this scan */
503
+ reputationOptions?: ReputationOptions;
504
+ };
505
+
435
506
  /**
436
507
  * Scan an email for spam
437
508
  * @param source - Email source (string, Uint8Array, or file path)
509
+ * @param scanOptions - Optional per-scan configuration
438
510
  * @returns Scan result
439
511
  */
440
- scan(source: ScanSource): Promise<ScanResult>;
512
+ scan(source: ScanSource, scanOptions?: {
513
+ enableAuthentication?: boolean;
514
+ authOptions?: AuthOptions;
515
+ enableReputation?: boolean;
516
+ reputationOptions?: ReputationOptions;
517
+ }): Promise<ScanResult>;
441
518
 
442
519
  /**
443
520
  * Get tokens and parsed mail from source
@@ -616,6 +693,32 @@ declare class SpamScanner {
616
693
  * @returns IDN detector or null
617
694
  */
618
695
  getIdnDetector(): Promise<EnhancedIdnDetector | undefined>;
696
+
697
+ /**
698
+ * Get authentication results using mailauth
699
+ * @param source - Email source
700
+ * @param mail - Parsed mail object
701
+ * @param options - Authentication options
702
+ * @returns Authentication result or null
703
+ */
704
+ getAuthenticationResults(
705
+ source: ScanSource,
706
+ mail: MailObject,
707
+ options?: AuthOptions
708
+ ): Promise<AuthenticationResult | null>;
709
+
710
+ /**
711
+ * Get reputation results from Forward Email API
712
+ * @param mail - Parsed mail object
713
+ * @param authOptions - Authentication options (for IP/sender)
714
+ * @param reputationOptions - Reputation API options
715
+ * @returns Reputation result or null
716
+ */
717
+ getReputationResults(
718
+ mail: MailObject,
719
+ authOptions?: AuthOptions,
720
+ reputationOptions?: ReputationOptions
721
+ ): Promise<ExtendedReputationResult | null>;
619
722
  }
620
723
 
621
724
  /**