Skip to content

Commit b2e79b3

Browse files
committed
local secret detection
1 parent a20d84a commit b2e79b3

File tree

7 files changed

+5304
-136
lines changed

7 files changed

+5304
-136
lines changed

changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## [0.3.5] - 01/04/2026
4+
- Local secret detection
5+
36
## [0.3.4] - 25/03/2026
47
- Telemetry opt-out flag
58

package-lock.json

Lines changed: 15 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codeant-cli",
3-
"version": "0.3.4",
3+
"version": "0.3.5",
44
"description": "Code review CLI tool",
55
"type": "module",
66
"bin": {
@@ -40,7 +40,8 @@
4040
"minimatch": "^10.1.1",
4141
"open": "^10.1.0",
4242
"posthog-node": "^5.28.5",
43-
"react": "^18.3.1"
43+
"react": "^18.3.1",
44+
"smol-toml": "^1.6.1"
4445
},
4546
"devDependencies": {
4647
"vitest": "^1.6.1"

src/commands/secrets.js

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import { useState, useEffect } from 'react';
22
import { useApp } from 'ink';
3-
import { getConfigValue } from '../utils/config.js';
4-
import { fetchApi } from '../utils/fetchApi.js';
53
import SecretsApiHelper from '../utils/secretsApiHelper.js';
4+
import { detectSecrets } from '../utils/secretsDetector.js';
65
import {
76
renderInitializing,
87
renderFetchingDiff,
98
renderScanning,
109
renderNoFiles,
1110
renderError,
12-
renderNotLoggedIn,
1311
renderDone,
1412
} from '../components/SecretsUI.js';
1513

@@ -22,30 +20,12 @@ export default function Secrets({ scanType = 'all', failOn = 'CRITICAL', include
2220
const [scanMeta, setScanMeta] = useState(null);
2321
const [startTime] = useState(() => Date.now());
2422

25-
const apiKey = getConfigValue('apiKey');
26-
27-
// Handle not logged in state
28-
useEffect(() => {
29-
if (!apiKey) {
30-
if (process.stdin.isTTY) {
31-
return;
32-
}
33-
const id = setTimeout(() => exit(new Error('Not logged in')), 0);
34-
return () => clearTimeout(id);
35-
}
36-
}, [apiKey, exit]);
37-
38-
if (!apiKey) {
39-
return renderNotLoggedIn();
40-
}
41-
42-
// Helper to check if a secret should cause failure based on failOn level
4323
const shouldFailOn = (confidenceScore) => {
4424
const score = confidenceScore?.toUpperCase();
4525
if (score === 'FALSE_POSITIVE') return false;
4626
if (failOn === 'HIGH') return score === 'HIGH';
4727
if (failOn === 'MEDIUM') return score === 'HIGH' || score === 'MEDIUM';
48-
return true; // 'all' - fail on any non-false-positive
28+
return true;
4929
};
5030

5131
useEffect(() => {
@@ -56,7 +36,6 @@ export default function Secrets({ scanType = 'all', failOn = 'CRITICAL', include
5636
if (cancelled) return;
5737
setStatus('fetching_diff');
5838

59-
// Initialize git helper and get files
6039
const helper = new SecretsApiHelper(process.cwd());
6140
await helper.init();
6241

@@ -66,8 +45,6 @@ export default function Secrets({ scanType = 'all', failOn = 'CRITICAL', include
6645

6746
const meta = requestBody._meta || null;
6847
setScanMeta(meta);
69-
70-
// Strip _meta before sending to API
7148
delete requestBody._meta;
7249

7350
if (requestBody.files.length === 0) {
@@ -78,17 +55,10 @@ export default function Secrets({ scanType = 'all', failOn = 'CRITICAL', include
7855
setFileCount(requestBody.files.length);
7956
setStatus('scanning');
8057

81-
// Call the secrets detection API
82-
const response = await fetchApi(
83-
'/extension/pr-review/secrets-detection',
84-
'POST',
85-
requestBody
86-
);
58+
// Local detection — no API call needed
59+
const detectedSecrets = detectSecrets(requestBody.files);
8760

8861
if (cancelled) return;
89-
const detectedSecrets = response.secretsDetected || [];
90-
91-
// Filter to only include files with actual secrets
9262
const filesWithSecrets = detectedSecrets.filter(
9363
file => file.secrets && file.secrets.length > 0
9464
);
@@ -106,25 +76,16 @@ export default function Secrets({ scanType = 'all', failOn = 'CRITICAL', include
10676
}
10777

10878
scanSecrets();
109-
110-
return () => {
111-
cancelled = true;
112-
};
79+
return () => { cancelled = true; };
11380
}, [scanType, include, exclude, lastNCommits, baseBranch, baseCommit]);
11481

115-
// Handle exit after status changes
11682
useEffect(() => {
11783
if (status === 'done') {
118-
// Check if any secrets should cause failure based on failOn level
11984
const hasBlockingSecrets = secrets.some(file =>
12085
file.secrets.some(secret => shouldFailOn(secret.confidence_score))
12186
);
122-
12387
if (hasBlockingSecrets) {
124-
setTimeout(() => {
125-
process.exitCode = 1;
126-
exit(new Error('Secrets detected'));
127-
}, 100);
88+
setTimeout(() => { process.exitCode = 1; exit(new Error('Secrets detected')); }, 100);
12889
} else {
12990
setTimeout(() => exit(), 100);
13091
}
@@ -135,8 +96,6 @@ export default function Secrets({ scanType = 'all', failOn = 'CRITICAL', include
13596
}
13697
}, [status, secrets]);
13798

138-
// ── Renders ──────────────────────────────────────────────────────────────
139-
14099
if (status === 'initializing') return renderInitializing(startTime);
141100
if (status === 'fetching_diff') return renderFetchingDiff(startTime);
142101
if (status === 'scanning') return renderScanning(startTime, fileCount, scanMeta);

0 commit comments

Comments
 (0)