Skip to content

Commit

Permalink
internal/scan: introduce format flag
Browse files Browse the repository at this point in the history
The current values are text and json. The latter is compatible with
-json flag that is designated as a legacy flag common to Go tools. This
CL is a precursor to sarif support.

Change-Id: I5a73b224e34c6c7f2798858c818f5f8d8e2437d0
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/564478
Run-TryBot: Zvonimir Pavlinovic <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Maceo Thompson <[email protected]>
  • Loading branch information
zpavlinovic committed Feb 26, 2024
1 parent bb77557 commit 685e27b
Show file tree
Hide file tree
Showing 23 changed files with 108 additions and 41 deletions.
2 changes: 1 addition & 1 deletion cmd/govulncheck/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func runTestSuite(t *testing.T, dir string, govulndb string, update bool) {

func isJSONMode(args []string) bool {
for _, arg := range args {
if arg == "-json" {
if arg == "json" {
return true
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test basic binary scanning with json output
$ govulncheck -json -mode=binary ${vuln_binary}
$ govulncheck -format json -mode binary ${vuln_binary}
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test basic binary scanning with json output
$ govulncheck -json -mode=binary ${vendored_binary}
$ govulncheck -format json -mode binary ${vendored_binary}
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test binary scanning at the module level with json output
$ govulncheck -json -mode=binary -scan=module ${vuln_binary}
$ govulncheck -format json -mode binary -scan module ${vuln_binary}
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test binary scanning at the package level with json output
$ govulncheck -json -mode=binary -scan=package ${vuln_binary}
$ govulncheck -format json -mode binary -scan package ${vuln_binary}
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion cmd/govulncheck/testdata/testfiles/failures/query_fail.ct
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#####
# Test of query mode with invalid input.
$ govulncheck -mode=query -json example.com/module@ --> FAIL 2
$ govulncheck -mode=query -format json example.com/module@ --> FAIL 2
invalid query example.com/module@: must be of the form module@version
15 changes: 15 additions & 0 deletions cmd/govulncheck/testdata/testfiles/failures/usage_fail.ct
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,18 @@ the -show flag is not supported for JSON output
# Test of invalid input to -scan
$ govulncheck -scan=invalid ./... --> FAIL 2
"invalid" is not a valid scan level

#####
# Test of invalid -format value
$ govulncheck -format invalid ./... --> FAIL 2
"invalid" is not a valid output format

#####
# Test of trying to run -json with '-format text' flag
$ govulncheck -C ${moddir}/vuln -json -format text . --> FAIL 2
the -json flag cannot be used with -format flag

#####
# Test of explicit format use together with -json flag
$ govulncheck -C ${moddir}/vuln -format json -json . --> FAIL 2
the -json flag cannot be used with -format flag
2 changes: 1 addition & 1 deletion cmd/govulncheck/testdata/testfiles/query/query_json.ct
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test of query mode for a third party module.
$ govulncheck -mode=query -json github.com/tidwall/[email protected]
$ govulncheck -mode=query -format json github.com/tidwall/[email protected]
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test of query mode with multiple inputs.
$ govulncheck -mode=query -json [email protected] github.com/tidwall/[email protected]
$ govulncheck -mode=query -format json [email protected] github.com/tidwall/[email protected]
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#####
#
$ govulncheck -C ${moddir}/vuln -json ./...
$ govulncheck -C ${moddir}/vuln -format json ./...
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#####
# Test souce mode with no callstacks
# Test source mode with no callstacks
$ govulncheck -C ${moddir}/informational -show=traces .
Scanning your code and P packages across M dependent modules for known vulnerabilities...

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test for multiple call stacks in source mode
$ govulncheck -json -C ${moddir}/multientry .
$ govulncheck -format json -C ${moddir}/multientry .
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test of source mode json on a module with a replace directive.
$ govulncheck -C ${moddir}/replace -json ./...
$ govulncheck -C ${moddir}/replace -format json ./...
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
#
$ govulncheck -C ${moddir}/vendored -json ./...
$ govulncheck -C ${moddir}/vendored -format json ./...
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test that findings with callstacks or packages are not emitted in module mode
$ govulncheck -json -scan module -C ${moddir}/multientry
$ govulncheck -format json -scan module -C ${moddir}/multientry
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test that findings with callstacks are not emitted in package mode
$ govulncheck -json -scan package -C ${moddir}/multientry .
$ govulncheck -format json -scan package -C ${moddir}/multientry .
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test of query mode with the standard library.
$ govulncheck -mode=query -json [email protected]
$ govulncheck -mode=query -format json [email protected]
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test of query mode with the standard library (with a v prefix on the version).
$ govulncheck -mode=query -json [email protected]
$ govulncheck -mode=query -format json [email protected]
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#####
# Test finding stdlib vulnerability in source mode with json output
$ govulncheck -C ${moddir}/stdlib -json .
$ govulncheck -C ${moddir}/stdlib -format json .
{
"config": {
"protocol_version": "v1.0.0",
Expand Down
28 changes: 28 additions & 0 deletions cmd/govulncheck/testdata/testfiles/usage/format.ct
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#####
# Test of explicit text format
$ govulncheck -C ${moddir}/informational -format text .
Scanning your code and P packages across M dependent modules for known vulnerabilities...

=== Symbol Results ===

No vulnerabilities found.

Your code is affected by 0 vulnerabilities.
This scan also found 1 vulnerability in packages you import and 1 vulnerability
in modules you require, but your code doesn't appear to call these
vulnerabilities.
Use '-show verbose' for more details.

# Test of explicit json format
$ govulncheck -C ${moddir}/informational -format json
{
"config": {
"protocol_version": "v1.0.0",
"scanner_name": "govulncheck",
"scanner_version": "v0.0.0-00000000000-20000101010101",
"db": "testdata/vulndb-v1",
"db_last_modified": "2023-04-03T15:57:51Z",
"go_version": "go1.18",
"scan_level": "symbol"
}
}
5 changes: 4 additions & 1 deletion cmd/govulncheck/testdata/testfiles/usage/usage.ct
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ Usage:
change to dir before running govulncheck
-db url
vulnerability database url (default "https://vuln.go.dev")
-format string
specify format output
The supported values are 'text' and 'json' (default 'text')
-json
output JSON
output JSON (Go compatible legacy flag, see format flag)
-mode string
supports source or binary (default "source")
-scan string
Expand Down
60 changes: 41 additions & 19 deletions internal/scan/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ type config struct {
patterns []string
mode string
db string
json bool
dir string
tags []string
tags buildutil.TagsFlag
test bool
show []string
show showFlag
format string
env []string
}

Expand All @@ -34,21 +34,25 @@ const (
modeConvert = "convert" // only intended for use by gopls
modeQuery = "query" // only intended for use by gopls
modeExtract = "extract" // currently, only binary extraction is supported

formatUnset = ""
formatJSON = "json"
formatText = "text"
)

func parseFlags(cfg *config, stderr io.Writer, args []string) error {
var tagsFlag buildutil.TagsFlag
var showFlag showFlag
var version bool
var json bool
flags := flag.NewFlagSet("", flag.ContinueOnError)
flags.SetOutput(stderr)
flags.BoolVar(&cfg.json, "json", false, "output JSON")
flags.BoolVar(&json, "json", false, "output JSON (Go compatible legacy flag, see format flag)")
flags.BoolVar(&cfg.test, "test", false, "analyze test files (only valid for source mode, default false)")
flags.StringVar(&cfg.dir, "C", "", "change to `dir` before running govulncheck")
flags.StringVar(&cfg.db, "db", "https://vuln.go.dev", "vulnerability database `url`")
flags.StringVar(&cfg.mode, "mode", modeSource, "supports source or binary")
flags.Var(&tagsFlag, "tags", "comma-separated `list` of build tags")
flags.Var(&showFlag, "show", "enable display of additional information specified by the comma separated `list`\nThe supported values are 'traces','color', 'version', and 'verbose'")
flags.Var(&cfg.tags, "tags", "comma-separated `list` of build tags")
flags.Var(&cfg.show, "show", "enable display of additional information specified by the comma separated `list`\nThe supported values are 'traces','color', 'version', and 'verbose'")
flags.StringVar(&cfg.format, "format", formatUnset, "specify format output\nThe supported values are 'text' and 'json' (default 'text')")
flags.BoolVar(&version, "version", false, "print the version information")
scanLevel := flags.String("scan", "symbol", "set the scanning level desired, one of module, package or symbol")
flags.Usage = func() {
Expand All @@ -70,13 +74,11 @@ Usage:
return err
}
cfg.patterns = flags.Args()
cfg.tags = tagsFlag
cfg.show = showFlag
if version {
cfg.show = append(cfg.show, "version")
}
cfg.ScanLevel = govulncheck.ScanLevel(*scanLevel)
if err := validateConfig(cfg); err != nil {
if err := validateConfig(cfg, json); err != nil {
fmt.Fprintln(flags.Output(), err)
return errUsage
}
Expand All @@ -97,13 +99,36 @@ var supportedLevels = map[string]bool{
govulncheck.ScanLevelSymbol: true,
}

func validateConfig(cfg *config) error {
var supportedFormats = map[string]bool{
formatJSON: true,
formatText: true,
}

func validateConfig(cfg *config, json bool) error {
if json {
if len(cfg.show) > 0 {
return fmt.Errorf("the -show flag is not supported for JSON output")
}
if cfg.format != formatUnset {
return fmt.Errorf("the -json flag cannot be used with -format flag")
}
cfg.format = formatJSON
} else {
if cfg.format == formatUnset {
cfg.format = formatText
}
}

if _, ok := supportedModes[cfg.mode]; !ok {
return fmt.Errorf("%q is not a valid mode", cfg.mode)
}
if _, ok := supportedLevels[string(cfg.ScanLevel)]; !ok {
return fmt.Errorf("%q is not a valid scan level", cfg.ScanLevel)
}
if _, ok := supportedFormats[cfg.format]; !ok {
return fmt.Errorf("%q is not a valid output format", cfg.format)
}

switch cfg.mode {
case modeSource:
if len(cfg.patterns) == 1 && isFile(cfg.patterns[0]) {
Expand Down Expand Up @@ -135,8 +160,8 @@ func validateConfig(cfg *config) error {
if len(cfg.patterns) != 1 {
return fmt.Errorf("only 1 binary can be extracted at a time")
}
if cfg.json {
return fmt.Errorf("the -json flag must be off in extract mode")
if cfg.format == formatJSON {
return fmt.Errorf("the json format must be off in extract mode")
}
if !isFile(cfg.patterns[0]) {
return fmt.Errorf("%q is not a file (source extraction is not supported)", cfg.patterns[0])
Expand All @@ -161,8 +186,8 @@ func validateConfig(cfg *config) error {
if len(cfg.tags) > 0 {
return fmt.Errorf("the -tags flag is not supported in query mode")
}
if !cfg.json {
return fmt.Errorf("the -json flag must be set in query mode")
if cfg.format != formatJSON {
return fmt.Errorf("the json format must be set in query mode")
}
for _, pattern := range cfg.patterns {
// Parse the input here so that we can catch errors before
Expand All @@ -172,9 +197,6 @@ func validateConfig(cfg *config) error {
}
}
}
if cfg.json && len(cfg.show) > 0 {
return fmt.Errorf("the -show flag is not supported for JSON output")
}
return nil
}

Expand Down
4 changes: 2 additions & 2 deletions internal/scan/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ func RunGovulncheck(ctx context.Context, env []string, r io.Reader, stdout io.Wr

prepareConfig(ctx, cfg, client)
var handler govulncheck.Handler
switch {
case cfg.json:
switch cfg.format {
case formatJSON:
handler = govulncheck.NewJSONHandler(stdout)
default:
th := NewTextHandler(stdout)
Expand Down

0 comments on commit 685e27b

Please sign in to comment.