• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dev-ignis / cross-log / 17016589983

17 Aug 2025 04:13AM UTC coverage: 87.462% (+1.8%) from 85.706%
17016589983

push

github

dev-ignis
test: - threshold;

673 of 840 branches covered (80.12%)

Branch coverage included in aggregate %.

1071 of 1154 relevant lines covered (92.81%)

393.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.63
/src/plugins/security.ts
1
import { PluginContext, SecurityPlugin, SecurityPluginConfig, SecuritySeverity, PluginInstance } from './types';
2
import { TypedPlugin } from './plugin-types';
3

4
export class SecurityEventPlugin implements TypedPlugin<'security'> {
30✔
5
  name: 'security' = 'security';
90✔
6
  version = '1.0.0';
90✔
7
  config: SecurityPluginConfig;
8
  methods?: SecurityPlugin;
9
  private context?: PluginContext;
10
  private failedAttempts: Map<string, number> = new Map();
90✔
11

12
  constructor(config?: SecurityPluginConfig) {
13
    this.config = {
90✔
14
      enabled: true,
15
      severity: true,
16
      includeIpAddress: false,
17
      includeUserAgent: true,
18
      ...config
19
    };
20
  }
21

22
  init(context: PluginContext): void {
23
    this.context = context;
90✔
24

25
    const methods: SecurityPlugin = {
90✔
26
      event: this.event.bind(this),
27
      authFailure: this.authFailure.bind(this),
28
      authSuccess: this.authSuccess.bind(this),
29
      accessDenied: this.accessDenied.bind(this),
30
      suspiciousActivity: this.suspiciousActivity.bind(this),
31
      vulnerability: this.vulnerability.bind(this)
32
    };
33

34
    (context as any).methods = methods;
90✔
35
    
36
    const instance = context as unknown as PluginInstance;
90✔
37
    instance.methods = methods as any;
90✔
38
  }
39

40
  private getSecurityContext(): Record<string, any> {
41
    const context: Record<string, any> = {
87✔
42
      timestamp: new Date().toISOString()
43
    };
44

45
    if (typeof window !== 'undefined') {
87✔
46
      if (this.config.includeUserAgent) {
3✔
47
        context.userAgent = window.navigator.userAgent;
3✔
48
      }
49
      context.origin = window.location.origin;
3✔
50
      context.path = window.location.pathname;
3✔
51
    }
52

53
    if (typeof process !== 'undefined') {
87✔
54
      context.processId = process.pid;
87✔
55
      context.platform = process.platform;
87✔
56
    }
57

58
    return context;
87✔
59
  }
60

61
  private getSeverityLevel(severity: SecuritySeverity): 'info' | 'warn' | 'error' {
62
    switch (severity) {
63!
63
      case 'low':
64
        return 'info';
39✔
65
      case 'medium':
66
        return 'warn';
12✔
67
      case 'high':
68
      case 'critical':
69
        return 'error';
12✔
70
      default:
71
        return 'info';
×
72
    }
73
  }
74

75
  private getSeverityEmoji(severity: SecuritySeverity): string {
76
    switch (severity) {
72!
77
      case 'low':
78
        return '🔵';
39✔
79
      case 'medium':
80
        return '🟡';
15✔
81
      case 'high':
82
        return '🟠';
15✔
83
      case 'critical':
84
        return '🔴';
3✔
85
      default:
86
        return '⚪';
×
87
    }
88
  }
89

90
  event(type: string, severity?: SecuritySeverity, details?: Record<string, any>): void {
91
    if (!this.context || !this.config.enabled) return;
36!
92

93
    const logData: Record<string, any> = {
36✔
94
      type: 'security_event',
95
      eventType: type,
96
      ...this.getSecurityContext()
97
    };
98

99
    if (this.config.severity && severity) {
36✔
100
      logData.severity = severity;
30✔
101
      logData.severityEmoji = this.getSeverityEmoji(severity);
30✔
102
    }
103

104
    if (details) {
36!
105
      logData.details = details;
×
106
    }
107

108
    const message = `Security Event: ${type}${severity ? ` [${severity.toUpperCase()}]` : ''}`;
36✔
109

110
    const level = severity ? this.getSeverityLevel(severity) : 'info';
36✔
111
    
112
    if (level === 'error') {
36✔
113
      this.context.logger.error(message, 'Security', logData);
6✔
114
    } else if (level === 'warn') {
30✔
115
      this.context.logger.warn(message, 'Security', logData);
6✔
116
    } else {
117
      this.context.logger.info(message, 'Security', logData);
24✔
118
    }
119
  }
120

121
  authFailure(reason: string, userId?: string, details?: Record<string, any>): void {
122
    if (!this.context || !this.config.enabled) return;
30!
123

124
    const key = userId || 'unknown';
30✔
125
    const attempts = (this.failedAttempts.get(key) || 0) + 1;
30✔
126
    this.failedAttempts.set(key, attempts);
30✔
127

128
    const logData: Record<string, any> = {
30✔
129
      type: 'auth_failure',
130
      reason,
131
      failedAttempts: attempts,
132
      ...this.getSecurityContext()
133
    };
134

135
    if (userId) {
30✔
136
      logData.userId = userId;
27✔
137
    }
138

139
    if (details) {
30!
140
      logData.details = details;
×
141
    }
142

143
    const severity: SecuritySeverity = attempts >= 5 ? 'high' : attempts >= 3 ? 'medium' : 'low';
30✔
144
    
145
    if (this.config.severity) {
30✔
146
      logData.severity = severity;
30✔
147
      logData.severityEmoji = this.getSeverityEmoji(severity);
30✔
148
    }
149

150
    const message = `Authentication Failure: ${reason}${userId ? ` (User: ${userId})` : ''} - Attempt #${attempts}`;
30✔
151

152
    const level = this.getSeverityLevel(severity);
30✔
153
    
154
    if (level === 'error') {
30✔
155
      this.context.logger.error(message, 'Security', logData);
3✔
156
    } else if (level === 'warn') {
27✔
157
      this.context.logger.warn(message, 'Security', logData);
6✔
158
    } else {
159
      this.context.logger.info(message, 'Security', logData);
21✔
160
    }
161

162
    if (attempts >= 5) {
30✔
163
      this.suspiciousActivity('multiple_auth_failures', { userId, attempts });
3✔
164
    }
165
  }
166

167
  authSuccess(userId: string, method?: string, details?: Record<string, any>): void {
168
    if (!this.context || !this.config.enabled) return;
9!
169

170
    this.failedAttempts.delete(userId);
9✔
171

172
    const logData: Record<string, any> = {
9✔
173
      type: 'auth_success',
174
      userId,
175
      ...this.getSecurityContext()
176
    };
177

178
    if (method) {
9✔
179
      logData.authMethod = method;
6✔
180
    }
181

182
    if (details) {
9!
183
      logData.details = details;
×
184
    }
185

186
    const message = `Authentication Success: User ${userId}${method ? ` via ${method}` : ''}`;
9✔
187

188
    this.context.logger.info(message, 'Security', logData);
9✔
189
  }
190

191
  accessDenied(resource: string, userId?: string, reason?: string): void {
192
    if (!this.context || !this.config.enabled) return;
3!
193

194
    const logData: Record<string, any> = {
3✔
195
      type: 'access_denied',
196
      resource,
197
      ...this.getSecurityContext()
198
    };
199

200
    if (userId) {
3✔
201
      logData.userId = userId;
3✔
202
    }
203

204
    if (reason) {
3✔
205
      logData.reason = reason;
3✔
206
    }
207

208
    if (this.config.severity) {
3✔
209
      logData.severity = 'medium';
3✔
210
      logData.severityEmoji = this.getSeverityEmoji('medium');
3✔
211
    }
212

213
    const message = `Access Denied: ${resource}${userId ? ` (User: ${userId})` : ''}${reason ? ` - ${reason}` : ''}`;
3!
214

215
    this.context.logger.warn(message, 'Security', logData);
3✔
216
  }
217

218
  suspiciousActivity(type: string, details?: Record<string, any>): void {
219
    if (!this.context || !this.config.enabled) return;
6!
220

221
    const logData: Record<string, any> = {
6✔
222
      type: 'suspicious_activity',
223
      activityType: type,
224
      ...this.getSecurityContext()
225
    };
226

227
    if (details) {
6✔
228
      logData.details = details;
3✔
229
    }
230

231
    if (this.config.severity) {
6✔
232
      logData.severity = 'high';
6✔
233
      logData.severityEmoji = this.getSeverityEmoji('high');
6✔
234
    }
235

236
    const message = `⚠️ Suspicious Activity Detected: ${type}`;
6✔
237

238
    this.context.logger.error(message, 'Security', logData);
6✔
239
  }
240

241
  vulnerability(type: string, severity: SecuritySeverity, details?: Record<string, any>): void {
242
    if (!this.context || !this.config.enabled) return;
3!
243

244
    const logData: Record<string, any> = {
3✔
245
      type: 'vulnerability_detected',
246
      vulnerabilityType: type,
247
      ...this.getSecurityContext()
248
    };
249

250
    if (this.config.severity) {
3✔
251
      logData.severity = severity;
3✔
252
      logData.severityEmoji = this.getSeverityEmoji(severity);
3✔
253
    }
254

255
    if (details) {
3✔
256
      logData.details = details;
3✔
257
    }
258

259
    const message = `🛡️ Vulnerability Detected: ${type} [${severity.toUpperCase()}]`;
3✔
260

261
    const level = this.getSeverityLevel(severity);
3✔
262
    
263
    if (level === 'error') {
3!
264
      this.context.logger.error(message, 'Security', logData);
3✔
265
    } else if (level === 'warn') {
×
266
      this.context.logger.warn(message, 'Security', logData);
×
267
    } else {
268
      this.context.logger.info(message, 'Security', logData);
×
269
    }
270
  }
271

272
  destroy(): void {
273
    this.context = undefined;
3✔
274
    this.failedAttempts.clear();
3✔
275
  }
276
}
277

278
export function createSecurityPlugin(config?: SecurityPluginConfig): SecurityEventPlugin {
30✔
279
  return new SecurityEventPlugin(config);
90✔
280
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc