agentshield-sdk 7.2.0 → 7.3.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,803 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Agent Shield -- Compliance Certification Authority
5
+ *
6
+ * Makes Agent Shield the SOC 2/OWASP auditor for AI agents.
7
+ * Generates tamper-proof compliance certificates that auditors can verify.
8
+ *
9
+ * Features:
10
+ * - ComplianceCertificateAuthority: audit, issue, verify, revoke certificates
11
+ * - ComplianceReport: multi-format compliance reporting (JSON, text, HTML)
12
+ * - ComplianceScheduler: scheduled audits with trend analysis
13
+ *
14
+ * All processing runs locally -- no data ever leaves your environment.
15
+ */
16
+
17
+ const crypto = require('crypto');
18
+
19
+ // =========================================================================
20
+ // Framework Definitions
21
+ // =========================================================================
22
+
23
+ /**
24
+ * Compliance framework definitions with control checks.
25
+ * @type {Object}
26
+ */
27
+ const AUTHORITY_FRAMEWORKS = {
28
+ owasp: {
29
+ id: 'owasp',
30
+ name: 'OWASP LLM Top 10 v2025',
31
+ version: '2025',
32
+ controls: [
33
+ { id: 'LLM01', name: 'Prompt Injection', weight: 15, check: 'injection_scanning' },
34
+ { id: 'LLM02', name: 'Sensitive Information Disclosure', weight: 15, check: 'pii_protection' },
35
+ { id: 'LLM03', name: 'Supply Chain', weight: 10, check: 'supply_chain' },
36
+ { id: 'LLM04', name: 'Data and Model Poisoning', weight: 10, check: 'data_validation' },
37
+ { id: 'LLM05', name: 'Improper Output Handling', weight: 10, check: 'output_scanning' },
38
+ { id: 'LLM06', name: 'Excessive Agency', weight: 15, check: 'tool_permissions' },
39
+ { id: 'LLM07', name: 'System Prompt Leakage', weight: 10, check: 'prompt_leakage' },
40
+ { id: 'LLM08', name: 'Vector and Embedding Weaknesses', weight: 5, check: 'rag_scanning' },
41
+ { id: 'LLM09', name: 'Misinformation', weight: 5, check: 'behavior_profiling' },
42
+ { id: 'LLM10', name: 'Unbounded Consumption', weight: 5, check: 'rate_limiting' }
43
+ ]
44
+ },
45
+ nist: {
46
+ id: 'nist',
47
+ name: 'NIST AI RMF',
48
+ version: '1.0-2025',
49
+ controls: [
50
+ { id: 'GOVERN-1', name: 'AI Risk Culture', weight: 10, check: 'documentation' },
51
+ { id: 'GOVERN-2', name: 'Accountability', weight: 10, check: 'access_control' },
52
+ { id: 'GOVERN-5', name: 'Risk Assessment', weight: 15, check: 'risk_assessment' },
53
+ { id: 'GOVERN-6', name: 'Policies & Procedures', weight: 10, check: 'policy_enforcement' },
54
+ { id: 'MAP-3', name: 'Risk Identification', weight: 10, check: 'threat_detection' },
55
+ { id: 'MAP-5', name: 'Documentation', weight: 10, check: 'audit_trail' },
56
+ { id: 'MEASURE-1', name: 'Metrics', weight: 10, check: 'metrics_collection' },
57
+ { id: 'MEASURE-2', name: 'Testing', weight: 10, check: 'security_testing' },
58
+ { id: 'MANAGE-1', name: 'Risk Treatment', weight: 10, check: 'injection_scanning' },
59
+ { id: 'MANAGE-2', name: 'Incident Response', weight: 5, check: 'incident_response' }
60
+ ]
61
+ },
62
+ eu_ai_act: {
63
+ id: 'eu_ai_act',
64
+ name: 'EU AI Act',
65
+ version: '2024',
66
+ controls: [
67
+ { id: 'AIA-1', name: 'Risk Assessment', weight: 20, check: 'risk_assessment' },
68
+ { id: 'AIA-2', name: 'Transparency', weight: 15, check: 'logging' },
69
+ { id: 'AIA-3', name: 'Human Oversight', weight: 15, check: 'human_in_loop' },
70
+ { id: 'AIA-4', name: 'Data Governance', weight: 15, check: 'pii_protection' },
71
+ { id: 'AIA-5', name: 'Technical Documentation', weight: 10, check: 'documentation' },
72
+ { id: 'AIA-6', name: 'Record Keeping', weight: 15, check: 'audit_trail' },
73
+ { id: 'AIA-7', name: 'Accuracy & Robustness', weight: 10, check: 'accuracy_testing' }
74
+ ]
75
+ },
76
+ soc2: {
77
+ id: 'soc2',
78
+ name: 'SOC 2 (AI Controls)',
79
+ version: '2024',
80
+ controls: [
81
+ { id: 'CC6.1', name: 'Logical Access', weight: 20, check: 'access_control' },
82
+ { id: 'CC6.3', name: 'Authorization', weight: 15, check: 'tool_permissions' },
83
+ { id: 'CC7.1', name: 'Detection', weight: 20, check: 'injection_scanning' },
84
+ { id: 'CC7.2', name: 'Monitoring', weight: 15, check: 'logging' },
85
+ { id: 'CC8.1', name: 'Change Management', weight: 15, check: 'supply_chain' },
86
+ { id: 'P3.1', name: 'Privacy Notice', weight: 15, check: 'pii_protection' }
87
+ ]
88
+ }
89
+ };
90
+
91
+ /**
92
+ * Maps check names to Agent Shield capabilities.
93
+ * @type {Object}
94
+ */
95
+ const CAPABILITY_MAP = {
96
+ injection_scanning: { module: 'detector-core', description: 'Real-time pattern matching against injection signatures' },
97
+ output_scanning: { module: 'detector-core', description: 'Output scanning for dangerous content' },
98
+ pii_protection: { module: 'pii', description: 'PII detection and redaction' },
99
+ tool_permissions: { module: 'tool-guard', description: 'Tool permission boundaries and sequence analysis' },
100
+ rate_limiting: { module: 'circuit-breaker', description: 'Rate limiting and circuit breaker' },
101
+ logging: { module: 'policy', description: 'Structured logging and audit trail' },
102
+ audit_trail: { module: 'audit-immutable', description: 'Hash-chained tamper-evident audit log' },
103
+ access_control: { module: 'enterprise', description: 'Role-based access control and multi-tenant' },
104
+ supply_chain: { module: 'model-fingerprint', description: 'Model fingerprinting and supply chain detection' },
105
+ prompt_leakage: { module: 'prompt-leakage', description: 'System prompt extraction detection' },
106
+ rag_scanning: { module: 'rag-vulnerability', description: 'RAG/vector vulnerability scanning' },
107
+ behavior_profiling: { module: 'behavior-profiling', description: 'Statistical baselining and anomaly detection' },
108
+ threat_detection: { module: 'threat-encyclopedia', description: 'Threat reference database' },
109
+ policy_enforcement: { module: 'policy-dsl', description: 'Policy DSL parser and runtime' },
110
+ metrics_collection: { module: 'observability', description: 'Prometheus metrics and structured logging' },
111
+ security_testing: { module: 'redteam', description: 'Attack simulation and payload fuzzing' },
112
+ incident_response: { module: 'compliance', description: 'Incident response playbooks' },
113
+ risk_assessment: { module: 'shield-score', description: 'Shield score calculator and benchmarks' },
114
+ accuracy_testing: { module: 'shield-score', description: 'Shield score and benchmarking suite' },
115
+ data_validation: { module: 'scanners', description: 'RAG document scanning and validation' },
116
+ documentation: { available: false, description: 'Manual -- maintain system documentation' },
117
+ human_in_loop: { available: false, description: 'Manual -- implement approval workflows' }
118
+ };
119
+
120
+ /**
121
+ * Certificate level thresholds.
122
+ * @type {Array<Object>}
123
+ */
124
+ const CERTIFICATE_LEVELS = [
125
+ { name: 'Platinum', minScore: 95, color: '#E5E4E2' },
126
+ { name: 'Gold', minScore: 85, color: '#FFD700' },
127
+ { name: 'Silver', minScore: 75, color: '#C0C0C0' },
128
+ { name: 'Bronze', minScore: 60, color: '#CD7F32' },
129
+ { name: 'Fail', minScore: 0, color: '#FF4444' }
130
+ ];
131
+
132
+ // =========================================================================
133
+ // ComplianceCertificateAuthority
134
+ // =========================================================================
135
+
136
+ /**
137
+ * Compliance Certification Authority for AI agents.
138
+ * Audits agent configurations against compliance frameworks and issues
139
+ * tamper-proof, signed certificates.
140
+ */
141
+ class ComplianceCertificateAuthority {
142
+ /**
143
+ * @param {Object} [options]
144
+ * @param {string} [options.signingKey] - HMAC-SHA256 signing key
145
+ * @param {string} [options.issuer='Agent Shield CA'] - Certificate issuer name
146
+ * @param {string[]} [options.frameworks] - Framework IDs to audit against (default: all)
147
+ */
148
+ constructor(options = {}) {
149
+ this.signingKey = options.signingKey || crypto.randomBytes(32).toString('hex');
150
+ this.issuer = options.issuer || 'Agent Shield CA';
151
+ this.frameworks = options.frameworks || Object.keys(AUTHORITY_FRAMEWORKS);
152
+ this._certificates = new Map();
153
+ this._revoked = new Set();
154
+ console.log(`[Agent Shield] ComplianceCertificateAuthority initialized (issuer: ${this.issuer})`);
155
+ }
156
+
157
+ /**
158
+ * Run a comprehensive compliance audit against all configured frameworks.
159
+ * @param {Object} agentConfig - Agent configuration to audit
160
+ * @param {string[]} [agentConfig.enabledModules] - List of active module names
161
+ * @param {Object} [agentConfig.settings] - Agent settings
162
+ * @returns {Object} AuditResult with per-framework scores
163
+ */
164
+ audit(agentConfig = {}) {
165
+ const enabledModules = agentConfig.enabledModules || [];
166
+ const timestamp = new Date().toISOString();
167
+ const frameworkResults = {};
168
+ let totalWeightedScore = 0;
169
+ let totalWeight = 0;
170
+
171
+ for (const fwId of this.frameworks) {
172
+ const fw = AUTHORITY_FRAMEWORKS[fwId];
173
+ if (!fw) continue;
174
+
175
+ const controlResults = [];
176
+ let fwWeightedScore = 0;
177
+ let fwTotalWeight = 0;
178
+
179
+ for (const control of fw.controls) {
180
+ const capability = CAPABILITY_MAP[control.check];
181
+ let status = 'not_implemented';
182
+ let score = 0;
183
+
184
+ if (capability && capability.module) {
185
+ if (enabledModules.includes(capability.module)) {
186
+ status = 'compliant';
187
+ score = 100;
188
+ } else {
189
+ status = 'available';
190
+ score = 25;
191
+ }
192
+ } else if (capability && capability.available === false) {
193
+ status = 'manual';
194
+ score = 0;
195
+ }
196
+
197
+ fwWeightedScore += score * control.weight;
198
+ fwTotalWeight += control.weight;
199
+
200
+ controlResults.push({
201
+ id: control.id,
202
+ name: control.name,
203
+ status,
204
+ score,
205
+ weight: control.weight,
206
+ check: control.check,
207
+ description: capability ? capability.description : 'No mapping available'
208
+ });
209
+ }
210
+
211
+ const frameworkScore = fwTotalWeight > 0 ? Math.round(fwWeightedScore / fwTotalWeight) : 0;
212
+ totalWeightedScore += frameworkScore;
213
+ totalWeight += 1;
214
+
215
+ const findings = controlResults
216
+ .filter(c => c.status !== 'compliant')
217
+ .map(c => ({
218
+ controlId: c.id,
219
+ controlName: c.name,
220
+ status: c.status,
221
+ recommendation: c.status === 'available'
222
+ ? `Enable module "${CAPABILITY_MAP[c.check].module}" to achieve compliance`
223
+ : c.status === 'manual'
224
+ ? `Requires manual process: ${c.description}`
225
+ : `Implement ${c.check} capability`
226
+ }));
227
+
228
+ frameworkResults[fwId] = {
229
+ framework: fw.name,
230
+ version: fw.version,
231
+ score: frameworkScore,
232
+ controls: controlResults,
233
+ findings,
234
+ compliantCount: controlResults.filter(c => c.status === 'compliant').length,
235
+ totalControls: controlResults.length
236
+ };
237
+ }
238
+
239
+ const overallScore = totalWeight > 0 ? Math.round(totalWeightedScore / totalWeight) : 0;
240
+
241
+ return {
242
+ id: `audit_${crypto.randomBytes(8).toString('hex')}`,
243
+ timestamp,
244
+ issuer: this.issuer,
245
+ subject: agentConfig.name || 'AI Agent',
246
+ overallScore,
247
+ level: this._getLevel(overallScore),
248
+ frameworks: frameworkResults,
249
+ enabledModules,
250
+ allFindings: Object.values(frameworkResults).reduce((acc, fw) => acc.concat(fw.findings), [])
251
+ };
252
+ }
253
+
254
+ /**
255
+ * Generate a signed, tamper-proof certificate from an audit result.
256
+ * @param {Object} auditResult - Result from audit()
257
+ * @returns {Object} Signed certificate
258
+ */
259
+ issueCertificate(auditResult) {
260
+ const certId = `cert_${crypto.randomBytes(12).toString('hex')}`;
261
+ const issuedAt = new Date().toISOString();
262
+ const expiresAt = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(); // 90 days
263
+
264
+ const frameworkScores = {};
265
+ for (const [fwId, fwResult] of Object.entries(auditResult.frameworks || {})) {
266
+ frameworkScores[fwId] = {
267
+ framework: fwResult.framework,
268
+ score: fwResult.score,
269
+ compliantCount: fwResult.compliantCount,
270
+ totalControls: fwResult.totalControls
271
+ };
272
+ }
273
+
274
+ const payload = {
275
+ id: certId,
276
+ type: 'agent-shield-compliance-certificate',
277
+ version: '1.0',
278
+ issuer: this.issuer,
279
+ subject: auditResult.subject,
280
+ issuedAt,
281
+ expiresAt,
282
+ overallScore: auditResult.overallScore,
283
+ level: auditResult.level,
284
+ frameworks: frameworkScores,
285
+ findings: auditResult.allFindings,
286
+ auditId: auditResult.id
287
+ };
288
+
289
+ const signature = this._sign(payload);
290
+
291
+ const certificate = {
292
+ ...payload,
293
+ signature
294
+ };
295
+
296
+ this._certificates.set(certId, certificate);
297
+ console.log(`[Agent Shield] Certificate issued: ${certId} (${auditResult.level}, score: ${auditResult.overallScore})`);
298
+
299
+ return certificate;
300
+ }
301
+
302
+ /**
303
+ * Verify a certificate's signature and expiry.
304
+ * @param {Object} certificate - Certificate to verify
305
+ * @returns {Object} Verification result { valid, expired, revoked, reason }
306
+ */
307
+ verifyCertificate(certificate) {
308
+ if (!certificate || !certificate.id || !certificate.signature) {
309
+ return { valid: false, expired: false, revoked: false, reason: 'Missing required certificate fields' };
310
+ }
311
+
312
+ if (this._revoked.has(certificate.id)) {
313
+ return { valid: false, expired: false, revoked: true, reason: 'Certificate has been revoked' };
314
+ }
315
+
316
+ const now = new Date();
317
+ const expiresAt = new Date(certificate.expiresAt);
318
+ if (now > expiresAt) {
319
+ return { valid: false, expired: true, revoked: false, reason: 'Certificate has expired' };
320
+ }
321
+
322
+ const payloadCopy = { ...certificate };
323
+ delete payloadCopy.signature;
324
+ const expectedSignature = this._sign(payloadCopy);
325
+
326
+ if (expectedSignature !== certificate.signature) {
327
+ return { valid: false, expired: false, revoked: false, reason: 'Signature verification failed -- certificate may have been tampered with' };
328
+ }
329
+
330
+ return { valid: true, expired: false, revoked: false, reason: 'Certificate is valid' };
331
+ }
332
+
333
+ /**
334
+ * Revoke a certificate.
335
+ * @param {string} certId - Certificate ID to revoke
336
+ * @param {string} reason - Reason for revocation
337
+ * @returns {Object} Revocation result
338
+ */
339
+ revokeCertificate(certId, reason) {
340
+ if (!this._certificates.has(certId)) {
341
+ return { success: false, reason: 'Certificate not found' };
342
+ }
343
+
344
+ this._revoked.add(certId);
345
+ console.log(`[Agent Shield] Certificate revoked: ${certId} (reason: ${reason})`);
346
+
347
+ return {
348
+ success: true,
349
+ certId,
350
+ reason,
351
+ revokedAt: new Date().toISOString()
352
+ };
353
+ }
354
+
355
+ /**
356
+ * Return all issued certificates.
357
+ * @returns {Array<Object>} Certificate history
358
+ */
359
+ getCertificateHistory() {
360
+ return Array.from(this._certificates.values()).map(cert => ({
361
+ id: cert.id,
362
+ issuer: cert.issuer,
363
+ subject: cert.subject,
364
+ issuedAt: cert.issuedAt,
365
+ expiresAt: cert.expiresAt,
366
+ overallScore: cert.overallScore,
367
+ level: cert.level,
368
+ revoked: this._revoked.has(cert.id)
369
+ }));
370
+ }
371
+
372
+ /**
373
+ * Compute HMAC-SHA256 signature for a payload.
374
+ * @private
375
+ * @param {Object} payload - Data to sign
376
+ * @returns {string} Hex-encoded HMAC signature
377
+ */
378
+ _sign(payload) {
379
+ const data = JSON.stringify(payload, Object.keys(payload).sort());
380
+ return crypto.createHmac('sha256', this.signingKey).update(data).digest('hex');
381
+ }
382
+
383
+ /**
384
+ * Determine certificate level from score.
385
+ * @private
386
+ * @param {number} score - Overall score 0-100
387
+ * @returns {string} Level name
388
+ */
389
+ _getLevel(score) {
390
+ for (const level of CERTIFICATE_LEVELS) {
391
+ if (score >= level.minScore) return level.name;
392
+ }
393
+ return 'Fail';
394
+ }
395
+ }
396
+
397
+ // =========================================================================
398
+ // ComplianceReport
399
+ // =========================================================================
400
+
401
+ /**
402
+ * Multi-format compliance report generator.
403
+ * Takes an audit result and produces reports in JSON, text, or HTML.
404
+ */
405
+ class ComplianceReport {
406
+ /**
407
+ * @param {Object} [auditResult] - Audit result to report on
408
+ */
409
+ constructor(auditResult) {
410
+ this._auditResult = auditResult || null;
411
+ this._report = null;
412
+ }
413
+
414
+ /**
415
+ * Generate a formatted compliance report from an audit result.
416
+ * @param {Object} auditResult - Result from ComplianceCertificateAuthority.audit()
417
+ * @returns {ComplianceReport} this (for chaining)
418
+ */
419
+ generate(auditResult) {
420
+ const result = auditResult || this._auditResult;
421
+ if (!result) throw new Error('No audit result provided');
422
+
423
+ this._auditResult = result;
424
+
425
+ const gapAnalysis = [];
426
+ const remediationSteps = [];
427
+ let stepPriority = 1;
428
+
429
+ for (const [fwId, fwResult] of Object.entries(result.frameworks || {})) {
430
+ for (const finding of fwResult.findings || []) {
431
+ gapAnalysis.push({
432
+ framework: fwResult.framework,
433
+ frameworkId: fwId,
434
+ controlId: finding.controlId,
435
+ controlName: finding.controlName,
436
+ status: finding.status,
437
+ recommendation: finding.recommendation
438
+ });
439
+
440
+ if (finding.status === 'available') {
441
+ remediationSteps.push({
442
+ priority: stepPriority++,
443
+ effort: 'low',
444
+ action: finding.recommendation,
445
+ control: `${fwId}/${finding.controlId}`,
446
+ impact: 'Immediate compliance improvement'
447
+ });
448
+ } else if (finding.status === 'not_implemented') {
449
+ remediationSteps.push({
450
+ priority: stepPriority++,
451
+ effort: 'medium',
452
+ action: finding.recommendation,
453
+ control: `${fwId}/${finding.controlId}`,
454
+ impact: 'Addresses compliance gap'
455
+ });
456
+ } else if (finding.status === 'manual') {
457
+ remediationSteps.push({
458
+ priority: stepPriority++,
459
+ effort: 'high',
460
+ action: finding.recommendation,
461
+ control: `${fwId}/${finding.controlId}`,
462
+ impact: 'Requires process implementation'
463
+ });
464
+ }
465
+ }
466
+ }
467
+
468
+ // Sort remediation: low effort first, then medium, then high
469
+ const effortOrder = { low: 0, medium: 1, high: 2 };
470
+ remediationSteps.sort((a, b) => (effortOrder[a.effort] || 2) - (effortOrder[b.effort] || 2));
471
+ remediationSteps.forEach((step, i) => { step.priority = i + 1; });
472
+
473
+ this._report = {
474
+ title: 'Agent Shield Compliance Report',
475
+ generatedAt: new Date().toISOString(),
476
+ subject: result.subject,
477
+ overallScore: result.overallScore,
478
+ level: result.level,
479
+ executiveSummary: this._generateExecutiveSummary(result),
480
+ frameworkBreakdown: Object.entries(result.frameworks || {}).map(([fwId, fw]) => ({
481
+ id: fwId,
482
+ name: fw.framework,
483
+ version: fw.version,
484
+ score: fw.score,
485
+ compliantCount: fw.compliantCount,
486
+ totalControls: fw.totalControls,
487
+ controls: fw.controls
488
+ })),
489
+ gapAnalysis,
490
+ remediationRoadmap: remediationSteps
491
+ };
492
+
493
+ return this;
494
+ }
495
+
496
+ /**
497
+ * Export report as JSON.
498
+ * @returns {string} JSON string
499
+ */
500
+ toJSON() {
501
+ if (!this._report) throw new Error('Report not generated. Call generate() first.');
502
+ return JSON.stringify(this._report, null, 2);
503
+ }
504
+
505
+ /**
506
+ * Export report as plain text.
507
+ * @returns {string} Formatted text
508
+ */
509
+ toText() {
510
+ if (!this._report) throw new Error('Report not generated. Call generate() first.');
511
+ const r = this._report;
512
+ const lines = [];
513
+
514
+ lines.push('================================================================');
515
+ lines.push(' AGENT SHIELD COMPLIANCE REPORT');
516
+ lines.push('================================================================');
517
+ lines.push('');
518
+ lines.push(` Subject: ${r.subject}`);
519
+ lines.push(` Generated: ${r.generatedAt}`);
520
+ lines.push(` Score: ${r.overallScore}/100`);
521
+ lines.push(` Level: ${r.level}`);
522
+ lines.push('');
523
+
524
+ // Executive summary
525
+ lines.push('-- EXECUTIVE SUMMARY --');
526
+ lines.push('');
527
+ lines.push(` ${r.executiveSummary}`);
528
+ lines.push('');
529
+
530
+ // Framework breakdown
531
+ lines.push('-- FRAMEWORK BREAKDOWN --');
532
+ lines.push('');
533
+ for (const fw of r.frameworkBreakdown) {
534
+ const bar = '#'.repeat(Math.round(fw.score / 5)) + '.'.repeat(20 - Math.round(fw.score / 5));
535
+ lines.push(` ${fw.name}`);
536
+ lines.push(` Score: [${bar}] ${fw.score}/100 (${fw.compliantCount}/${fw.totalControls} controls)`);
537
+ for (const ctrl of fw.controls) {
538
+ const icon = ctrl.status === 'compliant' ? '[OK]' : ctrl.status === 'available' ? '[--]' : '[ ]';
539
+ lines.push(` ${icon} ${ctrl.id} ${ctrl.name} (${ctrl.status})`);
540
+ }
541
+ lines.push('');
542
+ }
543
+
544
+ // Gap analysis
545
+ if (r.gapAnalysis.length > 0) {
546
+ lines.push('-- GAP ANALYSIS --');
547
+ lines.push('');
548
+ for (const gap of r.gapAnalysis) {
549
+ lines.push(` ${gap.frameworkId}/${gap.controlId} ${gap.controlName}`);
550
+ lines.push(` Status: ${gap.status}`);
551
+ lines.push(` Action: ${gap.recommendation}`);
552
+ }
553
+ lines.push('');
554
+ }
555
+
556
+ // Remediation roadmap
557
+ if (r.remediationRoadmap.length > 0) {
558
+ lines.push('-- REMEDIATION ROADMAP --');
559
+ lines.push('');
560
+ for (const step of r.remediationRoadmap) {
561
+ lines.push(` ${step.priority}. [${step.effort.toUpperCase()}] ${step.action}`);
562
+ lines.push(` Control: ${step.control} | Impact: ${step.impact}`);
563
+ }
564
+ lines.push('');
565
+ }
566
+
567
+ return lines.join('\n');
568
+ }
569
+
570
+ /**
571
+ * Export report as HTML.
572
+ * @returns {string} HTML document
573
+ */
574
+ toHTML() {
575
+ if (!this._report) throw new Error('Report not generated. Call generate() first.');
576
+ const r = this._report;
577
+
578
+ const levelColor = r.level === 'Platinum' ? '#E5E4E2'
579
+ : r.level === 'Gold' ? '#FFD700'
580
+ : r.level === 'Silver' ? '#C0C0C0'
581
+ : r.level === 'Bronze' ? '#CD7F32'
582
+ : '#FF4444';
583
+
584
+ const controlRows = r.frameworkBreakdown.map(fw => {
585
+ const rows = fw.controls.map(ctrl => {
586
+ const statusColor = ctrl.status === 'compliant' ? '#4CAF50'
587
+ : ctrl.status === 'available' ? '#FF9800'
588
+ : '#F44336';
589
+ return `<tr><td>${fw.name}</td><td>${ctrl.id}</td><td>${ctrl.name}</td><td style="color:${statusColor};font-weight:bold">${ctrl.status}</td><td>${ctrl.score}</td></tr>`;
590
+ });
591
+ return rows.join('\n');
592
+ }).join('\n');
593
+
594
+ const gapRows = r.gapAnalysis.map(gap =>
595
+ `<tr><td>${gap.frameworkId}</td><td>${gap.controlId}</td><td>${gap.controlName}</td><td>${gap.status}</td><td>${gap.recommendation}</td></tr>`
596
+ ).join('\n');
597
+
598
+ const remediationRows = r.remediationRoadmap.map(step =>
599
+ `<tr><td>${step.priority}</td><td>${step.effort}</td><td>${step.action}</td><td>${step.control}</td><td>${step.impact}</td></tr>`
600
+ ).join('\n');
601
+
602
+ return `<!DOCTYPE html>
603
+ <html lang="en">
604
+ <head>
605
+ <meta charset="UTF-8">
606
+ <title>Agent Shield Compliance Report</title>
607
+ <style>
608
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 2em; background: #fafafa; color: #333; }
609
+ h1 { border-bottom: 3px solid ${levelColor}; padding-bottom: 0.5em; }
610
+ h2 { color: #555; margin-top: 2em; }
611
+ .score-badge { display: inline-block; padding: 0.5em 1.5em; background: ${levelColor}; color: #333; font-size: 1.5em; font-weight: bold; border-radius: 8px; }
612
+ .summary { background: #fff; border: 1px solid #ddd; padding: 1.5em; border-radius: 8px; margin: 1em 0; }
613
+ table { border-collapse: collapse; width: 100%; margin: 1em 0; }
614
+ th, td { border: 1px solid #ddd; padding: 0.6em 1em; text-align: left; }
615
+ th { background: #f5f5f5; font-weight: 600; }
616
+ tr:nth-child(even) { background: #fafafa; }
617
+ .meta { color: #888; font-size: 0.9em; }
618
+ </style>
619
+ </head>
620
+ <body>
621
+ <h1>Agent Shield Compliance Report</h1>
622
+ <p class="meta">Subject: ${r.subject} | Generated: ${r.generatedAt}</p>
623
+ <div class="score-badge">${r.level} - ${r.overallScore}/100</div>
624
+
625
+ <h2>Executive Summary</h2>
626
+ <div class="summary">${r.executiveSummary}</div>
627
+
628
+ <h2>Framework Breakdown</h2>
629
+ <table>
630
+ <tr><th>Framework</th><th>Control ID</th><th>Control</th><th>Status</th><th>Score</th></tr>
631
+ ${controlRows}
632
+ </table>
633
+
634
+ <h2>Gap Analysis</h2>
635
+ <table>
636
+ <tr><th>Framework</th><th>Control ID</th><th>Control</th><th>Status</th><th>Recommendation</th></tr>
637
+ ${gapRows}
638
+ </table>
639
+
640
+ <h2>Remediation Roadmap</h2>
641
+ <table>
642
+ <tr><th>#</th><th>Effort</th><th>Action</th><th>Control</th><th>Impact</th></tr>
643
+ ${remediationRows}
644
+ </table>
645
+
646
+ <p class="meta">Generated by Agent Shield Compliance Authority</p>
647
+ </body>
648
+ </html>`;
649
+ }
650
+
651
+ /**
652
+ * Generate an executive summary string.
653
+ * @private
654
+ * @param {Object} auditResult - Audit result
655
+ * @returns {string} Summary text
656
+ */
657
+ _generateExecutiveSummary(auditResult) {
658
+ const fwNames = Object.values(auditResult.frameworks || {}).map(f => f.framework).join(', ');
659
+ const totalFindings = auditResult.allFindings ? auditResult.allFindings.length : 0;
660
+ const criticalGaps = (auditResult.allFindings || []).filter(f => f.status === 'not_implemented').length;
661
+ const availableQuickWins = (auditResult.allFindings || []).filter(f => f.status === 'available').length;
662
+
663
+ let summary = `The AI agent "${auditResult.subject}" was audited against ${fwNames}. `;
664
+ summary += `Overall compliance score: ${auditResult.overallScore}/100 (${auditResult.level}). `;
665
+ summary += `${totalFindings} finding(s) identified. `;
666
+
667
+ if (criticalGaps > 0) {
668
+ summary += `${criticalGaps} control(s) require implementation. `;
669
+ }
670
+ if (availableQuickWins > 0) {
671
+ summary += `${availableQuickWins} quick win(s) available by enabling existing modules. `;
672
+ }
673
+ if (auditResult.overallScore >= 95) {
674
+ summary += 'The agent demonstrates excellent compliance posture.';
675
+ } else if (auditResult.overallScore >= 85) {
676
+ summary += 'The agent demonstrates strong compliance posture with minor gaps.';
677
+ } else if (auditResult.overallScore >= 60) {
678
+ summary += 'The agent meets minimum compliance thresholds but has notable gaps to address.';
679
+ } else {
680
+ summary += 'The agent does not meet minimum compliance thresholds. Immediate remediation is required.';
681
+ }
682
+
683
+ return summary;
684
+ }
685
+ }
686
+
687
+ // =========================================================================
688
+ // ComplianceScheduler
689
+ // =========================================================================
690
+
691
+ /**
692
+ * Schedules periodic compliance audits and tracks trends.
693
+ */
694
+ class ComplianceScheduler {
695
+ constructor() {
696
+ this._history = [];
697
+ this._timers = [];
698
+ }
699
+
700
+ /**
701
+ * Schedule recurring audits.
702
+ * @param {number} interval - Interval in milliseconds between audits
703
+ * @param {Function} auditFn - Function that returns an audit result (sync or async)
704
+ * @returns {Object} Schedule handle with stop() method
705
+ */
706
+ schedule(interval, auditFn) {
707
+ const handle = {
708
+ id: `sched_${crypto.randomBytes(4).toString('hex')}`,
709
+ interval,
710
+ started: new Date().toISOString(),
711
+ _timer: null,
712
+ stopped: false
713
+ };
714
+
715
+ const runAudit = async () => {
716
+ if (handle.stopped) return;
717
+ try {
718
+ const result = typeof auditFn === 'function' ? await auditFn() : null;
719
+ if (result) {
720
+ this._history.push({
721
+ timestamp: new Date().toISOString(),
722
+ scheduleId: handle.id,
723
+ overallScore: result.overallScore,
724
+ level: result.level,
725
+ frameworkScores: Object.entries(result.frameworks || {}).reduce((acc, [k, v]) => {
726
+ acc[k] = v.score;
727
+ return acc;
728
+ }, {}),
729
+ findingCount: result.allFindings ? result.allFindings.length : 0
730
+ });
731
+ console.log(`[Agent Shield] Scheduled audit complete: score ${result.overallScore} (${result.level})`);
732
+ }
733
+ } catch (err) {
734
+ console.log(`[Agent Shield] Scheduled audit failed: ${err.message}`);
735
+ }
736
+ };
737
+
738
+ // Run first audit immediately
739
+ runAudit();
740
+
741
+ handle._timer = setInterval(runAudit, interval);
742
+ handle.stop = () => {
743
+ handle.stopped = true;
744
+ if (handle._timer) {
745
+ clearInterval(handle._timer);
746
+ handle._timer = null;
747
+ }
748
+ };
749
+
750
+ this._timers.push(handle);
751
+ return handle;
752
+ }
753
+
754
+ /**
755
+ * Return audit history with all recorded data.
756
+ * @returns {Array<Object>} Audit history entries
757
+ */
758
+ getHistory() {
759
+ return [...this._history];
760
+ }
761
+
762
+ /**
763
+ * Analyze the trend across recent audits.
764
+ * @returns {'improving'|'stable'|'degrading'} Trend direction
765
+ */
766
+ getTrend() {
767
+ if (this._history.length < 2) return 'stable';
768
+
769
+ const recent = this._history.slice(-5);
770
+ if (recent.length < 2) return 'stable';
771
+
772
+ const first = recent[0].overallScore;
773
+ const last = recent[recent.length - 1].overallScore;
774
+ const delta = last - first;
775
+
776
+ if (delta > 3) return 'improving';
777
+ if (delta < -3) return 'degrading';
778
+ return 'stable';
779
+ }
780
+
781
+ /**
782
+ * Stop all scheduled audits.
783
+ */
784
+ stopAll() {
785
+ for (const handle of this._timers) {
786
+ handle.stop();
787
+ }
788
+ this._timers = [];
789
+ }
790
+ }
791
+
792
+ // =========================================================================
793
+ // Exports
794
+ // =========================================================================
795
+
796
+ module.exports = {
797
+ ComplianceCertificateAuthority,
798
+ ComplianceReport,
799
+ ComplianceScheduler,
800
+ AUTHORITY_FRAMEWORKS,
801
+ CAPABILITY_MAP,
802
+ CERTIFICATE_LEVELS
803
+ };