Skip to content

Commit

Permalink
Merge branch 'main' into license
Browse files Browse the repository at this point in the history
  • Loading branch information
Hayley Denbraver authored Nov 29, 2023
2 parents 8511e6a + 4a849b8 commit 3332370
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 105 deletions.
32 changes: 26 additions & 6 deletions cmd/osv-scanner/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,13 @@ func run(args []string, stdout, stderr io.Writer) int {
Name: "experimental-all-packages",
Usage: "when json output is selected, prints all packages",
},
&cli.BoolFlag{
Name: "experimental-licenses-summary",
Usage: "report a license summary, implying the --experimental-all-packages flag",
},
&cli.StringSliceFlag{
Name: "experimental-licenses",
Usage: "report on licenses",
Usage: "report on licenses based on an allowlist",
},
},
ArgsUsage: "[directory1 directory2...]",
Expand Down Expand Up @@ -163,6 +167,17 @@ func run(args []string, stdout, stderr io.Writer) int {
}
}

if context.Bool("experimental-licenses-summary") && context.IsSet("experimental-licenses") {
return fmt.Errorf("--experimental-licenses-summary and --experimental-licenses flags cannot be set")
}
allowlist := context.StringSlice("experimental-licenses")
if context.IsSet("experimental-licenses") &&
(len(allowlist) == 0 ||
(len(allowlist) == 1 && allowlist[0] == "")) {
return fmt.Errorf("--experimental-licenses requires at least one value")
}
// TODO: verify that the licenses they passed in are indeed spdx.

