fivosense 0.1.5 → 0.2.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 (136) hide show
  1. package/.kilo/skill/fivosense/skill.json +5 -5
  2. package/COMPLETE_SUMMARY.md +412 -0
  3. package/DEPLOYMENT_GUIDE.md +2 -2
  4. package/FINAL_VERIFICATION.md +316 -0
  5. package/GITHUB_PUSH.md +4 -4
  6. package/LICENSE +1 -1
  7. package/README.md +290 -208
  8. package/RELEASE_READY.md +3 -3
  9. package/bin/fivosense.mjs +6 -0
  10. package/dist/ai/client.d.ts +33 -0
  11. package/dist/ai/client.d.ts.map +1 -0
  12. package/dist/ai/client.js +170 -0
  13. package/dist/ai/client.js.map +1 -0
  14. package/dist/ai/judge.d.ts +9 -3
  15. package/dist/ai/judge.d.ts.map +1 -1
  16. package/dist/ai/judge.js +49 -14
  17. package/dist/ai/judge.js.map +1 -1
  18. package/dist/cli/index.d.ts +3 -1
  19. package/dist/cli/index.d.ts.map +1 -1
  20. package/dist/cli/index.js +6 -1
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/core/orchestrator.d.ts +34 -0
  23. package/dist/core/orchestrator.d.ts.map +1 -0
  24. package/dist/core/orchestrator.js +211 -0
  25. package/dist/core/orchestrator.js.map +1 -0
  26. package/dist/core/scope.d.ts +32 -0
  27. package/dist/core/scope.d.ts.map +1 -0
  28. package/dist/core/scope.js +149 -0
  29. package/dist/core/scope.js.map +1 -0
  30. package/dist/editors/vscode.d.ts +4 -2
  31. package/dist/editors/vscode.d.ts.map +1 -1
  32. package/dist/editors/vscode.js +6 -0
  33. package/dist/editors/vscode.js.map +1 -1
  34. package/dist/engine/adversary.d.ts +9 -2
  35. package/dist/engine/adversary.d.ts.map +1 -1
  36. package/dist/engine/adversary.js +47 -13
  37. package/dist/engine/adversary.js.map +1 -1
  38. package/dist/engine/graph.d.ts +4 -1
  39. package/dist/engine/graph.d.ts.map +1 -1
  40. package/dist/engine/graph.js +6 -0
  41. package/dist/engine/graph.js.map +1 -1
  42. package/dist/engine/poc.d.ts +26 -0
  43. package/dist/engine/poc.d.ts.map +1 -0
  44. package/dist/engine/poc.js +179 -0
  45. package/dist/engine/poc.js.map +1 -0
  46. package/dist/engine/reach.d.ts +4 -2
  47. package/dist/engine/reach.d.ts.map +1 -1
  48. package/dist/engine/reach.js +6 -0
  49. package/dist/engine/reach.js.map +1 -1
  50. package/dist/engine/sinks.d.ts +22 -32
  51. package/dist/engine/sinks.d.ts.map +1 -1
  52. package/dist/engine/sinks.js +338 -44
  53. package/dist/engine/sinks.js.map +1 -1
  54. package/dist/engine/sources.d.ts +11 -19
  55. package/dist/engine/sources.d.ts.map +1 -1
  56. package/dist/engine/sources.js +100 -24
  57. package/dist/engine/sources.js.map +1 -1
  58. package/dist/engine/taint.d.ts +6 -0
  59. package/dist/engine/taint.d.ts.map +1 -1
  60. package/dist/engine/taint.js +6 -0
  61. package/dist/engine/taint.js.map +1 -1
  62. package/dist/engine/verify.d.ts +4 -1
  63. package/dist/engine/verify.d.ts.map +1 -1
  64. package/dist/engine/verify.js +6 -0
  65. package/dist/engine/verify.js.map +1 -1
  66. package/dist/features/badge.d.ts +6 -0
  67. package/dist/features/badge.d.ts.map +1 -1
  68. package/dist/features/badge.js +4 -1
  69. package/dist/features/badge.js.map +1 -1
  70. package/dist/features/fix.d.ts +6 -0
  71. package/dist/features/fix.d.ts.map +1 -1
  72. package/dist/features/fix.js +4 -1
  73. package/dist/features/fix.js.map +1 -1
  74. package/dist/features/index.d.ts +6 -0
  75. package/dist/features/index.d.ts.map +1 -1
  76. package/dist/features/index.js +6 -0
  77. package/dist/features/index.js.map +1 -1
  78. package/dist/features/roast.d.ts +6 -0
  79. package/dist/features/roast.d.ts.map +1 -1
  80. package/dist/features/roast.js +4 -1
  81. package/dist/features/roast.js.map +1 -1
  82. package/dist/hooks/agent.d.ts +4 -1
  83. package/dist/hooks/agent.d.ts.map +1 -1
  84. package/dist/hooks/agent.js +6 -0
  85. package/dist/hooks/agent.js.map +1 -1
  86. package/dist/hooks/git.d.ts +34 -0
  87. package/dist/hooks/git.d.ts.map +1 -0
  88. package/dist/hooks/git.js +161 -0
  89. package/dist/hooks/git.js.map +1 -0
  90. package/dist/index.d.ts +4 -2
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +6 -0
  93. package/dist/index.js.map +1 -1
  94. package/dist/rules/destructive.d.ts +12 -21
  95. package/dist/rules/destructive.d.ts.map +1 -1
  96. package/dist/rules/destructive.js +306 -24
  97. package/dist/rules/destructive.js.map +1 -1
  98. package/dist/rules/secrets.d.ts +8 -10
  99. package/dist/rules/secrets.d.ts.map +1 -1
  100. package/dist/rules/secrets.js +294 -17
  101. package/dist/rules/secrets.js.map +1 -1
  102. package/mcp/index.js +55 -20
  103. package/mcp/package-lock.json +382 -0
  104. package/mcp/package.json +21 -4
  105. package/package.json +5 -5
  106. package/src/ai/client.ts +226 -0
  107. package/src/ai/judge.ts +58 -14
  108. package/src/cli/index.ts +7 -1
  109. package/src/core/orchestrator.ts +266 -0
  110. package/src/core/scope.ts +175 -0
  111. package/src/editors/vscode.ts +7 -0
  112. package/src/engine/adversary.ts +55 -12
  113. package/src/engine/graph.ts +7 -0
  114. package/src/engine/poc.ts +219 -0
  115. package/src/engine/reach.ts +7 -0
  116. package/src/engine/sinks.ts +358 -45
  117. package/src/engine/sources.ts +109 -24
  118. package/src/engine/taint.ts +7 -0
  119. package/src/engine/verify.ts +7 -0
  120. package/src/features/badge.ts +7 -0
  121. package/src/features/fix.ts +7 -0
  122. package/src/features/index.ts +7 -0
  123. package/src/features/roast.ts +7 -0
  124. package/src/hooks/agent.ts +7 -0
  125. package/src/hooks/git.ts +194 -0
  126. package/src/index.ts +7 -0
  127. package/src/rules/destructive.ts +316 -26
  128. package/src/rules/secrets.ts +306 -17
  129. package/vscode-extension/CHANGELOG.md +14 -2
  130. package/vscode-extension/LICENSE +1 -1
  131. package/vscode-extension/README.md +28 -23
  132. package/vscode-extension/fivosense-vscode-0.1.0.vsix +0 -0
  133. package/vscode-extension/fivosense-vscode-0.1.1.vsix +0 -0
  134. package/vscode-extension/package-lock.json +6 -6
  135. package/vscode-extension/package.json +7 -5
  136. package/vscode-extension/src/extension.ts +65 -11
