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.
- package/LICENSE +21 -0
- package/README.md +446 -0
- package/deploy/AWS-DEPLOYMENT.md +358 -0
- package/deploy/terraform/main.tf +362 -0
- package/deploy/terraform/terraform.tfvars.example +6 -0
- package/dist/agents/base.d.ts +44 -0
- package/dist/agents/base.js +96 -0
- package/dist/agents/index.d.ts +14 -0
- package/dist/agents/index.js +17 -0
- package/dist/agents/policy/evaluator.d.ts +15 -0
- package/dist/agents/policy/evaluator.js +183 -0
- package/dist/agents/policy/index.d.ts +12 -0
- package/dist/agents/policy/index.js +15 -0
- package/dist/agents/policy/validator.d.ts +15 -0
- package/dist/agents/policy/validator.js +182 -0
- package/dist/agents/scanners/gitleaks.d.ts +14 -0
- package/dist/agents/scanners/gitleaks.js +155 -0
- package/dist/agents/scanners/grype.d.ts +14 -0
- package/dist/agents/scanners/grype.js +109 -0
- package/dist/agents/scanners/index.d.ts +15 -0
- package/dist/agents/scanners/index.js +27 -0
- package/dist/agents/scanners/npm-audit.d.ts +13 -0
- package/dist/agents/scanners/npm-audit.js +129 -0
- package/dist/agents/scanners/semgrep.d.ts +14 -0
- package/dist/agents/scanners/semgrep.js +131 -0
- package/dist/agents/scanners/trivy.d.ts +14 -0
- package/dist/agents/scanners/trivy.js +122 -0
- package/dist/agents/types.d.ts +137 -0
- package/dist/agents/types.js +91 -0
- package/dist/auditor/index.d.ts +3 -0
- package/dist/auditor/index.js +2 -0
- package/dist/auditor/pipeline.d.ts +19 -0
- package/dist/auditor/pipeline.js +240 -0
- package/dist/auditor/validator.d.ts +17 -0
- package/dist/auditor/validator.js +58 -0
- package/dist/aura/client.d.ts +29 -0
- package/dist/aura/client.js +125 -0
- package/dist/aura/index.d.ts +4 -0
- package/dist/aura/index.js +2 -0
- package/dist/aura/server.d.ts +45 -0
- package/dist/aura/server.js +343 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +1433 -0
- package/dist/client/index.d.ts +41 -0
- package/dist/client/index.js +170 -0
- package/dist/compliance/index.d.ts +40 -0
- package/dist/compliance/index.js +292 -0
- package/dist/database/index.d.ts +77 -0
- package/dist/database/index.js +395 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +762 -0
- package/dist/integrations/aura-scanner.d.ts +69 -0
- package/dist/integrations/aura-scanner.js +155 -0
- package/dist/integrations/aws-scanner.d.ts +63 -0
- package/dist/integrations/aws-scanner.js +624 -0
- package/dist/integrations/config.d.ts +69 -0
- package/dist/integrations/config.js +212 -0
- package/dist/integrations/github.d.ts +45 -0
- package/dist/integrations/github.js +201 -0
- package/dist/integrations/gitlab.d.ts +36 -0
- package/dist/integrations/gitlab.js +110 -0
- package/dist/integrations/index.d.ts +11 -0
- package/dist/integrations/index.js +11 -0
- package/dist/integrations/local-scanner.d.ts +146 -0
- package/dist/integrations/local-scanner.js +1654 -0
- package/dist/integrations/notifications.d.ts +99 -0
- package/dist/integrations/notifications.js +305 -0
- package/dist/integrations/scanners.d.ts +57 -0
- package/dist/integrations/scanners.js +217 -0
- package/dist/integrations/slop-scanner.d.ts +69 -0
- package/dist/integrations/slop-scanner.js +155 -0
- package/dist/integrations/webhook.d.ts +37 -0
- package/dist/integrations/webhook.js +256 -0
- package/dist/orchestrator/index.d.ts +72 -0
- package/dist/orchestrator/index.js +187 -0
- package/dist/output/index.d.ts +152 -0
- package/dist/output/index.js +399 -0
- package/dist/pipeline/index.d.ts +72 -0
- package/dist/pipeline/index.js +313 -0
- package/dist/sbom/index.d.ts +94 -0
- package/dist/sbom/index.js +298 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/input.schema.d.ts +87 -0
- package/dist/schemas/input.schema.js +44 -0
- package/dist/schemas/output.schema.d.ts +115 -0
- package/dist/schemas/output.schema.js +64 -0
- package/dist/serve-visualizer.d.ts +2 -0
- package/dist/serve-visualizer.js +78 -0
- package/dist/slop/client.d.ts +29 -0
- package/dist/slop/client.js +125 -0
- package/dist/slop/index.d.ts +4 -0
- package/dist/slop/index.js +2 -0
- package/dist/slop/server.d.ts +45 -0
- package/dist/slop/server.js +343 -0
- package/dist/types/events.d.ts +62 -0
- package/dist/types/events.js +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/visualizer/index.d.ts +4 -0
- package/dist/visualizer/index.js +181 -0
- package/dist/websocket/index.d.ts +88 -0
- package/dist/websocket/index.js +195 -0
- package/dist/zones/index.d.ts +7 -0
- package/dist/zones/index.js +7 -0
- package/dist/zones/manager.d.ts +101 -0
- package/dist/zones/manager.js +304 -0
- package/dist/zones/types.d.ts +78 -0
- package/dist/zones/types.js +33 -0
- package/package.json +84 -0
- package/visualizer/app.js +0 -0
- package/visualizer/index-minimal.html +1771 -0
- package/visualizer/index.html +2933 -0
- package/visualizer/landing.html +1328 -0
- package/visualizer/styles.css +0 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,762 @@
|
|
|
1
|
+
// aurasecurity - Main entry point
|
|
2
|
+
// No Aura bus = No run (fail-closed)
|
|
3
|
+
import { AuraServer } from './aura/server.js';
|
|
4
|
+
import { AuraClient } from './aura/client.js';
|
|
5
|
+
import { AuditorPipeline } from './auditor/pipeline.js';
|
|
6
|
+
import { SchemaValidator, ValidationError } from './auditor/validator.js';
|
|
7
|
+
import { LocalScanner, scanRemoteGit } from './integrations/local-scanner.js';
|
|
8
|
+
import { auraScan, getAuraState, getAvailableAgents } from './integrations/aura-scanner.js';
|
|
9
|
+
import { getWebSocketServer } from './websocket/index.js';
|
|
10
|
+
const PORT = parseInt(process.env.AURA_PORT ?? '3000', 10);
|
|
11
|
+
const WS_PORT = parseInt(process.env.WS_PORT ?? '3001', 10);
|
|
12
|
+
const AURA_BUS_URL = process.env.AURA_BUS_URL;
|
|
13
|
+
// Secret patterns for remote scanning - stricter patterns to avoid false positives
|
|
14
|
+
const REMOTE_SECRET_PATTERNS = [
|
|
15
|
+
{ name: 'AWS Access Key', regex: /AKIA[0-9A-Z]{16}/g, severity: 'critical' },
|
|
16
|
+
// AWS Secret Key - must have mixed chars (not just ===), and be in quotes or after =
|
|
17
|
+
{ name: 'AWS Secret Key', regex: /(?:secret|aws)[_-]?(?:key|access)?\s*[=:]\s*['"]([A-Za-z0-9\/+]{40})['"]/gi, severity: 'critical' },
|
|
18
|
+
{ name: 'Private Key', regex: /-----BEGIN\s+(RSA|EC|OPENSSH|PGP|ENCRYPTED)?\s*PRIVATE\s+KEY-----/g, severity: 'critical' },
|
|
19
|
+
{ name: 'GitHub Token', regex: /gh[pousr]_[A-Za-z0-9_]{36,}/g, severity: 'critical' },
|
|
20
|
+
{ name: 'GitLab Token', regex: /glpat-[A-Za-z0-9_-]{20,}/g, severity: 'critical' },
|
|
21
|
+
{ name: 'Stripe Key', regex: /sk_live_[A-Za-z0-9]{24,}/g, severity: 'critical' },
|
|
22
|
+
{ name: 'Stripe Test Key', regex: /sk_test_[A-Za-z0-9]{24,}/g, severity: 'medium' },
|
|
23
|
+
{ name: 'Slack Token', regex: /xox[baprs]-[A-Za-z0-9-]{10,}/g, severity: 'high' },
|
|
24
|
+
{ name: 'Database URL', regex: /(mongodb|postgres|mysql|redis):\/\/[^:]+:[^@]+@[^\s"']+/gi, severity: 'critical' },
|
|
25
|
+
{ name: 'JWT Token', regex: /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{20,}/g, severity: 'medium' },
|
|
26
|
+
{ name: 'OpenAI Key', regex: /sk-[A-Za-z0-9]{32,}/g, severity: 'high' },
|
|
27
|
+
{ name: 'API Key Assignment', regex: /api[_-]?key\s*[=:]\s*['"]([A-Za-z0-9_\-]{20,})['"]/gi, severity: 'high' },
|
|
28
|
+
{ name: 'Password Assignment', regex: /password\s*[=:]\s*['"]([^'"]{8,})['"]/gi, severity: 'critical' },
|
|
29
|
+
// Generic secret/key assignment with actual values (not placeholders)
|
|
30
|
+
{ name: 'Secret Assignment', regex: /(?:secret|private)[_-]?key\s*[=:]\s*['"]([A-Fa-f0-9]{32,})['"]/gi, severity: 'critical' },
|
|
31
|
+
{ name: 'Hex Private Key', regex: /(?:private[_-]?key|priv[_-]?key)\s*[=:]\s*['"]?([A-Fa-f0-9]{64})['"]?/gi, severity: 'critical' },
|
|
32
|
+
];
|
|
33
|
+
// Scan a remote Git repo via API without cloning
|
|
34
|
+
async function scanRemoteGitRepo(gitUrl) {
|
|
35
|
+
// Parse GitHub URL: https://github.com/owner/repo
|
|
36
|
+
const githubMatch = gitUrl.match(/github\.com\/([^\/]+)\/([^\/]+)/);
|
|
37
|
+
const gitlabMatch = gitUrl.match(/gitlab\.com\/([^\/]+)\/([^\/]+)/);
|
|
38
|
+
if (!githubMatch && !gitlabMatch) {
|
|
39
|
+
throw new Error('Only GitHub and GitLab URLs are supported for remote scanning');
|
|
40
|
+
}
|
|
41
|
+
const isGitHub = !!githubMatch;
|
|
42
|
+
const owner = (githubMatch || gitlabMatch)[1];
|
|
43
|
+
const repo = (githubMatch || gitlabMatch)[2].replace(/\.git$/, '');
|
|
44
|
+
console.log(`[AURA] Fetching ${isGitHub ? 'GitHub' : 'GitLab'} repo: ${owner}/${repo}`);
|
|
45
|
+
const secrets = [];
|
|
46
|
+
const discoveredServices = [];
|
|
47
|
+
const discoveredModules = [];
|
|
48
|
+
const scannedFiles = [];
|
|
49
|
+
try {
|
|
50
|
+
// Fetch repo tree
|
|
51
|
+
let treeUrl;
|
|
52
|
+
let headers = { 'User-Agent': 'Aura-Security' };
|
|
53
|
+
if (isGitHub) {
|
|
54
|
+
treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/HEAD?recursive=1`;
|
|
55
|
+
if (process.env.GITHUB_TOKEN) {
|
|
56
|
+
headers['Authorization'] = `token ${process.env.GITHUB_TOKEN}`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
treeUrl = `https://gitlab.com/api/v4/projects/${encodeURIComponent(`${owner}/${repo}`)}/repository/tree?recursive=true&per_page=100`;
|
|
61
|
+
if (process.env.GITLAB_TOKEN) {
|
|
62
|
+
headers['PRIVATE-TOKEN'] = process.env.GITLAB_TOKEN;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const treeRes = await fetch(treeUrl, { headers });
|
|
66
|
+
if (!treeRes.ok) {
|
|
67
|
+
throw new Error(`Failed to fetch repo tree: ${treeRes.status} ${treeRes.statusText}`);
|
|
68
|
+
}
|
|
69
|
+
const treeData = await treeRes.json();
|
|
70
|
+
// Get file list
|
|
71
|
+
let files;
|
|
72
|
+
if (isGitHub) {
|
|
73
|
+
const ghData = treeData;
|
|
74
|
+
files = ghData.tree?.filter(f => f.type === 'blob') || [];
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
files = treeData.filter(f => f.type === 'blob');
|
|
78
|
+
}
|
|
79
|
+
console.log(`[AURA] Found ${files.length} files in repo`);
|
|
80
|
+
// Filter to scannable files
|
|
81
|
+
const scanExtensions = ['.js', '.ts', '.jsx', '.tsx', '.json', '.yaml', '.yml', '.env', '.py', '.go', '.rb', '.php', '.java', '.cs', '.config', '.conf', '.sh', '.bash'];
|
|
82
|
+
const scanFiles = files.filter(f => {
|
|
83
|
+
const ext = f.path.substring(f.path.lastIndexOf('.')).toLowerCase();
|
|
84
|
+
const name = f.path.split('/').pop() || '';
|
|
85
|
+
return scanExtensions.includes(ext) || name.startsWith('.env') || name === 'package.json' || name === 'Dockerfile';
|
|
86
|
+
}).slice(0, 100); // Limit to 100 files for API rate limits
|
|
87
|
+
console.log(`[AURA] Scanning ${scanFiles.length} relevant files`);
|
|
88
|
+
// Scan each file
|
|
89
|
+
for (const file of scanFiles) {
|
|
90
|
+
try {
|
|
91
|
+
let contentUrl;
|
|
92
|
+
if (isGitHub) {
|
|
93
|
+
contentUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${encodeURIComponent(file.path)}`;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
contentUrl = `https://gitlab.com/api/v4/projects/${encodeURIComponent(`${owner}/${repo}`)}/repository/files/${encodeURIComponent(file.path)}/raw?ref=HEAD`;
|
|
97
|
+
}
|
|
98
|
+
const contentRes = await fetch(contentUrl, { headers });
|
|
99
|
+
if (!contentRes.ok)
|
|
100
|
+
continue;
|
|
101
|
+
let content;
|
|
102
|
+
if (isGitHub) {
|
|
103
|
+
const contentData = await contentRes.json();
|
|
104
|
+
if (contentData.content && contentData.encoding === 'base64') {
|
|
105
|
+
content = Buffer.from(contentData.content, 'base64').toString('utf-8');
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
content = await contentRes.text();
|
|
113
|
+
}
|
|
114
|
+
scannedFiles.push(file.path);
|
|
115
|
+
// Scan for secrets
|
|
116
|
+
const lines = content.split('\n');
|
|
117
|
+
for (let i = 0; i < lines.length; i++) {
|
|
118
|
+
const line = lines[i];
|
|
119
|
+
for (const pattern of REMOTE_SECRET_PATTERNS) {
|
|
120
|
+
pattern.regex.lastIndex = 0;
|
|
121
|
+
if (pattern.regex.test(line)) {
|
|
122
|
+
secrets.push({
|
|
123
|
+
file: file.path,
|
|
124
|
+
line: i + 1,
|
|
125
|
+
type: pattern.name,
|
|
126
|
+
severity: pattern.severity
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Check for service usage
|
|
132
|
+
if (content.includes('mongodb') || content.includes('mongoose')) {
|
|
133
|
+
if (!discoveredServices.find(s => s.id === 'mongodb')) {
|
|
134
|
+
discoveredServices.push({ id: 'mongodb', name: 'MongoDB', type: 'database', source: file.path, severity: 'high' });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (content.includes('postgres') || content.includes('pg.')) {
|
|
138
|
+
if (!discoveredServices.find(s => s.id === 'postgres')) {
|
|
139
|
+
discoveredServices.push({ id: 'postgres', name: 'PostgreSQL', type: 'database', source: file.path, severity: 'high' });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (content.includes('redis')) {
|
|
143
|
+
if (!discoveredServices.find(s => s.id === 'redis')) {
|
|
144
|
+
discoveredServices.push({ id: 'redis', name: 'Redis', type: 'cache', source: file.path, severity: 'medium' });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (content.includes('stripe')) {
|
|
148
|
+
if (!discoveredServices.find(s => s.id === 'stripe')) {
|
|
149
|
+
discoveredServices.push({ id: 'stripe', name: 'Stripe', type: 'api', source: file.path, severity: 'critical' });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (content.includes('aws-sdk') || content.includes('AWS')) {
|
|
153
|
+
if (!discoveredServices.find(s => s.id === 'aws')) {
|
|
154
|
+
discoveredServices.push({ id: 'aws', name: 'AWS', type: 'cloud', source: file.path, severity: 'critical' });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (content.includes('firebase')) {
|
|
158
|
+
if (!discoveredServices.find(s => s.id === 'firebase')) {
|
|
159
|
+
discoveredServices.push({ id: 'firebase', name: 'Firebase', type: 'cloud', source: file.path, severity: 'high' });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (content.includes('openai')) {
|
|
163
|
+
if (!discoveredServices.find(s => s.id === 'openai')) {
|
|
164
|
+
discoveredServices.push({ id: 'openai', name: 'OpenAI', type: 'api', source: file.path, severity: 'high' });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// Skip files that can't be fetched
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Detect modules from directory structure
|
|
173
|
+
const dirs = new Set();
|
|
174
|
+
files.forEach(f => {
|
|
175
|
+
const parts = f.path.split('/');
|
|
176
|
+
if (parts.length > 1) {
|
|
177
|
+
dirs.add(parts[0]);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
const modulePatterns = {
|
|
181
|
+
'src': { type: 'source', name: 'Source' },
|
|
182
|
+
'lib': { type: 'lib', name: 'Library' },
|
|
183
|
+
'components': { type: 'component', name: 'Components' },
|
|
184
|
+
'pages': { type: 'component', name: 'Pages' },
|
|
185
|
+
'api': { type: 'api', name: 'API' },
|
|
186
|
+
'services': { type: 'service', name: 'Services' },
|
|
187
|
+
'utils': { type: 'lib', name: 'Utils' },
|
|
188
|
+
'config': { type: 'config', name: 'Config' },
|
|
189
|
+
'tests': { type: 'test', name: 'Tests' },
|
|
190
|
+
'test': { type: 'test', name: 'Tests' },
|
|
191
|
+
'__tests__': { type: 'test', name: 'Tests' },
|
|
192
|
+
};
|
|
193
|
+
dirs.forEach(dir => {
|
|
194
|
+
const match = modulePatterns[dir.toLowerCase()];
|
|
195
|
+
if (match) {
|
|
196
|
+
const dirFiles = files.filter(f => f.path.startsWith(dir + '/')).map(f => f.path);
|
|
197
|
+
discoveredModules.push({
|
|
198
|
+
id: dir.toLowerCase(),
|
|
199
|
+
name: match.name,
|
|
200
|
+
type: match.type,
|
|
201
|
+
fileCount: dirFiles.length,
|
|
202
|
+
path: dir,
|
|
203
|
+
files: dirFiles.slice(0, 10)
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
// Build result
|
|
208
|
+
const hasCritical = secrets.some(s => s.severity === 'critical');
|
|
209
|
+
const hasHigh = secrets.some(s => s.severity === 'high');
|
|
210
|
+
const events = secrets.map(s => ({
|
|
211
|
+
event_type: s.severity === 'critical' ? 'escalation_triggered' : 'finding_raised',
|
|
212
|
+
target: 'self',
|
|
213
|
+
payload: {
|
|
214
|
+
severity: s.severity,
|
|
215
|
+
claim: `${s.type} found in ${s.file}:${s.line}`,
|
|
216
|
+
attack_path: ['Secret detected in source code', 'Exposed in public repository', 'Attacker extracts credentials'],
|
|
217
|
+
affected_assets: ['secrets'],
|
|
218
|
+
evidence_refs: [{ type: 'diff', pointer: `${s.file}:${s.line}` }],
|
|
219
|
+
assurance_break: ['integrity', 'access_control'],
|
|
220
|
+
confidence: 0.9
|
|
221
|
+
},
|
|
222
|
+
timestamp: new Date().toISOString()
|
|
223
|
+
}));
|
|
224
|
+
console.log(`[AURA] Remote scan complete. Found ${secrets.length} secrets, ${discoveredServices.length} services, ${discoveredModules.length} modules`);
|
|
225
|
+
return {
|
|
226
|
+
agent_id: 'exploit-reviewer',
|
|
227
|
+
agent_state: hasCritical ? 'escalated' : hasHigh ? 'conflict' : 'idle',
|
|
228
|
+
events,
|
|
229
|
+
meta: { assumptions: [], uncertainties: [] },
|
|
230
|
+
scan_details: {
|
|
231
|
+
path: `${isGitHub ? 'github' : 'gitlab'}:${owner}/${repo}`,
|
|
232
|
+
secrets_found: secrets.length,
|
|
233
|
+
packages_scanned: 0,
|
|
234
|
+
env_files: 0,
|
|
235
|
+
services_discovered: discoveredServices.length,
|
|
236
|
+
modules_discovered: discoveredModules.length,
|
|
237
|
+
git_info: { branch: 'HEAD', remoteUrl: gitUrl },
|
|
238
|
+
system_info: { platform: 'remote', hostname: isGitHub ? 'github.com' : 'gitlab.com' },
|
|
239
|
+
raw_findings: { secrets, packages: [], envFiles: [] },
|
|
240
|
+
discovered_services: discoveredServices,
|
|
241
|
+
discovered_modules: discoveredModules,
|
|
242
|
+
files_scanned: scannedFiles.length
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
console.error('[AURA] Remote scan error:', err);
|
|
248
|
+
throw err;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async function main() {
|
|
252
|
+
console.log('[AURA] Starting auditor pipeline...');
|
|
253
|
+
// Create Aura server
|
|
254
|
+
const server = new AuraServer({ port: PORT });
|
|
255
|
+
// Create Aura client for publishing to bus (if configured)
|
|
256
|
+
let busClient = null;
|
|
257
|
+
if (AURA_BUS_URL) {
|
|
258
|
+
busClient = new AuraClient({ baseUrl: AURA_BUS_URL });
|
|
259
|
+
try {
|
|
260
|
+
await busClient.connect();
|
|
261
|
+
console.log(`[AURA] Connected to bus at ${AURA_BUS_URL}`);
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
console.error('[AURA] FATAL: Cannot connect to Aura bus - fail-closed');
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
// Self-contained mode: client publishes to own server
|
|
270
|
+
busClient = new AuraClient({ baseUrl: `http://127.0.0.1:${PORT}` });
|
|
271
|
+
}
|
|
272
|
+
// Create pipeline
|
|
273
|
+
const pipeline = new AuditorPipeline({ auraClient: busClient });
|
|
274
|
+
const validator = new SchemaValidator();
|
|
275
|
+
// Register auditor tool
|
|
276
|
+
server.registerTool({
|
|
277
|
+
name: 'audit',
|
|
278
|
+
description: 'Analyze change event for security findings',
|
|
279
|
+
parameters: {
|
|
280
|
+
type: 'object',
|
|
281
|
+
required: ['change_event', 'evidence_bundle', 'policy_context']
|
|
282
|
+
},
|
|
283
|
+
handler: async (args) => {
|
|
284
|
+
try {
|
|
285
|
+
return await pipeline.analyze(args);
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
if (err instanceof ValidationError) {
|
|
289
|
+
return {
|
|
290
|
+
agent_id: 'exploit-reviewer',
|
|
291
|
+
agent_state: 'blocked',
|
|
292
|
+
events: [{
|
|
293
|
+
event_type: 'escalation_triggered',
|
|
294
|
+
target: 'self',
|
|
295
|
+
payload: {
|
|
296
|
+
severity: 'critical',
|
|
297
|
+
claim: 'Input validation failed - blocking execution',
|
|
298
|
+
attack_path: ['Malformed input received', 'Validation rejected payload'],
|
|
299
|
+
affected_assets: [],
|
|
300
|
+
evidence_refs: [],
|
|
301
|
+
assurance_break: ['integrity'],
|
|
302
|
+
confidence: 1.0
|
|
303
|
+
},
|
|
304
|
+
timestamp: new Date().toISOString()
|
|
305
|
+
}],
|
|
306
|
+
meta: {
|
|
307
|
+
assumptions: [],
|
|
308
|
+
uncertainties: [],
|
|
309
|
+
validation_errors: err.errors
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
throw err;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
// Register local scan tool
|
|
318
|
+
server.registerTool({
|
|
319
|
+
name: 'scan-local',
|
|
320
|
+
description: 'Scan local filesystem or Git repo for security issues (secrets, vulnerabilities, env files)',
|
|
321
|
+
parameters: {
|
|
322
|
+
type: 'object',
|
|
323
|
+
properties: {
|
|
324
|
+
targetPath: { type: 'string', description: 'Path to scan (defaults to current directory)' },
|
|
325
|
+
gitUrl: { type: 'string', description: 'Git URL to clone and scan' },
|
|
326
|
+
scanSecrets: { type: 'boolean', default: true },
|
|
327
|
+
scanPackages: { type: 'boolean', default: true },
|
|
328
|
+
scanEnvFiles: { type: 'boolean', default: true }
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
handler: async (args) => {
|
|
332
|
+
try {
|
|
333
|
+
let targetPath = args.targetPath || process.cwd();
|
|
334
|
+
// Handle Git URL - clone and scan with full tool suite
|
|
335
|
+
if (args.gitUrl) {
|
|
336
|
+
const gitUrl = args.gitUrl;
|
|
337
|
+
console.log(`[AURA] Cloning and scanning remote repo: ${gitUrl}`);
|
|
338
|
+
try {
|
|
339
|
+
// Use the clone-based scanner for full capabilities
|
|
340
|
+
const remoteResult = await scanRemoteGit({
|
|
341
|
+
gitUrl,
|
|
342
|
+
scanSecrets: args.scanSecrets !== false,
|
|
343
|
+
scanPackages: args.scanPackages !== false
|
|
344
|
+
});
|
|
345
|
+
console.log(`[AURA] Remote scan complete in ${remoteResult.cloneDuration + remoteResult.scanDuration}ms`);
|
|
346
|
+
console.log(`[AURA] Found: ${remoteResult.secrets.length} secrets, ${remoteResult.packages.length} vulns`);
|
|
347
|
+
// Convert to audit input and run through pipeline
|
|
348
|
+
const scanner = new LocalScanner({ targetPath: remoteResult.path });
|
|
349
|
+
const auditInput = scanner.toAuditorInput(remoteResult);
|
|
350
|
+
let auditResult;
|
|
351
|
+
try {
|
|
352
|
+
auditResult = await pipeline.analyze(auditInput);
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
auditResult = {
|
|
356
|
+
agent_id: 'exploit-reviewer',
|
|
357
|
+
agent_state: remoteResult.secrets.length > 0 ? 'conflict' : 'aligned',
|
|
358
|
+
events: [],
|
|
359
|
+
meta: { assumptions: [], uncertainties: [] }
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
// Build full response with scan details
|
|
363
|
+
return {
|
|
364
|
+
...auditResult,
|
|
365
|
+
scan_details: {
|
|
366
|
+
path: remoteResult.gitUrl,
|
|
367
|
+
secrets_found: remoteResult.secrets.length,
|
|
368
|
+
packages_scanned: remoteResult.packages.length,
|
|
369
|
+
package_vulns: remoteResult.packages.length,
|
|
370
|
+
sast_findings: remoteResult.sastFindings.length,
|
|
371
|
+
iac_findings: remoteResult.iacFindings.length,
|
|
372
|
+
dockerfile_findings: remoteResult.dockerfileFindings.length,
|
|
373
|
+
env_files: remoteResult.envFiles.length,
|
|
374
|
+
services_discovered: remoteResult.discoveredServices.length,
|
|
375
|
+
modules_discovered: remoteResult.discoveredModules.length,
|
|
376
|
+
git_info: remoteResult.gitInfo,
|
|
377
|
+
system_info: { platform: 'remote', hostname: 'git-clone' },
|
|
378
|
+
tools_used: remoteResult.toolsUsed,
|
|
379
|
+
languages_detected: remoteResult.languagesDetected,
|
|
380
|
+
clone_duration_ms: remoteResult.cloneDuration,
|
|
381
|
+
scan_duration_ms: remoteResult.scanDuration,
|
|
382
|
+
raw_findings: {
|
|
383
|
+
secrets: remoteResult.secrets,
|
|
384
|
+
packages: remoteResult.packages,
|
|
385
|
+
sastFindings: remoteResult.sastFindings,
|
|
386
|
+
iacFindings: remoteResult.iacFindings,
|
|
387
|
+
dockerfileFindings: remoteResult.dockerfileFindings
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
catch (gitErr) {
|
|
393
|
+
console.error(`[AURA] Remote Git scan failed:`, gitErr);
|
|
394
|
+
return {
|
|
395
|
+
agent_id: 'exploit-reviewer',
|
|
396
|
+
agent_state: 'blocked',
|
|
397
|
+
events: [],
|
|
398
|
+
meta: { assumptions: [], uncertainties: [] },
|
|
399
|
+
error: `Remote scan failed: ${gitErr instanceof Error ? gitErr.message : 'Unknown error'}`
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const scanner = new LocalScanner({
|
|
404
|
+
targetPath,
|
|
405
|
+
scanSecrets: args.scanSecrets !== false,
|
|
406
|
+
scanPackages: args.scanPackages !== false,
|
|
407
|
+
scanEnvFiles: args.scanEnvFiles !== false
|
|
408
|
+
});
|
|
409
|
+
console.log(`[AURA] Starting local scan of: ${targetPath}`);
|
|
410
|
+
// Notify WebSocket clients that scan is starting
|
|
411
|
+
const wsScanId = `scan-${Date.now()}`;
|
|
412
|
+
const ws = getWebSocketServer(WS_PORT);
|
|
413
|
+
ws.notifyAuditStarted({
|
|
414
|
+
auditId: wsScanId,
|
|
415
|
+
type: 'code',
|
|
416
|
+
target: targetPath
|
|
417
|
+
});
|
|
418
|
+
const scanResult = await scanner.scan();
|
|
419
|
+
console.log(`[AURA] Scan complete. Found ${scanResult.secrets.length} secrets, ${scanResult.packages.length} package issues, ${scanResult.sastFindings.length} SAST findings`);
|
|
420
|
+
// Convert to audit input and run through pipeline
|
|
421
|
+
const auditInput = scanner.toAuditorInput(scanResult);
|
|
422
|
+
let auditResult;
|
|
423
|
+
try {
|
|
424
|
+
auditResult = await pipeline.analyze(auditInput);
|
|
425
|
+
}
|
|
426
|
+
catch (pipelineErr) {
|
|
427
|
+
// If pipeline fails, create a basic result from scan data
|
|
428
|
+
console.log(`[AURA] Pipeline analysis skipped: ${pipelineErr}`);
|
|
429
|
+
const events = scanResult.secrets.map(s => ({
|
|
430
|
+
event_type: s.severity === 'critical' ? 'escalation_triggered' : 'finding_raised',
|
|
431
|
+
target: 'self',
|
|
432
|
+
payload: {
|
|
433
|
+
severity: s.severity,
|
|
434
|
+
claim: `${s.type} found in ${s.file}:${s.line}`,
|
|
435
|
+
attack_path: ['Secret detected in source code', 'Exposed in repository', 'Attacker extracts credentials'],
|
|
436
|
+
affected_assets: [s.type.toLowerCase().includes('aws') ? 'infra' : s.type.toLowerCase().includes('password') ? 'auth' : 'secrets'],
|
|
437
|
+
evidence_refs: [{ type: 'diff', pointer: `${s.file}:${s.line}` }],
|
|
438
|
+
assurance_break: ['integrity', 'access_control'],
|
|
439
|
+
confidence: 0.9
|
|
440
|
+
},
|
|
441
|
+
timestamp: new Date().toISOString()
|
|
442
|
+
}));
|
|
443
|
+
auditResult = {
|
|
444
|
+
agent_id: 'exploit-reviewer',
|
|
445
|
+
agent_state: scanResult.secrets.some(s => s.severity === 'critical') ? 'escalated' :
|
|
446
|
+
scanResult.secrets.length > 0 ? 'conflict' : 'idle',
|
|
447
|
+
events,
|
|
448
|
+
meta: { assumptions: [], uncertainties: [] }
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
// Include raw scan results in response
|
|
452
|
+
const scanId = `local-scan-${Date.now()}`;
|
|
453
|
+
const fullResult = {
|
|
454
|
+
...auditResult,
|
|
455
|
+
scan_details: {
|
|
456
|
+
path: scanResult.path,
|
|
457
|
+
secrets_found: scanResult.secrets.length,
|
|
458
|
+
packages_scanned: scanResult.packages.length,
|
|
459
|
+
package_vulns: scanResult.packages.filter(p => p.vulnerabilities > 0).length,
|
|
460
|
+
sast_findings: scanResult.sastFindings.length,
|
|
461
|
+
env_files: scanResult.envFiles.length,
|
|
462
|
+
services_discovered: scanResult.discoveredServices.length,
|
|
463
|
+
modules_discovered: scanResult.discoveredModules.length,
|
|
464
|
+
git_info: scanResult.gitInfo,
|
|
465
|
+
system_info: scanResult.systemInfo,
|
|
466
|
+
tools_used: scanResult.toolsUsed,
|
|
467
|
+
raw_findings: {
|
|
468
|
+
secrets: scanResult.secrets,
|
|
469
|
+
packages: scanResult.packages,
|
|
470
|
+
envFiles: scanResult.envFiles,
|
|
471
|
+
sastFindings: scanResult.sastFindings
|
|
472
|
+
},
|
|
473
|
+
// Discovered services for dynamic map building
|
|
474
|
+
discovered_services: scanResult.discoveredServices,
|
|
475
|
+
// Discovered code modules/directories for codebase mapping
|
|
476
|
+
discovered_modules: scanResult.discoveredModules
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
// Store scan result to memory for history browsing
|
|
480
|
+
try {
|
|
481
|
+
await busClient.publishToMemory({
|
|
482
|
+
key: `audit:${scanId}:${Date.now()}`,
|
|
483
|
+
value: fullResult,
|
|
484
|
+
metadata: {
|
|
485
|
+
type: 'local-scan',
|
|
486
|
+
path: scanResult.path,
|
|
487
|
+
timestamp: new Date().toISOString()
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
console.log(`[AURA] Scan result stored to memory: ${scanId}`);
|
|
491
|
+
}
|
|
492
|
+
catch (storeErr) {
|
|
493
|
+
console.error('[AURA] Failed to store scan result to memory:', storeErr);
|
|
494
|
+
}
|
|
495
|
+
// Store to SQLite database for persistent history
|
|
496
|
+
let auditId;
|
|
497
|
+
try {
|
|
498
|
+
const db = server.getDatabase();
|
|
499
|
+
auditId = db.saveAudit('code', scanResult.path, scanResult);
|
|
500
|
+
console.log(`[AURA] Scan result saved to database: ${auditId}`);
|
|
501
|
+
}
|
|
502
|
+
catch (dbErr) {
|
|
503
|
+
console.error('[AURA] Failed to save to database:', dbErr);
|
|
504
|
+
}
|
|
505
|
+
// Send notifications if enabled
|
|
506
|
+
if (auditId) {
|
|
507
|
+
try {
|
|
508
|
+
const notifyService = server.getNotificationService();
|
|
509
|
+
const summary = {
|
|
510
|
+
critical: scanResult.secrets?.filter((s) => s.severity === 'critical').length || 0,
|
|
511
|
+
high: scanResult.secrets?.filter((s) => s.severity === 'high').length || 0,
|
|
512
|
+
medium: (scanResult.packages?.length || 0) + (scanResult.sastFindings?.length || 0),
|
|
513
|
+
low: scanResult.envFiles?.length || 0
|
|
514
|
+
};
|
|
515
|
+
const result = await notifyService.notify({
|
|
516
|
+
title: `Security Scan Complete`,
|
|
517
|
+
message: `Scanned \`${scanResult.path}\``,
|
|
518
|
+
severity: summary.critical > 0 ? 'critical' : summary.high > 0 ? 'high' : 'low',
|
|
519
|
+
auditId,
|
|
520
|
+
target: scanResult.path,
|
|
521
|
+
findings: summary
|
|
522
|
+
});
|
|
523
|
+
if (result.sent.length > 0) {
|
|
524
|
+
console.log(`[AURA] Notifications sent: ${result.sent.join(', ')}`);
|
|
525
|
+
}
|
|
526
|
+
// Notify WebSocket clients that scan is complete
|
|
527
|
+
ws.notifyAuditCompleted({
|
|
528
|
+
auditId,
|
|
529
|
+
type: 'code',
|
|
530
|
+
target: scanResult.path,
|
|
531
|
+
summary
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
catch (notifyErr) {
|
|
535
|
+
console.error('[AURA] Notification error:', notifyErr);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return fullResult;
|
|
539
|
+
}
|
|
540
|
+
catch (err) {
|
|
541
|
+
console.error('[AURA] Local scan error:', err);
|
|
542
|
+
return {
|
|
543
|
+
agent_id: 'exploit-reviewer',
|
|
544
|
+
agent_state: 'blocked',
|
|
545
|
+
events: [{
|
|
546
|
+
event_type: 'escalation_triggered',
|
|
547
|
+
target: 'self',
|
|
548
|
+
payload: {
|
|
549
|
+
severity: 'medium',
|
|
550
|
+
claim: `Local scan failed: ${err instanceof Error ? err.message : 'Unknown error'}`,
|
|
551
|
+
attack_path: ['Scan execution failed'],
|
|
552
|
+
affected_assets: [],
|
|
553
|
+
evidence_refs: [],
|
|
554
|
+
assurance_break: [],
|
|
555
|
+
confidence: 1.0
|
|
556
|
+
},
|
|
557
|
+
timestamp: new Date().toISOString()
|
|
558
|
+
}],
|
|
559
|
+
meta: { assumptions: [], uncertainties: [`Scan error: ${err}`] }
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
// Register Aura Protocol scan tool (multi-agent architecture)
|
|
565
|
+
server.registerTool({
|
|
566
|
+
name: 'scan-aura',
|
|
567
|
+
description: 'Aura Protocol scan - Multi-agent parallel security scanning with isolated zones',
|
|
568
|
+
parameters: {
|
|
569
|
+
type: 'object',
|
|
570
|
+
properties: {
|
|
571
|
+
targetPath: { type: 'string', description: 'Path to scan (defaults to current directory)' },
|
|
572
|
+
fullScan: { type: 'boolean', default: true, description: 'Run policy evaluation (slower, fewer false positives)' }
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
handler: async (args) => {
|
|
576
|
+
try {
|
|
577
|
+
const targetPath = args.targetPath || process.cwd();
|
|
578
|
+
const fullScan = args.fullScan !== false;
|
|
579
|
+
console.log(`[AURA] Starting Aura Protocol scan of: ${targetPath}`);
|
|
580
|
+
console.log(`[AURA] Mode: ${fullScan ? 'Full (with policy evaluation)' : 'Quick (scanner only)'}`);
|
|
581
|
+
// Notify WebSocket clients that scan is starting
|
|
582
|
+
const wsScanId = `aura-scan-${Date.now()}`;
|
|
583
|
+
const ws = getWebSocketServer(WS_PORT);
|
|
584
|
+
ws.notifyAuditStarted({
|
|
585
|
+
auditId: wsScanId,
|
|
586
|
+
type: 'aura-protocol',
|
|
587
|
+
target: targetPath
|
|
588
|
+
});
|
|
589
|
+
// Run Aura Protocol scan
|
|
590
|
+
const result = await auraScan({
|
|
591
|
+
targetPath,
|
|
592
|
+
fullScan
|
|
593
|
+
});
|
|
594
|
+
console.log(`[AURA] Aura Protocol scan complete`);
|
|
595
|
+
console.log(`[AURA] Zones executed: ${result.aura.zones.map(z => z.name).join(', ')}`);
|
|
596
|
+
console.log(`[AURA] Agents used: ${result.aura.agents.filter(a => a.status === 'success').map(a => a.name).join(', ')}`);
|
|
597
|
+
console.log(`[AURA] Findings: ${result.aura.summary.totalFindings}`);
|
|
598
|
+
// Store in database
|
|
599
|
+
let auditId;
|
|
600
|
+
try {
|
|
601
|
+
const db = server.getDatabase();
|
|
602
|
+
auditId = db.saveAudit('code', targetPath, {
|
|
603
|
+
path: targetPath,
|
|
604
|
+
timestamp: new Date().toISOString(),
|
|
605
|
+
secrets: result.legacy.secrets,
|
|
606
|
+
packages: result.legacy.packages,
|
|
607
|
+
sastFindings: result.legacy.sastFindings,
|
|
608
|
+
iacFindings: [],
|
|
609
|
+
dockerfileFindings: [],
|
|
610
|
+
gitInfo: null,
|
|
611
|
+
envFiles: [],
|
|
612
|
+
systemInfo: result.legacy.systemInfo,
|
|
613
|
+
discoveredServices: [],
|
|
614
|
+
discoveredModules: [],
|
|
615
|
+
toolsUsed: result.aura.summary.agentsUsed,
|
|
616
|
+
languagesDetected: [],
|
|
617
|
+
zones: result.aura.zones,
|
|
618
|
+
agents: result.aura.agents
|
|
619
|
+
});
|
|
620
|
+
console.log(`[AURA] Aura scan saved to database: ${auditId}`);
|
|
621
|
+
}
|
|
622
|
+
catch (dbErr) {
|
|
623
|
+
console.error('[AURA] Database save error:', dbErr);
|
|
624
|
+
auditId = `aura-${Date.now()}`;
|
|
625
|
+
}
|
|
626
|
+
// Notify WebSocket clients
|
|
627
|
+
ws.notifyAuditCompleted({
|
|
628
|
+
auditId: auditId || `aura-${Date.now()}`,
|
|
629
|
+
type: 'aura-protocol',
|
|
630
|
+
target: targetPath,
|
|
631
|
+
summary: {
|
|
632
|
+
critical: result.aura.summary.bySeverity['critical'] || 0,
|
|
633
|
+
high: result.aura.summary.bySeverity['high'] || 0,
|
|
634
|
+
medium: result.aura.summary.bySeverity['medium'] || 0,
|
|
635
|
+
low: result.aura.summary.bySeverity['low'] || 0
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
return {
|
|
639
|
+
agent_id: 'aura-orchestrator',
|
|
640
|
+
agent_state: result.aura.summary.totalFindings > 0 ? 'conflict' : 'aligned',
|
|
641
|
+
events: result.aura.findings.map(f => ({
|
|
642
|
+
event_type: f.severity === 'critical' ? 'escalation_triggered' : 'finding_raised',
|
|
643
|
+
target: 'self',
|
|
644
|
+
payload: {
|
|
645
|
+
severity: f.severity,
|
|
646
|
+
claim: f.title,
|
|
647
|
+
description: f.description,
|
|
648
|
+
file: f.file,
|
|
649
|
+
line: f.line,
|
|
650
|
+
type: f.type,
|
|
651
|
+
agent: f.agentId
|
|
652
|
+
},
|
|
653
|
+
timestamp: new Date(f.timestamp).toISOString()
|
|
654
|
+
})),
|
|
655
|
+
meta: {
|
|
656
|
+
zones: result.aura.zones,
|
|
657
|
+
agents: result.aura.agents,
|
|
658
|
+
summary: result.aura.summary
|
|
659
|
+
},
|
|
660
|
+
scan_details: {
|
|
661
|
+
path: targetPath,
|
|
662
|
+
mode: fullScan ? 'full' : 'quick',
|
|
663
|
+
secrets_found: result.aura.summary.byType['secret'] || 0,
|
|
664
|
+
vulnerabilities_found: result.aura.summary.byType['vulnerability'] || 0,
|
|
665
|
+
total_findings: result.aura.summary.totalFindings,
|
|
666
|
+
tools_used: result.aura.summary.agentsUsed,
|
|
667
|
+
zones_executed: result.aura.zones.length,
|
|
668
|
+
agents_executed: result.aura.agents.length,
|
|
669
|
+
raw_findings: {
|
|
670
|
+
secrets: result.legacy.secrets,
|
|
671
|
+
packages: result.legacy.packages,
|
|
672
|
+
sastFindings: result.legacy.sastFindings
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
catch (err) {
|
|
678
|
+
console.error('[AURA] Aura Protocol scan error:', err);
|
|
679
|
+
return {
|
|
680
|
+
agent_id: 'aura-orchestrator',
|
|
681
|
+
agent_state: 'blocked',
|
|
682
|
+
events: [],
|
|
683
|
+
meta: { error: err instanceof Error ? err.message : 'Unknown error' }
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
// Register Aura state endpoint (for visualization)
|
|
689
|
+
server.registerTool({
|
|
690
|
+
name: 'aura-state',
|
|
691
|
+
description: 'Get current Aura Protocol state (zones, agents) for visualization',
|
|
692
|
+
parameters: { type: 'object', properties: {} },
|
|
693
|
+
handler: async () => {
|
|
694
|
+
const state = getAuraState();
|
|
695
|
+
const availableAgents = await getAvailableAgents();
|
|
696
|
+
return {
|
|
697
|
+
...state,
|
|
698
|
+
availableAgents: availableAgents.map(a => ({
|
|
699
|
+
id: a.config.id,
|
|
700
|
+
name: a.config.name,
|
|
701
|
+
role: a.config.role,
|
|
702
|
+
description: a.config.description
|
|
703
|
+
}))
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
// Start HTTP server
|
|
708
|
+
await server.start();
|
|
709
|
+
console.log(`[AURA] Auditor listening on http://127.0.0.1:${PORT}`);
|
|
710
|
+
console.log('[AURA] Endpoints: /info, /tools, /memory, /settings, /audits, /stats, /notifications');
|
|
711
|
+
// Start WebSocket server for real-time updates
|
|
712
|
+
const wsServer = getWebSocketServer(WS_PORT);
|
|
713
|
+
await wsServer.start();
|
|
714
|
+
console.log(`[AURA] WebSocket server on ws://127.0.0.1:${WS_PORT}`);
|
|
715
|
+
// Connect client to self for memory storage
|
|
716
|
+
if (!AURA_BUS_URL) {
|
|
717
|
+
await busClient.connect();
|
|
718
|
+
console.log('[AURA] Self-contained mode: client connected to local server');
|
|
719
|
+
}
|
|
720
|
+
// Graceful shutdown
|
|
721
|
+
process.on('SIGINT', async () => {
|
|
722
|
+
console.log('\n[AURA] Shutting down...');
|
|
723
|
+
await server.stop();
|
|
724
|
+
await busClient?.disconnect();
|
|
725
|
+
process.exit(0);
|
|
726
|
+
});
|
|
727
|
+
process.on('SIGTERM', async () => {
|
|
728
|
+
console.log('\n[AURA] Shutting down...');
|
|
729
|
+
await server.stop();
|
|
730
|
+
await busClient?.disconnect();
|
|
731
|
+
process.exit(0);
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
main().catch((err) => {
|
|
735
|
+
console.error('[AURA] FATAL:', err);
|
|
736
|
+
process.exit(1);
|
|
737
|
+
});
|
|
738
|
+
// Export for programmatic use
|
|
739
|
+
export { AuraServer } from './aura/server.js';
|
|
740
|
+
export { AuraClient } from './aura/client.js';
|
|
741
|
+
export { AuditorPipeline } from './auditor/pipeline.js';
|
|
742
|
+
export { SchemaValidator, ValidationError } from './auditor/validator.js';
|
|
743
|
+
export * from './types/events.js';
|
|
744
|
+
// Client SDK exports
|
|
745
|
+
export { AuditClient, createPullRequestEvent, createDeployEvent, createInfraChangeEvent } from './client/index.js';
|
|
746
|
+
// Pipeline framework exports
|
|
747
|
+
export { SecurityPipeline, SecretsDetectionStage, VulnerabilityScanStage, CriticalAssetStage, InfrastructureChangeStage, ProductionDeployStage } from './pipeline/index.js';
|
|
748
|
+
// Integration exports
|
|
749
|
+
export { WebhookServer, defaultHandlers } from './integrations/webhook.js';
|
|
750
|
+
export { GitHubIntegration } from './integrations/github.js';
|
|
751
|
+
export { GitLabIntegration } from './integrations/gitlab.js';
|
|
752
|
+
export { SnykParser, TrivyParser, SemgrepParser, NpmAuditParser, getParser } from './integrations/scanners.js';
|
|
753
|
+
export { ConfigLoader, configLoader } from './integrations/config.js';
|
|
754
|
+
export { LocalScanner, quickLocalScan, scanRemoteGit, isGitUrl } from './integrations/local-scanner.js';
|
|
755
|
+
// AWS Scanner exports
|
|
756
|
+
export { AWSScanner, scanAWS } from './integrations/aws-scanner.js';
|
|
757
|
+
// Database exports
|
|
758
|
+
export { AuditorDatabase, getDatabase, closeDatabase } from './database/index.js';
|
|
759
|
+
// Notification exports
|
|
760
|
+
export { NotificationService, createNotificationFromAudit } from './integrations/notifications.js';
|
|
761
|
+
// WebSocket exports
|
|
762
|
+
export { AuditorWebSocket, getWebSocketServer, closeWebSocketServer } from './websocket/index.js';
|