Skip to content

Commit d2a8d3b

Browse files
authored
Merge pull request #14 from CodeAnt-AI/feature/chinmay/secrets-api-key-fp
Fix generic-api-key false positive on import lines
2 parents 57ff16d + fccfdd3 commit d2a8d3b

4 files changed

Lines changed: 41 additions & 1 deletion

File tree

changelog.md

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

3+
## [0.4.6] - 17/04/2026
4+
- Secrets false positive
35

46
## [0.4.5] - 13/04/2026
57
- Selected commits review

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codeant-cli",
3-
"version": "0.4.5",
3+
"version": "0.4.6",
44
"description": "Code review CLI tool",
55
"type": "module",
66
"bin": {

src/rules/secrets.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,11 @@ id = "generic-api-key"
11731173
description = "Detected a Generic API Key, potentially exposing access to various services and sensitive operations."
11741174
regex = '''(?i)[\w.-]{0,50}?(?:access|auth|(?-i:[Aa]pi|API)|credential|creds|key|passw(?:or)?d|secret|token)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([\w.=-]{10,150}|[a-z0-9][a-z0-9+/]{11,}={0,3})(?:\\?['"\x60]|[\s;]|\\[nr]|$)'''
11751175
entropy = 3.5
1176+
# secretGroup = 1: the regex captures the actual secret value in group 1.
1177+
# Without this, the full match (including variable names, commas, spaces) is
1178+
# treated as the "secret", causing allowlist checks like ^[a-zA-Z_.-]+$ to fail
1179+
# on import/destructuring lines (e.g. "from config import STRIPE_API_KEY, SECRET").
1180+
secretGroup = 1
11761181
keywords = [
11771182
"access",
11781183
"api",

tests/secretsApiHelper.test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,39 @@ describe('Secrets detection e2e tests', () => {
188188
expect(result.secretsDetected.flatMap(f => f.secrets)).toHaveLength(0);
189189
}, 30000);
190190

191+
it('no secrets in Python import statements with keyword-like variable names', async () => {
192+
writeFile('src/app.py', [
193+
'import time',
194+
'from fastapi import Request',
195+
'from api.config import STRIPE_API_KEY, STRIPE_PAYMENT_WEBHOOK_SECRET',
196+
'from mangum import Mangum',
197+
'from starlette.responses import JSONResponse',
198+
''
199+
].join('\n'));
200+
git('add src/app.py');
201+
202+
const result = await scanSecrets('staged-only');
203+
204+
const pyFile = result.secretsDetected.find(f => f.file_path === 'src/app.py');
205+
const pySecrets = pyFile ? pyFile.secrets : [];
206+
expect(pySecrets).toHaveLength(0);
207+
}, 30000);
208+
209+
it('no secrets in JS destructured imports with keyword-like names', async () => {
210+
writeFile('src/config-loader.js', [
211+
'const { API_KEY, SECRET_TOKEN, AUTH_CREDENTIAL } = require("./config");',
212+
'module.exports = { API_KEY, SECRET_TOKEN, AUTH_CREDENTIAL };',
213+
''
214+
].join('\n'));
215+
git('add src/config-loader.js');
216+
217+
const result = await scanSecrets('staged-only');
218+
219+
const jsFile = result.secretsDetected.find(f => f.file_path === 'src/config-loader.js');
220+
const jsSecrets = jsFile ? jsFile.secrets : [];
221+
expect(jsSecrets).toHaveLength(0);
222+
}, 30000);
223+
191224
it('no secrets in placeholder/example values', async () => {
192225
writeFile('src/config.example.js', [
193226
'module.exports = {',

0 commit comments

Comments
 (0)