package/src/ai/judge.ts CHANGED
@@ -1,7 +1,16 @@
1
+ /**
2
+ * FivoSense - AI Security Scanner
3
+ * Copyright (c) 2026 thevinsoni
4
+ * Licensed under the MIT License
5
+ * https://github.com/thevinsoni/sense
6
+ */
7
+
1
8
  /**
2
9
  * AI Path Judge - Uses host AI to determine exploitability
3
10
  */
4
11
 
12
+ import { callAI, getAIProviderFromEnv, type AIProvider } from './client.js';
13
+
5
14
  export interface PathJudgment {
6
15
  exploitable: boolean;
7
16
  confidence: number;
@@ -80,21 +89,56 @@ export function parsePathJudgment(response: string): PathJudgment | null {
80
89
  }
81
90
 
82
91
  /**
83
- * Placeholder for host AI integration
84
- * In Phase 2, this will call the actual host AI (Claude/etc.)
92
+ * Judge path exploitability using AI
85
93
  */
86
- export async function judgePathWithAI(context: PathContext): Promise<PathJudgment> {
87
- const prompt = buildPathJudgePrompt(context);
94
+ export async function judgePathWithAI(
95
+ context: PathContext,
96
+ provider?: AIProvider
97
+ ): Promise<PathJudgment> {
98
+ // Get provider from env if not provided
99
+ const aiProvider = provider || getAIProviderFromEnv();
88
100
 
89
- // TODO: Phase 2 - integrate with host AI
90
- // For now, return a conservative judgment
91
- console.warn('⚠️ AI path judgment not yet integrated - using conservative defaults');
101
+ // If no AI provider available, return conservative judgment
102
+ if (!aiProvider) {
103
+ console.warn('⚠️ No AI provider configured - using conservative defaults');
104
+ console.warn('💡 Set OPENAI_API_KEY, ANTHROPIC_API_KEY, or OLLAMA_HOST to enable AI judgment');
105
+
106
+ return {
107
+ exploitable: true, // Conservative: assume exploitable
108
+ confidence: 0.7,
109
+ reasoning: 'AI judgment not configured - marked as potentially exploitable',
110
+ severity: 'high',
111
+ recommendation: 'Configure AI provider or review manually',
112
+ };
113
+ }
92
114
 
93
- return {
94
- exploitable: true, // Conservative: assume exploitable until AI confirms otherwise
95
- confidence: 0.7,
96
- reasoning: 'AI judgment not yet integrated - marked as potentially exploitable',
97
- severity: 'high',
98
- recommendation: 'Manual review required until AI integration complete',
99
- };
115
+ try {
116
+ const prompt = buildPathJudgePrompt(context);
117
+ const response = await callAI(aiProvider, prompt);
118
+
119
+ const judgment = parsePathJudgment(response.text);
120
+
121
+ if (!judgment) {
122
+ console.warn('⚠️ Failed to parse AI response - using conservative defaults');
123
+ return {
124
+ exploitable: true,
125
+ confidence: 0.6,
126
+ reasoning: 'Failed to parse AI response',
127
+ severity: 'high',
128
+ recommendation: 'Review manually',
129
+ };
130
+ }
131
+
132
+ return judgment;
133
+ } catch (error) {
134
+ console.warn(`⚠️ AI judgment failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
135
+
136
+ return {
137
+ exploitable: true,
138
+ confidence: 0.7,
139
+ reasoning: `AI judgment failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
140
+ severity: 'high',
141
+ recommendation: 'Review manually',
142
+ };
143
+ }
100
144
  }
package/src/cli/index.ts CHANGED
@@ -1,4 +1,10 @@
1
- #!/usr/bin/env node
1
+ /**
2
+ * FivoSense CLI
3
+ * Copyright (c) 2026 thevinsoni
4
+ * Licensed under the MIT License
5
+ * https://github.com/thevinsoni/sense
6
+ */
7
+
2
8
  /**
3
9
  * FivoSense CLI
4
10
  */
@@ -0,0 +1,266 @@
1
+ /**
2
+ * FivoSense - AI Security Scanner
3
+ * Copyright (c) 2026 thevinsoni
4
+ * Licensed under the MIT License
5
+ * https://github.com/thevinsoni/sense
6
+ */
7
+
8
+ /**
9
+ * Orchestrator - Coordinates analysis pipeline and flow control
10
+ */
11
+
12
+ import { buildDataFlowGraph } from '../engine/graph.js';
13
+ import { generateTaintTraces, type TaintTrace } from '../engine/taint.js';
14
+ import { detectSecrets } from '../rules/secrets.js';
15
+ import { detectDestructive } from '../rules/destructive.js';
16
+ import { judgePathWithAI } from '../ai/judge.js';
17
+ import { verifyWithAdversary } from '../engine/adversary.js';
18
+ import { generatePoC } from '../engine/poc.js';
19
+ import { getDiffScope, filterFindingsByScope, type CodeScope } from './scope.js';
20
+ import type { AuditResult } from '../index.js';
21
+ import type { AIProvider } from '../ai/client.js';
22
+
23
+ export interface OrchestratorOptions {
24
+ enableAI?: boolean;
25
+ enableAdversarial?: boolean;
26
+ enablePoC?: boolean;
27
+ scopeToDiff?: boolean;
28
+ diffBase?: string;
29
+ aiProvider?: AIProvider;
30
+ verbose?: boolean;
31
+ }
32
+
33
+ /**
34
+ * Main orchestration pipeline
35
+ */
36
+ export async function orchestrateAudit(
37
+ code: string,
38
+ filepath: string,
39
+ options: OrchestratorOptions = {}
40
+ ): Promise<AuditResult> {
41
+ const {
42
+ enableAI = false,
43
+ enableAdversarial = false,
44
+ enablePoC = false,
45
+ scopeToDiff = false,
46
+ diffBase = 'main',
47
+ aiProvider,
48
+ verbose = false,
49
+ } = options;
50
+
51
+ if (verbose) {
52
+ console.log(`🔍 Starting audit: ${filepath}`);
53
+ console.log(` AI Judge: ${enableAI ? '✅' : '❌'}`);
54
+ console.log(` Adversarial: ${enableAdversarial ? '✅' : '❌'}`);
55
+ console.log(` PoC Generation: ${enablePoC ? '✅' : '❌'}`);
56
+ console.log(` Scope to diff: ${scopeToDiff ? '✅' : '❌'}`);
57
+ }
58
+
59
+ // Step 1: Get scope if needed
60
+ let scope: CodeScope | undefined;
61
+ if (scopeToDiff) {
62
+ if (verbose) console.log(`📋 Getting diff scope (base: ${diffBase})...`);
63
+ scope = await getDiffScope(diffBase);
64
+ if (verbose) console.log(` ${scope.files.length} files in scope`);
65
+ }
66
+
67
+ // Step 2: Build data-flow graph
68
+ if (verbose) console.log(`🔨 Building data-flow graph...`);
69
+ const graph = buildDataFlowGraph(code, filepath);
70
+
71
+ // Step 3: Taint analysis
72
+ if (verbose) console.log(`🔍 Running taint analysis...`);
73
+ const traces = generateTaintTraces(graph, filepath);
74
+
75
+ // Convert traces to vulnerabilities format
76
+ const allVulnerabilities = traces.map(trace => ({
77
+ finding: trace.finding,
78
+ severity: trace.severity,
79
+ category: trace.category,
80
+ cwe: trace.cwe,
81
+ path: trace.path.split(' → '),
82
+ evidence: trace.evidence,
83
+ location: trace.location,
84
+ sanitized: trace.sanitized,
85
+ confidence: 0.8,
86
+ }));
87
+
88
+ // Step 4: Filter by scope if enabled
89
+ let vulnerabilities = allVulnerabilities;
90
+ if (scope && scopeToDiff) {
91
+ if (verbose) console.log(`🎯 Filtering by scope...`);
92
+ vulnerabilities = filterFindingsByScope(vulnerabilities, filepath, scope);
93
+ if (verbose) console.log(` ${vulnerabilities.length} vulnerabilities in scope`);
94
+ }
95
+
96
+ // Step 5: AI judgment (if enabled)
97
+ if (enableAI && vulnerabilities.length > 0) {
98
+ if (verbose) console.log(`🤖 Running AI judgment...`);
99
+
100
+ for (let i = 0; i < vulnerabilities.length; i++) {
101
+ const vuln = vulnerabilities[i];
102
+
103
+ try {
104
+ const judgment = await judgePathWithAI({
105
+ source: vuln.path[0] || 'unknown',
106
+ sourceType: 'user input',
107
+ sourceLoc: `line ${vuln.location.line}`,
108
+ sink: vuln.path[vuln.path.length - 1] || 'unknown',
109
+ sinkType: vuln.category,
110
+ category: vuln.category,
111
+ cwe: vuln.cwe,
112
+ dataFlow: vuln.path.join(' → '),
113
+ codeSnippet: code.split('\n').slice(
114
+ Math.max(0, vuln.location.line - 3),
115
+ vuln.location.line + 2
116
+ ).join('\n'),
117
+ language: filepath.endsWith('.ts') || filepath.endsWith('.tsx') ? 'typescript' : 'javascript',
118
+ }, aiProvider);
119
+
120
+ // Update vulnerability with AI judgment
121
+ vuln.confidence = judgment.confidence;
122
+
123
+ if (verbose) {
124
+ console.log(` [${i + 1}/${vulnerabilities.length}] ${vuln.finding}: ${judgment.exploitable ? '❌ Exploitable' : '✅ Safe'} (confidence: ${judgment.confidence})`);
125
+ }
126
+ } catch (error) {
127
+ if (verbose) {
128
+ console.warn(` [${i + 1}/${vulnerabilities.length}] AI judgment failed:`, error);
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ // Step 6: Adversarial verification (if enabled)
135
+ if (enableAdversarial && vulnerabilities.length > 0) {
136
+ if (verbose) console.log(`⚔️ Running adversarial verification...`);
137
+
138
+ for (let i = 0; i < vulnerabilities.length; i++) {
139
+ const vuln = vulnerabilities[i];
140
+
141
+ try {
142
+ const trace = traces.find((t: TaintTrace) =>
143
+ t.category === vuln.category && t.location.line === vuln.location.line
144
+ );
145
+
146
+ if (trace) {
147
+ const adversary = await verifyWithAdversary(trace, code, aiProvider);
148
+
149
+ // Update confidence based on adversarial result
150
+ vuln.confidence = Math.min(vuln.confidence, adversary.confidence);
151
+
152
+ if (verbose) {
153
+ console.log(` [${i + 1}/${vulnerabilities.length}] ${adversary.exploitable ? '⚔️ Exploit found' : '🛡️ Defense holds'}`);
154
+ }
155
+ }
156
+ } catch (error) {
157
+ if (verbose) {
158
+ console.warn(` [${i + 1}/${vulnerabilities.length}] Adversarial verification failed:`, error);
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ // Step 7: PoC generation (if enabled)
165
+ if (enablePoC && vulnerabilities.length > 0) {
166
+ if (verbose) console.log(`💣 Generating PoCs...`);
167
+
168
+ for (const vuln of vulnerabilities) {
169
+ try {
170
+ const trace = traces.find((t: TaintTrace) =>
171
+ t.category === vuln.category && t.location.line === vuln.location.line
172
+ );
173
+
174
+ if (trace) {
175
+ const poc = generatePoC(trace);
176
+ // Attach PoC to vulnerability (could extend Vulnerability type)
177
+ (vuln as any).poc = poc;
178
+ }
179
+ } catch (error) {
180
+ if (verbose) {
181
+ console.warn(` Failed to generate PoC:`, error);
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ // Step 8: Secret detection
188
+ if (verbose) console.log(`🔑 Detecting secrets...`);
189
+ const secrets = detectSecrets(code);
190
+
191
+ // Step 9: Destructive command detection
192
+ if (verbose) console.log(`💥 Detecting destructive commands...`);
193
+ const destructive = detectDestructive(code);
194
+
195
+ // Step 10: Build summary
196
+ const summary = {
197
+ total: vulnerabilities.length + secrets.length + destructive.length,
198
+ critical: vulnerabilities.filter((v: any) => v.severity === 'critical').length,
199
+ high: vulnerabilities.filter((v: any) => v.severity === 'high').length + secrets.length,
200
+ medium: vulnerabilities.filter((v: any) => v.severity === 'medium').length + destructive.length,
201
+ };
202
+
203
+ if (verbose) {
204
+ console.log(`\n📊 Audit complete:`);
205
+ console.log(` Total: ${summary.total}`);
206
+ console.log(` Critical: ${summary.critical}`);
207
+ console.log(` High: ${summary.high}`);
208
+ console.log(` Medium: ${summary.medium}\n`);
209
+ }
210
+
211
+ return {
212
+ vulnerabilities,
213
+ secrets,
214
+ destructive,
215
+ summary,
216
+ };
217
+ }
218
+
219
+ /**
220
+ * Quick audit (no AI, no scope)
221
+ */
222
+ export async function quickAudit(code: string, filepath: string): Promise<AuditResult> {
223
+ return orchestrateAudit(code, filepath, {
224
+ enableAI: false,
225
+ enableAdversarial: false,
226
+ enablePoC: false,
227
+ scopeToDiff: false,
228
+ verbose: false,
229
+ });
230
+ }
231
+
232
+ /**
233
+ * Full audit (with AI and adversarial)
234
+ */
235
+ export async function fullAudit(
236
+ code: string,
237
+ filepath: string,
238
+ aiProvider?: AIProvider
239
+ ): Promise<AuditResult> {
240
+ return orchestrateAudit(code, filepath, {
241
+ enableAI: true,
242
+ enableAdversarial: true,
243
+ enablePoC: true,
244
+ scopeToDiff: false,
245
+ aiProvider,
246
+ verbose: true,
247
+ });
248
+ }
249
+
250
+ /**
251
+ * Diff-scoped audit (only changed code)
252
+ */
253
+ export async function diffAudit(
254
+ code: string,
255
+ filepath: string,
256
+ diffBase: string = 'main'
257
+ ): Promise<AuditResult> {
258
+ return orchestrateAudit(code, filepath, {
259
+ enableAI: false,
260
+ enableAdversarial: false,
261
+ enablePoC: false,
262
+ scopeToDiff: true,
263
+ diffBase,
264
+ verbose: true,
265
+ });
266
+ }
@@ -0,0 +1,175 @@
1
+ /**
2
+ * FivoSense - AI Security Scanner
3
+ * Copyright (c) 2026 thevinsoni
4
+ * Licensed under the MIT License
5
+ * https://github.com/thevinsoni/sense
6
+ */
7
+
8
+ /**
9
+ * Scope Management - Track and filter relevant code changes
10
+ */
11
+
12
+ import { exec } from 'child_process';
13
+ import { promisify } from 'util';
14
+
15
+ const execAsync = promisify(exec);
16
+
17
+ export interface CodeScope {
18
+ files: string[];
19
+ lines: Map<string, Set<number>>;
20
+ changedFunctions: Map<string, string[]>;
21
+ }
22
+
23
+ /**
24
+ * Get diff scope for current changes
25
+ */
26
+ export async function getDiffScope(base: string = 'main'): Promise<CodeScope> {
27
+ const scope: CodeScope = {
28
+ files: [],
29
+ lines: new Map(),
30
+ changedFunctions: new Map(),
31
+ };
32
+
33
+ try {
34
+ // Get changed files
35
+ const { stdout: filesOutput } = await execAsync(`git diff --name-only ${base}...HEAD`);
36
+ const files = filesOutput
37
+ .split('\n')
38
+ .filter(f => f.endsWith('.js') || f.endsWith('.ts') || f.endsWith('.jsx') || f.endsWith('.tsx'))
39
+ .filter(f => f.trim().length > 0);
40
+
41
+ scope.files = files;
42
+
43
+ // Get changed lines for each file
44
+ for (const file of files) {
45
+ try {
46
+ const { stdout: diffOutput } = await execAsync(`git diff ${base}...HEAD -- ${file}`);
47
+ const changedLines = parseDiffLines(diffOutput);
48
+ scope.lines.set(file, changedLines);
49
+ } catch (error) {
50
+ console.warn(`Failed to get diff for ${file}:`, error);
51
+ }
52
+ }
53
+
54
+ return scope;
55
+ } catch (error) {
56
+ console.warn('Failed to get diff scope:', error);
57
+ return scope;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Parse diff output to extract changed line numbers
63
+ */
64
+ function parseDiffLines(diff: string): Set<number> {
65
+ const lines = new Set<number>();
66
+ const diffLines = diff.split('\n');
67
+
68
+ let currentLine = 0;
69
+
70
+ for (const line of diffLines) {
71
+ // Parse hunk header: @@ -1,5 +1,7 @@
72
+ const hunkMatch = line.match(/^@@\s+-\d+,?\d*\s+\+(\d+),?(\d*)\s+@@/);
73
+ if (hunkMatch) {
74
+ currentLine = parseInt(hunkMatch[1], 10);
75
+ continue;
76
+ }
77
+
78
+ // Track added/modified lines
79
+ if (line.startsWith('+') && !line.startsWith('+++')) {
80
+ lines.add(currentLine);
81
+ currentLine++;
82
+ } else if (!line.startsWith('-')) {
83
+ currentLine++;
84
+ }
85
+ }
86
+
87
+ return lines;
88
+ }
89
+
90
+ /**
91
+ * Filter findings to only those in changed scope
92
+ */
93
+ export function filterFindingsByScope<T extends { location?: { line: number } }>(
94
+ findings: T[],
95
+ file: string,
96
+ scope: CodeScope
97
+ ): T[] {
98
+ const changedLines = scope.lines.get(file);
99
+
100
+ if (!changedLines || changedLines.size === 0) {
101
+ // No scope info, include all findings
102
+ return findings;
103
+ }
104
+
105
+ return findings.filter(finding => {
106
+ const line = finding.location?.line;
107
+ if (!line) return false;
108
+
109
+ // Include if exact line changed
110
+ if (changedLines.has(line)) return true;
111
+
112
+ // Include if within 5 lines of a change (context)
113
+ for (const changedLine of changedLines) {
114
+ if (Math.abs(line - changedLine) <= 5) return true;
115
+ }
116
+
117
+ return false;
118
+ });
119
+ }
120
+
121
+ /**
122
+ * Get scope for staged changes
123
+ */
124
+ export async function getStagedScope(): Promise<CodeScope> {
125
+ const scope: CodeScope = {
126
+ files: [],
127
+ lines: new Map(),
128
+ changedFunctions: new Map(),
129
+ };
130
+
131
+ try {
132
+ // Get staged files
133
+ const { stdout: filesOutput } = await execAsync('git diff --cached --name-only --diff-filter=ACM');
134
+ const files = filesOutput
135
+ .split('\n')
136
+ .filter(f => f.endsWith('.js') || f.endsWith('.ts') || f.endsWith('.jsx') || f.endsWith('.tsx'))
137
+ .filter(f => f.trim().length > 0);
138
+
139
+ scope.files = files;
140
+
141
+ // Get changed lines for each file
142
+ for (const file of files) {
143
+ try {
144
+ const { stdout: diffOutput } = await execAsync(`git diff --cached -- ${file}`);
145
+ const changedLines = parseDiffLines(diffOutput);
146
+ scope.lines.set(file, changedLines);
147
+ } catch (error) {
148
+ console.warn(`Failed to get staged diff for ${file}:`, error);
149
+ }
150
+ }
151
+
152
+ return scope;
153
+ } catch (error) {
154
+ console.warn('Failed to get staged scope:', error);
155
+ return scope;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Check if a line is in scope
161
+ */
162
+ export function isLineInScope(file: string, line: number, scope: CodeScope): boolean {
163
+ const changedLines = scope.lines.get(file);
164
+ if (!changedLines) return false;
165
+
166
+ // Exact match
167
+ if (changedLines.has(line)) return true;
168
+
169
+ // Context match (within 5 lines)
170
+ for (const changedLine of changedLines) {
171
+ if (Math.abs(line - changedLine) <= 5) return true;
172
+ }
173
+
174
+ return false;
175
+ }
@@ -1,3 +1,10 @@
1
+ /**
2
+ * FivoSense - AI Security Scanner
3
+ * Copyright (c) 2026 thevinsoni
4
+ * Licensed under the MIT License
5
+ * https://github.com/thevinsoni/sense
6
+ */
7
+
1
8
  /**
2
9
  * VS Code Extension Adapter
3
10
  * Integrates FivoSense with VS Code
@@ -1,8 +1,16 @@
1
+ /**
2
+ * FivoSense - AI Security Scanner
3
+ * Copyright (c) 2026 thevinsoni
4
+ * Licensed under the MIT License
5
+ * https://github.com/thevinsoni/sense
6
+ */
7
+
1
8
  /**
2
9
  * Adversarial Verification - AI attacker proves exploitability
3
10
  */
4
11
 
5
12
  import { TaintTrace } from './taint.js';
13
+ import { callAI, getAIProviderFromEnv, type AIProvider } from '../ai/client.js';
6
14
 
7
15
  export interface AdversarialResult {
8
16
  exploitable: boolean;
@@ -79,22 +87,57 @@ export function parseAdversarialResult(response: string): AdversarialResult | nu
79
87
  }
80
88
 
81
89
  /**
82
- * Placeholder for adversarial verification
90
+ * Verify exploitability using adversarial AI
83
91
  */
84
92
  export async function verifyWithAdversary(
85
93
  trace: TaintTrace,
86
- code: string
94
+ code: string,
95
+ provider?: AIProvider
87
96
  ): Promise<AdversarialResult> {
88
- const prompt = buildAdversarialPrompt(trace, code);
97
+ // Get provider from env if not provided
98
+ const aiProvider = provider || getAIProviderFromEnv();
89
99
 
90
- // TODO: Phase 3 - integrate with host AI
91
- console.warn('⚠️ Adversarial verification not yet integrated');
100
+ // If no AI provider available, return conservative result
101
+ if (!aiProvider) {
102
+ console.warn('⚠️ No AI provider configured for adversarial verification');
103
+ console.warn('💡 Set OPENAI_API_KEY, ANTHROPIC_API_KEY, or OLLAMA_HOST to enable');
104
+
105
+ return {
106
+ exploitable: true,
107
+ confidence: 0.7,
108
+ attackVector: 'Adversarial verification not configured',
109
+ payload: '',
110
+ reasoning: 'Marked as potentially exploitable - configure AI provider to verify',
111
+ };
112
+ }
92
113
 
93
- return {
94
- exploitable: true,
95
- confidence: 0.7,
96
- attackVector: 'Adversarial verification not yet integrated',
97
- payload: '',
98
- reasoning: 'Marked as potentially exploitable until AI attacker confirms',
99
- };
114
+ try {
115
+ const prompt = buildAdversarialPrompt(trace, code);
116
+ const response = await callAI(aiProvider, prompt);
117
+
118
+ const result = parseAdversarialResult(response.text);
119
+
120
+ if (!result) {
121
+ console.warn('⚠️ Failed to parse adversarial response');
122
+ return {
123
+ exploitable: true,
124
+ confidence: 0.6,
125
+ attackVector: 'Failed to parse AI response',
126
+ payload: '',
127
+ reasoning: 'Parser error - marked as potentially exploitable',
128
+ };
129
+ }
130
+
131
+ return result;
132
+ } catch (error) {
133
+ console.warn(`⚠️ Adversarial verification failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
134
+
135
+ return {
136
+ exploitable: true,
137
+ confidence: 0.7,
138
+ attackVector: `AI verification failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
139
+ payload: '',
140
+ reasoning: 'Marked as potentially exploitable due to verification error',
141
+ };
142
+ }
100
143
  }
@@ -1,3 +1,10 @@
1
+ /**
2
+ * FivoSense - AI Security Scanner
3
+ * Copyright (c) 2026 thevinsoni
4
+ * Licensed under the MIT License
5
+ * https://github.com/thevinsoni/sense
6
+ */
7
+
1
8
  /**
2
9
  * FivoCore Graph Builder
3
10
  */