Skip to content

Commit

Permalink
Fetch vulnerabilities if SBOM doesn't provide them
Browse files Browse the repository at this point in the history
Signed-off-by: felipecruz91 <[email protected]>
  • Loading branch information
felipecruz91 committed Feb 13, 2024
1 parent a4f20cd commit 7c5e2df
Show file tree
Hide file tree
Showing 15 changed files with 176 additions and 90 deletions.
1 change: 1 addition & 0 deletions policy/data/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package data
import (
"context"
"fmt"

"olympos.io/encoding/edn"
)

Expand Down
75 changes: 0 additions & 75 deletions policy/policy_handler/legacy/builder.go

This file was deleted.

1 change: 1 addition & 0 deletions policy/policy_handler/legacy/image_packages_by_digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package legacy

import (
"context"

"github.com/atomist-skills/go-skill"
"github.com/atomist-skills/go-skill/policy/data"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package legacy

import (
"context"
"testing"

"github.com/atomist-skills/go-skill"
"github.com/atomist-skills/go-skill/internal/test_util"
"github.com/atomist-skills/go-skill/policy/data"
"github.com/stretchr/testify/assert"
"testing"
)

type MockDs struct {
Expand Down
11 changes: 8 additions & 3 deletions policy/policy_handler/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"

"github.com/atomist-skills/go-skill"
"github.com/atomist-skills/go-skill/policy/data"
"github.com/atomist-skills/go-skill/policy/goals"
"github.com/atomist-skills/go-skill/policy/policy_handler/legacy"
"github.com/atomist-skills/go-skill/policy/policy_handler/mocks"
"github.com/atomist-skills/go-skill/policy/types"
"io"
"olympos.io/encoding/edn"
)

Expand Down Expand Up @@ -84,11 +86,14 @@ func buildLocalDataSources(ctx context.Context, req skill.RequestContext, _ goal
}

var sbom *types.SBOM
// THE SBOM is a JSON here, not edn?!!
// THE SBOM is a JSON here, not edn
if err := json.Unmarshal(decodedSBOM, &sbom); err != nil {
return nil, fmt.Errorf("failed to unmarshal SBOM: %w", err)
}
srMeta.QueryResults = legacy.BuildLocalEvalMocks(sbom, req.Log)
srMeta.QueryResults, err = mocks.BuildLocalEvalMocks(ctx, req, sbom)
if err != nil {
return nil, fmt.Errorf("failed to build local evaluation mocks: %w", err)
}
}

fixedQueryResults := map[string][]byte{}
Expand Down
50 changes: 50 additions & 0 deletions policy/policy_handler/mocks/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package mocks

import (
"context"
"fmt"

"github.com/atomist-skills/go-skill"
"github.com/atomist-skills/go-skill/policy/policy_handler/legacy"
"github.com/atomist-skills/go-skill/policy/types"
"olympos.io/encoding/edn"
)

func BuildLocalEvalMocks(ctx context.Context, req skill.RequestContext, sb *types.SBOM) (map[edn.Keyword]edn.RawMessage, error) {
m := map[edn.Keyword]edn.RawMessage{}
if sb == nil {
req.Log.Info("No SBOM provided, returning empty map")
return m, nil
}

// Image packages by digest
imgPkgsMock, err := MockImagePackagesByDigest(ctx, req, sb)
if err != nil {
return m, err
}
m[legacy.ImagePackagesByDigestQueryName], err = edn.Marshal(imgPkgsMock)
if err != nil {
return m, fmt.Errorf("failed to marshal image packages by digest mock: %w", err)
}

// User
if sb.Source.Image != nil && sb.Source.Image.Config != nil {
userMock := MockGetUser(sb.Source.Image.Config.Config.User)
m[GetUserQueryName], err = edn.Marshal(userMock)
if err != nil {
return m, fmt.Errorf("failed to marshal get user mock: %w", err)
}
}

// Attestations
req.Log.Infof("SBOM has %d attestations", len(sb.Attestations))
if len(sb.Attestations) > 0 {
attestMock := MockGetInTotoAttestations(sb, req.Log)
m[GetInTotoAttestationsQueryName], err = edn.Marshal(attestMock)
if err != nil {
return m, fmt.Errorf("failed to marshal attestations mock: %w", err)
}
}

return m, nil
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package legacy
package mocks

import (
"context"
"reflect"
"testing"

Expand All @@ -11,6 +12,17 @@ import (
)

func Test_BuildLocalEvalMocks(t *testing.T) {
vulns := []types.VulnerabilitiesByPurl{
{
Purl: "pkg1",
Vulnerabilities: []types.Vulnerability{
{
Source: "source",
},
},
},
}

type args struct {
sb *types.SBOM
}
Expand Down Expand Up @@ -44,6 +56,7 @@ func Test_BuildLocalEvalMocks(t *testing.T) {
Name: "pkg1",
},
},
Vulnerabilities: vulns,
},
},
want: map[edn.Keyword]edn.RawMessage{
Expand All @@ -63,6 +76,7 @@ func Test_BuildLocalEvalMocks(t *testing.T) {
Name: "pkg1",
},
},
Vulnerabilities: vulns,
},
},
want: map[edn.Keyword]edn.RawMessage{
Expand All @@ -83,6 +97,7 @@ func Test_BuildLocalEvalMocks(t *testing.T) {
Name: "pkg1",
},
},
Vulnerabilities: vulns,
},
},
want: map[edn.Keyword]edn.RawMessage{
Expand All @@ -91,16 +106,23 @@ func Test_BuildLocalEvalMocks(t *testing.T) {
},
}

