codeant-cli 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/178.index.js +1 -1
- package/dist/239.index.js +1 -1
- package/dist/701.index.js +1 -1
- package/dist/880.index.js +1 -1
- package/dist/935.index.js +1 -1
- package/dist/966.index.js +1 -1
- package/dist/index.js +4 -4
- package/package.json +1 -1
- package/src/commands/secrets.js +40 -160
- package/src/components/SecretsUI.js +366 -0
- package/src/index.js +25 -12
- package/src/utils/secretsApiHelper.js +109 -1
package/src/index.js
CHANGED
|
@@ -49,26 +49,39 @@ if (process.argv.length === 2) {
|
|
|
49
49
|
program
|
|
50
50
|
.command('secrets')
|
|
51
51
|
.description('Scan for secrets in your code')
|
|
52
|
-
.option('--
|
|
53
|
-
.option('--
|
|
54
|
-
.option('--uncommitted', 'Scan uncommitted changes')
|
|
52
|
+
.option('--all', 'Scan committed + uncommitted changes (default)')
|
|
53
|
+
.option('--committed', 'Scan only unpushed commits')
|
|
54
|
+
.option('--uncommitted', 'Scan only uncommitted changes (staged + unstaged + untracked)')
|
|
55
|
+
.option('--staged', 'Scan only staged files')
|
|
55
56
|
.option('--last-commit', 'Scan last commit')
|
|
56
57
|
.option('--last-n-commits <n>', 'Scan last n commits (max 5)', parseInt)
|
|
57
|
-
.option('--
|
|
58
|
-
.option('--
|
|
59
|
-
.option('--
|
|
58
|
+
.option('--base <branch>', 'Compare against a specific base branch (e.g. --base develop)')
|
|
59
|
+
.option('--base-commit <commit>', 'Compare against a specific commit (e.g. --base-commit HEAD~3)')
|
|
60
|
+
.option('--fail-on <level>', 'Fail on issues at or above this level: BLOCKER, CRITICAL, MAJOR, MINOR, INFO (default: CRITICAL)', 'CRITICAL')
|
|
61
|
+
.option('--include <paths>', 'Comma-separated list of file paths glob patterns to include')
|
|
62
|
+
.option('--exclude <paths>', 'Comma-separated list of file paths glob patterns to exclude')
|
|
60
63
|
.action((options) => {
|
|
61
|
-
let scanType = '
|
|
64
|
+
let scanType = 'all';
|
|
62
65
|
let lastNCommits = 1;
|
|
66
|
+
let baseBranch = null;
|
|
67
|
+
let baseCommit = null;
|
|
63
68
|
|
|
69
|
+
if (options.base) {
|
|
70
|
+
scanType = 'base-branch';
|
|
71
|
+
baseBranch = options.base;
|
|
72
|
+
} else if (options.baseCommit) {
|
|
73
|
+
scanType = 'base-commit';
|
|
74
|
+
baseCommit = options.baseCommit;
|
|
64
75
|
// Check !== undefined, not truthy — passing 0 is valid and shouldn't fall through
|
|
65
|
-
if (options.lastNCommits !== undefined) {
|
|
76
|
+
} else if (options.lastNCommits !== undefined) {
|
|
66
77
|
scanType = 'last-n-commits';
|
|
67
78
|
lastNCommits = Math.min(Math.max(1, options.lastNCommits), 5);
|
|
68
|
-
} else if (options.
|
|
69
|
-
scanType = '
|
|
79
|
+
} else if (options.committed) {
|
|
80
|
+
scanType = 'committed';
|
|
70
81
|
} else if (options.uncommitted) {
|
|
71
82
|
scanType = 'uncommitted';
|
|
83
|
+
} else if (options.staged) {
|
|
84
|
+
scanType = 'staged-only';
|
|
72
85
|
} else if (options.lastCommit) {
|
|
73
86
|
scanType = 'last-commit';
|
|
74
87
|
}
|
|
@@ -81,9 +94,9 @@ program
|
|
|
81
94
|
? (Array.isArray(options.exclude) ? options.exclude : splitGlobs(options.exclude))
|
|
82
95
|
: [];
|
|
83
96
|
|
|
84
|
-
const failOn = options.failOn?.toUpperCase() || '
|
|
97
|
+
const failOn = options.failOn?.toUpperCase() || 'CRITICAL';
|
|
85
98
|
|
|
86
|
-
render(React.createElement(Secrets, { scanType, failOn, include, exclude, lastNCommits }));
|
|
99
|
+
render(React.createElement(Secrets, { scanType, failOn, include, exclude, lastNCommits, baseBranch, baseCommit }));
|
|
87
100
|
});
|
|
88
101
|
|
|
89
102
|
program
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import CommonApiHelper from './commonApiHelper.js';
|
|
2
|
+
import path from 'path';
|
|
2
3
|
import { minimatch } from 'minimatch';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -15,6 +16,58 @@ import { minimatch } from 'minimatch';
|
|
|
15
16
|
* ]
|
|
16
17
|
* }
|
|
17
18
|
*/
|
|
19
|
+
|
|
20
|
+
const EXCLUDED_FILENAMES = new Set([
|
|
21
|
+
'package-lock.json',
|
|
22
|
+
'yarn.lock',
|
|
23
|
+
'pnpm-lock.yaml',
|
|
24
|
+
'composer.lock',
|
|
25
|
+
'Gemfile.lock',
|
|
26
|
+
'Cargo.lock',
|
|
27
|
+
'poetry.lock',
|
|
28
|
+
'Pipfile.lock',
|
|
29
|
+
'go.sum',
|
|
30
|
+
'.DS_Store',
|
|
31
|
+
'Thumbs.db',
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const EXCLUDED_EXTENSIONS = new Set([
|
|
35
|
+
// Images
|
|
36
|
+
'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.svg', '.webp', '.avif',
|
|
37
|
+
// Fonts
|
|
38
|
+
'.woff', '.woff2', '.ttf', '.eot', '.otf',
|
|
39
|
+
// Binary / compiled
|
|
40
|
+
'.exe', '.dll', '.so', '.dylib', '.bin', '.o', '.a', '.class', '.pyc', '.pyo',
|
|
41
|
+
'.wasm', '.jar', '.war', '.ear',
|
|
42
|
+
// Archives
|
|
43
|
+
'.zip', '.tar', '.gz', '.bz2', '.7z', '.rar',
|
|
44
|
+
// Media
|
|
45
|
+
'.mp3', '.mp4', '.wav', '.avi', '.mov', '.flv', '.wmv', '.ogg', '.webm',
|
|
46
|
+
// Data / generated
|
|
47
|
+
'.min.js', '.min.css', '.map',
|
|
48
|
+
'.pb', '.proto.bin',
|
|
49
|
+
// PDF / docs
|
|
50
|
+
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
const MAX_FILE_LINES = 5000;
|
|
54
|
+
|
|
55
|
+
function isScannableFile(filePath) {
|
|
56
|
+
const basename = path.basename(filePath);
|
|
57
|
+
|
|
58
|
+
if (EXCLUDED_FILENAMES.has(basename)) return false;
|
|
59
|
+
|
|
60
|
+
const ext = path.extname(basename).toLowerCase();
|
|
61
|
+
if (EXCLUDED_EXTENSIONS.has(ext)) return false;
|
|
62
|
+
|
|
63
|
+
if (basename.endsWith('.min.js') || basename.endsWith('.min.css')) return false;
|
|
64
|
+
|
|
65
|
+
// Skip markdown files
|
|
66
|
+
if (ext === '.md') return false;
|
|
67
|
+
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
18
71
|
class SecretsApiHelper extends CommonApiHelper {
|
|
19
72
|
/**
|
|
20
73
|
* Transform diff info array to API format
|
|
@@ -124,10 +177,65 @@ class SecretsApiHelper extends CommonApiHelper {
|
|
|
124
177
|
files = [];
|
|
125
178
|
}
|
|
126
179
|
|
|
180
|
+
const totalChanged = files.length;
|
|
181
|
+
const skipped = [];
|
|
182
|
+
|
|
183
|
+
// Filter out non-scannable files (binary, lock files, markdown, etc.)
|
|
184
|
+
files = files.filter(fileObj => {
|
|
185
|
+
const filePath = fileObj.file_path;
|
|
186
|
+
|
|
187
|
+
if (!isScannableFile(filePath)) {
|
|
188
|
+
skipped.push({ file: filePath, reason: 'binary/generated' });
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Skip test files — backend filters these out anyway
|
|
193
|
+
if (filePath.toLowerCase().includes('test')) {
|
|
194
|
+
skipped.push({ file: filePath, reason: 'test file' });
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Skip deleted files (empty code)
|
|
199
|
+
if (fileObj.code === '' || fileObj.code === undefined) {
|
|
200
|
+
skipped.push({ file: filePath, reason: 'deleted' });
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Skip files exceeding line limit
|
|
205
|
+
if (fileObj.code) {
|
|
206
|
+
const lineCount = fileObj.code.split('\n').length;
|
|
207
|
+
if (lineCount > MAX_FILE_LINES) {
|
|
208
|
+
skipped.push({ file: filePath, reason: `too large (${lineCount} lines, max ${MAX_FILE_LINES})` });
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return true;
|
|
214
|
+
});
|
|
215
|
+
|
|
127
216
|
// Apply include/exclude filters
|
|
217
|
+
const beforePatternFilter = files.length;
|
|
128
218
|
files = this._filterFiles(files, includePatterns, excludePatterns);
|
|
129
219
|
|
|
130
|
-
|
|
220
|
+
// Track pattern-excluded files
|
|
221
|
+
if (files.length < beforePatternFilter) {
|
|
222
|
+
// We don't have exact file names for pattern-excluded, but the count is in skipped
|
|
223
|
+
const patternExcluded = beforePatternFilter - files.length;
|
|
224
|
+
if (patternExcluded > 0) {
|
|
225
|
+
skipped.push({ file: `${patternExcluded} file(s)`, reason: 'excluded by pattern' });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const scannedFiles = files.map(f => f.file_path);
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
files,
|
|
233
|
+
_meta: {
|
|
234
|
+
scanned_files: scannedFiles,
|
|
235
|
+
total_changed: totalChanged,
|
|
236
|
+
skipped,
|
|
237
|
+
},
|
|
238
|
+
};
|
|
131
239
|
}
|
|
132
240
|
}
|
|
133
241
|
|