diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 6d5ab66a2bd..c176ca24e54 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -55,7 +55,7 @@ jobs: - name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: '>=1.26.2' + go-version: '>=1.27.0' cache: true - name: Set up terraform uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 diff --git a/AGENTS.md b/AGENTS.md index bda75a613f5..73ee03eff12 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -125,7 +125,7 @@ Always format and lint your code before proposing changes. The repository provid - Linter: `golangci-lint` - **Running Go Linters**: Run `golangci-lint` using `go run` directly within the module directory (`go/`, `vulnfeeds/`, or `bindings/go/`), or run `tools/lint_and_format.sh` which automatically maps changed files to the enclosing module: ```bash - cd go && go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0 run ./... + cd go && go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.0 run ./... ``` *(Note: Run outside the sandbox so `go run` can fetch the linter toolchain if not cached).* - **Formatting Command**: diff --git a/bindings/go/.golangci.yaml b/bindings/go/.golangci.yaml index c66c97b37a0..14c23c589ac 100644 --- a/bindings/go/.golangci.yaml +++ b/bindings/go/.golangci.yaml @@ -3,6 +3,8 @@ version: "2" linters: default: all # prettier-ignore + enable: + - gomodguard_v2 # gomodguard is deprecated but it's still in the default disable: - forbidigo - paralleltest @@ -11,6 +13,7 @@ linters: - depguard # Too annoying - err113 # will re-add later (another-rex) - exhaustruct # overkill (g-rath) + - exhaustruct_v5 - forcetypeassert # too hard (g-rath) - funlen # - funcorder # diff --git a/bindings/go/go.mod b/bindings/go/go.mod index 750664c66d9..9d22b061b1c 100644 --- a/bindings/go/go.mod +++ b/bindings/go/go.mod @@ -1,6 +1,6 @@ module osv.dev/bindings/go -go 1.26.5 +go 1.27.0 require ( github.com/google/go-cmp v0.7.0 diff --git a/bindings/go/osvdevexperimental/paging.go b/bindings/go/osvdevexperimental/paging.go index 6b39e3b3e5f..cc05c84e50e 100644 --- a/bindings/go/osvdevexperimental/paging.go +++ b/bindings/go/osvdevexperimental/paging.go @@ -71,9 +71,9 @@ func BatchQueryPaging(ctx context.Context, c OSVClientInterface, queries []*api. } // --- Paging logic --- var errToReturn error - //nolint:prealloc + var nextPageQueries []*api.Query - //nolint:prealloc + var nextPageIndexMap []int for i, res := range batchResp.GetResults() { if res.GetNextPageToken() == "" { diff --git a/docker/ci/install_go.sh b/docker/ci/install_go.sh index b20fbd0a2b8..79cd95ab301 100755 --- a/docker/ci/install_go.sh +++ b/docker/ci/install_go.sh @@ -19,4 +19,4 @@ set -eux # Download and install Go # https://go.dev/doc/install -curl https://go.dev/dl/go1.26.2.linux-amd64.tar.gz -LO && rm -rf /usr/local/go && tar -C /usr/local -xzf go1.26.2.linux-amd64.tar.gz +curl https://go.dev/dl/go1.27.0.linux-amd64.tar.gz -LO && rm -rf /usr/local/go && tar -C /usr/local -xzf go1.27.0.linux-amd64.tar.gz diff --git a/docker/terraform/Dockerfile b/docker/terraform/Dockerfile index b17c3ddd5cf..e44eb6e9dd2 100644 --- a/docker/terraform/Dockerfile +++ b/docker/terraform/Dockerfile @@ -1,6 +1,6 @@ # Taken and modified from https://github.com/GoogleCloudPlatform/cloud-builders-community/tree/master/terraform -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS GO_BUILD +FROM golang:1.27.0-alpine AS GO_BUILD ARG TERRAFORM_VERSION WORKDIR /build/ diff --git a/docs/go.mod b/docs/go.mod index 417bbc34f3b..bbdfc4b1b3b 100644 --- a/docs/go.mod +++ b/docs/go.mod @@ -1,6 +1,6 @@ module osv.dev/tools -go 1.26.5 +go 1.27.0 require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 diff --git a/external/.golangci.yaml b/external/.golangci.yaml index c66c97b37a0..14c23c589ac 100644 --- a/external/.golangci.yaml +++ b/external/.golangci.yaml @@ -3,6 +3,8 @@ version: "2" linters: default: all # prettier-ignore + enable: + - gomodguard_v2 # gomodguard is deprecated but it's still in the default disable: - forbidigo - paralleltest @@ -11,6 +13,7 @@ linters: - depguard # Too annoying - err113 # will re-add later (another-rex) - exhaustruct # overkill (g-rath) + - exhaustruct_v5 - forcetypeassert # too hard (g-rath) - funlen # - funcorder # diff --git a/external/cmd/ids/main_test.go b/external/cmd/ids/main_test.go index cecfa50cd4f..63f260fa316 100644 --- a/external/cmd/ids/main_test.go +++ b/external/cmd/ids/main_test.go @@ -45,6 +45,7 @@ func TestAssignIDs(t *testing.T) { if err != nil { t.Fatalf("failed to read existing ID: %v", err) } + //nolint:gosec // G703: Writing to test temp directory err = os.WriteFile(filepath.Join(tmpDir, tt.existingName), existingData, 0600) if err != nil { t.Fatalf("failed to copy existing ID: %v", err) @@ -57,6 +58,7 @@ func TestAssignIDs(t *testing.T) { t.Fatalf("failed to read template %s: %v", templatePath, err) } destPath := filepath.Join(tmpDir, tt.templateName) + //nolint:gosec // G703: Writing to test temp directory if err := os.WriteFile(destPath, templateData, 0600); err != nil { t.Fatalf("failed to setup unassigned vuln: %v", err) } diff --git a/external/cmd/pypi/main.go b/external/cmd/pypi/main.go index 6a11b38628f..5206f7228ff 100644 --- a/external/cmd/pypi/main.go +++ b/external/cmd/pypi/main.go @@ -51,6 +51,7 @@ func loadExisting(vulnsDir string) (map[string]bool, error) { return nil } + //nolint:gosec // G122: Local directory walk in CLI script f, err := os.Open(path) if err != nil { return fmt.Errorf("failed to open %s: %w", path, err) diff --git a/external/cmd/pypi/main_test.go b/external/cmd/pypi/main_test.go index 26d9be8a485..0ec5e3d91e7 100644 --- a/external/cmd/pypi/main_test.go +++ b/external/cmd/pypi/main_test.go @@ -98,10 +98,6 @@ affected: } } -func strPtr(s string) *string { - return &s -} - func TestGeneratePyPIAffected(t *testing.T) { cve := models.NVDCVE{ ID: "CVE-2022-29194", @@ -115,13 +111,13 @@ func TestGeneratePyPIAffected(t *testing.T) { { Vulnerable: true, Criteria: "cpe:2.3:a:google:tensorflow:*:*:*:*:*:*:*:*", - VersionEndExcluding: strPtr("2.6.4"), + VersionEndExcluding: new("2.6.4"), }, { Vulnerable: true, Criteria: "cpe:2.3:a:google:tensorflow:*:*:*:*:*:*:*:*", - VersionStartIncluding: strPtr("2.7.0"), - VersionEndExcluding: strPtr("2.7.2"), + VersionStartIncluding: new("2.7.0"), + VersionEndExcluding: new("2.7.2"), }, }, }, diff --git a/external/go.mod b/external/go.mod index 797cd75dc3b..e576e0d5c2c 100644 --- a/external/go.mod +++ b/external/go.mod @@ -1,6 +1,6 @@ module github.com/google/osv.dev/external -go 1.26.5 +go 1.27.0 require ( github.com/aquasecurity/go-pep440-version v0.0.1 diff --git a/external/pypi/pypi.go b/external/pypi/pypi.go index 9e8e8376812..d8a13ef6ec3 100644 --- a/external/pypi/pypi.go +++ b/external/pypi/pypi.go @@ -351,9 +351,9 @@ func (p *PyPI) packageExists(pkg string) bool { func (p *PyPI) finalPkgCheck(cve models.NVDCVE, pkg string, falsePositives *triage.FalsePositives) bool { // To avoid false positives, check that the pkg name is mentioned in the description. desc := strings.ToLower(models.EnglishDescription(cve.Descriptions)) - pkgNameParts := strings.Split(pkg, "-") + pkgNameParts := strings.SplitSeq(pkg, "-") - for _, part := range pkgNameParts { + for part := range pkgNameParts { // Python packages can commonly be py or -py. // Remove this to be a bit more lenient when matching against the description. part = strings.TrimPrefix(part, "py") diff --git a/gcp/indexer/Dockerfile b/gcp/indexer/Dockerfile index 28084ae944d..0e20ba54419 100644 --- a/gcp/indexer/Dockerfile +++ b/gcp/indexer/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS GO_BUILD +FROM golang:1.27.0-alpine AS GO_BUILD WORKDIR /build # Cache dependencies in these steps diff --git a/gcp/indexer/go.mod b/gcp/indexer/go.mod index edf397f2f11..a30495927b2 100644 --- a/gcp/indexer/go.mod +++ b/gcp/indexer/go.mod @@ -1,6 +1,6 @@ module github.com/google/osv.dev/gcp/indexer -go 1.26.5 +go 1.27.0 require ( cloud.google.com/go/datastore v1.26.0 diff --git a/go/.golangci.yaml b/go/.golangci.yaml index 4d7c582435d..248cc73f1dd 100644 --- a/go/.golangci.yaml +++ b/go/.golangci.yaml @@ -3,6 +3,8 @@ version: "2" linters: default: all # prettier-ignore + enable: + - gomodguard_v2 # gomodguard is deprecated but it's still in the default disable: - forbidigo - paralleltest @@ -12,6 +14,7 @@ linters: - depguard # Too annoying - err113 # will re-add later (another-rex) - exhaustruct # overkill (g-rath) + - exhaustruct_v5 - forcetypeassert # too hard (g-rath) - funlen # - funcorder # diff --git a/go/Dockerfile b/go/Dockerfile index 3e3a566ade0..015b7fecd8f 100644 --- a/go/Dockerfile +++ b/go/Dockerfile @@ -31,7 +31,7 @@ # ======================================================== # Shared Builder Stage # ======================================================== -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS builder +FROM golang:1.27.0-alpine AS builder WORKDIR /workspace diff --git a/go/cmd/exporter/downloader.go b/go/cmd/exporter/downloader.go index 292faf7f5eb..733f506e944 100644 --- a/go/cmd/exporter/downloader.go +++ b/go/cmd/exporter/downloader.go @@ -85,6 +85,7 @@ func downloadThenProcessor(ctx context.Context, cancel context.CancelFunc, clien // Cache pre-compressed Deflate payload to local scratch disk. localPath := filepath.Join(scratchDir, vuln.GetId()+".deflate") + //nolint:gosec // G703: Staging temporary file in scratch directory if err := os.WriteFile(localPath, compressedBytes, 0600); err != nil { logger.ErrorContext(ctx, "failed to write cached vulnerability to disk", slog.String("id", vuln.GetId()), slog.Any("err", err)) // Cancel the exporter context if writing to the scratch disk fails (e.g. disk full) diff --git a/go/cmd/exporter/exporter.go b/go/cmd/exporter/exporter.go index d7818bf136b..660fa53db53 100644 --- a/go/cmd/exporter/exporter.go +++ b/go/cmd/exporter/exporter.go @@ -50,6 +50,7 @@ func main() { flag.Parse() scratchDir := *scratchDirFlag + //nolint:gosec // G703: Creating scratch directory if err := os.MkdirAll(scratchDir, 0755); err != nil { logger.FatalContext(ctx, "failed to create scratch directory", slog.String("dir", scratchDir), slog.Any("err", err)) } diff --git a/go/cmd/exporter/worker.go b/go/cmd/exporter/worker.go index c292c16497b..f90ea9ce4dd 100644 --- a/go/cmd/exporter/worker.go +++ b/go/cmd/exporter/worker.go @@ -291,6 +291,7 @@ func writeVanir(ctx context.Context, vanirVulnIDs []string, outCh chan<- writeMs vulns := make([]json.RawMessage, 0, len(vanirVulnIDs)) for _, id := range vanirVulnIDs { localPath := filepath.Join(scratchDir, id+".deflate") + //nolint:gosec // G703: Reading temporary local file in scratch directory f, err := os.Open(localPath) if err != nil { logger.ErrorContext(ctx, "failed to open local vuln file for vanir", slog.String("id", id), slog.Any("err", err)) diff --git a/go/cmd/extract_versions/main.go b/go/cmd/extract_versions/main.go index 0cb73925d39..3a1e7e47c01 100644 --- a/go/cmd/extract_versions/main.go +++ b/go/cmd/extract_versions/main.go @@ -38,9 +38,7 @@ func main() { // Start workers for range numWorkers { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { for file := range fileChan { r, err := file.Open() if err != nil { @@ -80,7 +78,7 @@ func main() { } } } - }() + }) } // Collector @@ -110,6 +108,7 @@ func main() { vers := slices.Sorted(maps.Keys(allVersions)) // Write to output file + //nolint:gosec // G703: Writing to CLI output file f, err := os.Create(outputFile) if err != nil { panic(err) diff --git a/go/cmd/gitter/git.go b/go/cmd/gitter/git.go index bd11a6556b4..b76f68f5ff1 100644 --- a/go/cmd/gitter/git.go +++ b/go/cmd/gitter/git.go @@ -21,6 +21,8 @@ type FetchOptions struct { // prepareCmd prepares the command with context cancellation handled by sending SIGINT. func prepareCmd(ctx context.Context, dir string, env []string, name string, args ...string) *exec.Cmd { + //nolint: gosec // G702: The command arguments are mostly from within gitter + // The only user controlled input is the repoURL, which is still from internal services cmd := exec.CommandContext(ctx, name, args...) if dir != "" { cmd.Dir = dir diff --git a/go/cmd/gitter/gitter.go b/go/cmd/gitter/gitter.go index db7432aa9ee..9a65d75eddc 100644 --- a/go/cmd/gitter/gitter.go +++ b/go/cmd/gitter/gitter.go @@ -390,6 +390,7 @@ func main() { if repoMaxCostUint > math.MaxInt64 { logger.Fatal("Repo cache max cost too large", slog.Uint64("maxCost", repoMaxCostUint)) } + //nolint:gosec // G115: The check is right above us repoCacheMaxCostBytes = int64(repoMaxCostUint) loadLastFetchMap() diff --git a/go/cmd/gitter/repository.go b/go/cmd/gitter/repository.go index ea303a76112..8d37d48e6a8 100644 --- a/go/cmd/gitter/repository.go +++ b/go/cmd/gitter/repository.go @@ -234,8 +234,8 @@ func (r *Repository) buildCommitGraph(ctx context.Context, cache *pb.RepositoryC continue } // Only keep tags - if strings.HasPrefix(ref, "tag: ") { - tags = append(tags, strings.TrimPrefix(ref, "tag: ")) + if after, ok := strings.CutPrefix(ref, "tag: "); ok { + tags = append(tags, after) } } diff --git a/go/cmd/osv-linter-worker/Dockerfile b/go/cmd/osv-linter-worker/Dockerfile index ae0cf0ff401..6861d503a55 100644 --- a/go/cmd/osv-linter-worker/Dockerfile +++ b/go/cmd/osv-linter-worker/Dockerfile @@ -14,7 +14,7 @@ # Stage 1: Build the Go linter binary -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS go_builder +FROM golang:1.27.0-alpine AS go_builder RUN apk add --no-cache git WORKDIR /src diff --git a/go/cmd/osv-linter-worker/main.go b/go/cmd/osv-linter-worker/main.go index cf1a27c0b60..96f8f9c29d8 100644 --- a/go/cmd/osv-linter-worker/main.go +++ b/go/cmd/osv-linter-worker/main.go @@ -33,7 +33,7 @@ import ( "os" "os/exec" "path/filepath" - "sort" + "slices" "strings" "time" @@ -296,8 +296,7 @@ func runLinter(binaryPath, dataDir string) ([]byte, error) { logger.Info("Executing linter", slog.String("cmd", cmd.String())) output, err := cmd.Output() // err usually just indicates that there were findings if err != nil { - var exitErr *exec.ExitError - if !errors.As(err, &exitErr) { + if _, ok := errors.AsType[*exec.ExitError](err); !ok { logger.Error("Linter execution failed unexpectedly", slog.Any("err", err)) return nil, err } @@ -373,9 +372,7 @@ func processLinterResult(ctx context.Context, store internalmodels.ImportFinding for f := range uniqueFindings { sortedFindings = append(sortedFindings, f) } - sort.Slice(sortedFindings, func(i, j int) bool { - return sortedFindings[i] < sortedFindings[j] - }) + slices.Sort(sortedFindings) prefix := strings.Split(bugID, "-")[0] + "-" source := prefixToSource[prefix] @@ -396,10 +393,7 @@ func processLinterResult(ctx context.Context, store internalmodels.ImportFinding // Fill channel for i := 0; i < len(findingsToPut); i += batchSize { - end := i + batchSize - if end > len(findingsToPut) { - end = len(findingsToPut) - } + end := min(i+batchSize, len(findingsToPut)) batchChan <- findingsToPut[i:end] } close(batchChan) @@ -441,10 +435,7 @@ func processLinterResult(ctx context.Context, store internalmodels.ImportFinding deleteBatchChan := make(chan []string, len(idsToDelete)/batchSize+1) for i := 0; i < len(idsToDelete); i += batchSize { - end := i + batchSize - if end > len(idsToDelete) { - end = len(idsToDelete) - } + end := min(i+batchSize, len(idsToDelete)) deleteBatchChan <- idsToDelete[i:end] } close(deleteBatchChan) diff --git a/go/cmd/recordchecker/recordchecker.go b/go/cmd/recordchecker/recordchecker.go index c48f22c0616..ca4bc8f7060 100644 --- a/go/cmd/recordchecker/recordchecker.go +++ b/go/cmd/recordchecker/recordchecker.go @@ -129,13 +129,11 @@ func run(ctx context.Context, env *appEnv) error { // Start the worker pool var workerWg sync.WaitGroup for range env.numWorkers { - workerWg.Add(1) - go func() { - defer workerWg.Done() + workerWg.Go(func() { for task := range tasksChan { resultsChan <- checkRecord(ctx, env.ds, env.storage, task.id, task.vuln) } - }() + }) } // Queue all invalid records from the previous run. diff --git a/go/cmd/relations/alias_test.go b/go/cmd/relations/alias_test.go index 732a3360b0d..18708be04c5 100644 --- a/go/cmd/relations/alias_test.go +++ b/go/cmd/relations/alias_test.go @@ -395,13 +395,7 @@ func TestUpdateAliasGroup(t *testing.T) { foundGroup := false expectedIDs := []string{"bbb-123", "bbb-234", "bbb-345", "bbb-456", "bbb-789"} for _, g := range groups { - contains := false - for _, id := range g.VulnIDs { - if id == "bbb-123" { - contains = true - break - } - } + contains := slices.Contains(g.VulnIDs, "bbb-123") if contains { foundGroup = true if len(g.VulnIDs) != len(expectedIDs) { @@ -485,13 +479,7 @@ func TestCreateAliasGroup(t *testing.T) { foundGroup := false expectedIDs := []string{"test-123", "test-124", "test-222"} for _, g := range groups { - contains := false - for _, id := range g.VulnIDs { - if id == "test-123" { - contains = true - break - } - } + contains := slices.Contains(g.VulnIDs, "test-123") if contains { foundGroup = true if len(g.VulnIDs) != len(expectedIDs) { @@ -727,13 +715,7 @@ func TestAllowList(t *testing.T) { slices.Sort(expectedIDs) for _, g := range groups { - contains := false - for _, id := range g.VulnIDs { - if id == "eee-123" { - contains = true - break - } - } + contains := slices.Contains(g.VulnIDs, "eee-123") if contains { foundGroup = true if len(g.VulnIDs) != len(expectedIDs) { @@ -962,12 +944,10 @@ func TestAliasGroupReachesLimit(t *testing.T) { } // Add one more bug that links to the group - vulns := []*models.Vulnerability{ - {AliasRaw: []string{"iii-0"}}, - } - keys := []*datastore.Key{ - datastore.NameKey("Vulnerability", "iii-new", nil), - } + vulns := make([]*models.Vulnerability, 0, 1+len(bugIDs)) + vulns = append(vulns, &models.Vulnerability{AliasRaw: []string{"iii-0"}}) + keys := make([]*datastore.Key, 0, 1+len(bugIDs)) + keys = append(keys, datastore.NameKey("Vulnerability", "iii-new", nil)) // Also create bugs for existing group members for i, id := range bugIDs { diff --git a/go/cmd/staging_api_test/generator.go b/go/cmd/staging_api_test/generator.go index dd03cf00702..d3e05bd3332 100644 --- a/go/cmd/staging_api_test/generator.go +++ b/go/cmd/staging_api_test/generator.go @@ -218,10 +218,7 @@ func buildBatchPayload(rng *rand.Rand, requestIDs []string, vulnMap map[string]* maxBatchQueries = 100 } - sampleSize := rng.IntN(maxBatchQueries) + 1 - if sampleSize > len(requestIDs) { - sampleSize = len(requestIDs) - } + sampleSize := min(rng.IntN(maxBatchQueries)+1, len(requestIDs)) // Sample random IDs perm := rng.Perm(len(requestIDs)) @@ -291,6 +288,8 @@ func newHTTPClient() *http.Client { } // executeRequest sends an HTTP request and updates stats. +// +//nolint:gosec // G704: Staging API load testing client func executeRequest(ctx context.Context, client *http.Client, req *http.Request, stats *GeneratorStats) { req = req.WithContext(ctx) resp, err := client.Do(req) @@ -344,6 +343,7 @@ func runVulnWorker(ctx context.Context, wg *sync.WaitGroup, client *http.Client, reqID := pools.VulnQueryIDs[index%length] index++ url := fmt.Sprintf("%s/%s", baseURL, reqID) + //nolint:gosec // G704: Staging test generator req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { continue diff --git a/go/cmd/staging_api_test/loader.go b/go/cmd/staging_api_test/loader.go index 3283a81927d..0f5b4ecad6b 100644 --- a/go/cmd/staging_api_test/loader.go +++ b/go/cmd/staging_api_test/loader.go @@ -234,10 +234,7 @@ func LoadQueryPoolsFromZip(ctx context.Context, zipReader *zip.Reader, rng *rand }) const mostCommon = 5000 - limit := mostCommon - if len(counts) < limit { - limit = len(counts) - } + limit := min(mostCommon, len(counts)) var largeBatchQueryIDs []string for i := range limit { @@ -277,6 +274,7 @@ func LoadQueryPools(ctx context.Context, gcsClient *storage.Client, bucket, zipP } if isTemp { defer func() { + //nolint:gosec // G703: Cleaning up temporary zip file _ = os.Remove(zipFilePath) }() } diff --git a/go/cmd/tools/reimport-tui/go.mod b/go/cmd/tools/reimport-tui/go.mod index 29633634aa0..d5b96400b7d 100644 --- a/go/cmd/tools/reimport-tui/go.mod +++ b/go/cmd/tools/reimport-tui/go.mod @@ -1,6 +1,6 @@ module github.com/google/osv.dev/go/cmd/tools/reimport-tui -go 1.26.5 +go 1.27.0 replace github.com/google/osv.dev/go => ../../../ diff --git a/go/cmd/tools/reimport-tui/main.go b/go/cmd/tools/reimport-tui/main.go index b82e31f8594..4184a324c21 100644 --- a/go/cmd/tools/reimport-tui/main.go +++ b/go/cmd/tools/reimport-tui/main.go @@ -326,7 +326,7 @@ func (m model) updateSelectEnv(msg tea.Msg) (tea.Model, tea.Cmd) { func (m model) updateLoading(msg tea.Msg) (tea.Model, tea.Cmd) { if msg, ok := msg.(sourcesLoadedMsg); ok { - var items []list.Item + items := make([]list.Item, 0, len(msg.sources)) for _, r := range msg.sources { items = append(items, &item{repo: r}) } @@ -478,19 +478,19 @@ func (m model) View() tea.View { cursor = cursorStyle.Render("> ") name = selectedItemStyle.Render(env.name) } - s.WriteString(fmt.Sprintf("%s%s\n", cursor, name)) + fmt.Fprintf(&s, "%s%s\n", cursor, name) } s.WriteString("\n" + helpStyle.Render("[Use up/down to navigate, Enter to select, ctrl+c to quit]") + "\n") case stateLoading: - s.WriteString(fmt.Sprintf("Connecting to Datastore and loading sources for %s...\n", m.selectedEnv.name)) + fmt.Fprintf(&s, "Connecting to Datastore and loading sources for %s...\n", m.selectedEnv.name) case stateSelectSources: s.WriteString(m.list.View()) case stateConfirm: s.WriteString(headerStyle.Render("Confirm Reimport Triggering:") + "\n\n") - s.WriteString(fmt.Sprintf("You are about to trigger reimport for the following sources in %s:\n", m.selectedEnv.name)) + fmt.Fprintf(&s, "You are about to trigger reimport for the following sources in %s:\n", m.selectedEnv.name) var checked []*item for _, li := range m.list.Items() { if i, ok := li.(*item); ok && i.checked { @@ -498,10 +498,7 @@ func (m model) View() tea.View { } } - maxItemsToPrint := contentHeight - 8 - if maxItemsToPrint < 1 { - maxItemsToPrint = 1 - } + maxItemsToPrint := max(contentHeight-8, 1) printed := 0 for _, i := range checked { @@ -535,19 +532,16 @@ func (m model) View() tea.View { } } - s.WriteString(fmt.Sprintf("Success: %d, Skipped: %d, Failed: %d\n", successes, skipped, len(failures))) + fmt.Fprintf(&s, "Success: %d, Skipped: %d, Failed: %d\n", successes, skipped, len(failures)) if len(failures) > 0 { s.WriteString("\n" + errorStyle.Render("Failures:") + "\n") - maxFailuresToPrint := contentHeight - 10 - if maxFailuresToPrint < 1 { - maxFailuresToPrint = 1 - } + maxFailuresToPrint := max(contentHeight-10, 1) printed := 0 for _, res := range failures { if printed < maxFailuresToPrint { - s.WriteString(fmt.Sprintf(" - %-25s : %v\n", res.name, res.err)) + fmt.Fprintf(&s, " - %-25s : %v\n", res.name, res.err) printed++ } } diff --git a/go/cmd/website-devserver/store.go b/go/cmd/website-devserver/store.go index 854cf8e008a..d25759f7a06 100644 --- a/go/cmd/website-devserver/store.go +++ b/go/cmd/website-devserver/store.go @@ -426,7 +426,7 @@ func (ds *DevStore) Search(_ context.Context, query models.VulnerabilitySearchQu } // Build ListedVulnerability - var packages []models.Package + packages := make([]models.Package, 0, len(v.GetAffected())) isFixed := false for _, a := range v.GetAffected() { pkg := models.Package{ diff --git a/go/go.mod b/go/go.mod index fcd7d234234..c0c7f1298b4 100644 --- a/go/go.mod +++ b/go/go.mod @@ -1,6 +1,6 @@ module github.com/google/osv.dev/go -go 1.26.5 +go 1.27.0 replace osv.dev/bindings/go => ../bindings/go diff --git a/go/internal/api/determine_version.go b/go/internal/api/determine_version.go index 2ffcd85d898..24a243ac01c 100644 --- a/go/internal/api/determine_version.go +++ b/go/internal/api/determine_version.go @@ -68,8 +68,8 @@ func shouldSkipBucket(path string) bool { if path == "" { return false } - components := strings.Split(path, "/") - for _, c := range components { + components := strings.SplitSeq(path, "/") + for c := range components { if _, ok := vendoredLibNames[strings.ToLower(c)]; ok { return true } diff --git a/go/internal/api/import_finding.go b/go/internal/api/import_finding.go index 60e1397bc1c..76d4efcb60a 100644 --- a/go/internal/api/import_finding.go +++ b/go/internal/api/import_finding.go @@ -39,7 +39,7 @@ func (s *server) ImportFindings(ctx context.Context, params *pb.ImportFindingsPa if f > math.MaxInt32 || f < math.MinInt32 { f = -1 } - //nolint:gosec // G115: f is checked to be within int32 range above + protoRecord.Findings[i] = pb.ImportFindingType(f) } result.InvalidRecords = append(result.InvalidRecords, protoRecord) diff --git a/go/internal/api/query_affected.go b/go/internal/api/query_affected.go index c27970eba09..5dd8d97b1e4 100644 --- a/go/internal/api/query_affected.go +++ b/go/internal/api/query_affected.go @@ -86,8 +86,7 @@ func (s *server) QueryAffected(ctx context.Context, params *pb.QueryAffectedPara estimatedSizeBytes, ) if err != nil { - var panicErr *safe.PanicError - if errors.As(err, &panicErr) { + if panicErr, ok := errors.AsType[*safe.PanicError](err); ok { logger.ErrorContext(ctx, "recovered panic in background worker", slog.Any("panic", panicErr.Value), slog.String("stack", string(panicErr.Stack)), @@ -307,8 +306,7 @@ func (s *server) QueryAffectedBatch(ctx context.Context, params *pb.QueryAffecte result := <-resultsChan if result.err != nil { cancelPipelines(result.err) // Abort all other running pipelines in the background - var panicErr *safe.PanicError - if errors.As(result.err, &panicErr) { + if panicErr, ok := errors.AsType[*safe.PanicError](result.err); ok { logger.ErrorContext(ctx, "recovered panic in batch worker", slog.Any("panic", panicErr.Value), slog.String("stack", string(panicErr.Stack)), @@ -692,8 +690,7 @@ func (s *server) collectAndSort(ctx context.Context, if errors.Is(err, models.ErrInvalidCursor) { return nil, status.Error(codes.InvalidArgument, "invalid cursor") } - var panicErr *safe.PanicError - if errors.As(err, &panicErr) { + if _, ok := errors.AsType[*safe.PanicError](err); ok { // Return the raw PanicError so the caller handlers can detect it, // log the stack trace, and obscure it into a clean "internal server error". return nil, err diff --git a/go/internal/database/datastore/affected_versions.go b/go/internal/database/datastore/affected_versions.go index d5bb6129164..cc372c82fc5 100644 --- a/go/internal/database/datastore/affected_versions.go +++ b/go/internal/database/datastore/affected_versions.go @@ -89,7 +89,7 @@ func computeAffectedVersions(vuln *osvschema.Vulnerability) []AffectedVersions { _ = osvutil.SortEvents(eHelper, events) } - var rangeEvents []AffectedEvent + rangeEvents := make([]AffectedEvent, 0, len(events)) for _, e := range events { rangeEvents = append(rangeEvents, AffectedEvent{Type: e.Type.String(), Value: e.Version}) } diff --git a/go/internal/database/datastore/import_finding.go b/go/internal/database/datastore/import_finding.go index 63dd706398b..e3dc73cb5d7 100644 --- a/go/internal/database/datastore/import_finding.go +++ b/go/internal/database/datastore/import_finding.go @@ -68,8 +68,7 @@ func (s *ImportFindingsStore) GetMulti(ctx context.Context, bugIDs []string) ([] entities := make([]*ImportFinding, len(bugIDs)) err := s.dsClient.GetMulti(ctx, keys, entities) if err != nil { - var multiErr datastore.MultiError - if errors.As(err, &multiErr) { + if multiErr, ok := errors.AsType[datastore.MultiError](err); ok { for i, e := range multiErr { if errors.Is(e, datastore.ErrNoSuchEntity) { entities[i] = nil diff --git a/go/internal/database/datastore/repo_index.go b/go/internal/database/datastore/repo_index.go index 670a0b5bdca..83b3409b88d 100644 --- a/go/internal/database/datastore/repo_index.go +++ b/go/internal/database/datastore/repo_index.go @@ -117,8 +117,7 @@ func (s *RepoIndexStore) GetRepoIndexes(ctx context.Context, ids []string) ([]*m dbIndexes := make([]*RepoIndex, len(ids)) err := s.client.GetMulti(ctx, keys, dbIndexes) if err != nil { - var multiErr datastore.MultiError - if errors.As(err, &multiErr) { + if multiErr, ok := errors.AsType[datastore.MultiError](err); ok { for i, e := range multiErr { if errors.Is(e, datastore.ErrNoSuchEntity) { dbIndexes[i] = nil diff --git a/go/internal/database/datastore/vulnerability.go b/go/internal/database/datastore/vulnerability.go index 338802076db..93e8cc31e84 100644 --- a/go/internal/database/datastore/vulnerability.go +++ b/go/internal/database/datastore/vulnerability.go @@ -169,8 +169,7 @@ func (s *VulnerabilityStore) batchGetModified(ctx context.Context, ids []string) err := s.client.GetMulti(ctx, keys, vulns) if err != nil { - var multiErr datastore.MultiError - if errors.As(err, &multiErr) { + if multiErr, ok := errors.AsType[datastore.MultiError](err); ok { for j := range ids { errIdx := multiErr[j] if errIdx == nil { @@ -378,10 +377,7 @@ func (s *VulnerabilityStore) updateAffectedCommits(ctx context.Context, id strin numPages := 0 const batchSize = 10000 for i := 0; i < len(sortedCommits); i += batchSize { - end := i + batchSize - if end > len(sortedCommits) { - end = len(sortedCommits) - } + end := min(i+batchSize, len(sortedCommits)) batch := sortedCommits[i:end] acKey := datastore.NameKey("AffectedCommits", fmt.Sprintf("%s-%d", id, numPages), nil) diff --git a/go/internal/database/datastore/vulnerability_matching_test.go b/go/internal/database/datastore/vulnerability_matching_test.go index 9ce3c428f70..2ef4bfc144a 100644 --- a/go/internal/database/datastore/vulnerability_matching_test.go +++ b/go/internal/database/datastore/vulnerability_matching_test.go @@ -676,6 +676,7 @@ func TestVulnerabilityStore_MatchPackages_Pagination_DuplicateIDAcrossCursor(t * } } +//nolint:gosec // G101: Test cursor tokens are not actually credentials func TestParsePythonCursor(t *testing.T) { tests := []struct { name string diff --git a/go/internal/importer/importer.go b/go/internal/importer/importer.go index cf00fab7b01..aed2d796513 100644 --- a/go/internal/importer/importer.go +++ b/go/internal/importer/importer.go @@ -206,7 +206,7 @@ func RunReconcile(ctx context.Context, config Config) error { } var wg sync.WaitGroup - //nolint:prealloc // Size is unknown since All() returns an iterator + var sourceRepos []*models.SourceRepository gitBranches := make(map[string]string) // url -> branch diff --git a/go/internal/models/import_finding.go b/go/internal/models/import_finding.go index b9b0fd6497c..b636a88992c 100644 --- a/go/internal/models/import_finding.go +++ b/go/internal/models/import_finding.go @@ -1,4 +1,3 @@ -// Package models contains the domain types for the OSV database. package models import ( diff --git a/go/internal/models/errors.go b/go/internal/models/models.go similarity index 81% rename from go/internal/models/errors.go rename to go/internal/models/models.go index 9337526030c..abfa24ae877 100644 --- a/go/internal/models/errors.go +++ b/go/internal/models/models.go @@ -1,3 +1,4 @@ +// Package models contains the domain types for the OSV database. package models import "errors" diff --git a/go/internal/models/relations.go b/go/internal/models/relations.go index a853369b563..64117afe3d0 100644 --- a/go/internal/models/relations.go +++ b/go/internal/models/relations.go @@ -1,4 +1,3 @@ -// Package models contains the domain types for the OSV database. package models import ( diff --git a/go/internal/models/repo_allowlist.go b/go/internal/models/repo_allowlist.go index 01b33fc4bbc..9d0b33b3fa1 100644 --- a/go/internal/models/repo_allowlist.go +++ b/go/internal/models/repo_allowlist.go @@ -1,4 +1,3 @@ -// Package models contains the domain types for the OSV database. package models import "context" diff --git a/go/internal/models/source_repository.go b/go/internal/models/source_repository.go index bf4bff6c7cd..35103379f1b 100644 --- a/go/internal/models/source_repository.go +++ b/go/internal/models/source_repository.go @@ -1,4 +1,3 @@ -// Package models contains the domain types for the OSV database. package models import ( diff --git a/go/internal/models/vulnerability.go b/go/internal/models/vulnerability.go index 3d616636f32..e69e4575a50 100644 --- a/go/internal/models/vulnerability.go +++ b/go/internal/models/vulnerability.go @@ -1,4 +1,3 @@ -// Package models contains the domain types for the OSV database. package models import ( diff --git a/go/internal/models/vulnerability_search.go b/go/internal/models/vulnerability_search.go index 0e0cb1c76d4..f94cc8e869b 100644 --- a/go/internal/models/vulnerability_search.go +++ b/go/internal/models/vulnerability_search.go @@ -1,4 +1,3 @@ -// Package models contains the domain types for the OSV database. package models import ( diff --git a/go/internal/osvutil/batcher/batcher.go b/go/internal/osvutil/batcher/batcher.go index 7e3f1a14062..88bc4d1f4a0 100644 --- a/go/internal/osvutil/batcher/batcher.go +++ b/go/internal/osvutil/batcher/batcher.go @@ -157,6 +157,7 @@ func (b *Batcher[K, R]) Get(ctx context.Context, key K) (R, error) { if isLeader { // The leader spawns the background worker to process this batch. + //nolint:contextcheck // worker uses mergedCtx from queued requests go b.runBatchLoop() } diff --git a/go/internal/osvutil/batcher/context_test.go b/go/internal/osvutil/batcher/context_test.go index 39dfd91b19c..e1017c185f7 100644 --- a/go/internal/osvutil/batcher/context_test.go +++ b/go/internal/osvutil/batcher/context_test.go @@ -46,8 +46,7 @@ func TestMergeContexts_AllCancelled(t *testing.T) { } func TestMergeContexts_ManualCancel(t *testing.T) { - ctx1, cancel1 := context.WithCancel(context.Background()) - defer cancel1() + ctx1 := t.Context() mergedCtx, cancelMerged := mergeContexts([]context.Context{ctx1}) diff --git a/go/internal/osvutil/schema/schema.go b/go/internal/osvutil/schema/schema.go index de02470c602..3466e463a78 100644 --- a/go/internal/osvutil/schema/schema.go +++ b/go/internal/osvutil/schema/schema.go @@ -60,9 +60,11 @@ func Validate(data []byte) error { if !result.Valid() { var errs string + var errsSb63 strings.Builder for _, desc := range result.Errors() { - errs += fmt.Sprintf("- %s\n", desc) + fmt.Fprintf(&errsSb63, "- %s\n", desc) } + errs += errsSb63.String() return fmt.Errorf("schema validation failed:\n%s", errs) } diff --git a/go/internal/recoverer/recoverer.go b/go/internal/recoverer/recoverer.go index 335dc80c9ab..a1a391b150a 100644 --- a/go/internal/recoverer/recoverer.go +++ b/go/internal/recoverer/recoverer.go @@ -418,8 +418,8 @@ func (r *Recoverer) HandleGCSGenMismatch(ctx context.Context, m *pubsub.Message) modified = dsVuln.Modified vulnProto := proto.Clone(&baseProto).(*osvschema.Vulnerability) - fields := strings.Split(fieldStr, ",") - for _, f := range fields { + fields := strings.SplitSeq(fieldStr, ",") + for f := range fields { switch strings.TrimSpace(f) { case "aliases": aliasResult, err := r.stores.Relations.GetAliases(ctx, vulnID) diff --git a/go/internal/repos/repos.go b/go/internal/repos/repos.go index 3ec61afad50..17448117725 100644 --- a/go/internal/repos/repos.go +++ b/go/internal/repos/repos.go @@ -137,6 +137,7 @@ func gitterGet(ctx context.Context, gitterHost, repoURL string, forceUpdate bool } // Use a custom transport to add OpenTelemetry tracing. client := http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)} + //nolint:gosec // G704: Request to internal gitter service req, err := http.NewRequestWithContext(ctx, http.MethodGet, getGitURL, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) @@ -148,6 +149,7 @@ func gitterGet(ctx context.Context, gitterHost, repoURL string, forceUpdate bool } req.URL.RawQuery = vals.Encode() logger.Info("Getting repo from gitter", slog.String("url", req.URL.String())) + //nolint:gosec // G704: Request to internal gitter service resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("failed to get repo from gitter: %w", err) diff --git a/go/internal/sharding/sharding.go b/go/internal/sharding/sharding.go index 191573f745c..35ddc9c3c0d 100644 --- a/go/internal/sharding/sharding.go +++ b/go/internal/sharding/sharding.go @@ -51,7 +51,7 @@ func ExpandBreakdownPrefixes(str string) []string { for _, word := range expand.Braces(w) { var buf bytes.Buffer _ = printer.Print(&buf, word) - for _, p := range strings.Split(buf.String(), ",") { + for p := range strings.SplitSeq(buf.String(), ",") { p = strings.TrimSpace(p) if p != "" { prefixes = append(prefixes, p) diff --git a/go/internal/website/blog.go b/go/internal/website/blog.go index a4dc5d13a15..3585ae62f05 100644 --- a/go/internal/website/blog.go +++ b/go/internal/website/blog.go @@ -34,11 +34,9 @@ func (s *Server) handleBlogIndex(w http.ResponseWriter, r *http.Request) { } data := BlogPageData{ - BasePageData: BasePageData{ - ActiveSection: "blog", - DisableTurboCache: false, - }, - Index: indexHTML, + ActiveSection: "blog", + DisableTurboCache: false, + Index: indexHTML, } s.render(w, r, "blog.html", http.StatusOK, data) @@ -78,11 +76,9 @@ func (s *Server) handleBlogPost(w http.ResponseWriter, r *http.Request) { } data := BlogPostPageData{ - BasePageData: BasePageData{ - ActiveSection: "blog", - DisableTurboCache: true, - }, - Content: postHTML, + ActiveSection: "blog", + DisableTurboCache: true, + Content: postHTML, } s.render(w, r, "blog_post.html", http.StatusOK, data) diff --git a/go/internal/website/linter.go b/go/internal/website/linter.go index 5d73fe8fcbe..c67eb32504b 100644 --- a/go/internal/website/linter.go +++ b/go/internal/website/linter.go @@ -17,11 +17,13 @@ func (s *Server) redirectLinterToTest(w http.ResponseWriter, r *http.Request) bo } if host == "osv.dev" { + //nolint:gosec // G710: Hardcoded redirect target http.Redirect(w, r, "https://test.osv.dev"+r.URL.RequestURI(), http.StatusFound) return true } if host == "api.osv.dev" { + //nolint:gosec // G710: Hardcoded redirect target http.Redirect(w, r, "https://api.test.osv.dev"+r.URL.RequestURI(), http.StatusFound) return true @@ -37,9 +39,7 @@ func (s *Server) handleLinterPage(w http.ResponseWriter, r *http.Request) { } data := LinterPageData{ - BasePageData: BasePageData{ - ActiveSection: "linter", - }, + ActiveSection: "linter", } s.renderStandalone(w, r, "linter.html", http.StatusOK, data) diff --git a/go/internal/website/list.go b/go/internal/website/list.go index 2dc24a3509f..358e4b1ac33 100644 --- a/go/internal/website/list.go +++ b/go/internal/website/list.go @@ -36,6 +36,7 @@ func (s *Server) handleList(w http.ResponseWriter, r *http.Request) { if encoded := queryVals.Encode(); encoded != "" { targetURL += "?" + encoded } + //nolint:gosec // G710: Internal canonical redirect http.Redirect(w, r, targetURL, http.StatusFound) return @@ -127,11 +128,9 @@ func (s *Server) handleList(w http.ResponseWriter, r *http.Request) { } data := ListPageData{ - BasePageData: BasePageData{ - Title: pageTitle, - ActiveSection: "vulnerabilities", - DisableTurboCache: true, - }, + Title: pageTitle, + ActiveSection: "vulnerabilities", + DisableTurboCache: true, Query: q, SelectedEcosystem: ecosystem, CurrentAfter: afterStr, diff --git a/go/internal/website/list_models.go b/go/internal/website/list_models.go index d367366faf4..0c5340f4a5e 100644 --- a/go/internal/website/list_models.go +++ b/go/internal/website/list_models.go @@ -38,10 +38,7 @@ func formatRelativeTime(value, now time.Time) string { return "" } - diff := now.Sub(value) - if diff < 0 { - diff = 0 - } + diff := max(now.Sub(value), 0) diffSeconds := int64(diff.Seconds()) diffMinutes := diffSeconds / 60 @@ -79,8 +76,8 @@ func formatRelativeTime(value, now time.Time) string { } func stripScheme(rawURL string) string { - if idx := strings.Index(rawURL, "://"); idx != -1 { - return rawURL[idx+3:] + if _, after, ok := strings.Cut(rawURL, "://"); ok { + return after } return rawURL @@ -92,10 +89,7 @@ func (v ListedVulnerabilityDisplay) DisplayPackages() []string { return nil } - limit := len(v.Packages) - if limit > 5 { - limit = 5 - } + limit := min(len(v.Packages), 5) result := make([]string, 0, limit) for i := range limit { diff --git a/go/internal/website/server_test.go b/go/internal/website/server_test.go index f16d13c5866..e7cadc7f8ce 100644 --- a/go/internal/website/server_test.go +++ b/go/internal/website/server_test.go @@ -925,6 +925,7 @@ func TestTriageEndpoints(t *testing.T) { }, }) req := httptest.NewRequest(http.MethodGet, "/triage", nil) + //nolint:gosec // G124: Synthetic cookie in test HTTP request req.AddCookie(&http.Cookie{ Name: "osv_session", Value: "tampered-or-invalid-cookie", diff --git a/go/internal/website/static.go b/go/internal/website/static.go index bfada675bbc..eea25ea1341 100644 --- a/go/internal/website/static.go +++ b/go/internal/website/static.go @@ -20,10 +20,8 @@ func (s *Server) RenderNotFound(w http.ResponseWriter, r *http.Request) { // RenderNotFoundWithVuln renders the 404 Not Found page with a failed import vulnerability ID. func (s *Server) RenderNotFoundWithVuln(w http.ResponseWriter, r *http.Request, failedImportVulnID string) { data := NotFoundPageData{ - BasePageData: BasePageData{ - ActiveSection: "", - DisableTurboCache: false, - }, + ActiveSection: "", + DisableTurboCache: false, FailedImportVulnID: failedImportVulnID, } @@ -86,11 +84,9 @@ func (s *Server) handleRoot(w http.ResponseWriter, r *http.Request) { } data := HomePageData{ - BasePageData: BasePageData{ - ActiveSection: "home", - DisableTurboCache: false, - }, - Ecosystems: computeEcosystemDisplays(s.getEcosystemCounts(r.Context())), + ActiveSection: "home", + DisableTurboCache: false, + Ecosystems: computeEcosystemDisplays(s.getEcosystemCounts(r.Context())), } s.render(w, r, "home.html", http.StatusOK, data) @@ -137,6 +133,7 @@ func (s *Server) handleRobots(w http.ResponseWriter, r *http.Request) { } sitemapURL := fmt.Sprintf("%s://%s/sitemap_index.xml", scheme, r.Host) w.Header().Set("Content-Type", "text/plain; charset=utf-8") + //nolint:gosec // G705: Response content type is text/plain _, _ = fmt.Fprintf(w, "Sitemap: %s\n", sitemapURL) } diff --git a/go/internal/website/triage.go b/go/internal/website/triage.go index 553be5db50e..daab4fbeed7 100644 --- a/go/internal/website/triage.go +++ b/go/internal/website/triage.go @@ -14,10 +14,8 @@ var cveIDRegex = regexp.MustCompile(`^(?i)CVE-\d{4}-\d+$`) // handleTriagePage handles serving the vulnerability triage UI page. func (s *Server) handleTriagePage(w http.ResponseWriter, r *http.Request) { data := TriagePageData{ - BasePageData: BasePageData{ - ActiveSection: "triage", - }, - Columns: []int{1, 2, 3}, + ActiveSection: "triage", + Columns: []int{1, 2, 3}, } s.render(w, r, "triage.html", http.StatusOK, data) @@ -69,6 +67,7 @@ func (s *Server) handleTriageProxy(w http.ResponseWriter, r *http.Request) { } w.Header().Set("Content-Type", "application/json") + //nolint:gosec // G705: Response content type is application/json if _, err := w.Write(data); err != nil { logger.ErrorContext(r.Context(), "failed to write triage file response", "source", source, "id", vulnID, "error", err) } diff --git a/go/internal/website/vulnerability.go b/go/internal/website/vulnerability.go index becbbb5f2bd..c12d8e431ee 100644 --- a/go/internal/website/vulnerability.go +++ b/go/internal/website/vulnerability.go @@ -178,9 +178,7 @@ func (s *Server) handleVulnerabilityDetails(w http.ResponseWriter, r *http.Reque } data := VulnerabilityPageData{ - BasePageData: BasePageData{ - ActiveSection: "vulnerabilities", - }, + ActiveSection: "vulnerabilities", Vulnerability: vuln, APIURL: s.config.APIURL, HumanSourceLink: humanLink, diff --git a/go/internal/website/vulnerability_helpers.go b/go/internal/website/vulnerability_helpers.go index 35599070768..3527a855cc2 100644 --- a/go/internal/website/vulnerability_helpers.go +++ b/go/internal/website/vulnerability_helpers.go @@ -132,9 +132,9 @@ func ConstructHierarchyHTML(targetID string, hierarchy *models.Hierarchy, knownI if vulnID != targetID { escapedID := template.HTMLEscapeString(vulnID) if _, known := knownIDs[vulnID]; known { - sb.WriteString(fmt.Sprintf(`
  • %s
  • `, escapedID, escapedID)) + fmt.Fprintf(&sb, `
  • %s
  • `, escapedID, escapedID) } else { - sb.WriteString(fmt.Sprintf("
  • %s
  • ", escapedID)) + fmt.Fprintf(&sb, "
  • %s
  • ", escapedID) } } @@ -208,8 +208,8 @@ func GitCommitLink(repoURL, commit string) string { // StripScheme removes http:// or https:// scheme from a URL string. func StripScheme(rawURL string) string { - if idx := strings.Index(rawURL, "://"); idx != -1 { - return rawURL[idx+3:] + if _, after, ok := strings.Cut(rawURL, "://"); ok { + return after } return rawURL diff --git a/go/internal/worker/engine.go b/go/internal/worker/engine.go index e4bedffda11..571fed19a07 100644 --- a/go/internal/worker/engine.go +++ b/go/internal/worker/engine.go @@ -1,4 +1,3 @@ -// Package worker contains the implementation for the vulnerability enrichment worker pipeline. package worker import ( diff --git a/go/osv/ecosystem/opam.go b/go/osv/ecosystem/opam.go index 95a988f4fa9..a1ddbbf876b 100644 --- a/go/osv/ecosystem/opam.go +++ b/go/osv/ecosystem/opam.go @@ -54,8 +54,8 @@ func (e opamEcosystem) getVersions(pkg string) ([]string, error) { seen := make(map[string]bool) for _, name := range list1 { - if strings.HasPrefix(name, prefix) { - v := strings.TrimPrefix(name, prefix) + if after, ok := strings.CutPrefix(name, prefix); ok { + v := after if !seen[v] { versions = append(versions, v) seen[v] = true @@ -63,8 +63,8 @@ func (e opamEcosystem) getVersions(pkg string) ([]string, error) { } } for _, name := range list2 { - if strings.HasPrefix(name, prefix) { - v := strings.TrimPrefix(name, prefix) + if after, ok := strings.CutPrefix(name, prefix); ok { + v := after if !seen[v] { versions = append(versions, v) seen[v] = true diff --git a/go/purl/ecosystem_packagist.go b/go/purl/ecosystem_packagist.go index c9fcd26172e..b7e9e1e3bb6 100644 --- a/go/purl/ecosystem_packagist.go +++ b/go/purl/ecosystem_packagist.go @@ -40,12 +40,14 @@ func packagistParser(purl packageurl.PackageURL) (packageName string, ecosystem return "", "", err } + var ecosystemSb43 strings.Builder for _, qualifier := range purl.Qualifiers { if qualifier.Key == repositoryURLQualifier && qualifier.Value != "" { - ecosystem += ":" + qualifier.Value + ecosystemSb43.WriteString(":" + qualifier.Value) break } } + ecosystem += ecosystemSb43.String() return packageName, ecosystem, nil } diff --git a/tools/apitester/.golangci.yaml b/tools/apitester/.golangci.yaml index c66c97b37a0..14c23c589ac 100644 --- a/tools/apitester/.golangci.yaml +++ b/tools/apitester/.golangci.yaml @@ -3,6 +3,8 @@ version: "2" linters: default: all # prettier-ignore + enable: + - gomodguard_v2 # gomodguard is deprecated but it's still in the default disable: - forbidigo - paralleltest @@ -11,6 +13,7 @@ linters: - depguard # Too annoying - err113 # will re-add later (another-rex) - exhaustruct # overkill (g-rath) + - exhaustruct_v5 - forcetypeassert # too hard (g-rath) - funlen # - funcorder # diff --git a/tools/apitester/go.mod b/tools/apitester/go.mod index 7beedd08fe1..a7f4d74f0ee 100644 --- a/tools/apitester/go.mod +++ b/tools/apitester/go.mod @@ -1,6 +1,6 @@ module github.com/google/apitester -go 1.26.5 +go 1.27.0 require ( github.com/gkampitakis/go-snaps v0.5.23 diff --git a/tools/apitester/internal/vcr/clean.go b/tools/apitester/internal/vcr/clean.go index 355e00d94cc..4183a406b58 100644 --- a/tools/apitester/internal/vcr/clean.go +++ b/tools/apitester/internal/vcr/clean.go @@ -39,7 +39,7 @@ func indent(str string, level int) string { // at least the size of the string being indented and one indent level s.Grow(len(str) + len(indenting)) - for _, line := range strings.Split(str, "\n") { + for line := range strings.SplitSeq(str, "\n") { s.WriteString(indenting) s.WriteString(line) s.WriteString("\n") diff --git a/tools/datastore-remover/go.mod b/tools/datastore-remover/go.mod index c90f8ca6e36..93f299449c4 100644 --- a/tools/datastore-remover/go.mod +++ b/tools/datastore-remover/go.mod @@ -1,6 +1,6 @@ module github.com/google/datastore-remover -go 1.26.5 +go 1.27.0 require ( cloud.google.com/go/datastore v1.26.0 diff --git a/tools/indexer-api-caller/caller.go b/tools/indexer-api-caller/caller.go index c104b158c82..ebaead4c114 100644 --- a/tools/indexer-api-caller/caller.go +++ b/tools/indexer-api-caller/caller.go @@ -126,7 +126,7 @@ func buildGit(repoDir string) ([]*FileResult, error) { log.Printf("Hashed %v files", len(fileResults)) b := strings.Builder{} - b.WriteString(fmt.Sprintf(`{"name":"%s", "file_hashes": [`, filepath.Base(repoDir))) + fmt.Fprintf(&b, `{"name":"%s", "file_hashes": [`, filepath.Base(repoDir)) for i, fr := range fileResults { if i == len(fileResults)-1 { diff --git a/tools/indexer-api-caller/go.mod b/tools/indexer-api-caller/go.mod index 62963ad0f97..76c9fb45c3c 100644 --- a/tools/indexer-api-caller/go.mod +++ b/tools/indexer-api-caller/go.mod @@ -1,3 +1,3 @@ module github.com/google/indexer-api-caller -go 1.26.5 +go 1.27.0 diff --git a/tools/lint_and_format.py b/tools/lint_and_format.py index 20bbbd45d12..15e019bbce6 100755 --- a/tools/lint_and_format.py +++ b/tools/lint_and_format.py @@ -369,7 +369,7 @@ def check_prerequisites(has_py: bool, has_go: bool, has_tf: bool) -> bool: GOLANGCI_LINT_PKG = ( - 'github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0') + 'github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.0') GOLANGCI_LINT_CMD = ['go', 'run', GOLANGCI_LINT_PKG] diff --git a/tools/repo-allowlist-sync/go.mod b/tools/repo-allowlist-sync/go.mod index 736c77e63d5..42e6fa0b8d0 100644 --- a/tools/repo-allowlist-sync/go.mod +++ b/tools/repo-allowlist-sync/go.mod @@ -1,6 +1,6 @@ module github.com/google/osv.dev/tools/repo-allowlist-sync -go 1.26.5 +go 1.27.0 require ( cloud.google.com/go/datastore v1.26.0 diff --git a/vulnfeeds/.golangci.yaml b/vulnfeeds/.golangci.yaml index c66c97b37a0..20886a2b7a0 100644 --- a/vulnfeeds/.golangci.yaml +++ b/vulnfeeds/.golangci.yaml @@ -3,6 +3,8 @@ version: "2" linters: default: all # prettier-ignore + enable: + - gomodguard_v2 # gomodguard is deprecated but it's still in the default disable: - forbidigo - paralleltest @@ -11,6 +13,7 @@ linters: - depguard # Too annoying - err113 # will re-add later (another-rex) - exhaustruct # overkill (g-rath) + - exhaustruct_v5 - forcetypeassert # too hard (g-rath) - funlen # - funcorder # @@ -34,6 +37,8 @@ linters: - wrapcheck # too difficult, will re-add later (another-rex) - wsl # disagree with, for now (g-rath) - wsl_v5 # disagree with, for now (g-rath) + - godoclint # too noisy at the moment + - gosec # temporary, until jess comes back from holiday settings: exhaustive: default-signifies-exhaustive: true diff --git a/vulnfeeds/cmd/combine-to-osv/Dockerfile b/vulnfeeds/cmd/combine-to-osv/Dockerfile index 11266e559e0..24621352119 100644 --- a/vulnfeeds/cmd/combine-to-osv/Dockerfile +++ b/vulnfeeds/cmd/combine-to-osv/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS GO_BUILD +FROM golang:1.27.0-alpine AS GO_BUILD RUN mkdir /src WORKDIR /src diff --git a/vulnfeeds/cmd/combine-to-osv/main.go b/vulnfeeds/cmd/combine-to-osv/main.go index 9f738d94cde..9bfd50f1c21 100644 --- a/vulnfeeds/cmd/combine-to-osv/main.go +++ b/vulnfeeds/cmd/combine-to-osv/main.go @@ -60,8 +60,8 @@ func cveIDFromPath(p string) models.CVEID { } func listObjects(ctx context.Context, client *storage.Client, pathStr string) ([]string, error) { - if strings.HasPrefix(pathStr, "gs://") { - trimmed := strings.TrimPrefix(pathStr, "gs://") + if after, ok := strings.CutPrefix(pathStr, "gs://"); ok { + trimmed := after bucketName, prefix, _ := strings.Cut(trimmed, "/") bucket := client.Bucket(bucketName) @@ -103,8 +103,8 @@ func listObjects(ctx context.Context, client *storage.Client, pathStr string) ([ } func readVulnerability(ctx context.Context, client *storage.Client, fullPath string) (*osvschema.Vulnerability, error) { - if strings.HasPrefix(fullPath, "gs://") { - trimmed := strings.TrimPrefix(fullPath, "gs://") + if after, ok := strings.CutPrefix(fullPath, "gs://"); ok { + trimmed := after bucketName, objName, _ := strings.Cut(trimmed, "/") rc, err := client.Bucket(bucketName).Object(objName).NewReader(ctx) if err != nil { @@ -157,25 +157,21 @@ func readAndCombineWorker(ctx context.Context, client *storage.Client, workChan var readVulnsWg sync.WaitGroup if work.CVE5Path != "" { - readVulnsWg.Add(1) - go func() { - defer readVulnsWg.Done() + readVulnsWg.Go(func() { cve5, cve5Err = readVulnerability(ctx, client, work.CVE5Path) if cve5Err != nil { logger.Error("Failed to read CVE5", slog.String("id", string(work.ID)), slog.Any("err", cve5Err)) } - }() + }) } if work.NVDPath != "" { - readVulnsWg.Add(1) - go func() { - defer readVulnsWg.Done() + readVulnsWg.Go(func() { nvd, nvdErr = readVulnerability(ctx, client, work.NVDPath) if nvdErr != nil { logger.Error("Failed to read NVD", slog.String("id", string(work.ID)), slog.Any("err", nvdErr)) } - }() + }) } readVulnsWg.Wait() @@ -299,11 +295,9 @@ func main() { var uploadVulnsWg sync.WaitGroup var successCount atomic.Uint64 for range *numWorkers { - uploadVulnsWg.Add(1) - go func() { - defer uploadVulnsWg.Done() + uploadVulnsWg.Go(func() { writer.VulnWorker(ctx, uploadVulnsChan, outBkt, overridesBkt, gcsHelper, *osvOutputPath, &successCount) - }() + }) } // Interpose Collector to gather valid IDs @@ -330,11 +324,9 @@ func main() { // Start ReadAndCombineWorkers (Read side) var readAndCombineWg sync.WaitGroup for range *numWorkers { - readAndCombineWg.Add(1) - go func() { - defer readAndCombineWg.Done() + readAndCombineWg.Go(func() { readAndCombineWorker(ctx, client, workChan, vulnChan) - }() + }) } // Feed Work diff --git a/vulnfeeds/cmd/converters/alpine/Dockerfile b/vulnfeeds/cmd/converters/alpine/Dockerfile index d00cea04b37..4b5f909f39f 100644 --- a/vulnfeeds/cmd/converters/alpine/Dockerfile +++ b/vulnfeeds/cmd/converters/alpine/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS GO_BUILD +FROM golang:1.27.0-alpine AS GO_BUILD RUN mkdir /src WORKDIR /src diff --git a/vulnfeeds/cmd/converters/alpine/main_test.go b/vulnfeeds/cmd/converters/alpine/main_test.go index 228174f0710..a6d7911dcc7 100644 --- a/vulnfeeds/cmd/converters/alpine/main_test.go +++ b/vulnfeeds/cmd/converters/alpine/main_test.go @@ -258,12 +258,12 @@ func TestGenerateAlpineOSV(t *testing.T) { { Criteria: xzCPE, Vulnerable: true, - VersionStartIncluding: newString("5.6.0"), + VersionStartIncluding: new("5.6.0"), }, { Criteria: fooCPE, Vulnerable: true, - VersionStartIncluding: newString("1.0.0"), + VersionStartIncluding: new("1.0.0"), }, }, }}, @@ -327,7 +327,3 @@ func TestGenerateAlpineOSV(t *testing.T) { t.Errorf("Expected fixed 5.6.1 for xz (v3.19), got %s", fixed) } } - -func newString(s string) *string { - return &s -} diff --git a/vulnfeeds/cmd/converters/cve/cve5/bulk-converter/Dockerfile b/vulnfeeds/cmd/converters/cve/cve5/bulk-converter/Dockerfile index 468311d44b0..cb57c7e799e 100644 --- a/vulnfeeds/cmd/converters/cve/cve5/bulk-converter/Dockerfile +++ b/vulnfeeds/cmd/converters/cve/cve5/bulk-converter/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS go_build +FROM golang:1.27.0-alpine AS go_build RUN mkdir /src WORKDIR /src diff --git a/vulnfeeds/cmd/converters/cve/cve5/bulk-converter/main.go b/vulnfeeds/cmd/converters/cve/cve5/bulk-converter/main.go index 47a96193274..94de4a9fe9e 100644 --- a/vulnfeeds/cmd/converters/cve/cve5/bulk-converter/main.go +++ b/vulnfeeds/cmd/converters/cve/cve5/bulk-converter/main.go @@ -102,7 +102,7 @@ func main() { if *cnaDenyList != "" { cnaList = strings.Split(*cnaDenyList, ",") } else { - for _, cna := range strings.Split(string(cnaDenylistData), "\n") { + for cna := range strings.SplitSeq(string(cnaDenylistData), "\n") { cna = strings.TrimSpace(cna) if cna != "" { cnaList = append(cnaList, cna) diff --git a/vulnfeeds/cmd/converters/cve/nvd-cve-osv/Dockerfile b/vulnfeeds/cmd/converters/cve/nvd-cve-osv/Dockerfile index aca210bfdaa..21927c890ed 100644 --- a/vulnfeeds/cmd/converters/cve/nvd-cve-osv/Dockerfile +++ b/vulnfeeds/cmd/converters/cve/nvd-cve-osv/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS GO_BUILD +FROM golang:1.27.0-alpine AS GO_BUILD WORKDIR /go/src diff --git a/vulnfeeds/cmd/converters/cve/nvd-cve-osv/main.go b/vulnfeeds/cmd/converters/cve/nvd-cve-osv/main.go index 74136e0cb8a..e3e7de3c831 100644 --- a/vulnfeeds/cmd/converters/cve/nvd-cve-osv/main.go +++ b/vulnfeeds/cmd/converters/cve/nvd-cve-osv/main.go @@ -226,7 +226,7 @@ func processCVE(cve models.NVDCVE, vpRepoCache *c.VPRepoCache, repoTagsCache git } var repos []string if nvd.IsLinuxKernelVulnerability(cve) { - metrics.AddNote("Linux kernel vulnerability detected, skipping repository processing") + metrics.AddNotef("Linux kernel vulnerability detected, skipping repository processing") } else { repos = nvd.FindRepos(cve, vpRepoCache, repoTagsCache, metrics, httpClient) } diff --git a/vulnfeeds/cmd/converters/debian/Dockerfile b/vulnfeeds/cmd/converters/debian/Dockerfile index 8809094e1c2..c217ac35229 100644 --- a/vulnfeeds/cmd/converters/debian/Dockerfile +++ b/vulnfeeds/cmd/converters/debian/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS GO_BUILD +FROM golang:1.27.0-alpine AS GO_BUILD RUN mkdir /src WORKDIR /src diff --git a/vulnfeeds/cmd/converters/dsa-dla-dtsa/Dockerfile b/vulnfeeds/cmd/converters/dsa-dla-dtsa/Dockerfile index c3194c8ea96..38e2a2b854f 100644 --- a/vulnfeeds/cmd/converters/dsa-dla-dtsa/Dockerfile +++ b/vulnfeeds/cmd/converters/dsa-dla-dtsa/Dockerfile @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS build +FROM golang:1.27.0-alpine AS build WORKDIR /src diff --git a/vulnfeeds/cmd/converters/dsa-dla-dtsa/main.go b/vulnfeeds/cmd/converters/dsa-dla-dtsa/main.go index 1aaa690a8a6..b7e7586db0d 100644 --- a/vulnfeeds/cmd/converters/dsa-dla-dtsa/main.go +++ b/vulnfeeds/cmd/converters/dsa-dla-dtsa/main.go @@ -150,8 +150,8 @@ func parseSecurityTrackerFile(advisories Advisories, securityTrackerRepo, securi line = strings.TrimLeft(line, " \t") if strings.HasPrefix(line, "{") { - upstreams := strings.Fields(strings.Trim(line, "{}")) - for _, u := range upstreams { + upstreams := strings.FieldsSeq(strings.Trim(line, "{}")) + for u := range upstreams { // This is not ideal, in the cases that there are missing // Debian Security Tracker CVEs, but it's better than not having them advisories[currentAdvisory].Upstream = append(advisories[currentAdvisory].Upstream, "DEBIAN-"+u) @@ -294,7 +294,7 @@ func parseWebwmlFiles(advisories Advisories, webwmlRepoPath, wmlFileSubPath stri // This is accounted for with the modified timestamp with git // below though, so we don't need to parse them here if len(reportDateMatches) > 0 { - reportDateStr := strings.Split(reportDateMatches[0][1], ",")[0] + reportDateStr, _, _ := strings.Cut(reportDateMatches[0][1], ",") parsedDate, err := time.Parse("2006-1-02", reportDateStr) if err == nil { advisory.Published = parsedDate diff --git a/vulnfeeds/cmd/mirrors/cpe-repo-gen/Dockerfile b/vulnfeeds/cmd/mirrors/cpe-repo-gen/Dockerfile index eb79f37a431..0393d489360 100644 --- a/vulnfeeds/cmd/mirrors/cpe-repo-gen/Dockerfile +++ b/vulnfeeds/cmd/mirrors/cpe-repo-gen/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS GO_BUILD +FROM golang:1.27.0-alpine AS GO_BUILD RUN mkdir /src WORKDIR /src diff --git a/vulnfeeds/cmd/mirrors/download-cves/Dockerfile b/vulnfeeds/cmd/mirrors/download-cves/Dockerfile index 768c966b9f5..6e9773f543b 100644 --- a/vulnfeeds/cmd/mirrors/download-cves/Dockerfile +++ b/vulnfeeds/cmd/mirrors/download-cves/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS GO_BUILD +FROM golang:1.27.0-alpine AS GO_BUILD RUN mkdir /src WORKDIR /src diff --git a/vulnfeeds/cmd/pypi/main_test.go b/vulnfeeds/cmd/pypi/main_test.go index 26d9be8a485..0ec5e3d91e7 100644 --- a/vulnfeeds/cmd/pypi/main_test.go +++ b/vulnfeeds/cmd/pypi/main_test.go @@ -98,10 +98,6 @@ affected: } } -func strPtr(s string) *string { - return &s -} - func TestGeneratePyPIAffected(t *testing.T) { cve := models.NVDCVE{ ID: "CVE-2022-29194", @@ -115,13 +111,13 @@ func TestGeneratePyPIAffected(t *testing.T) { { Vulnerable: true, Criteria: "cpe:2.3:a:google:tensorflow:*:*:*:*:*:*:*:*", - VersionEndExcluding: strPtr("2.6.4"), + VersionEndExcluding: new("2.6.4"), }, { Vulnerable: true, Criteria: "cpe:2.3:a:google:tensorflow:*:*:*:*:*:*:*:*", - VersionStartIncluding: strPtr("2.7.0"), - VersionEndExcluding: strPtr("2.7.2"), + VersionStartIncluding: new("2.7.0"), + VersionEndExcluding: new("2.7.2"), }, }, }, diff --git a/vulnfeeds/conversion/common.go b/vulnfeeds/conversion/common.go index 4077f41a701..ce60f936322 100644 --- a/vulnfeeds/conversion/common.go +++ b/vulnfeeds/conversion/common.go @@ -10,6 +10,7 @@ import ( "fmt" "io/fs" "log/slog" + "maps" "net/http" "os" "path" @@ -45,7 +46,7 @@ func AddAffected(v *vulns.Vulnerability, aff *osvschema.Affected, metrics *model for _, r := range aff.GetRanges() { rangeBytes, err := json.Marshal(r) if err != nil { - metrics.AddNote("Could not marshal range to check for duplicates, adding anyway: %+v", r) + metrics.AddNotef("Could not marshal range to check for duplicates, adding anyway: %+v", r) uniqueRanges = append(uniqueRanges, r) continue @@ -55,7 +56,7 @@ func AddAffected(v *vulns.Vulnerability, aff *osvschema.Affected, metrics *model uniqueRanges = append(uniqueRanges, r) allExistingRanges[rangeStr] = struct{}{} } else { - metrics.AddNote("Skipping duplicate range: %+v", r) + metrics.AddNotef("Skipping duplicate range: %+v", r) } } @@ -206,7 +207,7 @@ func GitVersionsToCommits(versionRanges []models.RangeWithMetadata, repos []stri repo, err := git.FindCanonicalLink(repo, httpClient, cache) if err != nil { - metrics.AddNote("Failed to find canonical link - %s %v", repo, err) + metrics.AddNotef("Failed to find canonical link - %s %v", repo, err) if git.IsRateLimit(err) { metrics.Outcome = models.Error return nil, nil, nil @@ -221,7 +222,7 @@ func GitVersionsToCommits(versionRanges []models.RangeWithMetadata, repos []stri metrics.Outcome = models.Error return nil, nil, nil } - metrics.AddNote("Failed to normalize tags - %s", repo) + metrics.AddNotef("Failed to normalize tags - %s", repo) continue } @@ -263,16 +264,16 @@ func GitVersionsToCommits(versionRanges []models.RangeWithMetadata, repos []stri } else { introducedCommit, err = git.VersionToCommit(introduced, normalizedTags) if err != nil { - metrics.AddNote("error resolving version to commit - %s - %s", introduced, err) + metrics.AddNotef("error resolving version to commit - %s - %s", introduced, err) } } fixedCommit, err := git.VersionToCommit(fixed, normalizedTags) if err != nil { - metrics.AddNote("error resolving version to commit - %s - %s", fixed, err) + metrics.AddNotef("error resolving version to commit - %s - %s", fixed, err) } lastAffectedCommit, err := git.VersionToCommit(lastAffected, normalizedTags) if err != nil { - metrics.AddNote("error resolving version to commit - %s - %s", lastAffected, err) + metrics.AddNotef("error resolving version to commit - %s - %s", lastAffected, err) } if fixedCommit != "" || lastAffectedCommit != "" { @@ -296,7 +297,7 @@ func GitVersionsToCommits(versionRanges []models.RangeWithMetadata, repos []stri } databaseSpecific, err := utility.NewStructpbFromMap(dbSpecificMap) if err != nil { - metrics.AddNote("failed to make database specific: %v", err) + metrics.AddNotef("failed to make database specific: %v", err) } else { newVR.DatabaseSpecific = databaseSpecific } @@ -431,9 +432,7 @@ func MergeDatabaseSpecificValues(val1, val2 any) (any, error) { case map[string]any: if v2, ok := val2.(map[string]any); ok { merged := make(map[string]any) - for k, v := range v1 { - merged[k] = v - } + maps.Copy(merged, v1) for k, v := range v2 { if existing, ok := merged[k]; ok { mergedVal, err := MergeDatabaseSpecificValues(existing, v) @@ -681,7 +680,7 @@ func FilterUnresolvedRanges(resolved []models.RangeWithMetadata, unresolved []mo filtered := make([]models.RangeWithMetadata, 0, len(unresolved)) for _, ur := range unresolved { - var eventStrings []string + eventStrings := make([]string, 0, len(ur.Range.GetEvents())) for _, e := range ur.Range.GetEvents() { eventStrings = append(eventStrings, fmt.Sprintf("%s|%s|%s", e.GetIntroduced(), e.GetFixed(), e.GetLastAffected())) } diff --git a/vulnfeeds/conversion/cve5/converter.go b/vulnfeeds/conversion/cve5/converter.go index d05662292b8..38a55c1bf0a 100644 --- a/vulnfeeds/conversion/cve5/converter.go +++ b/vulnfeeds/conversion/cve5/converter.go @@ -37,7 +37,7 @@ func extractConversionMetrics(cve models.CVE5, refs []*osvschema.Reference, metr } metrics.RefTypesCount = refTypeCounts for refType, count := range refTypeCounts { - metrics.AddNote("[%s]: Reference Type %s: %d", cve.Metadata.CVEID, refType, count) + metrics.AddNotef("[%s]: Reference Type %s: %d", cve.Metadata.CVEID, refType, count) } // TODO(jesslowe): Add more analysis based on ADP containers, CVSS, KEV, CWE, etc. @@ -63,7 +63,7 @@ func getCWEs(cna models.CNA, metrics *models.ConversionMetrics) []string { slices.Sort(cwes) cwes = slices.Compact(cwes) - metrics.AddNote("Extracted CWEIDs: %v", cwes) + metrics.AddNotef("Extracted CWEIDs: %v", cwes) return cwes } @@ -74,13 +74,13 @@ func getCWEs(cna models.CNA, metrics *models.ConversionMetrics) []string { func FromCVE5(cve models.CVE5, refs []models.Reference, metrics *models.ConversionMetrics, sourceLink string) *vulns.Vulnerability { published, err := models.ParseCVE5Timestamp(cve.Metadata.DatePublished) if err != nil { - metrics.AddNote("[%s]: Published date failed to parse, falling back to Epoch", cve.Metadata.CVEID) + metrics.AddNotef("[%s]: Published date failed to parse, falling back to Epoch", cve.Metadata.CVEID) published = time.Unix(0, 0).UTC() } modified, err := models.ParseCVE5Timestamp(cve.Metadata.DateUpdated) if err != nil { - metrics.AddNote("[%s]: Modified date failed to parse, falling back to Published time", cve.Metadata.CVEID) + metrics.AddNotef("[%s]: Modified date failed to parse, falling back to Published time", cve.Metadata.CVEID) modified = published } @@ -88,7 +88,7 @@ func FromCVE5(cve models.CVE5, refs []models.Reference, metrics *models.Conversi if cve.Metadata.State == "REJECTED" { withdrawn, err := models.ParseCVE5Timestamp(cve.Metadata.DateRejected) if err != nil { - metrics.AddNote("[%s]: Rejected date failed to parse or missing, falling back to Modified time", cve.Metadata.CVEID) + metrics.AddNotef("[%s]: Rejected date failed to parse or missing, falling back to Modified time", cve.Metadata.CVEID) withdrawn = modified } withdrawnTime = timestamppb.New(withdrawn) @@ -120,7 +120,7 @@ func FromCVE5(cve models.CVE5, refs []models.Reference, metrics *models.Conversi if len(dbSpecific) > 0 { databaseSpecific, err := utility.NewStructpbFromMap(dbSpecific) if err != nil { - metrics.AddNote("Failed to convert database specific: %v", err) + metrics.AddNotef("Failed to convert database specific: %v", err) } else { v.DatabaseSpecific = databaseSpecific } diff --git a/vulnfeeds/conversion/cve5/default_extractor.go b/vulnfeeds/conversion/cve5/default_extractor.go index 7a44fa6ca1a..5e01b097f96 100644 --- a/vulnfeeds/conversion/cve5/default_extractor.go +++ b/vulnfeeds/conversion/cve5/default_extractor.go @@ -49,7 +49,7 @@ func (d *DefaultVersionExtractor) ExtractVersions(cve models.CVE5, v *vulns.Vuln successfulRepos[s] = true } if len(r) == 0 { - metrics.AddNote("Failed to convert git versions to commits") + metrics.AddNotef("Failed to convert git versions to commits") return false } @@ -64,7 +64,7 @@ func (d *DefaultVersionExtractor) ExtractVersions(cve models.CVE5, v *vulns.Vuln } if !gotVersions { - metrics.AddNote("No versions in affected, attempting to extract from CPE") + metrics.AddNotef("No versions in affected, attempting to extract from CPE") versionRanges, _ := cpeVersionExtraction(cve, metrics) if len(versionRanges) != 0 { @@ -75,10 +75,10 @@ func (d *DefaultVersionExtractor) ExtractVersions(cve models.CVE5, v *vulns.Vuln } if !gotVersions { - metrics.AddNote("No versions in CPEs so attempting extraction from description") + metrics.AddNotef("No versions in CPEs so attempting extraction from description") textRanges := c.ExtractVersionsFromText(nil, models.EnglishDescription(cve.Containers.CNA.Descriptions), metrics, models.VersionSourceDescription) if len(textRanges) > 0 { - metrics.AddNote("Extracted versions from description: %v", textRanges) + metrics.AddNotef("Extracted versions from description: %v", textRanges) } if len(textRanges) != 0 { processRanges(textRanges) @@ -113,7 +113,7 @@ func (d *DefaultVersionExtractor) FindNormalAffectedRanges(affected models.Affec // In this case only vers.Version exists which either means that it is _only_ that version that is // affected, but more likely, it affects up to that version. It could also mean that the range is given // in one line instead - like "< 1.5.3" or "< 2.45.4, >= 2.0 " or just "before 1.4.7", so check for that. - metrics.AddNote("Only version exists") + metrics.AddNotef("Only version exists") av, err := git.ParseVersionRange(vers.Version) if err == nil { @@ -138,7 +138,7 @@ func (d *DefaultVersionExtractor) FindNormalAffectedRanges(affected models.Affec possibleVersions := c.ExtractVersionsFromText(nil, vers.Version, metrics, models.VersionSourceAffected) if possibleVersions != nil { - metrics.AddNote("Versions retrieved from text but not used CURRENTLY") + metrics.AddNotef("Versions retrieved from text but not used CURRENTLY") continue } @@ -147,10 +147,10 @@ func (d *DefaultVersionExtractor) FindNormalAffectedRanges(affected models.Affec var vr []*osvschema.Range if strings.EqualFold(metrics.CNA, "mitre") && len(affected.Versions) == 1 { vr = []*osvschema.Range{c.BuildVersionRange("", vers.Version, "")} - metrics.AddNote("Single version found %v for MITRE - Setting only last_affected", vers.Version) + metrics.AddNotef("Single version found %v for MITRE - Setting only last_affected", vers.Version) } else { vr = []*osvschema.Range{c.BuildVersionRange(vers.Version, vers.Version, "")} - metrics.AddNote("Single version found %v - Treating as standalone version", vers.Version) + metrics.AddNotef("Single version found %v - Treating as standalone version", vers.Version) } rwms := c.ToRangeWithMetadata(vr, models.VersionSourceAffected) for i := range rwms { diff --git a/vulnfeeds/conversion/cve5/linux_extractor.go b/vulnfeeds/conversion/cve5/linux_extractor.go index 3007e8ce650..82d3913ea10 100644 --- a/vulnfeeds/conversion/cve5/linux_extractor.go +++ b/vulnfeeds/conversion/cve5/linux_extractor.go @@ -61,13 +61,13 @@ func (l *LinuxVersionExtractor) ExtractVersions(cve models.CVE5, v *vulns.Vulner gotVersions := l.handleAffected(v, cve.Containers.CNA.Affected, metrics) if !gotVersions { - metrics.AddNote("No versions in affected, attempting to extract from CPE") + metrics.AddNotef("No versions in affected, attempting to extract from CPE") versionRanges, err := cpeVersionExtraction(cve, metrics) if err != nil { logger.Warn("Error when extracting CPE versions") } if len(versionRanges) != 0 { - var ranges []*osvschema.Range + ranges := make([]*osvschema.Range, 0, len(versionRanges)) for _, r := range versionRanges { ranges = append(ranges, r.Range) } @@ -115,7 +115,7 @@ func findInverseAffectedRanges(cveAff models.Affected, metrics *models.Conversio case 3: introduced = append(introduced, versionValue) default: - metrics.AddNote("Bad non-semver version given: %s", versionValue) + metrics.AddNotef("Bad non-semver version given: %s", versionValue) continue } } @@ -129,7 +129,7 @@ func findInverseAffectedRanges(cveAff models.Affected, metrics *models.Conversio fixed = append(fixed, versionValue) // Infer the next introduced version from the 'lessThanOrEqual' field. // For example, if "5.10.*" is unaffected, the next introduced version is "5.11.0". - minorVers := strings.Split(vers.LessThanOrEqual, ".*")[0] + minorVers, _, _ := strings.Cut(vers.LessThanOrEqual, ".*") parts := strings.Split(minorVers, ".") if len(parts) > 1 { if intMin, err := strconv.Atoi(parts[len(parts)-1]); err == nil { @@ -150,15 +150,15 @@ func findInverseAffectedRanges(cveAff models.Affected, metrics *models.Conversio for index, f := range fixed { if index < len(introduced) { ranges = append(ranges, c.BuildVersionRange(introduced[index], "", f)) - metrics.AddNote("Introduced from version value - %s", introduced[index]) - metrics.AddNote("Fixed from version value - %s", f) + metrics.AddNotef("Introduced from version value - %s", introduced[index]) + metrics.AddNotef("Fixed from version value - %s", f) } } if len(ranges) != 0 { return ranges, VersionRangeTypeSemver } - metrics.AddNote("no ranges found") + metrics.AddNotef("no ranges found") return nil, VersionRangeTypeUnknown } @@ -175,7 +175,7 @@ func (l *LinuxVersionExtractor) FindNormalAffectedRanges(affected models.Affecte // In this case only vers.Version exists which either means that it is _only_ that version that is // affected, but more likely, it affects up to that version. It could also mean that the range is given // in one line instead - like "< 1.5.3" or "< 2.45.4, >= 2.0 " or just "before 1.4.7", so check for that. - metrics.AddNote("Only version exists") + metrics.AddNotef("Only version exists") if currentVersionType == VersionRangeTypeGit { vr := []*osvschema.Range{c.BuildVersionRange(vers.Version, "", "")} @@ -188,7 +188,7 @@ func (l *LinuxVersionExtractor) FindNormalAffectedRanges(affected models.Affecte if vulns.CheckQuality(vers.Version).AtLeast(acceptableQuality) { vr := []*osvschema.Range{c.BuildVersionRange("0", vers.Version, "")} versionRanges = append(versionRanges, c.ToRangeWithMetadata(vr, models.VersionSourceAffected)...) - metrics.AddNote("Single version found %v - Assuming introduced = 0 and last affected = %v", vers.Version, vers.Version) + metrics.AddNotef("Single version found %v - Assuming introduced = 0 and last affected = %v", vers.Version, vers.Version) } } diff --git a/vulnfeeds/conversion/cve5/strategies.go b/vulnfeeds/conversion/cve5/strategies.go index 5a6de90fb6c..15714a9743a 100644 --- a/vulnfeeds/conversion/cve5/strategies.go +++ b/vulnfeeds/conversion/cve5/strategies.go @@ -15,7 +15,7 @@ func cpeVersionExtraction(cve models.CVE5, metrics *models.ConversionMetrics) ([ return cpeRanges, nil } else if err != nil { - metrics.AddNote("%s", err.Error()) + metrics.AddNotef("%s", err.Error()) } return nil, err @@ -35,7 +35,7 @@ func initialNormalExtraction(vers models.Versions, metrics *models.ConversionMet // Quality check the version strings to avoid using filler content. vQuality := vulns.CheckQuality(vers.Version) if !vQuality.AtLeast(acceptableQuality) { - metrics.AddNote("Version value for is filler or empty") + metrics.AddNotef("Version value for is filler or empty") } vLessThanQual := vulns.CheckQuality(vers.LessThan) vLTOEQual := vulns.CheckQuality(vers.LessThanOrEqual) @@ -44,26 +44,26 @@ func initialNormalExtraction(vers models.Versions, metrics *models.ConversionMet // Handle cases where 'lessThan' is mistakenly the same as 'version'. if vers.LessThan != "" && vers.LessThan == vers.Version { - metrics.AddNote("Warning: lessThan (%s) is the same as introduced (%s)\n", vers.LessThan, vers.Version) + metrics.AddNotef("Warning: lessThan (%s) is the same as introduced (%s)\n", vers.LessThan, vers.Version) hasRange = false } if vers.LessThanOrEqual != "" && vers.LessThanOrEqual == vers.Version { - metrics.AddNote("Warning: lessThanOrEqual (%s) is the same as introduced (%s)\n", vers.LessThanOrEqual, vers.Version) + metrics.AddNotef("Warning: lessThanOrEqual (%s) is the same as introduced (%s)\n", vers.LessThanOrEqual, vers.Version) hasRange = false } - metrics.AddNote("Range detected: %v", hasRange) + metrics.AddNotef("Range detected: %v", hasRange) if hasRange { if vQuality.AtLeast(acceptableQuality) { introduced = vers.Version - metrics.AddNote("%s - Introduced from version value - %s", vQuality.String(), vers.Version) + metrics.AddNotef("%s - Introduced from version value - %s", vQuality.String(), vers.Version) } if vLessThanQual.AtLeast(acceptableQuality) { fixed = vers.LessThan - metrics.AddNote("%s - Fixed from LessThan value - %s", vLessThanQual.String(), vers.LessThan) + metrics.AddNotef("%s - Fixed from LessThan value - %s", vLessThanQual.String(), vers.LessThan) } else if vLTOEQual.AtLeast(acceptableQuality) { lastaffected = vers.LessThanOrEqual - metrics.AddNote("%s - LastAffected from LessThanOrEqual value- %s", vLTOEQual.String(), vers.LessThanOrEqual) + metrics.AddNotef("%s - LastAffected from LessThanOrEqual value- %s", vLTOEQual.String(), vers.LessThanOrEqual) } var versionRanges []*osvschema.Range if fixed != "" { diff --git a/vulnfeeds/conversion/cve5/version_extraction_test.go b/vulnfeeds/conversion/cve5/version_extraction_test.go index 1a4d7b36efa..f8a43f41def 100644 --- a/vulnfeeds/conversion/cve5/version_extraction_test.go +++ b/vulnfeeds/conversion/cve5/version_extraction_test.go @@ -381,7 +381,7 @@ func TestGetVersionExtractor(t *testing.T) { AssignerShortName: "Linux", }, }, - expectedType: reflect.TypeOf(&LinuxVersionExtractor{}), + expectedType: reflect.TypeFor[*LinuxVersionExtractor](), }, { name: "Default CVE", @@ -390,12 +390,12 @@ func TestGetVersionExtractor(t *testing.T) { AssignerShortName: "Anything", }, }, - expectedType: reflect.TypeOf(&DefaultVersionExtractor{}), + expectedType: reflect.TypeFor[*DefaultVersionExtractor](), }, { name: "Empty provider", cve: models.CVE5{}, - expectedType: reflect.TypeOf(&DefaultVersionExtractor{}), + expectedType: reflect.TypeFor[*DefaultVersionExtractor](), }, } diff --git a/vulnfeeds/conversion/grouping.go b/vulnfeeds/conversion/grouping.go index 429c3ce2ec7..164fb7a007c 100644 --- a/vulnfeeds/conversion/grouping.go +++ b/vulnfeeds/conversion/grouping.go @@ -22,12 +22,12 @@ func GroupAffectedRanges(affected []*osvschema.Affected) { continue } - var rwms []models.RangeWithMetadata + rwms := make([]models.RangeWithMetadata, 0, len(aff.GetRanges())) for _, r := range aff.GetRanges() { rwms = append(rwms, models.RangeWithMetadata{Range: r}) } grouped := GroupRanges(rwms) - var out []*osvschema.Range + out := make([]*osvschema.Range, 0, len(grouped)) for _, rwm := range grouped { out = append(out, rwm.Range) } @@ -357,7 +357,7 @@ func MergeRangesAndCreateAffected( var err error mergedRange, err = MergeTwoRanges(mergedRange, vr) if err != nil { - metrics.AddNote("Failed to merge ranges: %v", err) + metrics.AddNotef("Failed to merge ranges: %v", err) } } } diff --git a/vulnfeeds/conversion/nvd/converter.go b/vulnfeeds/conversion/nvd/converter.go index 677dea97127..fc8982ec815 100644 --- a/vulnfeeds/conversion/nvd/converter.go +++ b/vulnfeeds/conversion/nvd/converter.go @@ -46,7 +46,7 @@ func CVEToOSV(cve models.NVDCVE, repos []string, vpRepoCache *c.VPRepoCache, cac if len(CPEs) > 0 { _, err := c.ParseCPE(CPEs[0]) // For naming the subdirectory used for output. if err != nil { - metrics.AddNote("Can't generate an OSV record without valid CPE data") + metrics.AddNotef("Can't generate an OSV record without valid CPE data") return nil, metrics, models.ConversionUnknown } } @@ -55,7 +55,7 @@ func CVEToOSV(cve models.NVDCVE, repos []string, vpRepoCache *c.VPRepoCache, cac v := vulns.FromNVDCVE(cve.ID, cve) databaseSpecific, err := utility.NewStructpbFromMap(make(map[string]any)) if err != nil { - metrics.AddNote("Failed to convert database specific: %v", err) + metrics.AddNotef("Failed to convert database specific: %v", err) } else { v.DatabaseSpecific = databaseSpecific } @@ -104,14 +104,14 @@ func CVEToOSV(cve models.NVDCVE, repos []string, vpRepoCache *c.VPRepoCache, cac // Extract Commits commits, err := c.ExtractCommitsFromRefs(refs, httpClient, cache) if err != nil { - metrics.AddNote("Failed to extract commits from refs: %v", err) + metrics.AddNotef("Failed to extract commits from refs: %v", err) if git.IsRateLimit(err) { metrics.SetOutcome(models.Error) return nil, metrics, models.Error } } if len(commits) > 0 { - metrics.AddNote("Extracted commits from refs: %v", commits) + metrics.AddNotef("Extracted commits from refs: %v", commits) for _, commit := range commits { successfulRepos[commit.Repo] = true } @@ -123,7 +123,7 @@ func CVEToOSV(cve models.NVDCVE, repos []string, vpRepoCache *c.VPRepoCache, cac if len(resolvedRanges) == 0 { textRanges := c.ExtractVersionsFromText(nil, models.EnglishDescription(cve.Descriptions), metrics, models.VersionSourceDescription) if len(textRanges) > 0 { - metrics.AddNote("Extracted versions from description: %v", textRanges) + metrics.AddNotef("Extracted versions from description: %v", textRanges) } r, un, sR := c.ProcessRanges(textRanges, repos, metrics, cache, httpClient) if metrics.Outcome == models.Error { @@ -137,7 +137,7 @@ func CVEToOSV(cve models.NVDCVE, repos []string, vpRepoCache *c.VPRepoCache, cac } if len(resolvedRanges) == 0 && len(commits) == 0 { - metrics.AddNote("No ranges detected") + metrics.AddNotef("No ranges detected") metrics.SetOutcome(models.NoRanges) } @@ -184,7 +184,7 @@ func FindRepos(cve models.NVDCVE, vpRepoCache *c.VPRepoCache, repoTagsCache git. var reposForCVE []string if len(refs) == 0 && len(CPEs) == 0 { - metrics.AddNote("Skipping due to lack of CPEs and lack of references") + metrics.AddNotef("Skipping due to lack of CPEs and lack of references") // 100% of these in 2022 were rejected CVEs metrics.SetOutcome(models.Rejected) @@ -194,17 +194,17 @@ func FindRepos(cve models.NVDCVE, vpRepoCache *c.VPRepoCache, repoTagsCache git. if len(refs) > 0 && len(CPEs) == 0 { repos := c.ReposFromReferences(nil, nil, refs, c.RefTagDenyList, repoTagsCache, metrics, httpClient) if len(repos) == 0 { - metrics.AddNote("Failed to derive any repos and there were no CPEs") + metrics.AddNotef("Failed to derive any repos and there were no CPEs") return nil } - metrics.AddNote("Derived repos for CVE with no CPEs: %v", repos) + metrics.AddNotef("Derived repos for CVE with no CPEs: %v", repos) reposForCVE = repos } vendorProductCombinations := make(map[c.VendorProduct]bool) for _, CPEstr := range CPEs { CPE, err := c.ParseCPE(CPEstr) if err != nil { - metrics.AddNote("Failed to parse CPE: %v", CPEstr) + metrics.AddNotef("Failed to parse CPE: %v", CPEstr) continue } if CPE.Part != "a" { // only care about application CPEs @@ -216,7 +216,7 @@ func FindRepos(cve models.NVDCVE, vpRepoCache *c.VPRepoCache, repoTagsCache git. // If there wasn't a repo from the CPE Dictionary, try and derive one from the CVE references. for vendorProductKey := range vendorProductCombinations { if repos, ok := vpRepoCache.Get(vendorProductKey); ok { - metrics.AddNote("Pre-references, derived repos using cache: %v", repos) + metrics.AddNotef("Pre-references, derived repos using cache: %v", repos) if len(reposForCVE) == 0 { reposForCVE = repos continue @@ -233,10 +233,10 @@ func FindRepos(cve models.NVDCVE, vpRepoCache *c.VPRepoCache, repoTagsCache git. } repos := c.ReposFromReferences(vpRepoCache, &vendorProductKey, refs, c.RefTagDenyList, repoTagsCache, metrics, httpClient) if len(repos) == 0 { - metrics.AddNote("Failed to derive any repos for %s/%s", vendorProductKey.Vendor, vendorProductKey.Product) + metrics.AddNotef("Failed to derive any repos for %s/%s", vendorProductKey.Vendor, vendorProductKey.Product) continue } - metrics.AddNote("Derived repos: %v", repos) + metrics.AddNotef("Derived repos: %v", repos) reposForCVE = append(reposForCVE, repos...) } } @@ -244,7 +244,7 @@ func FindRepos(cve models.NVDCVE, vpRepoCache *c.VPRepoCache, repoTagsCache git. filteredRepos := make([]string, 0, len(reposForCVE)) for _, repo := range reposForCVE { if IsLinuxKernelURL(repo) { - metrics.AddNote("Disregarding Linux kernel repository: %s", repo) + metrics.AddNotef("Disregarding Linux kernel repository: %s", repo) continue } filteredRepos = append(filteredRepos, repo) @@ -253,12 +253,12 @@ func FindRepos(cve models.NVDCVE, vpRepoCache *c.VPRepoCache, repoTagsCache git. if len(reposForCVE) == 0 { // We have nothing useful to work with, so we'll assume it's out of scope - metrics.AddNote("Passing due to lack of viable repository") + metrics.AddNotef("Passing due to lack of viable repository") return nil } - metrics.AddNote("Found Repos for CVE %s: %v", string(CVEID), reposForCVE) + metrics.AddNotef("Found Repos for CVE %s: %v", string(CVEID), reposForCVE) return reposForCVE } diff --git a/vulnfeeds/conversion/nvd/converter_test.go b/vulnfeeds/conversion/nvd/converter_test.go index 4f0a4576d18..4ece292d7ea 100644 --- a/vulnfeeds/conversion/nvd/converter_test.go +++ b/vulnfeeds/conversion/nvd/converter_test.go @@ -189,7 +189,7 @@ func TestCVEToOSV_TestJsonSnapshots(t *testing.T) { gitCache.SetCanonicalLink(repo, repo) tagMap := make(map[string]git.Tag) normMap := make(map[string]git.NormalizedTag) - var keys []string + keys := make([]string, 0, len(tagCommits)) for k := range tagCommits { keys = append(keys, k) } diff --git a/vulnfeeds/conversion/versions.go b/vulnfeeds/conversion/versions.go index 70a973309da..9b871e23c55 100644 --- a/vulnfeeds/conversion/versions.go +++ b/vulnfeeds/conversion/versions.go @@ -539,7 +539,7 @@ func resolveGitTag(parsedURL *url.URL, u string, gitSHA1Regex *regexp.Regexp, ht // For URLs referencing commits in supported Git repository hosts, return a cloneable AffectedCommit. func ExtractCommitsFromRefs(references []models.Reference, httpClient *http.Client, cache git.RepoTagsCache) ([]models.AffectedCommit, error) { - var commits []models.AffectedCommit //nolint:prealloc + var commits []models.AffectedCommit for _, ref := range references { // (Potentially faulty) Assumption: All viable Git commit reference links are fix commits. @@ -658,7 +658,7 @@ func ExtractVersionsFromText(validVersions []string, text string, metrics *model pattern := regexp.MustCompile(`(?i)([\w.+\-]+)?\s+(through|before)\s+(?:version\s+)?([\w.+\-]+)`) matches := pattern.FindAllStringSubmatch(text, -1) if matches == nil { - metrics.AddNote("Failed to parse versions from text") + metrics.AddNotef("Failed to parse versions from text") return nil } @@ -674,26 +674,26 @@ func ExtractVersionsFromText(validVersions []string, text string, metrics *model var err error fixed, err = nextVersion(validVersions, fixed) if err != nil { - metrics.AddNote("Failed to determine next version after %s: %s", fixed, err.Error()) + metrics.AddNotef("Failed to determine next version after %s: %s", fixed, err.Error()) // if that inference failed, we know this version was definitely still vulnerable. lastaffected = cleanVersion(match[3]) - metrics.AddNote("Using %s as last_affected version instead", cleanVersion(match[3])) + metrics.AddNotef("Using %s as last_affected version instead", cleanVersion(match[3])) } } if introduced == "" && fixed == "" && lastaffected == "" { - metrics.AddNote("Failed to match version range from text") + metrics.AddNotef("Failed to match version range from text") continue } if introduced != "" && !HasVersion(validVersions, introduced) { - metrics.AddNote("Extracted introduced version %s is not a valid version", introduced) + metrics.AddNotef("Extracted introduced version %s is not a valid version", introduced) } if fixed != "" && !HasVersion(validVersions, fixed) { - metrics.AddNote("Extracted fixed version %s is not a valid version", fixed) + metrics.AddNotef("Extracted fixed version %s is not a valid version", fixed) } if lastaffected != "" && !HasVersion(validVersions, lastaffected) { - metrics.AddNote("Extracted last_affected version %s is not a valid version", lastaffected) + metrics.AddNotef("Extracted last_affected version %s is not a valid version", lastaffected) } // Favour fixed over last_affected for schema compliance. if fixed != "" && lastaffected != "" { @@ -758,7 +758,7 @@ func ExtractVersionsFromCPEs(cve models.NVDCVE, validVersions []string, vpRepoCa var err error introduced, err = nextVersion(validVersions, cleanVersion(*match.VersionStartExcluding)) if err != nil { - metrics.AddNote("%v", err.Error()) + metrics.AddNotef("%v", err.Error()) } } @@ -769,10 +769,10 @@ func ExtractVersionsFromCPEs(cve models.NVDCVE, validVersions []string, vpRepoCa // Infer the fixed version from the next version after. fixed, err = nextVersion(validVersions, cleanVersion(*match.VersionEndIncluding)) if err != nil { - metrics.AddNote("%v", err.Error()) + metrics.AddNotef("%v", err.Error()) // if that inference failed, we know this version was definitely still vulnerable. lastaffected = cleanVersion(*match.VersionEndIncluding) - metrics.AddNote("Using %s as last_affected version instead", cleanVersion(*match.VersionEndIncluding)) + metrics.AddNotef("Using %s as last_affected version instead", cleanVersion(*match.VersionEndIncluding)) } } CPE, err := ParseCPE(match.Criteria) @@ -811,7 +811,7 @@ func ExtractVersionsFromCPEs(cve models.NVDCVE, validVersions []string, vpRepoCa } if introduced != "" && !HasVersion(validVersions, introduced) { - metrics.AddNote("Warning: %s is not a valid introduced version", introduced) + metrics.AddNotef("Warning: %s is not a valid introduced version", introduced) } if introduced == "" { @@ -819,7 +819,7 @@ func ExtractVersionsFromCPEs(cve models.NVDCVE, validVersions []string, vpRepoCa } if fixed != "" && !HasVersion(validVersions, fixed) { - metrics.AddNote("Warning: %s is not a valid fixed version", fixed) + metrics.AddNotef("Warning: %s is not a valid fixed version", fixed) } // Get the repositories attached to this CPE @@ -864,7 +864,7 @@ func ExtractVersionsFromCPEs(cve models.NVDCVE, validVersions []string, vpRepoCa if len(versions) == 0 { return nil } - metrics.AddNote("Extracted versions from CPEs: %v", versions) + metrics.AddNotef("Extracted versions from CPEs: %v", versions) return versions } @@ -893,7 +893,7 @@ func ExtractVersionInfo(cve models.NVDCVE, validVersions []string, metrics *mode var err error introduced, err = nextVersion(validVersions, cleanVersion(*match.VersionStartExcluding)) if err != nil { - metrics.AddNote("%v", err.Error()) + metrics.AddNotef("%v", err.Error()) } } @@ -904,10 +904,10 @@ func ExtractVersionInfo(cve models.NVDCVE, validVersions []string, metrics *mode // Infer the fixed version from the next version after. fixed, err = nextVersion(validVersions, cleanVersion(*match.VersionEndIncluding)) if err != nil { - metrics.AddNote("%v", err.Error()) + metrics.AddNotef("%v", err.Error()) // if that inference failed, we know this version was definitely still vulnerable. lastaffected = cleanVersion(*match.VersionEndIncluding) - metrics.AddNote("Using %s as last_affected version instead", cleanVersion(*match.VersionEndIncluding)) + metrics.AddNotef("Using %s as last_affected version instead", cleanVersion(*match.VersionEndIncluding)) } } @@ -937,11 +937,11 @@ func ExtractVersionInfo(cve models.NVDCVE, validVersions []string, metrics *mode } if introduced != "" && !HasVersion(validVersions, introduced) { - metrics.AddNote("Warning: %s is not a valid introduced version", introduced) + metrics.AddNotef("Warning: %s is not a valid introduced version", introduced) } if fixed != "" && !HasVersion(validVersions, fixed) { - metrics.AddNote("Warning: %s is not a valid fixed version", fixed) + metrics.AddNotef("Warning: %s is not a valid fixed version", fixed) } // gotVersions = true @@ -960,14 +960,14 @@ func ExtractVersionInfo(cve models.NVDCVE, validVersions []string, metrics *mode } if len(v.AffectedVersions) == 0 { - metrics.AddNote("No versions detected.") + metrics.AddNotef("No versions detected.") } // Valid versions should only be output if there are errors generating the record if len(metrics.Notes) > 0 && len(validVersions) > 0 { - metrics.AddNote("Valid versions:") + metrics.AddNotef("Valid versions:") for _, version := range validVersions { - metrics.AddNote(" - %v", version) + metrics.AddNotef(" - %v", version) } } @@ -1134,19 +1134,19 @@ func VersionInfoToCommits(v *models.VersionInfo, repos []string, cache git.RepoT metrics.Outcome = models.Error return } - metrics.AddNote("Failed to normalize tags %s %s", repo, err) + metrics.AddNotef("Failed to normalize tags %s %s", repo, err) continue } for _, av := range v.AffectedVersions { - metrics.AddNote("Attempting version resolution for %s in %s", av, repo) + metrics.AddNotef("Attempting version resolution for %s in %s", av, repo) introducedEquivalentCommit := "" if av.Introduced != "" && av.Introduced != "0" { ac, err := git.VersionToAffectedCommit(av.Introduced, repo, models.Introduced, normalizedTags) if err != nil { - metrics.AddNote("Failed to get a Git commit for introduced version %s %s", repo, av.Introduced) + metrics.AddNotef("Failed to get a Git commit for introduced version %s %s", repo, av.Introduced) } else { - metrics.AddNote("Successfully derived commit %s for introduced version %s", ac, av.Introduced) + metrics.AddNotef("Successfully derived commit %s for introduced version %s", ac, av.Introduced) introducedEquivalentCommit = ac.Introduced } } @@ -1160,13 +1160,13 @@ func VersionInfoToCommits(v *models.VersionInfo, repos []string, cache git.RepoT fixedEquivalentCommit := "" if v.HasFixedCommits(repo) && av.Fixed != "" && len(v.AffectedVersions) == 1 { fixedEquivalentCommit = v.FixedCommits(repo)[0] - metrics.AddNote("Using preassumed fixed commits instead of deriving from fixed version %s", av.Fixed) + metrics.AddNotef("Using preassumed fixed commits instead of deriving from fixed version %s", av.Fixed) } else if av.Fixed != "" { ac, err := git.VersionToAffectedCommit(av.Fixed, repo, models.Fixed, normalizedTags) if err != nil { - metrics.AddNote("Failed to get a Git commit for fixed version %s %s", repo, av.Fixed) + metrics.AddNotef("Failed to get a Git commit for fixed version %s %s", repo, av.Fixed) } else { - metrics.AddNote("Successfully derived commit %s for fixed version %s", ac, av.Fixed) + metrics.AddNotef("Successfully derived commit %s for fixed version %s", ac, av.Fixed) fixedEquivalentCommit = ac.Fixed } } @@ -1177,9 +1177,9 @@ func VersionInfoToCommits(v *models.VersionInfo, repos []string, cache git.RepoT if !v.HasFixedCommits(repo) && av.LastAffected != "" { ac, err := git.VersionToAffectedCommit(av.LastAffected, repo, models.LastAffected, normalizedTags) if err != nil { - metrics.AddNote("Failed to get a Git commit for last_affected version %s %s", repo, av.LastAffected) + metrics.AddNotef("Failed to get a Git commit for last_affected version %s %s", repo, av.LastAffected) } else { - metrics.AddNote("Successfully derived commit %s for last_affected version %s", ac, av.LastAffected) + metrics.AddNotef("Successfully derived commit %s for last_affected version %s", ac, av.LastAffected) lastAffectedEquivalentCommit = ac.LastAffected } } @@ -1198,15 +1198,15 @@ func VersionInfoToCommits(v *models.VersionInfo, repos []string, cache git.RepoT } if ac == (models.AffectedCommit{}) { // Nothing resolved, move on to the next AffectedVersion - metrics.AddNote("Sufficient resolution not possible for %s %s", repo, av) + metrics.AddNotef("Sufficient resolution not possible for %s %s", repo, av) continue } if ac.InvalidRange() { - metrics.AddNote("Invalid range for %s %s", repo, ac) + metrics.AddNotef("Invalid range for %s %s", repo, ac) continue } if v.Duplicated(ac) { - metrics.AddNote("Duplicate commit for %s %s", repo, ac) + metrics.AddNotef("Duplicate commit for %s %s", repo, ac) continue } v.AffectedCommits = append(v.AffectedCommits, ac) @@ -1221,7 +1221,7 @@ func ReposFromReferences(cache *VPRepoCache, vp *VendorProduct, refs []models.Re // If any of the denylist tags are in the ref's tag set, it's out of consideration. if !RefAcceptable(ref, tagDenyList) { cache.MaybeRemove(vp, ref.URL) - metrics.AddNote("Disregarding %q due to a denied tag in %q", ref.URL, ref.Tags) + metrics.AddNotef("Disregarding %q due to a denied tag in %q", ref.URL, ref.Tags) continue } @@ -1271,7 +1271,7 @@ func ReposFromReferencesCVEList(refs []models.Reference, tagDenyList []string, m for _, ref := range refs { // If any of the denylist tags are in the ref's tag set, it's out of consideration. if !RefAcceptable(ref, tagDenyList) { - metrics.AddNote("Disregarding %q due to a denied tag in %q", ref.URL, ref.Tags) + metrics.AddNotef("Disregarding %q due to a denied tag in %q", ref.URL, ref.Tags) continue } // if it ends with .md it is likely a researcher repo and _currently_ useless. @@ -1291,9 +1291,9 @@ func ReposFromReferencesCVEList(refs []models.Reference, tagDenyList []string, m repos = append(repos, repo) } if len(repos) == 0 { - metrics.AddNote("Failed to identify any repos using references") + metrics.AddNotef("Failed to identify any repos using references") } else { - metrics.AddNote("Derived %q (no CPEs) using references", repos) + metrics.AddNotef("Derived %q (no CPEs) using references", repos) } return repos diff --git a/vulnfeeds/conversion/writer/writer.go b/vulnfeeds/conversion/writer/writer.go index 323a948935d..517b9b96643 100644 --- a/vulnfeeds/conversion/writer/writer.go +++ b/vulnfeeds/conversion/writer/writer.go @@ -287,11 +287,9 @@ func UploadVulnsToGCS( vulnChan := make(chan *osvschema.Vulnerability, numWorkers) for range numWorkers { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { VulnWorker(ctx, vulnChan, outBkt, overridesBkt, gcsHelper, osvOutputPath, &successCount) - }() + }) } for _, v := range vulnerabilities { diff --git a/vulnfeeds/gcs-tools/gcs_test.go b/vulnfeeds/gcs-tools/gcs_test.go index 65d0514ad77..524f12bb857 100644 --- a/vulnfeeds/gcs-tools/gcs_test.go +++ b/vulnfeeds/gcs-tools/gcs_test.go @@ -80,18 +80,14 @@ func TestDownloadBucket(t *testing.T) { t.Run("success", func(t *testing.T) { objects := []fakestorage.Object{ { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "folder/file1.txt", - }, - Content: []byte("content 1"), + BucketName: "test-bucket", + Name: "folder/file1.txt", + Content: []byte("content 1"), }, { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "folder/file2.txt", - }, - Content: []byte("content 2"), + BucketName: "test-bucket", + Name: "folder/file2.txt", + Content: []byte("content 2"), }, { ObjectAttrs: fakestorage.ObjectAttrs{ @@ -101,11 +97,9 @@ func TestDownloadBucket(t *testing.T) { Content: []byte(""), }, { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "other-folder/file3.txt", - }, - Content: []byte("content 3"), + BucketName: "test-bucket", + Name: "other-folder/file3.txt", + Content: []byte("content 3"), }, } @@ -149,11 +143,9 @@ func TestDownloadBucket(t *testing.T) { t.Run("path traversal", func(t *testing.T) { objects := []fakestorage.Object{ { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "../malicious.txt", - }, - Content: []byte("malicious content"), + BucketName: "test-bucket", + Name: "../malicious.txt", + Content: []byte("malicious content"), }, } @@ -177,11 +169,9 @@ func TestDownloadBucket(t *testing.T) { t.Run("relative dest dir", func(t *testing.T) { objects := []fakestorage.Object{ { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "file.txt", - }, - Content: []byte("content"), + BucketName: "test-bucket", + Name: "file.txt", + Content: []byte("content"), }, } @@ -213,28 +203,20 @@ func TestDownloadBucket(t *testing.T) { func TestListObjectsFast(t *testing.T) { objects := []fakestorage.Object{ { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "cve5/CVE-1999-0001.json", - }, + BucketName: "test-bucket", + Name: "cve5/CVE-1999-0001.json", }, { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "cve5/CVE-2017-9999.json", - }, + BucketName: "test-bucket", + Name: "cve5/CVE-2017-9999.json", }, { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "cve5/CVE-2018-0001.json", - }, + BucketName: "test-bucket", + Name: "cve5/CVE-2018-0001.json", }, { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "cve5/CVE-2018-9999.json", - }, + BucketName: "test-bucket", + Name: "cve5/CVE-2018-9999.json", }, { ObjectAttrs: fakestorage.ObjectAttrs{ @@ -243,22 +225,16 @@ func TestListObjectsFast(t *testing.T) { }, }, { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "cve5/CVE-2019-0001.json", - }, + BucketName: "test-bucket", + Name: "cve5/CVE-2019-0001.json", }, { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "cve5/CVE-2020-0001.json", - }, + BucketName: "test-bucket", + Name: "cve5/CVE-2020-0001.json", }, { - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-bucket", - Name: "cve5/non-cve.json", - }, + BucketName: "test-bucket", + Name: "cve5/non-cve.json", }, { ObjectAttrs: fakestorage.ObjectAttrs{ diff --git a/vulnfeeds/git/repository.go b/vulnfeeds/git/repository.go index 494f36a456c..4788c8b90aa 100644 --- a/vulnfeeds/git/repository.go +++ b/vulnfeeds/git/repository.go @@ -62,8 +62,7 @@ func IsRateLimit(err error) bool { return true } - var gitterErr *GitterError - if errors.As(err, &gitterErr) { + if gitterErr, ok := errors.AsType[*GitterError](err); ok { if gitterErr.StatusCode == http.StatusTooManyRequests || (gitterErr.StatusCode >= 500 && gitterErr.StatusCode < 600) { return true } diff --git a/vulnfeeds/go.mod b/vulnfeeds/go.mod index 7a9a5078bc5..ba865043057 100644 --- a/vulnfeeds/go.mod +++ b/vulnfeeds/go.mod @@ -1,6 +1,6 @@ module github.com/google/osv.dev/vulnfeeds -go 1.26.5 +go 1.27.0 require ( charm.land/lipgloss/v2 v2.0.6 diff --git a/vulnfeeds/models/cve.go b/vulnfeeds/models/cve.go index cb5635eecd5..0db16d72358 100644 --- a/vulnfeeds/models/cve.go +++ b/vulnfeeds/models/cve.go @@ -85,14 +85,14 @@ type CVSS struct { type Metrics struct { Format string `json:"format,omitempty"` Scenarios []LangString `json:"scenarios,omitempty"` - CVSSv4_0 BaseCVSS `json:"cvssV4_0,omitempty"` - CVSSv3_1 BaseCVSS `json:"cvssV3_1,omitempty"` - CVSSv3_0 BaseCVSS `json:"cvssV3_0,omitempty"` - CVSSv2_0 BaseCVSS `json:"cvssV2_0,omitempty"` + CVSSv4_0 BaseCVSS `json:"cvssV4_0"` + CVSSv3_1 BaseCVSS `json:"cvssV3_1"` + CVSSv3_0 BaseCVSS `json:"cvssV3_0"` + CVSSv2_0 BaseCVSS `json:"cvssV2_0"` Other struct { Type string `json:"type,omitempty"` Content any `json:"content,omitempty"` - } `json:"other,omitempty"` + } `json:"other"` } type CNA struct { @@ -112,7 +112,7 @@ type CNA struct { type ADP struct { Title string `json:"title,omitempty"` - ProviderMetadata ProviderMetadata `json:"providerMetadata,omitempty"` + ProviderMetadata ProviderMetadata `json:"providerMetadata"` ProblemTypes ProblemTypes `json:"problemTypes,omitempty"` Metrics []Metrics `json:"metrics,omitempty"` References []Reference `json:"references,omitempty"` @@ -138,11 +138,11 @@ type Versions struct { type CVE5 struct { DataType string `json:"dataType,omitempty"` DataVersion string `json:"dataVersion,omitempty"` - Metadata CVE5Metadata `json:"cveMetadata,omitempty"` + Metadata CVE5Metadata `json:"cveMetadata"` Containers struct { CNA CNA `json:"cna"` ADP []CNA `json:"adp,omitempty"` - } `json:"containers,omitempty"` + } `json:"containers"` } func EnglishDescription(descriptions []LangString) string { diff --git a/vulnfeeds/models/metrics.go b/vulnfeeds/models/metrics.go index 0befba41e80..773ea6a99fd 100644 --- a/vulnfeeds/models/metrics.go +++ b/vulnfeeds/models/metrics.go @@ -9,7 +9,6 @@ import ( "github.com/ossf/osv-schema/bindings/go/osvschema" ) -//nolint:recvcheck type ConversionOutcome int const ( @@ -93,8 +92,8 @@ type ConversionMetrics struct { ResolvedRangesCount int `json:"resolved_ranges_count"` } -// AddNote adds a formatted note to the ConversionMetrics. -func (m *ConversionMetrics) AddNote(format string, a ...any) { +// AddNotef adds a formatted note to the ConversionMetrics. +func (m *ConversionMetrics) AddNotef(format string, a ...any) { m.Notes = append(m.Notes, fmt.Sprintf(format, a...)) logger.Debug(fmt.Sprintf(format, a...), slog.String("cna", m.CNA), slog.String("cve", string(m.CVEID))) } diff --git a/vulnfeeds/pypi/pypi.go b/vulnfeeds/pypi/pypi.go index 9e8e8376812..d8a13ef6ec3 100644 --- a/vulnfeeds/pypi/pypi.go +++ b/vulnfeeds/pypi/pypi.go @@ -351,9 +351,9 @@ func (p *PyPI) packageExists(pkg string) bool { func (p *PyPI) finalPkgCheck(cve models.NVDCVE, pkg string, falsePositives *triage.FalsePositives) bool { // To avoid false positives, check that the pkg name is mentioned in the description. desc := strings.ToLower(models.EnglishDescription(cve.Descriptions)) - pkgNameParts := strings.Split(pkg, "-") + pkgNameParts := strings.SplitSeq(pkg, "-") - for _, part := range pkgNameParts { + for part := range pkgNameParts { // Python packages can commonly be py or -py. // Remove this to be a bit more lenient when matching against the description. part = strings.TrimPrefix(part, "py") diff --git a/vulnfeeds/vulns/vulns.go b/vulnfeeds/vulns/vulns.go index 8473da6007e..0ba855568d5 100644 --- a/vulnfeeds/vulns/vulns.go +++ b/vulnfeeds/vulns/vulns.go @@ -215,7 +215,7 @@ type PackageInfo struct { PkgName string `json:"pkg_name,omitempty" yaml:"pkg_name,omitempty"` Ecosystem string `json:"ecosystem,omitempty" yaml:"ecosystem,omitempty"` PURL string `json:"purl,omitempty" yaml:"purl,omitempty"` - VersionInfo models.VersionInfo `json:"fixed_version,omitempty" yaml:"fixed_version,omitempty"` + VersionInfo models.VersionInfo `json:"fixed_version" yaml:"fixed_version"` EcosystemSpecific map[string]any `json:"ecosystem_specific,omitempty" yaml:"ecosystem_specific,omitempty"` } @@ -784,7 +784,7 @@ func GetCPEs(cpeApplicability []models.CPE, metrics *models.ConversionMetrics) [ for _, c := range cpeApplicability { for _, node := range c.Nodes { if node.Operator != "OR" { - metrics.AddNote("Node found without OR operator") + metrics.AddNotef("Node found without OR operator") continue } for _, match := range node.CPEMatch {