Skip to content

Commit

Permalink
chore: merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
junczhu committed Dec 7, 2023
2 parents c8fa6bf + 2a2cfd3 commit 2ff1060
Show file tree
Hide file tree
Showing 18 changed files with 2,770 additions and 47 deletions.
28 changes: 20 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ ALPINE_IMAGE_VULNERABLE ?= alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81
REDIS_IMAGE_TAG ?= 7.0-debian-11
CERT_ROTATION_ENABLED ?= false
REGO_POLICY_ENABLED ?= false
SBOM_TOOL_VERSION ?=v1.2.0
SBOM_TOOL_VERSION ?=v2.0.0
TRIVY_VERSION ?= 0.47.0

# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
Expand Down Expand Up @@ -366,6 +366,9 @@ e2e-sbom-setup:

# Install sbom-tool
curl -Lo .staging/sbom/sbom-tool https://github.com/microsoft/sbom-tool/releases/download/${SBOM_TOOL_VERSION}/sbom-tool-linux-x64 && chmod +x .staging/sbom/sbom-tool

# Install syft
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b .staging/sbom ${SYFT_VERSION}

# Build/Push Images
printf 'FROM ${ALPINE_IMAGE}\nCMD ["echo", "sbom image"]' > .staging/sbom/Dockerfile
Expand All @@ -374,25 +377,34 @@ e2e-sbom-setup:
printf 'FROM ${ALPINE_IMAGE}\nCMD ["echo", "sbom image unsigned"]' > .staging/sbom/Dockerfile
docker build --no-cache -t ${TEST_REGISTRY}/sbom:unsigned .staging/sbom
docker push ${TEST_REGISTRY}/sbom:unsigned

# Generate/Attach sbom
.staging/sbom/sbom-tool generate -b .staging/sbom -bc . -pn ratify -m .staging/sbom -pv 1.0 -ps acme -nsu ratify -nsb http://registry:5000 -D true
# -b (BuildDropPath) - The folder to save the generated SPDX SBOM manifests to
# -nsb (NamespaceUriBase) - The base path that will be used as the SBOM manifest's namespace. This should be a URL that's owned by your organization
# -pn (PackageName) - The name of the package that will be used in the SBOM manifest
# -pv (PackageVersion) - The version of the package that will be used in the SBOM manifest
# -ps (PackageSupplier) - The supplier of the package that will be used in the SBOM manifest
# -nsu (NamespaceUri) - The namespace that will be used as the SBOM manifest's namespace. This should be a URL that's owned by your organization
# -di (DockerImage) - The docker image that will be used to generate the SBOM manifest
# -m (ManifestPath) - The path to the SBOM manifest that will be generated
# -D (Debug) - Enable debug logging
.staging/sbom/sbom-tool generate -b .staging/sbom -pn ratify -di ${TEST_REGISTRY}/sbom:v0 -m .staging/sbom -pv 1.0 -ps acme -nsu ratify -nsb http://registry:5000 -D true
${GITHUB_WORKSPACE}/bin/oras attach \
--artifact-type org.example.sbom.v0 \
--artifact-type application/spdx+json \
${TEST_REGISTRY}/sbom:v0 \
.staging/sbom/_manifest/spdx_2.2/manifest.spdx.json:application/spdx+json
${GITHUB_WORKSPACE}/bin/oras attach \
--artifact-type org.example.sbom.v0 \
--artifact-type application/spdx+json \
${TEST_REGISTRY}/sbom:unsigned \
.staging/sbom/_manifest/spdx_2.2/manifest.spdx.json:application/spdx+json
${GITHUB_WORKSPACE}/bin/oras attach \
--artifact-type org.example.sbom.v0 \
--artifact-type application/spdx+json \
${TEST_REGISTRY}/all:v0 \
.staging/sbom/_manifest/spdx_2.2/manifest.spdx.json:application/spdx+json

