Skip to content

Commit

Permalink
Introduce Resolved Dependencies for SLSAv1.0 predicate
Browse files Browse the repository at this point in the history
This PR introduces resolved dependencies for the SLSAv1.0 predicate.
It addresses part of issue tektoncd#797
  • Loading branch information
chitrangpatel committed May 18, 2023
1 parent 1a3b099 commit b81fb75
Show file tree
Hide file tree
Showing 6 changed files with 1,028 additions and 91 deletions.
22 changes: 10 additions & 12 deletions pkg/artifacts/signable.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func ExtractSignableTargetFromResults(ctx context.Context, obj objects.TektonObj
if s == nil || s.Digest == "" || s.URI == "" {
continue
}
if err := checkDigest(s.Digest); err != nil {
if _, _, err := ParseDigest(s.Digest); err != nil {
logger.Errorf("error getting digest %s: %v", s.Digest, err)
continue
}
Expand Down Expand Up @@ -310,13 +310,11 @@ func RetrieveMaterialsFromStructuredResults(ctx context.Context, obj objects.Tek
mats := []common.ProvenanceMaterial{}
ssts := ExtractStructuredTargetFromResults(ctx, obj, ArtifactsInputsResultName)
for _, s := range ssts {
if err := checkDigest(s.Digest); err != nil {
alg, digest, err := ParseDigest(s.Digest)
if err != nil {
logger.Debugf("Digest for %s not in the right format: %s, %v", s.URI, s.Digest, err)
continue
}
splits := strings.Split(s.Digest, ":")
alg := splits[0]
digest := splits[1]
mats = append(mats, common.ProvenanceMaterial{
URI: s.URI,
Digest: map[string]string{alg: digest},
Expand Down Expand Up @@ -370,16 +368,16 @@ func isStructuredResult(res objects.Result, categoryMarker string) (bool, error)
if res.Value.ObjectVal["digest"] == "" {
return false, fmt.Errorf("%s should have digest field: %v", res.Name, res.Value.ObjectVal)
}
if err := checkDigest(res.Value.ObjectVal["digest"]); err != nil {
if _, _, err := ParseDigest(res.Value.ObjectVal["digest"]); err != nil {
return false, fmt.Errorf("error getting digest %s: %v", res.Value.ObjectVal["digest"], err)
}
return true, nil
}

func checkDigest(dig string) error {
func ParseDigest(dig string) (string, string, error) {
parts := strings.Split(dig, ":")
if len(parts) != 2 {
return fmt.Errorf("digest string %s, not in the format of <algorithm>:<digest>", dig)
return "", "", fmt.Errorf("digest string %s, not in the format of <algorithm>:<digest>", dig)
}
algo_string := strings.ToLower(strings.TrimSpace(parts[0]))
algo := digest.Algorithm(algo_string)
Expand All @@ -388,19 +386,19 @@ func checkDigest(dig string) error {
switch {
case algo.Available():
if err := algo.Validate(hex); err != nil {
return err
return "", "", err
}
case algo_string == "sha1":
// Version 1.0.0, which is the released version, of go_digest does not support SHA1,
// hence this has to be handled differently.
if !Sha1Regexp.MatchString(hex) {
return fmt.Errorf("sha1 digest %s does not match regexp %s", dig, Sha1Regexp.String())
return "", "", fmt.Errorf("sha1 digest %s does not match regexp %s", dig, Sha1Regexp.String())
}
default:
return fmt.Errorf("unsupported digest algorithm: %s", dig)
return "", "", fmt.Errorf("unsupported digest algorithm: %s", dig)

}
return nil
return algo_string, hex, nil
}

// split allows IMAGES to be separated either by commas (for backwards compatibility)
Expand Down
126 changes: 96 additions & 30 deletions pkg/chains/formats/slsa/internal/material/material.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ import (
const (
uriSeparator = "@"
digestSeparator = ":"
DigestAlgorithm = "digestAlgorithm"
DigestValue = "digestValue"
URI = "uri"
)

// AddStepImagesToMaterials adds step images to predicate.materials
Expand Down Expand Up @@ -60,40 +63,45 @@ func AddImageIDToMaterials(imageID string, mats *[]common.ProvenanceMaterial) er
m := common.ProvenanceMaterial{
Digest: common.DigestSet{},
}
uriDigest := strings.Split(imageID, uriSeparator)
if len(uriDigest) == 2 {
digest := strings.Split(uriDigest[1], digestSeparator)
if len(digest) == 2 {
// no point in partially populating the material
// do it if both conditions are valid.
uri := strings.TrimPrefix(uriDigest[0], "docker-pullable://")
m.URI = artifacts.OCIScheme + uri
m.Digest[digest[0]] = digest[1]
*mats = append(*mats, m)
} else {
return fmt.Errorf("expected imageID %s to be separable by @ and :", imageID)
}
} else {
return fmt.Errorf("expected imageID %s to be separable by @", imageID)
uriDigest, err := extractUriDigestFromImageID(imageID)
if err != nil {
return err
}
m.URI = uriDigest[URI]
m.Digest[uriDigest[DigestAlgorithm]] = uriDigest[DigestValue]
*mats = append(*mats, m)
return nil
}

// extractUriDigestFromImageID extracts uri and digest from an imageID with format <uri>@sha256:<digest>
func extractUriDigestFromImageID(imageID string) (map[string]string, error) {
uriDigest := strings.Split(imageID, uriSeparator)
if len(uriDigest) != 2 {
return map[string]string{}, fmt.Errorf("expected imageID %s to be separable by @", imageID)
}
digest := strings.Split(uriDigest[1], digestSeparator)
if len(digest) != 2 {
return map[string]string{}, fmt.Errorf("expected imageID %s to be separable by @ and :", imageID)
}
uri := strings.TrimPrefix(uriDigest[0], "docker-pullable://")
return map[string]string{URI: artifacts.OCIScheme + uri, DigestAlgorithm: digest[0], DigestValue: digest[1]}, nil
}

// Materials constructs `predicate.materials` section by collecting all the artifacts that influence a taskrun such as source code repo and step&sidecar base images.
func Materials(ctx context.Context, tro *objects.TaskRunObject) ([]common.ProvenanceMaterial, error) {
var mats []common.ProvenanceMaterial

// add step images
if err := AddStepImagesToMaterials(tro.Status.Steps, &mats); err != nil {
return mats, nil
return nil, err
}

// add sidecar images
if err := AddSidecarImagesToMaterials(tro.Status.Sidecars, &mats); err != nil {
return mats, nil
return nil, err
}

gitCommit, gitURL := gitInfo(tro)
gitCommit, gitURL := GitInfo(tro)

// Store git rev as Materials and Recipe.Material
if gitCommit != "" && gitURL != "" {
Expand All @@ -107,10 +115,22 @@ func Materials(ctx context.Context, tro *objects.TaskRunObject) ([]common.Proven
sms := artifacts.RetrieveMaterialsFromStructuredResults(ctx, tro, artifacts.ArtifactsInputsResultName)
mats = append(mats, sms...)

if tro.Spec.Resources != nil {
// add task resources
mats = AddTaskResourcesToMaterials(ctx, tro, mats)

// remove duplicate materials
mats, err := RemoveDuplicateMaterials(mats)
if err != nil {
return mats, err
}
return mats, nil
}

func AddTaskResourcesToMaterials(ctx context.Context, tro *objects.TaskRunObject, mats []common.ProvenanceMaterial) []common.ProvenanceMaterial {
if tro.Spec.Resources != nil { //nolint:all //incompatible with pipelines v0.45
// check for a Git PipelineResource
for _, input := range tro.Spec.Resources.Inputs {
if input.ResourceSpec == nil || input.ResourceSpec.Type != backport.PipelineResourceTypeGit {
for _, input := range tro.Spec.Resources.Inputs { //nolint:all //incompatible with pipelines v0.45
if input.ResourceSpec == nil || input.ResourceSpec.Type != backport.PipelineResourceTypeGit { //nolint:all //incompatible with pipelines v0.45
continue
}

Expand Down Expand Up @@ -143,18 +163,12 @@ func Materials(ctx context.Context, tro *objects.TaskRunObject) ([]common.Proven
mats = append(mats, m)
}
}

// remove duplicate materials
mats, err := RemoveDuplicateMaterials(mats)
if err != nil {
return mats, err
}
return mats, nil
return mats
}

// gitInfo scans over the input parameters and looks for parameters
// GitInfo scans over the input parameters and looks for parameters
// with specified names.
func gitInfo(tro *objects.TaskRunObject) (commit string, url string) {
func GitInfo(tro *objects.TaskRunObject) (commit string, url string) {
// Scan for git params to use for materials
if tro.Status.TaskSpec != nil {
for _, p := range tro.Status.TaskSpec.Params {
Expand Down Expand Up @@ -215,3 +229,55 @@ func RemoveDuplicateMaterials(mats []common.ProvenanceMaterial) ([]common.Proven
}
return out, nil
}

// AddMaterialsFromPipelineParamsAndResults extracts type hinted params and results and adds the url and digest to materials.
func AddMaterialsFromPipelineParamsAndResults(ctx context.Context, pro *objects.PipelineRunObject, mats []common.ProvenanceMaterial) []common.ProvenanceMaterial {
sms := artifacts.RetrieveMaterialsFromStructuredResults(ctx, pro, artifacts.ArtifactsInputsResultName)
mats = append(mats, sms...)

var commit, url string
// search status.PipelineSpec.params
if pro.Status.PipelineSpec != nil {
for _, p := range pro.Status.PipelineSpec.Params {
if p.Default == nil {
continue
}
if p.Name == attest.CommitParam {
commit = p.Default.StringVal
continue
}
if p.Name == attest.URLParam {
url = p.Default.StringVal
}
}
}

// search pipelineRunSpec.params
for _, p := range pro.Spec.Params {
if p.Name == attest.CommitParam {
commit = p.Value.StringVal
continue
}
if p.Name == attest.URLParam {
url = p.Value.StringVal
}
}

// search status.PipelineRunResults
for _, r := range pro.Status.PipelineResults {
if r.Name == attest.CommitParam {
commit = r.Value.StringVal
}
if r.Name == attest.URLParam {
url = r.Value.StringVal
}
}
if len(commit) > 0 && len(url) > 0 {
url = attest.SPDXGit(url, "")
mats = append(mats, common.ProvenanceMaterial{
URI: url,
Digest: map[string]string{"sha1": commit},
})
}
return mats
}
Loading

0 comments on commit b81fb75

Please sign in to comment.