Skip to content

Commit 92f19ff

Browse files
authored
chore: test coverage (#367)
1 parent 27cc7a6 commit 92f19ff

5 files changed

Lines changed: 169 additions & 10 deletions

File tree

packages/cli/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
"keywords": [
4343
"dotenv",
4444
"env",
45+
"environment",
46+
"variables",
4547
"dotenv-diff",
4648
"env-check",
4749
"env-validate",

packages/cli/src/services/processComparisonFile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import type {
2323
/**
2424
* Result of processing comparison file
2525
*/
26-
interface ProcessComparisonResult {
26+
export interface ProcessComparisonResult {
2727
scanResult: ScanResult;
2828
envVariables: Record<string, string | undefined>;
2929
comparedAgainst: string;

packages/cli/test/unit/commands/scanUsage.test.ts

Lines changed: 112 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
} from '../../../src/config/types.js';
66
import { type SecretFinding } from '../../../src/core/security/secretDetectors.js';
77
import { promptNoEnvScenario } from '../../../src/commands/prompts/promptNoEnvScenario.js';
8+
import { ProcessComparisonResult } from '../../../src/services/processComparisonFile.js';
89

910
vi.mock('../../../src/services/scanCodebase.js', () => ({
1011
scanCodebase: vi.fn(),
@@ -129,7 +130,7 @@ describe('scanUsage', () => {
129130

130131
vi.mocked(processComparisonFile).mockReturnValue({
131132
error: { message: 'err', shouldExit: true },
132-
} as any);
133+
} as ProcessComparisonResult);
133134

134135
vi.mocked(printComparisonError).mockReturnValue({ exit: true });
135136

@@ -171,7 +172,7 @@ describe('scanUsage', () => {
171172
severity: 'medium',
172173
},
173174
],
174-
} as any);
175+
} as ScanResult);
175176

176177
const result = await scanUsage({ ...baseOpts, json: true, strict: false });
177178

@@ -230,13 +231,17 @@ describe('scanUsage', () => {
230231
vi.mocked(processComparisonFile).mockReturnValue({
231232
scanResult: { ...baseScanResult },
232233
comparedAgainst: '.env',
234+
envVariables: {},
235+
duplicatesFound: false,
236+
dupsEnv: [],
237+
dupsEx: [],
233238
fix: {
234239
fixApplied: false,
235240
removedDuplicates: [],
236241
addedEnv: [],
237242
gitignoreUpdated: false,
238243
},
239-
} as any);
244+
} as ProcessComparisonResult);
240245

241246
await scanUsage({ ...baseOpts, isCiMode: false, json: false });
242247

@@ -279,7 +284,7 @@ describe('scanUsage', () => {
279284
});
280285
vi.mocked(processComparisonFile).mockReturnValue({
281286
error: { message: 'soft error', shouldExit: false },
282-
} as any);
287+
} as ProcessComparisonResult);
283288
vi.mocked(printComparisonError).mockReturnValue({ exit: false });
284289

285290
const result = await scanUsage(baseOpts);
@@ -295,6 +300,10 @@ describe('scanUsage', () => {
295300
vi.mocked(processComparisonFile).mockReturnValue({
296301
scanResult: { ...baseScanResult },
297302
comparedAgainst: '.env',
303+
envVariables: {},
304+
duplicatesFound: false,
305+
dupsEnv: [],
306+
dupsEx: [],
298307
fix: {
299308
fixApplied: false,
300309
removedDuplicates: [],
@@ -304,7 +313,7 @@ describe('scanUsage', () => {
304313
uppercaseWarnings: [{ key: 'myKey', suggestion: 'MY_KEY' }],
305314
expireWarnings: [{ key: 'OLD_KEY', date: '2024-01-01', daysLeft: -10 }],
306315
inconsistentNamingWarnings: [{ key1: 'A', key2: 'B', suggestion: 'A_B' }],
307-
} as any);
316+
} as ProcessComparisonResult);
308317

309318
await scanUsage(baseOpts);
310319

@@ -337,14 +346,18 @@ describe('scanUsage', () => {
337346
vi.mocked(processComparisonFile).mockReturnValue({
338347
scanResult: { ...baseScanResult },
339348
comparedAgainst: DEFAULT_EXAMPLE_FILE,
349+
envVariables: {},
350+
duplicatesFound: false,
351+
dupsEnv: [],
352+
dupsEx: [],
340353
exampleFull: { SECRET: 'abc123' },
341354
fix: {
342355
fixApplied: false,
343356
removedDuplicates: [],
344357
addedEnv: [],
345358
gitignoreUpdated: false,
346359
},
347-
} as any);
360+
} as ProcessComparisonResult);
348361

349362
await scanUsage(baseOpts);
350363

@@ -361,14 +374,18 @@ describe('scanUsage', () => {
361374
vi.mocked(processComparisonFile).mockReturnValue({
362375
scanResult: { ...baseScanResult },
363376
comparedAgainst: '.env',
377+
envVariables: {},
378+
duplicatesFound: false,
379+
dupsEnv: [],
380+
dupsEx: [],
364381
exampleFull: { SECRET: 'abc123' },
365382
fix: {
366383
fixApplied: false,
367384
removedDuplicates: [],
368385
addedEnv: [],
369386
gitignoreUpdated: false,
370387
},
371-
} as any);
388+
} as ProcessComparisonResult);
372389

373390
await scanUsage(baseOpts);
374391

@@ -525,4 +542,92 @@ describe('scanUsage', () => {
525542
expect(result.exitWithError).toBe(true);
526543
}
527544
});
545+
546+
it('filters out usages on HTML comment closing lines (-->)', async () => {
547+
vi.mocked(scanCodebase).mockResolvedValue({
548+
...baseScanResult,
549+
used: [
550+
{
551+
variable: 'A',
552+
file: 'f.ts',
553+
line: 1,
554+
column: 0,
555+
pattern: 'process.env',
556+
context: '--> process.env.A',
557+
},
558+
],
559+
});
560+
vi.mocked(determineComparisonFile).mockResolvedValue({ type: 'none' });
561+
562+
await scanUsage({ ...baseOpts, isCiMode: true });
563+
564+
expect(printScanResult).toHaveBeenCalledWith(
565+
expect.objectContaining({ used: [] }),
566+
expect.anything(),
567+
expect.anything(),
568+
expect.anything(),
569+
);
570+
});
571+
572+
it('filters out the dotenv-diff-ignore-end line itself', async () => {
573+
vi.mocked(scanCodebase).mockResolvedValue({
574+
...baseScanResult,
575+
used: [
576+
{
577+
variable: 'END_LINE',
578+
file: 'f.ts',
579+
line: 1,
580+
column: 0,
581+
pattern: 'process.env',
582+
context: '<!-- dotenv-diff-ignore-end -->',
583+
},
584+
],
585+
});
586+
vi.mocked(determineComparisonFile).mockResolvedValue({ type: 'none' });
587+
588+
await scanUsage({ ...baseOpts, isCiMode: true });
589+
590+
expect(printScanResult).toHaveBeenCalledWith(
591+
expect.objectContaining({ used: [] }),
592+
expect.anything(),
593+
expect.anything(),
594+
expect.anything(),
595+
);
596+
});
597+
598+
it('filters out the dotenv-diff-ignore-start line itself but not subsequent usages', async () => {
599+
vi.mocked(scanCodebase).mockResolvedValue({
600+
...baseScanResult,
601+
used: [
602+
{
603+
variable: 'START_LINE',
604+
file: 'f.ts',
605+
line: 1,
606+
column: 0,
607+
pattern: 'process.env',
608+
context: '<!-- dotenv-diff-ignore-start -->',
609+
},
610+
{
611+
variable: 'KEPT',
612+
file: 'f.ts',
613+
line: 2,
614+
column: 0,
615+
pattern: 'process.env',
616+
context: 'process.env.KEPT',
617+
},
618+
],
619+
});
620+
vi.mocked(determineComparisonFile).mockResolvedValue({ type: 'none' });
621+
622+
await scanUsage({ ...baseOpts, isCiMode: true });
623+
624+
expect(printScanResult).toHaveBeenCalledWith(
625+
expect.objectContaining({
626+
used: [expect.objectContaining({ variable: 'KEPT' })],
627+
}),
628+
expect.anything(),
629+
expect.anything(),
630+
expect.anything(),
631+
);
632+
});
528633
});

packages/cli/test/unit/core/security/secretDetectors.test.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,28 @@ const token = "AKIAIOSFODNN7EXAMPLE";
367367
expect(findings).toHaveLength(0);
368368
});
369369

