From 0a84ee99252a7e732d84870c5eaa69c73773ea1c Mon Sep 17 00:00:00 2001 From: Paul Norton Date: Tue, 13 Feb 2024 15:13:59 -0500 Subject: [PATCH] mock image platform and base image from sbom --- policy/policy_handler/local.go | 93 +++++++++++++++-------- policy/policy_handler/mocks/base_image.go | 34 ++++++++- policy/policy_handler/mocks/builder.go | 3 - 3 files changed, 94 insertions(+), 36 deletions(-) diff --git a/policy/policy_handler/local.go b/policy/policy_handler/local.go index b64d264..6197419 100644 --- a/policy/policy_handler/local.go +++ b/policy/policy_handler/local.go @@ -43,9 +43,26 @@ func getLocalSubscriptionData(_ context.Context, req skill.RequestContext) (*goa return nil, skill.Configuration{}, nil } - mockCommonSubscriptionData := goals.CommonSubscriptionQueryResult{ - ImageDigest: "localDigest", + _, sbom, err := parseMetadata(req) + if err != nil { + return nil, skill.Configuration{}, err } + + var mockCommonSubscriptionData goals.CommonSubscriptionQueryResult + if sbom != nil { + mockCommonSubscriptionData = goals.CommonSubscriptionQueryResult{ + ImageDigest: sbom.Source.Image.Digest, + ImagePlatforms: []goals.ImagePlatform{{ + Architecture: sbom.Source.Image.Platform.Architecture, + Os: sbom.Source.Image.Platform.Os, + }}, + } + } else { + mockCommonSubscriptionData = goals.CommonSubscriptionQueryResult{ + ImageDigest: "localDigest", + } + } + subscriptionData, err := edn.Marshal(mockCommonSubscriptionData) if err != nil { return nil, skill.Configuration{}, err @@ -61,39 +78,14 @@ func buildLocalDataSources(ctx context.Context, req skill.RequestContext, _ goal return []data.DataSource{}, nil } - var srMeta SyncRequestMetadata - err := edn.Unmarshal(req.Event.Context.SyncRequest.Metadata, &srMeta) + srMeta, sbom, err := parseMetadata(req) if err != nil { - return nil, fmt.Errorf("failed to unmarshal SyncRequest metadata: %w", err) + return nil, err } - if srMeta.SBOM != "" { - decodedSBOM, err := base64.StdEncoding.DecodeString(srMeta.SBOM) - if err != nil { - return nil, fmt.Errorf("failed to base64-decode SBOM: %w", err) - } - if srMeta.Encoding == "base64+gzip" { - reader := bytes.NewReader(decodedSBOM) - gzreader, err := gzip.NewReader(reader) - defer gzreader.Close() //nolint:errcheck - if err != nil { - return nil, fmt.Errorf("failed to decompress SBOM: %w", err) - } - decodedSBOM, err = io.ReadAll(gzreader) - if err != nil { - return nil, fmt.Errorf("failed to base64-decode SBOM: %w", err) - } - } - - var sbom *types.SBOM - // 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, err = mocks.BuildLocalEvalMocks(ctx, req, sbom) - if err != nil { - return nil, fmt.Errorf("failed to build local evaluation mocks: %w", err) - } + 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{} @@ -123,3 +115,40 @@ func buildLocalDataSources(ctx context.Context, req skill.RequestContext, _ goal func shouldTransactLocal(_ context.Context, req skill.RequestContext) bool { return req.Event.Context.SyncRequest.Name != eventNameLocalEval } + +func parseMetadata(req skill.RequestContext) (SyncRequestMetadata, *types.SBOM, error) { + var srMeta SyncRequestMetadata + err := edn.Unmarshal(req.Event.Context.SyncRequest.Metadata, &srMeta) + if err != nil { + return SyncRequestMetadata{}, nil, fmt.Errorf("failed to unmarshal SyncRequest metadata: %w", err) + } + + if srMeta.SBOM == "" { + return srMeta, nil, nil + } + + decodedSBOM, err := base64.StdEncoding.DecodeString(srMeta.SBOM) + if err != nil { + return srMeta, nil, fmt.Errorf("failed to base64-decode SBOM: %w", err) + } + if srMeta.Encoding == "base64+gzip" { + reader := bytes.NewReader(decodedSBOM) + gzreader, err := gzip.NewReader(reader) + defer gzreader.Close() //nolint:errcheck + if err != nil { + return srMeta, nil, fmt.Errorf("failed to decompress SBOM: %w", err) + } + decodedSBOM, err = io.ReadAll(gzreader) + if err != nil { + return srMeta, nil, fmt.Errorf("failed to base64-decode SBOM: %w", err) + } + } + + var sbom *types.SBOM + // THE SBOM is a JSON here, not edn + if err := json.Unmarshal(decodedSBOM, &sbom); err != nil { + return srMeta, nil, fmt.Errorf("failed to unmarshal SBOM: %w", err) + } + + return srMeta, sbom, nil +} diff --git a/policy/policy_handler/mocks/base_image.go b/policy/policy_handler/mocks/base_image.go index 37ae6ce..94ced1e 100644 --- a/policy/policy_handler/mocks/base_image.go +++ b/policy/policy_handler/mocks/base_image.go @@ -1,7 +1,9 @@ package mocks import ( + "fmt" "github.com/atomist-skills/go-skill/policy/types" + "strings" ) const ( @@ -29,11 +31,41 @@ func MockBaseImage(sb *types.SBOM) BaseImageQueryResult { FromReference: &SubscriptionImage{ Digest: sb.Source.Provenance.BaseImage.Digest, }, - FromRepo: nil, // TODO: this data is not present in the SBOM yet + FromRepo: parseFromReference(sb), FromTag: &sb.Source.Provenance.BaseImage.Tag, } } +func parseFromReference(sb *types.SBOM) *SubscriptionRepository { + // this is registry.com/namespace/repository form + // but minified (omits hub.docker.com and library/ if unnecessary) + fullName := sb.Source.Provenance.BaseImage.Name + if fullName == "" { + return nil + } + + parts := strings.SplitN(fullName, "/", 3) + switch len(parts) { + case 1: + return &SubscriptionRepository{ + Host: "hub.docker.com", + Repository: fmt.Sprintf("library/%s", parts[0]), + } + + case 2: + return &SubscriptionRepository{ + Host: "hub.docker.com", + Repository: fmt.Sprintf("%s/%s", parts[0], parts[1]), + } + + default: + return &SubscriptionRepository{ + Host: parts[0], + Repository: fmt.Sprintf("%s/%s", parts[1], parts[2]), + } + } +} + func MockSupportedTags(sb *types.SBOM) []string { return []string{} // TODO: query GraphQL for supported tags } diff --git a/policy/policy_handler/mocks/builder.go b/policy/policy_handler/mocks/builder.go index fa6eb9e..0b8bfe9 100644 --- a/policy/policy_handler/mocks/builder.go +++ b/policy/policy_handler/mocks/builder.go @@ -64,8 +64,5 @@ func BuildLocalEvalMocks(ctx context.Context, req skill.RequestContext, sb *type return m, fmt.Errorf("failed to marshal supported tags mock: %w", err) } - // TODO: Mock ImagePlatforms from CommonSubscriptionQueryResult (required for atomist/no-stale-base-images) - // TODO: Mock packages (required for atomist/license-goal) - return m, nil }