@ceon-oy/monitor-sdk 1.0.2 → 1.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/README.md +24 -1
- package/dist/index.d.mts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +147 -43
- package/dist/index.mjs +147 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,6 +96,7 @@ interface MonitorClientConfig {
|
|
|
96
96
|
environment: string;
|
|
97
97
|
}[];
|
|
98
98
|
excludePatterns?: string[]; // Glob patterns to exclude (e.g., '@types/*')
|
|
99
|
+
autoAudit?: boolean; // Enable automatic vulnerability scanning (default: false)
|
|
99
100
|
}
|
|
100
101
|
```
|
|
101
102
|
|
|
@@ -206,7 +207,29 @@ await monitor.auditDependencies({
|
|
|
206
207
|
});
|
|
207
208
|
```
|
|
208
209
|
|
|
209
|
-
####
|
|
210
|
+
#### Automatic Auditing (Recommended)
|
|
211
|
+
|
|
212
|
+
Enable `autoAudit: true` to let the SDK automatically scan for vulnerabilities based on the interval configured in the Ceon Monitor dashboard:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
const monitor = new MonitorClient({
|
|
216
|
+
apiKey: process.env.CEON_MONITOR_API_KEY!,
|
|
217
|
+
endpoint: 'https://monitor.example.com',
|
|
218
|
+
autoAudit: true, // Enable automatic vulnerability scanning
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
With `autoAudit` enabled:
|
|
223
|
+
- SDK fetches the scan interval from the server on startup
|
|
224
|
+
- Runs an initial vulnerability scan
|
|
225
|
+
- Schedules recurring scans based on server configuration (e.g., every 24 hours)
|
|
226
|
+
- Responds to on-demand "Run Scan" requests from the dashboard (polled every 5 minutes)
|
|
227
|
+
|
|
228
|
+
Configure the scan interval in the Ceon Monitor dashboard under the project's Vulnerabilities page.
|
|
229
|
+
|
|
230
|
+
#### Manual Scheduled Auditing
|
|
231
|
+
|
|
232
|
+
If you prefer manual control over scheduling:
|
|
210
233
|
|
|
211
234
|
```typescript
|
|
212
235
|
// Run on app startup
|
package/dist/index.d.mts
CHANGED
|
@@ -31,6 +31,8 @@ interface MonitorClientConfig {
|
|
|
31
31
|
maxRetries?: number;
|
|
32
32
|
/** Request timeout in ms (default: 10000) */
|
|
33
33
|
requestTimeoutMs?: number;
|
|
34
|
+
/** Enable automatic vulnerability scanning based on server-configured interval (default: false) */
|
|
35
|
+
autoAudit?: boolean;
|
|
34
36
|
}
|
|
35
37
|
interface TechnologyItem {
|
|
36
38
|
name: string;
|
|
@@ -135,12 +137,41 @@ declare class MonitorClient {
|
|
|
135
137
|
private retryCount;
|
|
136
138
|
private isFlushInProgress;
|
|
137
139
|
private requestTimeoutMs;
|
|
140
|
+
private autoAudit;
|
|
141
|
+
private auditIntervalTimer;
|
|
142
|
+
private settingsPollingTimer;
|
|
143
|
+
private lastScanTime;
|
|
144
|
+
private lastKnownScanRequestedAt;
|
|
138
145
|
constructor(config: MonitorClientConfig);
|
|
139
146
|
captureError(error: Error, context?: ErrorContext): Promise<void>;
|
|
140
147
|
captureMessage(message: string, severity?: Severity, context?: ErrorContext): Promise<void>;
|
|
141
148
|
flush(): Promise<void>;
|
|
142
149
|
private getErrorKey;
|
|
143
150
|
close(): Promise<void>;
|
|
151
|
+
private stopAuditIntervalTimer;
|
|
152
|
+
/**
|
|
153
|
+
* Fetch project settings from the monitoring server.
|
|
154
|
+
* Returns configuration including vulnerability scan interval and scan request timestamp.
|
|
155
|
+
*/
|
|
156
|
+
fetchProjectSettings(): Promise<{
|
|
157
|
+
name: string;
|
|
158
|
+
vulnerabilityScanIntervalHours: number;
|
|
159
|
+
scanRequestedAt: string | null;
|
|
160
|
+
} | null>;
|
|
161
|
+
/**
|
|
162
|
+
* Setup automatic vulnerability scanning based on server-configured interval.
|
|
163
|
+
* Fetches settings from server and sets up recurring scans.
|
|
164
|
+
* Also sets up polling for on-demand scan requests from the server.
|
|
165
|
+
*/
|
|
166
|
+
private setupAutoAudit;
|
|
167
|
+
/**
|
|
168
|
+
* Run a vulnerability scan and track the time it was run.
|
|
169
|
+
*/
|
|
170
|
+
private runScanAndTrackTime;
|
|
171
|
+
/**
|
|
172
|
+
* Check if the server has requested an on-demand scan.
|
|
173
|
+
*/
|
|
174
|
+
private checkForScanRequest;
|
|
144
175
|
private enqueue;
|
|
145
176
|
/**
|
|
146
177
|
* Fetch with timeout to prevent hanging requests
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ interface MonitorClientConfig {
|
|
|
31
31
|
maxRetries?: number;
|
|
32
32
|
/** Request timeout in ms (default: 10000) */
|
|
33
33
|
requestTimeoutMs?: number;
|
|
34
|
+
/** Enable automatic vulnerability scanning based on server-configured interval (default: false) */
|
|
35
|
+
autoAudit?: boolean;
|
|
34
36
|
}
|
|
35
37
|
interface TechnologyItem {
|
|
36
38
|
name: string;
|
|
@@ -135,12 +137,41 @@ declare class MonitorClient {
|
|
|
135
137
|
private retryCount;
|
|
136
138
|
private isFlushInProgress;
|
|
137
139
|
private requestTimeoutMs;
|
|
140
|
+
private autoAudit;
|
|
141
|
+
private auditIntervalTimer;
|
|
142
|
+
private settingsPollingTimer;
|
|
143
|
+
private lastScanTime;
|
|
144
|
+
private lastKnownScanRequestedAt;
|
|
138
145
|
constructor(config: MonitorClientConfig);
|
|
139
146
|
captureError(error: Error, context?: ErrorContext): Promise<void>;
|
|
140
147
|
captureMessage(message: string, severity?: Severity, context?: ErrorContext): Promise<void>;
|
|
141
148
|
flush(): Promise<void>;
|
|
142
149
|
private getErrorKey;
|
|
143
150
|
close(): Promise<void>;
|
|
151
|
+
private stopAuditIntervalTimer;
|
|
152
|
+
/**
|
|
153
|
+
* Fetch project settings from the monitoring server.
|
|
154
|
+
* Returns configuration including vulnerability scan interval and scan request timestamp.
|
|
155
|
+
*/
|
|
156
|
+
fetchProjectSettings(): Promise<{
|
|
157
|
+
name: string;
|
|
158
|
+
vulnerabilityScanIntervalHours: number;
|
|
159
|
+
scanRequestedAt: string | null;
|
|
160
|
+
} | null>;
|
|
161
|
+
/**
|
|
162
|
+
* Setup automatic vulnerability scanning based on server-configured interval.
|
|
163
|
+
* Fetches settings from server and sets up recurring scans.
|
|
164
|
+
* Also sets up polling for on-demand scan requests from the server.
|
|
165
|
+
*/
|
|
166
|
+
private setupAutoAudit;
|
|
167
|
+
/**
|
|
168
|
+
* Run a vulnerability scan and track the time it was run.
|
|
169
|
+
*/
|
|
170
|
+
private runScanAndTrackTime;
|
|
171
|
+
/**
|
|
172
|
+
* Check if the server has requested an on-demand scan.
|
|
173
|
+
*/
|
|
174
|
+
private checkForScanRequest;
|
|
144
175
|
private enqueue;
|
|
145
176
|
/**
|
|
146
177
|
* Fetch with timeout to prevent hanging requests
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,10 @@ var MonitorClient = class {
|
|
|
42
42
|
this.isClosed = false;
|
|
43
43
|
this.retryCount = /* @__PURE__ */ new Map();
|
|
44
44
|
this.isFlushInProgress = false;
|
|
45
|
+
this.auditIntervalTimer = null;
|
|
46
|
+
this.settingsPollingTimer = null;
|
|
47
|
+
this.lastScanTime = null;
|
|
48
|
+
this.lastKnownScanRequestedAt = null;
|
|
45
49
|
if (!config.apiKey || config.apiKey.trim().length === 0) {
|
|
46
50
|
throw new Error("[MonitorClient] API key is required");
|
|
47
51
|
}
|
|
@@ -86,12 +90,18 @@ var MonitorClient = class {
|
|
|
86
90
|
"@typescript-eslint/*"
|
|
87
91
|
];
|
|
88
92
|
this.excludePatterns = config.excludePatterns || defaultExcludePatterns;
|
|
93
|
+
this.autoAudit = config.autoAudit || false;
|
|
89
94
|
this.startFlushTimer();
|
|
90
95
|
if (this.trackDependencies) {
|
|
91
96
|
this.syncDependencies().catch((err) => {
|
|
92
97
|
console.error("[MonitorClient] Failed to sync dependencies:", err instanceof Error ? err.message : String(err));
|
|
93
98
|
});
|
|
94
99
|
}
|
|
100
|
+
if (this.autoAudit) {
|
|
101
|
+
this.setupAutoAudit().catch((err) => {
|
|
102
|
+
console.error("[MonitorClient] Failed to setup auto audit:", err instanceof Error ? err.message : String(err));
|
|
103
|
+
});
|
|
104
|
+
}
|
|
95
105
|
}
|
|
96
106
|
async captureError(error, context) {
|
|
97
107
|
if (this.isClosed) return;
|
|
@@ -168,8 +178,101 @@ var MonitorClient = class {
|
|
|
168
178
|
async close() {
|
|
169
179
|
this.isClosed = true;
|
|
170
180
|
this.stopFlushTimer();
|
|
181
|
+
this.stopAuditIntervalTimer();
|
|
171
182
|
await this.flush();
|
|
172
183
|
}
|
|
184
|
+
stopAuditIntervalTimer() {
|
|
185
|
+
if (this.auditIntervalTimer) {
|
|
186
|
+
clearInterval(this.auditIntervalTimer);
|
|
187
|
+
this.auditIntervalTimer = null;
|
|
188
|
+
}
|
|
189
|
+
if (this.settingsPollingTimer) {
|
|
190
|
+
clearInterval(this.settingsPollingTimer);
|
|
191
|
+
this.settingsPollingTimer = null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Fetch project settings from the monitoring server.
|
|
196
|
+
* Returns configuration including vulnerability scan interval and scan request timestamp.
|
|
197
|
+
*/
|
|
198
|
+
async fetchProjectSettings() {
|
|
199
|
+
try {
|
|
200
|
+
const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/project/settings`, {
|
|
201
|
+
method: "GET",
|
|
202
|
+
headers: {
|
|
203
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
204
|
+
"Content-Type": "application/json"
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
console.warn("[MonitorClient] Failed to fetch project settings:", response.status);
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
const result = await response.json();
|
|
212
|
+
return result.data || null;
|
|
213
|
+
} catch (err) {
|
|
214
|
+
console.warn("[MonitorClient] Failed to fetch project settings:", err instanceof Error ? err.message : String(err));
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Setup automatic vulnerability scanning based on server-configured interval.
|
|
220
|
+
* Fetches settings from server and sets up recurring scans.
|
|
221
|
+
* Also sets up polling for on-demand scan requests from the server.
|
|
222
|
+
*/
|
|
223
|
+
async setupAutoAudit() {
|
|
224
|
+
const settings = await this.fetchProjectSettings();
|
|
225
|
+
if (!settings) {
|
|
226
|
+
console.warn("[MonitorClient] Could not fetch project settings, auto audit disabled");
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (settings.scanRequestedAt) {
|
|
230
|
+
this.lastKnownScanRequestedAt = new Date(settings.scanRequestedAt);
|
|
231
|
+
}
|
|
232
|
+
const intervalHours = settings.vulnerabilityScanIntervalHours;
|
|
233
|
+
if (intervalHours <= 0) {
|
|
234
|
+
console.log("[MonitorClient] Scheduled vulnerability scanning disabled by server configuration");
|
|
235
|
+
} else {
|
|
236
|
+
console.log(`[MonitorClient] Auto vulnerability scanning enabled (every ${intervalHours} hours)`);
|
|
237
|
+
await this.runScanAndTrackTime();
|
|
238
|
+
const intervalMs = intervalHours * 60 * 60 * 1e3;
|
|
239
|
+
this.auditIntervalTimer = setInterval(() => {
|
|
240
|
+
this.runScanAndTrackTime();
|
|
241
|
+
}, intervalMs);
|
|
242
|
+
}
|
|
243
|
+
console.log("[MonitorClient] Polling for scan requests enabled (every 5 minutes)");
|
|
244
|
+
this.settingsPollingTimer = setInterval(() => {
|
|
245
|
+
this.checkForScanRequest();
|
|
246
|
+
}, 5 * 60 * 1e3);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Run a vulnerability scan and track the time it was run.
|
|
250
|
+
*/
|
|
251
|
+
async runScanAndTrackTime() {
|
|
252
|
+
try {
|
|
253
|
+
await this.auditDependencies();
|
|
254
|
+
this.lastScanTime = /* @__PURE__ */ new Date();
|
|
255
|
+
} catch (err) {
|
|
256
|
+
console.error("[MonitorClient] Vulnerability scan failed:", err instanceof Error ? err.message : String(err));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Check if the server has requested an on-demand scan.
|
|
261
|
+
*/
|
|
262
|
+
async checkForScanRequest() {
|
|
263
|
+
try {
|
|
264
|
+
const settings = await this.fetchProjectSettings();
|
|
265
|
+
if (!settings || !settings.scanRequestedAt) return;
|
|
266
|
+
const scanRequestedAt = new Date(settings.scanRequestedAt);
|
|
267
|
+
if (!this.lastKnownScanRequestedAt || scanRequestedAt > this.lastKnownScanRequestedAt) {
|
|
268
|
+
console.log("[MonitorClient] On-demand scan requested by server");
|
|
269
|
+
this.lastKnownScanRequestedAt = scanRequestedAt;
|
|
270
|
+
await this.runScanAndTrackTime();
|
|
271
|
+
}
|
|
272
|
+
} catch (err) {
|
|
273
|
+
console.error("[MonitorClient] Failed to check for scan request:", err instanceof Error ? err.message : String(err));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
173
276
|
enqueue(payload) {
|
|
174
277
|
if (this.queue.length >= this.maxQueueSize) {
|
|
175
278
|
console.warn("[MonitorClient] Queue full, dropping oldest error");
|
|
@@ -183,12 +286,12 @@ var MonitorClient = class {
|
|
|
183
286
|
/**
|
|
184
287
|
* Fetch with timeout to prevent hanging requests
|
|
185
288
|
*/
|
|
186
|
-
async fetchWithTimeout(url,
|
|
289
|
+
async fetchWithTimeout(url, options2, timeoutMs = this.requestTimeoutMs) {
|
|
187
290
|
const controller = new AbortController();
|
|
188
291
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
189
292
|
try {
|
|
190
293
|
const response = await fetch(url, {
|
|
191
|
-
...
|
|
294
|
+
...options2,
|
|
192
295
|
signal: controller.signal
|
|
193
296
|
});
|
|
194
297
|
return response;
|
|
@@ -271,13 +374,13 @@ var MonitorClient = class {
|
|
|
271
374
|
try {
|
|
272
375
|
const fsModule = await import("fs");
|
|
273
376
|
const pathModule = await import("path");
|
|
274
|
-
const
|
|
275
|
-
const
|
|
377
|
+
const fs2 = fsModule.default || fsModule;
|
|
378
|
+
const path2 = pathModule.default || pathModule;
|
|
276
379
|
const baseDir = process.cwd();
|
|
277
|
-
const resolvedPath =
|
|
278
|
-
const normalizedPath =
|
|
279
|
-
const normalizedBase =
|
|
280
|
-
if (!
|
|
380
|
+
const resolvedPath = path2.isAbsolute(packagePath) ? packagePath : path2.join(baseDir, packagePath);
|
|
381
|
+
const normalizedPath = path2.normalize(resolvedPath);
|
|
382
|
+
const normalizedBase = path2.normalize(baseDir);
|
|
383
|
+
if (!path2.isAbsolute(packagePath)) {
|
|
281
384
|
if (!normalizedPath.startsWith(normalizedBase)) {
|
|
282
385
|
console.warn("[MonitorClient] Path traversal attempt blocked:", packagePath);
|
|
283
386
|
return [];
|
|
@@ -291,10 +394,10 @@ var MonitorClient = class {
|
|
|
291
394
|
console.warn("[MonitorClient] Path must point to package.json");
|
|
292
395
|
return [];
|
|
293
396
|
}
|
|
294
|
-
if (!
|
|
397
|
+
if (!fs2.existsSync(normalizedPath)) {
|
|
295
398
|
return [];
|
|
296
399
|
}
|
|
297
|
-
const packageJson = JSON.parse(
|
|
400
|
+
const packageJson = JSON.parse(fs2.readFileSync(normalizedPath, "utf-8"));
|
|
298
401
|
const technologies = [];
|
|
299
402
|
const deps = {
|
|
300
403
|
...packageJson.dependencies,
|
|
@@ -328,7 +431,7 @@ var MonitorClient = class {
|
|
|
328
431
|
async sendTechnologies(technologies) {
|
|
329
432
|
await this.sendTechnologiesWithEnvironment(technologies, this.environment);
|
|
330
433
|
}
|
|
331
|
-
async sendTechnologiesWithEnvironment(technologies,
|
|
434
|
+
async sendTechnologiesWithEnvironment(technologies, environment2) {
|
|
332
435
|
const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/technologies/sync`, {
|
|
333
436
|
method: "POST",
|
|
334
437
|
headers: {
|
|
@@ -336,7 +439,7 @@ var MonitorClient = class {
|
|
|
336
439
|
Authorization: `Bearer ${this.apiKey}`
|
|
337
440
|
},
|
|
338
441
|
body: JSON.stringify({
|
|
339
|
-
environment,
|
|
442
|
+
environment: environment2,
|
|
340
443
|
technologies
|
|
341
444
|
})
|
|
342
445
|
});
|
|
@@ -373,74 +476,74 @@ var MonitorClient = class {
|
|
|
373
476
|
/**
|
|
374
477
|
* Capture a login failure event (convenience method)
|
|
375
478
|
*/
|
|
376
|
-
async captureLoginFailure(
|
|
479
|
+
async captureLoginFailure(options2) {
|
|
377
480
|
return this.captureSecurityEvent({
|
|
378
|
-
eventType: `login_failed_${
|
|
481
|
+
eventType: `login_failed_${options2.authMethod || "other"}`,
|
|
379
482
|
category: "AUTHENTICATION",
|
|
380
483
|
severity: "MEDIUM",
|
|
381
|
-
ip:
|
|
382
|
-
identifier:
|
|
383
|
-
endpoint:
|
|
384
|
-
userAgent:
|
|
385
|
-
metadata: { reason:
|
|
484
|
+
ip: options2.ip,
|
|
485
|
+
identifier: options2.identifier,
|
|
486
|
+
endpoint: options2.endpoint,
|
|
487
|
+
userAgent: options2.userAgent,
|
|
488
|
+
metadata: { reason: options2.reason, authMethod: options2.authMethod }
|
|
386
489
|
});
|
|
387
490
|
}
|
|
388
491
|
/**
|
|
389
492
|
* Capture a successful login event
|
|
390
493
|
*/
|
|
391
|
-
async captureLoginSuccess(
|
|
494
|
+
async captureLoginSuccess(options2) {
|
|
392
495
|
await this.captureSecurityEvent({
|
|
393
|
-
eventType: `login_success_${
|
|
496
|
+
eventType: `login_success_${options2.authMethod || "other"}`,
|
|
394
497
|
category: "AUTHENTICATION",
|
|
395
498
|
severity: "LOW",
|
|
396
|
-
ip:
|
|
397
|
-
identifier:
|
|
398
|
-
endpoint:
|
|
399
|
-
userAgent:
|
|
400
|
-
metadata: { authMethod:
|
|
499
|
+
ip: options2.ip,
|
|
500
|
+
identifier: options2.identifier,
|
|
501
|
+
endpoint: options2.endpoint,
|
|
502
|
+
userAgent: options2.userAgent,
|
|
503
|
+
metadata: { authMethod: options2.authMethod }
|
|
401
504
|
});
|
|
402
505
|
}
|
|
403
506
|
/**
|
|
404
507
|
* Capture a rate limit event
|
|
405
508
|
*/
|
|
406
|
-
async captureRateLimit(
|
|
509
|
+
async captureRateLimit(options2) {
|
|
407
510
|
await this.captureSecurityEvent({
|
|
408
511
|
eventType: "rate_limit_exceeded",
|
|
409
512
|
category: "RATE_LIMIT",
|
|
410
513
|
severity: "MEDIUM",
|
|
411
|
-
ip:
|
|
412
|
-
identifier:
|
|
413
|
-
endpoint:
|
|
414
|
-
userAgent:
|
|
415
|
-
metadata: { limit:
|
|
514
|
+
ip: options2.ip,
|
|
515
|
+
identifier: options2.identifier,
|
|
516
|
+
endpoint: options2.endpoint,
|
|
517
|
+
userAgent: options2.userAgent,
|
|
518
|
+
metadata: { limit: options2.limit, window: options2.window }
|
|
416
519
|
});
|
|
417
520
|
}
|
|
418
521
|
/**
|
|
419
522
|
* Capture an authorization failure (user tried to access unauthorized resource)
|
|
420
523
|
*/
|
|
421
|
-
async captureAuthorizationFailure(
|
|
524
|
+
async captureAuthorizationFailure(options2) {
|
|
422
525
|
await this.captureSecurityEvent({
|
|
423
526
|
eventType: "authorization_denied",
|
|
424
527
|
category: "AUTHORIZATION",
|
|
425
528
|
severity: "MEDIUM",
|
|
426
|
-
ip:
|
|
427
|
-
identifier:
|
|
428
|
-
endpoint:
|
|
429
|
-
userAgent:
|
|
430
|
-
metadata: { resource:
|
|
529
|
+
ip: options2.ip,
|
|
530
|
+
identifier: options2.identifier,
|
|
531
|
+
endpoint: options2.endpoint,
|
|
532
|
+
userAgent: options2.userAgent,
|
|
533
|
+
metadata: { resource: options2.resource, action: options2.action }
|
|
431
534
|
});
|
|
432
535
|
}
|
|
433
536
|
/**
|
|
434
537
|
* Check if an IP or identifier has triggered brute force detection
|
|
435
538
|
*/
|
|
436
|
-
async checkBruteForce(
|
|
539
|
+
async checkBruteForce(options2) {
|
|
437
540
|
const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/security/detect/brute-force`, {
|
|
438
541
|
method: "POST",
|
|
439
542
|
headers: {
|
|
440
543
|
"Content-Type": "application/json",
|
|
441
544
|
Authorization: `Bearer ${this.apiKey}`
|
|
442
545
|
},
|
|
443
|
-
body: JSON.stringify(
|
|
546
|
+
body: JSON.stringify(options2)
|
|
444
547
|
});
|
|
445
548
|
if (!response.ok) {
|
|
446
549
|
const errorText = await response.text().catch(() => "");
|
|
@@ -466,9 +569,10 @@ var MonitorClient = class {
|
|
|
466
569
|
let path;
|
|
467
570
|
let fs;
|
|
468
571
|
try {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
572
|
+
const _require = typeof globalThis.__webpack_require__ === "function" ? eval("require") : require;
|
|
573
|
+
execSync = _require("child_process").execSync;
|
|
574
|
+
path = _require("path");
|
|
575
|
+
fs = _require("fs");
|
|
472
576
|
} catch {
|
|
473
577
|
console.warn("[MonitorClient] auditDependencies requires Node.js (not available in bundled/browser environments)");
|
|
474
578
|
return null;
|
package/dist/index.mjs
CHANGED
|
@@ -13,6 +13,10 @@ var MonitorClient = class {
|
|
|
13
13
|
this.isClosed = false;
|
|
14
14
|
this.retryCount = /* @__PURE__ */ new Map();
|
|
15
15
|
this.isFlushInProgress = false;
|
|
16
|
+
this.auditIntervalTimer = null;
|
|
17
|
+
this.settingsPollingTimer = null;
|
|
18
|
+
this.lastScanTime = null;
|
|
19
|
+
this.lastKnownScanRequestedAt = null;
|
|
16
20
|
if (!config.apiKey || config.apiKey.trim().length === 0) {
|
|
17
21
|
throw new Error("[MonitorClient] API key is required");
|
|
18
22
|
}
|
|
@@ -57,12 +61,18 @@ var MonitorClient = class {
|
|
|
57
61
|
"@typescript-eslint/*"
|
|
58
62
|
];
|
|
59
63
|
this.excludePatterns = config.excludePatterns || defaultExcludePatterns;
|
|
64
|
+
this.autoAudit = config.autoAudit || false;
|
|
60
65
|
this.startFlushTimer();
|
|
61
66
|
if (this.trackDependencies) {
|
|
62
67
|
this.syncDependencies().catch((err) => {
|
|
63
68
|
console.error("[MonitorClient] Failed to sync dependencies:", err instanceof Error ? err.message : String(err));
|
|
64
69
|
});
|
|
65
70
|
}
|
|
71
|
+
if (this.autoAudit) {
|
|
72
|
+
this.setupAutoAudit().catch((err) => {
|
|
73
|
+
console.error("[MonitorClient] Failed to setup auto audit:", err instanceof Error ? err.message : String(err));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
66
76
|
}
|
|
67
77
|
async captureError(error, context) {
|
|
68
78
|
if (this.isClosed) return;
|
|
@@ -139,8 +149,101 @@ var MonitorClient = class {
|
|
|
139
149
|
async close() {
|
|
140
150
|
this.isClosed = true;
|
|
141
151
|
this.stopFlushTimer();
|
|
152
|
+
this.stopAuditIntervalTimer();
|
|
142
153
|
await this.flush();
|
|
143
154
|
}
|
|
155
|
+
stopAuditIntervalTimer() {
|
|
156
|
+
if (this.auditIntervalTimer) {
|
|
157
|
+
clearInterval(this.auditIntervalTimer);
|
|
158
|
+
this.auditIntervalTimer = null;
|
|
159
|
+
}
|
|
160
|
+
if (this.settingsPollingTimer) {
|
|
161
|
+
clearInterval(this.settingsPollingTimer);
|
|
162
|
+
this.settingsPollingTimer = null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Fetch project settings from the monitoring server.
|
|
167
|
+
* Returns configuration including vulnerability scan interval and scan request timestamp.
|
|
168
|
+
*/
|
|
169
|
+
async fetchProjectSettings() {
|
|
170
|
+
try {
|
|
171
|
+
const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/project/settings`, {
|
|
172
|
+
method: "GET",
|
|
173
|
+
headers: {
|
|
174
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
175
|
+
"Content-Type": "application/json"
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
if (!response.ok) {
|
|
179
|
+
console.warn("[MonitorClient] Failed to fetch project settings:", response.status);
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
const result = await response.json();
|
|
183
|
+
return result.data || null;
|
|
184
|
+
} catch (err) {
|
|
185
|
+
console.warn("[MonitorClient] Failed to fetch project settings:", err instanceof Error ? err.message : String(err));
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Setup automatic vulnerability scanning based on server-configured interval.
|
|
191
|
+
* Fetches settings from server and sets up recurring scans.
|
|
192
|
+
* Also sets up polling for on-demand scan requests from the server.
|
|
193
|
+
*/
|
|
194
|
+
async setupAutoAudit() {
|
|
195
|
+
const settings = await this.fetchProjectSettings();
|
|
196
|
+
if (!settings) {
|
|
197
|
+
console.warn("[MonitorClient] Could not fetch project settings, auto audit disabled");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (settings.scanRequestedAt) {
|
|
201
|
+
this.lastKnownScanRequestedAt = new Date(settings.scanRequestedAt);
|
|
202
|
+
}
|
|
203
|
+
const intervalHours = settings.vulnerabilityScanIntervalHours;
|
|
204
|
+
if (intervalHours <= 0) {
|
|
205
|
+
console.log("[MonitorClient] Scheduled vulnerability scanning disabled by server configuration");
|
|
206
|
+
} else {
|
|
207
|
+
console.log(`[MonitorClient] Auto vulnerability scanning enabled (every ${intervalHours} hours)`);
|
|
208
|
+
await this.runScanAndTrackTime();
|
|
209
|
+
const intervalMs = intervalHours * 60 * 60 * 1e3;
|
|
210
|
+
this.auditIntervalTimer = setInterval(() => {
|
|
211
|
+
this.runScanAndTrackTime();
|
|
212
|
+
}, intervalMs);
|
|
213
|
+
}
|
|
214
|
+
console.log("[MonitorClient] Polling for scan requests enabled (every 5 minutes)");
|
|
215
|
+
this.settingsPollingTimer = setInterval(() => {
|
|
216
|
+
this.checkForScanRequest();
|
|
217
|
+
}, 5 * 60 * 1e3);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Run a vulnerability scan and track the time it was run.
|
|
221
|
+
*/
|
|
222
|
+
async runScanAndTrackTime() {
|
|
223
|
+
try {
|
|
224
|
+
await this.auditDependencies();
|
|
225
|
+
this.lastScanTime = /* @__PURE__ */ new Date();
|
|
226
|
+
} catch (err) {
|
|
227
|
+
console.error("[MonitorClient] Vulnerability scan failed:", err instanceof Error ? err.message : String(err));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Check if the server has requested an on-demand scan.
|
|
232
|
+
*/
|
|
233
|
+
async checkForScanRequest() {
|
|
234
|
+
try {
|
|
235
|
+
const settings = await this.fetchProjectSettings();
|
|
236
|
+
if (!settings || !settings.scanRequestedAt) return;
|
|
237
|
+
const scanRequestedAt = new Date(settings.scanRequestedAt);
|
|
238
|
+
if (!this.lastKnownScanRequestedAt || scanRequestedAt > this.lastKnownScanRequestedAt) {
|
|
239
|
+
console.log("[MonitorClient] On-demand scan requested by server");
|
|
240
|
+
this.lastKnownScanRequestedAt = scanRequestedAt;
|
|
241
|
+
await this.runScanAndTrackTime();
|
|
242
|
+
}
|
|
243
|
+
} catch (err) {
|
|
244
|
+
console.error("[MonitorClient] Failed to check for scan request:", err instanceof Error ? err.message : String(err));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
144
247
|
enqueue(payload) {
|
|
145
248
|
if (this.queue.length >= this.maxQueueSize) {
|
|
146
249
|
console.warn("[MonitorClient] Queue full, dropping oldest error");
|
|
@@ -154,12 +257,12 @@ var MonitorClient = class {
|
|
|
154
257
|
/**
|
|
155
258
|
* Fetch with timeout to prevent hanging requests
|
|
156
259
|
*/
|
|
157
|
-
async fetchWithTimeout(url,
|
|
260
|
+
async fetchWithTimeout(url, options2, timeoutMs = this.requestTimeoutMs) {
|
|
158
261
|
const controller = new AbortController();
|
|
159
262
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
160
263
|
try {
|
|
161
264
|
const response = await fetch(url, {
|
|
162
|
-
...
|
|
265
|
+
...options2,
|
|
163
266
|
signal: controller.signal
|
|
164
267
|
});
|
|
165
268
|
return response;
|
|
@@ -242,13 +345,13 @@ var MonitorClient = class {
|
|
|
242
345
|
try {
|
|
243
346
|
const fsModule = await import("fs");
|
|
244
347
|
const pathModule = await import("path");
|
|
245
|
-
const
|
|
246
|
-
const
|
|
348
|
+
const fs2 = fsModule.default || fsModule;
|
|
349
|
+
const path2 = pathModule.default || pathModule;
|
|
247
350
|
const baseDir = process.cwd();
|
|
248
|
-
const resolvedPath =
|
|
249
|
-
const normalizedPath =
|
|
250
|
-
const normalizedBase =
|
|
251
|
-
if (!
|
|
351
|
+
const resolvedPath = path2.isAbsolute(packagePath) ? packagePath : path2.join(baseDir, packagePath);
|
|
352
|
+
const normalizedPath = path2.normalize(resolvedPath);
|
|
353
|
+
const normalizedBase = path2.normalize(baseDir);
|
|
354
|
+
if (!path2.isAbsolute(packagePath)) {
|
|
252
355
|
if (!normalizedPath.startsWith(normalizedBase)) {
|
|
253
356
|
console.warn("[MonitorClient] Path traversal attempt blocked:", packagePath);
|
|
254
357
|
return [];
|
|
@@ -262,10 +365,10 @@ var MonitorClient = class {
|
|
|
262
365
|
console.warn("[MonitorClient] Path must point to package.json");
|
|
263
366
|
return [];
|
|
264
367
|
}
|
|
265
|
-
if (!
|
|
368
|
+
if (!fs2.existsSync(normalizedPath)) {
|
|
266
369
|
return [];
|
|
267
370
|
}
|
|
268
|
-
const packageJson = JSON.parse(
|
|
371
|
+
const packageJson = JSON.parse(fs2.readFileSync(normalizedPath, "utf-8"));
|
|
269
372
|
const technologies = [];
|
|
270
373
|
const deps = {
|
|
271
374
|
...packageJson.dependencies,
|
|
@@ -299,7 +402,7 @@ var MonitorClient = class {
|
|
|
299
402
|
async sendTechnologies(technologies) {
|
|
300
403
|
await this.sendTechnologiesWithEnvironment(technologies, this.environment);
|
|
301
404
|
}
|
|
302
|
-
async sendTechnologiesWithEnvironment(technologies,
|
|
405
|
+
async sendTechnologiesWithEnvironment(technologies, environment2) {
|
|
303
406
|
const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/technologies/sync`, {
|
|
304
407
|
method: "POST",
|
|
305
408
|
headers: {
|
|
@@ -307,7 +410,7 @@ var MonitorClient = class {
|
|
|
307
410
|
Authorization: `Bearer ${this.apiKey}`
|
|
308
411
|
},
|
|
309
412
|
body: JSON.stringify({
|
|
310
|
-
environment,
|
|
413
|
+
environment: environment2,
|
|
311
414
|
technologies
|
|
312
415
|
})
|
|
313
416
|
});
|
|
@@ -344,74 +447,74 @@ var MonitorClient = class {
|
|
|
344
447
|
/**
|
|
345
448
|
* Capture a login failure event (convenience method)
|
|
346
449
|
*/
|
|
347
|
-
async captureLoginFailure(
|
|
450
|
+
async captureLoginFailure(options2) {
|
|
348
451
|
return this.captureSecurityEvent({
|
|
349
|
-
eventType: `login_failed_${
|
|
452
|
+
eventType: `login_failed_${options2.authMethod || "other"}`,
|
|
350
453
|
category: "AUTHENTICATION",
|
|
351
454
|
severity: "MEDIUM",
|
|
352
|
-
ip:
|
|
353
|
-
identifier:
|
|
354
|
-
endpoint:
|
|
355
|
-
userAgent:
|
|
356
|
-
metadata: { reason:
|
|
455
|
+
ip: options2.ip,
|
|
456
|
+
identifier: options2.identifier,
|
|
457
|
+
endpoint: options2.endpoint,
|
|
458
|
+
userAgent: options2.userAgent,
|
|
459
|
+
metadata: { reason: options2.reason, authMethod: options2.authMethod }
|
|
357
460
|
});
|
|
358
461
|
}
|
|
359
462
|
/**
|
|
360
463
|
* Capture a successful login event
|
|
361
464
|
*/
|
|
362
|
-
async captureLoginSuccess(
|
|
465
|
+
async captureLoginSuccess(options2) {
|
|
363
466
|
await this.captureSecurityEvent({
|
|
364
|
-
eventType: `login_success_${
|
|
467
|
+
eventType: `login_success_${options2.authMethod || "other"}`,
|
|
365
468
|
category: "AUTHENTICATION",
|
|
366
469
|
severity: "LOW",
|
|
367
|
-
ip:
|
|
368
|
-
identifier:
|
|
369
|
-
endpoint:
|
|
370
|
-
userAgent:
|
|
371
|
-
metadata: { authMethod:
|
|
470
|
+
ip: options2.ip,
|
|
471
|
+
identifier: options2.identifier,
|
|
472
|
+
endpoint: options2.endpoint,
|
|
473
|
+
userAgent: options2.userAgent,
|
|
474
|
+
metadata: { authMethod: options2.authMethod }
|
|
372
475
|
});
|
|
373
476
|
}
|
|
374
477
|
/**
|
|
375
478
|
* Capture a rate limit event
|
|
376
479
|
*/
|
|
377
|
-
async captureRateLimit(
|
|
480
|
+
async captureRateLimit(options2) {
|
|
378
481
|
await this.captureSecurityEvent({
|
|
379
482
|
eventType: "rate_limit_exceeded",
|
|
380
483
|
category: "RATE_LIMIT",
|
|
381
484
|
severity: "MEDIUM",
|
|
382
|
-
ip:
|
|
383
|
-
identifier:
|
|
384
|
-
endpoint:
|
|
385
|
-
userAgent:
|
|
386
|
-
metadata: { limit:
|
|
485
|
+
ip: options2.ip,
|
|
486
|
+
identifier: options2.identifier,
|
|
487
|
+
endpoint: options2.endpoint,
|
|
488
|
+
userAgent: options2.userAgent,
|
|
489
|
+
metadata: { limit: options2.limit, window: options2.window }
|
|
387
490
|
});
|
|
388
491
|
}
|
|
389
492
|
/**
|
|
390
493
|
* Capture an authorization failure (user tried to access unauthorized resource)
|
|
391
494
|
*/
|
|
392
|
-
async captureAuthorizationFailure(
|
|
495
|
+
async captureAuthorizationFailure(options2) {
|
|
393
496
|
await this.captureSecurityEvent({
|
|
394
497
|
eventType: "authorization_denied",
|
|
395
498
|
category: "AUTHORIZATION",
|
|
396
499
|
severity: "MEDIUM",
|
|
397
|
-
ip:
|
|
398
|
-
identifier:
|
|
399
|
-
endpoint:
|
|
400
|
-
userAgent:
|
|
401
|
-
metadata: { resource:
|
|
500
|
+
ip: options2.ip,
|
|
501
|
+
identifier: options2.identifier,
|
|
502
|
+
endpoint: options2.endpoint,
|
|
503
|
+
userAgent: options2.userAgent,
|
|
504
|
+
metadata: { resource: options2.resource, action: options2.action }
|
|
402
505
|
});
|
|
403
506
|
}
|
|
404
507
|
/**
|
|
405
508
|
* Check if an IP or identifier has triggered brute force detection
|
|
406
509
|
*/
|
|
407
|
-
async checkBruteForce(
|
|
510
|
+
async checkBruteForce(options2) {
|
|
408
511
|
const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/security/detect/brute-force`, {
|
|
409
512
|
method: "POST",
|
|
410
513
|
headers: {
|
|
411
514
|
"Content-Type": "application/json",
|
|
412
515
|
Authorization: `Bearer ${this.apiKey}`
|
|
413
516
|
},
|
|
414
|
-
body: JSON.stringify(
|
|
517
|
+
body: JSON.stringify(options2)
|
|
415
518
|
});
|
|
416
519
|
if (!response.ok) {
|
|
417
520
|
const errorText = await response.text().catch(() => "");
|
|
@@ -437,9 +540,10 @@ var MonitorClient = class {
|
|
|
437
540
|
let path;
|
|
438
541
|
let fs;
|
|
439
542
|
try {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
543
|
+
const _require = typeof globalThis.__webpack_require__ === "function" ? eval("require") : __require;
|
|
544
|
+
execSync = _require("child_process").execSync;
|
|
545
|
+
path = _require("path");
|
|
546
|
+
fs = _require("fs");
|
|
443
547
|
} catch {
|
|
444
548
|
console.warn("[MonitorClient] auditDependencies requires Node.js (not available in bundled/browser environments)");
|
|
445
549
|
return null;
|
package/package.json
CHANGED