Skip to content

Commit

Permalink
initial PoC
Browse files Browse the repository at this point in the history
Signed-off-by: Chuang Wang <chuangw@google.com>
  • Loading branch information
chuangw6 committed Mar 8, 2023
1 parent ee8ca6c commit f80c184
Show file tree
Hide file tree
Showing 12 changed files with 610 additions and 25 deletions.
151 changes: 147 additions & 4 deletions pkg/artifacts/signable.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package artifacts
import (
_ "crypto/sha256" // Recommended by go-digest.
_ "crypto/sha512" // Recommended by go-digest.
"encoding/json"
"fmt"
"regexp"
"strings"
Expand All @@ -25,8 +26,9 @@ import (
"github.com/opencontainers/go-digest"
"github.com/tektoncd/chains/pkg/chains/objects"
"github.com/tektoncd/chains/pkg/config"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" // WILL BE REMOVED
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/sets"
)

Expand All @@ -40,6 +42,7 @@ var (
)

type Signable interface {
// this needs to be refactored to support unstructured based TektonObject
ExtractObjects(obj objects.TektonObject) []interface{}
StorageBackend(cfg config.Config) sets.String
Signer(cfg config.Config) string
Expand Down Expand Up @@ -241,6 +244,27 @@ func ExtractOCIImagesFromResults(obj objects.TektonObject, logger *zap.SugaredLo
return objs
}

func ReimplExtractOCIImagesFromResults(u *unstructured.Unstructured, logger *zap.SugaredLogger) []interface{} {
objs := []interface{}{}
ss := reimplExtractTargetFromResults(u, "IMAGE_URL", "IMAGE_DIGEST", logger)
for _, s := range ss {
if s == nil || s.Digest == "" || s.URI == "" {
continue
}
dgst, err := name.NewDigest(fmt.Sprintf("%s@%s", s.URI, s.Digest))
if err != nil {
logger.Errorf("error getting digest: %v", err)
continue
}

objs = append(objs, dgst)
}
// look for a comma separated list of images
// ommited

return objs
}

// ExtractSignableTargetFromResults extracts signable targets that aim to generate intoto provenance as materials within TaskRun results and store them as StructuredSignable.
func ExtractSignableTargetFromResults(obj objects.TektonObject, logger *zap.SugaredLogger) []*StructuredSignable {
objs := []*StructuredSignable{}
Expand All @@ -261,6 +285,25 @@ func ExtractSignableTargetFromResults(obj objects.TektonObject, logger *zap.Suga
return objs
}

func ReimplExtractSignableTargetFromResults(u *unstructured.Unstructured, logger *zap.SugaredLogger) []*StructuredSignable {
objs := []*StructuredSignable{}
ss := reimplExtractTargetFromResults(u, "ARTIFACT_URI", "ARTIFACT_DIGEST", logger)
// Only add it if we got both the signable URI and digest.
for _, s := range ss {
if s == nil || s.Digest == "" || s.URI == "" {
continue
}
if err := checkDigest(s.Digest); err != nil {
logger.Errorf("error getting digest %s: %v", s.Digest, err)
continue
}

objs = append(objs, s)
}

return objs
}

// FullRef returns the full reference of the signable artifact in the format of URI@DIGEST
func (s *StructuredSignable) FullRef() string {
return fmt.Sprintf("%s@%s", s.URI, s.Digest)
Expand Down Expand Up @@ -301,6 +344,63 @@ func extractTargetFromResults(obj objects.TektonObject, identifierSuffix string,
return ss
}

func reimplExtractTargetFromResults(u *unstructured.Unstructured, identifierSuffix string, digestSuffix string, logger *zap.SugaredLogger) map[string]*StructuredSignable {
ss := map[string]*StructuredSignable{}

results, _ := getResults(u)
for _, res := range results {
if strings.HasSuffix(res.Name, identifierSuffix) {
if res.Value.StringVal == "" {
logger.Debugf("error getting string value for %s", res.Name)
continue
}
marker := strings.TrimSuffix(res.Name, identifierSuffix)
if v, ok := ss[marker]; ok {
v.URI = strings.TrimSpace(res.Value.StringVal)

} else {
ss[marker] = &StructuredSignable{URI: strings.TrimSpace(res.Value.StringVal)}
}
// TODO: add logic for Intoto signable target as input.
}
if strings.HasSuffix(res.Name, digestSuffix) {
if res.Value.StringVal == "" {
logger.Debugf("error getting string value for %s", res.Name)
continue
}
marker := strings.TrimSuffix(res.Name, digestSuffix)
if v, ok := ss[marker]; ok {
v.Digest = strings.TrimSpace(res.Value.StringVal)
} else {
ss[marker] = &StructuredSignable{Digest: strings.TrimSpace(res.Value.StringVal)}
}
}

}
return ss
}

// TODO: revise the result type and marshal and unmarshal
func getResults(u *unstructured.Unstructured) ([]*objects.Result, error) {
results, found, err := unstructured.NestedSlice(u.UnstructuredContent(), "status", "taskResults")
if err != nil {
return nil, err
}
if !found {
return nil, nil
}
jsonResults, err := json.Marshal(results)
if err != nil {
return nil, err
}

var r []*objects.Result
if err = json.Unmarshal(jsonResults, &r); err != nil {
return nil, err
}
return r, nil
}

// RetrieveMaterialsFromStructuredResults retrieves structured results from Tekton Object, and convert them into materials.
func RetrieveMaterialsFromStructuredResults(obj objects.TektonObject, categoryMarker string, logger *zap.SugaredLogger) []slsa.ProvenanceMaterial {
// Retrieve structured provenance for inputs.
Expand All @@ -322,6 +422,27 @@ func RetrieveMaterialsFromStructuredResults(obj objects.TektonObject, categoryMa
return mats
}

// RetrieveMaterialsFromStructuredResults retrieves structured results from Tekton Object, and convert them into materials.
func ReimplRetrieveMaterialsFromStructuredResults(u *unstructured.Unstructured, categoryMarker string, logger *zap.SugaredLogger) []slsa.ProvenanceMaterial {
// Retrieve structured provenance for inputs.
mats := []slsa.ProvenanceMaterial{}
ssts := ReimplExtractStructuredTargetFromResults(u, ArtifactsInputsResultName, logger)
for _, s := range ssts {
if err := checkDigest(s.Digest); 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, slsa.ProvenanceMaterial{
URI: s.URI,
Digest: map[string]string{alg: digest},
})
}
return mats
}

// ExtractStructuredTargetFromResults extracts structured signable targets aim to generate intoto provenance as materials within TaskRun results and store them as StructuredSignable.
// categoryMarker categorizes signable targets into inputs and outputs.
func ExtractStructuredTargetFromResults(obj objects.TektonObject, categoryMarker string, logger *zap.SugaredLogger) []*StructuredSignable {
Expand All @@ -331,9 +452,9 @@ func ExtractStructuredTargetFromResults(obj objects.TektonObject, categoryMarker
}

// TODO(#592): support structured results using Run
results := []objects.Result{}
results := []*objects.Result{}
for _, res := range obj.GetResults() {
results = append(results, objects.Result{
results = append(results, &objects.Result{
Name: res.Name,
Value: res.Value,
})
Expand All @@ -353,7 +474,29 @@ func ExtractStructuredTargetFromResults(obj objects.TektonObject, categoryMarker
return objs
}

func isStructuredResult(res objects.Result, categoryMarker string) (bool, error) {
func ReimplExtractStructuredTargetFromResults(u *unstructured.Unstructured, categoryMarker string, logger *zap.SugaredLogger) []*StructuredSignable {
objs := []*StructuredSignable{}
if categoryMarker != ArtifactsInputsResultName && categoryMarker != ArtifactsOutputsResultName {
return objs
}
results, _ := getResults(u)

for _, res := range results {
if strings.HasSuffix(res.Name, categoryMarker) {
valid, err := isStructuredResult(res, categoryMarker)
if err != nil {
logger.Debugf("ExtractStructuredTargetFromResults: %v", err)
}
if valid {
logger.Debugf("Extracted Structured data from Result %s, %s", res.Value.ObjectVal["uri"], res.Value.ObjectVal["digest"])
objs = append(objs, &StructuredSignable{URI: res.Value.ObjectVal["uri"], Digest: res.Value.ObjectVal["digest"]})
}
}
}
return objs
}

func isStructuredResult(res *objects.Result, categoryMarker string) (bool, error) {
if !strings.HasSuffix(res.Name, categoryMarker) {
return false, nil
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/chains/formats/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const (
)

var (
IntotoAttestationSet = map[config.PayloadType]struct{}{PayloadTypeInTotoIte6: {}, PayloadTypeSlsav1: {}}
IntotoAttestationSet = map[config.PayloadType]struct{}{PayloadTypeInTotoIte6: {}, PayloadTypeSlsav1: {}, PayloadTypeSlsav2: {}}
payloaderMap = map[config.PayloadType]PayloaderInit{}
)

Expand Down
53 changes: 53 additions & 0 deletions pkg/chains/formats/slsa/extract/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

// SubjectDigests returns software artifacts produced from the TaskRun/PipelineRun object
Expand Down Expand Up @@ -140,3 +141,55 @@ func RetrieveAllArtifactURIs(obj objects.TektonObject, logger *zap.SugaredLogger
}
return result
}

// Type hinting extraction
// For taskrun: extract artifacts from taskrun results
func ReimplSubjectDigests(u *unstructured.Unstructured, logger *zap.SugaredLogger) ([]intoto.Subject, error) {
var subjects []intoto.Subject

// old approach: string result `IMAGE_URL` & `IMAGE_DIGEST`
imgs := artifacts.ReimplExtractOCIImagesFromResults(u, logger)
for _, i := range imgs {
if d, ok := i.(name.Digest); ok {
subjects = append(subjects, intoto.Subject{
Name: d.Repository.Name(),
Digest: slsa.DigestSet{
"sha256": strings.TrimPrefix(d.DigestStr(), "sha256:"),
},
})
}
}

// old approach: string result `ARTIFACT_URI` & `ARTIFACT_DIGEST`
sts := artifacts.ReimplExtractSignableTargetFromResults(u, logger)
for _, obj := range sts {
splits := strings.Split(obj.Digest, ":")
if len(splits) != 2 {
logger.Errorf("Digest %s should be in the format of: algorthm:abc", obj.Digest)
continue
}
subjects = append(subjects, intoto.Subject{
Name: obj.URI,
Digest: slsa.DigestSet{
splits[0]: splits[1],
},
})
}

// new approach: object result `*ARTIFACT_INPUTS` / `*ARTIFACT_OUTPUTS` (with keys named uri and digest)
ssts := artifacts.ReimplExtractStructuredTargetFromResults(u, artifacts.ArtifactsOutputsResultName, logger)
for _, s := range ssts {
splits := strings.Split(s.Digest, ":")
alg := splits[0]
digest := splits[1]
subjects = append(subjects, intoto.Subject{
Name: s.URI,
Digest: slsa.DigestSet{
alg: digest,
},
})
}

return subjects, nil

}
Loading

0 comments on commit f80c184

Please sign in to comment.