Skip to content

Commit fed34e5

Browse files
Copilotfrjcomp
andauthored
Add configurable --hit-timeout flag for scan commands (#393)
* Initial plan * Add configurable scan hit timeout with --hit-timeout flag Co-authored-by: frjcomp <107982661+frjcomp@users.noreply.github.com> * added timeout to output * Add test for explicit timeout verification with 1ns timeout Co-authored-by: frjcomp <107982661+frjcomp@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: frjcomp <107982661+frjcomp@users.noreply.github.com>
1 parent 0123e8b commit fed34e5

23 files changed

Lines changed: 121 additions & 41 deletions

File tree

src/pipeleak/cmd/bitbucket/scan/scan.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ func Scan(cmd *cobra.Command, args []string) {
9595
options.MaxPipelines,
9696
options.MaxScanGoRoutines,
9797
options.ConfidenceFilter,
98+
options.HitTimeout,
9899
)
99100
if err != nil {
100101
log.Fatal().Err(err).Str("size", maxArtifactSize).Msg("Failed parsing max-artifact-size flag")

src/pipeleak/cmd/devops/scan/scan.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func Scan(cmd *cobra.Command, args []string) {
9494
options.MaxBuilds,
9595
options.MaxScanGoRoutines,
9696
options.ConfidenceFilter,
97+
options.HitTimeout,
9798
)
9899
if err != nil {
99100
log.Fatal().Err(err).Str("size", maxArtifactSize).Msg("Failed parsing max-artifact-size flag")

src/pipeleak/cmd/gitea/scan/scan.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ func Scan(cmd *cobra.Command, args []string) {
107107
scanOptions.StartRunID,
108108
scanOptions.MaxScanGoRoutines,
109109
scanOptions.ConfidenceFilter,
110+
scanOptions.HitTimeout,
110111
)
111112
if err != nil {
112113
log.Fatal().Err(err).Msg("Failed initializing scan options")

src/pipeleak/cmd/github/scan/scan.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ func Scan(cmd *cobra.Command, args []string) {
9999
options.MaxWorkflows,
100100
options.MaxScanGoRoutines,
101101
options.ConfidenceFilter,
102+
options.HitTimeout,
102103
)
103104
if err != nil {
104105
log.Fatal().Err(err).Str("size", maxArtifactSize).Msg("Failed parsing max-artifact-size flag")

src/pipeleak/cmd/gitlab/scan/scan.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ func Scan(cmd *cobra.Command, args []string) {
106106
options.JobLimit,
107107
options.MaxScanGoRoutines,
108108
options.ConfidenceFilter,
109+
options.HitTimeout,
109110
)
110111
if err != nil {
111112
log.Fatal().Err(err).Msg("Failed initializing scan options")

src/pipeleak/cmd/internal/flags/common.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package flags
22

33
import (
4+
"time"
5+
46
"github.com/CompassSecurity/pipeleak/pkg/config"
57
"github.com/spf13/cobra"
68
)
@@ -17,4 +19,6 @@ func AddCommonScanFlags(cmd *cobra.Command, opts *config.CommonScanOptions, maxA
1719
cmd.Flags().StringSliceVarP(&opts.ConfidenceFilter, "confidence", "", []string{},
1820
"Filter for confidence level, separate by comma if multiple. See readme for more info.")
1921
cmd.Flags().BoolVarP(&opts.Owned, "owned", "o", false, "Scan only user owned repositories")
22+
cmd.Flags().DurationVarP(&opts.HitTimeout, "hit-timeout", "", 60*time.Second,
23+
"Maximum time to wait for hit detection per scan item (e.g., 30s, 2m, 1h)")
2024
}

src/pipeleak/pkg/bitbucket/scan/scanner.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type ScanOptions struct {
3030
Artifacts bool
3131
BitBucketURL string
3232
MaxArtifactSize int64
33+
HitTimeout time.Duration
3334
Context context.Context
3435
Client BitBucketApiClient
3536
HasProvidedCookie bool
@@ -215,7 +216,7 @@ func (s *bbScanner) listArtifacts(workspaceSlug string, repoSlug string, buildId
215216
artifactBytes := s.options.Client.GetPipelineArtifact(workspaceSlug, repoSlug, buildId, art.UUID)
216217

217218
if filetype.IsArchive(artifactBytes) {
218-
pkgscanner.HandleArchiveArtifact(art.Name, artifactBytes, s.buildWebArtifactUrl(workspaceSlug, repoSlug, buildId, art.StepUUID), "Build "+strconv.Itoa(buildId), s.options.TruffleHogVerification)
219+
pkgscanner.HandleArchiveArtifact(art.Name, artifactBytes, s.buildWebArtifactUrl(workspaceSlug, repoSlug, buildId, art.StepUUID), "Build "+strconv.Itoa(buildId), s.options.TruffleHogVerification, s.options.HitTimeout)
219220
}
220221
}
221222

@@ -298,6 +299,7 @@ func (s *bbScanner) getSteplog(workspaceSlug string, repoSlug string, pipelineUu
298299
logResult, err := logline.ProcessLogs(logBytes, logline.ProcessOptions{
299300
MaxGoRoutines: s.options.MaxScanGoRoutines,
300301
VerifyCredentials: s.options.TruffleHogVerification,
302+
HitTimeout: s.options.HitTimeout,
301303
})
302304
if err != nil {
303305
log.Debug().Err(err).Str("stepUUid", stepUUID).Msg("Failed detecting secrets")
@@ -318,16 +320,16 @@ func (s *bbScanner) getDownloadArtifact(downloadUrl string, webUrl string, filen
318320
}
319321

320322
if filetype.IsArchive(fileBytes) {
321-
pkgscanner.HandleArchiveArtifact(filename, fileBytes, webUrl, "Download Artifact", s.options.TruffleHogVerification)
323+
pkgscanner.HandleArchiveArtifact(filename, fileBytes, webUrl, "Download Artifact", s.options.TruffleHogVerification, s.options.HitTimeout)
322324
} else {
323-
pkgscanner.DetectFileHits(fileBytes, webUrl, "Download Artifact", filename, "", s.options.TruffleHogVerification)
325+
pkgscanner.DetectFileHits(fileBytes, webUrl, "Download Artifact", filename, "", s.options.TruffleHogVerification, s.options.HitTimeout)
324326
}
325327
}
326328

327329
// InitializeOptions prepares scan options from CLI parameters.
328330
func InitializeOptions(email, accessToken, bitBucketCookie, bitBucketURL, workspace, after, maxArtifactSizeStr string,
329331
owned, public, artifacts, truffleHogVerification bool,
330-
maxPipelines, maxScanGoRoutines int, confidenceFilter []string) (ScanOptions, error) {
332+
maxPipelines, maxScanGoRoutines int, confidenceFilter []string, hitTimeout time.Duration) (ScanOptions, error) {
331333

332334
byteSize, err := format.ParseHumanSize(maxArtifactSizeStr)
333335
if err != nil {
@@ -351,6 +353,7 @@ func InitializeOptions(email, accessToken, bitBucketCookie, bitBucketURL, worksp
351353
Artifacts: artifacts,
352354
BitBucketURL: bitBucketURL,
353355
MaxArtifactSize: byteSize,
356+
HitTimeout: hitTimeout,
354357
Context: ctx,
355358
Client: client,
356359
HasProvidedCookie: bitBucketCookie != "",

src/pipeleak/pkg/config/scan_options.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// This package centralizes common configuration patterns across all platform scanners.
33
package config
44

5+
import "time"
6+
57
// CommonScanOptions contains configuration fields that are shared across all platform scanners.
68
// This helps reduce duplication and ensures consistency in option handling.
79
type CommonScanOptions struct {
@@ -17,6 +19,8 @@ type CommonScanOptions struct {
1719
MaxArtifactSize int64
1820
// Owned filters to only owned repositories
1921
Owned bool
22+
// HitTimeout is the maximum time to wait for hit detection per scan item
23+
HitTimeout time.Duration
2024
}
2125

2226
// DefaultCommonScanOptions returns sensible default values for common scan options.
@@ -28,5 +32,6 @@ func DefaultCommonScanOptions() CommonScanOptions {
2832
Artifacts: false,
2933
MaxArtifactSize: 500 * 1024 * 1024, // 500MB
3034
Owned: false,
35+
HitTimeout: 60 * time.Second,
3136
}
3237
}

src/pipeleak/pkg/devops/scan/scanner.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package scan
33
import (
44
"context"
55
"strconv"
6+
"time"
67

78
"github.com/CompassSecurity/pipeleak/pkg/format"
89
artifactproc "github.com/CompassSecurity/pipeleak/pkg/scan/artifact"
@@ -26,6 +27,7 @@ type ScanOptions struct {
2627
Artifacts bool
2728
DevOpsURL string
2829
MaxArtifactSize int64
30+
HitTimeout time.Duration
2931
Context context.Context
3032
Client AzureDevOpsApiClient
3133
}
@@ -173,6 +175,7 @@ func (s *devOpsScanner) scanLogLines(logs []byte, buildWebUrl string) {
173175
logResult, err := logline.ProcessLogs(logs, logline.ProcessOptions{
174176
MaxGoRoutines: s.options.MaxScanGoRoutines,
175177
VerifyCredentials: s.options.TruffleHogVerification,
178+
HitTimeout: s.options.HitTimeout,
176179
})
177180
if err != nil {
178181
log.Debug().Err(err).Str("build", buildWebUrl).Msg("Failed detecting secrets of a single log line")
@@ -227,6 +230,7 @@ func (s *devOpsScanner) analyzeArtifact(art Artifact, buildWebUrl string) {
227230
VerifyCredentials: s.options.TruffleHogVerification,
228231
BuildURL: buildWebUrl,
229232
ArtifactName: art.Name,
233+
HitTimeout: s.options.HitTimeout,
230234
})
231235
if err != nil {
232236
log.Err(err).Msg("Failed processing artifact")
@@ -237,7 +241,7 @@ func (s *devOpsScanner) analyzeArtifact(art Artifact, buildWebUrl string) {
237241
// InitializeOptions prepares scan options from CLI parameters.
238242
func InitializeOptions(username, accessToken, devOpsURL, organization, project, maxArtifactSizeStr string,
239243
artifacts, truffleHogVerification bool,
240-
maxBuilds, maxScanGoRoutines int, confidenceFilter []string) (ScanOptions, error) {
244+
maxBuilds, maxScanGoRoutines int, confidenceFilter []string, hitTimeout time.Duration) (ScanOptions, error) {
241245

242246
byteSize, err := format.ParseHumanSize(maxArtifactSizeStr)
243247
if err != nil {
@@ -259,6 +263,7 @@ func InitializeOptions(username, accessToken, devOpsURL, organization, project,
259263
Artifacts: artifacts,
260264
DevOpsURL: devOpsURL,
261265
MaxArtifactSize: byteSize,
266+
HitTimeout: hitTimeout,
262267
Context: ctx,
263268
Client: client,
264269
}, nil

src/pipeleak/pkg/gitea/scan/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net/http"
99
"net/url"
1010
"strconv"
11+
"time"
1112

1213
"code.gitea.io/sdk/gitea"
1314
"github.com/hashicorp/go-retryablehttp"
@@ -28,6 +29,7 @@ type GiteaScanOptions struct {
2829
RunsLimit int
2930
StartRunID int64
3031
MaxArtifactSize int64
32+
HitTimeout time.Duration
3133
Context context.Context
3234
Client *gitea.Client
3335
HttpClient *retryablehttp.Client

0 commit comments

Comments
 (0)