Repository: google/osv-scanner
Pull request #341 (2023-04-13 00:18:28)

Make the reporter public
Make the reporter package public so that it can be passed into the public DoScan function.


Patch:
diff --git a/cmd/osv-scanner/main.go b/cmd/osv-scanner/main.go
index 7d80646..c0c0df1 100644
--- a/cmd/osv-scanner/main.go
+++ b/cmd/osv-scanner/main.go
@@ -6,8 +6,8 @@ import (
 	"io"
 	"os"
 
-	"github.com/google/osv-scanner/internal/output"
 	"github.com/google/osv-scanner/pkg/osvscanner"
+	"github.com/google/osv-scanner/pkg/reporter"
 
 	"github.com/urfave/cli/v2"
 )
@@ -20,10 +20,10 @@ var (
 )
 
 func run(args []string, stdout, stderr io.Writer) int {
-	var r *output.Reporter
+	var r *reporter.Reporter
 
 	cli.VersionPrinter = func(ctx *cli.Context) {
-		r = output.NewReporter(ctx.App.Writer, ctx.App.ErrWriter, "")
+		r = reporter.NewReporter(ctx.App.Writer, ctx.App.ErrWriter, "")
 		r.PrintText(fmt.Sprintf("osv-scanner version: %s\ncommit: %s\nbuilt at: %s\n", ctx.App.Version, commit, date))
 	}
 
@@ -109,7 +109,7 @@ func run(args []string, stdout, stderr io.Writer) int {
 				format = "json"
 			}
 
-			r = output.NewReporter(stdout, stderr, format)
+			r = reporter.NewReporter(stdout, stderr, format)
 
 			vulnResult, err := osvscanner.DoScan(osvscanner.ScannerActions{
 				LockfilePaths:            context.StringSlice("lockfile"),
@@ -133,7 +133,7 @@ func run(args []string, stdout, stderr io.Writer) int {
 
 	if err := app.Run(args); err != nil {
 		if r == nil {
-			r = output.NewReporter(stdout, stderr, "")
+			r = reporter.NewReporter(stdout, stderr, "")
 		}
 		if errors.Is(err, osvscanner.VulnerabilitiesFoundErr) {
 			return 1
diff --git a/internal/sourceanalysis/go.go b/internal/sourceanalysis/go.go
index a2a599a..88ae0d5 100644
--- a/internal/sourceanalysis/go.go
+++ b/internal/sourceanalysis/go.go
@@ -5,13 +5,13 @@ import (
 	"path/filepath"
 
 	"github.com/google/osv-scanner/internal/govulncheckshim"
-	"github.com/google/osv-scanner/internal/output"
 	"github.com/google/osv-scanner/pkg/models"
+	"github.com/google/osv-scanner/pkg/reporter"
 	"golang.org/x/exp/slices"
 	"golang.org/x/vuln/exp/govulncheck"
 )
 
-func goAnalysis(r *output.Reporter, pkgs []models.PackageVulns, source models.SourceInfo) {
+func goAnalysis(r *reporter.Reporter, pkgs []models.PackageVulns, source models.SourceInfo) {
 	vulns, vulnsByID := vulnsFromAllPkgs(pkgs)
 	res, err := govulncheckshim.RunGoVulnCheck(filepath.Dir(source.Path), vulns)
 	if err != nil {
diff --git a/internal/sourceanalysis/sourceanalysis.go b/internal/sourceanalysis/sourceanalysis.go
index 121233b..885a2d0 100644
--- a/internal/sourceanalysis/sourceanalysis.go
+++ b/internal/sourceanalysis/sourceanalysis.go
@@ -3,8 +3,8 @@ package sourceanalysis
 import (
 	"path/filepath"
 
-	"github.com/google/osv-scanner/internal/output"
 	"github.com/google/osv-scanner/pkg/models"
+	"github.com/google/osv-scanner/pkg/reporter"
 )
 
 // vulnsFromAllPkgs returns the flattened list of unique vulnerabilities
@@ -25,7 +25,7 @@ func vulnsFromAllPkgs(pkgs []models.PackageVulns) ([]models.Vulnerability, map[s
 }
 
 // Run runs the language specific analyzers on the code given packages and source info
-func Run(r *output.Reporter, source models.SourceInfo, pkgs []models.PackageVulns) {
+func Run(r *reporter.Reporter, source models.SourceInfo, pkgs []models.PackageVulns) {
 	// GoVulnCheck
 	if source.Type == "lockfile" && filepath.Base(source.Path) == "go.mod" {
 		goAnalysis(r, pkgs, source)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 91c3226..cf076d1 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -6,9 +6,8 @@ import (
 	"path/filepath"
 	"time"
 
-	"github.com/google/osv-scanner/internal/output"
-
 	"github.com/BurntSushi/toml"
+	"github.com/google/osv-scanner/pkg/reporter"
 	"golang.org/x/exp/slices"
 )
 
@@ -64,7 +63,7 @@ func (c *ConfigManager) UseOverride(configPath string) error {
 }
 
 // Attempts to get the config
-func (c *ConfigManager) Get(r *output.Reporter, targetPath string) Config {
+func (c *ConfigManager) Get(r *reporter.Reporter, targetPath string) Config {
 	if c.OverrideConfig != nil {
 		return *c.OverrideConfig
 	}
diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go
index c502302..aadfaf0 100644
--- a/pkg/osvscanner/osvscanner.go
+++ b/pkg/osvscanner/osvscanner.go
@@ -9,12 +9,12 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/google/osv-scanner/internal/output"
 	"github.com/google/osv-scanner/internal/sbom"
 	"github.com/google/osv-scanner/pkg/config"
 	"github.com/google/osv-scanner/pkg/lockfile"
 	"github.com/google/osv-scanner/pkg/models"
 	"github.com/google/osv-scanner/pkg/osv"
+	"github.com/google/osv-scanner/pkg/reporter"
 
 	"github.com/go-git/go-billy/v5"
 	"github.com/go-git/go-billy/v5/osfs"
@@ -52,7 +52,7 @@ var OnlyUncalledVulnerabilitiesFoundErr = errors.New("only uncalled vulnerabilit
 //   - Any lockfiles with scanLockfile
 //   - Any SBOM files with scanSBOMFile
 //   - Any git repositories with scanGit
-func scanDir(r *output.Reporter, query *osv.BatchedQuery, dir string, skipGit bool, recursive bool, useGitIgnore bool) error {
+func scanDir(r *reporter.Reporter, query *osv.BatchedQuery, dir string, skipGit bool, recursive bool, useGitIgnore bool) error {
 	var ignoreMatcher *gitIgnoreMatcher
 	if useGitIgnore {
 		var err error
@@ -180,7 +180,7 @@ func (m *gitIgnoreMatcher) match(absPath string, isDir bool) (bool, error) {
 
 // scanLockfile will load, identify, and parse the lockfile path passed in, and add the dependencies specified
 // within to `query`
-func scanLockfile(r *output.Reporter, query *osv.BatchedQuery, path string, parseAs string) error {
+func scanLockfile(r *reporter.Reporter, query *osv.BatchedQuery, path string, parseAs string) error {
 	var err error
 	var parsedLockfile lockfile.Lockfile
 
@@ -221,7 +221,7 @@ func scanLockfile(r *output.Reporter, query *osv.BatchedQuery, path string, pars
 
 // scanSBOMFile will load, identify, and parse the SBOM path passed in, and add the dependencies specified
 // within to `query`
-func scanSBOMFile(r *output.Reporter, query *osv.BatchedQuery, path string) error {
+func scanSBOMFile(r *reporter.Reporter, query *osv.BatchedQuery, path string) error {
 	for _, provider := range sbom.Providers {
 		if !provider.MatchesRecognizedFileNames(path) {
 			// Skip if filename is not usually a sbom file of this format
@@ -289,7 +289,7 @@ func getCommitSHA(repoDir string) (string, error) {
 }
 
 // Scan git repository. Expects repoDir to end with /
-func scanGit(r *output.Reporter, query *osv.BatchedQuery, repoDir string) error {
+func scanGit(r *reporter.Reporter, query *osv.BatchedQuery, repoDir string) error {
 	commit, err := getCommitSHA(repoDir)
 	if err != nil {
 		return err
@@ -310,7 +310,7 @@ func scanGitCommit(query *osv.BatchedQuery, commit string, source string) error
 	return nil
 }
 
-func scanDebianDocker(r *output.Reporter, query *osv.BatchedQuery, dockerImageName string) error {
+func scanDebianDocker(r *reporter.Reporter, query *osv.BatchedQuery, dockerImageName string) error {
 	cmd := exec.Command("docker", "run", "--rm", "--entrypoint", "/usr/bin/dpkg-query", dockerImageName, "-f", "${Package}###${Version}\\n", "-W")
 	stdout, err := cmd.StdoutPipe()
 
@@ -358,7 +358,7 @@ func scanDebianDocker(r *output.Reporter, query *osv.BatchedQuery, dockerImageNa
 }
 
 // Filters results according to config, preserving order. Returns total number of vulnerabilities removed.
-func filterResults(r *output.Reporter, results *models.VulnerabilityResults, configManager *config.ConfigManager) int {
+func filterResults(r *reporter.Reporter, results *models.VulnerabilityResults, configManager *config.ConfigManager) int {
 	removedCount := 0
 	newResults := []models.PackageSource{} // Want 0 vulnerabilities to show in JSON as an empty list, not null.
 	for _, pkgSrc := range results.Results {
@@ -384,7 +384,7 @@ func filterResults(r *output.Reporter, results *models.VulnerabilityResults, con
 }
 
 // Filters package-grouped vulnerabilities according to config, preserving ordering. Returns filtered package vulnerabilities.
-func filterPackageVulns(r *output.Reporter, pkgVulns models.PackageVulns, configToUse config.Config) models.PackageVulns {
+func filterPackageVulns(r *reporter.Reporter, pkgVulns models.PackageVulns, configToUse config.Config) models.PackageVulns {
 	ignoredVulns := map[string]struct{}{}
 	// Iterate over groups first to remove all aliases of ignored vulnerabilities.
 	var newGroups []models.GroupInfo
@@ -441,9 +441,9 @@ func parseLockfilePath(lockfileElem string) (string, string) {
 }
 
 // Perform osv scanner action, with optional reporter to output information
-func DoScan(actions ScannerActions, r *output.Reporter) (models.VulnerabilityResults, error) {
+func DoScan(actions ScannerActions, r *reporter.Reporter) (models.VulnerabilityResults, error) {
 	if r == nil {
-		r = output.NewVoidReporter()
+		r = reporter.NewVoidReporter()
 	}
 
 	configManager := config.ConfigManager{
diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go
index fab5381..a477a5f 100644
--- a/pkg/osvscanner/vulnerability_result.go
+++ b/pkg/osvscanner/vulnerability_result.go
@@ -4,16 +4,16 @@ import (
 	"fmt"
 	"sort"
 
-	"github.com/google/osv-scanner/internal/output"
 	"github.com/google/osv-scanner/internal/sourceanalysis"
 	"github.com/google/osv-scanner/pkg/grouper"
 	"github.com/google/osv-scanner/pkg/models"
 	"github.com/google/osv-scanner/pkg/osv"
+	"github.com/google/osv-scanner/pkg/reporter"
 )
 
 // groupResponseBySource converts raw OSV API response into structured vulnerability information
 // grouped by source location.
-func groupResponseBySource(r *output.Reporter, query osv.BatchedQuery, resp *osv.HydratedBatchedResponse, callAnalysis bool) models.VulnerabilityResults {
+func groupResponseBySource(r *reporter.Reporter, query osv.BatchedQuery, resp *osv.HydratedBatchedResponse, callAnalysis bool) models.VulnerabilityResults {
 	output := models.VulnerabilityResults{
 		Results: []models.PackageSource{},
 	}
diff --git a/internal/output/reporter.go b/pkg/reporter/reporter.go
similarity index 86%
rename from internal/output/reporter.go
rename to pkg/reporter/reporter.go
index f37234a..0cf7171 100644
--- a/internal/output/reporter.go
+++ b/pkg/reporter/reporter.go
@@ -1,10 +1,11 @@
-package output
+package reporter
 
 import (
 	"fmt"
 	"io"
 	"strings"
 
+	"github.com/google/osv-scanner/internal/output"
 	"github.com/google/osv-scanner/pkg/models"
 )
 
@@ -60,11 +61,11 @@ func (r *Reporter) PrintText(msg string) {
 func (r *Reporter) PrintResult(vulnResult *models.VulnerabilityResults) error {
 	switch r.format {
 	case "json":
-		return PrintJSONResults(vulnResult, r.stdout)
+		return output.PrintJSONResults(vulnResult, r.stdout)
 	case "markdown":
-		PrintMarkdownTableResults(vulnResult, r.stdout)
+		output.PrintMarkdownTableResults(vulnResult, r.stdout)
 	case "table":
-		PrintTableResults(vulnResult, r.stdout)
+		output.PrintTableResults(vulnResult, r.stdout)
 	}
 
 	return nil


Test patch:
diff --git a/pkg/osvscanner/osvscanner_internal_test.go b/pkg/osvscanner/osvscanner_internal_test.go
index fa7f310..f45e38d 100644
--- a/pkg/osvscanner/osvscanner_internal_test.go
+++ b/pkg/osvscanner/osvscanner_internal_test.go
@@ -5,10 +5,10 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
-	"github.com/google/osv-scanner/internal/output"
 	"github.com/google/osv-scanner/internal/testutility"
 	"github.com/google/osv-scanner/pkg/config"
 	"github.com/google/osv-scanner/pkg/models"
+	"github.com/google/osv-scanner/pkg/reporter"
 )
 
 func Test_filterResults(t *testing.T) {
@@ -51,7 +51,7 @@ func Test_filterResults(t *testing.T) {
 		tt := tt // Reinitialize for t.Parallel()
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
-			r := output.NewVoidReporter()
+			r := reporter.NewVoidReporter()
 			// ConfigManager looks for osv-scanner.toml in the source path.
 			// Sources in the test input should point to files/folders in the text fixture folder for this to work correctly.
 			configManager := config.ConfigManager{
diff --git a/pkg/osvscanner/vulnerability_result_internal_test.go b/pkg/osvscanner/vulnerability_result_internal_test.go
index 79e860b..212b5ff 100644
--- a/pkg/osvscanner/vulnerability_result_internal_test.go
+++ b/pkg/osvscanner/vulnerability_result_internal_test.go
@@ -4,15 +4,15 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/google/osv-scanner/internal/output"
 	"github.com/google/osv-scanner/pkg/models"
 	"github.com/google/osv-scanner/pkg/osv"
+	"github.com/google/osv-scanner/pkg/reporter"
 )
 
 func Test_groupResponseBySource(t *testing.T) {
 	t.Parallel()
 	type args struct {
-		r            *output.Reporter
+		r            *reporter.Reporter
 		query        osv.BatchedQuery
 		resp         *osv.HydratedBatchedResponse
 		callAnalysis bool