logger := skill.Logger{
Info: func(msg string) {},
Infof: func(format string, a ...any) {
req := skill.RequestContext{
Log: skill.Logger{
Info: func(msg string) {},
Infof: func(format string, a ...any) {

},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := BuildLocalEvalMocks(tt.args.sb, logger); !reflect.DeepEqual(got, tt.want) {
got, err := BuildLocalEvalMocks(context.Background(), req, tt.args.sb)
if err != nil {
t.Error(err)
}

if !reflect.DeepEqual(got, tt.want) {
t.Errorf("BuildLocalEvalMocks() = %v, want %v", got, tt.want)
}
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package legacy
package mocks

const (
GetUserQueryName = "get-user"
Expand All @@ -8,7 +8,7 @@ type DockerImageUser struct {
ImageUser string `edn:"docker.image/user,omitempty"`
}

func MockGetUserForLocalEval(user string) DockerImageUser {
func MockGetUser(user string) DockerImageUser {
return DockerImageUser{
ImageUser: user,
}
Expand Down
79 changes: 79 additions & 0 deletions policy/policy_handler/mocks/image_packages_by_digest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package mocks

import (
"context"

"github.com/atomist-skills/go-skill"
"github.com/atomist-skills/go-skill/policy/policy_handler/legacy"
"github.com/atomist-skills/go-skill/policy/types"
)

func MockImagePackagesByDigest(ctx context.Context, req skill.RequestContext, sb *types.SBOM) (legacy.ImagePackagesByDigestResponse, error) {
req.Log.Info("Building local evaluation mocks for image packages by digest")

if len(sb.Vulnerabilities) == 0 {
req.Log.Info("SBOM doesn't provide any vulnerabilities directly, fetching them from GraphQL")

var pkgs []legacy.Package
for _, a := range sb.Artifacts {
pkgs = append(pkgs, legacy.Package{
Type: a.Type,
Namespace: a.Namespace,
Name: a.Name,
Version: a.Version,
Purl: a.Purl,
Licenses: a.Licenses,
})
}
res, err := legacy.MockImagePackagesByDigest(ctx, req, pkgs)
if err != nil {
return res, err
}
return res, nil
}

req.Log.Info("SBOM provides vulnerabilities")
vulns := map[string][]legacy.Vulnerability{}
for _, tuple := range sb.Vulnerabilities {
vulnsForPurl := []legacy.Vulnerability{}
for _, v := range tuple.Vulnerabilities {
vulnsForPurl = append(vulnsForPurl, legacy.Vulnerability{
Cvss: legacy.Cvss{
Severity: &v.Cvss.Severity,
Score: &v.Cvss.Score,
},
PublishedAt: v.PublishedAt,
UpdatedAt: v.UpdatedAt,
FixedBy: &v.FixedBy,
Source: v.Source,
SourceID: v.SourceId,
URL: &v.Url,
VulnerableRange: v.VulnerableRange,
})
}
vulns[tuple.Purl] = vulnsForPurl
}

pkgs := []legacy.Packages{}
for _, a := range sb.Artifacts {
pkgs = append(pkgs, legacy.Packages{
Package: legacy.PackageWithLicenses{
Licenses: a.Licenses,
Name: a.Name,
Namespace: &a.Namespace,
Version: a.Version,
Purl: a.Purl,
Type: a.Type,
Vulnerabilities: vulns[a.Purl],
},
})
}

return legacy.ImagePackagesByDigestResponse{
ImagePackagesByDigest: &legacy.ImagePackagesByDigest{
ImagePackages: legacy.ImagePackages{
Packages: pkgs,
},
},
}, nil
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package legacy
package mocks

import (
"encoding/base64"
Expand Down Expand Up @@ -39,7 +39,7 @@ type Predicate struct {

// https://github.com/in-toto/attestation/blob/main/spec/README.md
// https://github.com/secure-systems-lab/dsse/blob/master/envelope.md
func MockGetInTotoAttestationsForLocalEval(sb *types.SBOM, log skill.Logger) ImageAttestationQueryResult {
func MockGetInTotoAttestations(sb *types.SBOM, log skill.Logger) ImageAttestationQueryResult {
subjects := []Subject{}

// The envelope is the outtermost layer of the attestation
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package legacy
package mocks

import (
"encoding/base64"
Expand Down Expand Up @@ -104,7 +104,7 @@ func TestMockGetInTotoAttestationsForLocalEval(t *testing.T) {
logger := skill.Logger{
Infof: func(format string, a ...any) {},
}
if got := MockGetInTotoAttestationsForLocalEval(tt.args.sb, logger); !reflect.DeepEqual(got, tt.want) {
if got := MockGetInTotoAttestations(tt.args.sb, logger); !reflect.DeepEqual(got, tt.want) {
t.Errorf("MockGetInTotoAttestationsForLocalEval() = %v, want %v", got, tt.want)
}
})
Expand Down
Loading

0 comments on commit 7c5e2df

Please sign in to comment.