Skip to content

Commit 76ac320

Browse files
add unit and e2e tests
1 parent 55ebf81 commit 76ac320

File tree

2 files changed

+305
-0
lines changed

2 files changed

+305
-0
lines changed

engine/engine_test.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,3 +1320,198 @@ func TestBuildSecret(t *testing.T) {
13201320
assert.Equal(t, pageID, value)
13211321
})
13221322
}
1323+
1324+
func TestMaxRuleMatchesPerFragmentFlag(t *testing.T) {
1325+
// Content with multiple secrets that would match the same rule
1326+
multipleSecrets := `
1327+
token1: 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
1328+
token2: ghp_1234567890abcdefghijklmnopqrstuvwxyz
1329+
token3: ghp_abcdefghijklmnopqrstuvwxyz1234567890
1330+
token4: ghp_9876543210zyxwvutsrqponmlkjihgfedcba
1331+
token5: ghp_aB3cD4eF5gH6iJ7kL8mN9oP0qR1sT2uV3wX4
1332+
`
1333+
1334+
testCases := []struct {
1335+
name string
1336+
limit uint64
1337+
expectedCount int
1338+
}{
1339+
{
1340+
name: "no limit - finds all matches",
1341+
limit: 0,
1342+
expectedCount: 5,
1343+
},
1344+
{
1345+
name: "limit of 2 - finds only 2 matches",
1346+
limit: 2,
1347+
expectedCount: 2,
1348+
},
1349+
{
1350+
name: "limit of 1 - finds only 1 match",
1351+
limit: 1,
1352+
expectedCount: 1,
1353+
},
1354+
}
1355+
1356+
for _, tc := range testCases {
1357+
t.Run(tc.name, func(t *testing.T) {
1358+
eng, err := initEngine(&EngineConfig{
1359+
DetectorWorkerPoolSize: 1,
1360+
MaxRuleMatchesPerFragment: tc.limit,
1361+
})
1362+
require.NoError(t, err)
1363+
defer eng.Shutdown()
1364+
1365+
secretsChan := make(chan *secrets.Secret, 10)
1366+
fsPlugin := &plugins.FileSystemPlugin{}
1367+
err = eng.DetectFragment(item{content: &multipleSecrets}, secretsChan, fsPlugin.GetName())
1368+
require.NoError(t, err)
1369+
close(secretsChan)
1370+
1371+
count := 0
1372+
for range secretsChan {
1373+
count++
1374+
}
1375+
assert.Equal(t, tc.expectedCount, count)
1376+
})
1377+
}
1378+
}
1379+
1380+
func TestMaxSecretSizeFlag(t *testing.T) {
1381+
// Valid GitHub PAT format - 40 chars
1382+
secret := "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"
1383+
1384+
testCases := []struct {
1385+
name string
1386+
limit uint64
1387+
shouldFind bool
1388+
}{
1389+
{
1390+
name: "no limit - finds secret",
1391+
limit: 0,
1392+
shouldFind: true,
1393+
},
1394+
{
1395+
name: "limit larger than secret - finds secret",
1396+
limit: 200,
1397+
shouldFind: true,
1398+
},
1399+
{
1400+
name: "limit smaller than secret - ignores secret",
1401+
limit: 10,
1402+
shouldFind: false,
1403+
},
1404+
{
1405+
name: "limit exactly at secret size boundary - finds secret",
1406+
limit: 40,
1407+
shouldFind: true,
1408+
},
1409+
}
1410+
1411+
for _, tc := range testCases {
1412+
t.Run(tc.name, func(t *testing.T) {
1413+
eng, err := initEngine(&EngineConfig{
1414+
DetectorWorkerPoolSize: 1,
1415+
MaxSecretSize: tc.limit,
1416+
})
1417+
require.NoError(t, err)
1418+
defer eng.Shutdown()
1419+
1420+
secretsChan := make(chan *secrets.Secret, 1)
1421+
fsPlugin := &plugins.FileSystemPlugin{}
1422+
err = eng.DetectFragment(item{content: &secret}, secretsChan, fsPlugin.GetName())
1423+
require.NoError(t, err)
1424+
close(secretsChan)
1425+
1426+
s := <-secretsChan
1427+
if tc.shouldFind {
1428+
assert.NotNil(t, s)
1429+
} else {
1430+
assert.Nil(t, s)
1431+
}
1432+
})
1433+
}
1434+
}
1435+
1436+
func TestMaxFindingsFlag(t *testing.T) {
1437+
// Content with multiple secrets in single fragment
1438+
multipleSecrets := `
1439+
github_token: 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
1440+
another_token: ghp_1234567890abcdefghijklmnopqrstuvwxyz
1441+
third_token: ghp_abcdefghijklmnopqrstuvwxyz1234567890
1442+
`
1443+
1444+
testCases := []struct {
1445+
name string
1446+
limit uint64
1447+
fragments []string
1448+
expectedCount int
1449+
}{
1450+
{
1451+
name: "no limit - finds all secrets",
1452+
limit: 0,
1453+
fragments: []string{multipleSecrets},
1454+
expectedCount: 3,
1455+
},
1456+
{
1457+
name: "limit of 2 - stops after 2 findings",
1458+
limit: 2,
1459+
fragments: []string{multipleSecrets},
1460+
expectedCount: 2,
1461+
},
1462+
{
1463+
name: "limit of 1 - stops after 1 finding",
1464+
limit: 1,
1465+
fragments: []string{multipleSecrets},
1466+
expectedCount: 1,
1467+
},
1468+
{
1469+
name: "limit of 2 across 3 fragments",
1470+
limit: 2,
1471+
fragments: []string{
1472+
"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",
1473+
"ghp_1234567890abcdefghijklmnopqrstuvwxyz",
1474+
"ghp_abcdefghijklmnopqrstuvwxyz1234567890",
1475+
},
1476+
expectedCount: 2,
1477+
},
1478+
{
1479+
name: "limit of 1 across 3 fragments",
1480+
limit: 1,
1481+
fragments: []string{
1482+
"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",
1483+
"ghp_1234567890abcdefghijklmnopqrstuvwxyz",
1484+
"ghp_abcdefghijklmnopqrstuvwxyz1234567890",
1485+
},
1486+
expectedCount: 1,
1487+
},
1488+
}
1489+
1490+
for _, tc := range testCases {
1491+
t.Run(tc.name, func(t *testing.T) {
1492+
eng, err := initEngine(&EngineConfig{
1493+
DetectorWorkerPoolSize: 1,
1494+
MaxFindings: tc.limit,
1495+
})
1496+
require.NoError(t, err)
1497+
defer eng.Shutdown()
1498+
1499+
secretsChan := make(chan *secrets.Secret, 10)
1500+
fsPlugin := &plugins.FileSystemPlugin{}
1501+
1502+
for _, fragment := range tc.fragments {
1503+
f := fragment // capture for closure
1504+
err = eng.DetectFragment(item{content: &f}, secretsChan, fsPlugin.GetName())
1505+
require.NoError(t, err)
1506+
}
1507+
1508+
close(secretsChan)
1509+
1510+
count := 0
1511+
for range secretsChan {
1512+
count++
1513+
}
1514+
assert.Equal(t, tc.expectedCount, count)
1515+
})
1516+
}
1517+
}

