Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ _testmain.go

/scanner
data/cvedb.regular
data/govulndb.zip
/task/scannerTask_test1.go
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ REPO ?= neuvector
IMAGE = $(REPO)/scanner:$(TAG)
BUILD_ACTION = --load

.PHONY: all copy_scan build
.PHONY: all copy_scan build

ARCH := $(shell uname -p)

Expand All @@ -107,12 +107,14 @@ gen_license:
copy_scan_slsa:
mkdir -p ${STAGE_DIR}/usr/local/bin/
mkdir -p ${STAGE_DIR}/etc/neuvector/db
mkdir -p ${STAGE_DIR}/etc/neuvector/govulndb
#
cp monitor/monitor ${STAGE_DIR}/usr/local/bin/
cp scanner ${STAGE_DIR}/usr/local/bin/
cp task/scannerTask ${STAGE_DIR}/usr/local/bin/
cp sigstore-interface/sigstore-interface ${STAGE_DIR}/usr/local/bin/sigstore-interface
cp data/cvedb.regular ${STAGE_DIR}/etc/neuvector/db/cvedb
cp data/govulndb.zip ${STAGE_DIR}/etc/neuvector/

buildx-machine:
docker buildx ls
Expand Down
89 changes: 89 additions & 0 deletions common/govulndb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package common

import (
"archive/zip"
"fmt"
"io"
"os"
"path/filepath"
"strings"

scanUtils "github.com/neuvector/neuvector/share/scan"
log "github.com/sirupsen/logrus"
)

func ExtractGovulnDB() error {
// Check if govulndb directory already exists and is not empty
if info, err := os.Stat(scanUtils.GovulcheckDBPath); err == nil && info.IsDir() {
return nil
}
Comment on lines +17 to +19

@holyspectral holyspectral Apr 30, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you can just os.Stat() against the expected path? If the zip file doesn't contain the file it's probably an error too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will just simply check if the extracted result path exist, what do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes that's what I mean. As long as the end result isn't right we should always re-extract.


zipPath := fmt.Sprintf("%s.zip", scanUtils.GovulcheckDBPath)

if _, err := os.Stat(zipPath); os.IsNotExist(err) {
log.WithFields(log.Fields{"path": zipPath}).Error("govulndb.zip not found, skipping extraction")
return err
}

if err := os.RemoveAll(scanUtils.GovulcheckDBPath); err != nil {
return fmt.Errorf("failed to remove existing govulndb directory: %w", err)
}

if err := os.MkdirAll(scanUtils.GovulcheckDBPath, 0o755); err != nil {
return fmt.Errorf("failed to create govulndb directory: %w", err)
}

if err := unzipFile(zipPath, scanUtils.GovulcheckDBPath); err != nil {
return fmt.Errorf("failed to extract govulndb.zip: %w", err)
}

log.WithFields(log.Fields{"target": scanUtils.GovulcheckDBPath}).Info("govulndb extracted successfully")
return nil
}

func unzipFile(zipPath, targetPath string) error {
reader, err := zip.OpenReader(zipPath)
if err != nil {
return fmt.Errorf("failed to open zip file: %w", err)
}
defer reader.Close()

for _, file := range reader.File {
if err := extractZipFile(file, targetPath); err != nil {
return fmt.Errorf("failed to extract %s: %w", file.Name, err)
}
}

return nil
}