# Push Signature to sbom
.staging/notation/notation sign -u ${TEST_REGISTRY_USERNAME} -p ${TEST_REGISTRY_PASSWORD} ${TEST_REGISTRY}/sbom@`oras discover -o json --artifact-type org.example.sbom.v0 ${TEST_REGISTRY}/sbom:v0 | jq -r ".manifests[0].digest"`
.staging/notation/notation sign -u ${TEST_REGISTRY_USERNAME} -p ${TEST_REGISTRY_PASSWORD} ${TEST_REGISTRY}/all@`oras discover -o json --artifact-type org.example.sbom.v0 ${TEST_REGISTRY}/all:v0 | jq -r ".manifests[0].digest"`
.staging/notation/notation sign -u ${TEST_REGISTRY_USERNAME} -p ${TEST_REGISTRY_PASSWORD} ${TEST_REGISTRY}/sbom@`oras discover -o json --artifact-type application/spdx+json ${TEST_REGISTRY}/sbom:v0 | jq -r ".manifests[0].digest"`
.staging/notation/notation sign -u ${TEST_REGISTRY_USERNAME} -p ${TEST_REGISTRY_PASSWORD} ${TEST_REGISTRY}/all@`oras discover -o json --artifact-type application/spdx+json ${TEST_REGISTRY}/all:v0 | jq -r ".manifests[0].digest"`

e2e-schemavalidator-setup:
rm -rf .staging/schemavalidator
Expand Down
2 changes: 1 addition & 1 deletion config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
},
{
"name": "sbom",
"artifactTypes": "org.example.sbom.v0",
"artifactTypes": "application/spdx+json",
"nestedReferences": "application/vnd.cncf.notary.signature"
},
{
Expand Down
2 changes: 1 addition & 1 deletion config/samples/config_v1beta1_verifier_sbom.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ metadata:
name: verifier-sbom
spec:
name: sbom
artifactTypes: org.example.sbom.v0
artifactTypes: application/spdx+json
parameters:
nestedReferences: application/vnd.cncf.notary.signature
12 changes: 6 additions & 6 deletions pkg/policyprovider/configpolicy/configpolicy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestPolicyEnforcer_ContinueVerifyOnFailure(t *testing.T) {
"name": "configPolicy",
"artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{
"application/vnd.cncf.notary.signature": "any",
"org.example.sbom.v0": "all",
"application/spdx+json": "all",
"default": "any",
},
}
Expand Down Expand Up @@ -71,7 +71,7 @@ func TestPolicyEnforcer_ContinueVerifyOnFailure(t *testing.T) {
t.Fatalf("For policy of 'any' PolicyEnforcer should allow continuing on verify failure")
}

referenceDesc.ArtifactType = "org.example.sbom.v0"
referenceDesc.ArtifactType = "application/spdx+json"

check = policyEnforcer.ContinueVerifyOnFailure(ctx, subjectReference, referenceDesc, result)

