Skip to content

Commit

Permalink
Updated query known and slsa parser (guacsec#2018)
Browse files Browse the repository at this point in the history
* updated query known and slsa parser

Signed-off-by: Yaxhveer <yaxhcod@gmail.com>

* added tests

Signed-off-by: Yaxhveer <yaxhcod@gmail.com>

* corrected lint

Signed-off-by: Yaxhveer <yaxhcod@gmail.com>

---------

Signed-off-by: Yaxhveer <yaxhcod@gmail.com>
  • Loading branch information
Yaxhveer authored Jul 17, 2024
1 parent 0b17411 commit bc9361d
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 20 deletions.
49 changes: 35 additions & 14 deletions cmd/guacone/cmd/known.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,24 @@ func getOutputBasedOnNode(ctx context.Context, gqlclient graphql.Client, collect
tableRows = append(tableRows, table.Row{vexLinkStr, vex.Id, "Vex Status: " + vex.Status})
}
case hasSBOMStr:
for _, sbom := range collectedNeighbors.hasSBOMs {
tableRows = append(tableRows, table.Row{hasSBOMStr, sbom.Id, "SBOM Download Location: " + sbom.DownloadLocation})
if len(collectedNeighbors.hasSBOMs) > 0 {
for _, sbom := range collectedNeighbors.hasSBOMs {
tableRows = append(tableRows, table.Row{hasSBOMStr, sbom.Id, "SBOM Download Location: " + sbom.DownloadLocation})
}
} else {
// if there is an isOccurrence, check to see if there are sbom associated with it
for _, occurrence := range collectedNeighbors.occurrences {
neighborResponseHasSBOM, err := getAssociatedArtifact(ctx, gqlclient, occurrence, model.EdgeArtifactHasSbom)
if err != nil {
logger.Debugf("error querying neighbors: %v", err)
} else {
for _, neighborHasSBOM := range neighborResponseHasSBOM.Neighbors {
if hasSBOM, ok := neighborHasSBOM.(*model.NeighborsNeighborsHasSBOM); ok {
tableRows = append(tableRows, table.Row{hasSBOMStr, hasSBOM.Id, "SBOM Download Location: " + hasSBOM.DownloadLocation})
}
}
}
}
}
case hasSLSAStr:
if len(collectedNeighbors.hasSLSAs) > 0 {
Expand All @@ -372,18 +388,7 @@ func getOutputBasedOnNode(ctx context.Context, gqlclient graphql.Client, collect
} else {
// if there is an isOccurrence, check to see if there are slsa attestation associated with it
for _, occurrence := range collectedNeighbors.occurrences {
artifactFilter := &model.ArtifactSpec{
Algorithm: &occurrence.Artifact.Algorithm,
Digest: &occurrence.Artifact.Digest,
}
artifactResponse, err := model.Artifacts(ctx, gqlclient, *artifactFilter)
if err != nil {
logger.Debugf("error querying for artifacts: %v", err)
}
if len(artifactResponse.Artifacts) != 1 {
logger.Debugf("failed to located artifacts based on (algorithm:digest)")
}
neighborResponseHasSLSA, err := model.Neighbors(ctx, gqlclient, artifactResponse.Artifacts[0].Id, []model.Edge{model.EdgeArtifactHasSlsa})
neighborResponseHasSLSA, err := getAssociatedArtifact(ctx, gqlclient, occurrence, model.EdgeArtifactHasSlsa)
if err != nil {
logger.Debugf("error querying neighbors: %v", err)
} else {
Expand Down Expand Up @@ -449,6 +454,22 @@ func getOutputBasedOnNode(ctx context.Context, gqlclient graphql.Client, collect
return tableRows
}

func getAssociatedArtifact(ctx context.Context, gqlclient graphql.Client, occurrence *model.NeighborsNeighborsIsOccurrence, edge model.Edge) (*model.NeighborsResponse, error) {
logger := logging.FromContext(ctx)
artifactFilter := &model.ArtifactSpec{
Algorithm: &occurrence.Artifact.Algorithm,
Digest: &occurrence.Artifact.Digest,
}
artifactResponse, err := model.Artifacts(ctx, gqlclient, *artifactFilter)
if err != nil {
logger.Debugf("error querying for artifacts: %v", err)
}
if len(artifactResponse.Artifacts) != 1 {
logger.Debugf("failed to located artifacts based on (algorithm:digest)")
}
return model.Neighbors(ctx, gqlclient, artifactResponse.Artifacts[0].Id, []model.Edge{edge})
}

func validateQueryKnownFlags(graphqlEndpoint, headerFile string, args []string) (queryKnownOptions, error) {
var opts queryKnownOptions
opts.graphqlEndpoint = graphqlEndpoint
Expand Down
110 changes: 110 additions & 0 deletions internal/testing/testdata/testdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,44 @@ var (
]
}
`

ite6SLSA1_2 = `
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "sigstore",
"uri": "pkg:npm/sigstore/sigstore-js@4.2.0",
"digest": {
"sha1": "428601801d1f5d105351a403f58c38269de93f680"
}
}
],
"predicateType": "https://slsa.dev/provenance/v1",
"predicate": {
"buildDefinition": {
"buildType": "https://github.com/npm/cli/gha/v2",
"resolved_dependencies": [
{
"uri": "pkg:npm/sigstore/segs@1.2.0",
"digest": {
"sha1": "5b8c0801d1f5d105351a403f58c38269de93f680"
}
}
]
},
"runDetails": {
"builder": {
"id": "https://github.com/actions/runner"
},
"metadata": {
"invocationId": "b6186090-c8ff-4f91-97cf-7a3b47699e57",
"startedOn": "2022-05-24T12:13:35.054695403Z"
}
}
}
}`

Ite6SLSA1Doc = processor.Document{
Blob: []byte(ite6SLSA1),
Type: processor.DocumentITE6SLSA,
Expand All @@ -396,6 +434,16 @@ var (
},
}

Ite6SLSA1Doc_2 = processor.Document{
Blob: []byte(ite6SLSA1_2),
Type: processor.DocumentITE6SLSA,
Format: processor.FormatJSON,
SourceInformation: processor.SourceInformation{
Collector: "TestCollector",
Source: "TestSource",
},
}

b64ITE6SLSA = base64.StdEncoding.EncodeToString([]byte(ite6SLSA02))
Ite6Payload, _ = json.Marshal(dsse.Envelope{
PayloadType: "https://in-toto.io/Statement/v0.1",
Expand Down Expand Up @@ -577,6 +625,68 @@ var (
},
}

slsa1time_2, _ = time.Parse(time.RFC3339, "2022-05-24T12:13:35.054695403Z")
SlsaPreds1_2 = assembler.IngestPredicates{
IsOccurrence: []assembler.IsOccurrenceIngest{
{
Pkg: &model.PkgInputSpec{
Type: "npm",
Namespace: ptrfrom.String("sigstore"),
Name: "segs",
Version: ptrfrom.String("1.2.0"),
Subpath: ptrfrom.String(""),
},
Artifact: &model.ArtifactInputSpec{
Algorithm: "sha1",
Digest: "5b8c0801d1f5d105351a403f58c38269de93f680",
},
IsOccurrence: &slsaIsOccurrence,
},
{
Pkg: &model.PkgInputSpec{
Type: "npm",
Namespace: ptrfrom.String("sigstore"),
Name: "sigstore-js",
Version: ptrfrom.String("4.2.0"),
Subpath: ptrfrom.String(""),
},
Artifact: &model.ArtifactInputSpec{
Algorithm: "sha1",
Digest: "428601801d1f5d105351a403f58c38269de93f680",
},
IsOccurrence: &slsaIsOccurrence,
},
},
HasSlsa: []assembler.HasSlsaIngest{
{
Artifact: &model.ArtifactInputSpec{
Algorithm: "sha1",
Digest: "428601801d1f5d105351a403f58c38269de93f680",
},
Builder: &model.BuilderInputSpec{
Uri: "https://github.com/actions/runner",
},
Materials: []model.ArtifactInputSpec{{
Algorithm: "sha1",
Digest: "5b8c0801d1f5d105351a403f58c38269de93f680",
}},
HasSlsa: &model.SLSAInputSpec{
BuildType: "https://github.com/npm/cli/gha/v2",
SlsaVersion: "https://slsa.dev/provenance/v1",
StartedOn: &slsa1time_2,
SlsaPredicate: []model.SLSAPredicateInputSpec{
{Key: "slsa.buildDefinition.buildType", Value: "https://github.com/npm/cli/gha/v2"},
{Key: "slsa.buildDefinition.resolvedDependencies.0.digest.sha1", Value: "5b8c0801d1f5d105351a403f58c38269de93f680"},
{Key: "slsa.buildDefinition.resolvedDependencies.0.uri", Value: "pkg:npm/sigstore/segs@1.2.0"},
{Key: "slsa.runDetails.builder.id", Value: "https://github.com/actions/runner"},
{Key: "slsa.runDetails.metadata.invocationId", Value: "b6186090-c8ff-4f91-97cf-7a3b47699e57"},
{Key: "slsa.runDetails.metadata.startedOn", Value: "2022-05-24T12:13:35.054695403Z"},
},
},
},
},
}

// TODO: needs to be resolved by https://github.com/guacsec/guac/issues/75
Ident = []common.TrustInformation{}
// Ident = assembler.IdentityNode{
Expand Down
14 changes: 9 additions & 5 deletions pkg/ingestor/parser/slsa/parser_slsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (s *slsaParser) getSubject() error {
// append artifact node for the subjects
for _, sub := range s.smt.Subject {
s.identifierStrings.UnclassifiedStrings = append(s.identifierStrings.UnclassifiedStrings, sub.Name)
se, err := getSlsaEntity(sub.Name, sub.Digest)
se, err := getSlsaEntity(sub.Name, sub.Uri, sub.Digest)
if err != nil {
return err
}
Expand Down Expand Up @@ -149,7 +149,7 @@ func (s *slsaParser) getMaterials1(rds []*attestationv1.ResourceDescriptor) erro
}
// Digest(s) and URI are set, create IsOccurrence between them.
s.identifierStrings.UnclassifiedStrings = append(s.identifierStrings.UnclassifiedStrings, rd.Uri)
se, err := getSlsaEntity(rd.Uri, rd.Digest)
se, err := getSlsaEntity(rd.Name, rd.Uri, rd.Digest)
if err != nil {
return err
}
Expand All @@ -162,7 +162,7 @@ func (s *slsaParser) getMaterials0(materials []scommon.ProvenanceMaterial) error
// append dependency nodes for the materials
for _, mat := range materials {
s.identifierStrings.UnclassifiedStrings = append(s.identifierStrings.UnclassifiedStrings, mat.URI)
se, err := getSlsaEntity(mat.URI, mat.Digest)
se, err := getSlsaEntity("", mat.URI, mat.Digest)
if err != nil {
return err
}
Expand All @@ -182,7 +182,7 @@ func getArtifacts(digests scommon.DigestSet) []*model.ArtifactInputSpec {
return artifacts
}

func getSlsaEntity(name string, digests scommon.DigestSet) (*slsaEntity, error) {
func getSlsaEntity(name, uri string, digests scommon.DigestSet) (*slsaEntity, error) {
artifacts := getArtifacts(digests)
slsa := &slsaEntity{
artifacts: artifacts,
Expand All @@ -191,7 +191,11 @@ func getSlsaEntity(name string, digests scommon.DigestSet) (*slsaEntity, error)
},
}

if pkg, err := helpers.PurlToPkg(name); err == nil {
if name == "" {
name = uri
}

if pkg, err := helpers.PurlToPkg(uri); err == nil {
slsa.pkg = pkg
return slsa, nil
}
Expand Down
110 changes: 109 additions & 1 deletion pkg/ingestor/parser/slsa/parser_slsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ package slsa

import (
"context"
"reflect"
"testing"
"time"

scommon "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
slsa01 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.1"

"github.com/in-toto/in-toto-golang/in_toto"
Expand Down Expand Up @@ -47,11 +49,17 @@ func Test_slsaParser(t *testing.T) {
wantErr: false,
},
{
name: "testing v0.1",
name: "testing v1",
doc: &testdata.Ite6SLSA1Doc,
wantPredicates: &testdata.SlsaPreds1,
wantErr: false,
},
{
name: "testing v1-2",
doc: &testdata.Ite6SLSA1Doc_2,
wantPredicates: &testdata.SlsaPreds1_2,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -137,3 +145,103 @@ func Test_fillSLSA01(t *testing.T) {
})
}
}

func Test_getSlsaEntity(t *testing.T) {
namespace := "sigstore"
genericNamespace := "generic"
version := "4.2.0"
emptyString := ""
tests := []struct {
testname string
uri string
name string
digest scommon.DigestSet
expected *slsaEntity
wantErr bool
}{
{
testname: "with uri and digest",
uri: "pkg:npm/sigstore/sigstore-js@4.2.0",
digest: scommon.DigestSet{
"sha1": "428601801d1f5d105351a403f58c38269de93f680",
},
expected: &slsaEntity{
artifacts: []*model.ArtifactInputSpec{
{
Algorithm: "sha1",
Digest: "428601801d1f5d105351a403f58c38269de93f680",
},
},
pkg: &model.PkgInputSpec{
Type: "npm",
Namespace: &namespace,
Name: "sigstore-js",
Version: &version,
Subpath: &emptyString,
},
occurence: &model.IsOccurrenceInputSpec{
Justification: "from SLSA definition of checksums for subject/materials",
},
},
wantErr: false,
},
{
testname: "with name and digest",
name: "sigstore",
digest: scommon.DigestSet{
"sha1": "428601801d1f5d105351a403f58c38269de93f680",
},
expected: &slsaEntity{
artifacts: []*model.ArtifactInputSpec{
{
Algorithm: "sha1",
Digest: "428601801d1f5d105351a403f58c38269de93f680",
},
},
pkg: &model.PkgInputSpec{
Type: "guac",
Namespace: &genericNamespace,
Name: "sigstore",
Subpath: &emptyString,
Version: &emptyString,
},
occurence: &model.IsOccurrenceInputSpec{
Justification: "from SLSA definition of checksums for subject/materials",
},
},
wantErr: false,
},
{
testname: "without name and uri",
digest: scommon.DigestSet{
"sha1": "428601801d1f5d105351a403f58c38269de93f680",
},
wantErr: true,
},
}

for _, test := range tests {
t.Run(test.testname, func(t *testing.T) {
s, err := getSlsaEntity(test.name, test.uri, test.digest)
if (err != nil) != test.wantErr {
t.Errorf("slsa.Parse() error is not as expected. Expected: %v, Got: %v", err, test.wantErr)
return
}
if err != nil {
return
}
if !reflect.DeepEqual(s.pkg, test.expected.pkg) {
t.Errorf("getSlsaEntity() package is not as expected. Expected: %v, Got: %v", *s.pkg.Version, *test.expected.pkg.Version)
}
if !reflect.DeepEqual(s.source, test.expected.source) {
t.Errorf("getSlsaEntity() source is not as expected. Expected: %v, Got: %v", s.source, test.expected.source)
}
if !reflect.DeepEqual(s.occurence, test.expected.occurence) {
t.Errorf("getSlsaEntity() occurence is not as expected. Expected: %v, Got: %v", s.occurence, test.expected.occurence)
}
if !reflect.DeepEqual(s.artifacts, test.expected.artifacts) {
t.Errorf("getSlsaEntity() artifact is not as expected. Expected: %v, Got: %v", s.artifacts, test.expected.artifacts)
}
})
}
}

0 comments on commit bc9361d

Please sign in to comment.