370+
it('upgrades severity from medium to high when same line matches both suspicious key and provider pattern', () => {
371+
// "secret" triggers SUSPICIOUS_KEYS (medium), "ghp_..." triggers PROVIDER_PATTERNS (high)
372+
// Both are kind: 'pattern' on same line → dedup should keep the high severity one
373+
const source =
374+
'const secret = "ghp_1234567890abcdefghijklmnopqrstuvwxyz";';
375+
const findings = detectSecretsInSource('test.ts', source);
376+
377+
expect(findings).toHaveLength(1);
378+
expect(findings[0].severity).toBe('high');
379+
expect(findings[0].message).toContain('known provider key pattern');
380+
});
381+
382+
it('does not downgrade severity when a lower severity finding appears after a higher one', () => {
383+
// If somehow high came first and medium second, high should be kept
384+
// provider pattern (high) + suspicious key (medium) on same line → stays high
385+
const source = 'const apikey = "AKIAIOSFODNN7EXAMPLE";';
386+
const findings = detectSecretsInSource('test.ts', source);
387+
388+
expect(findings).toHaveLength(1);
389+
expect(findings[0].severity).toBe('high');
390+
});
391+
370392
it('should use higher threshold for test files', () => {
371393
const source =
372394
'const key = "aB3dE5fG7hI9jK0lM2nO4pQ6rS8tU1vW3xY5zA7bC9dE1fG3hI5jK7lM9nO0pQ2";';
@@ -476,13 +498,15 @@ const email = "user@example.com";
476498
});
477499

