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.
- package/dist/agents/slop/chain-mapper-agent.d.ts +1 -0
- package/dist/agents/slop/chain-mapper-agent.js +106 -22
- package/dist/agents/slop/redteam-agent.js +11 -4
- package/dist/agents/slop/scanner-agent.js +32 -1
- package/dist/agents/slop/types.d.ts +22 -1
- package/dist/agents/slop/types.js +20 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +320 -2
- package/dist/integrations/ai-verifier.d.ts +38 -0
- package/dist/integrations/ai-verifier.js +478 -0
- package/dist/integrations/index.d.ts +4 -0
- package/dist/integrations/index.js +4 -0
- package/dist/integrations/local-scanner.d.ts +2 -0
- package/dist/integrations/local-scanner.js +127 -13
- package/dist/integrations/scam-detector.d.ts +72 -0
- package/dist/integrations/scam-detector.js +668 -0
- package/dist/integrations/trust-scanner.backup.d.ts +50 -0
- package/dist/integrations/trust-scanner.backup.js +665 -0
- package/dist/integrations/trust-scanner.d.ts +7 -0
- package/dist/integrations/trust-scanner.js +338 -4
- package/dist/integrations/x-scanner.d.ts +53 -0
- package/dist/integrations/x-scanner.js +442 -0
- package/package.json +1 -1
- package/visualizer/index-minimal.html +275 -12
|
@@ -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
|
-
|
|
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 =>
|
|
197
|
-
|
|
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
|
|
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[
|
|
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
|
-
|
|
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
|
-
'
|
|
466
|
-
'
|
|
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[
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
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';
|