aura-security 0.4.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.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +446 -0
  3. package/deploy/AWS-DEPLOYMENT.md +358 -0
  4. package/deploy/terraform/main.tf +362 -0
  5. package/deploy/terraform/terraform.tfvars.example +6 -0
  6. package/dist/agents/base.d.ts +44 -0
  7. package/dist/agents/base.js +96 -0
  8. package/dist/agents/index.d.ts +14 -0
  9. package/dist/agents/index.js +17 -0
  10. package/dist/agents/policy/evaluator.d.ts +15 -0
  11. package/dist/agents/policy/evaluator.js +183 -0
  12. package/dist/agents/policy/index.d.ts +12 -0
  13. package/dist/agents/policy/index.js +15 -0
  14. package/dist/agents/policy/validator.d.ts +15 -0
  15. package/dist/agents/policy/validator.js +182 -0
  16. package/dist/agents/scanners/gitleaks.d.ts +14 -0
  17. package/dist/agents/scanners/gitleaks.js +155 -0
  18. package/dist/agents/scanners/grype.d.ts +14 -0
  19. package/dist/agents/scanners/grype.js +109 -0
  20. package/dist/agents/scanners/index.d.ts +15 -0
  21. package/dist/agents/scanners/index.js +27 -0
  22. package/dist/agents/scanners/npm-audit.d.ts +13 -0
  23. package/dist/agents/scanners/npm-audit.js +129 -0
  24. package/dist/agents/scanners/semgrep.d.ts +14 -0
  25. package/dist/agents/scanners/semgrep.js +131 -0
  26. package/dist/agents/scanners/trivy.d.ts +14 -0
  27. package/dist/agents/scanners/trivy.js +122 -0
  28. package/dist/agents/types.d.ts +137 -0
  29. package/dist/agents/types.js +91 -0
  30. package/dist/auditor/index.d.ts +3 -0
  31. package/dist/auditor/index.js +2 -0
  32. package/dist/auditor/pipeline.d.ts +19 -0
  33. package/dist/auditor/pipeline.js +240 -0
  34. package/dist/auditor/validator.d.ts +17 -0
  35. package/dist/auditor/validator.js +58 -0
  36. package/dist/aura/client.d.ts +29 -0
  37. package/dist/aura/client.js +125 -0
  38. package/dist/aura/index.d.ts +4 -0
  39. package/dist/aura/index.js +2 -0
  40. package/dist/aura/server.d.ts +45 -0
  41. package/dist/aura/server.js +343 -0
  42. package/dist/cli.d.ts +17 -0
  43. package/dist/cli.js +1433 -0
  44. package/dist/client/index.d.ts +41 -0
  45. package/dist/client/index.js +170 -0
  46. package/dist/compliance/index.d.ts +40 -0
  47. package/dist/compliance/index.js +292 -0
  48. package/dist/database/index.d.ts +77 -0
  49. package/dist/database/index.js +395 -0
  50. package/dist/index.d.ts +25 -0
  51. package/dist/index.js +762 -0
  52. package/dist/integrations/aura-scanner.d.ts +69 -0
  53. package/dist/integrations/aura-scanner.js +155 -0
  54. package/dist/integrations/aws-scanner.d.ts +63 -0
  55. package/dist/integrations/aws-scanner.js +624 -0
  56. package/dist/integrations/config.d.ts +69 -0
  57. package/dist/integrations/config.js +212 -0
  58. package/dist/integrations/github.d.ts +45 -0
  59. package/dist/integrations/github.js +201 -0
  60. package/dist/integrations/gitlab.d.ts +36 -0
  61. package/dist/integrations/gitlab.js +110 -0
  62. package/dist/integrations/index.d.ts +11 -0
  63. package/dist/integrations/index.js +11 -0
  64. package/dist/integrations/local-scanner.d.ts +146 -0
  65. package/dist/integrations/local-scanner.js +1654 -0
  66. package/dist/integrations/notifications.d.ts +99 -0
  67. package/dist/integrations/notifications.js +305 -0
  68. package/dist/integrations/scanners.d.ts +57 -0
  69. package/dist/integrations/scanners.js +217 -0
  70. package/dist/integrations/slop-scanner.d.ts +69 -0
  71. package/dist/integrations/slop-scanner.js +155 -0
  72. package/dist/integrations/webhook.d.ts +37 -0
  73. package/dist/integrations/webhook.js +256 -0
  74. package/dist/orchestrator/index.d.ts +72 -0
  75. package/dist/orchestrator/index.js +187 -0
  76. package/dist/output/index.d.ts +152 -0
  77. package/dist/output/index.js +399 -0
  78. package/dist/pipeline/index.d.ts +72 -0
  79. package/dist/pipeline/index.js +313 -0
  80. package/dist/sbom/index.d.ts +94 -0
  81. package/dist/sbom/index.js +298 -0
  82. package/dist/schemas/index.d.ts +2 -0
  83. package/dist/schemas/index.js +2 -0
  84. package/dist/schemas/input.schema.d.ts +87 -0
  85. package/dist/schemas/input.schema.js +44 -0
  86. package/dist/schemas/output.schema.d.ts +115 -0
  87. package/dist/schemas/output.schema.js +64 -0
  88. package/dist/serve-visualizer.d.ts +2 -0
  89. package/dist/serve-visualizer.js +78 -0
  90. package/dist/slop/client.d.ts +29 -0
  91. package/dist/slop/client.js +125 -0
  92. package/dist/slop/index.d.ts +4 -0
  93. package/dist/slop/index.js +2 -0
  94. package/dist/slop/server.d.ts +45 -0
  95. package/dist/slop/server.js +343 -0
  96. package/dist/types/events.d.ts +62 -0
  97. package/dist/types/events.js +2 -0
  98. package/dist/types/index.d.ts +1 -0
  99. package/dist/types/index.js +1 -0
  100. package/dist/visualizer/index.d.ts +4 -0
  101. package/dist/visualizer/index.js +181 -0
  102. package/dist/websocket/index.d.ts +88 -0
  103. package/dist/websocket/index.js +195 -0
  104. package/dist/zones/index.d.ts +7 -0
  105. package/dist/zones/index.js +7 -0
  106. package/dist/zones/manager.d.ts +101 -0
  107. package/dist/zones/manager.js +304 -0
  108. package/dist/zones/types.d.ts +78 -0
  109. package/dist/zones/types.js +33 -0
  110. package/package.json +84 -0
  111. package/visualizer/app.js +0 -0
  112. package/visualizer/index-minimal.html +1771 -0
  113. package/visualizer/index.html +2933 -0
  114. package/visualizer/landing.html +1328 -0
  115. package/visualizer/styles.css +0 -0
