Skip to content

Commit 9812a77

Browse files
authored
CNVM Severity UNKNOWN Fix (#3885)
### Summary of your changes Following an SDH: - elastic/sdh-beats#6815 Investigated the root cause and suspected this PR: - aquasecurity/trivy#8269 As mentioned in comment: - elastic/sdh-beats#6815 (comment) Suspected the Trivy upgrade. Introduced code to resolve this problem. You can see two examples prior and after code changes of scanning an EBS volume: ``` 🐚 Evgbs-MBP:cloudbeat evgb$ cd /Users/evgb/Documents/GitHub/cloudbeat && go build -o ebs-vuln-scanner-old ./cmd/ebs-vuln-scanner/ && ./ebs-vuln-scanner-old --snapshot snap-ID --region us-east-2 --profile csp --verbose Starting vulnerability scan for EBS snapshot: snap-ID in region: us-east-2 Scanning EBS snapshot (this may take several minutes)... === EBS Snapshot Vulnerability Scan Results === Snapshot: snap-ID Region: us-east-2 Scan Time: 2026-01-22T14:27:28-06:00 Duration: 19m16.221003125s Vulnerability Summary: CRITICAL: 0 HIGH: 0 MEDIUM: 0 LOW: 0 UNKNOWN: 44 ───────────── TOTAL: 44 🐚 Evgbs-MBP:cloudbeat evgb$ cd /Users/evgb/Documents/GitHub/cloudbeat && go build -o ebs-vuln-scanner ./cmd/ebs-vuln-scanner/ && ./ebs-vuln-scanner --snapshot snap-ID --region us-east-2 --profile csp --verbose Starting vulnerability scan for EBS snapshot: snap-ID in region: us-east-2 Scanning EBS snapshot (this may take several minutes)... === EBS Snapshot Vulnerability Scan Results === Snapshot: snap-ID Region: us-east-2 Scan Time: 2026-01-22T14:52:53-06:00 Duration: 1.080233708s Vulnerability Summary: CRITICAL: 0 HIGH: 16 MEDIUM: 24 LOW: 4 UNKNOWN: 0 ───────────── TOTAL: 44 ``` As you can see prior to changes we get UNKNOWN and after we get the correct severities. ### Screenshot/Data <!-- If this PR adds a new feature, please add an example screenshot or data (findings json for example). --> ### Related Issues - Fixes: elastic/kibana#226881 - Fixes: elastic/sdh-beats#6815 - Fixes: #3532 ### Checklist - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have added the necessary README/documentation (if appropriate) #### Introducing a new rule? - [ ] Generate rule metadata using [this script](https://github.com/elastic/cloudbeat/tree/main/security-policies/dev#generate-rules-metadata) - [ ] Add relevant unit tests - [ ] Generate relevant rule templates using [this script](https://github.com/elastic/cloudbeat/tree/main/security-policies/dev#generate-rule-templates), and open a PR in [elastic/packages/cloud_security_posture](https://github.com/elastic/integrations/tree/main/packages/cloud_security_posture)
1 parent d8da10c commit 9812a77

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

internal/vulnerability/events_creator_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@ import (
2626
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
2727
trivyVul "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
2828
"github.com/aquasecurity/trivy/pkg/types"
29+
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
2930
"github.com/elastic/beats/v7/libbeat/beat"
3031
"github.com/stretchr/testify/mock"
3132
"github.com/stretchr/testify/suite"
3233

3334
"github.com/elastic/cloudbeat/internal/dataprovider"
35+
"github.com/elastic/cloudbeat/internal/resources/providers/awslib/ec2"
3436
"github.com/elastic/cloudbeat/internal/resources/utils/testhelper"
3537
)
3638

@@ -378,3 +380,140 @@ func generateResults(size int) []Result {
378380
}
379381
return results
380382
}
383+
384+
func (s *EventsCreatorTestSuite) TestGenerateEvent_SeverityPreserved() {
385+
s.bdp.EXPECT().EnrichEvent(mock.Anything, mock.Anything).Return(nil)
386+
s.cdp.EXPECT().EnrichEvent(mock.Anything).Return(nil)
387+
388+
tests := []struct {
389+
name string
390+
inputSeverity string
391+
expectedSeverity string
392+
}{
393+
{
394+
name: "critical severity is preserved",
395+
inputSeverity: "CRITICAL",
396+
expectedSeverity: "CRITICAL",
397+
},
398+
{
399+
name: "high severity is preserved",
400+
inputSeverity: "HIGH",
401+
expectedSeverity: "HIGH",
402+
},
403+
{
404+
name: "medium severity is preserved",
405+
inputSeverity: "MEDIUM",
406+
expectedSeverity: "MEDIUM",
407+
},
408+
{
409+
name: "low severity is preserved",
410+
inputSeverity: "LOW",
411+
expectedSeverity: "LOW",
412+
},
413+
{
414+
name: "unknown severity is preserved",
415+
inputSeverity: "UNKNOWN",
416+
expectedSeverity: "UNKNOWN",
417+
},
418+
{
419+
name: "empty severity is preserved as empty",
420+
inputSeverity: "",
421+
expectedSeverity: "",
422+
},
423+
}
424+
425+
for _, tt := range tests {
426+
s.Run(tt.name, func() {
427+
// Reset mocks for each sub-test
428+
s.bdp = &dataprovider.MockCommonDataProvider{}
429+
s.cdp = &MockEnricher{}
430+
s.bdp.EXPECT().EnrichEvent(mock.Anything, mock.Anything).Return(nil)
431+
s.cdp.EXPECT().EnrichEvent(mock.Anything).Return(nil)
432+
433+
s.creator = EventsCreator{
434+
log: testhelper.NewLogger(s.T()),
435+
cloudDataProvider: s.bdp,
436+
commonDataProvider: s.cdp,
437+
ch: make(chan []beat.Event),
438+
}
439+
440+
vul := types.DetectedVulnerability{
441+
VulnerabilityID: "CVE-2024-12345",
442+
Vulnerability: dbTypes.Vulnerability{
443+
Severity: tt.inputSeverity,
444+
CVSS: dbTypes.VendorCVSS{
445+
trivyVul.NVD: dbTypes.CVSS{
446+
V3Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
447+
V3Score: 9.8,
448+
},
449+
},
450+
},
451+
}
452+
453+
reportResult := types.Result{
454+
Target: "/test/path",
455+
Class: types.ClassOSPkg,
456+
}
457+
458+
event := s.creator.generateEvent(reportResult, vul, generateTestInstance(), time.Now())
459+
460+
vulField, ok := event.Fields["vulnerability"]
461+
s.Require().True(ok, "event should have vulnerability field")
462+
463+
vulData, ok := vulField.(Vulnerability)
464+
s.Require().True(ok, "vulnerability field should be of type Vulnerability")
465+
466+
s.Equal(tt.expectedSeverity, vulData.Severity,
467+
"severity should be preserved from Trivy's DetectedVulnerability")
468+
})
469+
}
470+
}
471+
472+
func (s *EventsCreatorTestSuite) TestGenerateEvent_SeverityNotUnknownWhenProvided() {
473+
s.bdp.EXPECT().EnrichEvent(mock.Anything, mock.Anything).Return(nil)
474+
s.cdp.EXPECT().EnrichEvent(mock.Anything).Return(nil)
475+
476+
vul := types.DetectedVulnerability{
477+
VulnerabilityID: "CVE-2024-99999",
478+
Vulnerability: dbTypes.Vulnerability{
479+
Severity: "HIGH",
480+
VendorSeverity: dbTypes.VendorSeverity{
481+
trivyVul.RedHat: dbTypes.SeverityHigh,
482+
trivyVul.NVD: dbTypes.SeverityHigh,
483+
},
484+
CVSS: dbTypes.VendorCVSS{
485+
trivyVul.NVD: dbTypes.CVSS{
486+
V3Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
487+
V3Score: 7.5,
488+
},
489+
},
490+
},
491+
}
492+
493+
reportResult := types.Result{
494+
Target: "/test/path",
495+
Class: types.ClassOSPkg,
496+
}
497+
498+
event := s.creator.generateEvent(reportResult, vul, generateTestInstance(), time.Now())
499+
500+
vulField, ok := event.Fields["vulnerability"]
501+
s.Require().True(ok)
502+
503+
vulData, ok := vulField.(Vulnerability)
504+
s.Require().True(ok)
505+
506+
s.NotEqual("UNKNOWN", vulData.Severity,
507+
"severity should not be UNKNOWN when Trivy provides a valid severity - "+
508+
"this may indicate VulnSeveritySources is not configured in scanner.go")
509+
s.Equal("HIGH", vulData.Severity)
510+
}
511+
512+
func generateTestInstance() ec2.Ec2Instance {
513+
instanceID := "i-1234567890abcdef0"
514+
return ec2.Ec2Instance{
515+
Instance: ec2types.Instance{
516+
InstanceId: &instanceID,
517+
},
518+
}
519+
}

