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.
- package/CHANGELOG.md +90 -1
- package/README.md +38 -5
- package/bin/agent-shield.js +19 -0
- package/package.json +8 -4
- package/src/attack-genome.js +536 -0
- package/src/attack-replay.js +246 -0
- package/src/audit.js +619 -0
- package/src/behavioral-dna.js +762 -0
- package/src/circuit-breaker.js +321 -321
- package/src/compliance-authority.js +803 -0
- package/src/detector-core.js +3 -3
- package/src/distributed.js +403 -359
- package/src/errors.js +9 -0
- package/src/evolution-simulator.js +650 -0
- package/src/flight-recorder.js +379 -0
- package/src/fuzzer.js +764 -764
- package/src/herd-immunity.js +521 -0
- package/src/index.js +28 -11
- package/src/intent-firewall.js +775 -0
- package/src/main.js +135 -2
- package/src/mcp-security-runtime.js +36 -10
- package/src/mcp-server.js +12 -8
- package/src/middleware.js +306 -208
- package/src/multi-agent.js +421 -404
- package/src/pii.js +404 -390
- package/src/real-attack-datasets.js +246 -0
- package/src/report-generator.js +640 -0
- package/src/soc-dashboard.js +394 -0
- package/src/stream-scanner.js +34 -4
- package/src/supply-chain.js +667 -0
- package/src/testing.js +505 -505
- package/src/threat-intel-federation.js +343 -0
- package/src/utils.js +199 -83
- package/types/index.d.ts +374 -0
|
@@ -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
|
+
};
|