-
Notifications
You must be signed in to change notification settings - Fork 551
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor policy related code, add support for vuln verify #1747
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,15 +18,13 @@ package verify | |
import ( | ||
"context" | ||
"crypto" | ||
"encoding/base64" | ||
"encoding/json" | ||
"crypto/ecdsa" | ||
"flag" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/google/go-containerregistry/pkg/name" | ||
"github.com/in-toto/in-toto-golang/in_toto" | ||
"github.com/pkg/errors" | ||
"github.com/sigstore/cosign/pkg/cosign/pkcs11key" | ||
"github.com/sigstore/cosign/pkg/cosign/rego" | ||
|
@@ -39,6 +37,7 @@ import ( | |
"github.com/sigstore/cosign/pkg/cosign" | ||
"github.com/sigstore/cosign/pkg/cosign/cue" | ||
"github.com/sigstore/cosign/pkg/cosign/pivkey" | ||
"github.com/sigstore/cosign/pkg/policy" | ||
sigs "github.com/sigstore/cosign/pkg/signature" | ||
) | ||
|
||
|
@@ -47,11 +46,11 @@ import ( | |
type VerifyAttestationCommand struct { | ||
options.RegistryOptions | ||
CheckClaims bool | ||
KeyRef string | ||
CertRef string | ||
CertEmail string | ||
CertOidcIssuer string | ||
CertChain string | ||
KeyRef string | ||
Sk bool | ||
Slot string | ||
Output string | ||
|
@@ -122,7 +121,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e | |
return errors.Wrap(err, "loading certificate from reference") | ||
} | ||
if c.CertChain == "" { | ||
co.SigVerifier, err = signature.LoadVerifier(cert.PublicKey, crypto.SHA256) | ||
co.SigVerifier, err = signature.LoadECDSAVerifier(cert.PublicKey.(*ecdsa.PublicKey), crypto.SHA256) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Umm, this seems a regression, as we will want to support ECDS and non ECDSA keys There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh snaps, that must have been a bad rebase, Thanks for catching this!!! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was added in #1740 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fun fact, I wanted to take a crack at actually combining these and verify.go flag handling as they are identical (see the name of my branch LOL ), but then went off the rails and did this instead. But I'll try to get to it :) |
||
if err != nil { | ||
return errors.Wrap(err, "creating certificate verifier") | ||
} | ||
|
@@ -182,80 +181,15 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e | |
|
||
var validationErrors []error | ||
for _, vp := range verified { | ||
var payloadData map[string]interface{} | ||
|
||
p, err := vp.Payload() | ||
if err != nil { | ||
return errors.Wrap(err, "could not get payload") | ||
} | ||
|
||
err = json.Unmarshal(p, &payloadData) | ||
payload, err := policy.AttestationToPayloadJSON(ctx, c.PredicateType, vp) | ||
if err != nil { | ||
return errors.Wrap(err, "unmarshal payload data") | ||
return errors.Wrap(err, "converting to consumable policy validation") | ||
} | ||
|
||
var decodedPayload []byte | ||
if val, ok := payloadData["payload"]; ok { | ||
decodedPayload, err = base64.StdEncoding.DecodeString(val.(string)) | ||
if err != nil { | ||
return fmt.Errorf("could not decode 'payload': %w", err) | ||
} | ||
} else { | ||
return fmt.Errorf("could not find 'payload' in payload data") | ||
} | ||
|
||
predicateURI, ok := options.PredicateTypeMap[c.PredicateType] | ||
if !ok { | ||
return fmt.Errorf("invalid predicate type: %s", c.PredicateType) | ||
} | ||
|
||
// Only apply the policy against the requested predicate type | ||
var statement in_toto.Statement | ||
if err := json.Unmarshal(decodedPayload, &statement); err != nil { | ||
return fmt.Errorf("unmarshal in-toto statement: %w", err) | ||
} | ||
if statement.PredicateType != predicateURI { | ||
if len(payload) == 0 { | ||
// This is not the predicate type we're looking for. | ||
continue | ||
} | ||
|
||
var payload []byte | ||
switch c.PredicateType { | ||
case options.PredicateCustom: | ||
payload, err = json.Marshal(statement) | ||
if err != nil { | ||
return fmt.Errorf("error when generating CosignStatement: %w", err) | ||
} | ||
case options.PredicateLink: | ||
var linkStatement in_toto.LinkStatement | ||
if err := json.Unmarshal(decodedPayload, &linkStatement); err != nil { | ||
return fmt.Errorf("unmarshal LinkStatement: %w", err) | ||
} | ||
payload, err = json.Marshal(linkStatement) | ||
if err != nil { | ||
return fmt.Errorf("error when generating LinkStatement: %w", err) | ||
} | ||
case options.PredicateSLSA: | ||
var slsaProvenanceStatement in_toto.ProvenanceStatement | ||
if err := json.Unmarshal(decodedPayload, &slsaProvenanceStatement); err != nil { | ||
return fmt.Errorf("unmarshal ProvenanceStatement: %w", err) | ||
} | ||
payload, err = json.Marshal(slsaProvenanceStatement) | ||
if err != nil { | ||
return fmt.Errorf("error when generating ProvenanceStatement: %w", err) | ||
} | ||
case options.PredicateSPDX: | ||
var spdxStatement in_toto.SPDXStatement | ||
if err := json.Unmarshal(decodedPayload, &spdxStatement); err != nil { | ||
return fmt.Errorf("unmarshal SPDXStatement: %w", err) | ||
} | ||
payload, err = json.Marshal(spdxStatement) | ||
if err != nil { | ||
return fmt.Errorf("error when generating SPDXStatement: %w", err) | ||
} | ||
default: | ||
return fmt.Errorf("unsupported predicate type: %s", c.PredicateType) | ||
} | ||
|
||
if len(cuePolicies) > 0 { | ||
fmt.Fprintf(os.Stderr, "will be validating against CUE policies: %v\n", cuePolicies) | ||
cueValidationErr := cue.ValidateJSON(payload, cuePolicies) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// | ||
// Copyright 2022 The Sigstore Authors. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package policy | ||
|
||
import ( | ||
"context" | ||
"encoding/base64" | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/in-toto/in-toto-golang/in_toto" | ||
"github.com/pkg/errors" | ||
"github.com/sigstore/cosign/pkg/oci" | ||
|
||
"github.com/sigstore/cosign/cmd/cosign/cli/options" | ||
"github.com/sigstore/cosign/pkg/cosign/attestation" | ||
) | ||
|
||
// AttestationToPayloadJSON takes in a verified Attestation (oci.Signature) and | ||
// marshals it into a JSON depending on the payload that's then consumable | ||
// by policy engine like cue, rego, etc. | ||
// | ||
// Anything fed here must have been validated with either | ||
// `VerifyLocalImageAttestations` or `VerifyImageAttestations` | ||
// | ||
// If there's no error, and payload is empty means the predicateType did not | ||
// match the attestation. | ||
func AttestationToPayloadJSON(ctx context.Context, predicateType string, verifiedAttestation oci.Signature) ([]byte, error) { | ||
// Check the predicate up front, no point in wasting time if it's invalid. | ||
predicateURI, ok := options.PredicateTypeMap[predicateType] | ||
if !ok { | ||
return nil, fmt.Errorf("invalid predicate type: %s", predicateType) | ||
} | ||
|
||
var payloadData map[string]interface{} | ||
|
||
p, err := verifiedAttestation.Payload() | ||
if err != nil { | ||
return nil, errors.Wrap(err, "getting payload") | ||
} | ||
|
||
err = json.Unmarshal(p, &payloadData) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "unmarshaling payload data") | ||
} | ||
|
||
var decodedPayload []byte | ||
if val, ok := payloadData["payload"]; ok { | ||
decodedPayload, err = base64.StdEncoding.DecodeString(val.(string)) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "decoding payload") | ||
} | ||
} else { | ||
return nil, fmt.Errorf("could not find payload in payload data") | ||
} | ||
|
||
// Only apply the policy against the requested predicate type | ||
var statement in_toto.Statement | ||
if err := json.Unmarshal(decodedPayload, &statement); err != nil { | ||
return nil, fmt.Errorf("unmarshal in-toto statement: %w", err) | ||
} | ||
if statement.PredicateType != predicateURI { | ||
// This is not the predicate we're looking for, so skip it. | ||
return nil, nil | ||
} | ||
|
||
// NB: In many (all?) of these cases, we could just return the | ||
// 'json.Marshal', but we check for errors here to decorate them | ||
// with more meaningful error message. | ||
var payload []byte | ||
switch predicateType { | ||
case options.PredicateCustom: | ||
payload, err = json.Marshal(statement) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "generating CosignStatement") | ||
} | ||
case options.PredicateLink: | ||
var linkStatement in_toto.LinkStatement | ||
if err := json.Unmarshal(decodedPayload, &linkStatement); err != nil { | ||
return nil, errors.Wrap(err, "unmarshaling LinkStatement") | ||
} | ||
payload, err = json.Marshal(linkStatement) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "marshaling LinkStatement") | ||
} | ||
case options.PredicateSLSA: | ||
var slsaProvenanceStatement in_toto.ProvenanceStatement | ||
if err := json.Unmarshal(decodedPayload, &slsaProvenanceStatement); err != nil { | ||
return nil, errors.Wrap(err, "unmarshaling ProvenanceStatement") | ||
} | ||
payload, err = json.Marshal(slsaProvenanceStatement) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "marshaling ProvenanceStatement") | ||
} | ||
case options.PredicateSPDX: | ||
var spdxStatement in_toto.SPDXStatement | ||
if err := json.Unmarshal(decodedPayload, &spdxStatement); err != nil { | ||
return nil, errors.Wrap(err, "unmarshaling SPDXStatement") | ||
} | ||
payload, err = json.Marshal(spdxStatement) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "marshaling SPDXStatement") | ||
} | ||
case options.PredicateVuln: | ||
var vulnStatement attestation.CosignVulnStatement | ||
if err := json.Unmarshal(decodedPayload, &vulnStatement); err != nil { | ||
return nil, errors.Wrap(err, "unmarshaling CosignVulnStatement") | ||
} | ||
payload, err = json.Marshal(vulnStatement) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "marshaling CosignVulnStatement") | ||
} | ||
default: | ||
return nil, fmt.Errorf("unsupported predicate type: %s", predicateType) | ||
} | ||
return payload, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice