Skip to content

Commit

Permalink
feat(report): GitHub Dependency Snapshots support (aquasecurity#1522)
Browse files Browse the repository at this point in the history
Co-authored-by: Shira Cohen <[email protected]>
Co-authored-by: knqyf263 <[email protected]>
  • Loading branch information
3 people authored May 26, 2022
1 parent b7ec642 commit 4ab696e
Show file tree
Hide file tree
Showing 15 changed files with 565 additions and 28 deletions.
23 changes: 21 additions & 2 deletions integration/client_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
testcontainers "github.com/testcontainers/testcontainers-go"
"github.com/urfave/cli/v2"

"github.com/aquasecurity/trivy/pkg/clock"
"github.com/aquasecurity/trivy/pkg/commands"
"github.com/aquasecurity/trivy/pkg/report"
)
Expand Down Expand Up @@ -256,7 +257,7 @@ func TestClientServer(t *testing.T) {
}
}

func TestClientServerWithTemplate(t *testing.T) {
func TestClientServerWithFormat(t *testing.T) {
tests := []struct {
name string
args csArgs
Expand Down Expand Up @@ -306,17 +307,35 @@ func TestClientServerWithTemplate(t *testing.T) {
},
golden: "testdata/alpine-310.html.golden",
},
{
name: "alpine 3.10 with github dependency snapshots format",
args: csArgs{
Format: "github",
Input: "testdata/fixtures/images/alpine-310.tar.gz",
},
golden: "testdata/alpine-310.gsbom.golden",
},
}

fakeTime := time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC)
clock.SetFakeTime(t, fakeTime)

report.CustomTemplateFuncMap = map[string]interface{}{
"now": func() time.Time {
return time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC)
return fakeTime
},
"date": func(format string, t time.Time) string {
return t.Format(format)
},
}

// For GitHub Dependency Snapshots
t.Setenv("GITHUB_REF", "/ref/feature-1")
t.Setenv("GITHUB_SHA", "39da54a1ff04120a31df8cbc94ce9ede251d21a3")
t.Setenv("GITHUB_JOB", "integration")
t.Setenv("GITHUB_RUN_ID", "1910764383")
t.Setenv("GITHUB_WORKFLOW", "workflow-name")

t.Cleanup(func() {
report.CustomTemplateFuncMap = map[string]interface{}{}
})
Expand Down
91 changes: 91 additions & 0 deletions integration/testdata/alpine-310.gsbom.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
"detector": {
"name": "trivy",
"version": "dev",
"url": "https://github.com/aquasecurity/trivy"
},
"ref": "/ref/feature-1",
"sha": "39da54a1ff04120a31df8cbc94ce9ede251d21a3",
"job": {
"correlator": "workflow-name_integration",
"id": "1910764383"
},
"scanned": "2020-08-10T07:28:17Z",
"manifests": {
"testdata/fixtures/images/alpine-310.tar.gz (alpine 3.10.2)": {
"name": "alpine",
"resolved": {
"alpine-baselayout": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"alpine-keys": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"apk-tools": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"busybox": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"ca-certificates-cacert": {
"package_url": "pkg:apk/ca-certificates-cacert@20190108-r0",
"relationship": "direct",
"scope": "runtime"
},
"libc-utils": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"libcrypto1.1": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"libssl1.1": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"libtls-standalone": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"musl": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"musl-utils": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"scanelf": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"ssl_client": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
},
"zlib": {
"package_url": "pkg:apk/[email protected]",
"relationship": "direct",
"scope": "runtime"
}
}
}
}
}
23 changes: 23 additions & 0 deletions pkg/clock/clock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package clock

import (
"testing"
"time"

"k8s.io/utils/clock"
clocktesting "k8s.io/utils/clock/testing"
)

var c clock.Clock = clock.RealClock{}

// SetFakeTime sets a fake time for testing.
func SetFakeTime(t *testing.T, fakeTime time.Time) {
c = clocktesting.NewFakeClock(fakeTime)
t.Cleanup(func() {
c = clock.RealClock{}
})
}

func Now() time.Time {
return c.Now()
}
9 changes: 5 additions & 4 deletions pkg/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/aquasecurity/trivy/pkg/commands/server"
"github.com/aquasecurity/trivy/pkg/k8s"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/result"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
Expand All @@ -41,8 +42,8 @@ var (
formatFlag = cli.StringFlag{
Name: "format",
Aliases: []string{"f"},
Value: "table",
Usage: "format (table, json, sarif, template)",
Value: report.FormatTable,
Usage: "format (table, json, sarif, template, cyclonedx, spdx, spdx-json, github)",
EnvVars: []string{"TRIVY_FORMAT"},
}

Expand Down Expand Up @@ -906,8 +907,8 @@ func NewSbomCommand() *cli.Command {
&cli.StringFlag{
Name: "sbom-format",
Aliases: []string{"format"},
Value: "cyclonedx",
Usage: "SBOM format (cyclonedx, spdx, spdx-json)",
Value: report.FormatCycloneDX,
Usage: "SBOM format (cyclonedx, spdx, spdx-json, github)",
EnvVars: []string{"TRIVY_SBOM_FORMAT"},
},
},
Expand Down
37 changes: 37 additions & 0 deletions pkg/commands/artifact/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
)

Expand Down Expand Up @@ -208,6 +209,23 @@ func TestOption_Init(t *testing.T) {
},
},
},
{
name: "json and list all packages",
args: []string{"--format", "json", "--list-all-pkgs", "gitlab/gitlab-ce:12.7.2-ce.0"},
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Format: "json",
ListAllPkgs: true,
},
ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
},
},
{
name: "invalid option combination: --format template without --template",
args: []string{"--format", "template", "--severity", "MEDIUM", "gitlab/gitlab-ce:12.7.2-ce.0"},
Expand All @@ -227,6 +245,24 @@ func TestOption_Init(t *testing.T) {
},
},
},
{
name: "github enables list-all-pkgs",
args: []string{"--format", "github", "alpine:3.15"},
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Format: report.FormatGitHub,
ListAllPkgs: true,
},
ArtifactOption: option.ArtifactOption{
Target: "alpine:3.15",
},
},
},

{
name: "sad: skip and download db",
args: []string{"--skip-db-update", "--download-db-only", "alpine:3.10"},
Expand All @@ -253,6 +289,7 @@ func TestOption_Init(t *testing.T) {
set.Bool("reset", false, "")
set.Bool("skip-db-update", false, "")
set.Bool("download-db-only", false, "")
set.Bool("list-all-pkgs", false, "")
set.String("severity", "CRITICAL", "")
set.String("vuln-type", "os,library", "")
set.String("security-checks", "vuln", "")
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/option/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (c *ReportOption) populateSecurityChecks() error {

func (c *ReportOption) forceListAllPkgs(logger *zap.SugaredLogger) bool {
if slices.Contains(supportedSbomFormats, c.Format) && !c.ListAllPkgs {
logger.Debugf("'cyclonedx', 'spdx', and 'spdx-json' automatically enables '--list-all-pkgs'.")
logger.Debugf("'github', 'cyclonedx', 'spdx', and 'spdx-json' automatically enables '--list-all-pkgs'.")
return true
}
return false
Expand Down
7 changes: 4 additions & 3 deletions pkg/commands/option/report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"go.uber.org/zap/zaptest/observer"

dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
)

Expand Down Expand Up @@ -78,15 +79,15 @@ func TestReportReportConfig_Init(t *testing.T) {
severities: "CRITICAL",
vulnType: "os,library",
securityChecks: "vuln",
Format: "cyclonedx",
Format: report.FormatCycloneDX,
listAllPksgs: true,
},
args: []string{"centos:7"},
want: ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Format: "cyclonedx",
Format: report.FormatCycloneDX,
Output: os.Stdout,
ListAllPkgs: true,
},
Expand All @@ -103,7 +104,7 @@ func TestReportReportConfig_Init(t *testing.T) {
},
args: []string{"centos:7"},
logs: []string{
"'cyclonedx', 'spdx', and 'spdx-json' automatically enables '--list-all-pkgs'.",
"'github', 'cyclonedx', 'spdx', and 'spdx-json' automatically enables '--list-all-pkgs'.",
"Severities: CRITICAL",
},
want: ReportOption{
Expand Down
7 changes: 4 additions & 3 deletions pkg/commands/option/sbom.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package option

import (
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"golang.org/x/exp/slices"
"golang.org/x/xerrors"

"github.com/urfave/cli/v2"
"go.uber.org/zap"
"github.com/aquasecurity/trivy/pkg/report"
)

var supportedSbomFormats = []string{"cyclonedx", "spdx", "spdx-json"}
var supportedSbomFormats = []string{report.FormatCycloneDX, report.FormatSPDX, report.FormatSPDXJSON, report.FormatGitHub}

// SbomOption holds the options for SBOM generation
type SbomOption struct {
Expand Down
21 changes: 19 additions & 2 deletions pkg/purl/purl.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,14 @@ func NewPackageURL(t string, metadata types.Metadata, pkg ftypes.Package) (Packa
qualifiers = append(qualifiers, qs...)
case packageurl.TypeDebian:
qualifiers = append(qualifiers, parseDeb(metadata.OS)...)
namespace = metadata.OS.Family
if metadata.OS != nil {
namespace = metadata.OS.Family
}
case string(analyzer.TypeApk): // TODO: replace with packageurl.TypeApk once they add it.
qualifiers = append(qualifiers, parseApk(metadata.OS)...)
namespace = metadata.OS.Family
if metadata.OS != nil {
namespace = metadata.OS.Family
}
case packageurl.TypeMaven:
namespace, name = parseMaven(name)
case packageurl.TypePyPi:
Expand Down Expand Up @@ -119,6 +123,10 @@ func parseOCI(metadata types.Metadata) (packageurl.PackageURL, error) {
}

func parseApk(fos *ftypes.OS) packageurl.Qualifiers {
if fos == nil {
return packageurl.Qualifiers{}
}

return packageurl.Qualifiers{
{
Key: "distro",
Expand All @@ -129,6 +137,11 @@ func parseApk(fos *ftypes.OS) packageurl.Qualifiers {

// ref. https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#deb
func parseDeb(fos *ftypes.OS) packageurl.Qualifiers {

if fos == nil {
return packageurl.Qualifiers{}
}

distro := fmt.Sprintf("%s-%s", fos.Family, fos.Name)
return packageurl.Qualifiers{
{
Expand All @@ -140,6 +153,10 @@ func parseDeb(fos *ftypes.OS) packageurl.Qualifiers {

// ref. https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#rpm
func parseRPM(fos *ftypes.OS, modularityLabel string) (string, packageurl.Qualifiers) {
if fos == nil {
return "", packageurl.Qualifiers{}
}

// SLES string has whitespace
family := fos.Family
if fos.Family == os.SLES {
Expand Down
Loading

0 comments on commit 4ab696e

Please sign in to comment.