func extractZipFile(file *zip.File, targetPath string) error {
filePath := filepath.Join(targetPath, file.Name)

if !strings.HasPrefix(filePath, filepath.Clean(targetPath)+string(os.PathSeparator)) {
return nil
}
Comment on lines +63 to +65

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd hardcode the expected file path and ignore others.


if file.FileInfo().IsDir() {
return os.MkdirAll(filePath, file.Mode())
}

if err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil {
return err
}

outFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
if err != nil {
return err
}
defer outFile.Close()

rc, err := file.Open()
if err != nil {
return err
}
defer rc.Close()

_, err = io.Copy(outFile, rc)
return err
}
22 changes: 22 additions & 0 deletions cvetools/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ func (cv *ScanTools) DetectAppVul(path string, apps []detectors.AppFeatureVersio
func checkForVulns(app detectors.AppFeatureVersion, appIndex int, apps []detectors.AppFeatureVersion, mv []common.AppModuleVul) []vulFullReport {
vuls := make([]vulFullReport, 0)
for _, v := range mv {
if isGovulncheckFalsePositive(app, v.VulName) {
continue
}

if common.Debugs.Enabled {
if common.Debugs.CVEs.Contains(v.VulName) && common.Debugs.Features.Contains(app.AppName) {
Expand Down Expand Up @@ -120,6 +123,25 @@ func checkForVulns(app detectors.AppFeatureVersion, appIndex int, apps []detecto
return vuls
}

func isGovulncheckFalsePositive(app detectors.AppFeatureVersion, vulName string) bool {
if app.AppName != "golang" {
return false
}

for _, finding := range app.GovulncheckFindings {
if finding.OSV == vulName {
return false
}
for _, alias := range finding.Aliases {
if alias == vulName {
return false
}
}
}

return true
}

func appVul2FullVul(app detectors.AppFeatureVersion, mv common.AppModuleVul) vulFullReport {
var fv vulFullReport
fv.Vf.Name = mv.VulName
Expand Down
83 changes: 83 additions & 0 deletions cvetools/apps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package cvetools
import (
"testing"

"github.com/neuvector/neuvector/share/scan"
"github.com/neuvector/neuvector/share/utils"
"github.com/neuvector/scanner/common"
"github.com/neuvector/scanner/detectors"
)

type versionTestCase struct {
Expand Down Expand Up @@ -67,3 +69,84 @@ func TestFixedVersion(t *testing.T) {
}
}
}

func TestIsGovulncheckFalsePositive(t *testing.T) {
app := detectors.AppFeatureVersion{
AppPackage: scan.AppPackage{
AppName: "golang",
ModuleName: "go:github.com/docker/docker",
Version: "28.5.2+incompatible",
GovulncheckFindings: []scan.GovulnFinding{
{
OSV: "GO-2026-4883",
Aliases: []string{"CVE-2026-33997", "GHSA-pxq6-2prw-chj9"},
},
},
},
}

if isGovulncheckFalsePositive(app, "GO-2026-4883") {
t.Fatal("OSV should not be treated as false positive")
}
if isGovulncheckFalsePositive(app, "CVE-2026-33997") {
t.Fatal("CVE alias should not be treated as false positive")
}
if isGovulncheckFalsePositive(app, "GHSA-pxq6-2prw-chj9") {
t.Fatal("GHSA alias should not be treated as false positive")
}
if !isGovulncheckFalsePositive(app, "CVE-2015-3627") {
t.Fatal("unexpected vuln should be treated as false positive")
}
}

func TestCheckForVulnsFilteredByGovulncheck(t *testing.T) {
apps := []detectors.AppFeatureVersion{
{
AppPackage: scan.AppPackage{
AppName: "golang",
ModuleName: "go:github.com/docker/docker",
Version: "28.5.2+incompatible",
FileName: "usr/bin/example",
GovulncheckFindings: []scan.GovulnFinding{
{
OSV: "GO-2026-4883",
Aliases: []string{"CVE-2026-33997"},
},
},
},
},
}

dbVuls := []common.AppModuleVul{
{
VulName: "CVE-2026-33997",
ModuleName: "go:github.com/docker/docker",
Severity: "High",
Description: "kept",
AffectedVer: []common.AppModuleVersion{{OpCode: "", Version: "28.5.2+incompatible"}},
FixedVer: []common.AppModuleVersion{{OpCode: "", Version: "28.5.3"}},
},
{
VulName: "CVE-2015-3627",
ModuleName: "go:github.com/docker/docker",
Severity: "High",
Description: "filtered",
AffectedVer: []common.AppModuleVersion{{OpCode: "", Version: "28.5.2+incompatible"}},
FixedVer: []common.AppModuleVersion{{OpCode: "", Version: "28.5.3"}},
},
}

got := checkForVulns(apps[0], 0, apps, dbVuls)
if len(got) != 1 {
t.Fatalf("checkForVulns() count = %d, want 1", len(got))
}
if got[0].Vf.Name != "CVE-2026-33997" {
t.Fatalf("kept vuln = %q, want CVE-2026-33997", got[0].Vf.Name)
}
if len(apps[0].ModuleVuls) != 1 {
t.Fatalf("ModuleVuls count = %d, want 1", len(apps[0].ModuleVuls))
}
if apps[0].ModuleVuls[0].Name != "CVE-2026-33997" {
t.Fatalf("ModuleVuls[0] = %q, want CVE-2026-33997", apps[0].ModuleVuls[0].Name)
}
}
Binary file added data/govulndb.zip.local
Binary file not shown.
3 changes: 3 additions & 0 deletions package/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
ARG VULNDB_VERSION=4.148
ARG VULNDB_CHECKSUM=8781ee7a9c80db7f945d915584be5dcc7bb4e4440e49a3e21b77a6d77da83505
ARG GOVULNDB_CHECKSUM=be132d3a8f0405e0720e83c7b5921fb96b23b0478fda27761bf13a25198e8736
ARG SIGSTORE_VERSION=426a5b0c9dff026dbc1961c81e232509a7c335cb
#
# Builder
#
FROM registry.suse.com/bci/golang:1.26.2 AS builder
ARG VERSION
ARG VULNDB_CHECKSUM
ARG GOVULNDB_CHECKSUM
ARG SIGSTORE_VERSION

RUN zypper in -y wget
Expand All @@ -24,6 +26,7 @@ COPY Makefile go.mod go.sum *.go genlic.sh /src/
WORKDIR /src
RUN git clone https://github.com/neuvector/sigstore-interface --single-branch sigstore-interface && cd sigstore-interface && git checkout ${SIGSTORE_VERSION} && make
RUN if [ -f "data/cvedb.regular" ]; then echo "using cvedb.regular"; echo "$VULNDB_CHECKSUM data/cvedb.regular" | sha256sum --check --status; else echo "using cvedb"; cp "data/cvedb" "data/cvedb.regular"; fi
RUN if [ -f "data/govulndb.zip" ]; then echo "using govulndb.zip"; echo "$GOVULNDB_CHECKSUM data/govulndb.zip" | sha256sum --check --status; else echo "using govulndb.zip.local"; cp "data/govulndb.zip.local" "data/govulndb.zip"; fi
RUN make slsa_all

#
Expand Down
5 changes: 5 additions & 0 deletions scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ func main() {
log.SetLevel(log.InfoLevel)
log.SetFormatter(&utils.LogFormatter{Module: "SCN"})

if err := common.ExtractGovulnDB(); err != nil {
log.WithFields(log.Fields{"error": err}).Error("Failed to extract govulndb")
os.Exit(1)
}

dbPath := flag.String("d", "./dbgen/", "cve database file directory")
join := flag.String("j", "", "Controller join address")
joinPort := flag.Uint("join_port", 0, "Controller join port")
Expand Down