@oculum/cli 1.0.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.
- package/LICENSE +21 -0
- package/README.md +425 -0
- package/dist/commands/auth.d.ts +11 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +156 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/scan.d.ts +23 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +323 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/watch.d.ts +10 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +231 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47624 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/api.d.ts +42 -0
- package/dist/utils/api.d.ts.map +1 -0
- package/dist/utils/api.js +120 -0
- package/dist/utils/api.js.map +1 -0
- package/dist/utils/config.d.ts +50 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +91 -0
- package/dist/utils/config.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Scan Command
|
|
4
|
+
* Main scanning functionality for the CLI
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.scanCommand = void 0;
|
|
11
|
+
exports.runScan = runScan;
|
|
12
|
+
const commander_1 = require("commander");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
const fs_1 = require("fs");
|
|
15
|
+
const glob_1 = require("glob");
|
|
16
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
17
|
+
const ora_1 = __importDefault(require("ora"));
|
|
18
|
+
const scanner_1 = require("@oculum/scanner");
|
|
19
|
+
const formatters_1 = require("@oculum/scanner/formatters");
|
|
20
|
+
const config_js_1 = require("../utils/config.js");
|
|
21
|
+
const api_js_1 = require("../utils/api.js");
|
|
22
|
+
/**
|
|
23
|
+
* Check if file should be scanned
|
|
24
|
+
*/
|
|
25
|
+
function isScannableFile(filePath) {
|
|
26
|
+
const ext = (0, path_1.extname)(filePath).toLowerCase();
|
|
27
|
+
const fileName = (0, path_1.basename)(filePath);
|
|
28
|
+
// Check special files first
|
|
29
|
+
if (scanner_1.SPECIAL_FILES.includes(fileName)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// Check extensions
|
|
33
|
+
return scanner_1.SCANNABLE_EXTENSIONS.includes(ext);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Map file extension to language
|
|
37
|
+
*/
|
|
38
|
+
function getLanguage(filePath) {
|
|
39
|
+
const ext = (0, path_1.extname)(filePath).toLowerCase();
|
|
40
|
+
const langMap = {
|
|
41
|
+
'.js': 'javascript',
|
|
42
|
+
'.jsx': 'javascript',
|
|
43
|
+
'.mjs': 'javascript',
|
|
44
|
+
'.cjs': 'javascript',
|
|
45
|
+
'.ts': 'typescript',
|
|
46
|
+
'.tsx': 'typescript',
|
|
47
|
+
'.py': 'python',
|
|
48
|
+
'.go': 'go',
|
|
49
|
+
'.java': 'java',
|
|
50
|
+
'.rb': 'ruby',
|
|
51
|
+
'.php': 'php',
|
|
52
|
+
'.cs': 'csharp',
|
|
53
|
+
'.yaml': 'yaml',
|
|
54
|
+
'.yml': 'yaml',
|
|
55
|
+
'.json': 'json',
|
|
56
|
+
'.toml': 'toml',
|
|
57
|
+
'.env': 'dotenv',
|
|
58
|
+
'.sh': 'shell',
|
|
59
|
+
'.bash': 'shell',
|
|
60
|
+
};
|
|
61
|
+
return langMap[ext] || 'text';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Collect files to scan from a directory
|
|
65
|
+
*/
|
|
66
|
+
async function collectFiles(targetPath) {
|
|
67
|
+
const absolutePath = (0, path_1.resolve)(targetPath);
|
|
68
|
+
const stats = (0, fs_1.statSync)(absolutePath);
|
|
69
|
+
const files = [];
|
|
70
|
+
if (stats.isFile()) {
|
|
71
|
+
// Single file scan
|
|
72
|
+
if (isScannableFile(absolutePath)) {
|
|
73
|
+
const content = (0, fs_1.readFileSync)(absolutePath, 'utf-8');
|
|
74
|
+
if (content.length <= scanner_1.MAX_FILE_SIZE) {
|
|
75
|
+
files.push({
|
|
76
|
+
path: (0, path_1.relative)(process.cwd(), absolutePath),
|
|
77
|
+
content,
|
|
78
|
+
language: getLanguage(absolutePath),
|
|
79
|
+
size: content.length,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (stats.isDirectory()) {
|
|
85
|
+
// Directory scan - use glob to find all files
|
|
86
|
+
const patterns = [
|
|
87
|
+
'**/*.js',
|
|
88
|
+
'**/*.jsx',
|
|
89
|
+
'**/*.ts',
|
|
90
|
+
'**/*.tsx',
|
|
91
|
+
'**/*.py',
|
|
92
|
+
'**/*.go',
|
|
93
|
+
'**/*.java',
|
|
94
|
+
'**/*.rb',
|
|
95
|
+
'**/*.php',
|
|
96
|
+
'**/*.cs',
|
|
97
|
+
'**/*.yaml',
|
|
98
|
+
'**/*.yml',
|
|
99
|
+
'**/*.json',
|
|
100
|
+
'**/*.toml',
|
|
101
|
+
'**/*.env*',
|
|
102
|
+
'**/Dockerfile*',
|
|
103
|
+
'**/docker-compose*.yml',
|
|
104
|
+
'**/package.json',
|
|
105
|
+
'**/.env*',
|
|
106
|
+
];
|
|
107
|
+
const ignorePatterns = [
|
|
108
|
+
'**/node_modules/**',
|
|
109
|
+
'**/dist/**',
|
|
110
|
+
'**/build/**',
|
|
111
|
+
'**/.git/**',
|
|
112
|
+
'**/vendor/**',
|
|
113
|
+
'**/__pycache__/**',
|
|
114
|
+
'**/venv/**',
|
|
115
|
+
'**/.venv/**',
|
|
116
|
+
'**/coverage/**',
|
|
117
|
+
'**/.next/**',
|
|
118
|
+
'**/.nuxt/**',
|
|
119
|
+
];
|
|
120
|
+
const foundFiles = await (0, glob_1.glob)(patterns, {
|
|
121
|
+
cwd: absolutePath,
|
|
122
|
+
ignore: ignorePatterns,
|
|
123
|
+
nodir: true,
|
|
124
|
+
absolute: true,
|
|
125
|
+
});
|
|
126
|
+
for (const filePath of foundFiles) {
|
|
127
|
+
try {
|
|
128
|
+
const fileStats = (0, fs_1.statSync)(filePath);
|
|
129
|
+
if (fileStats.size > scanner_1.MAX_FILE_SIZE)
|
|
130
|
+
continue;
|
|
131
|
+
const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
132
|
+
files.push({
|
|
133
|
+
path: (0, path_1.relative)(process.cwd(), filePath),
|
|
134
|
+
content,
|
|
135
|
+
language: getLanguage(filePath),
|
|
136
|
+
size: content.length,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Skip files that can't be read
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return files;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Format result based on output format
|
|
148
|
+
*/
|
|
149
|
+
function formatOutput(result, format, noColor) {
|
|
150
|
+
switch (format) {
|
|
151
|
+
case 'json':
|
|
152
|
+
return (0, formatters_1.formatJSON)(result, true);
|
|
153
|
+
case 'sarif':
|
|
154
|
+
return JSON.stringify((0, formatters_1.formatSARIF)(result), null, 2);
|
|
155
|
+
case 'markdown':
|
|
156
|
+
return formatMarkdown(result);
|
|
157
|
+
case 'terminal':
|
|
158
|
+
default:
|
|
159
|
+
return (0, formatters_1.formatTerminalOutput)(result, { noColor });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Format as markdown (for docs/reports)
|
|
164
|
+
*/
|
|
165
|
+
function formatMarkdown(result) {
|
|
166
|
+
const { vulnerabilities, severityCounts, filesScanned, scanDuration } = result;
|
|
167
|
+
let md = `# Oculum Security Scan Report\n\n`;
|
|
168
|
+
md += `**Scanned:** ${filesScanned} files in ${(scanDuration / 1000).toFixed(1)}s\n\n`;
|
|
169
|
+
// Summary table
|
|
170
|
+
md += `## Summary\n\n`;
|
|
171
|
+
md += `| Severity | Count |\n`;
|
|
172
|
+
md += `|----------|-------|\n`;
|
|
173
|
+
md += `| Critical | ${severityCounts.critical} |\n`;
|
|
174
|
+
md += `| High | ${severityCounts.high} |\n`;
|
|
175
|
+
md += `| Medium | ${severityCounts.medium} |\n`;
|
|
176
|
+
md += `| Low | ${severityCounts.low} |\n`;
|
|
177
|
+
md += `| Info | ${severityCounts.info} |\n\n`;
|
|
178
|
+
if (vulnerabilities.length === 0) {
|
|
179
|
+
md += `## Findings\n\n`;
|
|
180
|
+
md += `No security issues found.\n`;
|
|
181
|
+
return md;
|
|
182
|
+
}
|
|
183
|
+
// Findings by severity
|
|
184
|
+
md += `## Findings\n\n`;
|
|
185
|
+
for (const severity of ['critical', 'high', 'medium', 'low', 'info']) {
|
|
186
|
+
const findings = vulnerabilities.filter(v => v.severity === severity);
|
|
187
|
+
if (findings.length === 0)
|
|
188
|
+
continue;
|
|
189
|
+
md += `### ${severity.charAt(0).toUpperCase() + severity.slice(1)} (${findings.length})\n\n`;
|
|
190
|
+
for (const finding of findings) {
|
|
191
|
+
md += `#### ${finding.title}\n\n`;
|
|
192
|
+
md += `- **File:** \`${finding.filePath}:${finding.lineNumber}\`\n`;
|
|
193
|
+
md += `- **Category:** ${finding.category}\n`;
|
|
194
|
+
md += `- **Description:** ${finding.description}\n`;
|
|
195
|
+
if (finding.suggestedFix) {
|
|
196
|
+
md += `- **Fix:** ${finding.suggestedFix}\n`;
|
|
197
|
+
}
|
|
198
|
+
md += `\n`;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return md;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get severity rank for comparison
|
|
205
|
+
*/
|
|
206
|
+
function severityRank(severity) {
|
|
207
|
+
const ranks = {
|
|
208
|
+
critical: 5,
|
|
209
|
+
high: 4,
|
|
210
|
+
medium: 3,
|
|
211
|
+
low: 2,
|
|
212
|
+
info: 1,
|
|
213
|
+
none: 0,
|
|
214
|
+
};
|
|
215
|
+
return ranks[severity] || 0;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Main scan function
|
|
219
|
+
*/
|
|
220
|
+
async function runScan(targetPath, options) {
|
|
221
|
+
const spinner = (0, ora_1.default)();
|
|
222
|
+
const config = (0, config_js_1.getConfig)();
|
|
223
|
+
const noColor = options.color === false;
|
|
224
|
+
// Validate depth against auth
|
|
225
|
+
if ((options.depth === 'validated' || options.depth === 'deep') && !(0, config_js_1.isAuthenticated)()) {
|
|
226
|
+
console.log(chalk_1.default.yellow('\nNote: validated and deep scans require authentication.'));
|
|
227
|
+
console.log(chalk_1.default.dim('Run `oculum login` to authenticate, or use `--depth cheap` for free local scans.\n'));
|
|
228
|
+
console.log(chalk_1.default.dim('Falling back to cheap scan...\n'));
|
|
229
|
+
options.depth = 'cheap';
|
|
230
|
+
}
|
|
231
|
+
// Collect files
|
|
232
|
+
spinner.start('Collecting files to scan...');
|
|
233
|
+
let files;
|
|
234
|
+
try {
|
|
235
|
+
files = await collectFiles(targetPath);
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
spinner.fail(`Failed to collect files: ${err}`);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
if (files.length === 0) {
|
|
242
|
+
spinner.fail('No scannable files found');
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
spinner.succeed(`Found ${files.length} files to scan`);
|
|
246
|
+
// Progress callback
|
|
247
|
+
const onProgress = (progress) => {
|
|
248
|
+
switch (progress.status) {
|
|
249
|
+
case 'layer1':
|
|
250
|
+
spinner.text = `Layer 1: Pattern matching... (${progress.vulnerabilitiesFound} findings)`;
|
|
251
|
+
break;
|
|
252
|
+
case 'layer2':
|
|
253
|
+
spinner.text = `Layer 2: Structural analysis... (${progress.vulnerabilitiesFound} findings)`;
|
|
254
|
+
break;
|
|
255
|
+
case 'validating':
|
|
256
|
+
spinner.text = `AI validation... (${progress.vulnerabilitiesFound} candidates)`;
|
|
257
|
+
break;
|
|
258
|
+
case 'layer3':
|
|
259
|
+
spinner.text = `Layer 3: AI semantic analysis...`;
|
|
260
|
+
break;
|
|
261
|
+
case 'complete':
|
|
262
|
+
spinner.succeed(`Scan complete: ${progress.vulnerabilitiesFound} issues found`);
|
|
263
|
+
break;
|
|
264
|
+
case 'failed':
|
|
265
|
+
spinner.fail(progress.message);
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
// Run scan
|
|
270
|
+
let result;
|
|
271
|
+
try {
|
|
272
|
+
spinner.start('Starting scan...');
|
|
273
|
+
// For validated/deep scans, call the backend API
|
|
274
|
+
if (options.depth !== 'cheap' && (0, config_js_1.isAuthenticated)()) {
|
|
275
|
+
result = await (0, api_js_1.callBackendAPI)(files, options.depth, config.apiKey);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
// Local scan for cheap depth
|
|
279
|
+
result = await (0, scanner_1.runScan)(files, {
|
|
280
|
+
name: (0, path_1.basename)((0, path_1.resolve)(targetPath)),
|
|
281
|
+
url: '',
|
|
282
|
+
branch: 'local',
|
|
283
|
+
}, {
|
|
284
|
+
enableAI: false, // Cheap mode = no AI
|
|
285
|
+
scanDepth: 'cheap',
|
|
286
|
+
}, onProgress);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
spinner.fail(`Scan failed: ${err}`);
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
// Output results
|
|
294
|
+
const output = formatOutput(result, options.format, noColor);
|
|
295
|
+
console.log(output);
|
|
296
|
+
// Write to file if specified
|
|
297
|
+
if (options.output) {
|
|
298
|
+
const { writeFileSync } = await import('fs');
|
|
299
|
+
writeFileSync(options.output, output);
|
|
300
|
+
console.log(chalk_1.default.dim(`\nResults written to ${options.output}`));
|
|
301
|
+
}
|
|
302
|
+
// Exit code based on findings
|
|
303
|
+
const failThreshold = severityRank(options.failOn);
|
|
304
|
+
const highestSeverity = result.vulnerabilities.reduce((max, v) => Math.max(max, severityRank(v.severity)), 0);
|
|
305
|
+
if (highestSeverity >= failThreshold) {
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Scan command definition
|
|
311
|
+
*/
|
|
312
|
+
exports.scanCommand = new commander_1.Command('scan')
|
|
313
|
+
.description('Scan a directory or file for security vulnerabilities')
|
|
314
|
+
.argument('[path]', 'path to scan', '.')
|
|
315
|
+
.option('-d, --depth <depth>', 'scan depth: cheap (free), validated, deep', 'cheap')
|
|
316
|
+
.option('-f, --format <format>', 'output format: terminal, json, sarif, markdown', 'terminal')
|
|
317
|
+
.option('--fail-on <severity>', 'exit with error code if findings at severity', 'high')
|
|
318
|
+
.option('--no-color', 'disable colored output')
|
|
319
|
+
.option('-o, --output <file>', 'write output to file')
|
|
320
|
+
.option('--incremental', 'only scan changed files (requires git)')
|
|
321
|
+
.option('--diff <ref>', 'diff against branch/commit for incremental scan')
|
|
322
|
+
.action(runScan);
|
|
323
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAmPnC;;GAEG;AACH,eAAO,MAAM,YAAY,SAYrB,CAAA"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Watch Command
|
|
4
|
+
* File watcher for continuous scanning during development
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.watchCommand = void 0;
|
|
11
|
+
const commander_1 = require("commander");
|
|
12
|
+
const path_1 = require("path");
|
|
13
|
+
const fs_1 = require("fs");
|
|
14
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
15
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
16
|
+
const scanner_1 = require("@oculum/scanner");
|
|
17
|
+
const formatters_1 = require("@oculum/scanner/formatters");
|
|
18
|
+
const config_js_1 = require("../utils/config.js");
|
|
19
|
+
/**
|
|
20
|
+
* Check if file should be scanned
|
|
21
|
+
*/
|
|
22
|
+
function isScannableFile(filePath) {
|
|
23
|
+
const ext = (0, path_1.extname)(filePath).toLowerCase();
|
|
24
|
+
const fileName = (0, path_1.basename)(filePath);
|
|
25
|
+
if (scanner_1.SPECIAL_FILES.includes(fileName))
|
|
26
|
+
return true;
|
|
27
|
+
return scanner_1.SCANNABLE_EXTENSIONS.includes(ext);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Map file extension to language
|
|
31
|
+
*/
|
|
32
|
+
function getLanguage(filePath) {
|
|
33
|
+
const ext = (0, path_1.extname)(filePath).toLowerCase();
|
|
34
|
+
const langMap = {
|
|
35
|
+
'.js': 'javascript',
|
|
36
|
+
'.jsx': 'javascript',
|
|
37
|
+
'.ts': 'typescript',
|
|
38
|
+
'.tsx': 'typescript',
|
|
39
|
+
'.py': 'python',
|
|
40
|
+
'.go': 'go',
|
|
41
|
+
'.java': 'java',
|
|
42
|
+
'.rb': 'ruby',
|
|
43
|
+
'.php': 'php',
|
|
44
|
+
'.yaml': 'yaml',
|
|
45
|
+
'.yml': 'yaml',
|
|
46
|
+
'.json': 'json',
|
|
47
|
+
};
|
|
48
|
+
return langMap[ext] || 'text';
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Read file as ScanFile
|
|
52
|
+
*/
|
|
53
|
+
function readScanFile(filePath) {
|
|
54
|
+
try {
|
|
55
|
+
const stats = (0, fs_1.statSync)(filePath);
|
|
56
|
+
if (stats.size > scanner_1.MAX_FILE_SIZE)
|
|
57
|
+
return null;
|
|
58
|
+
const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
59
|
+
return {
|
|
60
|
+
path: (0, path_1.relative)(process.cwd(), filePath),
|
|
61
|
+
content,
|
|
62
|
+
language: getLanguage(filePath),
|
|
63
|
+
size: content.length,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Debounce function
|
|
72
|
+
*/
|
|
73
|
+
function debounce(fn, delay) {
|
|
74
|
+
let timeoutId = null;
|
|
75
|
+
return (...args) => {
|
|
76
|
+
if (timeoutId)
|
|
77
|
+
clearTimeout(timeoutId);
|
|
78
|
+
timeoutId = setTimeout(() => fn(...args), delay);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Watch command
|
|
83
|
+
*/
|
|
84
|
+
async function watch(targetPath, options) {
|
|
85
|
+
const absolutePath = (0, path_1.resolve)(targetPath);
|
|
86
|
+
const config = (0, config_js_1.getConfig)();
|
|
87
|
+
// Validate depth against auth
|
|
88
|
+
if ((options.depth === 'validated' || options.depth === 'deep') && !(0, config_js_1.isAuthenticated)()) {
|
|
89
|
+
console.log(chalk_1.default.yellow('\nNote: validated and deep scans require authentication.'));
|
|
90
|
+
console.log(chalk_1.default.dim('Run `oculum login` to authenticate.\n'));
|
|
91
|
+
options.depth = 'cheap';
|
|
92
|
+
}
|
|
93
|
+
console.log(chalk_1.default.bold('\nOculum Watch Mode'));
|
|
94
|
+
console.log(chalk_1.default.dim('─'.repeat(40)));
|
|
95
|
+
console.log(chalk_1.default.dim(`Watching: ${absolutePath}`));
|
|
96
|
+
console.log(chalk_1.default.dim(`Depth: ${options.depth}`));
|
|
97
|
+
console.log(chalk_1.default.dim(`Debounce: ${options.debounce}ms`));
|
|
98
|
+
console.log(chalk_1.default.dim('\nPress Ctrl+C to stop\n'));
|
|
99
|
+
// Track changed files for incremental scanning
|
|
100
|
+
const changedFiles = new Set();
|
|
101
|
+
let isScanning = false;
|
|
102
|
+
// Scan function (debounced)
|
|
103
|
+
const runScanOnChanges = debounce(async () => {
|
|
104
|
+
if (isScanning || changedFiles.size === 0)
|
|
105
|
+
return;
|
|
106
|
+
isScanning = true;
|
|
107
|
+
const filesToScan = Array.from(changedFiles);
|
|
108
|
+
changedFiles.clear();
|
|
109
|
+
// Clear console if requested
|
|
110
|
+
if (options.clearOnScan) {
|
|
111
|
+
console.clear();
|
|
112
|
+
}
|
|
113
|
+
console.log(chalk_1.default.cyan(`\n[${new Date().toLocaleTimeString()}] Scanning ${filesToScan.length} changed file(s)...`));
|
|
114
|
+
// Read files
|
|
115
|
+
const scanFiles = [];
|
|
116
|
+
for (const filePath of filesToScan) {
|
|
117
|
+
const file = readScanFile(filePath);
|
|
118
|
+
if (file)
|
|
119
|
+
scanFiles.push(file);
|
|
120
|
+
}
|
|
121
|
+
if (scanFiles.length === 0) {
|
|
122
|
+
console.log(chalk_1.default.dim('No scannable files found.'));
|
|
123
|
+
isScanning = false;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const result = await (0, scanner_1.runScan)(scanFiles, {
|
|
128
|
+
name: (0, path_1.basename)(absolutePath),
|
|
129
|
+
url: '',
|
|
130
|
+
branch: 'watch',
|
|
131
|
+
}, {
|
|
132
|
+
enableAI: options.depth !== 'cheap' && (0, config_js_1.isAuthenticated)(),
|
|
133
|
+
scanDepth: options.depth,
|
|
134
|
+
scanMode: 'incremental',
|
|
135
|
+
});
|
|
136
|
+
// Output results
|
|
137
|
+
if (result.vulnerabilities.length === 0) {
|
|
138
|
+
console.log(chalk_1.default.green('No issues found.'));
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
console.log((0, formatters_1.formatTerminalOutput)(result, {
|
|
142
|
+
maxFindingsPerGroup: 5,
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
console.error(chalk_1.default.red(`Scan error: ${err}`));
|
|
148
|
+
}
|
|
149
|
+
isScanning = false;
|
|
150
|
+
}, options.debounce);
|
|
151
|
+
// Set up watcher
|
|
152
|
+
const watcher = chokidar_1.default.watch(absolutePath, {
|
|
153
|
+
ignored: [
|
|
154
|
+
'**/node_modules/**',
|
|
155
|
+
'**/dist/**',
|
|
156
|
+
'**/build/**',
|
|
157
|
+
'**/.git/**',
|
|
158
|
+
'**/vendor/**',
|
|
159
|
+
'**/__pycache__/**',
|
|
160
|
+
'**/venv/**',
|
|
161
|
+
'**/.venv/**',
|
|
162
|
+
'**/coverage/**',
|
|
163
|
+
'**/.next/**',
|
|
164
|
+
'**/.nuxt/**',
|
|
165
|
+
],
|
|
166
|
+
persistent: true,
|
|
167
|
+
ignoreInitial: true,
|
|
168
|
+
});
|
|
169
|
+
watcher.on('change', (filePath) => {
|
|
170
|
+
if (!isScannableFile(filePath))
|
|
171
|
+
return;
|
|
172
|
+
changedFiles.add((0, path_1.resolve)(filePath));
|
|
173
|
+
console.log(chalk_1.default.dim(`Changed: ${(0, path_1.relative)(process.cwd(), filePath)}`));
|
|
174
|
+
runScanOnChanges();
|
|
175
|
+
});
|
|
176
|
+
watcher.on('add', (filePath) => {
|
|
177
|
+
if (!isScannableFile(filePath))
|
|
178
|
+
return;
|
|
179
|
+
changedFiles.add((0, path_1.resolve)(filePath));
|
|
180
|
+
console.log(chalk_1.default.dim(`Added: ${(0, path_1.relative)(process.cwd(), filePath)}`));
|
|
181
|
+
runScanOnChanges();
|
|
182
|
+
});
|
|
183
|
+
watcher.on('error', (error) => {
|
|
184
|
+
console.error(chalk_1.default.red(`Watcher error: ${error}`));
|
|
185
|
+
});
|
|
186
|
+
// Handle shutdown
|
|
187
|
+
process.on('SIGINT', () => {
|
|
188
|
+
console.log(chalk_1.default.dim('\n\nStopping watch mode...'));
|
|
189
|
+
watcher.close();
|
|
190
|
+
process.exit(0);
|
|
191
|
+
});
|
|
192
|
+
// Initial scan
|
|
193
|
+
console.log(chalk_1.default.dim('Performing initial scan...'));
|
|
194
|
+
const { glob } = await import('glob');
|
|
195
|
+
const patterns = [
|
|
196
|
+
'**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx',
|
|
197
|
+
'**/*.py', '**/*.go', '**/*.java', '**/*.rb',
|
|
198
|
+
'**/*.php', '**/*.cs', '**/package.json',
|
|
199
|
+
];
|
|
200
|
+
const ignorePatterns = [
|
|
201
|
+
'**/node_modules/**', '**/dist/**', '**/build/**',
|
|
202
|
+
'**/.git/**', '**/vendor/**', '**/__pycache__/**',
|
|
203
|
+
];
|
|
204
|
+
const allFiles = await glob(patterns, {
|
|
205
|
+
cwd: absolutePath,
|
|
206
|
+
ignore: ignorePatterns,
|
|
207
|
+
nodir: true,
|
|
208
|
+
absolute: true,
|
|
209
|
+
});
|
|
210
|
+
for (const filePath of allFiles) {
|
|
211
|
+
changedFiles.add(filePath);
|
|
212
|
+
}
|
|
213
|
+
runScanOnChanges();
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Watch command definition
|
|
217
|
+
*/
|
|
218
|
+
exports.watchCommand = new commander_1.Command('watch')
|
|
219
|
+
.description('Watch files and scan on changes')
|
|
220
|
+
.argument('[path]', 'path to watch', '.')
|
|
221
|
+
.option('-d, --depth <depth>', 'scan depth: cheap, validated, deep', 'cheap')
|
|
222
|
+
.option('--debounce <ms>', 'debounce time in milliseconds', '500')
|
|
223
|
+
.option('--clear', 'clear console before each scan', false)
|
|
224
|
+
.action((path, options) => {
|
|
225
|
+
watch(path, {
|
|
226
|
+
depth: options.depth,
|
|
227
|
+
debounce: parseInt(options.debounce, 10),
|
|
228
|
+
clearOnScan: options.clear,
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG"}
|