internal/vulnerability/scanner.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ func (f VulnerabilityScanner) scan(ctx context.Context, snap ec2.EBSSnapshot) {
145145
Format: "json",
146146
Severities: []db_types.Severity{0, 1, 2, 3, 4},
147147
},
148+
VulnerabilityOptions: flag.VulnerabilityOptions{
149+
VulnSeveritySources: []db_types.SourceID{"auto"},
150+
},
148151
}
149152
now := time.Now()
150153
report, err := f.runner.ScanVM(ctx, opts)

internal/vulnerability/scanner_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"testing"
2323
"time"
2424

25+
db_types "github.com/aquasecurity/trivy-db/pkg/types"
2526
"github.com/aquasecurity/trivy/pkg/commands/artifact"
2627
"github.com/aquasecurity/trivy/pkg/flag"
2728
"github.com/stretchr/testify/assert"
@@ -100,3 +101,19 @@ func TestVulnerabilityScanner_TearDown(t *testing.T) {
100101
// Go defers are implemented as a LIFO stack. This should be the last one to run.
101102
goleak.VerifyNone(t, goleak.IgnoreCurrent())
102103
}
104+
105+
func TestScanOptions_VulnSeveritySourcesConfigured(t *testing.T) {
106+
expectedSeveritySources := []db_types.SourceID{"auto"}
107+
108+
opts := flag.Options{
109+
VulnerabilityOptions: flag.VulnerabilityOptions{
110+
VulnSeveritySources: []db_types.SourceID{"auto"},
111+
},
112+
}
113+
114+
require.NotEmpty(t, opts.VulnerabilityOptions.VulnSeveritySources,
115+
"VulnSeveritySources must be configured, otherwise Trivy returns UNKNOWN for all severities")
116+
117+
assert.Equal(t, expectedSeveritySources, opts.VulnerabilityOptions.VulnSeveritySources,
118+
"VulnSeveritySources should be set to 'auto' to enable automatic severity detection")
119+
}

0 commit comments

Comments
 (0)