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,58 @@
1
+ // Schema Validator - Strict validation with fail-closed behavior
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ import Ajv from 'ajv';
4
+ import addFormats from 'ajv-formats';
5
+ import { auditorInputSchema } from '../schemas/input.schema.js';
6
+ import { auditorOutputSchema } from '../schemas/output.schema.js';
7
+ export class ValidationError extends Error {
8
+ errors;
9
+ constructor(message, errors) {
10
+ super(message);
11
+ this.name = 'ValidationError';
12
+ this.errors = errors;
13
+ }
14
+ }
15
+ export class SchemaValidator {
16
+ ajv;
17
+ validateInput;
18
+ validateOutput;
19
+ constructor() {
20
+ // Handle ESM/CJS interop
21
+ const AjvClass = Ajv.default ?? Ajv;
22
+ const addFormatsFunc = addFormats.default ?? addFormats;
23
+ this.ajv = new AjvClass({
24
+ strict: true,
25
+ allErrors: true,
26
+ verbose: true
27
+ });
28
+ addFormatsFunc(this.ajv);
29
+ this.validateInput = this.ajv.compile(auditorInputSchema);
30
+ this.validateOutput = this.ajv.compile(auditorOutputSchema);
31
+ }
32
+ // Fail-closed: throws on invalid input
33
+ assertValidInput(data) {
34
+ if (!this.validateInput(data)) {
35
+ throw new ValidationError('Input validation failed', this.validateInput.errors ?? []);
36
+ }
37
+ }
38
+ // Fail-closed: throws on invalid output
39
+ assertValidOutput(data) {
40
+ if (!this.validateOutput(data)) {
41
+ throw new ValidationError('Output validation failed', this.validateOutput.errors ?? []);
42
+ }
43
+ }
44
+ isValidInput(data) {
45
+ return this.validateInput(data);
46
+ }
47
+ isValidOutput(data) {
48
+ return this.validateOutput(data);
49
+ }
50
+ getInputErrors(data) {
51
+ this.validateInput(data);
52
+ return this.validateInput.errors ?? [];
53
+ }
54
+ getOutputErrors(data) {
55
+ this.validateOutput(data);
56
+ return this.validateOutput.errors ?? [];
57
+ }
58
+ }
@@ -0,0 +1,29 @@
1
+ export interface AuraClientConfig {
2
+ baseUrl: string;
3
+ apiKey?: string;
4
+ timeout?: number;
5
+ }
6
+ export interface AuraToolCall {
7
+ tool: string;
8
+ arguments: Record<string, unknown>;
9
+ }
10
+ export interface AuraMemoryEntry {
11
+ key: string;
12
+ value: unknown;
13
+ metadata?: Record<string, unknown>;
14
+ }
15
+ export declare class AuraConnectionError extends Error {
16
+ constructor(message: string);
17
+ }
18
+ export declare class AuraClient {
19
+ private config;
20
+ private _connected;
21
+ constructor(config: AuraClientConfig);
22
+ get connected(): boolean;
23
+ private headers;
24
+ connect(): Promise<void>;
25
+ disconnect(): Promise<void>;
26
+ publishToMemory(entry: AuraMemoryEntry): Promise<void>;
27
+ callTool(call: AuraToolCall): Promise<unknown>;
28
+ healthCheck(): Promise<boolean>;
29
+ }
@@ -0,0 +1,125 @@
1
+ // Aura Client - Publish-only client for auditor pipeline
2
+ // Implements: /tools, /memory endpoints per Aura spec
3
+ export class AuraConnectionError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = 'AuraConnectionError';
7
+ }
8
+ }
9
+ export class AuraClient {
10
+ config;
11
+ _connected = false;
12
+ constructor(config) {
13
+ this.config = {
14
+ baseUrl: config.baseUrl.replace(/\/$/, ''),
15
+ apiKey: config.apiKey ?? '',
16
+ timeout: config.timeout ?? 30000
17
+ };
18
+ }
19
+ get connected() {
20
+ return this._connected;
21
+ }
22
+ headers() {
23
+ const h = {
24
+ 'Content-Type': 'application/json'
25
+ };
26
+ if (this.config.apiKey) {
27
+ h['Authorization'] = `Bearer ${this.config.apiKey}`;
28
+ }
29
+ return h;
30
+ }
31
+ async connect() {
32
+ // Verify Aura server is available via /info
33
+ const controller = new AbortController();
34
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
35
+ try {
36
+ const res = await fetch(`${this.config.baseUrl}/info`, {
37
+ method: 'GET',
38
+ headers: this.headers(),
39
+ signal: controller.signal
40
+ });
41
+ clearTimeout(timeoutId);
42
+ if (!res.ok) {
43
+ throw new AuraConnectionError(`Aura server returned ${res.status}`);
44
+ }
45
+ this._connected = true;
46
+ }
47
+ catch (err) {
48
+ clearTimeout(timeoutId);
49
+ if (err instanceof AuraConnectionError)
50
+ throw err;
51
+ throw new AuraConnectionError(`Failed to connect to Aura server: ${err}`);
52
+ }
53
+ }
54
+ async disconnect() {
55
+ this._connected = false;
56
+ }
57
+ // Fail-closed: throws on any error
58
+ async publishToMemory(entry) {
59
+ if (!this._connected) {
60
+ throw new AuraConnectionError('Not connected to Aura server');
61
+ }
62
+ const controller = new AbortController();
63
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
64
+ try {
65
+ const res = await fetch(`${this.config.baseUrl}/memory`, {
66
+ method: 'POST',
67
+ headers: this.headers(),
68
+ body: JSON.stringify(entry),
69
+ signal: controller.signal
70
+ });
71
+ clearTimeout(timeoutId);
72
+ if (!res.ok) {
73
+ throw new AuraConnectionError(`Memory publish failed: ${res.status}`);
74
+ }
75
+ }
76
+ catch (err) {
77
+ clearTimeout(timeoutId);
78
+ if (err instanceof AuraConnectionError)
79
+ throw err;
80
+ throw new AuraConnectionError(`Memory publish error: ${err}`);
81
+ }
82
+ }
83
+ async callTool(call) {
84
+ if (!this._connected) {
85
+ throw new AuraConnectionError('Not connected to Aura server');
86
+ }
87
+ const controller = new AbortController();
88
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
89
+ try {
90
+ const res = await fetch(`${this.config.baseUrl}/tools`, {
91
+ method: 'POST',
92
+ headers: this.headers(),
93
+ body: JSON.stringify(call),
94
+ signal: controller.signal
95
+ });
96
+ clearTimeout(timeoutId);
97
+ if (!res.ok) {
98
+ throw new AuraConnectionError(`Tool call failed: ${res.status}`);
99
+ }
100
+ return await res.json();
101
+ }
102
+ catch (err) {
103
+ clearTimeout(timeoutId);
104
+ if (err instanceof AuraConnectionError)
105
+ throw err;
106
+ throw new AuraConnectionError(`Tool call error: ${err}`);
107
+ }
108
+ }
109
+ async healthCheck() {
110
+ try {
111
+ const controller = new AbortController();
112
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
113
+ const res = await fetch(`${this.config.baseUrl}/info`, {
114
+ method: 'GET',
115
+ headers: this.headers(),
116
+ signal: controller.signal
117
+ });
118
+ clearTimeout(timeoutId);
119
+ return res.ok;
120
+ }
121
+ catch {
122
+ return false;
123
+ }
124
+ }
125
+ }
@@ -0,0 +1,4 @@
1
+ export { AuraServer } from './server.js';
2
+ export type { AuraTool, AuraServerConfig } from './server.js';
3
+ export { AuraClient, AuraConnectionError } from './client.js';
4
+ export type { AuraClientConfig, AuraToolCall, AuraMemoryEntry } from './client.js';
@@ -0,0 +1,2 @@
1
+ export { AuraServer } from './server.js';
2
+ export { AuraClient, AuraConnectionError } from './client.js';
@@ -0,0 +1,45 @@
1
+ import { type AuditorDatabase } from '../database/index.js';
2
+ import { NotificationService } from '../integrations/notifications.js';
3
+ export interface AuraTool {
4
+ name: string;
5
+ description: string;
6
+ parameters: Record<string, unknown>;
7
+ handler: (args: Record<string, unknown>) => Promise<unknown>;
8
+ }
9
+ export interface AuraServerConfig {
10
+ port: number;
11
+ host?: string;
12
+ dbPath?: string;
13
+ }
14
+ export declare class AuraServer {
15
+ private server;
16
+ private tools;
17
+ private memory;
18
+ private config;
19
+ private db;
20
+ private notificationService;
21
+ constructor(config: AuraServerConfig);
22
+ getNotificationService(): NotificationService;
23
+ reloadNotifications(): void;
24
+ registerTool(tool: AuraTool): void;
25
+ getDatabase(): AuditorDatabase;
26
+ private handleRequest;
27
+ private handleInfo;
28
+ private handleListTools;
29
+ private handleCallTool;
30
+ private handleMemoryWrite;
31
+ private handleMemoryRead;
32
+ private handleGetSettings;
33
+ private handleSaveSettings;
34
+ private handleGetAudits;
35
+ private handleGetAudit;
36
+ private handleDeleteAudit;
37
+ private handleGetStats;
38
+ private handleGetNotifications;
39
+ private handleTestNotification;
40
+ private handleSendNotification;
41
+ private readBody;
42
+ start(): Promise<void>;
43
+ stop(): Promise<void>;
44
+ getMemorySnapshot(): Map<string, unknown>;
45
+ }
@@ -0,0 +1,343 @@
1
+ // Aura 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 AuraServer {
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 Aura 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: 'aura-security',
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 Aura 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
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aurasecurity CLI
4
+ *
5
+ * A security auditor that can scan local directories, git repos, and more.
6
+ * Works standalone (no server needed) or connected to Aura server.
7
+ *
8
+ * Usage:
9
+ * aura-security scan <path> Scan a local directory for security issues
10
+ * aura-security serve Start the Aura server
11
+ * aura-security visualizer Start the 3D visualizer
12
+ * aura-security status Show server status
13
+ * aura-security audit [file] Run audit via server
14
+ * aura-security logs Show audit log entries
15
+ * aura-security watch Watch for new audits
16
+ */
17
+ export {};