if r, err = reporter.New(format, stdout, stderr, termWidth); err != nil {
return err
}
Expand All @@ -186,11 +201,16 @@ func run(args []string, stdout, stderr io.Writer) int {
DirectoryPaths: context.Args().Slice(),
CallAnalysisStates: callAnalysisStates,
ExperimentalScannerActions: osvscanner.ExperimentalScannerActions{
LocalDBPath: context.String("experimental-local-db-path"),
CompareLocally: context.Bool("experimental-local-db"),
CompareOffline: context.Bool("experimental-offline"),
ShowAllPackages: context.Bool("experimental-all-packages"),
ScanLicenses: context.IsSet("experimental-licenses"),
LocalDBPath: context.String("experimental-local-db-path"),
CompareLocally: context.Bool("experimental-local-db"),
CompareOffline: context.Bool("experimental-offline"),
// License summary mode causes all
// packages to appear in the json as
// every package has a license - even
// if it's just the UNKNOWN license.
ShowAllPackages: context.Bool("experimental-all-packages") ||
context.Bool("experimental-licenses-summary"),
ScanLicensesSummary: context.Bool("experimental-licenses-summary"),
ScanLicensesAllowlist: context.StringSlice("experimental-licenses"),
},
}, r)
Expand Down
89 changes: 74 additions & 15 deletions cmd/osv-scanner/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ func TestRun(t *testing.T) {
"results": [],
"experimental_config": {
"licenses": {
"enabled": false,
"summary": false,
"allowlist": null
}
}
Expand All @@ -373,7 +373,7 @@ func TestRun(t *testing.T) {
"results": [],
"experimental_config": {
"licenses": {
"enabled": false,
"summary": false,
"allowlist": null
}
}
Expand Down Expand Up @@ -1075,7 +1075,7 @@ func TestRun_LocalDatabases(t *testing.T) {
"results": [],
"experimental_config": {
"licenses": {
"enabled": false,
"summary": false,
"allowlist": null
}
}
Expand All @@ -1096,7 +1096,7 @@ func TestRun_LocalDatabases(t *testing.T) {
"results": [],
"experimental_config": {
"licenses": {
"enabled": false,
"summary": false,
"allowlist": null
}
}
Expand Down Expand Up @@ -1147,9 +1147,9 @@ func TestRun_Licenses(t *testing.T) {
t.Parallel()
tests := []cliTestCase{
{
name: "No vulnerabilities but contains license violations",
args: []string{"", "--experimental-licenses", "", "./fixtures/locks-many"},
wantExitCode: 1,
name: "No vulnerabilities with license summary",
args: []string{"", "--experimental-licenses-summary", "./fixtures/locks-many"},
wantExitCode: 0,
wantStdout: `
Scanning dir ./fixtures/locks-many
Scanned <rootdir>/fixtures/locks-many/Gemfile.lock file and found 1 package
Expand All @@ -1172,9 +1172,9 @@ func TestRun_Licenses(t *testing.T) {
wantStderr: "",
},
{
name: "No vulnerabilities but contains license violations markdown",
args: []string{"", "--experimental-licenses", "", "--format=markdown", "./fixtures/locks-many"},
wantExitCode: 1,
name: "No vulnerabilities with license summary in markdown",
args: []string{"", "--experimental-licenses-summary", "--format=markdown", "./fixtures/locks-many"},
wantExitCode: 0,
wantStdout: `Scanning dir ./fixtures/locks-many
Scanned <rootdir>/fixtures/locks-many/Gemfile.lock file and found 1 package
Scanned <rootdir>/fixtures/locks-many/alpine.cdx.xml as CycloneDX SBOM and found 15 packages
Expand All @@ -1194,8 +1194,8 @@ Filtered 2 vulnerabilities from output
wantStderr: "",
},
{
name: "Vulnerabilities and license violations",
args: []string{"", "--experimental-licenses", "", "--config=./fixtures/osv-scanner-empty-config.toml", "./fixtures/locks-many/package-lock.json"},
name: "Vulnerabilities and license summary",
args: []string{"", "--experimental-licenses-summary", "--config=./fixtures/osv-scanner-empty-config.toml", "./fixtures/locks-many/package-lock.json"},
wantExitCode: 1,
wantStdout: `
Scanning dir ./fixtures/locks-many/package-lock.json
Expand Down Expand Up @@ -1299,7 +1299,7 @@ Filtered 2 vulnerabilities from output
],
"experimental_config": {
"licenses": {
"enabled": true,
"summary": false,
"allowlist": [
"MIT"
]
Expand Down Expand Up @@ -1343,7 +1343,7 @@ Filtered 2 vulnerabilities from output
],
"experimental_config": {
"licenses": {
"enabled": true,
"summary": false,
"allowlist": [
"MIT"
]
Expand Down Expand Up @@ -1404,7 +1404,7 @@ Filtered 2 vulnerabilities from output
],
"experimental_config": {
"licenses": {
"enabled": true,
"summary": false,
"allowlist": [
"MIT",
"Apache-2.0"
Expand All @@ -1418,6 +1418,65 @@ Filtered 2 vulnerabilities from output
Scanned <rootdir>/fixtures/locks-licenses/package-lock.json file and found 3 packages
`,
},
{
name: "Licenses in summary mode json",
args: []string{"", "--format=json", "--experimental-licenses-summary", "./fixtures/locks-licenses/package-lock.json"},
wantExitCode: 0,
wantStdout: `
{
"results": [
{
"source": {
"path": "<rootdir>/fixtures/locks-licenses/package-lock.json",
"type": "lockfile"
},
"packages": [
{
"package": {
"name": "babel",
"version": "6.23.0",
"ecosystem": "npm"
},
"licenses": [
"MIT"
]
},
{
"package": {
"name": "human-signals",
"version": "5.0.0",
"ecosystem": "npm"
},
"licenses": [
"Apache-2.0"
]
},
{
"package": {
"name": "ms",
"version": "2.1.3",
"ecosystem": "npm"
},
"licenses": [
"MIT"
]
}
]
}
],
"experimental_config": {
"licenses": {
"summary": true,
"allowlist": []
}
}
}
`,
wantStderr: `
Scanning dir ./fixtures/locks-licenses/package-lock.json
Scanned <rootdir>/fixtures/locks-licenses/package-lock.json file and found 3 packages
`,
},
}
for _, tt := range tests {
tt := tt
Expand Down
11 changes: 5 additions & 6 deletions internal/output/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,13 @@ func MaxSeverity(group models.GroupInfo, pkg models.PackageVulns) string {

func licenseTableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResults) table.Writer {
licenseConfig := vulnResult.ExperimentalAnalysisConfig.Licenses
if !licenseConfig.Enabled {
return outputTable
}
if len(licenseConfig.Allowlist) == 0 {
if licenseConfig.Summary {
return licenseSummaryTableBuilder(outputTable, vulnResult)
} else if len(licenseConfig.Allowlist) > 0 {
return licenseViolationsTableBuilder(outputTable, vulnResult)
} else {
return outputTable
}

return licenseViolationsTableBuilder(outputTable, vulnResult)
}

func licenseSummaryTableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResults) table.Writer {
Expand Down
2 changes: 1 addition & 1 deletion pkg/models/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type ExperimentalAnalysisConfig struct {
}

type ExperimentalLicenseConfig struct {
Enabled bool `json:"enabled"`
Summary bool `json:"summary"`
Allowlist []License `json:"allowlist"`
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/osvscanner/osvscanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type ExperimentalScannerActions struct {
CompareLocally bool
CompareOffline bool
ShowAllPackages bool
ScanLicenses bool
ScanLicensesSummary bool
ScanLicensesAllowlist []string

LocalDBPath string
Expand Down Expand Up @@ -786,14 +786,13 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe
}

var licensesResp [][]models.License
if actions.ScanLicenses {
if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary {
licensesResp, err = makeLicensesRequests(filteredScannedPackages)
if err != nil {
return models.VulnerabilityResults{}, err
}
}

results := buildVulnerabilityResults(r, filteredScannedPackages, vulnsResp, licensesResp, actions.CallAnalysisStates, actions.ShowAllPackages, actions.ScanLicenses, actions.ScanLicensesAllowlist)
results := buildVulnerabilityResults(r, filteredScannedPackages, vulnsResp, licensesResp, actions)

filtered := filterResults(r, &results, &configManager, actions.ShowAllPackages)
if filtered > 0 {
Expand Down Expand Up @@ -823,6 +822,7 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe
}
}
onlyUncalledVuln = onlyUncalledVuln && vuln
licenseViolation = licenseViolation && len(actions.ScanLicensesAllowlist) > 0

if (!vuln || onlyUncalledVuln) && !licenseViolation {
// There is no error.
Expand Down
27 changes: 12 additions & 15 deletions pkg/osvscanner/vulnerability_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,14 @@ func buildVulnerabilityResults(
packages []scannedPackage,
vulnsResp *osv.HydratedBatchedResponse,
licensesResp [][]models.License,
callAnalysisStates map[string]bool,
allPackages bool,
licenses bool,
licensesAllowlist []string,
actions ScannerActions,
) models.VulnerabilityResults {
output := models.VulnerabilityResults{
Results: []models.PackageSource{},
}
groupedBySource := map[models.SourceInfo][]models.PackageVulns{}
if len(licensesAllowlist) == 1 && licensesAllowlist[0] == "" {
licensesAllowlist = []string{}
}
for i, rawPkg := range packages {
includePackage := allPackages
includePackage := actions.ShowAllPackages
var pkg models.PackageVulns
if rawPkg.Commit != "" {
pkg.Package.Commit = rawPkg.Commit
Expand Down Expand Up @@ -62,10 +56,10 @@ func buildVulnerabilityResults(
pkg.Vulnerabilities = vulnsResp.Results[i].Vulns
pkg.Groups = grouper.Group(grouper.ConvertVulnerabilityToIDAliases(pkg.Vulnerabilities))
}
if licenses {
if len(actions.ScanLicensesAllowlist) > 0 {
pkg.Licenses = licensesResp[i]
allowlist := make(map[string]bool)
for _, license := range licensesAllowlist {
for _, license := range actions.ScanLicensesAllowlist {
allowlist[strings.ToLower(license)] = true
}
for _, license := range pkg.Licenses {
Expand All @@ -77,13 +71,16 @@ func buildVulnerabilityResults(
includePackage = true
}
}
if actions.ScanLicensesSummary {
pkg.Licenses = licensesResp[i]
}
if includePackage {
groupedBySource[rawPkg.Source] = append(groupedBySource[rawPkg.Source], pkg)
}
}

for source, packages := range groupedBySource {
sourceanalysis.Run(r, source, packages, callAnalysisStates)
sourceanalysis.Run(r, source, packages, actions.CallAnalysisStates)
output.Results = append(output.Results, models.PackageSource{
Source: source,
Packages: packages,
Expand All @@ -98,10 +95,10 @@ func buildVulnerabilityResults(
return output.Results[i].Source.Path < output.Results[j].Source.Path
})

if licenses {
output.ExperimentalAnalysisConfig.Licenses.Enabled = true
allowlist := make([]models.License, len(licensesAllowlist))
for i, l := range licensesAllowlist {
if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary {
output.ExperimentalAnalysisConfig.Licenses.Summary = actions.ScanLicensesSummary
allowlist := make([]models.License, len(actions.ScanLicensesAllowlist))
for i, l := range actions.ScanLicensesAllowlist {
allowlist[i] = models.License(l)
}
output.ExperimentalAnalysisConfig.Licenses.Allowlist = allowlist
Expand Down
Loading

0 comments on commit 3332370

Please sign in to comment.