spamscanner 6.0.0 → 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,236 @@
1
+ /**
2
+ * Enhanced IDN Detector options
3
+ */
4
+ export type EnhancedIdnDetectorOptions = {
5
+ /** Enable strict mode */
6
+ strictMode?: boolean;
7
+ /** Enable domain whitelist */
8
+ enableWhitelist?: boolean;
9
+ /** Enable brand protection */
10
+ enableBrandProtection?: boolean;
11
+ /** Enable context analysis */
12
+ enableContextAnalysis?: boolean;
13
+ /** Maximum similarity threshold (0-1) */
14
+ maxSimilarityThreshold?: number;
15
+ /** Minimum domain age in days */
16
+ minDomainAge?: number;
17
+ };
18
+
19
+ /**
20
+ * Context for IDN analysis
21
+ */
22
+ export type IdnAnalysisContext = {
23
+ /** Email content */
24
+ emailContent?: string;
25
+ /** Display text (if different from domain) */
26
+ displayText?: string | undefined;
27
+ /** Sender reputation (0-1) */
28
+ senderReputation?: number;
29
+ /** Email headers */
30
+ emailHeaders?: Map<string, unknown> | Record<string, unknown>;
31
+ };
32
+
33
+ /**
34
+ * IDN analysis result
35
+ */
36
+ export type IdnAnalysisResult = {
37
+ /** The domain analyzed */
38
+ domain: string;
39
+ /** Whether the domain is an IDN */
40
+ isIdn: boolean;
41
+ /** Risk score (0-1) */
42
+ riskScore: number;
43
+ /** Risk factors identified */
44
+ riskFactors: string[];
45
+ /** Recommendations */
46
+ recommendations: string[];
47
+ /** Confidence level (0-1) */
48
+ confidence: number;
49
+ };
50
+
51
+ /**
52
+ * Confusable character analysis result
53
+ */
54
+ export type ConfusableAnalysis = {
55
+ /** Risk score contribution */
56
+ score: number;
57
+ /** Risk factors identified */
58
+ factors: string[];
59
+ };
60
+
61
+ /**
62
+ * Brand similarity analysis result
63
+ */
64
+ export type BrandAnalysis = {
65
+ /** Risk score contribution */
66
+ score: number;
67
+ /** Risk factors identified */
68
+ factors: string[];
69
+ };
70
+
71
+ /**
72
+ * Script mixing analysis result
73
+ */
74
+ export type ScriptAnalysis = {
75
+ /** Risk score contribution */
76
+ score: number;
77
+ /** Risk factors identified */
78
+ factors: string[];
79
+ };
80
+
81
+ /**
82
+ * Context analysis result
83
+ */
84
+ export type ContextAnalysis = {
85
+ /** Risk score contribution */
86
+ score: number;
87
+ /** Risk factors identified */
88
+ factors: string[];
89
+ };
90
+
91
+ /**
92
+ * Punycode analysis result
93
+ */
94
+ export type PunycodeAnalysis = {
95
+ /** Risk score contribution */
96
+ score: number;
97
+ /** Risk factors identified */
98
+ factors: string[];
99
+ };
100
+
101
+ /**
102
+ * Enhanced IDN Homograph Attack Detector
103
+ */
104
+ declare class EnhancedIdnDetector {
105
+ /** Detector options */
106
+ options: EnhancedIdnDetectorOptions & {
107
+ strictMode: boolean;
108
+ enableWhitelist: boolean;
109
+ enableBrandProtection: boolean;
110
+ enableContextAnalysis: boolean;
111
+ maxSimilarityThreshold: number;
112
+ minDomainAge: number;
113
+ };
114
+
115
+ /** Analysis cache */
116
+ cache: Map<string, IdnAnalysisResult>;
117
+
118
+ /**
119
+ * Create a new EnhancedIdnDetector instance
120
+ * @param options - Configuration options
121
+ */
122
+ constructor(options?: EnhancedIdnDetectorOptions);
123
+
124
+ /**
125
+ * Detect homograph attack in a domain
126
+ * @param domain - Domain to analyze
127
+ * @param context - Analysis context
128
+ * @returns Analysis result
129
+ */
130
+ detectHomographAttack(domain: string, context?: IdnAnalysisContext): IdnAnalysisResult;
131
+
132
+ /**
133
+ * Comprehensive analysis of a domain
134
+ * @param domain - Domain to analyze
135
+ * @param context - Analysis context
136
+ * @returns Analysis result
137
+ */
138
+ analyzeComprehensive(domain: string, context: IdnAnalysisContext): IdnAnalysisResult;
139
+
140
+ /**
141
+ * Check if domain contains IDN characters
142
+ * @param domain - Domain to check
143
+ * @returns Whether the domain is an IDN
144
+ */
145
+ isIdnDomain(domain: string): boolean;
146
+
147
+ /**
148
+ * Check if domain is whitelisted
149
+ * @param domain - Domain to check
150
+ * @returns Whether the domain is whitelisted
151
+ */
152
+ isWhitelisted(domain: string): boolean;
153
+
154
+ /**
155
+ * Analyze confusable characters in domain
156
+ * @param domain - Domain to analyze
157
+ * @returns Confusable analysis result
158
+ */
159
+ analyzeConfusableCharacters(domain: string): ConfusableAnalysis;
160
+
161
+ /**
162
+ * Analyze brand similarity
163
+ * @param domain - Domain to analyze
164
+ * @returns Brand analysis result
165
+ */
166
+ analyzeBrandSimilarity(domain: string): BrandAnalysis;
167
+
168
+ /**
169
+ * Analyze script mixing patterns
170
+ * @param domain - Domain to analyze
171
+ * @returns Script analysis result
172
+ */
173
+ analyzeScriptMixing(domain: string): ScriptAnalysis;
174
+
175
+ /**
176
+ * Analyze context for additional risk factors
177
+ * @param domain - Domain to analyze
178
+ * @param context - Analysis context
179
+ * @returns Context analysis result
180
+ */
181
+ analyzeContext(domain: string, context: IdnAnalysisContext): ContextAnalysis;
182
+
183
+ /**
184
+ * Analyze punycode domain
185
+ * @param domain - Domain to analyze
186
+ * @returns Punycode analysis result
187
+ */
188
+ analyzePunycode(domain: string): PunycodeAnalysis;
189
+
190
+ /**
191
+ * Normalize domain for comparison
192
+ * @param domain - Domain to normalize
193
+ * @returns Normalized domain
194
+ */
195
+ normalizeDomain(domain: string): string;
196
+
197
+ /**
198
+ * Calculate string similarity using Levenshtein distance
199
+ * @param string1 - First string
200
+ * @param string2 - Second string
201
+ * @returns Similarity score (0-1)
202
+ */
203
+ calculateSimilarity(string1: string, string2: string): number;
204
+
205
+ /**
206
+ * Detect scripts used in domain
207
+ * @param domain - Domain to analyze
208
+ * @returns Set of detected scripts
209
+ */
210
+ detectScripts(domain: string): Set<string>;
211
+
212
+ /**
213
+ * Decode punycode domain
214
+ * @param domain - Domain to decode
215
+ * @returns Decoded domain
216
+ */
217
+ decodePunycode(domain: string): string;
218
+
219
+ /**
220
+ * Generate recommendations based on analysis
221
+ * @param analysis - Analysis result
222
+ * @returns Array of recommendations
223
+ */
224
+ generateRecommendations(analysis: IdnAnalysisResult): string[];
225
+
226
+ /**
227
+ * Get cache key for analysis
228
+ * @param domain - Domain
229
+ * @param context - Analysis context
230
+ * @returns Cache key
231
+ */
232
+ getCacheKey(domain: string, context: IdnAnalysisContext): string;
233
+ }
234
+
235
+ export default EnhancedIdnDetector;
236
+ export {EnhancedIdnDetector};
@@ -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;