Expand Down Expand Up @@ -243,7 +243,7 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) {
"name": "configPolicy",
"artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{
"application/vnd.cncf.notary.signature": "any",
"org.example.sbom.v0": "any",
"application/spdx+json": "any",
},
},
verifierReports: []interface{}{
Expand All @@ -255,7 +255,7 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) {
vr.VerifierResult{
Subject: "",
IsSuccess: false,
ArtifactType: "org.example.sbom.v0",
ArtifactType: "application/spdx+json",
},
},
output: false,
Expand All @@ -266,7 +266,7 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) {
"name": "configPolicy",
"artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{
"application/vnd.cncf.notary.signature": "any",
"org.example.sbom.v0": "all",
"application/spdx+json": "all",
},
},
verifierReports: []interface{}{
Expand All @@ -278,7 +278,7 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) {
vr.VerifierResult{
Subject: "",
IsSuccess: true,
ArtifactType: "org.example.sbom.v0",
ArtifactType: "application/spdx+json",
},
},
output: true,
Expand Down
2 changes: 1 addition & 1 deletion pkg/referrerstore/mocks/memory_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func CreateNewTestStoreForNestedSbom() referrerstore.ReferrerStore {

const (
TestSubjectWithDigest = "localhost:5000/net-monitor:v1@sha256:b556844e6e59451caf4429eb1de50aa7c50e4b1cc985f9f5893affe4b73f9935"
SbomArtifactType = "org.example.sbom.v0"
SbomArtifactType = "application/spdx+json"
SignatureArtifactType = "application/vnd.cncf.notary.signature"
dockerMediaType = "application/vnd.docker.distribution.manifest.v2+json"
artifactMediaType = "application/vnd.oci.artifact.manifest.v1+json"
Expand Down
148 changes: 124 additions & 24 deletions plugins/verifier/sbom/sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,41 +20,43 @@ import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/deislabs/ratify/pkg/common"
"github.com/deislabs/ratify/pkg/ocispecs"
"github.com/deislabs/ratify/pkg/referrerstore"
"github.com/deislabs/ratify/plugins/verifier/sbom/utils"

// This import is required to utilize the oras built-in referrer store
_ "github.com/deislabs/ratify/pkg/referrerstore/oras"
"github.com/deislabs/ratify/pkg/verifier"
"github.com/deislabs/ratify/pkg/verifier/plugin/skel"

jsonLoader "github.com/spdx/tools-golang/json"
"github.com/spdx/tools-golang/spdx"
"github.com/spdx/tools-golang/spdx/v2/v2_3"
)

// PluginConfig describes the configuration of the sbom verifier
type PluginConfig struct {
Name string `json:"name"`
Type string `json:"type"`
Name string `json:"name"`
Type string `json:"type"`
DisallowedLicenses []string `json:"disallowedLicenses,omitempty"`
DisallowedPackages []utils.PackageInfo `json:"disallowedPackages,omitempty"`
}

type PluginInputConfig struct {
Config PluginConfig `json:"config"`
}

type PackageInfo struct {
Name string `json:"name,omitempty"`
Version string `json:"versionInfo,omitempty"`
}

const (
SpdxJSONMediaType string = "application/spdx+json"
CreationInfo string = "creationInfo"
LicenseViolation string = "licenseViolations"
PackageViolation string = "packageViolations"
)

func main() {
skel.PluginMain("sbom", "1.0.0", VerifyReference, []string{"1.0.0"})
skel.PluginMain("sbom", "2.0.0-alpha.1", VerifyReference, []string{"1.0.0", "2.0.0-alpha.1"})
}

func parseInput(stdin []byte) (*PluginConfig, error) {
Expand Down Expand Up @@ -88,9 +90,16 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe
}, err
}

var mediaType string
if len(referenceManifest.Blobs) == 0 {
return &verifier.VerifierResult{
Name: input.Name,
IsSuccess: false,
Message: fmt.Sprintf("SBOM validation failed: no layers found in manifest for referrer %s@%s", subjectReference.Path, referenceDescriptor.Digest.String()),
}, nil
}

artifactType := referenceDescriptor.ArtifactType
for _, blobDesc := range referenceManifest.Blobs {
mediaType = blobDesc.MediaType
refBlob, err := referrerStore.GetBlobContent(ctx, subjectReference, blobDesc.Digest)

if err != nil {
Expand All @@ -102,32 +111,90 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe
}, err
}

switch mediaType {
switch artifactType {
case SpdxJSONMediaType:
return processSpdxJSONMediaType(input.Name, verifierType, refBlob)
return processSpdxJSONMediaType(input.Name, verifierType, refBlob, input.DisallowedLicenses, input.DisallowedPackages)
default:
return &verifier.VerifierResult{
Name: input.Name,
Type: verifierType,

Check warning on line 120 in plugins/verifier/sbom/sbom.go

View check run for this annotation

Codecov / codecov/patch

plugins/verifier/sbom/sbom.go#L120

Added line #L120 was not covered by tests
IsSuccess: false,
Message: fmt.Sprintf("Unsupported artifactType: %s", artifactType),
}, nil
}
}

return &verifier.VerifierResult{
Name: input.Name,
Type: verifierType,

Check warning on line 129 in plugins/verifier/sbom/sbom.go

View check run for this annotation

Codecov / codecov/patch

plugins/verifier/sbom/sbom.go#L129

Added line #L129 was not covered by tests
IsSuccess: false,
Message: fmt.Sprintf("Unsupported mediaType: %s", mediaType),
IsSuccess: true,
Message: "SBOM verification success. No license or package violation found.",
}, nil
}

