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/cli.js
ADDED
|
@@ -0,0 +1,1433 @@
|
|
|
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
|
+
import { visualize, visualizeState, visualizeCompact } from './visualizer/index.js';
|
|
18
|
+
import { LocalScanner, quickLocalScan } from './integrations/local-scanner.js';
|
|
19
|
+
import { scanAWS } from './integrations/aws-scanner.js';
|
|
20
|
+
import { generateSBOM } from './sbom/index.js';
|
|
21
|
+
import { checkLicenses, POLICY_NAMES } from './compliance/index.js';
|
|
22
|
+
import { toSARIF, toJUnit, toJSONSummary, toGitLabReport } from './output/index.js';
|
|
23
|
+
import { existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
24
|
+
import { join, resolve, basename } from 'path';
|
|
25
|
+
import { spawnSync } from 'child_process';
|
|
26
|
+
const AURA_URL = process.env.AURA_URL ?? 'http://127.0.0.1:3000';
|
|
27
|
+
const VERSION = '0.3.0';
|
|
28
|
+
// ANSI colors for terminal output
|
|
29
|
+
const colors = {
|
|
30
|
+
reset: '\x1b[0m',
|
|
31
|
+
bold: '\x1b[1m',
|
|
32
|
+
dim: '\x1b[2m',
|
|
33
|
+
red: '\x1b[31m',
|
|
34
|
+
green: '\x1b[32m',
|
|
35
|
+
yellow: '\x1b[33m',
|
|
36
|
+
blue: '\x1b[34m',
|
|
37
|
+
magenta: '\x1b[35m',
|
|
38
|
+
cyan: '\x1b[36m',
|
|
39
|
+
white: '\x1b[37m',
|
|
40
|
+
bgRed: '\x1b[41m',
|
|
41
|
+
bgYellow: '\x1b[43m',
|
|
42
|
+
bgGreen: '\x1b[42m',
|
|
43
|
+
};
|
|
44
|
+
function c(color, text) {
|
|
45
|
+
return `${colors[color]}${text}${colors.reset}`;
|
|
46
|
+
}
|
|
47
|
+
const SECURITY_TOOLS = [
|
|
48
|
+
{
|
|
49
|
+
name: 'gitleaks',
|
|
50
|
+
command: 'gitleaks',
|
|
51
|
+
description: 'Secrets detection in code and git history',
|
|
52
|
+
category: 'secrets',
|
|
53
|
+
install: { brew: 'brew install gitleaks', apt: 'apt install gitleaks', other: 'https://github.com/gitleaks/gitleaks#installing' },
|
|
54
|
+
license: 'MIT (Free)',
|
|
55
|
+
required: true
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'trivy',
|
|
59
|
+
command: 'trivy',
|
|
60
|
+
description: 'Vulnerability scanner for containers and filesystems',
|
|
61
|
+
category: 'vulnerabilities',
|
|
62
|
+
install: { brew: 'brew install trivy', apt: 'apt install trivy', other: 'https://github.com/aquasecurity/trivy#installation' },
|
|
63
|
+
license: 'Apache 2.0 (Free)',
|
|
64
|
+
required: true
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'grype',
|
|
68
|
+
command: 'grype',
|
|
69
|
+
description: 'Universal vulnerability scanner',
|
|
70
|
+
category: 'vulnerabilities',
|
|
71
|
+
install: { brew: 'brew install grype', other: 'curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh' },
|
|
72
|
+
license: 'Apache 2.0 (Free)',
|
|
73
|
+
required: false
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'semgrep',
|
|
77
|
+
command: 'semgrep',
|
|
78
|
+
description: 'Static analysis (SAST) for code security',
|
|
79
|
+
category: 'sast',
|
|
80
|
+
install: { pip: 'pip install semgrep', brew: 'brew install semgrep' },
|
|
81
|
+
license: 'LGPL 2.1 (Free, Pro rules paid)',
|
|
82
|
+
required: false
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'checkov',
|
|
86
|
+
command: 'checkov',
|
|
87
|
+
description: 'Infrastructure as Code (IaC) security scanner',
|
|
88
|
+
category: 'iac',
|
|
89
|
+
install: { pip: 'pip install checkov', brew: 'brew install checkov' },
|
|
90
|
+
license: 'Apache 2.0 (Free)',
|
|
91
|
+
required: false
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'hadolint',
|
|
95
|
+
command: 'hadolint',
|
|
96
|
+
description: 'Dockerfile linter and security checker',
|
|
97
|
+
category: 'container',
|
|
98
|
+
install: { brew: 'brew install hadolint', other: 'https://github.com/hadolint/hadolint#install' },
|
|
99
|
+
license: 'GPL-3.0 (Free)',
|
|
100
|
+
required: false
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'pip-audit',
|
|
104
|
+
command: 'pip-audit',
|
|
105
|
+
description: 'Python package vulnerability scanner',
|
|
106
|
+
category: 'vulnerabilities',
|
|
107
|
+
install: { pip: 'pip install pip-audit' },
|
|
108
|
+
license: 'Apache 2.0 (Free)',
|
|
109
|
+
required: false
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'govulncheck',
|
|
113
|
+
command: 'govulncheck',
|
|
114
|
+
description: 'Go module vulnerability scanner',
|
|
115
|
+
category: 'vulnerabilities',
|
|
116
|
+
install: { go: 'go install golang.org/x/vuln/cmd/govulncheck@latest' },
|
|
117
|
+
license: 'BSD-3-Clause (Free)',
|
|
118
|
+
required: false
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'cargo-audit',
|
|
122
|
+
command: 'cargo-audit',
|
|
123
|
+
description: 'Rust crate vulnerability scanner',
|
|
124
|
+
category: 'vulnerabilities',
|
|
125
|
+
install: { cargo: 'cargo install cargo-audit' },
|
|
126
|
+
license: 'Apache 2.0 / MIT (Free)',
|
|
127
|
+
required: false
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'bundle-audit',
|
|
131
|
+
command: 'bundle-audit',
|
|
132
|
+
description: 'Ruby gem vulnerability scanner',
|
|
133
|
+
category: 'vulnerabilities',
|
|
134
|
+
install: { gem: 'gem install bundler-audit' },
|
|
135
|
+
license: 'GPLv3 (Free)',
|
|
136
|
+
required: false
|
|
137
|
+
}
|
|
138
|
+
];
|
|
139
|
+
function isToolInstalled(command) {
|
|
140
|
+
try {
|
|
141
|
+
const result = spawnSync(command, ['--version'], {
|
|
142
|
+
encoding: 'utf-8',
|
|
143
|
+
timeout: 5000,
|
|
144
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
145
|
+
});
|
|
146
|
+
if (result.status === 0) {
|
|
147
|
+
const version = (result.stdout || result.stderr || '').split('\n')[0].trim().slice(0, 50);
|
|
148
|
+
return { installed: true, version };
|
|
149
|
+
}
|
|
150
|
+
return { installed: false };
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return { installed: false };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function runDoctor() {
|
|
157
|
+
console.log('');
|
|
158
|
+
console.log(c('cyan', '╔═══════════════════════════════════════════════════════════════╗'));
|
|
159
|
+
console.log(c('cyan', '║') + c('bold', ' AURASECURITY - Security Tools Check ') + c('cyan', '║'));
|
|
160
|
+
console.log(c('cyan', '╚═══════════════════════════════════════════════════════════════╝'));
|
|
161
|
+
console.log('');
|
|
162
|
+
// Check Node.js
|
|
163
|
+
console.log(c('bold', '► Runtime Environment'));
|
|
164
|
+
console.log(` ${c('green', '✓')} Node.js ${process.version}`);
|
|
165
|
+
console.log(` ${c('green', '✓')} Platform: ${process.platform} (${process.arch})`);
|
|
166
|
+
console.log('');
|
|
167
|
+
// Check Git
|
|
168
|
+
const gitCheck = isToolInstalled('git');
|
|
169
|
+
console.log(c('bold', '► Required Tools'));
|
|
170
|
+
if (gitCheck.installed) {
|
|
171
|
+
console.log(` ${c('green', '✓')} git ${c('dim', gitCheck.version || '')}`);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
console.log(` ${c('red', '✗')} git - ${c('yellow', 'Required for remote repo scanning')}`);
|
|
175
|
+
}
|
|
176
|
+
// Group tools by category
|
|
177
|
+
const categories = {
|
|
178
|
+
secrets: { name: 'Secrets Detection', tools: [] },
|
|
179
|
+
vulnerabilities: { name: 'Vulnerability Scanning', tools: [] },
|
|
180
|
+
sast: { name: 'Static Analysis (SAST)', tools: [] },
|
|
181
|
+
iac: { name: 'Infrastructure as Code', tools: [] },
|
|
182
|
+
container: { name: 'Container Security', tools: [] },
|
|
183
|
+
license: { name: 'License Compliance', tools: [] }
|
|
184
|
+
};
|
|
185
|
+
for (const tool of SECURITY_TOOLS) {
|
|
186
|
+
categories[tool.category].tools.push(tool);
|
|
187
|
+
}
|
|
188
|
+
let installedCount = 0;
|
|
189
|
+
let totalCount = 0;
|
|
190
|
+
const missingTools = [];
|
|
191
|
+
for (const [, category] of Object.entries(categories)) {
|
|
192
|
+
if (category.tools.length === 0)
|
|
193
|
+
continue;
|
|
194
|
+
console.log('');
|
|
195
|
+
console.log(c('bold', `► ${category.name}`));
|
|
196
|
+
for (const tool of category.tools) {
|
|
197
|
+
totalCount++;
|
|
198
|
+
const check = isToolInstalled(tool.command);
|
|
199
|
+
if (check.installed) {
|
|
200
|
+
installedCount++;
|
|
201
|
+
console.log(` ${c('green', '✓')} ${tool.name} ${c('dim', check.version || '')}`);
|
|
202
|
+
console.log(` ${c('dim', tool.description)}`);
|
|
203
|
+
console.log(` ${c('dim', `License: ${tool.license}`)}`);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
missingTools.push(tool);
|
|
207
|
+
const marker = tool.required ? c('red', '✗') : c('yellow', '○');
|
|
208
|
+
const status = tool.required ? c('red', 'MISSING') : c('yellow', 'optional');
|
|
209
|
+
console.log(` ${marker} ${tool.name} [${status}]`);
|
|
210
|
+
console.log(` ${c('dim', tool.description)}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Native capabilities
|
|
215
|
+
console.log('');
|
|
216
|
+
console.log(c('bold', '► Native Capabilities (No External Tools Needed)'));
|
|
217
|
+
console.log(` ${c('green', '✓')} Regex-based secret detection (fallback)`);
|
|
218
|
+
console.log(` ${c('green', '✓')} Service/technology discovery`);
|
|
219
|
+
console.log(` ${c('green', '✓')} Codebase structure mapping`);
|
|
220
|
+
console.log(` ${c('green', '✓')} Environment file scanning`);
|
|
221
|
+
console.log(` ${c('green', '✓')} Git repository analysis`);
|
|
222
|
+
console.log(` ${c('green', '✓')} Language auto-detection`);
|
|
223
|
+
console.log(` ${c('green', '✓')} npm audit (built into npm)`);
|
|
224
|
+
// Summary
|
|
225
|
+
console.log('');
|
|
226
|
+
console.log('─'.repeat(67));
|
|
227
|
+
const pct = Math.round((installedCount / totalCount) * 100);
|
|
228
|
+
const pctColor = pct >= 80 ? 'green' : pct >= 50 ? 'yellow' : 'red';
|
|
229
|
+
console.log(`Tools installed: ${c(pctColor, `${installedCount}/${totalCount}`)} (${pct}%)`);
|
|
230
|
+
if (missingTools.length > 0) {
|
|
231
|
+
console.log('');
|
|
232
|
+
console.log(c('yellow', '► Installation Commands for Missing Tools:'));
|
|
233
|
+
console.log('');
|
|
234
|
+
for (const tool of missingTools) {
|
|
235
|
+
console.log(` ${c('cyan', tool.name)}:`);
|
|
236
|
+
if (tool.install.brew)
|
|
237
|
+
console.log(` macOS: ${tool.install.brew}`);
|
|
238
|
+
if (tool.install.apt)
|
|
239
|
+
console.log(` Linux: ${tool.install.apt}`);
|
|
240
|
+
if (tool.install.pip)
|
|
241
|
+
console.log(` pip: ${tool.install.pip}`);
|
|
242
|
+
if (tool.install.npm)
|
|
243
|
+
console.log(` npm: ${tool.install.npm}`);
|
|
244
|
+
if (tool.install.go)
|
|
245
|
+
console.log(` go: ${tool.install.go}`);
|
|
246
|
+
if (tool.install.cargo)
|
|
247
|
+
console.log(` cargo: ${tool.install.cargo}`);
|
|
248
|
+
if (tool.install.gem)
|
|
249
|
+
console.log(` gem: ${tool.install.gem}`);
|
|
250
|
+
if (tool.install.other)
|
|
251
|
+
console.log(` other: ${tool.install.other}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Quick install script suggestion
|
|
255
|
+
const requiredMissing = missingTools.filter(t => t.required);
|
|
256
|
+
if (requiredMissing.length > 0) {
|
|
257
|
+
console.log('');
|
|
258
|
+
console.log(c('yellow', '⚠ Some required tools are missing. Quick install (macOS):'));
|
|
259
|
+
console.log('');
|
|
260
|
+
console.log(c('cyan', ` brew install ${requiredMissing.filter(t => t.install.brew).map(t => t.name).join(' ')}`));
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
console.log('');
|
|
264
|
+
console.log(c('green', '✓ All required tools are installed!'));
|
|
265
|
+
}
|
|
266
|
+
console.log('');
|
|
267
|
+
}
|
|
268
|
+
async function main() {
|
|
269
|
+
const args = process.argv.slice(2);
|
|
270
|
+
const command = args[0];
|
|
271
|
+
// Handle flags
|
|
272
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
273
|
+
console.log(`aura-security v${VERSION}`);
|
|
274
|
+
process.exit(0);
|
|
275
|
+
}
|
|
276
|
+
if (args.includes('--help') || args.includes('-h') || !command) {
|
|
277
|
+
showHelp();
|
|
278
|
+
process.exit(0);
|
|
279
|
+
}
|
|
280
|
+
switch (command) {
|
|
281
|
+
case 'init':
|
|
282
|
+
await runInit(args.slice(1));
|
|
283
|
+
break;
|
|
284
|
+
case 'doctor':
|
|
285
|
+
case 'check':
|
|
286
|
+
await runDoctor();
|
|
287
|
+
break;
|
|
288
|
+
case 'scan':
|
|
289
|
+
await runScan(args.slice(1));
|
|
290
|
+
break;
|
|
291
|
+
case 'aws':
|
|
292
|
+
await runAWSScan(args.slice(1));
|
|
293
|
+
break;
|
|
294
|
+
case 'sbom':
|
|
295
|
+
await runSBOM(args.slice(1));
|
|
296
|
+
break;
|
|
297
|
+
case 'license-check':
|
|
298
|
+
await runLicenseCheck(args.slice(1));
|
|
299
|
+
break;
|
|
300
|
+
case 'serve':
|
|
301
|
+
await startServer();
|
|
302
|
+
break;
|
|
303
|
+
case 'visualizer':
|
|
304
|
+
await startVisualizer();
|
|
305
|
+
break;
|
|
306
|
+
case 'status':
|
|
307
|
+
await showStatus();
|
|
308
|
+
break;
|
|
309
|
+
case 'audit':
|
|
310
|
+
await runAudit(args.slice(1));
|
|
311
|
+
break;
|
|
312
|
+
case 'logs':
|
|
313
|
+
await showLogs();
|
|
314
|
+
break;
|
|
315
|
+
case 'watch':
|
|
316
|
+
await watchMode();
|
|
317
|
+
break;
|
|
318
|
+
default:
|
|
319
|
+
console.error(c('red', `Unknown command: ${command}`));
|
|
320
|
+
console.log('Run aura-security --help for usage information.');
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
// ============ INIT COMMAND ============
|
|
325
|
+
async function runInit(args) {
|
|
326
|
+
const targetDir = args[0] ? resolve(args[0]) : process.cwd();
|
|
327
|
+
const configDir = join(targetDir, '.aura-security');
|
|
328
|
+
const configFile = join(configDir, 'config.json');
|
|
329
|
+
const gitignorePath = join(targetDir, '.gitignore');
|
|
330
|
+
console.log('');
|
|
331
|
+
console.log(c('cyan', '🔧 Initializing aurasecurity configuration...'));
|
|
332
|
+
console.log('');
|
|
333
|
+
// Check if already initialized
|
|
334
|
+
if (existsSync(configFile)) {
|
|
335
|
+
console.log(c('yellow', `Configuration already exists at: ${configFile}`));
|
|
336
|
+
console.log('');
|
|
337
|
+
console.log('To reinitialize, delete the .aura-security directory first.');
|
|
338
|
+
process.exit(0);
|
|
339
|
+
}
|
|
340
|
+
// Create config directory
|
|
341
|
+
if (!existsSync(configDir)) {
|
|
342
|
+
mkdirSync(configDir, { recursive: true });
|
|
343
|
+
console.log(c('green', '✓') + ` Created directory: ${configDir}`);
|
|
344
|
+
}
|
|
345
|
+
// Default configuration
|
|
346
|
+
const defaultConfig = {
|
|
347
|
+
"$schema": "https://raw.githubusercontent.com/aurasecurity/aura-security/main/schemas/config.schema.json",
|
|
348
|
+
"version": "1.0",
|
|
349
|
+
"project": {
|
|
350
|
+
"name": basename(targetDir),
|
|
351
|
+
"description": "Security audit configuration"
|
|
352
|
+
},
|
|
353
|
+
"scanning": {
|
|
354
|
+
"enabled": true,
|
|
355
|
+
"secrets": true,
|
|
356
|
+
"packages": true,
|
|
357
|
+
"sast": true,
|
|
358
|
+
"maxDepth": 5,
|
|
359
|
+
"exclude": [
|
|
360
|
+
"node_modules",
|
|
361
|
+
".git",
|
|
362
|
+
"dist",
|
|
363
|
+
"build",
|
|
364
|
+
".next",
|
|
365
|
+
"coverage",
|
|
366
|
+
"__pycache__",
|
|
367
|
+
"venv",
|
|
368
|
+
".venv"
|
|
369
|
+
]
|
|
370
|
+
},
|
|
371
|
+
"tools": {
|
|
372
|
+
"gitleaks": {
|
|
373
|
+
"enabled": true,
|
|
374
|
+
"configPath": null
|
|
375
|
+
},
|
|
376
|
+
"trivy": {
|
|
377
|
+
"enabled": true,
|
|
378
|
+
"severity": ["CRITICAL", "HIGH", "MEDIUM"]
|
|
379
|
+
},
|
|
380
|
+
"semgrep": {
|
|
381
|
+
"enabled": true,
|
|
382
|
+
"config": "auto"
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
"server": {
|
|
386
|
+
"port": 3000,
|
|
387
|
+
"visualizerPort": 8080
|
|
388
|
+
},
|
|
389
|
+
"thresholds": {
|
|
390
|
+
"failOnCritical": true,
|
|
391
|
+
"failOnHigh": false,
|
|
392
|
+
"maxCritical": 0,
|
|
393
|
+
"maxHigh": 5
|
|
394
|
+
},
|
|
395
|
+
"notifications": {
|
|
396
|
+
"slack": {
|
|
397
|
+
"enabled": false,
|
|
398
|
+
"webhookUrl": null
|
|
399
|
+
},
|
|
400
|
+
"discord": {
|
|
401
|
+
"enabled": false,
|
|
402
|
+
"webhookUrl": null
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
"integrations": {
|
|
406
|
+
"github": {
|
|
407
|
+
"enabled": false,
|
|
408
|
+
"token": null,
|
|
409
|
+
"createCheckRuns": true,
|
|
410
|
+
"commentOnPR": true
|
|
411
|
+
},
|
|
412
|
+
"gitlab": {
|
|
413
|
+
"enabled": false,
|
|
414
|
+
"token": null
|
|
415
|
+
},
|
|
416
|
+
"aws": {
|
|
417
|
+
"enabled": false,
|
|
418
|
+
"region": "us-east-1",
|
|
419
|
+
"services": ["iam", "s3", "ec2", "lambda", "rds"]
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
// Write config file
|
|
424
|
+
writeFileSync(configFile, JSON.stringify(defaultConfig, null, 2));
|
|
425
|
+
console.log(c('green', '✓') + ` Created config: ${configFile}`);
|
|
426
|
+
// Create a sample .env.example
|
|
427
|
+
const envExamplePath = join(configDir, '.env.example');
|
|
428
|
+
const envExample = `# aurasecurity Environment Variables
|
|
429
|
+
# Copy this to .env and fill in your values
|
|
430
|
+
|
|
431
|
+
# Server Configuration
|
|
432
|
+
AURA_PORT=3000
|
|
433
|
+
VISUALIZER_PORT=8080
|
|
434
|
+
|
|
435
|
+
# GitHub Integration (optional)
|
|
436
|
+
GITHUB_TOKEN=
|
|
437
|
+
|
|
438
|
+
# GitLab Integration (optional)
|
|
439
|
+
GITLAB_TOKEN=
|
|
440
|
+
|
|
441
|
+
# Slack Notifications (optional)
|
|
442
|
+
SLACK_WEBHOOK_URL=
|
|
443
|
+
|
|
444
|
+
# Discord Notifications (optional)
|
|
445
|
+
DISCORD_WEBHOOK_URL=
|
|
446
|
+
|
|
447
|
+
# AWS Integration (optional)
|
|
448
|
+
AWS_REGION=us-east-1
|
|
449
|
+
AWS_ACCESS_KEY_ID=
|
|
450
|
+
AWS_SECRET_ACCESS_KEY=
|
|
451
|
+
`;
|
|
452
|
+
writeFileSync(envExamplePath, envExample);
|
|
453
|
+
console.log(c('green', '✓') + ` Created: ${envExamplePath}`);
|
|
454
|
+
// Update .gitignore if it exists
|
|
455
|
+
if (existsSync(gitignorePath)) {
|
|
456
|
+
const { readFileSync } = await import('fs');
|
|
457
|
+
const gitignore = readFileSync(gitignorePath, 'utf-8');
|
|
458
|
+
const linesToAdd = [
|
|
459
|
+
'.aura-security/.env',
|
|
460
|
+
'.aura-security/results/',
|
|
461
|
+
'.aura-security/*.log'
|
|
462
|
+
];
|
|
463
|
+
const missingLines = linesToAdd.filter(line => !gitignore.includes(line));
|
|
464
|
+
if (missingLines.length > 0) {
|
|
465
|
+
const addition = '\n# aurasecurity\n' + missingLines.join('\n') + '\n';
|
|
466
|
+
const { appendFileSync } = await import('fs');
|
|
467
|
+
appendFileSync(gitignorePath, addition);
|
|
468
|
+
console.log(c('green', '✓') + ' Updated .gitignore');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
console.log('');
|
|
472
|
+
console.log(c('green', '✓ Initialization complete!'));
|
|
473
|
+
console.log('');
|
|
474
|
+
console.log('Next steps:');
|
|
475
|
+
console.log(` 1. Edit ${c('cyan', configFile)} to customize settings`);
|
|
476
|
+
console.log(` 2. Copy ${c('cyan', envExamplePath)} to .env and add credentials`);
|
|
477
|
+
console.log(` 3. Run ${c('cyan', 'aura-security scan .')} to scan your project`);
|
|
478
|
+
console.log('');
|
|
479
|
+
}
|
|
480
|
+
// ============ SCAN COMMAND (Standalone - no server needed) ============
|
|
481
|
+
async function runScan(args) {
|
|
482
|
+
// Parse arguments
|
|
483
|
+
let targetPath = '.';
|
|
484
|
+
let outputFormat = 'console';
|
|
485
|
+
let outputFile;
|
|
486
|
+
let languages;
|
|
487
|
+
let scanners;
|
|
488
|
+
let failOn = ['critical']; // Default: fail on critical
|
|
489
|
+
for (let i = 0; i < args.length; i++) {
|
|
490
|
+
const arg = args[i];
|
|
491
|
+
if (arg === '--json' || arg === '-j') {
|
|
492
|
+
outputFormat = 'json';
|
|
493
|
+
}
|
|
494
|
+
else if (arg === '--format' || arg === '-f') {
|
|
495
|
+
const fmt = args[++i]?.toLowerCase();
|
|
496
|
+
if (['json', 'sarif', 'junit', 'gitlab-sast', 'gitlab-deps', 'summary', 'console'].includes(fmt)) {
|
|
497
|
+
outputFormat = fmt;
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
console.error(c('red', `Unknown format: ${fmt}`));
|
|
501
|
+
console.log('Available formats: console, json, sarif, junit, gitlab-sast, gitlab-deps, summary');
|
|
502
|
+
process.exit(1);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
else if (arg === '--output' || arg === '-o') {
|
|
506
|
+
outputFile = args[++i];
|
|
507
|
+
}
|
|
508
|
+
else if (arg === '--languages' || arg === '-l') {
|
|
509
|
+
languages = args[++i].split(',').map(l => l.trim());
|
|
510
|
+
}
|
|
511
|
+
else if (arg === '--scanners' || arg === '-S') {
|
|
512
|
+
scanners = args[++i].split(',').map(s => s.trim());
|
|
513
|
+
}
|
|
514
|
+
else if (arg === '--fail-on') {
|
|
515
|
+
failOn = args[++i].split(',').map(s => s.trim().toLowerCase());
|
|
516
|
+
}
|
|
517
|
+
else if (arg === '--no-fail') {
|
|
518
|
+
failOn = [];
|
|
519
|
+
}
|
|
520
|
+
else if (!arg.startsWith('-')) {
|
|
521
|
+
targetPath = arg;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// Resolve path
|
|
525
|
+
targetPath = resolve(targetPath);
|
|
526
|
+
if (!existsSync(targetPath)) {
|
|
527
|
+
console.error(c('red', `Error: Path does not exist: ${targetPath}`));
|
|
528
|
+
process.exit(1);
|
|
529
|
+
}
|
|
530
|
+
// Print header
|
|
531
|
+
console.log('');
|
|
532
|
+
console.log(c('cyan', '╔═══════════════════════════════════════════════════════════════╗'));
|
|
533
|
+
console.log(c('cyan', '║') + c('bold', ' AURASECURITY - Security Scanner ') + c('cyan', '║'));
|
|
534
|
+
console.log(c('cyan', '╚═══════════════════════════════════════════════════════════════╝'));
|
|
535
|
+
console.log('');
|
|
536
|
+
console.log(c('dim', `Target: ${targetPath}`));
|
|
537
|
+
console.log(c('dim', `Time: ${new Date().toISOString()}`));
|
|
538
|
+
if (languages)
|
|
539
|
+
console.log(c('dim', `Languages: ${languages.join(', ')}`));
|
|
540
|
+
if (scanners)
|
|
541
|
+
console.log(c('dim', `Scanners: ${scanners.join(', ')}`));
|
|
542
|
+
console.log('');
|
|
543
|
+
// Run the scan
|
|
544
|
+
console.log(c('yellow', '⏳ Starting security scan...'));
|
|
545
|
+
console.log('');
|
|
546
|
+
const startTime = Date.now();
|
|
547
|
+
const scanner = new LocalScanner({
|
|
548
|
+
targetPath,
|
|
549
|
+
languages,
|
|
550
|
+
scanners,
|
|
551
|
+
scanIaC: true,
|
|
552
|
+
scanDockerfiles: true
|
|
553
|
+
});
|
|
554
|
+
const result = await scanner.scan();
|
|
555
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
556
|
+
console.log('');
|
|
557
|
+
console.log(c('green', `✓ Scan completed in ${elapsed}s`));
|
|
558
|
+
console.log('');
|
|
559
|
+
// Generate output based on format
|
|
560
|
+
let outputContent;
|
|
561
|
+
let showConsole = true;
|
|
562
|
+
switch (outputFormat) {
|
|
563
|
+
case 'json':
|
|
564
|
+
outputContent = JSON.stringify(result, null, 2);
|
|
565
|
+
showConsole = false;
|
|
566
|
+
console.log(outputContent);
|
|
567
|
+
break;
|
|
568
|
+
case 'sarif':
|
|
569
|
+
outputContent = JSON.stringify(toSARIF(result, targetPath), null, 2);
|
|
570
|
+
showConsole = false;
|
|
571
|
+
if (!outputFile)
|
|
572
|
+
console.log(outputContent);
|
|
573
|
+
break;
|
|
574
|
+
case 'junit':
|
|
575
|
+
outputContent = toJUnit(result);
|
|
576
|
+
showConsole = false;
|
|
577
|
+
if (!outputFile)
|
|
578
|
+
console.log(outputContent);
|
|
579
|
+
break;
|
|
580
|
+
case 'gitlab-sast':
|
|
581
|
+
outputContent = JSON.stringify(toGitLabReport(result, 'sast'), null, 2);
|
|
582
|
+
showConsole = false;
|
|
583
|
+
if (!outputFile)
|
|
584
|
+
console.log(outputContent);
|
|
585
|
+
break;
|
|
586
|
+
case 'gitlab-deps':
|
|
587
|
+
outputContent = JSON.stringify(toGitLabReport(result, 'dependency_scanning'), null, 2);
|
|
588
|
+
showConsole = false;
|
|
589
|
+
if (!outputFile)
|
|
590
|
+
console.log(outputContent);
|
|
591
|
+
break;
|
|
592
|
+
case 'summary':
|
|
593
|
+
outputContent = JSON.stringify(toJSONSummary(result), null, 2);
|
|
594
|
+
showConsole = false;
|
|
595
|
+
console.log(outputContent);
|
|
596
|
+
break;
|
|
597
|
+
default:
|
|
598
|
+
outputContent = JSON.stringify(result, null, 2);
|
|
599
|
+
displayScanResults(result);
|
|
600
|
+
}
|
|
601
|
+
// Write to file if requested
|
|
602
|
+
if (outputFile) {
|
|
603
|
+
const outputPath = resolve(outputFile);
|
|
604
|
+
writeFileSync(outputPath, outputContent);
|
|
605
|
+
if (showConsole) {
|
|
606
|
+
console.log('');
|
|
607
|
+
}
|
|
608
|
+
console.log(c('green', `✓ Results saved to: ${outputPath}`));
|
|
609
|
+
}
|
|
610
|
+
// Exit with appropriate code based on --fail-on flag
|
|
611
|
+
const hasCritical = result.secrets.some(s => s.severity === 'critical') ||
|
|
612
|
+
result.packages.some(p => p.severity === 'critical') ||
|
|
613
|
+
result.iacFindings.some(i => i.severity === 'critical') ||
|
|
614
|
+
result.dockerfileFindings.some(d => d.severity === 'critical');
|
|
615
|
+
const hasHigh = result.secrets.some(s => s.severity === 'high') ||
|
|
616
|
+
result.packages.some(p => p.severity === 'high') ||
|
|
617
|
+
result.iacFindings.some(i => i.severity === 'high') ||
|
|
618
|
+
result.dockerfileFindings.some(d => d.severity === 'high');
|
|
619
|
+
const hasMedium = result.secrets.some(s => s.severity === 'medium') ||
|
|
620
|
+
result.packages.some(p => p.severity === 'medium') ||
|
|
621
|
+
result.iacFindings.some(i => i.severity === 'medium') ||
|
|
622
|
+
result.dockerfileFindings.some(d => d.severity === 'medium');
|
|
623
|
+
if (failOn.includes('critical') && hasCritical) {
|
|
624
|
+
process.exit(2); // Critical findings
|
|
625
|
+
}
|
|
626
|
+
else if (failOn.includes('high') && hasHigh) {
|
|
627
|
+
process.exit(1); // High findings
|
|
628
|
+
}
|
|
629
|
+
else if (failOn.includes('medium') && hasMedium) {
|
|
630
|
+
process.exit(1); // Medium findings
|
|
631
|
+
}
|
|
632
|
+
process.exit(0); // Clean
|
|
633
|
+
}
|
|
634
|
+
function displayScanResults(result) {
|
|
635
|
+
// Summary counts
|
|
636
|
+
const secretCount = result.secrets.length;
|
|
637
|
+
const packageCount = result.packages.length;
|
|
638
|
+
const sastCount = result.sastFindings.length;
|
|
639
|
+
const iacCount = result.iacFindings.length;
|
|
640
|
+
const dockerfileCount = result.dockerfileFindings.length;
|
|
641
|
+
const serviceCount = result.discoveredServices.length;
|
|
642
|
+
const moduleCount = result.discoveredModules.length;
|
|
643
|
+
const criticalCount = result.secrets.filter(s => s.severity === 'critical').length +
|
|
644
|
+
result.packages.filter(p => p.severity === 'critical').length +
|
|
645
|
+
result.iacFindings.filter(i => i.severity === 'critical').length +
|
|
646
|
+
result.dockerfileFindings.filter(d => d.severity === 'critical').length;
|
|
647
|
+
const highCount = result.secrets.filter(s => s.severity === 'high').length +
|
|
648
|
+
result.packages.filter(p => p.severity === 'high').length +
|
|
649
|
+
result.iacFindings.filter(i => i.severity === 'high').length +
|
|
650
|
+
result.dockerfileFindings.filter(d => d.severity === 'high').length;
|
|
651
|
+
const mediumCount = result.secrets.filter(s => s.severity === 'medium').length +
|
|
652
|
+
result.packages.filter(p => p.severity === 'medium').length +
|
|
653
|
+
result.iacFindings.filter(i => i.severity === 'medium').length +
|
|
654
|
+
result.dockerfileFindings.filter(d => d.severity === 'medium').length;
|
|
655
|
+
const lowCount = result.secrets.filter(s => s.severity === 'low').length +
|
|
656
|
+
result.packages.filter(p => p.severity === 'low').length +
|
|
657
|
+
result.iacFindings.filter(i => i.severity === 'low').length +
|
|
658
|
+
result.dockerfileFindings.filter(d => d.severity === 'low').length;
|
|
659
|
+
// Stats bar
|
|
660
|
+
console.log('┌─────────────────────────────────────────────────────────────────┐');
|
|
661
|
+
console.log(`│ ${c('bgRed', ` CRITICAL ${criticalCount} `)} ${c('red', `HIGH ${highCount}`)} ${c('yellow', `MEDIUM ${mediumCount}`)} ${c('dim', `LOW ${lowCount}`)} │`);
|
|
662
|
+
console.log('└─────────────────────────────────────────────────────────────────┘');
|
|
663
|
+
console.log('');
|
|
664
|
+
// Tools used
|
|
665
|
+
console.log(c('cyan', '► Tools Used:'));
|
|
666
|
+
console.log(` ${result.toolsUsed.join(', ') || 'regex-patterns (fallback)'}`);
|
|
667
|
+
console.log('');
|
|
668
|
+
// Secrets
|
|
669
|
+
if (secretCount > 0) {
|
|
670
|
+
console.log(c('red', `► Secrets Found (${secretCount}):`));
|
|
671
|
+
for (const s of result.secrets.slice(0, 10)) {
|
|
672
|
+
const sev = s.severity === 'critical' ? c('bgRed', ' CRIT ') :
|
|
673
|
+
s.severity === 'high' ? c('red', ' HIGH ') :
|
|
674
|
+
s.severity === 'medium' ? c('yellow', ' MED ') : c('dim', ' LOW ');
|
|
675
|
+
console.log(` ${sev} ${s.type}`);
|
|
676
|
+
console.log(` ${c('dim', s.file)}:${s.line}`);
|
|
677
|
+
}
|
|
678
|
+
if (secretCount > 10) {
|
|
679
|
+
console.log(c('dim', ` ... and ${secretCount - 10} more`));
|
|
680
|
+
}
|
|
681
|
+
console.log('');
|
|
682
|
+
}
|
|
683
|
+
// Package vulnerabilities
|
|
684
|
+
if (packageCount > 0) {
|
|
685
|
+
console.log(c('yellow', `► Package Vulnerabilities (${packageCount}):`));
|
|
686
|
+
for (const p of result.packages.slice(0, 10)) {
|
|
687
|
+
const sev = p.severity === 'critical' ? c('bgRed', ' CRIT ') :
|
|
688
|
+
p.severity === 'high' ? c('red', ' HIGH ') :
|
|
689
|
+
p.severity === 'medium' ? c('yellow', ' MED ') : c('dim', ' LOW ');
|
|
690
|
+
console.log(` ${sev} ${p.name}@${p.version}`);
|
|
691
|
+
if (p.vulnId) {
|
|
692
|
+
console.log(` ${c('dim', p.vulnId)}${p.title ? ': ' + p.title : ''}`);
|
|
693
|
+
}
|
|
694
|
+
if (p.fixedVersion) {
|
|
695
|
+
console.log(` ${c('green', 'Fix:')} Upgrade to ${p.fixedVersion}`);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (packageCount > 10) {
|
|
699
|
+
console.log(c('dim', ` ... and ${packageCount - 10} more`));
|
|
700
|
+
}
|
|
701
|
+
console.log('');
|
|
702
|
+
}
|
|
703
|
+
// SAST findings
|
|
704
|
+
if (sastCount > 0) {
|
|
705
|
+
console.log(c('magenta', `► SAST Findings (${sastCount}):`));
|
|
706
|
+
for (const s of result.sastFindings.slice(0, 10)) {
|
|
707
|
+
console.log(` [${s.severity}] ${s.rule}`);
|
|
708
|
+
console.log(` ${c('dim', s.file)}:${s.line}`);
|
|
709
|
+
console.log(` ${s.message}`);
|
|
710
|
+
}
|
|
711
|
+
if (sastCount > 10) {
|
|
712
|
+
console.log(c('dim', ` ... and ${sastCount - 10} more`));
|
|
713
|
+
}
|
|
714
|
+
console.log('');
|
|
715
|
+
}
|
|
716
|
+
// IaC findings
|
|
717
|
+
if (iacCount > 0) {
|
|
718
|
+
console.log(c('cyan', `► IaC Security Issues (${iacCount}):`));
|
|
719
|
+
for (const i of result.iacFindings.slice(0, 10)) {
|
|
720
|
+
const sev = i.severity === 'critical' ? c('bgRed', ' CRIT ') :
|
|
721
|
+
i.severity === 'high' ? c('red', ' HIGH ') :
|
|
722
|
+
i.severity === 'medium' ? c('yellow', ' MED ') : c('dim', ' LOW ');
|
|
723
|
+
console.log(` ${sev} ${i.checkId}`);
|
|
724
|
+
console.log(` ${c('dim', i.file)} - ${i.resource}`);
|
|
725
|
+
if (i.guideline) {
|
|
726
|
+
console.log(` ${c('green', 'Guide:')} ${i.guideline}`);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
if (iacCount > 10) {
|
|
730
|
+
console.log(c('dim', ` ... and ${iacCount - 10} more`));
|
|
731
|
+
}
|
|
732
|
+
console.log('');
|
|
733
|
+
}
|
|
734
|
+
// Dockerfile findings
|
|
735
|
+
if (dockerfileCount > 0) {
|
|
736
|
+
console.log(c('blue', `► Dockerfile Issues (${dockerfileCount}):`));
|
|
737
|
+
for (const d of result.dockerfileFindings.slice(0, 10)) {
|
|
738
|
+
const sev = d.severity === 'critical' ? c('bgRed', ' CRIT ') :
|
|
739
|
+
d.severity === 'high' ? c('red', ' HIGH ') :
|
|
740
|
+
d.severity === 'medium' ? c('yellow', ' MED ') : c('dim', ' LOW ');
|
|
741
|
+
console.log(` ${sev} ${d.code}`);
|
|
742
|
+
console.log(` ${c('dim', d.file)}:${d.line}`);
|
|
743
|
+
console.log(` ${d.message}`);
|
|
744
|
+
}
|
|
745
|
+
if (dockerfileCount > 10) {
|
|
746
|
+
console.log(c('dim', ` ... and ${dockerfileCount - 10} more`));
|
|
747
|
+
}
|
|
748
|
+
console.log('');
|
|
749
|
+
}
|
|
750
|
+
// Languages detected
|
|
751
|
+
if (result.languagesDetected && result.languagesDetected.length > 0) {
|
|
752
|
+
console.log(c('dim', '► Languages Detected:'));
|
|
753
|
+
console.log(` ${result.languagesDetected.join(', ')}`);
|
|
754
|
+
console.log('');
|
|
755
|
+
}
|
|
756
|
+
// Discovered services
|
|
757
|
+
if (serviceCount > 0) {
|
|
758
|
+
console.log(c('blue', `► Discovered Services (${serviceCount}):`));
|
|
759
|
+
for (const s of result.discoveredServices) {
|
|
760
|
+
const typeColor = s.type === 'database' ? 'blue' :
|
|
761
|
+
s.type === 'cloud' ? 'cyan' :
|
|
762
|
+
s.type === 'api' ? 'yellow' :
|
|
763
|
+
s.type === 'auth' ? 'green' : 'white';
|
|
764
|
+
console.log(` ${c(typeColor, `[${s.type.toUpperCase()}]`)} ${s.name}`);
|
|
765
|
+
console.log(` ${c('dim', 'Source:')} ${s.source}`);
|
|
766
|
+
}
|
|
767
|
+
console.log('');
|
|
768
|
+
}
|
|
769
|
+
// Discovered modules
|
|
770
|
+
if (moduleCount > 0) {
|
|
771
|
+
console.log(c('green', `► Codebase Structure (${moduleCount} modules):`));
|
|
772
|
+
for (const m of result.discoveredModules) {
|
|
773
|
+
console.log(` [${m.type}] ${m.name} (${m.fileCount} files)`);
|
|
774
|
+
}
|
|
775
|
+
console.log('');
|
|
776
|
+
}
|
|
777
|
+
// Git info
|
|
778
|
+
if (result.gitInfo) {
|
|
779
|
+
console.log(c('dim', '► Git Info:'));
|
|
780
|
+
console.log(` Branch: ${result.gitInfo.branch}`);
|
|
781
|
+
if (result.gitInfo.remoteUrl) {
|
|
782
|
+
console.log(` Remote: ${result.gitInfo.remoteUrl}`);
|
|
783
|
+
}
|
|
784
|
+
if (result.gitInfo.uncommittedChanges > 0) {
|
|
785
|
+
console.log(` ${c('yellow', `Uncommitted changes: ${result.gitInfo.uncommittedChanges}`)}`);
|
|
786
|
+
}
|
|
787
|
+
console.log('');
|
|
788
|
+
}
|
|
789
|
+
// Env files
|
|
790
|
+
if (result.envFiles.length > 0) {
|
|
791
|
+
console.log(c('dim', '► Environment Files:'));
|
|
792
|
+
for (const e of result.envFiles) {
|
|
793
|
+
const warning = e.hasSecrets ? c('yellow', ' (contains secrets)') : '';
|
|
794
|
+
console.log(` ${e.file}${warning} - ${e.variables.length} variables`);
|
|
795
|
+
}
|
|
796
|
+
console.log('');
|
|
797
|
+
}
|
|
798
|
+
// Summary
|
|
799
|
+
const totalIssues = criticalCount + highCount + mediumCount + lowCount;
|
|
800
|
+
if (totalIssues === 0) {
|
|
801
|
+
console.log(c('green', '✓ No security issues found!'));
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
console.log('─'.repeat(67));
|
|
805
|
+
console.log(`Total issues: ${totalIssues}`);
|
|
806
|
+
if (criticalCount > 0) {
|
|
807
|
+
console.log(c('red', `⚠ ${criticalCount} critical issues require immediate attention!`));
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
// ============ AWS SCAN COMMAND ============
|
|
812
|
+
async function runAWSScan(args) {
|
|
813
|
+
// Parse arguments
|
|
814
|
+
let region = process.env.AWS_REGION || 'us-east-1';
|
|
815
|
+
let profile;
|
|
816
|
+
let services;
|
|
817
|
+
let outputFormat = 'console';
|
|
818
|
+
let outputFile;
|
|
819
|
+
for (let i = 0; i < args.length; i++) {
|
|
820
|
+
const arg = args[i];
|
|
821
|
+
if (arg === '--region' || arg === '-r') {
|
|
822
|
+
region = args[++i];
|
|
823
|
+
}
|
|
824
|
+
else if (arg === '--profile' || arg === '-p') {
|
|
825
|
+
profile = args[++i];
|
|
826
|
+
}
|
|
827
|
+
else if (arg === '--services' || arg === '-s') {
|
|
828
|
+
services = args[++i].split(',');
|
|
829
|
+
}
|
|
830
|
+
else if (arg === '--json' || arg === '-j') {
|
|
831
|
+
outputFormat = 'json';
|
|
832
|
+
}
|
|
833
|
+
else if (arg === '--output' || arg === '-o') {
|
|
834
|
+
outputFile = args[++i];
|
|
835
|
+
outputFormat = 'both';
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
// Print header
|
|
839
|
+
console.log('');
|
|
840
|
+
console.log(c('cyan', '╔═══════════════════════════════════════════════════════════════╗'));
|
|
841
|
+
console.log(c('cyan', '║') + c('bold', ' AURASECURITY - AWS Security Scan ') + c('cyan', '║'));
|
|
842
|
+
console.log(c('cyan', '╚═══════════════════════════════════════════════════════════════╝'));
|
|
843
|
+
console.log('');
|
|
844
|
+
console.log(c('dim', `Region: ${region}`));
|
|
845
|
+
if (profile)
|
|
846
|
+
console.log(c('dim', `Profile: ${profile}`));
|
|
847
|
+
console.log(c('dim', `Time: ${new Date().toISOString()}`));
|
|
848
|
+
console.log('');
|
|
849
|
+
// Check for AWS credentials
|
|
850
|
+
if (!process.env.AWS_ACCESS_KEY_ID && !process.env.AWS_PROFILE && !profile) {
|
|
851
|
+
console.log(c('yellow', '⚠ No AWS credentials detected.'));
|
|
852
|
+
console.log('');
|
|
853
|
+
console.log('Set credentials using one of these methods:');
|
|
854
|
+
console.log(` 1. Environment variables: ${c('cyan', 'AWS_ACCESS_KEY_ID')} and ${c('cyan', 'AWS_SECRET_ACCESS_KEY')}`);
|
|
855
|
+
console.log(` 2. AWS profile: ${c('cyan', 'aura-security aws --profile <name>')}`);
|
|
856
|
+
console.log(` 3. AWS config file: ${c('cyan', '~/.aws/credentials')}`);
|
|
857
|
+
console.log('');
|
|
858
|
+
process.exit(1);
|
|
859
|
+
}
|
|
860
|
+
console.log(c('yellow', '⏳ Scanning AWS infrastructure...'));
|
|
861
|
+
console.log('');
|
|
862
|
+
const startTime = Date.now();
|
|
863
|
+
try {
|
|
864
|
+
const result = await scanAWS({ region, profile, services });
|
|
865
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
866
|
+
console.log('');
|
|
867
|
+
console.log(c('green', `✓ Scan completed in ${elapsed}s`));
|
|
868
|
+
console.log('');
|
|
869
|
+
// Display results
|
|
870
|
+
if (outputFormat === 'json') {
|
|
871
|
+
console.log(JSON.stringify(result, null, 2));
|
|
872
|
+
}
|
|
873
|
+
else {
|
|
874
|
+
displayAWSResults(result);
|
|
875
|
+
}
|
|
876
|
+
// Write to file if requested
|
|
877
|
+
if (outputFile) {
|
|
878
|
+
const outputPath = resolve(outputFile);
|
|
879
|
+
writeFileSync(outputPath, JSON.stringify(result, null, 2));
|
|
880
|
+
console.log('');
|
|
881
|
+
console.log(c('green', `✓ Results saved to: ${outputPath}`));
|
|
882
|
+
}
|
|
883
|
+
// Exit with appropriate code
|
|
884
|
+
if (result.summary.critical > 0) {
|
|
885
|
+
process.exit(2);
|
|
886
|
+
}
|
|
887
|
+
else if (result.summary.high > 0) {
|
|
888
|
+
process.exit(1);
|
|
889
|
+
}
|
|
890
|
+
process.exit(0);
|
|
891
|
+
}
|
|
892
|
+
catch (err) {
|
|
893
|
+
console.error(c('red', 'AWS scan failed:'), err.message || err);
|
|
894
|
+
console.log('');
|
|
895
|
+
console.log('Possible causes:');
|
|
896
|
+
console.log(' - Invalid credentials');
|
|
897
|
+
console.log(' - Insufficient IAM permissions');
|
|
898
|
+
console.log(' - Network connectivity issues');
|
|
899
|
+
console.log('');
|
|
900
|
+
process.exit(1);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
// ============ SBOM COMMAND ============
|
|
904
|
+
async function runSBOM(args) {
|
|
905
|
+
let targetPath = '.';
|
|
906
|
+
let format = 'cyclonedx';
|
|
907
|
+
let outputFile;
|
|
908
|
+
let includeVulns = false;
|
|
909
|
+
let projectName;
|
|
910
|
+
for (let i = 0; i < args.length; i++) {
|
|
911
|
+
const arg = args[i];
|
|
912
|
+
if (arg === '--format' || arg === '-f') {
|
|
913
|
+
const fmt = args[++i]?.toLowerCase();
|
|
914
|
+
if (fmt === 'cyclonedx' || fmt === 'spdx') {
|
|
915
|
+
format = fmt;
|
|
916
|
+
}
|
|
917
|
+
else {
|
|
918
|
+
console.error(c('red', `Unknown SBOM format: ${fmt}`));
|
|
919
|
+
console.log('Available formats: cyclonedx, spdx');
|
|
920
|
+
process.exit(1);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
else if (arg === '--output' || arg === '-o') {
|
|
924
|
+
outputFile = args[++i];
|
|
925
|
+
}
|
|
926
|
+
else if (arg === '--include-vulns' || arg === '--vulns') {
|
|
927
|
+
includeVulns = true;
|
|
928
|
+
}
|
|
929
|
+
else if (arg === '--name') {
|
|
930
|
+
projectName = args[++i];
|
|
931
|
+
}
|
|
932
|
+
else if (!arg.startsWith('-')) {
|
|
933
|
+
targetPath = arg;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
targetPath = resolve(targetPath);
|
|
937
|
+
if (!existsSync(targetPath)) {
|
|
938
|
+
console.error(c('red', `Error: Path does not exist: ${targetPath}`));
|
|
939
|
+
process.exit(1);
|
|
940
|
+
}
|
|
941
|
+
console.log('');
|
|
942
|
+
console.log(c('cyan', '╔═══════════════════════════════════════════════════════════════╗'));
|
|
943
|
+
console.log(c('cyan', '║') + c('bold', ' AURASECURITY - SBOM Generator ') + c('cyan', '║'));
|
|
944
|
+
console.log(c('cyan', '╚═══════════════════════════════════════════════════════════════╝'));
|
|
945
|
+
console.log('');
|
|
946
|
+
console.log(c('dim', `Target: ${targetPath}`));
|
|
947
|
+
console.log(c('dim', `Format: ${format.toUpperCase()}`));
|
|
948
|
+
console.log('');
|
|
949
|
+
console.log(c('yellow', '⏳ Generating SBOM...'));
|
|
950
|
+
// Run a quick scan if including vulnerabilities
|
|
951
|
+
let scanResult;
|
|
952
|
+
if (includeVulns) {
|
|
953
|
+
console.log(c('dim', ' Including vulnerability information...'));
|
|
954
|
+
scanResult = await quickLocalScan(targetPath);
|
|
955
|
+
}
|
|
956
|
+
const sbom = generateSBOM(targetPath, scanResult, {
|
|
957
|
+
format,
|
|
958
|
+
includeVulnerabilities: includeVulns,
|
|
959
|
+
projectName: projectName || basename(targetPath)
|
|
960
|
+
});
|
|
961
|
+
const output = JSON.stringify(sbom, null, 2);
|
|
962
|
+
console.log('');
|
|
963
|
+
console.log(c('green', '✓ SBOM generated successfully'));
|
|
964
|
+
if (outputFile) {
|
|
965
|
+
const outputPath = resolve(outputFile);
|
|
966
|
+
writeFileSync(outputPath, output);
|
|
967
|
+
console.log(c('green', `✓ Saved to: ${outputPath}`));
|
|
968
|
+
}
|
|
969
|
+
else {
|
|
970
|
+
console.log('');
|
|
971
|
+
console.log(output);
|
|
972
|
+
}
|
|
973
|
+
// Summary
|
|
974
|
+
const componentCount = 'components' in sbom ? sbom.components.length : ('packages' in sbom ? sbom.packages.length : 0);
|
|
975
|
+
console.log('');
|
|
976
|
+
console.log(c('dim', `Components: ${componentCount}`));
|
|
977
|
+
if (includeVulns && scanResult) {
|
|
978
|
+
console.log(c('dim', `Vulnerabilities: ${scanResult.packages.length}`));
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
// ============ LICENSE CHECK COMMAND ============
|
|
982
|
+
async function runLicenseCheck(args) {
|
|
983
|
+
let targetPath = '.';
|
|
984
|
+
let policy = 'permissive';
|
|
985
|
+
let allowedLicenses = [];
|
|
986
|
+
let deniedLicenses = [];
|
|
987
|
+
let outputFile;
|
|
988
|
+
let outputJson = false;
|
|
989
|
+
for (let i = 0; i < args.length; i++) {
|
|
990
|
+
const arg = args[i];
|
|
991
|
+
if (arg === '--policy' || arg === '-p') {
|
|
992
|
+
policy = args[++i] || 'permissive';
|
|
993
|
+
if (!POLICY_NAMES.includes(policy)) {
|
|
994
|
+
console.error(c('red', `Unknown policy: ${policy}`));
|
|
995
|
+
console.log(`Available policies: ${POLICY_NAMES.join(', ')}`);
|
|
996
|
+
process.exit(1);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
else if (arg === '--allow') {
|
|
1000
|
+
allowedLicenses = args[++i].split(',').map(s => s.trim());
|
|
1001
|
+
}
|
|
1002
|
+
else if (arg === '--deny') {
|
|
1003
|
+
deniedLicenses = args[++i].split(',').map(s => s.trim());
|
|
1004
|
+
}
|
|
1005
|
+
else if (arg === '--output' || arg === '-o') {
|
|
1006
|
+
outputFile = args[++i];
|
|
1007
|
+
}
|
|
1008
|
+
else if (arg === '--json') {
|
|
1009
|
+
outputJson = true;
|
|
1010
|
+
}
|
|
1011
|
+
else if (!arg.startsWith('-')) {
|
|
1012
|
+
targetPath = arg;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
targetPath = resolve(targetPath);
|
|
1016
|
+
if (!existsSync(targetPath)) {
|
|
1017
|
+
console.error(c('red', `Error: Path does not exist: ${targetPath}`));
|
|
1018
|
+
process.exit(1);
|
|
1019
|
+
}
|
|
1020
|
+
console.log('');
|
|
1021
|
+
console.log(c('cyan', '╔═══════════════════════════════════════════════════════════════╗'));
|
|
1022
|
+
console.log(c('cyan', '║') + c('bold', ' AURASECURITY - License Compliance ') + c('cyan', '║'));
|
|
1023
|
+
console.log(c('cyan', '╚═══════════════════════════════════════════════════════════════╝'));
|
|
1024
|
+
console.log('');
|
|
1025
|
+
console.log(c('dim', `Target: ${targetPath}`));
|
|
1026
|
+
console.log(c('dim', `Policy: ${policy}`));
|
|
1027
|
+
console.log('');
|
|
1028
|
+
console.log(c('yellow', '⏳ Checking licenses...'));
|
|
1029
|
+
console.log('');
|
|
1030
|
+
const result = checkLicenses(targetPath, {
|
|
1031
|
+
policy,
|
|
1032
|
+
allowedLicenses: allowedLicenses.length > 0 ? allowedLicenses : undefined,
|
|
1033
|
+
deniedLicenses: deniedLicenses.length > 0 ? deniedLicenses : undefined
|
|
1034
|
+
});
|
|
1035
|
+
if (outputJson) {
|
|
1036
|
+
const output = JSON.stringify(result, null, 2);
|
|
1037
|
+
if (outputFile) {
|
|
1038
|
+
writeFileSync(resolve(outputFile), output);
|
|
1039
|
+
console.log(c('green', `✓ Results saved to: ${outputFile}`));
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
console.log(output);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
else {
|
|
1046
|
+
// Display results
|
|
1047
|
+
console.log('┌─────────────────────────────────────────────────────────────────┐');
|
|
1048
|
+
console.log(`│ Total: ${result.summary.total} ${c('green', `Compliant: ${result.summary.compliant}`)} ${c('red', `Violations: ${result.summary.violations}`)} ${c('yellow', `Unknown: ${result.summary.unknown}`)} │`);
|
|
1049
|
+
console.log('└─────────────────────────────────────────────────────────────────┘');
|
|
1050
|
+
console.log('');
|
|
1051
|
+
if (result.violations.length > 0) {
|
|
1052
|
+
console.log(c('red', `► License Violations (${result.violations.length}):`));
|
|
1053
|
+
for (const v of result.violations.slice(0, 15)) {
|
|
1054
|
+
const sev = v.severity === 'high' ? c('red', ' HIGH ') :
|
|
1055
|
+
v.severity === 'medium' ? c('yellow', ' MED ') : c('dim', ' LOW ');
|
|
1056
|
+
console.log(` ${sev} ${v.package}@${v.version}`);
|
|
1057
|
+
console.log(` License: ${v.license}`);
|
|
1058
|
+
console.log(` ${c('dim', v.violation)}`);
|
|
1059
|
+
}
|
|
1060
|
+
if (result.violations.length > 15) {
|
|
1061
|
+
console.log(c('dim', ` ... and ${result.violations.length - 15} more`));
|
|
1062
|
+
}
|
|
1063
|
+
console.log('');
|
|
1064
|
+
}
|
|
1065
|
+
// Show some compliant licenses
|
|
1066
|
+
const compliant = result.licenses.filter(l => !result.violations.some(v => v.package === l.package && v.version === l.version));
|
|
1067
|
+
if (compliant.length > 0) {
|
|
1068
|
+
console.log(c('green', `► Compliant Packages (${compliant.length}):`));
|
|
1069
|
+
const licenseCounts = new Map();
|
|
1070
|
+
for (const l of compliant) {
|
|
1071
|
+
licenseCounts.set(l.license, (licenseCounts.get(l.license) || 0) + 1);
|
|
1072
|
+
}
|
|
1073
|
+
const sorted = [...licenseCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
1074
|
+
for (const [license, count] of sorted.slice(0, 10)) {
|
|
1075
|
+
console.log(` ${license}: ${count} packages`);
|
|
1076
|
+
}
|
|
1077
|
+
console.log('');
|
|
1078
|
+
}
|
|
1079
|
+
if (outputFile) {
|
|
1080
|
+
writeFileSync(resolve(outputFile), JSON.stringify(result, null, 2));
|
|
1081
|
+
console.log(c('green', `✓ Results saved to: ${outputFile}`));
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
// Exit with error if violations found
|
|
1085
|
+
const highViolations = result.violations.filter(v => v.severity === 'high').length;
|
|
1086
|
+
if (highViolations > 0) {
|
|
1087
|
+
process.exit(1);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
function displayAWSResults(result) {
|
|
1091
|
+
const { summary, findings, scannedServices, errors } = result;
|
|
1092
|
+
// Stats bar
|
|
1093
|
+
console.log('┌─────────────────────────────────────────────────────────────────┐');
|
|
1094
|
+
console.log(`│ ${c('bgRed', ` CRITICAL ${summary.critical} `)} ${c('red', `HIGH ${summary.high}`)} ${c('yellow', `MEDIUM ${summary.medium}`)} ${c('dim', `LOW ${summary.low}`)} INFO ${summary.info} │`);
|
|
1095
|
+
console.log('└─────────────────────────────────────────────────────────────────┘');
|
|
1096
|
+
console.log('');
|
|
1097
|
+
// Scanned services
|
|
1098
|
+
console.log(c('cyan', '► Services Scanned:'));
|
|
1099
|
+
console.log(` ${scannedServices.join(', ') || 'None'}`);
|
|
1100
|
+
console.log('');
|
|
1101
|
+
// Errors
|
|
1102
|
+
if (errors.length > 0) {
|
|
1103
|
+
console.log(c('red', '► Scan Errors:'));
|
|
1104
|
+
for (const err of errors) {
|
|
1105
|
+
console.log(` [${err.service}] ${err.error}`);
|
|
1106
|
+
}
|
|
1107
|
+
console.log('');
|
|
1108
|
+
}
|
|
1109
|
+
// Group findings by service
|
|
1110
|
+
const byService = new Map();
|
|
1111
|
+
for (const finding of findings) {
|
|
1112
|
+
const existing = byService.get(finding.service) || [];
|
|
1113
|
+
existing.push(finding);
|
|
1114
|
+
byService.set(finding.service, existing);
|
|
1115
|
+
}
|
|
1116
|
+
// Display findings by service
|
|
1117
|
+
for (const [service, serviceFindings] of byService) {
|
|
1118
|
+
const serviceUpper = service.toUpperCase();
|
|
1119
|
+
const serviceColor = service === 'iam' ? 'green' :
|
|
1120
|
+
service === 's3' ? 'blue' :
|
|
1121
|
+
service === 'ec2' ? 'yellow' :
|
|
1122
|
+
service === 'lambda' ? 'magenta' :
|
|
1123
|
+
service === 'rds' ? 'cyan' : 'white';
|
|
1124
|
+
console.log(c(serviceColor, `► ${serviceUpper} Findings (${serviceFindings.length}):`));
|
|
1125
|
+
for (const f of serviceFindings.slice(0, 10)) {
|
|
1126
|
+
const sev = f.severity === 'critical' ? c('bgRed', ' CRIT ') :
|
|
1127
|
+
f.severity === 'high' ? c('red', ' HIGH ') :
|
|
1128
|
+
f.severity === 'medium' ? c('yellow', ' MED ') :
|
|
1129
|
+
f.severity === 'low' ? c('dim', ' LOW ') : c('dim', ' INFO ');
|
|
1130
|
+
console.log(` ${sev} ${f.title}`);
|
|
1131
|
+
console.log(` ${c('dim', f.resourceType)}: ${f.resourceId}`);
|
|
1132
|
+
if (f.remediation) {
|
|
1133
|
+
console.log(` ${c('green', 'Fix:')} ${f.remediation}`);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
if (serviceFindings.length > 10) {
|
|
1137
|
+
console.log(c('dim', ` ... and ${serviceFindings.length - 10} more`));
|
|
1138
|
+
}
|
|
1139
|
+
console.log('');
|
|
1140
|
+
}
|
|
1141
|
+
// Summary
|
|
1142
|
+
if (summary.total === 0) {
|
|
1143
|
+
console.log(c('green', '✓ No security issues found in AWS!'));
|
|
1144
|
+
}
|
|
1145
|
+
else {
|
|
1146
|
+
console.log('─'.repeat(67));
|
|
1147
|
+
console.log(`Total AWS findings: ${summary.total}`);
|
|
1148
|
+
if (summary.critical > 0) {
|
|
1149
|
+
console.log(c('red', `⚠ ${summary.critical} critical issues require immediate attention!`));
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
// ============ SERVER COMMANDS ============
|
|
1154
|
+
async function startServer() {
|
|
1155
|
+
console.log(c('cyan', '🚀 Starting aurasecurity server...'));
|
|
1156
|
+
console.log('');
|
|
1157
|
+
// Dynamically import index.js which auto-starts the server
|
|
1158
|
+
try {
|
|
1159
|
+
await import('./index.js');
|
|
1160
|
+
// The server runs until stopped via SIGINT/SIGTERM
|
|
1161
|
+
}
|
|
1162
|
+
catch (err) {
|
|
1163
|
+
console.error(c('red', 'Failed to start server:'), err);
|
|
1164
|
+
process.exit(1);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
async function startVisualizer() {
|
|
1168
|
+
console.log(c('cyan', '🎮 Starting 3D Visualizer...'));
|
|
1169
|
+
console.log('');
|
|
1170
|
+
try {
|
|
1171
|
+
// Dynamically import serve-visualizer.js which auto-starts
|
|
1172
|
+
await import('./serve-visualizer.js');
|
|
1173
|
+
}
|
|
1174
|
+
catch (err) {
|
|
1175
|
+
console.error(c('red', 'Failed to start visualizer:'), err);
|
|
1176
|
+
process.exit(1);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
async function showStatus() {
|
|
1180
|
+
try {
|
|
1181
|
+
const res = await fetch(`${AURA_URL}/info`);
|
|
1182
|
+
const info = await res.json();
|
|
1183
|
+
console.log('');
|
|
1184
|
+
console.log(c('cyan', '╔═══════════════════════════════════════╗'));
|
|
1185
|
+
console.log(c('cyan', '║') + c('bold', ' AURASECURITY STATUS ') + c('cyan', '║'));
|
|
1186
|
+
console.log(c('cyan', '╚═══════════════════════════════════════╝'));
|
|
1187
|
+
console.log('');
|
|
1188
|
+
console.log(` Server: ${c('green', AURA_URL)}`);
|
|
1189
|
+
console.log(` Name: ${info.name}`);
|
|
1190
|
+
console.log(` Version: ${info.version}`);
|
|
1191
|
+
console.log(` Tools: ${info.tools.join(', ')}`);
|
|
1192
|
+
console.log(` Endpoints: ${info.endpoints.join(', ')}`);
|
|
1193
|
+
console.log('');
|
|
1194
|
+
}
|
|
1195
|
+
catch (err) {
|
|
1196
|
+
console.error(c('red', '✗ Cannot connect to Aura server at'), AURA_URL);
|
|
1197
|
+
console.log('');
|
|
1198
|
+
console.log(' Is the server running? Start it with:');
|
|
1199
|
+
console.log(c('cyan', ' aura-security serve'));
|
|
1200
|
+
console.log('');
|
|
1201
|
+
process.exit(1);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
async function runAudit(args) {
|
|
1205
|
+
const inputFile = args[0];
|
|
1206
|
+
if (!inputFile) {
|
|
1207
|
+
// Run interactive demo
|
|
1208
|
+
const demoInput = {
|
|
1209
|
+
tool: 'audit',
|
|
1210
|
+
arguments: {
|
|
1211
|
+
change_event: {
|
|
1212
|
+
id: 'demo-' + Date.now(),
|
|
1213
|
+
type: 'pull_request',
|
|
1214
|
+
environment: 'staging',
|
|
1215
|
+
repo: 'demo/app',
|
|
1216
|
+
commit: 'a'.repeat(40),
|
|
1217
|
+
files_changed: ['src/auth/login.ts'],
|
|
1218
|
+
diff: '+const API_KEY = "secret123";'
|
|
1219
|
+
},
|
|
1220
|
+
evidence_bundle: {
|
|
1221
|
+
vuln_scan: 'critical: 1\nhigh: 2'
|
|
1222
|
+
},
|
|
1223
|
+
policy_context: {
|
|
1224
|
+
critical_assets: ['auth'],
|
|
1225
|
+
risk_tolerance: 'low'
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
console.log('');
|
|
1230
|
+
console.log(c('yellow', '🔍 Running demo audit...'));
|
|
1231
|
+
console.log('');
|
|
1232
|
+
try {
|
|
1233
|
+
const res = await fetch(`${AURA_URL}/tools`, {
|
|
1234
|
+
method: 'POST',
|
|
1235
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1236
|
+
body: JSON.stringify(demoInput)
|
|
1237
|
+
});
|
|
1238
|
+
const data = await res.json();
|
|
1239
|
+
const output = data.result;
|
|
1240
|
+
console.log(visualize(output));
|
|
1241
|
+
console.log('');
|
|
1242
|
+
console.log(visualizeState(output.agent_state));
|
|
1243
|
+
console.log('');
|
|
1244
|
+
console.log(visualizeCompact(output));
|
|
1245
|
+
}
|
|
1246
|
+
catch (err) {
|
|
1247
|
+
console.error(c('red', '✗ Server not reachable. Start it with: aura-security serve'));
|
|
1248
|
+
process.exit(1);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
else {
|
|
1252
|
+
// Load from file
|
|
1253
|
+
const { readFileSync } = await import('fs');
|
|
1254
|
+
const input = JSON.parse(readFileSync(inputFile, 'utf-8'));
|
|
1255
|
+
const payload = input.tool ? input : { tool: 'audit', arguments: input };
|
|
1256
|
+
const res = await fetch(`${AURA_URL}/tools`, {
|
|
1257
|
+
method: 'POST',
|
|
1258
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1259
|
+
body: JSON.stringify(payload)
|
|
1260
|
+
});
|
|
1261
|
+
const data = await res.json();
|
|
1262
|
+
const output = data.result;
|
|
1263
|
+
console.log(visualize(output));
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
async function showLogs() {
|
|
1267
|
+
try {
|
|
1268
|
+
const res = await fetch(`${AURA_URL}/memory`);
|
|
1269
|
+
const data = await res.json();
|
|
1270
|
+
console.log('');
|
|
1271
|
+
console.log(c('cyan', '╔═══════════════════════════════════════╗'));
|
|
1272
|
+
console.log(c('cyan', '║') + c('bold', ' AUDIT LOG ENTRIES ') + c('cyan', '║'));
|
|
1273
|
+
console.log(c('cyan', '╚═══════════════════════════════════════╝'));
|
|
1274
|
+
console.log('');
|
|
1275
|
+
if (data.keys.length === 0) {
|
|
1276
|
+
console.log(' No audit logs found.');
|
|
1277
|
+
}
|
|
1278
|
+
else {
|
|
1279
|
+
for (const key of data.keys) {
|
|
1280
|
+
const parts = key.split(':');
|
|
1281
|
+
console.log(` • ${parts[1]} (${new Date(parseInt(parts[2])).toISOString()})`);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
console.log('');
|
|
1285
|
+
}
|
|
1286
|
+
catch {
|
|
1287
|
+
console.error(c('red', '✗ Server not reachable. Start it with: aura-security serve'));
|
|
1288
|
+
process.exit(1);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
async function watchMode() {
|
|
1292
|
+
console.log('');
|
|
1293
|
+
console.log(c('cyan', '👁️ aurasecurity Watch Mode'));
|
|
1294
|
+
console.log(c('dim', ' Monitoring for new audits... (Ctrl+C to exit)'));
|
|
1295
|
+
console.log('');
|
|
1296
|
+
let lastCount = 0;
|
|
1297
|
+
setInterval(async () => {
|
|
1298
|
+
try {
|
|
1299
|
+
const res = await fetch(`${AURA_URL}/memory`);
|
|
1300
|
+
const data = await res.json();
|
|
1301
|
+
if (data.keys.length > lastCount) {
|
|
1302
|
+
const newKeys = data.keys.slice(lastCount);
|
|
1303
|
+
for (const key of newKeys) {
|
|
1304
|
+
const entryRes = await fetch(`${AURA_URL}/memory?key=${encodeURIComponent(key)}`);
|
|
1305
|
+
const entry = await entryRes.json();
|
|
1306
|
+
if (entry.value) {
|
|
1307
|
+
console.log('');
|
|
1308
|
+
console.log(c('green', '━━━ NEW AUDIT ━━━'));
|
|
1309
|
+
console.log(visualizeCompact(entry.value));
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
lastCount = data.keys.length;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
catch {
|
|
1316
|
+
// Ignore errors in watch mode
|
|
1317
|
+
}
|
|
1318
|
+
}, 2000);
|
|
1319
|
+
}
|
|
1320
|
+
function showHelp() {
|
|
1321
|
+
console.log(`
|
|
1322
|
+
${c('cyan', 'aurasecurity')} - Security Scanner & Audit Pipeline
|
|
1323
|
+
${c('dim', `Version ${VERSION}`)}
|
|
1324
|
+
|
|
1325
|
+
${c('bold', 'USAGE:')}
|
|
1326
|
+
aura-security <command> [options]
|
|
1327
|
+
|
|
1328
|
+
${c('bold', 'COMMANDS:')}
|
|
1329
|
+
${c('green', 'doctor')} Check installed security tools and show install commands
|
|
1330
|
+
${c('green', 'init')} [path] Initialize config in a project directory
|
|
1331
|
+
${c('green', 'scan')} <path> Scan a directory for security issues (standalone)
|
|
1332
|
+
${c('green', 'sbom')} <path> Generate Software Bill of Materials
|
|
1333
|
+
${c('green', 'license-check')} <path> Check license compliance
|
|
1334
|
+
${c('green', 'aws')} Scan AWS infrastructure for security issues
|
|
1335
|
+
${c('green', 'serve')} Start the Aura server (port 3000)
|
|
1336
|
+
${c('green', 'visualizer')} Start the 3D visualizer (port 8080)
|
|
1337
|
+
${c('green', 'status')} Show server connection status
|
|
1338
|
+
${c('green', 'audit')} [file] Run audit via server (demo if no file)
|
|
1339
|
+
${c('green', 'logs')} Show audit log entries from server
|
|
1340
|
+
${c('green', 'watch')} Watch for new audits in real-time
|
|
1341
|
+
|
|
1342
|
+
${c('bold', 'SCAN OPTIONS:')}
|
|
1343
|
+
-j, --json Output results as JSON
|
|
1344
|
+
-f, --format Output format: console, json, sarif, junit, gitlab-sast, gitlab-deps, summary
|
|
1345
|
+
-o, --output Save results to file
|
|
1346
|
+
-l, --languages Filter by languages: js,py,go,rust,ruby,php,java
|
|
1347
|
+
-S, --scanners Use specific scanners: grype,trivy,gitleaks,semgrep,checkov,hadolint,pip-audit,govulncheck,cargo-audit,bundle-audit,composer
|
|
1348
|
+
--fail-on Exit with error on severity: critical,high,medium (default: critical)
|
|
1349
|
+
--no-fail Never exit with error code
|
|
1350
|
+
|
|
1351
|
+
${c('bold', 'SBOM OPTIONS:')}
|
|
1352
|
+
-f, --format SBOM format: cyclonedx (default), spdx
|
|
1353
|
+
-o, --output Save SBOM to file
|
|
1354
|
+
--include-vulns Include vulnerability information
|
|
1355
|
+
--name Project name for SBOM metadata
|
|
1356
|
+
|
|
1357
|
+
${c('bold', 'LICENSE OPTIONS:')}
|
|
1358
|
+
-p, --policy License policy: permissive (default), strict, copyleft
|
|
1359
|
+
--allow Additional licenses to allow (comma-separated)
|
|
1360
|
+
--deny Additional licenses to deny (comma-separated)
|
|
1361
|
+
--json Output as JSON
|
|
1362
|
+
-o, --output Save results to file
|
|
1363
|
+
|
|
1364
|
+
${c('bold', 'AWS OPTIONS:')}
|
|
1365
|
+
-r, --region AWS region (default: us-east-1)
|
|
1366
|
+
-p, --profile AWS profile name
|
|
1367
|
+
-s, --services Services to scan: iam,s3,ec2,lambda,rds
|
|
1368
|
+
|
|
1369
|
+
${c('bold', 'EXAMPLES:')}
|
|
1370
|
+
${c('dim', '# Initialize config in current directory')}
|
|
1371
|
+
aura-security init
|
|
1372
|
+
|
|
1373
|
+
${c('dim', '# Scan current directory')}
|
|
1374
|
+
aura-security scan .
|
|
1375
|
+
|
|
1376
|
+
${c('dim', '# Scan with SARIF output (GitHub Code Scanning)')}
|
|
1377
|
+
aura-security scan . --format sarif -o results.sarif
|
|
1378
|
+
|
|
1379
|
+
${c('dim', '# Scan with GitLab security report format')}
|
|
1380
|
+
aura-security scan . --format gitlab-sast -o gl-sast-report.json
|
|
1381
|
+
|
|
1382
|
+
${c('dim', '# Scan only Python projects')}
|
|
1383
|
+
aura-security scan . --languages py
|
|
1384
|
+
|
|
1385
|
+
${c('dim', '# Use specific scanners')}
|
|
1386
|
+
aura-security scan . --scanners grype,checkov,hadolint
|
|
1387
|
+
|
|
1388
|
+
${c('dim', '# Generate CycloneDX SBOM')}
|
|
1389
|
+
aura-security sbom . --format cyclonedx -o sbom.json
|
|
1390
|
+
|
|
1391
|
+
${c('dim', '# Generate SPDX SBOM with vulnerabilities')}
|
|
1392
|
+
aura-security sbom . --format spdx --include-vulns -o sbom-spdx.json
|
|
1393
|
+
|
|
1394
|
+
${c('dim', '# Check license compliance')}
|
|
1395
|
+
aura-security license-check . --policy strict
|
|
1396
|
+
|
|
1397
|
+
${c('dim', '# License check with custom allowed licenses')}
|
|
1398
|
+
aura-security license-check . --allow "MPL-2.0,LGPL-3.0"
|
|
1399
|
+
|
|
1400
|
+
${c('dim', '# Scan AWS infrastructure')}
|
|
1401
|
+
aura-security aws --region us-west-2
|
|
1402
|
+
|
|
1403
|
+
${c('dim', '# Start full stack (2 terminals)')}
|
|
1404
|
+
aura-security serve ${c('dim', '# Terminal 1')}
|
|
1405
|
+
aura-security visualizer ${c('dim', '# Terminal 2')}
|
|
1406
|
+
|
|
1407
|
+
${c('bold', 'CI/CD INTEGRATION:')}
|
|
1408
|
+
${c('dim', 'See templates/ci/ for GitHub Actions and GitLab CI examples')}
|
|
1409
|
+
|
|
1410
|
+
${c('bold', 'OUTPUT FORMATS:')}
|
|
1411
|
+
console Interactive terminal output (default)
|
|
1412
|
+
json Full JSON results
|
|
1413
|
+
sarif SARIF 2.1.0 for GitHub Code Scanning, VS Code
|
|
1414
|
+
junit JUnit XML for CI test reporters
|
|
1415
|
+
gitlab-sast GitLab SAST security report
|
|
1416
|
+
gitlab-deps GitLab Dependency Scanning report
|
|
1417
|
+
summary Compact JSON summary
|
|
1418
|
+
|
|
1419
|
+
${c('bold', 'ENVIRONMENT:')}
|
|
1420
|
+
AURA_URL Server URL (default: http://127.0.0.1:3000)
|
|
1421
|
+
|
|
1422
|
+
${c('bold', 'EXIT CODES:')}
|
|
1423
|
+
0 Clean - no issues found
|
|
1424
|
+
1 High severity issues found
|
|
1425
|
+
2 Critical severity issues found
|
|
1426
|
+
|
|
1427
|
+
${c('dim', 'For more info: https://github.com/aurasecurity/aura-security')}
|
|
1428
|
+
`);
|
|
1429
|
+
}
|
|
1430
|
+
main().catch((err) => {
|
|
1431
|
+
console.error(c('red', 'Error:'), err.message || err);
|
|
1432
|
+
process.exit(1);
|
|
1433
|
+
});
|