Return targeted scan errors (#2995)
Targeted scans should return their errors so that consumers can process them. By creating a type that combines an error with a targeted secret ID, we can return these errors without having to modify the Source interface.
This commit is contained in:
@@ -57,3 +57,20 @@ func (s *ScanErrors) Errors() error {
|
||||
defer s.mu.RUnlock()
|
||||
return errors.Join(s.errors...)
|
||||
}
|
||||
|
||||
// TargetedScanError is an error with a secret ID attached. Collections of them can be returned by targeted scans that
|
||||
// scan multiple targets in order to associate individual errors with individual scan targets.
|
||||
type TargetedScanError struct {
|
||||
Err error
|
||||
SecretID int64
|
||||
}
|
||||
|
||||
var _ error = (*TargetedScanError)(nil)
|
||||
|
||||
func (t TargetedScanError) Error() string {
|
||||
return t.Err.Error()
|
||||
}
|
||||
|
||||
func (t TargetedScanError) Unwrap() error {
|
||||
return t.Err
|
||||
}
|
||||
|
||||
@@ -361,7 +361,8 @@ func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk, tar
|
||||
// Otherwise, we're scanning all data.
|
||||
// This allows us to only scan the commit where a vulnerability was found.
|
||||
if len(targets) > 0 {
|
||||
return s.scanTargets(ctx, targets, chunksChan)
|
||||
errs := s.scanTargets(ctx, targets, chunksChan)
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// Reset consumption and rate limit metrics on each run.
|
||||
@@ -1514,14 +1515,16 @@ func (s *Source) chunkPullRequestComments(ctx context.Context, repoInfo repoInfo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Source) scanTargets(ctx context.Context, targets []sources.ChunkingTarget, chunksChan chan *sources.Chunk) error {
|
||||
func (s *Source) scanTargets(ctx context.Context, targets []sources.ChunkingTarget, chunksChan chan *sources.Chunk) []error {
|
||||
var errs []error
|
||||
for _, tgt := range targets {
|
||||
if err := s.scanTarget(ctx, tgt, chunksChan); err != nil {
|
||||
ctx.Logger().Error(err, "error scanning target")
|
||||
errs = append(errs, &sources.TargetedScanError{Err: err, SecretID: tgt.SecretID})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return errs
|
||||
}
|
||||
|
||||
func (s *Source) scanTarget(ctx context.Context, target sources.ChunkingTarget, chunksChan chan *sources.Chunk) error {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-github/v62/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
"gopkg.in/h2non/gock.v1"
|
||||
@@ -844,3 +846,29 @@ func TestGetGistID(t *testing.T) {
|
||||
assert.Equal(t, tt.expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
// This isn't really a GitHub test, but GitHub is the only source that supports scan targeting right now, so this is
|
||||
// where I've put this targeted scan test.
|
||||
func Test_ScanMultipleTargets_MultipleErrors(t *testing.T) {
|
||||
s := &Source{conn: &sourcespb.GitHub{}} // This test doesn't require initialization
|
||||
ctx := context.Background()
|
||||
chunksChan := make(chan *sources.Chunk)
|
||||
|
||||
targets := []sources.ChunkingTarget{
|
||||
{SecretID: 1},
|
||||
{SecretID: 2},
|
||||
}
|
||||
|
||||
// The specific error text doesn't matter for the test, but it has to match what the source generates
|
||||
want := []*sources.TargetedScanError{
|
||||
{SecretID: 1, Err: errors.New("unable to cast metadata type for targeted scan")},
|
||||
{SecretID: 2, Err: errors.New("unable to cast metadata type for targeted scan")},
|
||||
}
|
||||
|
||||
err := s.Chunks(ctx, chunksChan, targets...)
|
||||
unwrappable, ok := err.(interface{ Unwrap() []error })
|
||||
if assert.True(t, ok, "returned error was not unwrappable") {
|
||||
got := unwrappable.Unwrap()
|
||||
assert.ElementsMatch(t, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user