func processSpdxJSONMediaType(name string, verifierType string, refBlob []byte) (*verifier.VerifierResult, error) {
// getViolations returns the package and license violations based on the deny list
func getViolations(spdxDoc *spdx.Document, disallowedLicenses []string, disallowedPackages []utils.PackageInfo) ([]utils.PackageLicense, []utils.PackageLicense) {
packageLicenses := utils.GetPackageLicenses(*spdxDoc)
// load disallowed packageInfo into a map for easier existence check
packageMap, packageNameMap := loadDisallowedPackagesMap(disallowedPackages)

// detect violation
licenseViolation, packageViolation := filterDisallowedPackages(packageLicenses, disallowedLicenses, packageMap, packageNameMap)
return packageViolation, licenseViolation
}

// load disallowed packageInfo, and disallowed packageName into a map for easier existence check
func loadDisallowedPackagesMap(packages []utils.PackageInfo) (map[utils.PackageInfo]struct{}, map[string]struct{}) {
packagesInfo := map[utils.PackageInfo]struct{}{}
packagesName := map[string]struct{}{}

for _, item := range packages {
// if the deny list item has no specific version, add to separate map
if len(item.Version) == 0 {
packagesName[item.Name] = struct{}{}
}
packagesInfo[item] = struct{}{}
}
return packagesInfo, packagesName
}

// parse through the spdx blob and returns the verifier result
func processSpdxJSONMediaType(name string, verifierType string, refBlob []byte, disallowedLicenses []string, disallowedPackages []utils.PackageInfo) (*verifier.VerifierResult, error) {
var err error
var doc *v2_3.Document
if doc, err = jsonLoader.Read(bytes.NewReader(refBlob)); doc != nil {
var spdxDoc *v2_3.Document
if spdxDoc, err = jsonLoader.Read(bytes.NewReader(refBlob)); spdxDoc != nil && err == nil {
if len(disallowedLicenses) != 0 || len(disallowedPackages) != 0 {
packageViolation, licenseViolation := getViolations(spdxDoc, disallowedLicenses, disallowedPackages)

var extensionData = make(map[string]interface{})
extensionData[CreationInfo] = spdxDoc.CreationInfo
if len(licenseViolation) != 0 {
extensionData[LicenseViolation] = licenseViolation
}

if len(packageViolation) != 0 {
extensionData[PackageViolation] = packageViolation
}

if len(licenseViolation) != 0 || len(packageViolation) != 0 {
return &verifier.VerifierResult{
Name: name,
IsSuccess: false,
Extensions: extensionData,
Message: "SBOM validation failed.",
}, err
}
}

return &verifier.VerifierResult{
Name: name,
Type: verifierType,
IsSuccess: true,
Extensions: doc.CreationInfo,
Message: "SBOM verification success. The schema is good.",
}, err
Name: name,
Type: verifierType,
IsSuccess: true,
Extensions: map[string]interface{}{
CreationInfo: spdxDoc.CreationInfo,
},
Message: "SBOM verification success. No license or package violation found.",
}, nil
}
return &verifier.VerifierResult{
Name: name,
Expand All @@ -136,3 +203,36 @@ func processSpdxJSONMediaType(name string, verifierType string, refBlob []byte)
Message: fmt.Sprintf("SBOM failed to parse: %v", err),
}, err
}

// iterate through all package info and check against the deny list
// return the violation packages
func filterDisallowedPackages(packageLicenses []utils.PackageLicense, disallowedLicense []string, disallowedPackage map[utils.PackageInfo]struct{}, disallowedPackageName map[string]struct{}) ([]utils.PackageLicense, []utils.PackageLicense) {
var violationLicense []utils.PackageLicense
var violationPackage []utils.PackageLicense

for _, packageInfo := range packageLicenses {
// if license contains disallowed, add to violation
for _, disallowed := range disallowedLicense {
license := packageInfo.License
if license != "" && strings.Contains(strings.ToLower(license), strings.ToLower(disallowed)) {
violationLicense = append(violationLicense, packageInfo)
}
}

current := utils.PackageInfo{
Name: packageInfo.Name,
Version: packageInfo.Version,
}

// check if this package is in the deny list by package name
if _, ok := disallowedPackageName[current.Name]; ok {
violationPackage = append(violationPackage, packageInfo)
}

// check if this package is in the deny list by matching name and version
if _, ok := disallowedPackage[current]; ok {
violationPackage = append(violationPackage, packageInfo)
}
}
return violationLicense, violationPackage
}
Loading

0 comments on commit 2ff1060

Please sign in to comment.