Skip to content

Commit 1d87fba

Browse files
authored
Apply configured include/ignore lists to GitLab unit scans (#4592)
Previously, the GitLab include and ignore lists were only applied during repository enumeration, which meant that they would be ignored after enumeration completed. For large environments, post-enumeration scanning can take days, and it was awkward that the include/ignore lists could effectively not be modified during that time. This PR changes things such that repositories can be configured to be skipped even post-enumeration. Importantly, repositories cannot be "un-ignored" post-enumeration. This is unfortunate, but this PR still represents improvement on the status quo.
1 parent f1f4872 commit 1d87fba

File tree

2 files changed

+94
-7
lines changed

2 files changed

+94
-7
lines changed

pkg/sources/gitlab/gitlab.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,18 @@ type Source struct {
3939
jobID sources.JobID
4040
verify bool
4141

42-
authMethod string
43-
user string
44-
password string
45-
token string
46-
url string
47-
repos []string
48-
groupIds []string
42+
authMethod string
43+
user string
44+
password string
45+
token string
46+
url string
47+
repos []string
48+
groupIds []string
49+
50+
// These lists are checked both during enumeration and when ChunkUnit is called. This means that if they're modified
51+
// between enumeration and individual unit scans, units will be scanned only if they pass the filter during
52+
// enumeration and also if they pass the filter during unit scanning. This means that units can be "removed" from
53+
// the enumerated list post-enumeration by modifying the filters, but they can never be added post-enumeration.
4954
ignoreRepos []string
5055
includeRepos []string
5156

@@ -1086,6 +1091,14 @@ func (s *Source) Enumerate(ctx context.Context, reporter sources.UnitReporter) e
10861091
func (s *Source) ChunkUnit(ctx context.Context, unit sources.SourceUnit, reporter sources.ChunkReporter) error {
10871092
repoURL, _ := unit.SourceUnitID()
10881093

1094+
ignoreRepo := buildIgnorer(s.includeRepos, s.ignoreRepos, func(err error, pattern string) {
1095+
ctx.Logger().Error(err, "could not compile include/exclude repo glob", "glob", pattern)
1096+
})
1097+
if ignoreRepo(repoURL) {
1098+
ctx.Logger().V(3).Info("skipping project", "reason", "ignored in config")
1099+
return nil
1100+
}
1101+
10891102
var path string
10901103
var repo *gogit.Repository
10911104
var err error

pkg/sources/gitlab/gitlab_integration_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/kylelemons/godebug/pretty"
1313
"github.com/stretchr/testify/assert"
14+
"github.com/stretchr/testify/require"
1415
"google.golang.org/protobuf/types/known/anypb"
1516

1617
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
@@ -498,6 +499,79 @@ func TestSource_Chunks_TargetedScan(t *testing.T) {
498499
}
499500
}
500501

502+
func TestSource_ChunkUnit_RepoFiltersRespected(t *testing.T) {
503+
ctx := context.Background()
504+
505+
// Arrange: Get test environment token
506+
secret, err := common.GetTestSecret(ctx)
507+
if err != nil {
508+
t.Fatal(fmt.Errorf("failed to access secret: %v", err))
509+
}
510+
token := secret.MustGetField("GITLAB_TOKEN")
511+
512+
// Arrange: Build a unit to scan
513+
unit := sources.CommonSourceUnit{
514+
Kind: "repo",
515+
ID: "https://gitlab.com/testermctestface/testy",
516+
}
517+
518+
tests := []struct {
519+
name string
520+
includeRepos []string
521+
ignoreRepos []string
522+
wantAnyChunks bool
523+
}{
524+
{
525+
name: "empty include, empty ignore",
526+
wantAnyChunks: true,
527+
},
528+
{
529+
name: "unit matches include",
530+
includeRepos: []string{"https://gitlab.com/testermctestface/testy"},
531+
wantAnyChunks: true,
532+
},
533+
{
534+
name: "unit does not match include",
535+
includeRepos: []string{"https://gitlab.com/testermctestface/something-else"},
536+
wantAnyChunks: false,
537+
},
538+
{
539+
name: "unit matches ignore",
540+
ignoreRepos: []string{"https://gitlab.com/testermctestface/testy"},
541+
wantAnyChunks: false,
542+
},
543+
}
544+
545+
for _, tt := range tests {
546+
t.Run(tt.name, func(t *testing.T) {
547+
// Arrange: Create the connection
548+
typedConn := &sourcespb.GitLab{
549+
Credential: &sourcespb.GitLab_Token{
550+
Token: token,
551+
},
552+
IncludeRepos: tt.includeRepos,
553+
IgnoreRepos: tt.ignoreRepos,
554+
}
555+
conn, err := anypb.New(typedConn)
556+
require.NoError(t, err)
557+
558+
// Arrange: Instantiate and initialize the source
559+
s := &Source{}
560+
require.NoError(t, s.Init(ctx, "test source", 1, 1, false, conn, 1))
561+
562+
// Arrange: Build the chunk reporter
563+
chunksChan := make(chan *sources.Chunk, 1024)
564+
chunkReporter := sources.ChanReporter{chunksChan}
565+
566+
// Act: Scan the unit
567+
require.NoError(t, s.ChunkUnit(ctx, unit, chunkReporter))
568+
569+
// Assert: Verify that chunk production was correct
570+
assert.Equal(t, tt.wantAnyChunks, len(chunksChan) > 0)
571+
})
572+
}
573+
}
574+
501575
func TestSource_InclusionGlobbing(t *testing.T) {
502576
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
503577
defer cancel()

0 commit comments

Comments
 (0)