478500
it('still flags hardcoded secret in JSX prop string literal', () => {
479-
const source = '<SecretField value="sk_live_abcdefghijklmnopqrstuvwxyz123456" />';
501+
const source =
502+
'<SecretField value="sk_live_abcdefghijklmnopqrstuvwxyz123456" />';
480503
const findings = detectSecretsInSource('Component.tsx', source);
481504
expect(findings.length).toBeGreaterThan(0);
482505
});
483506

484507
it('still flags hardcoded token in JSX prop expression string', () => {
485-
const source = '<TokenField token={"ghp_abcdefghijklmnopqrstuvwxyz1234567890"} />';
508+
const source =
509+
'<TokenField token={"ghp_abcdefghijklmnopqrstuvwxyz1234567890"} />';
486510
const findings = detectSecretsInSource('Component.tsx', source);
487511
expect(findings.length).toBeGreaterThan(0);
488512
});

packages/cli/test/unit/services/detectEnvExpirations.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,32 @@ describe('detectEnvExpirations', () => {
147147
},
148148
]);
149149
});
150+
151+
it('skips env key when expire date has invalid month or day (e.g. 00)', () => {
152+
fs.writeFileSync(
153+
envPath,
154+
`
155+
# @expire 2024-00-01
156+
API_KEY=123
157+
`,
158+
);
159+
160+
const result = detectEnvExpirations(envPath);
161+
162+
expect(result).toEqual([]);
163+
});
164+
165+
it('skips env key when expire date has zero day', () => {
166+
fs.writeFileSync(
167+
envPath,
168+
`
169+
# @expire 2024-12-00
170+
API_KEY=123
171+
`,
172+
);
173+
174+
const result = detectEnvExpirations(envPath);
175+
176+
expect(result).toEqual([]);
177+
});
150178
});

0 commit comments

Comments
 (0)