blob: bf93a8f5e93a06cd8c9668cac73d027cd8083d3d [file] [log] [blame]
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package worker
import (
"context"
"fmt"
"net/http"
"strings"
"cloud.google.com/go/storage"
"golang.org/x/pkgsite-metrics/internal/derrors"
"golang.org/x/pkgsite-metrics/internal/log"
"golang.org/x/pkgsite-metrics/internal/queue"
"golang.org/x/pkgsite-metrics/internal/scan"
"google.golang.org/api/iterator"
)
// handleEnqueue enqueues multiple modules for a single vulncheck mode.
// Query params:
// - suffix: appended to task queue IDs to generate unique tasks
// - mode: type of analysis to run; see [modes]
// - file: path to file containing modules; if missing, use DB
// - min: minimum import-by count for a module to be included
func (h *VulncheckServer) handleEnqueue(w http.ResponseWriter, r *http.Request) error {
ctx := r.Context()
suffix := r.FormValue("suffix")
mode, err := vulncheckMode(scan.ParseMode(r))
if err != nil {
return err
}
var sreqs []*scan.Request
if mode == ModeBinary {
var err error
sreqs, err = readBinaries(ctx, h.cfg.BinaryBucket)
if err != nil {
return err
}
} else {
minImpCount, err := scan.ParseOptionalIntParam(r, "min", defaultMinImportedByCount)
if err != nil {
return err
}
modspecs, err := readModules(ctx, h.cfg, r.FormValue("file"), minImpCount)
if err != nil {
return err
}
sreqs = moduleSpecsToScanRequests(modspecs, mode)
}
return enqueueModules(ctx, sreqs, h.queue, &queue.Options{Namespace: "vulncheck", TaskNameSuffix: suffix})
}
func vulncheckMode(mode string) (string, error) {
if mode == "" {
// VTA is the default mode
return ModeVTA, nil
}
mode = strings.ToUpper(mode)
if _, ok := modes[mode]; !ok {
return "", fmt.Errorf("unsupported mode: %v", mode)
}
return mode, nil
}
// handleEnqueueAll enqueues multiple modules for all vulncheck modes.
// Query params:
// - suffix: appended to task queue IDs to generate unique tasks
// - file: path to file containing modules; if missing, use DB
// - min: minimum import-by count for a module to be included
func (h *VulncheckServer) handleEnqueueAll(w http.ResponseWriter, r *http.Request) error {
ctx := r.Context()
suffix := r.FormValue("suffix")
minImpCount, err := scan.ParseOptionalIntParam(r, "min", defaultMinImportedByCount)
if err != nil {
return err
}
modspecs, err := readModules(ctx, h.cfg, r.FormValue("file"), minImpCount)
if err != nil {
return err
}
opts := &queue.Options{Namespace: "vulncheck", TaskNameSuffix: suffix}
for mode := range modes {
var sreqs []*scan.Request
if mode == ModeBinary {
sreqs, err = readBinaries(ctx, h.cfg.BinaryBucket)
if err != nil {
return err
}
} else {
sreqs = moduleSpecsToScanRequests(modspecs, mode)
}
if err := enqueueModules(ctx, sreqs, h.queue, opts); err != nil {
return err
}
}
return nil
}
// binaryDir is the directory in the GCS bucket that contains binaries that should be scanned.
const binaryDir = "binaries"
func readBinaries(ctx context.Context, bucketName string) (sreqs []*scan.Request, err error) {
defer derrors.Wrap(&err, "readBinaries(%q)", bucketName)
if bucketName == "" {
log.Infof(ctx, "binary bucket not configured; not enqueuing binaries")
return nil, nil
}
c, err := storage.NewClient(ctx)
if err != nil {
return nil, err
}
iter := c.Bucket(bucketName).Objects(ctx, &storage.Query{Prefix: binaryDir})
for {
attrs, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, err
}
mod, vers, suff, err := scan.ParseModuleVersionSuffix(strings.TrimPrefix(attrs.Name, binaryDir+"/"))
if err != nil {
return nil, err
}
sreqs = append(sreqs, &scan.Request{
Module: mod,
Version: vers,
Suffix: suff,
Mode: ModeBinary,
})
}
return sreqs, nil
}