aura-security 1.0.0 → 1.0.2

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.
@@ -100,6 +100,7 @@ export declare class ChainMapperAgent extends SLOPAgent {
100
100
  private buildPath;
101
101
  private findingToNode;
102
102
  private canConnect;
103
+ private categorizeType;
103
104
  private getConnectionAction;
104
105
  private calculateProbability;
105
106
  private severityScore;
@@ -12,7 +12,8 @@
12
12
  * - analyze-blast-radius: Calculate impact radius of a finding
13
13
  */
14
14
  import { SLOPAgent } from './base.js';
15
- // MITRE ATT&CK mapping for common finding types
15
+ import { normalizeFinding, } from './types.js';
16
+ // MITRE ATT&CK mapping for finding categories
16
17
  const MITRE_MAPPING = {
17
18
  'secret': [
18
19
  { technique: 'T1552.001', tactic: 'Credential Access - Credentials In Files' },
@@ -26,6 +27,16 @@ const MITRE_MAPPING = {
26
27
  { technique: 'T1059', tactic: 'Execution - Command and Scripting Interpreter' },
27
28
  { technique: 'T1055', tactic: 'Defense Evasion - Process Injection' },
28
29
  ],
30
+ 'injection': [
31
+ { technique: 'T1190', tactic: 'Initial Access - Exploit Public-Facing Application' },
32
+ { technique: 'T1059', tactic: 'Execution - Command and Scripting Interpreter' },
33
+ { technique: 'T1027', tactic: 'Defense Evasion - Obfuscated Files' },
34
+ ],
35
+ 'auth': [
36
+ { technique: 'T1078', tactic: 'Persistence - Valid Accounts' },
37
+ { technique: 'T1110', tactic: 'Credential Access - Brute Force' },
38
+ { technique: 'T1548', tactic: 'Privilege Escalation - Abuse Elevation Control' },
39
+ ],
29
40
  'docker': [
30
41
  { technique: 'T1611', tactic: 'Privilege Escalation - Escape to Host' },
31
42
  { technique: 'T1610', tactic: 'Execution - Deploy Container' },
@@ -34,6 +45,15 @@ const MITRE_MAPPING = {
34
45
  { technique: 'T1538', tactic: 'Discovery - Cloud Service Dashboard' },
35
46
  { technique: 'T1580', tactic: 'Discovery - Cloud Infrastructure Discovery' },
36
47
  ],
48
+ 'escalation': [
49
+ { technique: 'T1548', tactic: 'Privilege Escalation - Abuse Elevation Control' },
50
+ { technique: 'T1068', tactic: 'Privilege Escalation - Exploitation for Privilege Escalation' },
51
+ ],
52
+ 'data': [
53
+ { technique: 'T1005', tactic: 'Collection - Data from Local System' },
54
+ { technique: 'T1039', tactic: 'Collection - Data from Network Shared Drive' },
55
+ { technique: 'T1567', tactic: 'Exfiltration - Exfiltration Over Web Service' },
56
+ ],
37
57
  };
38
58
  // Attack chain templates
39
59
  const CHAIN_TEMPLATES = [
@@ -163,20 +183,22 @@ export class ChainMapperAgent extends SLOPAgent {
163
183
  }
164
184
  async handleToolCall(call) {
165
185
  const { tool, arguments: args } = call;
186
+ // Normalize external findings to internal format
187
+ const normalizeFindings = (findings) => (findings || []).map(normalizeFinding);
166
188
  try {
167
189
  switch (tool) {
168
190
  case 'trace-path':
169
- return { result: await this.tracePath(args.findings, args.entryPoint, args.maxDepth) };
191
+ return { result: await this.tracePath(normalizeFindings(args.findings), args.entryPoint, args.maxDepth) };
170
192
  case 'find-pivots':
171
- return { result: await this.findPivots(args.findings) };
193
+ return { result: await this.findPivots(normalizeFindings(args.findings)) };
172
194
  case 'simulate-attack':
173
195
  return { result: await this.simulateAttack(args.path, args.verbose) };
174
196
  case 'generate-report':
175
- return { result: await this.generateReport(args.findings, args.format) };
197
+ return { result: await this.generateReport(normalizeFindings(args.findings), args.format) };
176
198
  case 'analyze-blast-radius':
177
- return { result: await this.analyzeBlastRadius(args.finding, args.context) };
199
+ return { result: await this.analyzeBlastRadius(normalizeFinding(args.finding), args.context) };
178
200
  case 'map-mitre':
179
- return { result: await this.mapToMitre(args.findings) };
201
+ return { result: await this.mapToMitre(normalizeFindings(args.findings)) };
180
202
  default:
181
203
  return { error: `Unknown tool: ${tool}` };
182
204
  }
@@ -193,8 +215,13 @@ export class ChainMapperAgent extends SLOPAgent {
193
215
  // Find entry points (secrets and high-severity vulns are common entries)
194
216
  const entryPoints = entryPoint
195
217
  ? findings.filter(f => f.id === entryPoint)
196
- : findings.filter(f => f.type === 'secret' ||
197
- (f.type === 'vulnerability' && f.severity === 'critical'));
218
+ : findings.filter(f => {
219
+ const category = this.categorizeType(f.type);
220
+ return category === 'secret' ||
221
+ category === 'injection' ||
222
+ (category === 'vulnerability' && f.severity === 'critical') ||
223
+ f.severity === 'critical';
224
+ });
198
225
  if (entryPoints.length === 0 && findings.length > 0) {
199
226
  // Use highest severity as entry
200
227
  entryPoints.push(findings.sort((a, b) => this.severityScore(b.severity) - this.severityScore(a.severity))[0]);
@@ -388,7 +415,8 @@ export class ChainMapperAgent extends SLOPAgent {
388
415
  const allTechniques = new Set();
389
416
  const allTactics = new Set();
390
417
  for (const finding of findings) {
391
- const techniques = MITRE_MAPPING[finding.type] || [];
418
+ const category = this.categorizeType(finding.type);
419
+ const techniques = MITRE_MAPPING[category] || [];
392
420
  mappings.push({ finding, techniques });
393
421
  for (const t of techniques) {
394
422
  allTechniques.add(t.technique);
@@ -445,27 +473,83 @@ export class ChainMapperAgent extends SLOPAgent {
445
473
  };
446
474
  }
447
475
  findingToNode(finding, type) {
476
+ const category = this.categorizeType(finding.type);
448
477
  return {
449
- id: finding.id,
478
+ id: finding.id || `finding-${Math.random().toString(36).slice(2, 9)}`,
450
479
  findingId: finding.id,
451
480
  type,
452
- name: finding.title,
453
- description: finding.description,
454
- technique: MITRE_MAPPING[finding.type]?.[0]?.technique,
455
- severity: finding.severity,
456
- exploitability: this.severityScore(finding.severity) * 10,
481
+ name: finding.title || finding.type || 'Unknown Finding',
482
+ description: finding.description || finding.message || '',
483
+ technique: MITRE_MAPPING[category]?.[0]?.technique,
484
+ severity: finding.severity || 'medium',
485
+ exploitability: this.severityScore(finding.severity || 'medium') * 10,
486
+ metadata: { originalType: finding.type, category },
457
487
  };
458
488
  }
459
489
  canConnect(from, to) {
460
- // Define connection rules
490
+ const fromCategory = this.categorizeType(from.type);
491
+ const toCategory = this.categorizeType(to.type);
492
+ // Define connection rules between categories
461
493
  const connections = {
462
- 'secret': ['vulnerability', 'code-issue', 'iac'],
463
- 'vulnerability': ['code-issue', 'docker', 'secret'],
464
- 'code-issue': ['vulnerability', 'secret'],
465
- 'docker': ['vulnerability', 'iac'],
466
- 'iac': ['secret', 'vulnerability'],
494
+ 'secret': ['vulnerability', 'code-issue', 'iac', 'auth', 'injection'],
495
+ 'vulnerability': ['code-issue', 'docker', 'secret', 'escalation', 'data'],
496
+ 'code-issue': ['vulnerability', 'secret', 'injection', 'data'],
497
+ 'injection': ['data', 'auth', 'escalation', 'code-issue'],
498
+ 'auth': ['data', 'escalation', 'secret'],
499
+ 'docker': ['vulnerability', 'iac', 'escalation'],
500
+ 'iac': ['secret', 'vulnerability', 'escalation'],
501
+ 'escalation': ['data', 'secret', 'vulnerability'],
502
+ 'data': ['secret'],
467
503
  };
468
- return connections[from.type]?.includes(to.type) || false;
504
+ return connections[fromCategory]?.includes(toCategory) || false;
505
+ }
506
+ categorizeType(type) {
507
+ const t = type.toLowerCase();
508
+ // Secrets
509
+ if (t.includes('secret') || t.includes('credential') || t.includes('key') ||
510
+ t.includes('token') || t.includes('password') || t.includes('api-key') ||
511
+ t.includes('aws') || t.includes('private-key')) {
512
+ return 'secret';
513
+ }
514
+ // Injection vulnerabilities
515
+ if (t.includes('injection') || t.includes('sqli') || t.includes('xss') ||
516
+ t.includes('xxe') || t.includes('ssti') || t.includes('command-injection') ||
517
+ t.includes('code-injection') || t.includes('rce') || t.includes('eval')) {
518
+ return 'injection';
519
+ }
520
+ // Authentication/Authorization issues
521
+ if (t.includes('auth') || t.includes('bypass') || t.includes('broken-access') ||
522
+ t.includes('idor') || t.includes('privilege') || t.includes('session') ||
523
+ t.includes('jwt') || t.includes('csrf')) {
524
+ return 'auth';
525
+ }
526
+ // Docker/Container issues
527
+ if (t.includes('docker') || t.includes('container') || t.includes('kubernetes') ||
528
+ t.includes('k8s') || t.includes('helm')) {
529
+ return 'docker';
530
+ }
531
+ // Infrastructure as Code
532
+ if (t.includes('iac') || t.includes('terraform') || t.includes('cloudformation') ||
533
+ t.includes('misconfiguration') || t.includes('s3') || t.includes('cloud')) {
534
+ return 'iac';
535
+ }
536
+ // Privilege escalation
537
+ if (t.includes('escalation') || t.includes('privesc') || t.includes('root') ||
538
+ t.includes('admin') || t.includes('sudo')) {
539
+ return 'escalation';
540
+ }
541
+ // Data exposure
542
+ if (t.includes('data') || t.includes('leak') || t.includes('exposure') ||
543
+ t.includes('sensitive') || t.includes('pii') || t.includes('ssrf')) {
544
+ return 'data';
545
+ }
546
+ // Vulnerabilities (CVEs, dependencies)
547
+ if (t.includes('vuln') || t.includes('cve') || t.includes('dependency') ||
548
+ t.includes('outdated') || t.includes('package')) {
549
+ return 'vulnerability';
550
+ }
551
+ // Default to code-issue for SAST findings
552
+ return 'code-issue';
469
553
  }
470
554
  getConnectionAction(from, to) {
471
555
  if (from.type === 'secret' && to.type === 'vulnerability') {
@@ -218,22 +218,29 @@ export class RedTeamAgent extends SLOPAgent {
218
218
  let falsePositive = false;
219
219
  let riskLevel = 'needs-verification';
220
220
  logs.push(`[RedTeam] Starting validation for ${finding.id}: ${finding.title}`);
221
+ // Normalize finding type (handle different naming conventions)
222
+ const normalizedType = (finding.type || '').toLowerCase();
223
+ const isSecret = normalizedType === 'secret' || normalizedType === 'secrets';
224
+ const isVuln = normalizedType === 'vulnerability' || normalizedType === 'vuln' || normalizedType === 'package';
225
+ const isCode = normalizedType === 'code-issue' || normalizedType === 'code' || normalizedType === 'sast';
226
+ const isIac = normalizedType === 'iac' || normalizedType === 'infrastructure';
227
+ const isDocker = normalizedType === 'docker' || normalizedType === 'container';
221
228
  // Validate based on finding type
222
- if (finding.type === 'secret') {
229
+ if (isSecret) {
223
230
  const secretResult = await this.analyzeSecret(finding);
224
231
  exploitable = secretResult.likelyValid;
225
232
  confidence = secretResult.confidence;
226
233
  evidence.push(...secretResult.evidence);
227
234
  logs.push(...secretResult.logs);
228
235
  }
229
- else if (finding.type === 'vulnerability') {
236
+ else if (isVuln) {
230
237
  const vulnResult = await this.analyzeVulnerability(finding, aggressive);
231
238
  exploitable = vulnResult.likelyExploitable;
232
239
  confidence = vulnResult.confidence;
233
240
  evidence.push(...vulnResult.evidence);
234
241
  logs.push(...vulnResult.logs);
235
242
  }
236
- else if (finding.type === 'code-issue') {
243
+ else if (isCode || isIac || isDocker) {
237
244
  const codeResult = await this.analyzeCodeIssue(finding);
238
245
  exploitable = codeResult.likelyExploitable;
239
246
  confidence = codeResult.confidence;
@@ -242,7 +249,7 @@ export class RedTeamAgent extends SLOPAgent {
242
249
  }
243
250
  else {
244
251
  confidence = 50;
245
- logs.push(`[RedTeam] No specific validation for ${finding.type}, using heuristics`);
252
+ logs.push(`[RedTeam] No specific validation for ${finding.type} (normalized: ${normalizedType}), using heuristics`);
246
253
  }
247
254
  // Determine risk level
248
255
  if (exploitable && confidence >= 80) {
@@ -176,6 +176,24 @@ export class ScannerAgent extends SLOPAgent {
176
176
  async runGitleaks(target) {
177
177
  const findings = [];
178
178
  const tempFile = `/tmp/gitleaks-${Date.now()}.json`;
179
+ // Patterns to exclude (false positives)
180
+ const excludePatterns = [
181
+ /\.secrets\.baseline$/, // detect-secrets baseline files
182
+ /\.test\.[jt]sx?$/, // Test files (.test.ts, .test.js, .test.tsx, .test.jsx)
183
+ /\.spec\.[jt]sx?$/, // Spec files (.spec.ts, .spec.js)
184
+ /test\.py$/, // Python test files
185
+ /_test\.go$/, // Go test files
186
+ /\.fuzz\.test\.[jt]s$/, // Fuzz test files
187
+ /\/tests?\//i, // Files in test directories
188
+ /\/fixtures?\//i, // Test fixtures
189
+ /\/mocks?\//i, // Mock files
190
+ ];
191
+ // Rule + path combinations to exclude (documentation examples)
192
+ const excludeRulePaths = [
193
+ { rule: /curl-auth-header/, path: /\.md$/ }, // curl examples in docs
194
+ { rule: /generic-api-key/, path: /\.md$/ }, // API key examples in docs
195
+ { rule: /generic-api-key/, path: /\.example$/ }, // Example files
196
+ ];
179
197
  try {
180
198
  // Gitleaks exits with code 1 when it finds leaks - that's not an error!
181
199
  const result = await this.executeCommand('gitleaks', ['detect', '--source', target, '--report-format', 'json', '--report-path', tempFile, '--no-git']);
@@ -187,7 +205,20 @@ export class ScannerAgent extends SLOPAgent {
187
205
  if (jsonContent && jsonContent.trim()) {
188
206
  const gitleaksFindings = JSON.parse(jsonContent);
189
207
  if (Array.isArray(gitleaksFindings)) {
208
+ let skipped = 0;
190
209
  for (const f of gitleaksFindings) {
210
+ const filePath = f.File || '';
211
+ const ruleId = f.RuleID || f.Rule || f.rule || '';
212
+ // Skip files matching exclusion patterns
213
+ if (excludePatterns.some(pattern => pattern.test(filePath))) {
214
+ skipped++;
215
+ continue;
216
+ }
217
+ // Skip rule+path combinations (documentation examples)
218
+ if (excludeRulePaths.some(({ rule, path }) => rule.test(ruleId) && path.test(filePath))) {
219
+ skipped++;
220
+ continue;
221
+ }
191
222
  findings.push({
192
223
  id: `gitleaks-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
193
224
  type: 'secret',
@@ -199,7 +230,7 @@ export class ScannerAgent extends SLOPAgent {
199
230
  metadata: { rule: f.RuleID || f.Rule || f.rule, match: f.Match?.substring(0, 100) },
200
231
  });
201
232
  }
202
- console.log(`[Scanner] Gitleaks found ${findings.length} secrets`);
233
+ console.log(`[Scanner] Gitleaks found ${findings.length} secrets (${skipped} filtered as false positives)`);
203
234
  }
204
235
  }
205
236
  }
@@ -49,7 +49,7 @@ export interface AgentMessage {
49
49
  }
50
50
  export interface Finding {
51
51
  id: string;
52
- type: 'secret' | 'vulnerability' | 'code-issue' | 'iac' | 'docker';
52
+ type: string;
53
53
  severity: 'critical' | 'high' | 'medium' | 'low';
54
54
  title: string;
55
55
  description: string;
@@ -59,8 +59,29 @@ export interface Finding {
59
59
  version?: string;
60
60
  cve?: string;
61
61
  cwe?: string;
62
+ rule?: string;
63
+ scanner?: string;
64
+ message?: string;
62
65
  metadata?: Record<string, unknown>;
63
66
  }
67
+ export interface ExternalFinding {
68
+ id?: string;
69
+ type: string;
70
+ severity?: 'critical' | 'high' | 'medium' | 'low';
71
+ title?: string;
72
+ description?: string;
73
+ message?: string;
74
+ file?: string;
75
+ line?: number;
76
+ package?: string;
77
+ version?: string;
78
+ cve?: string;
79
+ cwe?: string;
80
+ rule?: string;
81
+ scanner?: string;
82
+ metadata?: Record<string, unknown>;
83
+ }
84
+ export declare function normalizeFinding(f: ExternalFinding): Finding;
64
85
  export interface TriageResult {
65
86
  finding: Finding;
66
87
  validated: boolean;
@@ -3,4 +3,23 @@
3
3
  *
4
4
  * Types for SLOP-native multi-agent communication
5
5
  */
6
- export {};
6
+ // Helper to convert external finding to internal format
7
+ export function normalizeFinding(f) {
8
+ return {
9
+ id: f.id || `finding-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
10
+ type: f.type,
11
+ severity: f.severity || 'medium',
12
+ title: f.title || f.type || 'Unknown Finding',
13
+ description: f.description || f.message || '',
14
+ file: f.file,
15
+ line: f.line,
16
+ package: f.package,
17
+ version: f.version,
18
+ cve: f.cve,
19
+ cwe: f.cwe,
20
+ rule: f.rule,
21
+ scanner: f.scanner,
22
+ message: f.message,
23
+ metadata: f.metadata,
24
+ };
25
+ }
package/dist/index.d.ts CHANGED
@@ -25,3 +25,7 @@ export { AuditorWebSocket, getWebSocketServer, closeWebSocketServer } from './we
25
25
  export type { WSMessage, AuditStartedPayload, AuditCompletedPayload, FindingPayload } from './websocket/index.js';
26
26
  export { performTrustScan } from './integrations/trust-scanner.js';
27
27
  export type { TrustScanResult, TrustCheck, TrustMetrics } from './integrations/trust-scanner.js';
28
+ export { performAIVerification } from './integrations/ai-verifier.js';
29
+ export type { AIVerifyResult } from './integrations/ai-verifier.js';
30
+ export { detectScamPatterns, quickScamScan, getScamSignatures, addScamSignature } from './integrations/scam-detector.js';
31
+ export type { ScamSignature, ScamDetectionResult, SimilarityMatch } from './integrations/scam-detector.js';