agentshield-sdk 7.0.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.
- package/CHANGELOG.md +191 -0
- package/LICENSE +21 -0
- package/README.md +975 -0
- package/bin/agent-shield.js +680 -0
- package/package.json +118 -0
- package/src/adaptive.js +330 -0
- package/src/agent-protocol.js +998 -0
- package/src/alert-tuning.js +480 -0
- package/src/allowlist.js +603 -0
- package/src/audit-immutable.js +914 -0
- package/src/audit-streaming.js +469 -0
- package/src/badges.js +196 -0
- package/src/behavior-profiling.js +289 -0
- package/src/benchmark-harness.js +804 -0
- package/src/canary.js +271 -0
- package/src/certification.js +563 -0
- package/src/circuit-breaker.js +321 -0
- package/src/compliance.js +617 -0
- package/src/confidence-tuning.js +324 -0
- package/src/confused-deputy.js +624 -0
- package/src/context-scoring.js +360 -0
- package/src/conversation.js +494 -0
- package/src/cost-optimizer.js +1024 -0
- package/src/ctf.js +462 -0
- package/src/detector-core.js +1999 -0
- package/src/distributed.js +359 -0
- package/src/document-scanner.js +795 -0
- package/src/embedding.js +307 -0
- package/src/encoding.js +429 -0
- package/src/enterprise.js +405 -0
- package/src/errors.js +100 -0
- package/src/eu-ai-act.js +523 -0
- package/src/fuzzer.js +764 -0
- package/src/honeypot.js +328 -0
- package/src/i18n-patterns.js +523 -0
- package/src/index.js +430 -0
- package/src/integrations.js +528 -0
- package/src/llm-redteam.js +670 -0
- package/src/main.js +741 -0
- package/src/main.mjs +38 -0
- package/src/mcp-bridge.js +542 -0
- package/src/mcp-certification.js +846 -0
- package/src/mcp-sdk-integration.js +355 -0
- package/src/mcp-security-runtime.js +741 -0
- package/src/mcp-server.js +740 -0
- package/src/middleware.js +208 -0
- package/src/model-finetuning.js +884 -0
- package/src/model-fingerprint.js +1042 -0
- package/src/multi-agent-trust.js +453 -0
- package/src/multi-agent.js +404 -0
- package/src/multimodal.js +296 -0
- package/src/nist-mapping.js +505 -0
- package/src/observability.js +330 -0
- package/src/openclaw.js +450 -0
- package/src/otel.js +544 -0
- package/src/owasp-2025.js +483 -0
- package/src/pii.js +390 -0
- package/src/plugin-marketplace.js +628 -0
- package/src/plugin-system.js +349 -0
- package/src/policy-dsl.js +775 -0
- package/src/policy-extended.js +635 -0
- package/src/policy.js +443 -0
- package/src/presets.js +409 -0
- package/src/production.js +557 -0
- package/src/prompt-leakage.js +321 -0
- package/src/rag-vulnerability.js +579 -0
- package/src/redteam.js +475 -0
- package/src/response-handler.js +429 -0
- package/src/scanners.js +357 -0
- package/src/self-healing.js +363 -0
- package/src/semantic.js +339 -0
- package/src/shield-score.js +250 -0
- package/src/sso-saml.js +897 -0
- package/src/stream-scanner.js +806 -0
- package/src/testing.js +505 -0
- package/src/threat-encyclopedia.js +629 -0
- package/src/threat-intel-network.js +1017 -0
- package/src/token-analysis.js +467 -0
- package/src/tool-guard.js +412 -0
- package/src/tool-output-validator.js +354 -0
- package/src/utils.js +83 -0
- package/src/watermark.js +235 -0
- package/src/worker-scanner.js +601 -0
- package/types/index.d.ts +2088 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agent Shield — Audit Log Streaming (v2.1)
|
|
5
|
+
*
|
|
6
|
+
* Stream security audit events to external logging/SIEM systems.
|
|
7
|
+
* Supports Splunk HEC, Elasticsearch, file-based logging, and custom transports.
|
|
8
|
+
*
|
|
9
|
+
* Zero dependencies — uses Node.js built-in http/https/fs modules.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const http = require('http');
|
|
15
|
+
const https = require('https');
|
|
16
|
+
|
|
17
|
+
// =========================================================================
|
|
18
|
+
// TRANSPORT INTERFACE
|
|
19
|
+
// =========================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Base transport class. Extend for custom destinations.
|
|
23
|
+
*/
|
|
24
|
+
class AuditTransport {
|
|
25
|
+
/**
|
|
26
|
+
* Send an event to the transport.
|
|
27
|
+
* @param {object} event - Audit event.
|
|
28
|
+
* @returns {Promise<void>}
|
|
29
|
+
*/
|
|
30
|
+
async send(event) { throw new Error('Not implemented'); }
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Send multiple events in a batch.
|
|
34
|
+
* @param {Array<object>} events
|
|
35
|
+
* @returns {Promise<void>}
|
|
36
|
+
*/
|
|
37
|
+
async sendBatch(events) {
|
|
38
|
+
for (const event of events) {
|
|
39
|
+
await this.send(event);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Flush any buffered events.
|
|
45
|
+
* @returns {Promise<void>}
|
|
46
|
+
*/
|
|
47
|
+
async flush() {}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Close the transport.
|
|
51
|
+
* @returns {Promise<void>}
|
|
52
|
+
*/
|
|
53
|
+
async close() {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// =========================================================================
|
|
57
|
+
// FILE TRANSPORT
|
|
58
|
+
// =========================================================================
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Writes audit events to a local file (JSONL format).
|
|
62
|
+
*/
|
|
63
|
+
class FileTransport extends AuditTransport {
|
|
64
|
+
/**
|
|
65
|
+
* @param {object} [options]
|
|
66
|
+
* @param {string} [options.filePath='./agent-shield-audit.log'] - Log file path.
|
|
67
|
+
* @param {number} [options.maxSizeMB=100] - Max file size before rotation.
|
|
68
|
+
* @param {number} [options.maxFiles=5] - Number of rotated files to keep.
|
|
69
|
+
*/
|
|
70
|
+
constructor(options = {}) {
|
|
71
|
+
super();
|
|
72
|
+
this.filePath = options.filePath || './agent-shield-audit.log';
|
|
73
|
+
this.maxSizeBytes = (options.maxSizeMB || 100) * 1024 * 1024;
|
|
74
|
+
this.maxFiles = options.maxFiles || 5;
|
|
75
|
+
this._buffer = [];
|
|
76
|
+
this._bufferSize = 0;
|
|
77
|
+
this._flushInterval = setInterval(() => this.flush(), 5000);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async send(event) {
|
|
81
|
+
const line = JSON.stringify(event) + '\n';
|
|
82
|
+
this._buffer.push(line);
|
|
83
|
+
this._bufferSize += line.length;
|
|
84
|
+
|
|
85
|
+
if (this._bufferSize >= 64 * 1024) {
|
|
86
|
+
await this.flush();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async flush() {
|
|
91
|
+
if (this._buffer.length === 0) return;
|
|
92
|
+
|
|
93
|
+
const data = this._buffer.join('');
|
|
94
|
+
this._buffer = [];
|
|
95
|
+
this._bufferSize = 0;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// Check rotation
|
|
99
|
+
if (fs.existsSync(this.filePath)) {
|
|
100
|
+
const stat = fs.statSync(this.filePath);
|
|
101
|
+
if (stat.size + data.length > this.maxSizeBytes) {
|
|
102
|
+
this._rotate();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fs.appendFileSync(this.filePath, data);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
console.warn('[Agent Shield] FileTransport write error:', e.message);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async close() {
|
|
113
|
+
clearInterval(this._flushInterval);
|
|
114
|
+
await this.flush();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** @private */
|
|
118
|
+
_rotate() {
|
|
119
|
+
for (let i = this.maxFiles - 1; i >= 1; i--) {
|
|
120
|
+
const from = `${this.filePath}.${i}`;
|
|
121
|
+
const to = `${this.filePath}.${i + 1}`;
|
|
122
|
+
if (fs.existsSync(from)) {
|
|
123
|
+
if (i + 1 > this.maxFiles) fs.unlinkSync(from);
|
|
124
|
+
else fs.renameSync(from, to);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (fs.existsSync(this.filePath)) {
|
|
128
|
+
fs.renameSync(this.filePath, `${this.filePath}.1`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// =========================================================================
|
|
134
|
+
// SPLUNK HEC TRANSPORT
|
|
135
|
+
// =========================================================================
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Sends audit events to Splunk via HTTP Event Collector (HEC).
|
|
139
|
+
*/
|
|
140
|
+
class SplunkTransport extends AuditTransport {
|
|
141
|
+
/**
|
|
142
|
+
* @param {object} options
|
|
143
|
+
* @param {string} options.url - Splunk HEC URL (e.g., https://splunk:8088/services/collector/event).
|
|
144
|
+
* @param {string} options.token - HEC token.
|
|
145
|
+
* @param {string} [options.index='main'] - Splunk index.
|
|
146
|
+
* @param {string} [options.source='agent-shield'] - Event source.
|
|
147
|
+
* @param {string} [options.sourcetype='_json'] - Source type.
|
|
148
|
+
* @param {number} [options.batchSize=50] - Events per batch.
|
|
149
|
+
* @param {number} [options.flushIntervalMs=5000] - Auto-flush interval.
|
|
150
|
+
*/
|
|
151
|
+
constructor(options = {}) {
|
|
152
|
+
super();
|
|
153
|
+
this.url = options.url;
|
|
154
|
+
this.token = options.token;
|
|
155
|
+
this.index = options.index || 'main';
|
|
156
|
+
this.source = options.source || 'agent-shield';
|
|
157
|
+
this.sourcetype = options.sourcetype || '_json';
|
|
158
|
+
this.batchSize = options.batchSize || 50;
|
|
159
|
+
this._buffer = [];
|
|
160
|
+
this._flushInterval = setInterval(() => this.flush(), options.flushIntervalMs || 5000);
|
|
161
|
+
this._stats = { sent: 0, errors: 0 };
|
|
162
|
+
|
|
163
|
+
if (!this.url || !this.token) {
|
|
164
|
+
console.warn('[Agent Shield] SplunkTransport: url and token are required.');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async send(event) {
|
|
169
|
+
this._buffer.push({
|
|
170
|
+
time: event.timestamp ? event.timestamp / 1000 : Date.now() / 1000,
|
|
171
|
+
source: this.source,
|
|
172
|
+
sourcetype: this.sourcetype,
|
|
173
|
+
index: this.index,
|
|
174
|
+
event
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
if (this._buffer.length >= this.batchSize) {
|
|
178
|
+
await this.flush();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async flush() {
|
|
183
|
+
if (this._buffer.length === 0 || !this.url || !this.token) return;
|
|
184
|
+
|
|
185
|
+
const events = this._buffer.splice(0, this.batchSize);
|
|
186
|
+
const payload = events.map(e => JSON.stringify(e)).join('\n');
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
await this._post(this.url, payload, {
|
|
190
|
+
'Authorization': `Splunk ${this.token}`,
|
|
191
|
+
'Content-Type': 'application/json'
|
|
192
|
+
});
|
|
193
|
+
this._stats.sent += events.length;
|
|
194
|
+
} catch (e) {
|
|
195
|
+
this._stats.errors += events.length;
|
|
196
|
+
console.warn('[Agent Shield] SplunkTransport error:', e.message);
|
|
197
|
+
// Re-queue failed events
|
|
198
|
+
this._buffer.unshift(...events);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async close() {
|
|
203
|
+
clearInterval(this._flushInterval);
|
|
204
|
+
await this.flush();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
getStats() { return { ...this._stats, buffered: this._buffer.length }; }
|
|
208
|
+
|
|
209
|
+
/** @private */
|
|
210
|
+
_post(url, body, headers) {
|
|
211
|
+
return new Promise((resolve, reject) => {
|
|
212
|
+
const parsed = new URL(url);
|
|
213
|
+
const lib = parsed.protocol === 'https:' ? https : http;
|
|
214
|
+
const req = lib.request({
|
|
215
|
+
hostname: parsed.hostname, port: parsed.port,
|
|
216
|
+
path: parsed.pathname, method: 'POST',
|
|
217
|
+
headers: { ...headers, 'Content-Length': Buffer.byteLength(body) },
|
|
218
|
+
rejectUnauthorized: false, timeout: 10000
|
|
219
|
+
}, (res) => {
|
|
220
|
+
let data = '';
|
|
221
|
+
res.on('data', c => data += c);
|
|
222
|
+
res.on('end', () => resolve(data));
|
|
223
|
+
});
|
|
224
|
+
req.on('error', reject);
|
|
225
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
|
|
226
|
+
req.write(body);
|
|
227
|
+
req.end();
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// =========================================================================
|
|
233
|
+
// ELASTICSEARCH TRANSPORT
|
|
234
|
+
// =========================================================================
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Sends audit events to Elasticsearch.
|
|
238
|
+
*/
|
|
239
|
+
class ElasticsearchTransport extends AuditTransport {
|
|
240
|
+
/**
|
|
241
|
+
* @param {object} options
|
|
242
|
+
* @param {string} options.url - Elasticsearch URL (e.g., http://localhost:9200).
|
|
243
|
+
* @param {string} [options.index='agent-shield-audit'] - Index name.
|
|
244
|
+
* @param {string} [options.apiKey] - API key for authentication.
|
|
245
|
+
* @param {number} [options.batchSize=100] - Events per bulk request.
|
|
246
|
+
* @param {number} [options.flushIntervalMs=5000]
|
|
247
|
+
*/
|
|
248
|
+
constructor(options = {}) {
|
|
249
|
+
super();
|
|
250
|
+
this.url = options.url;
|
|
251
|
+
this.index = options.index || 'agent-shield-audit';
|
|
252
|
+
this.apiKey = options.apiKey || null;
|
|
253
|
+
this.batchSize = options.batchSize || 100;
|
|
254
|
+
this._buffer = [];
|
|
255
|
+
this._flushInterval = setInterval(() => this.flush(), options.flushIntervalMs || 5000);
|
|
256
|
+
this._stats = { sent: 0, errors: 0 };
|
|
257
|
+
|
|
258
|
+
if (!this.url) {
|
|
259
|
+
console.warn('[Agent Shield] ElasticsearchTransport: url is required.');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async send(event) {
|
|
264
|
+
this._buffer.push(event);
|
|
265
|
+
if (this._buffer.length >= this.batchSize) {
|
|
266
|
+
await this.flush();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async flush() {
|
|
271
|
+
if (this._buffer.length === 0 || !this.url) return;
|
|
272
|
+
|
|
273
|
+
const events = this._buffer.splice(0, this.batchSize);
|
|
274
|
+
const dateStr = new Date().toISOString().split('T')[0].replace(/-/g, '.');
|
|
275
|
+
const indexName = `${this.index}-${dateStr}`;
|
|
276
|
+
|
|
277
|
+
// Build NDJSON bulk payload
|
|
278
|
+
const lines = [];
|
|
279
|
+
for (const event of events) {
|
|
280
|
+
lines.push(JSON.stringify({ index: { _index: indexName } }));
|
|
281
|
+
lines.push(JSON.stringify({ ...event, '@timestamp': event.timestamp || Date.now() }));
|
|
282
|
+
}
|
|
283
|
+
const payload = lines.join('\n') + '\n';
|
|
284
|
+
|
|
285
|
+
const headers = { 'Content-Type': 'application/x-ndjson' };
|
|
286
|
+
if (this.apiKey) headers['Authorization'] = `ApiKey ${this.apiKey}`;
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
await this._post(`${this.url}/_bulk`, payload, headers);
|
|
290
|
+
this._stats.sent += events.length;
|
|
291
|
+
} catch (e) {
|
|
292
|
+
this._stats.errors += events.length;
|
|
293
|
+
console.warn('[Agent Shield] ElasticsearchTransport error:', e.message);
|
|
294
|
+
this._buffer.unshift(...events);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async close() {
|
|
299
|
+
clearInterval(this._flushInterval);
|
|
300
|
+
await this.flush();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
getStats() { return { ...this._stats, buffered: this._buffer.length }; }
|
|
304
|
+
|
|
305
|
+
/** @private */
|
|
306
|
+
_post(url, body, headers) {
|
|
307
|
+
return new Promise((resolve, reject) => {
|
|
308
|
+
const parsed = new URL(url);
|
|
309
|
+
const lib = parsed.protocol === 'https:' ? https : http;
|
|
310
|
+
const req = lib.request({
|
|
311
|
+
hostname: parsed.hostname, port: parsed.port,
|
|
312
|
+
path: parsed.pathname, method: 'POST',
|
|
313
|
+
headers: { ...headers, 'Content-Length': Buffer.byteLength(body) },
|
|
314
|
+
timeout: 10000
|
|
315
|
+
}, (res) => {
|
|
316
|
+
let data = '';
|
|
317
|
+
res.on('data', c => data += c);
|
|
318
|
+
res.on('end', () => resolve(data));
|
|
319
|
+
});
|
|
320
|
+
req.on('error', reject);
|
|
321
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
|
|
322
|
+
req.write(body);
|
|
323
|
+
req.end();
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// =========================================================================
|
|
329
|
+
// AUDIT STREAM MANAGER
|
|
330
|
+
// =========================================================================
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Manages multiple audit transports and routes events to all of them.
|
|
334
|
+
*/
|
|
335
|
+
class AuditStreamManager {
|
|
336
|
+
/**
|
|
337
|
+
* @param {object} [options]
|
|
338
|
+
* @param {Array<AuditTransport>} [options.transports] - Initial transports.
|
|
339
|
+
* @param {boolean} [options.includeMetadata=true] - Add instance metadata to events.
|
|
340
|
+
* @param {string} [options.environment='production'] - Environment label.
|
|
341
|
+
*/
|
|
342
|
+
constructor(options = {}) {
|
|
343
|
+
this._transports = options.transports || [];
|
|
344
|
+
this.includeMetadata = options.includeMetadata !== false;
|
|
345
|
+
this.environment = options.environment || 'production';
|
|
346
|
+
this._eventCount = 0;
|
|
347
|
+
|
|
348
|
+
console.log('[Agent Shield] AuditStreamManager initialized (%d transports)', this._transports.length);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Add a transport.
|
|
353
|
+
* @param {AuditTransport} transport
|
|
354
|
+
*/
|
|
355
|
+
addTransport(transport) {
|
|
356
|
+
this._transports.push(transport);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Emit an audit event to all transports.
|
|
361
|
+
* @param {string} type - Event type (e.g., 'scan', 'threat', 'block', 'config_change').
|
|
362
|
+
* @param {object} data - Event data.
|
|
363
|
+
* @returns {Promise<void>}
|
|
364
|
+
*/
|
|
365
|
+
async emit(type, data = {}) {
|
|
366
|
+
this._eventCount++;
|
|
367
|
+
|
|
368
|
+
const event = {
|
|
369
|
+
type,
|
|
370
|
+
...data,
|
|
371
|
+
timestamp: Date.now(),
|
|
372
|
+
eventId: `evt_${this._eventCount}_${Date.now()}`
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
if (this.includeMetadata) {
|
|
376
|
+
event.environment = this.environment;
|
|
377
|
+
event.agentShieldVersion = '2.1.0';
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const promises = this._transports.map(t =>
|
|
381
|
+
t.send(event).catch(e => console.warn('[Agent Shield] Transport error:', e.message))
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
await Promise.all(promises);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Emit a scan event.
|
|
389
|
+
* @param {object} scanResult - Result from scanText/AgentShield.
|
|
390
|
+
* @param {object} [context] - Additional context.
|
|
391
|
+
*/
|
|
392
|
+
async emitScan(scanResult, context = {}) {
|
|
393
|
+
await this.emit('scan', {
|
|
394
|
+
status: scanResult.status,
|
|
395
|
+
threatCount: scanResult.threats ? scanResult.threats.length : 0,
|
|
396
|
+
categories: scanResult.threats ? [...new Set(scanResult.threats.map(t => t.category))] : [],
|
|
397
|
+
scanTimeMs: scanResult.stats ? scanResult.stats.scanTimeMs : 0,
|
|
398
|
+
...context
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Emit a threat event.
|
|
404
|
+
* @param {object} threat - Individual threat object.
|
|
405
|
+
* @param {object} [context]
|
|
406
|
+
*/
|
|
407
|
+
async emitThreat(threat, context = {}) {
|
|
408
|
+
await this.emit('threat', {
|
|
409
|
+
severity: threat.severity,
|
|
410
|
+
category: threat.category,
|
|
411
|
+
description: threat.description,
|
|
412
|
+
confidence: threat.confidence,
|
|
413
|
+
...context
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Emit a block event.
|
|
419
|
+
* @param {string} reason
|
|
420
|
+
* @param {object} [context]
|
|
421
|
+
*/
|
|
422
|
+
async emitBlock(reason, context = {}) {
|
|
423
|
+
await this.emit('block', { reason, ...context });
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Flush all transports.
|
|
428
|
+
* @returns {Promise<void>}
|
|
429
|
+
*/
|
|
430
|
+
async flush() {
|
|
431
|
+
await Promise.all(this._transports.map(t => t.flush()));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Close all transports.
|
|
436
|
+
* @returns {Promise<void>}
|
|
437
|
+
*/
|
|
438
|
+
async close() {
|
|
439
|
+
await Promise.all(this._transports.map(t => t.close()));
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Get streaming statistics.
|
|
444
|
+
* @returns {object}
|
|
445
|
+
*/
|
|
446
|
+
getStats() {
|
|
447
|
+
return {
|
|
448
|
+
eventCount: this._eventCount,
|
|
449
|
+
transports: this._transports.length,
|
|
450
|
+
transportStats: this._transports.map((t, i) => ({
|
|
451
|
+
index: i,
|
|
452
|
+
type: t.constructor.name,
|
|
453
|
+
stats: typeof t.getStats === 'function' ? t.getStats() : null
|
|
454
|
+
}))
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// =========================================================================
|
|
460
|
+
// EXPORTS
|
|
461
|
+
// =========================================================================
|
|
462
|
+
|
|
463
|
+
module.exports = {
|
|
464
|
+
AuditStreamManager,
|
|
465
|
+
AuditTransport,
|
|
466
|
+
FileTransport,
|
|
467
|
+
SplunkTransport,
|
|
468
|
+
ElasticsearchTransport
|
|
469
|
+
};
|
package/src/badges.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agent Shield — Integration Badges & GitHub Action Support
|
|
5
|
+
*
|
|
6
|
+
* Generate SVG badges for READMEs and CI/CD pipelines.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// =========================================================================
|
|
10
|
+
// Badge Generator
|
|
11
|
+
// =========================================================================
|
|
12
|
+
|
|
13
|
+
class BadgeGenerator {
|
|
14
|
+
/**
|
|
15
|
+
* Generate a Shield Score badge in SVG.
|
|
16
|
+
*/
|
|
17
|
+
static shieldScore(score) {
|
|
18
|
+
const color = score >= 90 ? '22c55e' : score >= 70 ? 'eab308' : score >= 50 ? 'f97316' : 'ef4444';
|
|
19
|
+
const grade = score >= 95 ? 'A+' : score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : score >= 50 ? 'D' : 'F';
|
|
20
|
+
return BadgeGenerator.generateSVG('shield score', `${score} (${grade})`, color);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generate a protection status badge.
|
|
25
|
+
*/
|
|
26
|
+
static protectionStatus(enabled = true) {
|
|
27
|
+
return BadgeGenerator.generateSVG(
|
|
28
|
+
'agent shield',
|
|
29
|
+
enabled ? 'protected' : 'unprotected',
|
|
30
|
+
enabled ? '3b82f6' : 'ef4444'
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generate a detection rate badge.
|
|
36
|
+
*/
|
|
37
|
+
static detectionRate(rate) {
|
|
38
|
+
const num = parseFloat(rate);
|
|
39
|
+
const color = num >= 90 ? '22c55e' : num >= 70 ? 'eab308' : 'ef4444';
|
|
40
|
+
return BadgeGenerator.generateSVG('detection rate', `${num}%`, color);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Generate a scan count badge.
|
|
45
|
+
*/
|
|
46
|
+
static scanCount(count) {
|
|
47
|
+
return BadgeGenerator.generateSVG('scans', count.toLocaleString(), '06b6d4');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Generate a compliance badge.
|
|
52
|
+
*/
|
|
53
|
+
static compliance(framework, rate) {
|
|
54
|
+
const num = parseFloat(rate);
|
|
55
|
+
const color = num >= 80 ? '22c55e' : num >= 50 ? 'eab308' : 'ef4444';
|
|
56
|
+
return BadgeGenerator.generateSVG(framework, `${num}%`, color);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Generate a custom badge.
|
|
61
|
+
*/
|
|
62
|
+
static custom(label, value, color = '3b82f6') {
|
|
63
|
+
return BadgeGenerator.generateSVG(label, value, color);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Generate Markdown badge links for README.
|
|
68
|
+
*/
|
|
69
|
+
static markdownBadges(options = {}) {
|
|
70
|
+
const lines = [];
|
|
71
|
+
|
|
72
|
+
if (options.score !== undefined) {
|
|
73
|
+
const color = options.score >= 90 ? 'brightgreen' : options.score >= 70 ? 'yellow' : options.score >= 50 ? 'orange' : 'red';
|
|
74
|
+
lines.push(``);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
lines.push(``);
|
|
78
|
+
|
|
79
|
+
if (options.detectionRate) {
|
|
80
|
+
const color = parseFloat(options.detectionRate) >= 90 ? 'brightgreen' : 'yellow';
|
|
81
|
+
lines.push(``);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return lines.join('\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Core SVG badge generator.
|
|
89
|
+
*/
|
|
90
|
+
static generateSVG(label, value, color) {
|
|
91
|
+
const labelWidth = label.length * 7 + 12;
|
|
92
|
+
const valueWidth = String(value).length * 7 + 12;
|
|
93
|
+
const totalWidth = labelWidth + valueWidth;
|
|
94
|
+
|
|
95
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${totalWidth}" height="20" role="img">
|
|
96
|
+
<title>${label}: ${value}</title>
|
|
97
|
+
<linearGradient id="s" x2="0" y2="100%">
|
|
98
|
+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
|
99
|
+
<stop offset="1" stop-opacity=".1"/>
|
|
100
|
+
</linearGradient>
|
|
101
|
+
<clipPath id="r"><rect width="${totalWidth}" height="20" rx="3" fill="#fff"/></clipPath>
|
|
102
|
+
<g clip-path="url(#r)">
|
|
103
|
+
<rect width="${labelWidth}" height="20" fill="#555"/>
|
|
104
|
+
<rect x="${labelWidth}" width="${valueWidth}" height="20" fill="#${color}"/>
|
|
105
|
+
<rect width="${totalWidth}" height="20" fill="url(#s)"/>
|
|
106
|
+
</g>
|
|
107
|
+
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
|
|
108
|
+
<text x="${labelWidth / 2}" y="14" fill="#010101" fill-opacity=".3">${label}</text>
|
|
109
|
+
<text x="${labelWidth / 2}" y="13">${label}</text>
|
|
110
|
+
<text x="${labelWidth + valueWidth / 2}" y="14" fill="#010101" fill-opacity=".3">${value}</text>
|
|
111
|
+
<text x="${labelWidth + valueWidth / 2}" y="13">${value}</text>
|
|
112
|
+
</g>
|
|
113
|
+
</svg>`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// =========================================================================
|
|
118
|
+
// GitHub Action Output
|
|
119
|
+
// =========================================================================
|
|
120
|
+
|
|
121
|
+
class GitHubActionReporter {
|
|
122
|
+
constructor() {
|
|
123
|
+
this.annotations = [];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Report scan results as GitHub Action annotations.
|
|
128
|
+
*/
|
|
129
|
+
reportScan(result, file = '', line = 0) {
|
|
130
|
+
if (!result.threats || result.threats.length === 0) return;
|
|
131
|
+
|
|
132
|
+
for (const threat of result.threats) {
|
|
133
|
+
const level = threat.severity === 'critical' || threat.severity === 'high' ? 'error' : 'warning';
|
|
134
|
+
const msg = `[Agent Shield] ${threat.description} (${threat.severity})`;
|
|
135
|
+
|
|
136
|
+
// GitHub Actions annotation format
|
|
137
|
+
if (file) {
|
|
138
|
+
console.log(`::${level} file=${file},line=${line}::${msg}`);
|
|
139
|
+
} else {
|
|
140
|
+
console.log(`::${level}::${msg}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.annotations.push({ level, file, line, message: msg });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Set GitHub Action output variables.
|
|
149
|
+
*/
|
|
150
|
+
setOutputs(results) {
|
|
151
|
+
const total = results.threats ? results.threats.length : 0;
|
|
152
|
+
const blocked = results.blocked || false;
|
|
153
|
+
const status = results.status || 'unknown';
|
|
154
|
+
|
|
155
|
+
// Use GITHUB_OUTPUT env file (modern) with fallback to deprecated ::set-output
|
|
156
|
+
const githubOutput = process.env.GITHUB_OUTPUT;
|
|
157
|
+
if (githubOutput) {
|
|
158
|
+
const fs = require('fs');
|
|
159
|
+
fs.appendFileSync(githubOutput, `threat_count=${total}\n`);
|
|
160
|
+
fs.appendFileSync(githubOutput, `status=${status}\n`);
|
|
161
|
+
fs.appendFileSync(githubOutput, `blocked=${blocked}\n`);
|
|
162
|
+
} else {
|
|
163
|
+
console.log(`::set-output name=threat_count::${total}`);
|
|
164
|
+
console.log(`::set-output name=status::${status}`);
|
|
165
|
+
console.log(`::set-output name=blocked::${blocked}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Create a summary for GitHub Actions.
|
|
171
|
+
*/
|
|
172
|
+
createSummary(shieldScore, scanResults) {
|
|
173
|
+
const lines = [];
|
|
174
|
+
lines.push('## Agent Shield Scan Results\n');
|
|
175
|
+
lines.push(`| Metric | Value |`);
|
|
176
|
+
lines.push(`|--------|-------|`);
|
|
177
|
+
|
|
178
|
+
if (shieldScore) {
|
|
179
|
+
lines.push(`| Shield Score | ${shieldScore.score}/100 (${shieldScore.grade}) |`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (scanResults) {
|
|
183
|
+
lines.push(`| Status | ${scanResults.status} |`);
|
|
184
|
+
lines.push(`| Threats | ${scanResults.threats ? scanResults.threats.length : 0} |`);
|
|
185
|
+
lines.push(`| Blocked | ${scanResults.blocked ? 'Yes' : 'No'} |`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
lines.push('');
|
|
189
|
+
return lines.join('\n');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
BadgeGenerator,
|
|
195
|
+
GitHubActionReporter
|
|
196
|
+
};
|