tests/e2e_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,4 +766,114 @@ api_key: test-key-456`
766766
}
767767
}
768768
})
769+
770+
t.Run("--max-findings flag: caps total number of findings", func(t *testing.T) {
771+
projectDir := t.TempDir()
772+
773+
// Create multiple files with secrets to ensure we have more than the limit
774+
for i := 1; i <= 5; i++ {
775+
content := fmt.Sprintf("secret%d: ghp_%dabcdefghijklmnopqrstuvwxyz12345678", i, i)
776+
err := os.WriteFile(path.Join(projectDir, fmt.Sprintf("secret%d.txt", i)), []byte(content), 0644)
777+
require.NoError(t, err, "failed to create test file")
778+
}
779+
780+
// Run scan with --max-findings set to 2
781+
err = executable.run("filesystem", "--path", projectDir, "--max-findings", "2", "--ignore-on-exit", "results")
782+
assert.NoError(t, err, "scan should succeed with max-findings flag")
783+
784+
report, err := executable.getReport()
785+
require.NoError(t, err, "failed to get report")
786+
787+
totalSecrets := report.GetTotalSecretsFound()
788+
t.Logf("Total secrets found with --max-findings=2: %d", totalSecrets)
789+
assert.LessOrEqual(t, totalSecrets, 2, "should find at most 2 secrets when --max-findings=2")
790+
})
791+
792+
t.Run("--max-rule-matches-per-fragment flag: limits matches per rule per fragment", func(t *testing.T) {
793+
projectDir := t.TempDir()
794+
795+
// Create a single file with multiple secrets that match the same rule
796+
content := `Multiple GitHub PATs in one file:
797+
token1: ghp_1234567890abcdefghijklmnopqrstuvwxyz
798+
token2: ghp_abcdefghijklmnopqrstuvwxyz1234567890
799+
token3: ghp_9876543210zyxwvutsrqponmlkjihgfedcba
800+
token4: ghp_aB3cD4eF5gH6iJ7kL8mN9oP0qR1sT2uV3wX4
801+
token5: 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`
802+
803+
err := os.WriteFile(path.Join(projectDir, "multi_secrets.txt"), []byte(content), 0644)
804+
require.NoError(t, err, "failed to create test file")
805+
806+
// Run scan with --max-rule-matches-per-fragment set to 2
807+
err = executable.run("filesystem", "--path", projectDir, "--max-rule-matches-per-fragment", "2", "--ignore-on-exit", "results")
808+
assert.NoError(t, err, "scan should succeed with max-rule-matches-per-fragment flag")
809+
810+
report, err := executable.getReport()
811+
require.NoError(t, err, "failed to get report")
812+
813+
totalSecrets := report.GetTotalSecretsFound()
814+
t.Logf("Total secrets found with --max-rule-matches-per-fragment=2: %d", totalSecrets)
815+
assert.LessOrEqual(t, totalSecrets, 2, "should find at most 2 secrets per rule per fragment")
816+
})
817+
818+
t.Run("--max-secret-size flag: ignores secrets larger than specified size", func(t *testing.T) {
819+
projectDir := t.TempDir()
820+
821+
// Create a file with a normal-sized secret
822+
normalSecret := "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" // 40 chars
823+
err := os.WriteFile(path.Join(projectDir, "normal.txt"), []byte(normalSecret), 0644)
824+
require.NoError(t, err, "failed to create test file")
825+
826+
// Run scan with --max-secret-size set to a value smaller than the secret
827+
err = executable.run("filesystem", "--path", projectDir, "--max-secret-size", "10", "--ignore-on-exit", "results")
828+
assert.NoError(t, err, "scan should succeed with max-secret-size flag")
829+
830+
report, err := executable.getReport()
831+
require.NoError(t, err, "failed to get report")
832+
833+
totalSecrets := report.GetTotalSecretsFound()
834+
t.Logf("Total secrets found with --max-secret-size=10: %d", totalSecrets)
835+
assert.Equal(t, 0, totalSecrets, "should find no secrets when max-secret-size is smaller than secret")
836+
837+
// Run scan with --max-secret-size set to a value larger than the secret
838+
err = executable.run("filesystem", "--path", projectDir, "--max-secret-size", "100", "--ignore-on-exit", "results")
839+
assert.NoError(t, err, "scan should succeed with max-secret-size flag")
840+
841+
report, err = executable.getReport()
842+
require.NoError(t, err, "failed to get report")
843+
844+
totalSecrets = report.GetTotalSecretsFound()
845+
t.Logf("Total secrets found with --max-secret-size=100: %d", totalSecrets)
846+
assert.GreaterOrEqual(t, totalSecrets, 1, "should find secrets when max-secret-size is larger than secret")
847+
})
848+
849+
t.Run("Combined limit flags: multiple limit flags together", func(t *testing.T) {
850+
projectDir := t.TempDir()
851+
852+
// Create multiple files with multiple secrets each
853+
for i := 1; i <= 5; i++ {
854+
content := fmt.Sprintf(`File %d secrets:
855+
token1: ghp_%d234567890abcdefghijklmnopqrstuvwxy
856+
token2: ghp_%dabcdefghijklmnopqrstuvwxyz123456789`, i, i, i)
857+
err := os.WriteFile(path.Join(projectDir, fmt.Sprintf("file%d.txt", i)), []byte(content), 0644)
858+
require.NoError(t, err, "failed to create test file")
859+
}
860+
861+
// Run scan with multiple limit flags
862+
err = executable.run("filesystem",
863+
"--path", projectDir,
864+
"--max-findings", "3",
865+
"--max-rule-matches-per-fragment", "1",
866+
"--max-secret-size", "100",
867+
"--ignore-on-exit", "results")
868+
assert.NoError(t, err, "scan should succeed with combined limit flags")
869+
870+
report, err := executable.getReport()
871+
require.NoError(t, err, "failed to get report")
872+
873+
totalSecrets := report.GetTotalSecretsFound()
874+
t.Logf("Total secrets found with combined limit flags: %d", totalSecrets)
875+
// With max-rule-matches-per-fragment=1, we get at most 1 per file (3 files)
876+
// With max-findings=3, we get at most 3 total
877+
assert.LessOrEqual(t, totalSecrets, 3, "should respect combined limit flags")
878+
})
769879
}

0 commit comments

Comments
 (0)