...

Source file src/github.com/sigstore/cosign/v2/pkg/policy/attestation.go

Documentation: github.com/sigstore/cosign/v2/pkg/policy

     1  //
     2  // Copyright 2022 The Sigstore Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package policy
    17  
    18  import (
    19  	"context"
    20  	"encoding/base64"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  
    25  	"github.com/in-toto/in-toto-golang/in_toto"
    26  	"github.com/sigstore/cosign/v2/pkg/oci"
    27  
    28  	"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
    29  	"github.com/sigstore/cosign/v2/pkg/cosign/attestation"
    30  )
    31  
    32  // AttestationToPayloadJSON takes in a verified Attestation (oci.Signature) and
    33  // marshals it into a JSON depending on the payload that's then consumable
    34  // by policy engine like cue, rego, etc.
    35  //
    36  // Anything fed here must have been validated with either
    37  // `VerifyLocalImageAttestations` or `VerifyImageAttestations`
    38  //
    39  // If there's no error, and payload is empty means the predicateType did not
    40  // match the attestation.
    41  // Returns the attestation type (PredicateType) if the payload was decoded
    42  // before the error happened, or in the case the predicateType that was
    43  // requested does not match. This is useful for callers to be able to provide
    44  // better error messages. For example, if there's a typo in the predicateType,
    45  // or the predicateType is not the one they are looking for. Without returning
    46  // this, it's hard for users to know which attestations/predicateTypes were
    47  // inspected.
    48  func AttestationToPayloadJSON(_ context.Context, predicateType string, verifiedAttestation oci.Signature) ([]byte, string, error) {
    49  	if predicateType == "" {
    50  		return nil, "", errors.New("missing predicate type")
    51  	}
    52  	predicateURI, ok := options.PredicateTypeMap[predicateType]
    53  	if !ok {
    54  		// Not a custom one, use it as is.
    55  		predicateURI = predicateType
    56  	}
    57  	var payloadData map[string]interface{}
    58  
    59  	p, err := verifiedAttestation.Payload()
    60  	if err != nil {
    61  		return nil, "", fmt.Errorf("getting payload: %w", err)
    62  	}
    63  
    64  	err = json.Unmarshal(p, &payloadData)
    65  	if err != nil {
    66  		return nil, "", fmt.Errorf("unmarshaling payload data")
    67  	}
    68  
    69  	var decodedPayload []byte
    70  	if val, ok := payloadData["payload"]; ok {
    71  		decodedPayload, err = base64.StdEncoding.DecodeString(val.(string))
    72  		if err != nil {
    73  			return nil, "", fmt.Errorf("decoding payload: %w", err)
    74  		}
    75  	} else {
    76  		return nil, "", fmt.Errorf("could not find payload in payload data")
    77  	}
    78  
    79  	// Only apply the policy against the requested predicate type
    80  	var statement in_toto.Statement
    81  	if err := json.Unmarshal(decodedPayload, &statement); err != nil {
    82  		return nil, "", fmt.Errorf("unmarshal in-toto statement: %w", err)
    83  	}
    84  	if statement.PredicateType != predicateURI {
    85  		// This is not the predicate we're looking for, so skip it.
    86  		return nil, statement.PredicateType, nil
    87  	}
    88  
    89  	// NB: In many (all?) of these cases, we could just return the
    90  	// 'json.Marshal', but we check for errors here to decorate them
    91  	// with more meaningful error message.
    92  	var payload []byte
    93  	switch predicateType {
    94  	case options.PredicateCustom:
    95  		payload, err = json.Marshal(statement)
    96  		if err != nil {
    97  			return nil, statement.PredicateType, fmt.Errorf("generating CosignStatement: %w", err)
    98  		}
    99  	case options.PredicateLink:
   100  		var linkStatement in_toto.LinkStatement
   101  		if err := json.Unmarshal(decodedPayload, &linkStatement); err != nil {
   102  			return nil, statement.PredicateType, fmt.Errorf("unmarshaling LinkStatement: %w", err)
   103  		}
   104  		payload, err = json.Marshal(linkStatement)
   105  		if err != nil {
   106  			return nil, statement.PredicateType, fmt.Errorf("marshaling LinkStatement: %w", err)
   107  		}
   108  	case options.PredicateSLSA:
   109  		var slsaProvenanceStatement in_toto.ProvenanceStatementSLSA02
   110  		if err := json.Unmarshal(decodedPayload, &slsaProvenanceStatement); err != nil {
   111  			return nil, statement.PredicateType, fmt.Errorf("unmarshaling ProvenanceStatementSLSA02): %w", err)
   112  		}
   113  		payload, err = json.Marshal(slsaProvenanceStatement)
   114  		if err != nil {
   115  			return nil, statement.PredicateType, fmt.Errorf("marshaling ProvenanceStatementSLSA02: %w", err)
   116  		}
   117  	case options.PredicateSPDX, options.PredicateSPDXJSON:
   118  		var spdxStatement in_toto.SPDXStatement
   119  		if err := json.Unmarshal(decodedPayload, &spdxStatement); err != nil {
   120  			return nil, statement.PredicateType, fmt.Errorf("unmarshaling SPDXStatement: %w", err)
   121  		}
   122  		payload, err = json.Marshal(spdxStatement)
   123  		if err != nil {
   124  			return nil, statement.PredicateType, fmt.Errorf("marshaling SPDXStatement: %w", err)
   125  		}
   126  	case options.PredicateCycloneDX:
   127  		var cyclonedxStatement in_toto.CycloneDXStatement
   128  		if err := json.Unmarshal(decodedPayload, &cyclonedxStatement); err != nil {
   129  			return nil, statement.PredicateType, fmt.Errorf("unmarshaling CycloneDXStatement: %w", err)
   130  		}
   131  		payload, err = json.Marshal(cyclonedxStatement)
   132  		if err != nil {
   133  			return nil, statement.PredicateType, fmt.Errorf("marshaling CycloneDXStatement: %w", err)
   134  		}
   135  	case options.PredicateVuln:
   136  		var vulnStatement attestation.CosignVulnStatement
   137  		if err := json.Unmarshal(decodedPayload, &vulnStatement); err != nil {
   138  			return nil, statement.PredicateType, fmt.Errorf("unmarshaling CosignVulnStatement: %w", err)
   139  		}
   140  		payload, err = json.Marshal(vulnStatement)
   141  		if err != nil {
   142  			return nil, statement.PredicateType, fmt.Errorf("marshaling CosignVulnStatement: %w", err)
   143  		}
   144  	default:
   145  		// Valid URI type reaches here.
   146  		payload, err = json.Marshal(statement)
   147  		if err != nil {
   148  			return nil, statement.PredicateType, fmt.Errorf("generating Statement: %w", err)
   149  		}
   150  	}
   151  	return payload, statement.PredicateType, nil
   152  }
   153  

View as plain text