Skip to content

Commit ebe3f50

Browse files
committed
cmd/vulnreport: add subcommand framework
Adds a new unified framework for vulnreport subcommands. Each subcommand now implements an interface, command, which has functions for setting up, running, and tearing down the command. Change-Id: I7c6ab5cf1b4c19b300dbdef6df0d76e2f1f303ea Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/559955 Reviewed-by: Damien Neil <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 6d7904a commit ebe3f50

14 files changed

+647
-337
lines changed

cmd/vulnreport/command.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"context"
9+
"fmt"
10+
"os"
11+
"path/filepath"
12+
13+
"golang.org/x/vulndb/cmd/vulnreport/log"
14+
)
15+
16+
// command represents a subcommand of vulnreport.
17+
type command interface {
18+
// name outputs the string used to invoke the subcommand.
19+
name() string
20+
// usage outputs strings indicating how to use the subcommand.
21+
usage() (args string, desc string)
22+
// setup populates any state needed to run the subcommand.
23+
setup(context.Context) error
24+
// parseArgs takes in the raw args passed to the command line,
25+
// and converts them to a representation understood by "run".
26+
// This function need not be one-to-one: there may be more
27+
// inputs than args or vice-versa.
28+
parseArgs(_ context.Context, args []string) (inputs []string, _ error)
29+
// run executes the subcommand on the given input.
30+
run(_ context.Context, input string) error
31+
// close cleans up state and/or completes tasks that should occur
32+
// after run is called on all inputs.
33+
close() error
34+
}
35+
36+
// run executes the given command on the given raw arguments.
37+
func run(ctx context.Context, c command, args []string) error {
38+
if err := c.setup(ctx); err != nil {
39+
return err
40+
}
41+
defer c.close()
42+
43+
inputs, err := c.parseArgs(ctx, args)
44+
if err != nil {
45+
return err
46+
}
47+
48+
for _, input := range inputs {
49+
log.Infof("%s %v", c.name(), input)
50+
if err := c.run(ctx, input); err != nil {
51+
log.Errf("%s: %s", c.name(), err)
52+
}
53+
}
54+
return nil
55+
}
56+
57+
const (
58+
filenameArgs = "[filename | github-id] ..."
59+
ghIssueArgs = "[github-id] ..."
60+
)
61+
62+
// filenameParser implements the "parseArgs" function of the command
63+
// interface, and can be used by commands that operate on YAML filenames.
64+
type filenameParser bool
65+
66+
func (filenameParser) parseArgs(_ context.Context, args []string) (filenames []string, _ error) {
67+
if len(args) == 0 {
68+
return nil, fmt.Errorf("no arguments provided")
69+
}
70+
for _, arg := range args {
71+
fname, err := argToFilename(arg)
72+
if err != nil {
73+
log.Err(err)
74+
continue
75+
}
76+
filenames = append(filenames, fname)
77+
}
78+
return filenames, nil
79+
}
80+
81+
func argToFilename(arg string) (string, error) {
82+
if _, err := os.Stat(arg); err != nil {
83+
// If arg isn't a file, see if it might be an issue ID
84+
// with an existing report.
85+
for _, padding := range []string{"", "0", "00", "000"} {
86+
m, _ := filepath.Glob("data/*/GO-*-" + padding + arg + ".yaml")
87+
if len(m) == 1 {
88+
return m[0], nil
89+
}
90+
}
91+
return "", fmt.Errorf("%s is not a valid filename or issue ID with existing report: %w", arg, err)
92+
}
93+
return arg, nil
94+
}

cmd/vulnreport/commit.go

+25-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"strings"
1212

1313
"golang.org/x/exp/slices"
14-
"golang.org/x/vulndb/internal/derrors"
1514
"golang.org/x/vulndb/internal/ghsa"
1615
"golang.org/x/vulndb/internal/proxy"
1716
"golang.org/x/vulndb/internal/report"
@@ -21,16 +20,36 @@ var (
2120
updateIssue = flag.Bool("up", false, "for commit, create a CL that updates (doesn't fix) the tracking bug")
2221
)
2322

24-
func commit(ctx context.Context, filename string, ghsaClient *ghsa.Client, pc *proxy.Client, force bool) (err error) {
25-
defer derrors.Wrap(&err, "commit(%q)", filename)
23+
type commit struct {
24+
pc *proxy.Client
25+
gc *ghsa.Client
2626

27+
filenameParser
28+
}
29+
30+
func (commit) name() string { return "commit" }
31+
32+
func (commit) usage() (string, string) {
33+
const desc = "creates new commits for YAML reports"
34+
return filenameArgs, desc
35+
}
36+
37+
func (c *commit) setup(ctx context.Context) error {
38+
c.pc = proxy.NewDefaultClient()
39+
c.gc = ghsa.NewClient(ctx, *githubToken)
40+
return nil
41+
}
42+
43+
func (c *commit) close() error { return nil }
44+
45+
func (c *commit) run(ctx context.Context, filename string) (err error) {
2746
// Clean up the report file and lint the result.
2847
// Stop if there any problems.
29-
if err := fix(ctx, filename, ghsaClient, pc, force); err != nil {
48+
r, err := report.ReadAndLint(filename, c.pc)
49+
if err != nil {
3050
return err
3151
}
32-
r, err := report.ReadAndLint(filename, pc)
33-
if err != nil {
52+
if err := fixReport(ctx, r, filename, c.pc, c.gc); err != nil {
3453
return err
3554
}
3655
if hasUnaddressedTodos(r) {

0 commit comments

Comments
 (0)