fivosense 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
  2. package/.github/PULL_REQUEST_TEMPLATE.md +22 -0
  3. package/.github/workflows/ci.yml +52 -0
  4. package/BLUEPRINT.md +215 -0
  5. package/BUILD_PLAN.md +175 -0
  6. package/CONTRIBUTING.md +80 -0
  7. package/DOCS_VERIFICATION.md +232 -0
  8. package/FINAL_CHECKLIST.md +263 -0
  9. package/FINAL_SUMMARY.md +238 -0
  10. package/GITHUB_PUSH.md +64 -0
  11. package/LICENSE +21 -0
  12. package/PROGRESS.md +153 -0
  13. package/README.md +443 -0
  14. package/RELEASE_READY.md +201 -0
  15. package/SECURITY.md +211 -0
  16. package/SECURITY_DEEP_AUDIT.md +331 -0
  17. package/TODO.md +52 -0
  18. package/dist/ai/judge.d.ts +36 -0
  19. package/dist/ai/judge.d.ts.map +1 -0
  20. package/dist/ai/judge.js +75 -0
  21. package/dist/ai/judge.js.map +1 -0
  22. package/dist/cli/index.d.ts +6 -0
  23. package/dist/cli/index.d.ts.map +1 -0
  24. package/dist/cli/index.js +39 -0
  25. package/dist/cli/index.js.map +1 -0
  26. package/dist/editors/vscode.d.ts +30 -0
  27. package/dist/editors/vscode.d.ts.map +1 -0
  28. package/dist/editors/vscode.js +103 -0
  29. package/dist/editors/vscode.js.map +1 -0
  30. package/dist/engine/adversary.d.ts +24 -0
  31. package/dist/engine/adversary.d.ts.map +1 -0
  32. package/dist/engine/adversary.js +83 -0
  33. package/dist/engine/adversary.js.map +1 -0
  34. package/dist/engine/graph.d.ts +38 -0
  35. package/dist/engine/graph.d.ts.map +1 -0
  36. package/dist/engine/graph.js +131 -0
  37. package/dist/engine/graph.js.map +1 -0
  38. package/dist/engine/reach.d.ts +22 -0
  39. package/dist/engine/reach.d.ts.map +1 -0
  40. package/dist/engine/reach.js +107 -0
  41. package/dist/engine/reach.js.map +1 -0
  42. package/dist/engine/sinks.d.ts +52 -0
  43. package/dist/engine/sinks.d.ts.map +1 -0
  44. package/dist/engine/sinks.js +96 -0
  45. package/dist/engine/sinks.js.map +1 -0
  46. package/dist/engine/sources.d.ts +35 -0
  47. package/dist/engine/sources.d.ts.map +1 -0
  48. package/dist/engine/sources.js +59 -0
  49. package/dist/engine/sources.js.map +1 -0
  50. package/dist/engine/taint.d.ts +37 -0
  51. package/dist/engine/taint.d.ts.map +1 -0
  52. package/dist/engine/taint.js +83 -0
  53. package/dist/engine/taint.js.map +1 -0
  54. package/dist/engine/verify.d.ts +20 -0
  55. package/dist/engine/verify.d.ts.map +1 -0
  56. package/dist/engine/verify.js +65 -0
  57. package/dist/engine/verify.js.map +1 -0
  58. package/dist/features/badge.d.ts +20 -0
  59. package/dist/features/badge.d.ts.map +1 -0
  60. package/dist/features/badge.js +86 -0
  61. package/dist/features/badge.js.map +1 -0
  62. package/dist/features/fix.d.ts +20 -0
  63. package/dist/features/fix.d.ts.map +1 -0
  64. package/dist/features/fix.js +115 -0
  65. package/dist/features/fix.js.map +1 -0
  66. package/dist/features/roast.d.ts +23 -0
  67. package/dist/features/roast.d.ts.map +1 -0
  68. package/dist/features/roast.js +96 -0
  69. package/dist/features/roast.js.map +1 -0
  70. package/dist/hooks/agent.d.ts +19 -0
  71. package/dist/hooks/agent.d.ts.map +1 -0
  72. package/dist/hooks/agent.js +69 -0
  73. package/dist/hooks/agent.js.map +1 -0
  74. package/dist/index.d.ts +34 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +116 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/rules/destructive.d.ts +35 -0
  79. package/dist/rules/destructive.d.ts.map +1 -0
  80. package/dist/rules/destructive.js +117 -0
  81. package/dist/rules/destructive.js.map +1 -0
  82. package/dist/rules/secrets.d.ts +29 -0
  83. package/dist/rules/secrets.d.ts.map +1 -0
  84. package/dist/rules/secrets.js +100 -0
  85. package/dist/rules/secrets.js.map +1 -0
  86. package/package.json +56 -0
  87. package/skill/SKILL.md +86 -0
  88. package/skill/prompts/path-judge.md +22 -0
  89. package/src/ai/judge.ts +100 -0
  90. package/src/cli/index.ts +46 -0
  91. package/src/editors/vscode.ts +125 -0
  92. package/src/engine/adversary.ts +100 -0
  93. package/src/engine/graph.ts +167 -0
  94. package/src/engine/reach.ts +141 -0
  95. package/src/engine/sinks.ts +113 -0
  96. package/src/engine/sources.ts +71 -0
  97. package/src/engine/taint.ts +117 -0
  98. package/src/engine/verify.ts +94 -0
  99. package/src/features/badge.ts +102 -0
  100. package/src/features/fix.ts +138 -0
  101. package/src/features/roast.ts +110 -0
  102. package/src/hooks/agent.ts +84 -0
  103. package/src/index.ts +147 -0
  104. package/src/rules/destructive.ts +131 -0
  105. package/src/rules/secrets.ts +120 -0
  106. package/test/engine.test.ts +110 -0
  107. package/test/features.test.ts +131 -0
  108. package/test/phase3.test.ts +129 -0
  109. package/tsconfig.json +20 -0
  110. package/vitest.config.ts +9 -0
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Destructive command detection
3
+ * Blocks dangerous operations: rm -rf, DROP TABLE, mass deletes, etc.
4
+ */
5
+
6
+ export interface DestructivePattern {
7
+ pattern: RegExp;
8
+ description: string;
9
+ severity: 'critical' | 'high';
10
+ category: 'filesystem' | 'database' | 'system';
11
+ }
12
+
13
+ /**
14
+ * Filesystem destructive patterns
15
+ */
16
+ export const FS_DESTRUCTIVE: DestructivePattern[] = [
17
+ {
18
+ pattern: /rm\s+-rf\s+[\/~]/,
19
+ description: 'Recursive force delete from root',
20
+ severity: 'critical',
21
+ category: 'filesystem',
22
+ },
23
+ {
24
+ pattern: /rm\s+-rf\s+\*/,
25
+ description: 'Recursive force delete with wildcard',
26
+ severity: 'critical',
27
+ category: 'filesystem',
28
+ },
29
+ {
30
+ pattern: /unlink\s*\(\s*['"]\/['"]\s*\)/,
31
+ description: 'Unlink root directory',
32
+ severity: 'critical',
33
+ category: 'filesystem',
34
+ },
35
+ {
36
+ pattern: /fs\.rmdir\s*\(\s*['"]\/['"]/,
37
+ description: 'Remove root directory',
38
+ severity: 'critical',
39
+ category: 'filesystem',
40
+ },
41
+ ];
42
+
43
+ /**
44
+ * Database destructive patterns
45
+ */
46
+ export const DB_DESTRUCTIVE: DestructivePattern[] = [
47
+ {
48
+ pattern: /DROP\s+TABLE/i,
49
+ description: 'SQL DROP TABLE',
50
+ severity: 'critical',
51
+ category: 'database',
52
+ },
53
+ {
54
+ pattern: /DROP\s+DATABASE/i,
55
+ description: 'SQL DROP DATABASE',
56
+ severity: 'critical',
57
+ category: 'database',
58
+ },
59
+ {
60
+ pattern: /TRUNCATE\s+TABLE/i,
61
+ description: 'SQL TRUNCATE TABLE',
62
+ severity: 'high',
63
+ category: 'database',
64
+ },
65
+ {
66
+ pattern: /DELETE\s+FROM\s+\w+\s*;/i,
67
+ description: 'SQL DELETE without WHERE clause',
68
+ severity: 'critical',
69
+ category: 'database',
70
+ },
71
+ {
72
+ pattern: /db\.collection\(\w+\)\.drop\(\)/,
73
+ description: 'MongoDB collection drop',
74
+ severity: 'high',
75
+ category: 'database',
76
+ },
77
+ ];
78
+
79
+ /**
80
+ * System destructive patterns
81
+ */
82
+ export const SYSTEM_DESTRUCTIVE: DestructivePattern[] = [
83
+ {
84
+ pattern: /shutdown|reboot|halt/i,
85
+ description: 'System shutdown command',
86
+ severity: 'critical',
87
+ category: 'system',
88
+ },
89
+ {
90
+ pattern: /kill\s+-9\s+1/,
91
+ description: 'Kill init process',
92
+ severity: 'critical',
93
+ category: 'system',
94
+ },
95
+ ];
96
+
97
+ /**
98
+ * All destructive patterns
99
+ */
100
+ export const ALL_DESTRUCTIVE = [
101
+ ...FS_DESTRUCTIVE,
102
+ ...DB_DESTRUCTIVE,
103
+ ...SYSTEM_DESTRUCTIVE,
104
+ ];
105
+
106
+ /**
107
+ * Check if code contains destructive patterns
108
+ */
109
+ export function detectDestructive(code: string): DestructivePattern[] {
110
+ const matches: DestructivePattern[] = [];
111
+
112
+ for (const pattern of ALL_DESTRUCTIVE) {
113
+ if (pattern.pattern.test(code)) {
114
+ matches.push(pattern);
115
+ }
116
+ }
117
+
118
+ return matches;
119
+ }
120
+
121
+ /**
122
+ * Check if specific line contains destructive command
123
+ */
124
+ export function isDestructiveLine(line: string): DestructivePattern | null {
125
+ for (const pattern of ALL_DESTRUCTIVE) {
126
+ if (pattern.pattern.test(line)) {
127
+ return pattern;
128
+ }
129
+ }
130
+ return null;
131
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Secret detection - finds hardcoded API keys, tokens, passwords
3
+ */
4
+
5
+ export interface SecretPattern {
6
+ pattern: RegExp;
7
+ type: string;
8
+ description: string;
9
+ severity: 'high' | 'medium';
10
+ }
11
+
12
+ /**
13
+ * Common secret patterns
14
+ */
15
+ export const SECRET_PATTERNS: SecretPattern[] = [
16
+ {
17
+ pattern: /['"][A-Za-z0-9_]{32,}['"]/,
18
+ type: 'generic_token',
19
+ description: 'Generic API token (32+ chars)',
20
+ severity: 'high',
21
+ },
22
+ {
23
+ pattern: /['"]sk-[A-Za-z0-9]{48}['"]/,
24
+ type: 'openai_key',
25
+ description: 'OpenAI API key',
26
+ severity: 'high',
27
+ },
28
+ {
29
+ pattern: /['"]AIza[A-Za-z0-9_-]{35}['"]/,
30
+ type: 'google_api_key',
31
+ description: 'Google API key',
32
+ severity: 'high',
33
+ },
34
+ {
35
+ pattern: /['"]AKIA[A-Z0-9]{16}['"]/,
36
+ type: 'aws_access_key',
37
+ description: 'AWS Access Key ID',
38
+ severity: 'high',
39
+ },
40
+ {
41
+ pattern: /['"]ghp_[A-Za-z0-9]{36}['"]/,
42
+ type: 'github_token',
43
+ description: 'GitHub Personal Access Token',
44
+ severity: 'high',
45
+ },
46
+ {
47
+ pattern: /['"]xox[baprs]-[A-Za-z0-9-]{10,}['"]/,
48
+ type: 'slack_token',
49
+ description: 'Slack Token',
50
+ severity: 'high',
51
+ },
52
+ {
53
+ pattern: /password\s*[:=]\s*['"][^'"]+['"]/i,
54
+ type: 'password',
55
+ description: 'Hardcoded password',
56
+ severity: 'high',
57
+ },
58
+ {
59
+ pattern: /api[_-]?key\s*[:=]\s*['"][^'"]+['"]/i,
60
+ type: 'api_key',
61
+ description: 'Hardcoded API key',
62
+ severity: 'high',
63
+ },
64
+ {
65
+ pattern: /secret\s*[:=]\s*['"][^'"]+['"]/i,
66
+ type: 'secret',
67
+ description: 'Hardcoded secret',
68
+ severity: 'high',
69
+ },
70
+ ];
71
+
72
+ export interface SecretMatch {
73
+ type: string;
74
+ description: string;
75
+ severity: 'high' | 'medium';
76
+ line: number;
77
+ match: string;
78
+ }
79
+
80
+ /**
81
+ * Detect secrets in code
82
+ */
83
+ export function detectSecrets(code: string): SecretMatch[] {
84
+ const lines = code.split('\n');
85
+ const matches: SecretMatch[] = [];
86
+
87
+ lines.forEach((line, index) => {
88
+ // Skip comments
89
+ if (line.trim().startsWith('//') || line.trim().startsWith('*')) {
90
+ return;
91
+ }
92
+
93
+ for (const pattern of SECRET_PATTERNS) {
94
+ const match = line.match(pattern.pattern);
95
+ if (match) {
96
+ matches.push({
97
+ type: pattern.type,
98
+ description: pattern.description,
99
+ severity: pattern.severity,
100
+ line: index + 1,
101
+ match: match[0].substring(0, 20) + '...',
102
+ });
103
+ }
104
+ }
105
+ });
106
+
107
+ return matches;
108
+ }
109
+
110
+ /**
111
+ * Check if specific line contains a secret
112
+ */
113
+ export function isSecretLine(line: string): SecretPattern | null {
114
+ for (const pattern of SECRET_PATTERNS) {
115
+ if (pattern.pattern.test(line)) {
116
+ return pattern;
117
+ }
118
+ }
119
+ return null;
120
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * FivoCore Engine Tests
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import { buildDataFlowGraph, getVulnerablePaths } from '../src/engine/graph.js';
7
+ import { generateTaintTraces, getVulnerableTraces } from '../src/engine/taint.js';
8
+ import { detectSecrets } from '../src/rules/secrets.js';
9
+ import { detectDestructive } from '../src/rules/destructive.js';
10
+
11
+ describe('FivoCore Engine', () => {
12
+ describe('Taint Analysis', () => {
13
+ it('should detect SQL injection', () => {
14
+ const code = `
15
+ const userId = req.query.id;
16
+ const query = \`SELECT * FROM users WHERE id = \${userId}\`;
17
+ db.execute(query);
18
+ `;
19
+
20
+ const graph = buildDataFlowGraph(code);
21
+ const vulnPaths = getVulnerablePaths(graph);
22
+
23
+ expect(vulnPaths.length).toBeGreaterThan(0);
24
+ expect(vulnPaths[0].sink.sinkPattern?.category).toBe('sql');
25
+ });
26
+
27
+ it('should detect XSS', () => {
28
+ const code = `
29
+ const name = req.query.name;
30
+ res.send('<h1>Hello ' + name + '</h1>');
31
+ `;
32
+
33
+ const graph = buildDataFlowGraph(code);
34
+ const vulnPaths = getVulnerablePaths(graph);
35
+
36
+ expect(vulnPaths.length).toBeGreaterThan(0);
37
+ expect(vulnPaths[0].sink.sinkPattern?.category).toBe('xss');
38
+ });
39
+
40
+ it('should detect command injection', () => {
41
+ const code = `
42
+ const file = req.body.filename;
43
+ exec('tar -czf backup.tar.gz ' + file);
44
+ `;
45
+
46
+ const graph = buildDataFlowGraph(code);
47
+ const vulnPaths = getVulnerablePaths(graph);
48
+
49
+ expect(vulnPaths.length).toBeGreaterThan(0);
50
+ expect(vulnPaths[0].sink.sinkPattern?.category).toBe('command');
51
+ });
52
+
53
+ it('should track taint through variables', () => {
54
+ const code = `
55
+ const userInput = req.query.name;
56
+ const greeting = 'Hello ' + userInput;
57
+ res.send(greeting);
58
+ `;
59
+
60
+ const graph = buildDataFlowGraph(code);
61
+ const vulnPaths = getVulnerablePaths(graph);
62
+
63
+ expect(vulnPaths.length).toBeGreaterThan(0);
64
+ });
65
+ });
66
+
67
+ describe('Secret Detection', () => {
68
+ it('should detect hardcoded API keys', () => {
69
+ const code = `
70
+ const apiKey = "sk-AbCdEfGhIjKlMnOpQrStUvWxYz1234567890123456";
71
+ const client = new OpenAI({ apiKey });
72
+ `;
73
+
74
+ const secrets = detectSecrets(code);
75
+ expect(secrets.length).toBeGreaterThan(0);
76
+ // Match either openai_key or api_key (both valid)
77
+ expect(['openai_key', 'api_key']).toContain(secrets[0].type);
78
+ });
79
+
80
+ it('should detect hardcoded passwords', () => {
81
+ const code = `
82
+ const config = {
83
+ password: "SuperSecret123"
84
+ };
85
+ `;
86
+
87
+ const secrets = detectSecrets(code);
88
+ expect(secrets.length).toBeGreaterThan(0);
89
+ expect(secrets[0].type).toBe('password');
90
+ });
91
+ });
92
+
93
+ describe('Destructive Command Detection', () => {
94
+ it('should detect rm -rf', () => {
95
+ const code = `exec('rm -rf /')`;
96
+ const destructive = detectDestructive(code);
97
+
98
+ expect(destructive.length).toBeGreaterThan(0);
99
+ expect(destructive[0].severity).toBe('critical');
100
+ });
101
+
102
+ it('should detect DROP TABLE', () => {
103
+ const code = `db.execute('DROP TABLE users')`;
104
+ const destructive = detectDestructive(code);
105
+
106
+ expect(destructive.length).toBeGreaterThan(0);
107
+ expect(destructive[0].category).toBe('database');
108
+ });
109
+ });
110
+ });
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Features Tests - Roast, Badge, Fix, Verify
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import { generateRoast } from '../src/features/roast.js';
7
+ import { generateBadge } from '../src/features/badge.js';
8
+ import { generateFix } from '../src/features/fix.js';
9
+ import { verifyFix } from '../src/engine/verify.js';
10
+ import { auditCode } from '../src/index.js';
11
+
12
+ describe('Features', () => {
13
+ describe('Roast Mode', () => {
14
+ it('should generate roast for clean code', async () => {
15
+ const result = await auditCode('const x = 1;');
16
+ const roast = generateRoast(result);
17
+
18
+ expect(roast.title).toBe('Clean Code Champion');
19
+ expect(roast.severity).toBe('mild');
20
+ });
21
+
22
+ it('should generate spicy/brutal roast for multiple critical issues', async () => {
23
+ const code = `
24
+ const id = req.query.id;
25
+ db.execute(\`DELETE FROM users WHERE id = \${id}\`);
26
+ exec('rm -rf ' + req.body.path);
27
+ res.send('<h1>' + req.query.name + '</h1>');
28
+ `;
29
+
30
+ const result = await auditCode(code);
31
+ const roast = generateRoast(result);
32
+
33
+ expect(['spicy', 'brutal']).toContain(roast.severity);
34
+ });
35
+ });
36
+
37
+ describe('Security Badge', () => {
38
+ it('should generate A+ grade for clean code', async () => {
39
+ const result = await auditCode('const x = 1;');
40
+ const badge = generateBadge(result);
41
+
42
+ expect(badge.grade).toBe('A+');
43
+ expect(badge.score).toBe(100);
44
+ expect(badge.color).toBe('brightgreen');
45
+ });
46
+
47
+ it('should generate low grade for critical issues', async () => {
48
+ const code = `
49
+ const id = req.query.id;
50
+ db.execute(\`DELETE FROM users WHERE id = \${id}\`);
51
+ exec('rm -rf ' + req.body.path);
52
+ `;
53
+
54
+ const result = await auditCode(code);
55
+ const badge = generateBadge(result);
56
+
57
+ expect(['D', 'F']).toContain(badge.grade);
58
+ expect(badge.score).toBeLessThan(100);
59
+ });
60
+ });
61
+
62
+ describe('Fix Generation', () => {
63
+ it('should generate SQL injection fix', async () => {
64
+ const code = `
65
+ const userId = req.query.id;
66
+ const query = \`SELECT * FROM users WHERE id = \${userId}\`;
67
+ db.execute(query);
68
+ `;
69
+
70
+ const result = await auditCode(code);
71
+ const vuln = result.vulnerabilities[0];
72
+ const fix = generateFix(vuln, code);
73
+
74
+ expect(fix).not.toBeNull();
75
+ expect(fix?.type).toBe('parameterize');
76
+ expect(fix?.confidence).toBeGreaterThan(0.5);
77
+ });
78
+
79
+ it('should generate XSS fix', async () => {
80
+ const code = `
81
+ const name = req.query.name;
82
+ res.send('<h1>Hello ' + name + '</h1>');
83
+ `;
84
+
85
+ const result = await auditCode(code);
86
+ const vuln = result.vulnerabilities[0];
87
+ const fix = generateFix(vuln, code);
88
+
89
+ expect(fix).not.toBeNull();
90
+ expect(fix?.type).toBe('encode');
91
+ });
92
+ });
93
+
94
+ describe('Fix Verification', () => {
95
+ it('should verify successful fix', () => {
96
+ const original = `
97
+ const userId = req.query.id;
98
+ db.execute(\`SELECT * FROM users WHERE id = \${userId}\`);
99
+ `;
100
+
101
+ const fixed = `
102
+ const userId = parseInt(req.query.id);
103
+ db.execute(\`SELECT * FROM users WHERE id = \${userId}\`);
104
+ `;
105
+
106
+ const verification = verifyFix(original, fixed, 3);
107
+
108
+ expect(verification.success).toBe(true);
109
+ expect(verification.regression).toBe(false);
110
+ });
111
+
112
+ it('should detect regressions', () => {
113
+ const original = `
114
+ const userId = req.query.id;
115
+ db.execute(\`SELECT * FROM users WHERE id = \${userId}\`);
116
+ `;
117
+
118
+ const fixed = `
119
+ const userId = req.query.id;
120
+ const name = req.query.name;
121
+ db.execute(\`SELECT * FROM users WHERE id = \${userId}\`);
122
+ eval(name);
123
+ `;
124
+
125
+ const verification = verifyFix(original, fixed, 3);
126
+
127
+ expect(verification.regression).toBe(true);
128
+ expect(verification.newIssues).toBeGreaterThan(0);
129
+ });
130
+ });
131
+ });
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Phase 3 Tests - Reachability, Adversary, Agent Hooks
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import { analyzeReachability, filterReachablePaths } from '../src/engine/reach.js';
7
+ import { buildAdversarialPrompt, parseAdversarialResult } from '../src/engine/adversary.js';
8
+ import { checkAction, preToolUseHook } from '../src/hooks/agent.js';
9
+
10
+ describe('Phase 3 Features', () => {
11
+ describe('Reachability Analysis', () => {
12
+ it('should identify entry points', () => {
13
+ const code = `
14
+ app.get('/users', (req, res) => {
15
+ const id = req.query.id;
16
+ getUser(id);
17
+ });
18
+
19
+ function getUser(id) {
20
+ return db.query('SELECT * FROM users WHERE id = ' + id);
21
+ }
22
+ `;
23
+
24
+ const result = analyzeReachability(code);
25
+
26
+ expect(result.entryPoints.length).toBeGreaterThan(0);
27
+ expect(result.entryPoints[0]).toContain('app.get');
28
+ });
29
+
30
+ it('should filter reachable paths', () => {
31
+ const paths = [
32
+ { location: { line: 5 } },
33
+ { location: { line: 10 } },
34
+ { location: { line: 15 } },
35
+ ];
36
+
37
+ const reachability = {
38
+ reachableFunctions: new Set(),
39
+ reachableLines: new Set([5, 10]),
40
+ entryPoints: [],
41
+ totalFunctions: 0,
42
+ reachableFunctions: 0,
43
+ reductionPercent: 0,
44
+ };
45
+
46
+ const filtered = filterReachablePaths(paths, reachability);
47
+
48
+ expect(filtered.length).toBe(2);
49
+ });
50
+ });
51
+
52
+ describe('Adversarial Verification', () => {
53
+ it('should build adversarial prompt', () => {
54
+ const trace = {
55
+ finding: 'SQL Injection',
56
+ category: 'sql',
57
+ cwe: 'CWE-89',
58
+ path: 'req.query.id → db.execute',
59
+ evidence: [],
60
+ location: { file: 'test.js', line: 5, column: 0 },
61
+ sanitized: false,
62
+ confidence: 0.9,
63
+ severity: 'critical' as const,
64
+ };
65
+
66
+ const prompt = buildAdversarialPrompt(trace, 'const id = req.query.id;');
67
+
68
+ expect(prompt).toContain('security researcher');
69
+ expect(prompt).toContain('SQL Injection');
70
+ expect(prompt).toContain('exploit');
71
+ });
72
+
73
+ it('should parse adversarial result', () => {
74
+ const response = `
75
+ Here's my analysis:
76
+ \`\`\`json
77
+ {
78
+ "exploitable": true,
79
+ "confidence": 0.95,
80
+ "attackVector": "SQL injection via query parameter",
81
+ "payload": "' OR '1'='1",
82
+ "reasoning": "No sanitization present"
83
+ }
84
+ \`\`\`
85
+ `;
86
+
87
+ const result = parseAdversarialResult(response);
88
+
89
+ expect(result).not.toBeNull();
90
+ expect(result?.exploitable).toBe(true);
91
+ expect(result?.confidence).toBe(0.95);
92
+ });
93
+ });
94
+
95
+ describe('Agent Hooks', () => {
96
+ it('should block destructive commands', () => {
97
+ const result = checkAction('rm -rf /');
98
+
99
+ expect(result.blocked).toBe(true);
100
+ expect(result.severity).toBe('critical');
101
+ });
102
+
103
+ it('should block hardcoded secrets', () => {
104
+ const code = 'const apiKey = "sk-1234567890123456789012345678901234567890123456";';
105
+ const result = checkAction('', code);
106
+
107
+ expect(result.blocked).toBe(true);
108
+ expect(result.severity).toBe('high');
109
+ });
110
+
111
+ it('should allow safe actions', () => {
112
+ const result = checkAction('ls -la');
113
+
114
+ expect(result.blocked).toBe(false);
115
+ });
116
+
117
+ it('should return exit code 2 for blocked actions', () => {
118
+ const exitCode = preToolUseHook('bash', { command: 'rm -rf /' });
119
+
120
+ expect(exitCode).toBe(2);
121
+ });
122
+
123
+ it('should return exit code 0 for allowed actions', () => {
124
+ const exitCode = preToolUseHook('bash', { command: 'echo hello' });
125
+
126
+ expect(exitCode).toBe(0);
127
+ });
128
+ });
129
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "lib": ["ES2022"],
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true
17
+ },
18
+ "include": ["src/**/*"],
19
+ "exclude": ["node_modules", "dist", "test", "**/*.test.ts"]
20
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ include: ['test/**/*.test.ts'],
8
+ },
9
+ });