Skip to content

Commit f01eeec

Browse files
Merge branch 'main' into INS-79-jira-cloud-on-prem-autodetection
2 parents 1149a7f + 35a5bf2 commit f01eeec

File tree

3 files changed

+229
-1
lines changed

3 files changed

+229
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ trufflehog git https://github.com/trufflesecurity/trufflehog.git
499499

500500
## Configuration
501501

502-
TruffleHog supports defining [custom regex detectors](#regex-detector-alpha)
502+
TruffleHog supports defining [custom regex detectors](#custom-regex-detector-alpha)
503503
and multiple sources in a configuration file provided via the `--config` flag.
504504
The regex detectors can be used with any subcommand, while the sources defined
505505
in configuration are only for the `multi-scan` subcommand.

pkg/engine/engine.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,12 @@ func (e *Engine) filterResults(
11701170
return results
11711171
}
11721172

1173+
// processResult generates a detectors.ResultWithMetadata from the provided chunk and result and puts it on the results
1174+
// channel, unless the result exists on a line with an ignore tag, in which case no result is generated.
1175+
//
1176+
// CMR: The provided chunk is wrapped in a detectableChunk, but I'm pretty sure that's purely out of convenience
1177+
// (because that's what this function's callers are using when they call this function). We're past detection at this
1178+
// point in the engine, so we should probably refactor that parameter into a less confusing data type.
11731179
func (e *Engine) processResult(
11741180
ctx context.Context,
11751181
data detectableChunk,

pkg/engine/engine_test.go

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"time"
1313

1414
"github.com/stretchr/testify/assert"
15+
"github.com/stretchr/testify/require"
1516

1617
"github.com/trufflesecurity/trufflehog/v3/pkg/config"
1718
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
@@ -26,6 +27,7 @@ import (
2627
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
2728
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
2829
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
30+
"github.com/trufflesecurity/trufflehog/v3/pkg/verificationcache"
2931
)
3032

3133
const fakeDetectorKeyword = "fakedetector"
@@ -1318,3 +1320,223 @@ def test_something():
13181320
})
13191321
}
13201322
}
1323+
1324+
type passthroughDetector struct {
1325+
detectorType detectorspb.DetectorType
1326+
keywords []string
1327+
secret string
1328+
}
1329+
1330+
func (p passthroughDetector) FromData(_ aCtx.Context, verify bool, data []byte) ([]detectors.Result, error) {
1331+
raw := data
1332+
if p.secret != "" {
1333+
raw = []byte(p.secret)
1334+
}
1335+
return []detectors.Result{
1336+
{
1337+
Raw: raw,
1338+
Verified: verify,
1339+
},
1340+
}, nil
1341+
}
1342+
1343+
func (p passthroughDetector) Keywords() []string { return p.keywords }
1344+
func (p passthroughDetector) Type() detectorspb.DetectorType { return p.detectorType }
1345+
func (p passthroughDetector) Description() string { return "fake detector for testing" }
1346+
1347+
type passthroughDecoder struct{}
1348+
1349+
func (p passthroughDecoder) FromChunk(chunk *sources.Chunk) *decoders.DecodableChunk {
1350+
return &decoders.DecodableChunk{
1351+
Chunk: chunk,
1352+
DecoderType: detectorspb.DecoderType(-1),
1353+
}
1354+
}
1355+
1356+
func (p passthroughDecoder) Type() detectorspb.DecoderType { return detectorspb.DecoderType(-1) }
1357+
1358+
func TestEngine_DetectChunk_UsesVerifyFlag(t *testing.T) {
1359+
ctx := context.Background()
1360+
1361+
// Arrange: Create a minimal engine.
1362+
e := &Engine{
1363+
results: make(chan detectors.ResultWithMetadata, 1),
1364+
verificationCache: verificationcache.New(nil, &verificationcache.InMemoryMetrics{}),
1365+
}
1366+
1367+
// Arrange: Create a detector match. We can't create one directly, so we have to use a minimal A-H core.
1368+
ahcore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{passthroughDetector{keywords: []string{"keyword"}}})
1369+
detectorMatches := ahcore.FindDetectorMatches([]byte("keyword"))
1370+
require.Len(t, detectorMatches, 1)
1371+
1372+
// Arrange: Create a chunk to detect.
1373+
chunk := detectableChunk{
1374+
chunk: sources.Chunk{
1375+
Verify: true,
1376+
},
1377+
detector: detectorMatches[0],
1378+
wgDoneFn: func() {},
1379+
}
1380+
1381+
// Act
1382+
e.detectChunk(ctx, chunk)
1383+
close(e.results)
1384+
1385+
// Assert: Confirm that a result was generated and that it has the expected verify flag.
1386+
select {
1387+
case result := <-e.results:
1388+
assert.True(t, result.Result.Verified)
1389+
default:
1390+
t.Errorf("expected a result but did not get one")
1391+
}
1392+
}
1393+
1394+
func TestEngine_ScannerWorker_DetectableChunkHasCorrectVerifyFlag(t *testing.T) {
1395+
ctx := context.Background()
1396+
1397+
// Arrange: Create a minimal engine.
1398+
detector := &passthroughDetector{keywords: []string{"keyword"}}
1399+
e := &Engine{
1400+
AhoCorasickCore: ahocorasick.NewAhoCorasickCore([]detectors.Detector{detector}),
1401+
decoders: []decoders.Decoder{passthroughDecoder{}},
1402+
detectableChunksChan: make(chan detectableChunk, 1),
1403+
sourceManager: sources.NewManager(),
1404+
verify: true,
1405+
}
1406+
1407+
// Arrange: Create a chunk to scan.
1408+
chunk := sources.Chunk{
1409+
Data: []byte("keyword"),
1410+
Verify: true,
1411+
}
1412+
1413+
// Arrange: Enqueue a chunk to be scanned.
1414+
e.sourceManager.ScanChunk(&chunk)
1415+
1416+
// Act
1417+
go e.scannerWorker(ctx)
1418+
1419+
// Assert: Confirm that a chunk was generated and that it has the expected verify flag.
1420+
select {
1421+
case chunk := <-e.detectableChunksChan:
1422+
assert.True(t, chunk.chunk.Verify)
1423+
case <-time.After(1 * time.Second):
1424+
t.Errorf("expected a detectableChunk but did not get one")
1425+
}
1426+
}
1427+
1428+
func TestEngine_VerificationOverlapWorker_DetectableChunkHasCorrectVerifyFlag(t *testing.T) {
1429+
ctx := context.Background()
1430+
1431+
t.Run("overlap", func(t *testing.T) {
1432+
// Arrange: Create a minimal engine
1433+
e := &Engine{
1434+
detectableChunksChan: make(chan detectableChunk, 2),
1435+
results: make(chan detectors.ResultWithMetadata, 2),
1436+
retainFalsePositives: true,
1437+
verificationOverlapChunksChan: make(chan verificationOverlapChunk, 2),
1438+
verify: true,
1439+
}
1440+
1441+
// Arrange: Set up a fake detectableChunk processor so that any chunks (incorrectly) sent to
1442+
// e.detectableChunksChan don't block the test.
1443+
processedDetectableChunks := make(chan detectableChunk, 2)
1444+
go func() {
1445+
for chunk := range e.detectableChunksChan {
1446+
chunk.wgDoneFn()
1447+
processedDetectableChunks <- chunk
1448+
}
1449+
}()
1450+
1451+
// Arrange: Create a chunk to "scan."
1452+
chunk := sources.Chunk{
1453+
Data: []byte("keyword ;oahpow8heg;blaisd"),
1454+
Verify: true,
1455+
}
1456+
1457+
// Arrange: Create overlapping detector matches. We can't create them directly, so we have to use a minimal A-H
1458+
// core.
1459+
ahcore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{
1460+
passthroughDetector{detectorType: detectorspb.DetectorType(-1), keywords: []string{"keyw"}},
1461+
passthroughDetector{detectorType: detectorspb.DetectorType(-2), keywords: []string{"keyword"}},
1462+
})
1463+
detectorMatches := ahcore.FindDetectorMatches(chunk.Data)
1464+
require.Len(t, detectorMatches, 2)
1465+
1466+
// Arrange: Enqueue a verification overlap chunk
1467+
e.verificationOverlapChunksChan <- verificationOverlapChunk{
1468+
chunk: chunk,
1469+
detectors: detectorMatches,
1470+
verificationOverlapWgDoneFn: func() { close(e.verificationOverlapChunksChan) },
1471+
}
1472+
1473+
// Act
1474+
e.verificationOverlapWorker(ctx)
1475+
close(e.results)
1476+
close(e.detectableChunksChan)
1477+
close(processedDetectableChunks)
1478+
1479+
// Assert: Confirm that every generated result is unverified (because overlap detection precluded it).
1480+
for result := range e.results {
1481+
assert.False(t, result.Result.Verified)
1482+
}
1483+
1484+
// Assert: Confirm that every generated detectable chunk carries the original Verify flag.
1485+
// CMR: There should be not be any of these chunks. However, due to what I believe is an unrelated bug, there
1486+
// are. This test ensures that even in that erroneous case, their Verify flag is correct.
1487+
for detectableChunk := range processedDetectableChunks {
1488+
assert.True(t, detectableChunk.chunk.Verify)
1489+
}
1490+
})
1491+
t.Run("no overlap", func(t *testing.T) {
1492+
// Arrange: Create a minimal engine
1493+
e := &Engine{
1494+
detectableChunksChan: make(chan detectableChunk, 2),
1495+
retainFalsePositives: true,
1496+
verificationOverlapChunksChan: make(chan verificationOverlapChunk, 2),
1497+
verify: true,
1498+
}
1499+
1500+
// Arrange: Set up a fake detectableChunk processor so that any chunks sent to e.detectableChunksChan don't
1501+
// block the test.
1502+
processedDetectableChunks := make(chan detectableChunk, 2)
1503+
go func() {
1504+
for chunk := range e.detectableChunksChan {
1505+
chunk.wgDoneFn()
1506+
processedDetectableChunks <- chunk
1507+
}
1508+
}()
1509+
1510+
// Arrange: Create a chunk to "scan."
1511+
chunk := sources.Chunk{
1512+
Data: []byte("keyword ;oahpow8heg;blaisd"),
1513+
Verify: true,
1514+
}
1515+
1516+
// Arrange: Create non-overlapping detector matches. We can't create them directly, so we have to use a minimal
1517+
// A-H core.
1518+
ahcore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{
1519+
passthroughDetector{detectorType: detectorspb.DetectorType(-1), keywords: []string{"keyw"}, secret: "oahpow"},
1520+
passthroughDetector{detectorType: detectorspb.DetectorType(-2), keywords: []string{"keyword"}, secret: "blaisd"},
1521+
})
1522+
detectorMatches := ahcore.FindDetectorMatches(chunk.Data)
1523+
require.Len(t, detectorMatches, 2)
1524+
1525+
// Arrange: Enqueue a verification overlap chunk
1526+
e.verificationOverlapChunksChan <- verificationOverlapChunk{
1527+
chunk: chunk,
1528+
detectors: detectorMatches,
1529+
verificationOverlapWgDoneFn: func() { close(e.verificationOverlapChunksChan) },
1530+
}
1531+
1532+
// Act
1533+
e.verificationOverlapWorker(ctx)
1534+
close(e.detectableChunksChan)
1535+
close(processedDetectableChunks)
1536+
1537+
// Assert: Confirm that every generated detectable chunk carries the original Verify flag.
1538+
for detectableChunk := range processedDetectableChunks {
1539+
assert.True(t, detectableChunk.chunk.Verify)
1540+
}
1541+
})
1542+
}

0 commit comments

Comments
 (0)