Skip to content

Commit 2995ef0

Browse files
fix: multiple scans using the same scanner instance caused "close of closed channel" panic (#300)
1 parent 3bb71d3 commit 2995ef0

File tree

5 files changed

+251
-164
lines changed

5 files changed

+251
-164
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
with:
2525
go-version: "^1.22"
2626
- name: Go Linter
27-
run: docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.61.0 golangci-lint run -v -E gofmt --timeout=5m
27+
run: docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v2.1.5 golangci-lint run --timeout=5m
2828

2929
- name: Unit Tests
3030
run: go test ./...

engine/rules/rules.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const TagPublicSecret = "public-secret"
7070
const TagSensitiveUrl = "sensitive-url"
7171
const TagWebhook = "webhook"
7272

73-
func getDefaultRules() *[]Rule {
73+
func GetDefaultRules() *[]Rule {
7474
allRules := &[]Rule{
7575
{Rule: *rules.AdafruitAPIKey(), Tags: []string{TagApiKey}, ScoreParameters: ScoreParameters{Category: CategoryIoTPlatform, RuleType: 4}},
7676
{Rule: *rules.AdobeClientID(), Tags: []string{TagClientId}, ScoreParameters: ScoreParameters{Category: CategorySaaS, RuleType: 1}},
@@ -293,7 +293,7 @@ func FilterRules(selectedList, ignoreList, specialList []string) *[]Rule {
293293
log.Warn().Msgf("Both 'rule' and 'ignoreRule' flags were provided, I will first take all in 'rule' and then remove all in 'ignoreRule' from the list.")
294294
}
295295

296-
selectedRules := getDefaultRules()
296+
selectedRules := GetDefaultRules()
297297
if len(selectedList) > 0 {
298298
selectedRules = selectRules(selectedRules, selectedList)
299299
}

engine/rules/rules_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
)
88

99
func TestLoadAllRules(t *testing.T) {
10-
rules := getDefaultRules()
10+
rules := GetDefaultRules()
1111

1212
if len(*rules) <= 1 {
1313
t.Error("no rules were loaded")
@@ -16,7 +16,7 @@ func TestLoadAllRules(t *testing.T) {
1616

1717
func TestLoadAllRules_DuplicateRuleID(t *testing.T) {
1818
ruleIDMap := make(map[string]bool)
19-
allRules := getDefaultRules()
19+
allRules := GetDefaultRules()
2020

2121
for _, rule := range *allRules {
2222
if _, ok := ruleIDMap[rule.Rule.RuleID]; ok {
@@ -29,7 +29,7 @@ func TestLoadAllRules_DuplicateRuleID(t *testing.T) {
2929

3030
func Test_FilterRules_SelectRules(t *testing.T) {
3131
specialRule := HardcodedPassword()
32-
allRules := *getDefaultRules()
32+
allRules := *GetDefaultRules()
3333
rulesCount := len(allRules)
3434

3535
tests := []struct {

pkg/scan.go

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package scanner
33
import (
44
"errors"
55
"fmt"
6+
"github.com/checkmarx/2ms/v3/lib/secrets"
7+
"github.com/checkmarx/2ms/v3/plugins"
68
"sync"
79

810
"github.com/checkmarx/2ms/v3/lib/reporting"
@@ -23,23 +25,36 @@ func NewScanner() Scanner {
2325
return &scanner{}
2426
}
2527

28+
func resetCmdGlobals() {
29+
cmd.Channels = plugins.Channels{
30+
Items: make(chan plugins.ISourceItem),
31+
Errors: make(chan error),
32+
WaitGroup: &sync.WaitGroup{},
33+
}
34+
35+
cmd.Report = reporting.Init()
36+
37+
cmd.SecretsChan = make(chan *secrets.Secret)
38+
cmd.SecretsExtrasChan = make(chan *secrets.Secret)
39+
cmd.ValidationChan = make(chan *secrets.Secret)
40+
cmd.CvssScoreWithoutValidationChan = make(chan *secrets.Secret)
41+
}
42+
2643
func (s *scanner) Scan(scanItems []ScanItem, scanConfig ScanConfig) (*reporting.Report, error) {
27-
itemsCh := cmd.Channels.Items
28-
errorsCh := cmd.Channels.Errors
44+
resetCmdGlobals()
45+
2946
bufferedErrors := make(chan error, len(scanItems)+1)
3047
wg := &sync.WaitGroup{}
3148

32-
// Error listener
3349
go func() {
34-
for err := range errorsCh {
50+
for err := range cmd.Channels.Errors {
3551
if err != nil {
3652
bufferedErrors <- err
3753
}
3854
}
3955
close(bufferedErrors)
4056
}()
4157

42-
// Initialize engine
4358
engineConfig := engine.EngineConfig{
4459
IgnoredIds: scanConfig.IgnoreResultIds,
4560
IgnoreList: scanConfig.IgnoreRules,
@@ -49,25 +64,21 @@ func (s *scanner) Scan(scanItems []ScanItem, scanConfig ScanConfig) (*reporting.
4964
return &reporting.Report{}, fmt.Errorf("error initializing engine: %w", err)
5065
}
5166

52-
// Start processing pipeline
5367
startPipeline(engineInstance, scanConfig.WithValidation)
5468

55-
// Send scan items
5669
for _, item := range scanItems {
5770
wg.Add(1)
5871
go func(si ScanItem) {
5972
defer wg.Done()
60-
itemsCh <- si
73+
cmd.Channels.Items <- si
6174
}(item)
6275
}
6376
wg.Wait()
64-
close(itemsCh)
77+
close(cmd.Channels.Items)
6578

66-
// Wait for all processing
6779
cmd.Channels.WaitGroup.Wait()
68-
close(errorsCh)
80+
close(cmd.Channels.Errors)
6981

70-
// Collect errors
7182
var errs []error
7283
for err = range bufferedErrors {
7384
errs = append(errs, err)
@@ -96,46 +107,34 @@ func startPipeline(engineInstance engine.IEngine, withValidation bool) {
96107
}
97108

98109
func (s *scanner) ScanDynamic(itemsIn <-chan ScanItem, scanConfig ScanConfig) (*reporting.Report, error) {
99-
itemsCh := cmd.Channels.Items
100-
errorsCh := cmd.Channels.Errors
110+
resetCmdGlobals()
101111

102-
// Initialize engine configuration.
103-
engineConfig := engine.EngineConfig{IgnoredIds: scanConfig.IgnoreResultIds, IgnoreList: scanConfig.IgnoreRules}
112+
engineConfig := engine.EngineConfig{
113+
IgnoredIds: scanConfig.IgnoreResultIds,
114+
IgnoreList: scanConfig.IgnoreRules,
115+
}
104116
engineInstance, err := engine.Init(engineConfig)
105117
if err != nil {
106118
return &reporting.Report{}, fmt.Errorf("error initializing engine: %w", err)
107119
}
108120

109-
// Start processing routines.
110-
cmd.Channels.WaitGroup.Add(1)
111-
go cmd.ProcessItems(engineInstance, "custom")
112-
113-
cmd.Channels.WaitGroup.Add(1)
114-
go cmd.ProcessSecrets()
115-
116-
cmd.Channels.WaitGroup.Add(1)
117-
go cmd.ProcessSecretsExtras()
118-
119-
cmd.Channels.WaitGroup.Add(1)
120-
go cmd.ProcessScoreWithoutValidation(engineInstance)
121+
startPipeline(engineInstance, false)
121122

122-
for item := range itemsIn {
123-
itemsCh <- item
124-
}
125-
close(itemsCh)
123+
go func() {
124+
for item := range itemsIn {
125+
cmd.Channels.Items <- item
126+
}
127+
close(cmd.Channels.Items)
128+
}()
126129

127-
// Wait for all processing routines.
128130
cmd.Channels.WaitGroup.Wait()
129-
close(errorsCh)
131+
close(cmd.Channels.Errors)
130132

131-
// Check if any error occurred.
132-
for err := range errorsCh {
133+
for err := range cmd.Channels.Errors {
133134
if err != nil {
134135
return &reporting.Report{}, fmt.Errorf("error processing scan items: %w", err)
135136
}
136137
}
137138

138-
// Finalize and generate report.
139-
report := cmd.Report
140-
return report, nil
139+
return cmd.Report, nil
141140
}

0 commit comments

Comments
 (0)