@@ -0,0 +1,343 @@
1
+ // SLOP Server - Minimal implementation for auditor pipeline
2
+ // Exposes /tools, /memory, /info, /settings, /audits, /stats endpoints
3
+ import { createServer } from 'http';
4
+ import { getDatabase } from '../database/index.js';
5
+ import { NotificationService, createNotificationFromAudit } from '../integrations/notifications.js';
6
+ export class SlopServer {
7
+ server = null;
8
+ tools = new Map();
9
+ memory = new Map();
10
+ config;
11
+ db;
12
+ notificationService;
13
+ constructor(config) {
14
+ this.config = {
15
+ port: config.port,
16
+ host: config.host ?? '127.0.0.1',
17
+ dbPath: config.dbPath ?? process.cwd()
18
+ };
19
+ // Initialize database
20
+ this.db = getDatabase(this.config.dbPath);
21
+ // Initialize notification service
22
+ this.notificationService = new NotificationService({}, this.config.dbPath);
23
+ this.notificationService.loadFromDatabase();
24
+ }
25
+ getNotificationService() {
26
+ return this.notificationService;
27
+ }
28
+ reloadNotifications() {
29
+ this.notificationService.loadFromDatabase();
30
+ }
31
+ registerTool(tool) {
32
+ this.tools.set(tool.name, tool);
33
+ }
34
+ getDatabase() {
35
+ return this.db;
36
+ }
37
+ async handleRequest(req, res) {
38
+ const url = new URL(req.url ?? '/', `http://${req.headers.host}`);
39
+ const path = url.pathname;
40
+ // CORS headers for visualizer access
41
+ res.setHeader('Access-Control-Allow-Origin', '*');
42
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
43
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
44
+ res.setHeader('Content-Type', 'application/json');
45
+ // Handle preflight
46
+ if (req.method === 'OPTIONS') {
47
+ res.statusCode = 204;
48
+ res.end();
49
+ return;
50
+ }
51
+ try {
52
+ // Core SLOP endpoints
53
+ if (path === '/info' && req.method === 'GET') {
54
+ await this.handleInfo(res);
55
+ }
56
+ else if (path === '/tools' && req.method === 'GET') {
57
+ await this.handleListTools(res);
58
+ }
59
+ else if (path === '/tools' && req.method === 'POST') {
60
+ await this.handleCallTool(req, res);
61
+ }
62
+ else if (path === '/memory' && req.method === 'POST') {
63
+ await this.handleMemoryWrite(req, res);
64
+ }
65
+ else if (path === '/memory' && req.method === 'GET') {
66
+ await this.handleMemoryRead(url, res);
67
+ }
68
+ // Settings endpoints
69
+ else if (path === '/settings' && req.method === 'GET') {
70
+ await this.handleGetSettings(url, res);
71
+ }
72
+ else if (path === '/settings' && req.method === 'POST') {
73
+ await this.handleSaveSettings(req, res);
74
+ }
75
+ // Audit history endpoints
76
+ else if (path === '/audits' && req.method === 'GET') {
77
+ await this.handleGetAudits(url, res);
78
+ }
79
+ else if (path.startsWith('/audits/') && req.method === 'GET') {
80
+ const id = path.slice(8);
81
+ await this.handleGetAudit(id, res);
82
+ }
83
+ else if (path.startsWith('/audits/') && req.method === 'DELETE') {
84
+ const id = path.slice(8);
85
+ await this.handleDeleteAudit(id, res);
86
+ }
87
+ // Stats endpoint
88
+ else if (path === '/stats' && req.method === 'GET') {
89
+ await this.handleGetStats(res);
90
+ }
91
+ // Notifications endpoints
92
+ else if (path === '/notifications' && req.method === 'GET') {
93
+ await this.handleGetNotifications(url, res);
94
+ }
95
+ else if (path === '/notifications/test' && req.method === 'POST') {
96
+ await this.handleTestNotification(req, res);
97
+ }
98
+ else if (path === '/notifications/send' && req.method === 'POST') {
99
+ await this.handleSendNotification(req, res);
100
+ }
101
+ else {
102
+ res.statusCode = 404;
103
+ res.end(JSON.stringify({ error: 'Not found' }));
104
+ }
105
+ }
106
+ catch (err) {
107
+ console.error('[SERVER] Error:', err);
108
+ // Fail-closed: return 500 on any error
109
+ res.statusCode = 500;
110
+ res.end(JSON.stringify({
111
+ error: 'Internal server error',
112
+ message: err instanceof Error ? err.message : 'Unknown error',
113
+ blocked: true
114
+ }));
115
+ }
116
+ }
117
+ async handleInfo(res) {
118
+ res.statusCode = 200;
119
+ res.end(JSON.stringify({
120
+ name: 'slop-auditor',
121
+ version: '0.2.0',
122
+ endpoints: ['/info', '/tools', '/memory', '/settings', '/audits', '/stats', '/notifications'],
123
+ tools: Array.from(this.tools.keys()),
124
+ database: true
125
+ }));
126
+ }
127
+ async handleListTools(res) {
128
+ const toolList = Array.from(this.tools.values()).map(t => ({
129
+ name: t.name,
130
+ description: t.description,
131
+ parameters: t.parameters
132
+ }));
133
+ res.statusCode = 200;
134
+ res.end(JSON.stringify({ tools: toolList }));
135
+ }
136
+ async handleCallTool(req, res) {
137
+ const body = await this.readBody(req);
138
+ const { tool, arguments: args } = JSON.parse(body);
139
+ const toolDef = this.tools.get(tool);
140
+ if (!toolDef) {
141
+ res.statusCode = 404;
142
+ res.end(JSON.stringify({ error: `Tool not found: ${tool}` }));
143
+ return;
144
+ }
145
+ const result = await toolDef.handler(args ?? {});
146
+ res.statusCode = 200;
147
+ res.end(JSON.stringify({ result }));
148
+ }
149
+ async handleMemoryWrite(req, res) {
150
+ const body = await this.readBody(req);
151
+ const { key, value, metadata } = JSON.parse(body);
152
+ this.memory.set(key, { value, metadata, timestamp: new Date().toISOString() });
153
+ res.statusCode = 201;
154
+ res.end(JSON.stringify({ status: 'stored', key }));
155
+ }
156
+ async handleMemoryRead(url, res) {
157
+ const key = url.searchParams.get('key');
158
+ if (key) {
159
+ const entry = this.memory.get(key);
160
+ if (entry) {
161
+ res.statusCode = 200;
162
+ res.end(JSON.stringify(entry));
163
+ }
164
+ else {
165
+ res.statusCode = 404;
166
+ res.end(JSON.stringify({ error: 'Key not found' }));
167
+ }
168
+ }
169
+ else {
170
+ res.statusCode = 200;
171
+ res.end(JSON.stringify({ keys: Array.from(this.memory.keys()) }));
172
+ }
173
+ }
174
+ // ============ SETTINGS ENDPOINTS ============
175
+ async handleGetSettings(url, res) {
176
+ const prefix = url.searchParams.get('prefix');
177
+ let settings;
178
+ if (prefix) {
179
+ settings = this.db.getSettings(prefix);
180
+ }
181
+ else {
182
+ settings = this.db.getAllSettings();
183
+ }
184
+ res.statusCode = 200;
185
+ res.end(JSON.stringify({ settings }));
186
+ }
187
+ async handleSaveSettings(req, res) {
188
+ const body = await this.readBody(req);
189
+ const { settings } = JSON.parse(body);
190
+ if (!settings || typeof settings !== 'object') {
191
+ res.statusCode = 400;
192
+ res.end(JSON.stringify({ error: 'Invalid settings object' }));
193
+ return;
194
+ }
195
+ this.db.setSettings(settings);
196
+ res.statusCode = 200;
197
+ res.end(JSON.stringify({ status: 'saved', count: Object.keys(settings).length }));
198
+ }
199
+ // ============ AUDIT HISTORY ENDPOINTS ============
200
+ async handleGetAudits(url, res) {
201
+ const limit = parseInt(url.searchParams.get('limit') || '50', 10);
202
+ const offset = parseInt(url.searchParams.get('offset') || '0', 10);
203
+ const type = url.searchParams.get('type') || undefined;
204
+ const audits = this.db.getAudits(limit, offset, type);
205
+ const total = this.db.getAuditCount(type);
206
+ // Return without full data for list view (lighter response)
207
+ const auditList = audits.map(a => ({
208
+ id: a.id,
209
+ type: a.type,
210
+ timestamp: a.timestamp,
211
+ target: a.target,
212
+ summary: a.summary
213
+ }));
214
+ res.statusCode = 200;
215
+ res.end(JSON.stringify({ audits: auditList, total, limit, offset }));
216
+ }
217
+ async handleGetAudit(id, res) {
218
+ const audit = this.db.getAudit(id);
219
+ if (!audit) {
220
+ res.statusCode = 404;
221
+ res.end(JSON.stringify({ error: 'Audit not found' }));
222
+ return;
223
+ }
224
+ // Parse the stored JSON data
225
+ let data;
226
+ try {
227
+ data = JSON.parse(audit.data);
228
+ }
229
+ catch {
230
+ data = audit.data;
231
+ }
232
+ res.statusCode = 200;
233
+ res.end(JSON.stringify({
234
+ id: audit.id,
235
+ type: audit.type,
236
+ timestamp: audit.timestamp,
237
+ target: audit.target,
238
+ summary: audit.summary,
239
+ data
240
+ }));
241
+ }
242
+ async handleDeleteAudit(id, res) {
243
+ const deleted = this.db.deleteAudit(id);
244
+ if (!deleted) {
245
+ res.statusCode = 404;
246
+ res.end(JSON.stringify({ error: 'Audit not found' }));
247
+ return;
248
+ }
249
+ res.statusCode = 200;
250
+ res.end(JSON.stringify({ status: 'deleted', id }));
251
+ }
252
+ // ============ STATS ENDPOINT ============
253
+ async handleGetStats(res) {
254
+ const stats = this.db.getStats();
255
+ res.statusCode = 200;
256
+ res.end(JSON.stringify(stats));
257
+ }
258
+ // ============ NOTIFICATIONS ENDPOINT ============
259
+ async handleGetNotifications(url, res) {
260
+ const auditId = url.searchParams.get('audit_id') || undefined;
261
+ const limit = parseInt(url.searchParams.get('limit') || '50', 10);
262
+ const notifications = this.db.getNotifications(auditId, limit);
263
+ res.statusCode = 200;
264
+ res.end(JSON.stringify({ notifications }));
265
+ }
266
+ async handleTestNotification(req, res) {
267
+ const body = await this.readBody(req);
268
+ const { channel } = JSON.parse(body);
269
+ if (!channel || !['slack', 'discord', 'webhook'].includes(channel)) {
270
+ res.statusCode = 400;
271
+ res.end(JSON.stringify({ error: 'Invalid channel. Must be: slack, discord, or webhook' }));
272
+ return;
273
+ }
274
+ // Reload settings before testing
275
+ this.notificationService.loadFromDatabase();
276
+ const result = await this.notificationService.testChannel(channel);
277
+ res.statusCode = result.success ? 200 : 400;
278
+ res.end(JSON.stringify(result));
279
+ }
280
+ async handleSendNotification(req, res) {
281
+ const body = await this.readBody(req);
282
+ const { auditId, title, message, severity } = JSON.parse(body);
283
+ // If auditId provided, create notification from audit data
284
+ let payload;
285
+ if (auditId) {
286
+ const audit = this.db.getAudit(auditId);
287
+ if (!audit) {
288
+ res.statusCode = 404;
289
+ res.end(JSON.stringify({ error: 'Audit not found' }));
290
+ return;
291
+ }
292
+ payload = createNotificationFromAudit(audit.id, audit.type, audit.target, audit.summary);
293
+ }
294
+ else {
295
+ // Manual notification
296
+ payload = {
297
+ title: title || 'Manual Notification',
298
+ message: message || 'Test notification from SLOP Auditor',
299
+ severity: severity || 'low'
300
+ };
301
+ }
302
+ // Reload settings and send
303
+ this.notificationService.loadFromDatabase();
304
+ const result = await this.notificationService.notify(payload);
305
+ res.statusCode = 200;
306
+ res.end(JSON.stringify(result));
307
+ }
308
+ readBody(req) {
309
+ return new Promise((resolve, reject) => {
310
+ const chunks = [];
311
+ req.on('data', chunk => chunks.push(chunk));
312
+ req.on('end', () => resolve(Buffer.concat(chunks).toString()));
313
+ req.on('error', reject);
314
+ });
315
+ }
316
+ async start() {
317
+ return new Promise((resolve, reject) => {
318
+ this.server = createServer((req, res) => {
319
+ this.handleRequest(req, res).catch(() => {
320
+ res.statusCode = 500;
321
+ res.end(JSON.stringify({ error: 'Internal error', blocked: true }));
322
+ });
323
+ });
324
+ this.server.on('error', reject);
325
+ this.server.listen(this.config.port, this.config.host, () => {
326
+ resolve();
327
+ });
328
+ });
329
+ }
330
+ async stop() {
331
+ return new Promise((resolve) => {
332
+ if (this.server) {
333
+ this.server.close(() => resolve());
334
+ }
335
+ else {
336
+ resolve();
337
+ }
338
+ });
339
+ }
340
+ getMemorySnapshot() {
341
+ return new Map(this.memory);
342
+ }
343
+ }
@@ -0,0 +1,62 @@
1
+ export type ChangeEventType = 'pull_request' | 'deploy' | 'infra_change';
2
+ export type Environment = 'dev' | 'staging' | 'prod';
3
+ export type RiskTolerance = 'low' | 'medium' | 'high';
4
+ export type Severity = 'low' | 'medium' | 'high' | 'critical';
5
+ export type AgentState = 'idle' | 'analyzing' | 'conflict' | 'escalated' | 'blocked';
6
+ export type EventType = 'analysis_started' | 'finding_raised' | 'conflict_detected' | 'escalation_triggered';
7
+ export type EvidenceType = 'diff' | 'sbom' | 'scan' | 'provenance' | 'runtime';
8
+ export type AssuranceBreak = 'integrity' | 'access_control' | 'isolation' | 'auditability';
9
+ export interface ChangeEvent {
10
+ id: string;
11
+ type: ChangeEventType;
12
+ environment: Environment;
13
+ repo: string;
14
+ commit: string;
15
+ files_changed: string[];
16
+ diff: string;
17
+ }
18
+ export interface EvidenceBundle {
19
+ sbom?: string;
20
+ vuln_scan?: string;
21
+ sast_results?: string;
22
+ iac_scan?: string;
23
+ provenance?: string;
24
+ runtime_delta?: string;
25
+ }
26
+ export interface PolicyContext {
27
+ critical_assets: string[];
28
+ risk_tolerance: RiskTolerance;
29
+ }
30
+ export interface AuditorInput {
31
+ change_event: ChangeEvent;
32
+ evidence_bundle: EvidenceBundle;
33
+ policy_context: PolicyContext;
34
+ }
35
+ export interface EvidenceRef {
36
+ type: EvidenceType;
37
+ pointer: string;
38
+ }
39
+ export interface FindingPayload {
40
+ severity: Severity;
41
+ claim: string;
42
+ attack_path: string[];
43
+ affected_assets: string[];
44
+ evidence_refs: EvidenceRef[];
45
+ assurance_break: AssuranceBreak[];
46
+ confidence: number;
47
+ }
48
+ export interface AuditEvent {
49
+ event_type: EventType;
50
+ target: string;
51
+ payload: FindingPayload;
52
+ timestamp: string;
53
+ }
54
+ export interface AuditorOutput {
55
+ agent_id: string;
56
+ agent_state: AgentState;
57
+ events: AuditEvent[];
58
+ meta: {
59
+ assumptions: string[];
60
+ uncertainties: string[];
61
+ };
62
+ }
@@ -0,0 +1,2 @@
1
+ // Aura Event Types - Strict schema definitions
2
+ export {};
@@ -0,0 +1 @@
1
+ export * from './events.js';
@@ -0,0 +1 @@
1
+ export * from './events.js';
@@ -0,0 +1,4 @@
1
+ import type { AuditorOutput, AgentState } from '../types/events.js';
2
+ export declare function visualize(output: AuditorOutput): string;
3
+ export declare function visualizeState(state: AgentState): string;
4
+ export declare function visualizeCompact(output: AuditorOutput): string;
@@ -0,0 +1,181 @@
1
+ // Aura Visualizer - Renders audit findings to console/terminal
2
+ // Auracraft-style visualization
3
+ const COLORS = {
4
+ reset: '\x1b[0m',
5
+ bright: '\x1b[1m',
6
+ dim: '\x1b[2m',
7
+ red: '\x1b[31m',
8
+ green: '\x1b[32m',
9
+ yellow: '\x1b[33m',
10
+ blue: '\x1b[34m',
11
+ magenta: '\x1b[35m',
12
+ cyan: '\x1b[36m',
13
+ white: '\x1b[37m',
14
+ bgRed: '\x1b[41m',
15
+ bgGreen: '\x1b[42m',
16
+ bgYellow: '\x1b[43m',
17
+ bgBlue: '\x1b[44m'
18
+ };
19
+ const SEVERITY_COLORS = {
20
+ critical: COLORS.bgRed + COLORS.white,
21
+ high: COLORS.red,
22
+ medium: COLORS.yellow,
23
+ low: COLORS.green
24
+ };
25
+ const STATE_ICONS = {
26
+ idle: '○',
27
+ analyzing: '◐',
28
+ conflict: '⚡',
29
+ escalated: '▲',
30
+ blocked: '■'
31
+ };
32
+ const STATE_COLORS = {
33
+ idle: COLORS.dim,
34
+ analyzing: COLORS.cyan,
35
+ conflict: COLORS.yellow,
36
+ escalated: COLORS.magenta,
37
+ blocked: COLORS.red
38
+ };
39
+ export function visualize(output) {
40
+ const lines = [];
41
+ // Header
42
+ lines.push('');
43
+ lines.push(`${COLORS.bright}╔══════════════════════════════════════════════════════════════╗${COLORS.reset}`);
44
+ lines.push(`${COLORS.bright}║ AURASECURITY - SECURITY ANALYSIS REPORT ║${COLORS.reset}`);
45
+ lines.push(`${COLORS.bright}╚══════════════════════════════════════════════════════════════╝${COLORS.reset}`);
46
+ lines.push('');
47
+ // Agent Status
48
+ const stateColor = STATE_COLORS[output.agent_state];
49
+ const stateIcon = STATE_ICONS[output.agent_state];
50
+ lines.push(`${COLORS.bright}AGENT:${COLORS.reset} ${output.agent_id}`);
51
+ lines.push(`${COLORS.bright}STATE:${COLORS.reset} ${stateColor}${stateIcon} ${output.agent_state.toUpperCase()}${COLORS.reset}`);
52
+ lines.push('');
53
+ // Findings Summary
54
+ const criticalCount = output.events.filter(e => e.payload.severity === 'critical').length;
55
+ const highCount = output.events.filter(e => e.payload.severity === 'high').length;
56
+ const mediumCount = output.events.filter(e => e.payload.severity === 'medium').length;
57
+ const lowCount = output.events.filter(e => e.payload.severity === 'low').length;
58
+ lines.push(`${COLORS.bright}FINDINGS SUMMARY:${COLORS.reset}`);
59
+ lines.push(` ${SEVERITY_COLORS.critical} CRITICAL ${COLORS.reset} ${criticalCount}`);
60
+ lines.push(` ${SEVERITY_COLORS.high} HIGH ${COLORS.reset} ${highCount}`);
61
+ lines.push(` ${SEVERITY_COLORS.medium} MEDIUM ${COLORS.reset} ${mediumCount}`);
62
+ lines.push(` ${SEVERITY_COLORS.low} LOW ${COLORS.reset} ${lowCount}`);
63
+ lines.push('');
64
+ // Events Detail
65
+ lines.push(`${COLORS.bright}─────────────────────────────────────────────────────────────────${COLORS.reset}`);
66
+ lines.push(`${COLORS.bright}EVENTS:${COLORS.reset}`);
67
+ lines.push('');
68
+ for (const event of output.events) {
69
+ lines.push(formatEvent(event));
70
+ lines.push('');
71
+ }
72
+ // Meta
73
+ if (output.meta.assumptions.length > 0 || output.meta.uncertainties.length > 0) {
74
+ lines.push(`${COLORS.bright}─────────────────────────────────────────────────────────────────${COLORS.reset}`);
75
+ lines.push(`${COLORS.bright}META:${COLORS.reset}`);
76
+ if (output.meta.assumptions.length > 0) {
77
+ lines.push(` ${COLORS.dim}Assumptions:${COLORS.reset}`);
78
+ for (const a of output.meta.assumptions) {
79
+ lines.push(` • ${a}`);
80
+ }
81
+ }
82
+ if (output.meta.uncertainties.length > 0) {
83
+ lines.push(` ${COLORS.dim}Uncertainties:${COLORS.reset}`);
84
+ for (const u of output.meta.uncertainties) {
85
+ lines.push(` • ${u}`);
86
+ }
87
+ }
88
+ lines.push('');
89
+ }
90
+ // Footer
91
+ lines.push(`${COLORS.bright}═══════════════════════════════════════════════════════════════${COLORS.reset}`);
92
+ return lines.join('\n');
93
+ }
94
+ function formatEvent(event) {
95
+ const lines = [];
96
+ const sevColor = SEVERITY_COLORS[event.payload.severity];
97
+ // Event header
98
+ lines.push(` ${sevColor}[${event.payload.severity.toUpperCase()}]${COLORS.reset} ${COLORS.bright}${event.event_type}${COLORS.reset}`);
99
+ lines.push(` ${COLORS.dim}${event.timestamp}${COLORS.reset}`);
100
+ lines.push('');
101
+ // Claim
102
+ lines.push(` ${COLORS.cyan}CLAIM:${COLORS.reset} ${event.payload.claim}`);
103
+ lines.push(` ${COLORS.cyan}CONFIDENCE:${COLORS.reset} ${(event.payload.confidence * 100).toFixed(0)}%`);
104
+ // Attack path
105
+ if (event.payload.attack_path.length > 0) {
106
+ lines.push(` ${COLORS.cyan}ATTACK PATH:${COLORS.reset}`);
107
+ for (let i = 0; i < event.payload.attack_path.length; i++) {
108
+ const prefix = i === event.payload.attack_path.length - 1 ? '└─' : '├─';
109
+ lines.push(` ${prefix} ${event.payload.attack_path[i]}`);
110
+ }
111
+ }
112
+ // Affected assets
113
+ if (event.payload.affected_assets.length > 0) {
114
+ lines.push(` ${COLORS.cyan}AFFECTED:${COLORS.reset} ${event.payload.affected_assets.join(', ')}`);
115
+ }
116
+ // Assurance breaks
117
+ if (event.payload.assurance_break.length > 0) {
118
+ lines.push(` ${COLORS.cyan}ASSURANCE BREAK:${COLORS.reset} ${event.payload.assurance_break.join(', ')}`);
119
+ }
120
+ // Evidence refs
121
+ if (event.payload.evidence_refs.length > 0) {
122
+ lines.push(` ${COLORS.cyan}EVIDENCE:${COLORS.reset}`);
123
+ for (const ref of event.payload.evidence_refs) {
124
+ lines.push(` • [${ref.type}] ${ref.pointer}`);
125
+ }
126
+ }
127
+ return lines.join('\n');
128
+ }
129
+ // Simple ASCII visualization for state
130
+ export function visualizeState(state) {
131
+ const frames = {
132
+ idle: [
133
+ ' ○ ',
134
+ ' ─── ',
135
+ ' │ │ ',
136
+ ' └───┘ '
137
+ ],
138
+ analyzing: [
139
+ ' ◐ ',
140
+ ' ╱─╲ ',
141
+ ' │ ⚙ │ ',
142
+ ' └───┘ '
143
+ ],
144
+ conflict: [
145
+ ' ⚡⚡ ',
146
+ ' ╱─╲ ',
147
+ ' │ ! │ ',
148
+ ' └───┘ '
149
+ ],
150
+ escalated: [
151
+ ' ▲ ',
152
+ ' ╱!╲ ',
153
+ ' │ ▲ │ ',
154
+ ' └───┘ '
155
+ ],
156
+ blocked: [
157
+ ' ███ ',
158
+ ' █■█ ',
159
+ ' █ ✗ █ ',
160
+ ' ███ '
161
+ ]
162
+ };
163
+ const stateColor = STATE_COLORS[state];
164
+ return frames[state].map(line => `${stateColor}${line}${COLORS.reset}`).join('\n');
165
+ }
166
+ export function visualizeCompact(output) {
167
+ const criticalCount = output.events.filter(e => e.payload.severity === 'critical').length;
168
+ const highCount = output.events.filter(e => e.payload.severity === 'high').length;
169
+ const stateColor = STATE_COLORS[output.agent_state];
170
+ const stateIcon = STATE_ICONS[output.agent_state];
171
+ let statusLine = `${stateColor}${stateIcon}${COLORS.reset} `;
172
+ statusLine += `${output.agent_id} │ `;
173
+ statusLine += `${stateColor}${output.agent_state.toUpperCase()}${COLORS.reset} │ `;
174
+ if (criticalCount > 0) {
175
+ statusLine += `${SEVERITY_COLORS.critical} ${criticalCount} CRIT ${COLORS.reset} `;
176
+ }
177
+ if (highCount > 0) {
178
+ statusLine += `${SEVERITY_COLORS.high} ${highCount} HIGH ${COLORS.reset} `;
179
+ }
180
+ return statusLine;
181
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * WebSocket Server for aurasecurity
3
+ *
4
+ * Provides real-time updates to connected clients:
5
+ * - Audit started/completed events
6
+ * - Finding notifications
7
+ * - Settings changes
8
+ * - Server status updates
9
+ */
10
+ export interface WSMessage {
11
+ type: 'audit_started' | 'audit_completed' | 'finding' | 'settings_changed' | 'status' | 'ping' | 'pong';
12
+ payload: unknown;
13
+ timestamp: string;
14
+ }
15
+ export interface AuditStartedPayload {
16
+ auditId: string;
17
+ type: string;
18
+ target: string;
19
+ }
20
+ export interface AuditCompletedPayload {
21
+ auditId: string;
22
+ type: string;
23
+ target: string;
24
+ summary: {
25
+ critical: number;
26
+ high: number;
27
+ medium: number;
28
+ low: number;
29
+ };
30
+ duration?: number;
31
+ }
32
+ export interface FindingPayload {
33
+ auditId: string;
34
+ severity: 'critical' | 'high' | 'medium' | 'low';
35
+ type: string;
36
+ message: string;
37
+ file?: string;
38
+ line?: number;
39
+ }
40
+ export declare class AuditorWebSocket {
41
+ private port;
42
+ private wss;
43
+ private clients;
44
+ private pingInterval;
45
+ constructor(port?: number);
46
+ /**
47
+ * Start the WebSocket server
48
+ */
49
+ start(): Promise<void>;
50
+ /**
51
+ * Stop the WebSocket server
52
+ */
53
+ stop(): Promise<void>;
54
+ /**
55
+ * Handle incoming messages from clients
56
+ */
57
+ private handleMessage;
58
+ /**
59
+ * Send message to a specific client
60
+ */
61
+ private sendTo;
62
+ /**
63
+ * Broadcast message to all connected clients
64
+ */
65
+ broadcast(message: WSMessage): void;
66
+ /**
67
+ * Notify clients that an audit has started
68
+ */
69
+ notifyAuditStarted(payload: AuditStartedPayload): void;
70
+ /**
71
+ * Notify clients that an audit has completed
72
+ */
73
+ notifyAuditCompleted(payload: AuditCompletedPayload): void;
74
+ /**
75
+ * Notify clients of a finding during an audit
76
+ */
77
+ notifyFinding(payload: FindingPayload): void;
78
+ /**
79
+ * Notify clients that settings have changed
80
+ */
81
+ notifySettingsChanged(section?: string): void;
82
+ /**
83
+ * Get the number of connected clients
84
+ */
85
+ getClientCount(): number;
86
+ }
87
+ export declare function getWebSocketServer(port?: number): AuditorWebSocket;
88
+ export declare function closeWebSocketServer(): Promise<void>;