diff --git a/internal/testing/e2e/e2e b/internal/testing/e2e/e2e index 637f1eff94..f3aeb4f999 100755 --- a/internal/testing/e2e/e2e +++ b/internal/testing/e2e/e2e @@ -69,7 +69,7 @@ diff -u "${SCRIPT_DIR}/expectPkgQ1.json" "${GUAC_DIR}/gotPkgQ1.json" cat "$queries" | gql-cli http://localhost:8080/query -o PkgQ2 | jq ' .packages[].namespaces |= sort ' > "${GUAC_DIR}/gotPkgQ2.json" diff -u "${SCRIPT_DIR}/expectPkgQ2.json" "${GUAC_DIR}/gotPkgQ2.json" -cat "$queries" | gql-cli http://localhost:8080/query -o PkgQ3 | jq ' del(.. | .id?) | .packages[].namespaces |= sort_by(.namespace) | .packages[].namespaces[].names[].versions |= sort_by(.version) | .packages[].namespaces[].names[].versions[].qualifiers |= sort_by(.key) ' > "${GUAC_DIR}/gotPkgQ3.json" +cat "$queries" | gql-cli http://localhost:8080/query -o PkgQ3 | jq ' .packages[].namespaces |= sort_by(.namespace) | .packages[].namespaces[].names[].versions |= sort_by(.id) | .packages[].namespaces[].names[].versions[].qualifiers |= sort_by(.key) | del(.. | .id?) ' > "${GUAC_DIR}/gotPkgQ3.json" diff -u "${SCRIPT_DIR}/expectPkgQ3.json" "${GUAC_DIR}/gotPkgQ3.json" cat "$queries" | gql-cli http://localhost:8080/query -o PkgQ4 | jq ' del(.. | .id?) '> "${GUAC_DIR}/gotPkgQ4.json" diff --git a/internal/testing/mocks/backend.go b/internal/testing/mocks/backend.go index 5a1c74808b..7b4b1ab15d 100644 --- a/internal/testing/mocks/backend.go +++ b/internal/testing/mocks/backend.go @@ -486,33 +486,33 @@ func (mr *MockBackendMockRecorder) IngestHasMetadata(ctx, subject, pkgMatchType, } // IngestHasSBOMs mocks base method. -func (m *MockBackend) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec) ([]*model.HasSbom, error) { +func (m *MockBackend) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec, includes []*model.HasSBOMIncludesInputSpec) ([]*model.HasSbom, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IngestHasSBOMs", ctx, subjects, hasSBOMs) + ret := m.ctrl.Call(m, "IngestHasSBOMs", ctx, subjects, hasSBOMs, includes) ret0, _ := ret[0].([]*model.HasSbom) ret1, _ := ret[1].(error) return ret0, ret1 } // IngestHasSBOMs indicates an expected call of IngestHasSBOMs. -func (mr *MockBackendMockRecorder) IngestHasSBOMs(ctx, subjects, hasSBOMs interface{}) *gomock.Call { +func (mr *MockBackendMockRecorder) IngestHasSBOMs(ctx, subjects, hasSBOMs, includes interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IngestHasSBOMs", reflect.TypeOf((*MockBackend)(nil).IngestHasSBOMs), ctx, subjects, hasSBOMs) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IngestHasSBOMs", reflect.TypeOf((*MockBackend)(nil).IngestHasSBOMs), ctx, subjects, hasSBOMs, includes) } // IngestHasSbom mocks base method. -func (m *MockBackend) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec) (*model.HasSbom, error) { +func (m *MockBackend) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec, includes model.HasSBOMIncludesInputSpec) (*model.HasSbom, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IngestHasSbom", ctx, subject, hasSbom) + ret := m.ctrl.Call(m, "IngestHasSbom", ctx, subject, hasSbom, includes) ret0, _ := ret[0].(*model.HasSbom) ret1, _ := ret[1].(error) return ret0, ret1 } // IngestHasSbom indicates an expected call of IngestHasSbom. -func (mr *MockBackendMockRecorder) IngestHasSbom(ctx, subject, hasSbom interface{}) *gomock.Call { +func (mr *MockBackendMockRecorder) IngestHasSbom(ctx, subject, hasSbom, includes interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IngestHasSbom", reflect.TypeOf((*MockBackend)(nil).IngestHasSbom), ctx, subject, hasSbom) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IngestHasSbom", reflect.TypeOf((*MockBackend)(nil).IngestHasSbom), ctx, subject, hasSbom, includes) } // IngestHasSourceAt mocks base method. diff --git a/pkg/assembler/assembler.go b/pkg/assembler/assembler.go index cd742043b5..9946b1a3b3 100644 --- a/pkg/assembler/assembler.go +++ b/pkg/assembler/assembler.go @@ -147,7 +147,8 @@ type HasSBOMIngest struct { Pkg *generated.PkgInputSpec `json:"pkg,omitempty"` Artifact *generated.ArtifactInputSpec `json:"artifact,omitempty"` - HasSBOM *generated.HasSBOMInputSpec `json:"hasSbom,omitempty"` + HasSBOM *generated.HasSBOMInputSpec `json:"hasSbom,omitempty"` + Includes *generated.HasSBOMIncludesInputSpec `json:"includes,omitempty"` } type VexIngest struct { diff --git a/pkg/assembler/backends/arangodb/hasSBOM.go b/pkg/assembler/backends/arangodb/hasSBOM.go index 9a29db540f..32e5b01962 100644 --- a/pkg/assembler/backends/arangodb/hasSBOM.go +++ b/pkg/assembler/backends/arangodb/hasSBOM.go @@ -218,7 +218,8 @@ func getHasSBOMQueryValues(pkg *model.PkgInputSpec, artifact *model.ArtifactInpu return values } -func (c *arangoClient) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec) ([]*model.HasSbom, error) { +func (c *arangoClient) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec, includes []*model.HasSBOMIncludesInputSpec) ([]*model.HasSbom, error) { + // TODO(knrc) - handle includes if len(subjects.Packages) > 0 { var listOfValues []map[string]any @@ -401,7 +402,8 @@ func (c *arangoClient) IngestHasSBOMs(ctx context.Context, subjects model.Packag } } -func (c *arangoClient) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec) (*model.HasSbom, error) { +func (c *arangoClient) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec, includes model.HasSBOMIncludesInputSpec) (*model.HasSbom, error) { + // TODO(knrc) - handle includes if subject.Artifact != nil { query := `LET artifact = FIRST(FOR art IN artifacts FILTER art.algorithm == @art_algorithm FILTER art.digest == @art_digest RETURN art) diff --git a/pkg/assembler/backends/arangodb/hasSBOM_test.go b/pkg/assembler/backends/arangodb/hasSBOM_test.go index abe0cb89fe..f5dc493377 100644 --- a/pkg/assembler/backends/arangodb/hasSBOM_test.go +++ b/pkg/assembler/backends/arangodb/hasSBOM_test.go @@ -45,6 +45,7 @@ func TestHasSBOM(t *testing.T) { type call struct { Sub model.PackageOrArtifactInput HS *model.HasSBOMInputSpec + Inc *model.HasSBOMIncludesInputSpec } tests := []struct { Name string @@ -577,7 +578,8 @@ func TestHasSBOM(t *testing.T) { } } for _, o := range test.Calls { - found, err := b.IngestHasSbom(ctx, o.Sub, *o.HS) + // TODO (knrc) handle includes + found, err := b.IngestHasSbom(ctx, o.Sub, *o.HS, model.HasSBOMIncludesInputSpec{}) if (err != nil) != test.ExpIngestErr { t.Fatalf("did not get expected ingest error, want: %v, got: %v", test.ExpIngestErr, err) } @@ -640,6 +642,7 @@ func TestIngestHasSBOM(t *testing.T) { type call struct { Sub model.PackageOrArtifactInputs HS []*model.HasSBOMInputSpec + Inc []*model.HasSBOMIncludesInputSpec } tests := []struct { Name string @@ -835,7 +838,7 @@ func TestIngestHasSBOM(t *testing.T) { } } for _, o := range test.Calls { - _, err := b.IngestHasSBOMs(ctx, o.Sub, o.HS) + _, err := b.IngestHasSBOMs(ctx, o.Sub, o.HS, o.Inc) if (err != nil) != test.ExpIngestErr { t.Fatalf("did not get expected ingest error, want: %v, got: %v", test.ExpIngestErr, err) } @@ -1031,7 +1034,8 @@ func Test_buildHasSbomByID(t *testing.T) { } } for _, o := range test.Calls { - found, err := b.IngestHasSbom(ctx, o.Sub, *o.HS) + // TODO (knrc) handle includes + found, err := b.IngestHasSbom(ctx, o.Sub, *o.HS, model.HasSBOMIncludesInputSpec{}) if (err != nil) != test.ExpIngestErr { t.Fatalf("did not get expected ingest error, want: %v, got: %v", test.ExpIngestErr, err) } diff --git a/pkg/assembler/backends/arangodb/path_test.go b/pkg/assembler/backends/arangodb/path_test.go index 469296778a..62e8c59906 100644 --- a/pkg/assembler/backends/arangodb/path_test.go +++ b/pkg/assembler/backends/arangodb/path_test.go @@ -883,7 +883,8 @@ func Test_Nodes(t *testing.T) { nodeID = found.ID } if tt.hasSBOMCall != nil { - found, err := b.IngestHasSbom(ctx, tt.hasSBOMCall.Sub, *tt.hasSBOMCall.HS) + // TODO (knrc) handle includes + found, err := b.IngestHasSbom(ctx, tt.hasSBOMCall.Sub, *tt.hasSBOMCall.HS, model.HasSBOMIncludesInputSpec{}) if (err != nil) != tt.wantErr { t.Fatalf("did not get expected ingest error, want: %v, got: %v", tt.wantErr, err) } @@ -3377,7 +3378,8 @@ func Test_Neighbors(t *testing.T) { } } if tt.hasSBOMCall != nil { - found, err := b.IngestHasSbom(ctx, tt.hasSBOMCall.Sub, *tt.hasSBOMCall.HS) + // TODO (knrc) handle includes + found, err := b.IngestHasSbom(ctx, tt.hasSBOMCall.Sub, *tt.hasSBOMCall.HS, model.HasSBOMIncludesInputSpec{}) if (err != nil) != tt.wantErr { t.Fatalf("did not get expected ingest error, want: %v, got: %v", tt.wantErr, err) } diff --git a/pkg/assembler/backends/backends.go b/pkg/assembler/backends/backends.go index c30aa31989..a4b236d927 100644 --- a/pkg/assembler/backends/backends.go +++ b/pkg/assembler/backends/backends.go @@ -77,8 +77,8 @@ type Backend interface { IngestCertifyLegals(ctx context.Context, subjects model.PackageOrSourceInputs, declaredLicensesList [][]*model.LicenseInputSpec, discoveredLicensesList [][]*model.LicenseInputSpec, certifyLegals []*model.CertifyLegalInputSpec) ([]*model.CertifyLegal, error) IngestDependency(ctx context.Context, pkg model.PkgInputSpec, depPkg model.PkgInputSpec, depPkgMatchType model.MatchFlags, dependency model.IsDependencyInputSpec) (*model.IsDependency, error) IngestDependencies(ctx context.Context, pkgs []*model.PkgInputSpec, depPkgs []*model.PkgInputSpec, depPkgMatchType model.MatchFlags, dependencies []*model.IsDependencyInputSpec) ([]*model.IsDependency, error) - IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec) (*model.HasSbom, error) - IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec) ([]*model.HasSbom, error) + IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec, includes model.HasSBOMIncludesInputSpec) (*model.HasSbom, error) + IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec, includes []*model.HasSBOMIncludesInputSpec) ([]*model.HasSbom, error) IngestHasSourceAt(ctx context.Context, pkg model.PkgInputSpec, pkgMatchType model.MatchFlags, source model.SourceInputSpec, hasSourceAt model.HasSourceAtInputSpec) (*model.HasSourceAt, error) IngestHasSourceAts(ctx context.Context, pkgs []*model.PkgInputSpec, pkgMatchType *model.MatchFlags, sources []*model.SourceInputSpec, hasSourceAts []*model.HasSourceAtInputSpec) ([]string, error) IngestHasMetadata(ctx context.Context, subject model.PackageSourceOrArtifactInput, pkgMatchType *model.MatchFlags, hasMetadata model.HasMetadataInputSpec) (*model.HasMetadata, error) diff --git a/pkg/assembler/backends/ent/backend/sbom.go b/pkg/assembler/backends/ent/backend/sbom.go index ad36209254..f63b219c67 100644 --- a/pkg/assembler/backends/ent/backend/sbom.go +++ b/pkg/assembler/backends/ent/backend/sbom.go @@ -69,7 +69,8 @@ func (b *EntBackend) HasSBOM(ctx context.Context, spec *model.HasSBOMSpec) ([]*m return collect(records, toModelHasSBOM), nil } -func (b *EntBackend) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, spec model.HasSBOMInputSpec) (*model.HasSbom, error) { +func (b *EntBackend) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, spec model.HasSBOMInputSpec, includes model.HasSBOMIncludesInputSpec) (*model.HasSbom, error) { + // TODO(knrc) - handle includes funcName := "IngestHasSbom" sbomId, err := WithinTX(ctx, b.client, func(ctx context.Context) (*int, error) { @@ -158,7 +159,7 @@ func (b *EntBackend) IngestHasSbom(ctx context.Context, subject model.PackageOrA return toModelHasSBOM(sbom), nil } -func (b *EntBackend) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec) ([]*model.HasSbom, error) { +func (b *EntBackend) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec, includes []*model.HasSBOMIncludesInputSpec) ([]*model.HasSbom, error) { var modelHasSboms []*model.HasSbom for i, hasSbom := range hasSBOMs { var subject model.PackageOrArtifactInput @@ -167,7 +168,8 @@ func (b *EntBackend) IngestHasSBOMs(ctx context.Context, subjects model.PackageO } else { subject = model.PackageOrArtifactInput{Package: subjects.Packages[i]} } - modelHasSbom, err := b.IngestHasSbom(ctx, subject, *hasSbom) + // TODO(knrc) - handle includes + modelHasSbom, err := b.IngestHasSbom(ctx, subject, *hasSbom, model.HasSBOMIncludesInputSpec{}) if err != nil { return nil, gqlerror.Errorf("IngestHasSBOMs failed with err: %v", err) } diff --git a/pkg/assembler/backends/ent/backend/sbom_test.go b/pkg/assembler/backends/ent/backend/sbom_test.go index cd485df7b5..0bf2c976e2 100644 --- a/pkg/assembler/backends/ent/backend/sbom_test.go +++ b/pkg/assembler/backends/ent/backend/sbom_test.go @@ -30,6 +30,7 @@ func (s *Suite) Test_HasSBOM() { type call struct { Sub model.PackageOrArtifactInput Spec *model.HasSBOMInputSpec + Inc *model.HasSBOMIncludesInputSpec } tests := []struct { Name string @@ -505,7 +506,8 @@ func (s *Suite) Test_HasSBOM() { recordIDs := make([]string, len(test.Calls)) for i, o := range test.Calls { - dep, err := b.IngestHasSbom(ctx, o.Sub, *o.Spec) + // TODO (knrc) handle includes + dep, err := b.IngestHasSbom(ctx, o.Sub, *o.Spec, model.HasSBOMIncludesInputSpec{}) if (err != nil) != test.ExpIngestErr { s.T().Fatalf("did not get expected ingest error, want: %v, got: %v", test.ExpIngestErr, err) } @@ -545,6 +547,7 @@ func (s *Suite) TestIngestHasSBOMs() { type call struct { Sub model.PackageOrArtifactInputs HS []*model.HasSBOMInputSpec + Inc []*model.HasSBOMIncludesInputSpec } tests := []struct { Name string @@ -768,7 +771,7 @@ func (s *Suite) TestIngestHasSBOMs() { } } for _, o := range test.Calls { - _, err := b.IngestHasSBOMs(ctx, o.Sub, o.HS) + _, err := b.IngestHasSBOMs(ctx, o.Sub, o.HS, o.Inc) if (err != nil) != test.ExpIngestErr { t.Fatalf("did not get expected ingest error, want: %v, got: %v", test.ExpIngestErr, err) } diff --git a/pkg/assembler/backends/inmem/backend.go b/pkg/assembler/backends/inmem/backend.go index 28a3b1a1d0..924f9393bc 100644 --- a/pkg/assembler/backends/inmem/backend.go +++ b/pkg/assembler/backends/inmem/backend.go @@ -20,6 +20,9 @@ import ( "errors" "fmt" "math" + "reflect" + "slices" + "strconv" "strings" "sync" "sync/atomic" @@ -201,3 +204,62 @@ func unlock(m *sync.RWMutex, readOnly bool) { m.Unlock() } } + +func parseIDs(ids []string) ([]uint32, error) { + keys := make([]uint32, 0, len(ids)) + for _, id := range ids { + if key, err := parseID(id); err != nil { + return nil, err + } else { + keys = append(keys, key) + } + } + return keys, nil +} + +func parseID(id string) (uint32, error) { + id64, err := strconv.ParseUint(id, 10, 32) + return uint32(id64), err +} + +func sortAndRemoveDups(ids []uint32) []uint32 { + numIDs := len(ids) + if numIDs > 1 { + slices.Sort(ids) + nextIndex := 1 + for index := 1; index < numIDs; index++ { + currentVal := ids[index] + if ids[index-1] != currentVal { + ids[nextIndex] = currentVal + nextIndex++ + } + } + ids = ids[:nextIndex] + } + return ids +} + +func (c *demoClient) getPackageVersionAndArtifacts(pkgOrArt []uint32) (pkgs []uint32, arts []uint32, err error) { + for _, id := range pkgOrArt { + switch entry := c.index[id].(type) { + case *pkgVersionNode: + pkgs = append(pkgs, entry.id) + case *artStruct: + arts = append(arts, entry.id) + default: + return nil, nil, fmt.Errorf("unexpected type in package or artifact list: %s", reflect.TypeOf(entry)) + } + } + + return +} + +// IDs should be sorted +func (c *demoClient) isIDPresent(id string, linkIDs []uint32) bool { + linkID, err := strconv.ParseUint(id, 10, 32) + if err != nil { + return false + } + _, found := slices.BinarySearch[[]uint32](linkIDs, uint32(linkID)) + return found +} diff --git a/pkg/assembler/backends/inmem/hasSBOM.go b/pkg/assembler/backends/inmem/hasSBOM.go index 0b0f786631..2fd24b0920 100644 --- a/pkg/assembler/backends/inmem/hasSBOM.go +++ b/pkg/assembler/backends/inmem/hasSBOM.go @@ -17,39 +17,55 @@ package inmem import ( "context" + "fmt" + "reflect" "strconv" "strings" "time" "github.com/vektah/gqlparser/v2/gqlerror" + "golang.org/x/exp/slices" "github.com/guacsec/guac/pkg/assembler/graphql/model" ) type hasSBOMList []*hasSBOMStruct type hasSBOMStruct struct { - id uint32 - pkg uint32 - artifact uint32 - uri string - algorithm string - digest string - downloadLocation string - origin string - collector string - knownSince time.Time + id uint32 + pkg uint32 + artifact uint32 + uri string + algorithm string + digest string + downloadLocation string + origin string + collector string + knownSince time.Time + includedSoftware []uint32 + includedDependencies []uint32 + includedOccurrences []uint32 } func (n *hasSBOMStruct) ID() uint32 { return n.id } func (n *hasSBOMStruct) Neighbors(allowedEdges edgeMap) []uint32 { + out := []uint32{} if n.pkg != 0 && allowedEdges[model.EdgeHasSbomPackage] { - return []uint32{n.pkg} + out = append(out, n.pkg) } - if allowedEdges[model.EdgeHasSbomArtifact] { - return []uint32{n.artifact} + if n.artifact != 0 && allowedEdges[model.EdgeHasSbomArtifact] { + out = append(out, n.artifact) } - return []uint32{} + if allowedEdges[model.EdgeHasSbomIncludedSoftware] { + out = append(out, n.includedSoftware...) + } + if allowedEdges[model.EdgeHasSbomIncludedDependencies] { + out = append(out, n.includedDependencies...) + } + if allowedEdges[model.EdgeHasSbomIncludedOccurrences] { + out = append(out, n.includedOccurrences...) + } + return sortAndRemoveDups(out) } func (n *hasSBOMStruct) BuildModelNode(c *demoClient) (model.Node, error) { @@ -58,7 +74,7 @@ func (n *hasSBOMStruct) BuildModelNode(c *demoClient) (model.Node, error) { // Ingest HasSBOM -func (c *demoClient) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec) ([]*model.HasSbom, error) { +func (c *demoClient) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec, includes []*model.HasSBOMIncludesInputSpec) ([]*model.HasSbom, error) { var modelHasSboms []*model.HasSbom for i := range hasSBOMs { @@ -66,13 +82,13 @@ func (c *demoClient) IngestHasSBOMs(ctx context.Context, subjects model.PackageO var err error if len(subjects.Packages) > 0 { subject := model.PackageOrArtifactInput{Package: subjects.Packages[i]} - hasSBOM, err = c.IngestHasSbom(ctx, subject, *hasSBOMs[i]) + hasSBOM, err = c.IngestHasSbom(ctx, subject, *hasSBOMs[i], *includes[i]) if err != nil { return nil, gqlerror.Errorf("IngestHasSbom failed with err: %v", err) } } else { subject := model.PackageOrArtifactInput{Artifact: subjects.Artifacts[i]} - hasSBOM, err = c.IngestHasSbom(ctx, subject, *hasSBOMs[i]) + hasSBOM, err = c.IngestHasSbom(ctx, subject, *hasSBOMs[i], *includes[i]) if err != nil { return nil, gqlerror.Errorf("IngestHasSbom failed with err: %v", err) } @@ -82,11 +98,51 @@ func (c *demoClient) IngestHasSBOMs(ctx context.Context, subjects model.PackageO return modelHasSboms, nil } -func (c *demoClient) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, input model.HasSBOMInputSpec) (*model.HasSbom, error) { - return c.ingestHasSbom(ctx, subject, input, true) +func (c *demoClient) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, input model.HasSBOMInputSpec, includes model.HasSBOMIncludesInputSpec) (*model.HasSbom, error) { + funcName := "IngestHasSbom" + + var softwareIDs []uint32 + var dependencyIDs []uint32 + var occurrenceIDs []uint32 + var err error + + if includes.Software != nil { + if softwareIDs, err = parseIDs(includes.Software); err != nil { + return nil, gqlerror.Errorf("%v :: %s", funcName, err) + } + for _, id := range softwareIDs { + if err := c.validateSoftwareId(funcName, id); err != nil { + return nil, err + } + } + softwareIDs = sortAndRemoveDups(softwareIDs) + } + if includes.Dependencies != nil { + if dependencyIDs, err = parseIDs(includes.Dependencies); err != nil { + return nil, gqlerror.Errorf("%v :: %s", funcName, err) + } + for _, id := range dependencyIDs { + if _, err := byID[*isDependencyLink](id, c); err != nil { + return nil, gqlerror.Errorf("%v :: dependency id %d is not an ingested isDependency", funcName, id) + } + } + dependencyIDs = sortAndRemoveDups(dependencyIDs) + } + if includes.Occurrences != nil { + if occurrenceIDs, err = parseIDs(includes.Occurrences); err != nil { + return nil, gqlerror.Errorf("%v :: %s", funcName, err) + } + for _, id := range occurrenceIDs { + if isOccurrence, err := byID[*isOccurrenceStruct](id, c); isOccurrence == nil || err != nil { + return nil, gqlerror.Errorf("%v :: occurrence id %d is not an ingested isOccurrence", funcName, id) + } + } + occurrenceIDs = sortAndRemoveDups(occurrenceIDs) + } + return c.ingestHasSbom(ctx, subject, input, softwareIDs, dependencyIDs, occurrenceIDs, true) } -func (c *demoClient) ingestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, input model.HasSBOMInputSpec, readOnly bool) (*model.HasSbom, error) { +func (c *demoClient) ingestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, input model.HasSBOMInputSpec, includedSoftware, includedDependencies, includedOccurrences []uint32, readOnly bool) (*model.HasSbom, error) { funcName := "IngestHasSbom" lock(&c.m, readOnly) defer unlock(&c.m, readOnly) @@ -134,29 +190,35 @@ func (c *demoClient) ingestHasSbom(ctx context.Context, subject model.PackageOrA h.downloadLocation == input.DownloadLocation && h.origin == input.Origin && h.collector == input.Collector && - input.KnownSince.Equal(h.knownSince) { + input.KnownSince.Equal(h.knownSince) && + slices.Equal(h.includedSoftware, includedSoftware) && + slices.Equal(h.includedDependencies, includedDependencies) && + slices.Equal(h.includedOccurrences, includedOccurrences) { return c.convHasSBOM(h) } } if readOnly { c.m.RUnlock() - b, err := c.ingestHasSbom(ctx, subject, input, false) + b, err := c.ingestHasSbom(ctx, subject, input, includedSoftware, includedDependencies, includedOccurrences, false) c.m.RLock() // relock so that defer unlock does not panic return b, err } h := &hasSBOMStruct{ - id: c.getNextID(), - pkg: packageID, - artifact: artID, - uri: input.URI, - algorithm: algorithm, - digest: digest, - downloadLocation: input.DownloadLocation, - origin: input.Origin, - collector: input.Collector, - knownSince: input.KnownSince.UTC(), + id: c.getNextID(), + pkg: packageID, + artifact: artID, + uri: input.URI, + algorithm: algorithm, + digest: digest, + downloadLocation: input.DownloadLocation, + origin: input.Origin, + collector: input.Collector, + knownSince: input.KnownSince.UTC(), + includedSoftware: includedSoftware, + includedDependencies: includedDependencies, + includedOccurrences: includedOccurrences, } c.index[h.id] = h c.hasSBOMs = append(c.hasSBOMs, h) @@ -192,6 +254,57 @@ func (c *demoClient) convHasSBOM(in *hasSBOMStruct) (*model.HasSbom, error) { } out.Subject = c.convArtifact(art) } + if len(in.includedSoftware) > 0 { + out.IncludedSoftware = make([]model.PackageOrArtifact, 0, len(in.includedSoftware)) + for _, id := range in.includedSoftware { + switch node := c.index[id].(type) { + case *pkgVersionNode: + if pkg, err := c.buildPackageResponse(id, nil); err != nil { + return nil, err + } else { + out.IncludedSoftware = append(out.IncludedSoftware, pkg) + } + case *artStruct: + if art, err := c.buildArtifactResponse(id, nil); err != nil { + return nil, err + } else { + out.IncludedSoftware = append(out.IncludedSoftware, art) + } + default: + return nil, fmt.Errorf("expected Package or Artifact, found %s", reflect.TypeOf(node)) + } + } + } + if len(in.includedDependencies) > 0 { + out.IncludedDependencies = make([]*model.IsDependency, 0, len(in.includedDependencies)) + for _, id := range in.includedDependencies { + switch node := c.index[id].(type) { + case *isDependencyLink: + if isDep, err := c.buildIsDependency(node, nil, true); err != nil { + return nil, err + } else { + out.IncludedDependencies = append(out.IncludedDependencies, isDep) + } + default: + return nil, fmt.Errorf("expected IsDependency, found %s", reflect.TypeOf(node)) + } + } + } + if len(in.includedOccurrences) > 0 { + out.IncludedOccurrences = make([]*model.IsOccurrence, 0, len(in.includedOccurrences)) + for _, id := range in.includedOccurrences { + switch node := c.index[id].(type) { + case *isOccurrenceStruct: + if isOcc, err := c.convOccurrence(node); err != nil { + return nil, err + } else { + out.IncludedOccurrences = append(out.IncludedOccurrences, isOcc) + } + default: + return nil, fmt.Errorf("expected IsOccurrence, found %s", reflect.TypeOf(node)) + } + } + } return out, nil } @@ -283,6 +396,20 @@ func (c *demoClient) addHasSBOMIfMatch(out []*model.HasSbom, (filter.KnownSince != nil && filter.KnownSince.After(link.knownSince)) { return out, nil } + // collect packages and artifacts from included software + pkgs, artifacts, err := c.getPackageVersionAndArtifacts(link.includedSoftware) + if err != nil { + return out, err + } + + pkgFilters, artFilters := getPackageAndArtifactFilters(filter.IncludedSoftware) + + if !c.matchPackages(pkgFilters, pkgs) || !c.matchArtifacts(artFilters, artifacts) || + !c.matchDependencies(filter.IncludedDependencies, link.includedDependencies) || + !c.matchOccurrences(filter.IncludedOccurrences, link.includedOccurrences) { + return out, nil + } + if filter.Subject != nil { if filter.Subject.Package != nil { if link.pkg == 0 { @@ -311,3 +438,28 @@ func (c *demoClient) addHasSBOMIfMatch(out []*model.HasSbom, } return append(out, sb), nil } + +func getPackageAndArtifactFilters(filters []*model.PackageOrArtifactSpec) (pkgs []*model.PkgSpec, arts []*model.ArtifactSpec) { + for _, pkgOrArtSpec := range filters { + if pkgOrArtSpec.Package != nil { + pkgs = append(pkgs, pkgOrArtSpec.Package) + } else if pkgOrArtSpec.Artifact != nil { + arts = append(arts, pkgOrArtSpec.Artifact) + } + } + return +} + +func (c *demoClient) validateSoftwareId(funcName string, id uint32) error { + node, ok := c.index[id] + if !ok { + return gqlerror.Errorf("%v :: software id %d is invalid", funcName, id) + } + if _, ok = node.(*pkgVersionNode); !ok { + _, ok = node.(*artStruct) + } + if !ok { + return gqlerror.Errorf("%v :: software id %d is neither an ingested Package nor an ingested Artifact", funcName, id) + } + return nil +} diff --git a/pkg/assembler/backends/inmem/hasSBOM_test.go b/pkg/assembler/backends/inmem/hasSBOM_test.go index 3f889b455f..4cc4702925 100644 --- a/pkg/assembler/backends/inmem/hasSBOM_test.go +++ b/pkg/assembler/backends/inmem/hasSBOM_test.go @@ -17,6 +17,8 @@ package inmem_test import ( "context" + "fmt" + "reflect" "slices" "strings" "testing" @@ -28,6 +30,327 @@ import ( "github.com/guacsec/guac/pkg/assembler/graphql/model" ) +type testDependency struct { + pkg *model.PkgInputSpec + depPkg *model.PkgInputSpec + matchType model.MatchFlags + isDep *model.IsDependencyInputSpec +} + +type testOccurrence struct { + Subj *model.PackageOrSourceInput + Art *model.ArtifactInputSpec + isOcc *model.IsOccurrenceInputSpec +} + +// Test resources + +var includedPackage1QualifierKey = "p1_key" +var includedPackage1QualifierValue = "p1_value" + +var includedPackage1 = &model.PkgInputSpec{ + Type: "p1_type", + Namespace: ptrfrom.String("p1_namespace"), + Name: "p1_name", + Version: ptrfrom.String("v1.0.0-p1version"), + Qualifiers: []*model.PackageQualifierInputSpec{{ + Key: includedPackage1QualifierKey, + Value: includedPackage1QualifierValue, + }}, + Subpath: ptrfrom.String("p1_subpath"), +} + +var includedPackage2QualifierKey = "p2_key" +var includedPackage2QualifierValue = "p2_value" + +var includedPackage2 = &model.PkgInputSpec{ + Type: "p2_type", + Namespace: ptrfrom.String("p2_namespace"), + Name: "p2_name", + Version: ptrfrom.String("v1.0.0-p2version"), + Qualifiers: []*model.PackageQualifierInputSpec{{ + Key: includedPackage2QualifierKey, + Value: includedPackage2QualifierValue, + }}, + Subpath: ptrfrom.String("p2_subpath"), +} + +var includedPackage3 = &model.PkgInputSpec{ + Type: "p3_type", + Namespace: ptrfrom.String("p3_namespace"), + Name: "p3_name", + Version: ptrfrom.String("v1.0.0-p3version"), + Qualifiers: []*model.PackageQualifierInputSpec{}, + Subpath: ptrfrom.String("p3_subpath"), +} + +var includedPackages = []*model.PkgInputSpec{includedPackage1, includedPackage2, includedPackage3} + +var includedArtifact1 = &model.ArtifactInputSpec{ + Algorithm: "a1_algorithm", + Digest: "a1_digest", +} + +var includedArtifact2 = &model.ArtifactInputSpec{ + Algorithm: "a2_algorithm", + Digest: "a2_digest", +} + +var includedArtifacts = []*model.ArtifactInputSpec{includedArtifact1, includedArtifact2} + +var includedPackageArtifacts = &model.PackageOrArtifactInputs{ + Packages: includedPackages, + Artifacts: includedArtifacts, +} + +var includedDependency1 = &model.IsDependencyInputSpec{ + VersionRange: "dep1_range", + DependencyType: model.DependencyTypeDirect, + Justification: "dep1_justification", + Origin: "dep1_origin", + Collector: "dep1_collector", +} + +var includedDependency2 = &model.IsDependencyInputSpec{ + VersionRange: "dep2_range", + DependencyType: model.DependencyTypeIndirect, + Justification: "dep2_justification", + Origin: "dep2_origin", + Collector: "dep2_collector", +} + +var includedTestDependency1 = &testDependency{ + pkg: includedPackage1, + depPkg: includedPackage2, + matchType: mSpecific, + isDep: includedDependency1, +} + +var includedTestDependency2 = &testDependency{ + pkg: includedPackage1, + depPkg: includedPackage3, + matchType: mSpecific, + isDep: includedDependency2, +} + +var includedTestDependencies = []testDependency{*includedTestDependency1, *includedTestDependency2} + +var includedSource = &model.SourceInputSpec{ + Type: "src_type", + Namespace: "src_namespace", + Name: "src_name", + Tag: ptrfrom.String("src_tag"), + Commit: ptrfrom.String("src_commit"), +} + +var includedSources = []*model.SourceInputSpec{includedSource} + +var includedOccurrence = &model.IsOccurrenceInputSpec{ + Justification: "occ_justification", + Origin: "occ_origin", + Collector: "occ_collector", +} + +var includedTestOccurrences = []testOccurrence{{ + Subj: &model.PackageOrSourceInput{Package: includedPackage1}, + Art: includedArtifact1, + isOcc: includedOccurrence, +}, { + Subj: &model.PackageOrSourceInput{Source: includedSource}, + Art: includedArtifact1, + isOcc: includedOccurrence, +}} + +var includedHasSBOM = &model.HasSBOMInputSpec{ + URI: "sbom_URI", + Algorithm: "sbom_algorithm", + Digest: "sbom_digest", + DownloadLocation: "sbom_download_location", + Origin: "sbom_origin", + Collector: "sbom_collector", +} + +var includedTestExpectedPackage1 = &model.Package{ + Type: "p1_type", + Namespaces: []*model.PackageNamespace{{ + Namespace: "p1_namespace", + Names: []*model.PackageName{{ + Name: "p1_name", + Versions: []*model.PackageVersion{{ + Version: "v1.0.0-p1version", + Qualifiers: []*model.PackageQualifier{{ + Key: includedPackage1QualifierKey, + Value: includedPackage1QualifierValue, + }}, + Subpath: "p1_subpath", + }}, + }}, + }}, +} + +var includedTestExpectedPackage2 = &model.Package{ + Type: "p2_type", + Namespaces: []*model.PackageNamespace{{ + Namespace: "p2_namespace", + Names: []*model.PackageName{{ + Name: "p2_name", + Versions: []*model.PackageVersion{{ + Version: "v1.0.0-p2version", + Qualifiers: []*model.PackageQualifier{{ + Key: includedPackage2QualifierKey, + Value: includedPackage2QualifierValue, + }}, + Subpath: "p2_subpath", + }}, + }}, + }}, +} + +var includedTestExpectedPackage3 = &model.Package{ + Type: "p3_type", + Namespaces: []*model.PackageNamespace{{ + Namespace: "p3_namespace", + Names: []*model.PackageName{{ + Name: "p3_name", + Versions: []*model.PackageVersion{{ + Version: "v1.0.0-p3version", + Qualifiers: []*model.PackageQualifier{}, + Subpath: "p3_subpath", + }}, + }}, + }}, +} + +var includedTestExpectedArtifact1 = &model.Artifact{ + Algorithm: "a1_algorithm", + Digest: "a1_digest", +} + +var includedTestExpectedArtifact2 = &model.Artifact{ + Algorithm: "a2_algorithm", + Digest: "a2_digest", +} + +var includedTestExpectedSource = &model.Source{ + Type: "src_type", + Namespaces: []*model.SourceNamespace{{ + Namespace: "src_namespace", + Names: []*model.SourceName{{ + Name: "src_name", + Tag: ptrfrom.String("src_tag"), + Commit: ptrfrom.String("src_commit"), + }}, + }}, +} + +var includedTestExpectedSBOM = &model.HasSbom{ + Subject: includedTestExpectedPackage1, + URI: "sbom_URI", + Algorithm: "sbom_algorithm", + Digest: "sbom_digest", + DownloadLocation: "sbom_download_location", + Origin: "sbom_origin", + Collector: "sbom_collector", + IncludedSoftware: []model.PackageOrArtifact{ + includedTestExpectedPackage1, + includedTestExpectedPackage2, + includedTestExpectedPackage3, + includedTestExpectedArtifact1, + includedTestExpectedArtifact2, + }, + IncludedDependencies: []*model.IsDependency{{ + Package: includedTestExpectedPackage1, + DependencyPackage: includedTestExpectedPackage2, + VersionRange: "dep1_range", + DependencyType: model.DependencyTypeDirect, + Justification: "dep1_justification", + Origin: "dep1_origin", + Collector: "dep1_collector", + }, { + Package: includedTestExpectedPackage1, + DependencyPackage: includedTestExpectedPackage3, + VersionRange: "dep2_range", + DependencyType: model.DependencyTypeIndirect, + Justification: "dep2_justification", + Origin: "dep2_origin", + Collector: "dep2_collector", + }}, + IncludedOccurrences: []*model.IsOccurrence{{ + Subject: includedTestExpectedPackage1, + Artifact: includedTestExpectedArtifact1, + Justification: "occ_justification", + Origin: "occ_origin", + Collector: "occ_collector", + }, { + Subject: includedTestExpectedSource, + Artifact: includedTestExpectedArtifact1, + Justification: "occ_justification", + Origin: "occ_origin", + Collector: "occ_collector", + }}, +} + +// End of Test resources + +func getPackageVersionFromIngestedPackage(pkg *model.Package) (string, error) { + if pkg != nil { + if len(pkg.Namespaces) == 1 { + namespace := pkg.Namespaces[0] + if len(namespace.Names) == 1 { + name := namespace.Names[0] + if len(name.Versions) == 1 { + version := name.Versions[0] + return version.ID, nil + } + } + } + } + return "", fmt.Errorf("could not retrieve ingested PackageVersion from package id %v", pkg) +} + +func getNodeIds(nodes []model.Node) ([]string, error) { + var ids []string + for _, node := range nodes { + var id *string + switch typed := node.(type) { + case *model.Package: + switch len(typed.Namespaces) { + case 0: + id = &typed.ID + case 1: + namespace := typed.Namespaces[0] + switch len(namespace.Names) { + case 0: + id = &namespace.ID + case 1: + name := namespace.Names[0] + switch len(name.Versions) { + case 0: + id = &name.ID + case 1: + version := name.Versions[0] + id = &version.ID + } + } + } + case *model.Artifact: + id = &typed.ID + case *model.HasSbom: + id = &typed.ID + case *model.IsDependency: + id = &typed.ID + case *model.IsOccurrence: + id = &typed.ID + } + if id == nil { + return nil, fmt.Errorf("Could not idenitfy correct id for node: %v", reflect.TypeOf(node)) + } else { + ids = append(ids, *id) + } + } + return ids, nil +} + func TestHasSBOM(t *testing.T) { curTime := time.Now() timeAfterOneSecond := curTime.Add(time.Second) @@ -39,6 +362,10 @@ func TestHasSBOM(t *testing.T) { Name string InPkg []*model.PkgInputSpec InArt []*model.ArtifactInputSpec + PkgArt *model.PackageOrArtifactInputs + InSrc []*model.SourceInputSpec + IsDeps []testDependency + IsOccs []testOccurrence Calls []call Query *model.HasSBOMSpec ExpHS []*model.HasSbom @@ -48,6 +375,9 @@ func TestHasSBOM(t *testing.T) { { Name: "HappyPath", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ @@ -63,14 +393,18 @@ func TestHasSBOM(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: p1out, - URI: "test uri", + Subject: p1out, + URI: "test uri", + IncludedSoftware: []model.PackageOrArtifact{p1out}, }, }, }, { Name: "Ingest same twice", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ @@ -94,14 +428,18 @@ func TestHasSBOM(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: p1out, - URI: "test uri", + Subject: p1out, + URI: "test uri", + IncludedSoftware: []model.PackageOrArtifact{p1out}, }, }, }, { Name: "Query on URI", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ @@ -125,8 +463,9 @@ func TestHasSBOM(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: p1out, - URI: "test uri one", + Subject: p1out, + URI: "test uri one", + IncludedSoftware: []model.PackageOrArtifact{p1out}, }, }, }, @@ -163,12 +502,29 @@ func TestHasSBOM(t *testing.T) { }, { Name: "Query on Package", - InPkg: []*model.PkgInputSpec{p1, p2}, + InPkg: []*model.PkgInputSpec{p2, p4}, InArt: []*model.ArtifactInputSpec{a1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p2, p4}, + Artifacts: []*model.ArtifactInputSpec{a1}, + }, + IsDeps: []testDependency{{ + pkg: p2, + depPkg: p4, + matchType: mSpecific, + isDep: &model.IsDependencyInputSpec{ + Justification: "test justification", + }, + }}, + IsOccs: []testOccurrence{{ + Subj: &model.PackageOrSourceInput{Package: p4}, + Art: a1, + isOcc: &model.IsOccurrenceInputSpec{Justification: "test justification"}, + }}, Calls: []call{ { Sub: model.PackageOrArtifactInput{ - Package: p1, + Package: p2, }, HS: &model.HasSBOMInputSpec{ URI: "test uri", @@ -176,7 +532,7 @@ func TestHasSBOM(t *testing.T) { }, { Sub: model.PackageOrArtifactInput{ - Package: p2, + Package: p4, }, HS: &model.HasSBOMInputSpec{ URI: "test uri", @@ -200,19 +556,34 @@ func TestHasSBOM(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: p2out, - URI: "test uri", + Subject: p2out, + URI: "test uri", + IncludedSoftware: []model.PackageOrArtifact{p2out, p4out, a1out}, + IncludedDependencies: []*model.IsDependency{{ + Package: p2out, + DependencyPackage: p4out, + Justification: "test justification", + }}, + IncludedOccurrences: []*model.IsOccurrence{{ + Subject: p4out, + Artifact: a1out, + Justification: "test justification", + }}, }, }, }, { Name: "Query on Artifact", - InPkg: []*model.PkgInputSpec{p1}, + InPkg: []*model.PkgInputSpec{p2}, InArt: []*model.ArtifactInputSpec{a1, a2}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p2}, + Artifacts: []*model.ArtifactInputSpec{a1, a2}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ - Package: p1, + Package: p2, }, HS: &model.HasSBOMInputSpec{ URI: "test uri", @@ -244,14 +615,18 @@ func TestHasSBOM(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: a2out, - URI: "test uri", + Subject: a2out, + URI: "test uri", + IncludedSoftware: []model.PackageOrArtifact{p2out, a1out, a2out}, }, }, }, { Name: "Query on Algorithm", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ @@ -275,18 +650,36 @@ func TestHasSBOM(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: p1out, - Algorithm: "qwerasdf", + Subject: p1out, + Algorithm: "qwerasdf", + IncludedSoftware: []model.PackageOrArtifact{p1out}, }, }, }, { Name: "Query on Digest", - InPkg: []*model.PkgInputSpec{p1}, + InPkg: []*model.PkgInputSpec{p2, p4}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p2, p4}, + Artifacts: []*model.ArtifactInputSpec{a1}, + }, + IsDeps: []testDependency{{ + pkg: p2, + depPkg: p4, + matchType: mSpecific, + isDep: &model.IsDependencyInputSpec{ + Justification: "test justification", + }, + }}, + IsOccs: []testOccurrence{{ + Subj: &model.PackageOrSourceInput{Package: p4}, + Art: a1, + isOcc: &model.IsOccurrenceInputSpec{Justification: "test justification"}, + }}, Calls: []call{ { Sub: model.PackageOrArtifactInput{ - Package: p1, + Package: p2, }, HS: &model.HasSBOMInputSpec{ Digest: "QWERasdf", @@ -294,7 +687,7 @@ func TestHasSBOM(t *testing.T) { }, { Sub: model.PackageOrArtifactInput{ - Package: p1, + Package: p2, }, HS: &model.HasSBOMInputSpec{ Digest: "QWERasdf two", @@ -306,14 +699,28 @@ func TestHasSBOM(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: p1out, - Digest: "qwerasdf", + Subject: p2out, + Digest: "qwerasdf", + IncludedSoftware: []model.PackageOrArtifact{p2out, p4out, a1out}, + IncludedDependencies: []*model.IsDependency{{ + Package: p2out, + DependencyPackage: p4out, + Justification: "test justification", + }}, + IncludedOccurrences: []*model.IsOccurrence{{ + Subject: p4out, + Artifact: a1out, + Justification: "test justification", + }}, }, }, }, { Name: "Query on DownloadLocation", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ @@ -339,12 +746,16 @@ func TestHasSBOM(t *testing.T) { { Subject: p1out, DownloadLocation: "location two", + IncludedSoftware: []model.PackageOrArtifact{p1out}, }, }, }, { Name: "Query none", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ @@ -414,6 +825,9 @@ func TestHasSBOM(t *testing.T) { { Name: "Query on ID", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ @@ -439,6 +853,7 @@ func TestHasSBOM(t *testing.T) { { Subject: p1out, DownloadLocation: "location two", + IncludedSoftware: []model.PackageOrArtifact{p1out}, }, }, }, @@ -459,6 +874,9 @@ func TestHasSBOM(t *testing.T) { { Name: "Query bad ID", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ @@ -485,6 +903,9 @@ func TestHasSBOM(t *testing.T) { { Name: "Query without hasSBOMSpec", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ @@ -500,9 +921,1731 @@ func TestHasSBOM(t *testing.T) { { Subject: p1out, DownloadLocation: "location one", + IncludedSoftware: []model.PackageOrArtifact{p1out}, }, }, }, + { + Name: "Includeds - include without filters", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedSoftware - Valid Included Package ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{ID: ptrfrom.String("8")}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedSoftware - Invalid Included Package ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{ID: ptrfrom.String("10000")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedSoftware - Valid Included Package Namespace", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Namespace: includedPackage2.Namespace}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedSoftware - Invalid Included Package Namespace", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Namespace: ptrfrom.String("invalid_namespace")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedSoftware - Valid Included Package Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Name: &includedPackage2.Name}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedSoftware - Invalid Included Package Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Name: ptrfrom.String("invalid_name")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedSoftware - Valid Included Package Version", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Version: includedPackage2.Version}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedSoftware - Invalid Included Package Version", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Version: ptrfrom.String("v1.0.0-invalid-version")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedSoftware - Valid Included Package Qualifier", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: includedPackage2QualifierKey, Value: ptrfrom.String(includedPackage2QualifierValue)}}}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedSoftware - Invalid Included Package Qualifier Key", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: "invalid_qualifier_key", Value: ptrfrom.String(includedPackage2QualifierValue)}}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedSoftware - Invalid Subject Package Qualifier Value", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: includedPackage2QualifierKey, Value: ptrfrom.String("invalid_qualifier_value")}}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedSoftware - Valid Included Package Subpath", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Subpath: includedPackage2.Subpath}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedSoftware - Invalid Included Package Subpath", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Package: &model.PkgSpec{Subpath: ptrfrom.String("invalid_subpath")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedSoftware - Valid Included Artifact ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Artifact: &model.ArtifactSpec{ID: ptrfrom.String("13")}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedSoftware - Invalid Included Artifact ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Artifact: &model.ArtifactSpec{ID: ptrfrom.String("10000")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedSoftware - Valid Included Artifact Algorithm", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Artifact: &model.ArtifactSpec{Algorithm: &includedArtifact1.Algorithm}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedSoftware - Invalid Included Artifact Algorithm", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Artifact: &model.ArtifactSpec{Algorithm: ptrfrom.String("invalid_algorithm")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedSoftware - Valid Included Artifact Digest", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Artifact: &model.ArtifactSpec{Digest: &includedArtifact1.Digest}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedSoftware - Invalid Included Artifact Digest", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedSoftware: []*model.PackageOrArtifactSpec{{Artifact: &model.ArtifactSpec{Digest: ptrfrom.String("invalid_digest")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{ID: ptrfrom.String("19")}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{ID: ptrfrom.String("10000")}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Package ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{ID: ptrfrom.String("4")}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included Package ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{ID: ptrfrom.String("10000")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Package Namespace", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Namespace: includedPackage1.Namespace}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included Package Namespace", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Namespace: ptrfrom.String("invalid_namespace")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Package Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Name: &includedPackage1.Name}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included Package Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Name: ptrfrom.String("invalid_name")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Package Version", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Version: includedPackage1.Version}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included Package Version", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Version: ptrfrom.String("v1.0.0-invalid-version")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Package Qualifier", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: includedPackage1QualifierKey, Value: ptrfrom.String(includedPackage1QualifierValue)}}}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included Package Qualifier Key", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: "invalid_qualifier_key", Value: ptrfrom.String(includedPackage1QualifierValue)}}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Invalid Subject Package Qualifier Value", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: includedPackage1QualifierKey, Value: ptrfrom.String("invalid_qualifier_value")}}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Package Subpath", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Subpath: includedPackage1.Subpath}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included Package Subpath", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Subpath: ptrfrom.String("invalid_subpath")}}}}, + ExpHS: nil, + }, + + { + Name: "IncludedDependencies - Valid Included DependencyPackage ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{ID: ptrfrom.String("8")}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included DependencyPackage ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{ID: ptrfrom.String("10000")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included DependencyPackage Namespace", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Namespace: includedPackage2.Namespace}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included DependencyPackage Namespace", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Namespace: ptrfrom.String("invalid_namespace")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included DependencyPackage Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Name: &includedPackage2.Name}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included DependencyPackage Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Name: ptrfrom.String("invalid_name")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included DependencyPackage Version", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Version: includedPackage2.Version}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included DependencyPackage Version", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Version: ptrfrom.String("v1.0.0-invalid-version")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included DependencyPackage Qualifier", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: includedPackage2QualifierKey, Value: ptrfrom.String(includedPackage2QualifierValue)}}}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included DependencyPackage Qualifier Key", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: "invalid_qualifier_key", Value: ptrfrom.String(includedPackage2QualifierValue)}}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Invalid Subject DependencyPackage Qualifier Value", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: includedPackage2QualifierKey, Value: ptrfrom.String("invalid_qualifier_value")}}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included DependencyPackage Subpath", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Subpath: includedPackage2.Subpath}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included DependencyPackage Subpath", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyPackage: &model.PkgSpec{Subpath: ptrfrom.String("invalid_subpath")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Package ID and DependencyPackage ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{ID: ptrfrom.String("4")}, DependencyPackage: &model.PkgSpec{ID: ptrfrom.String("8")}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Valid Included Package ID and Invalid DependencyPackage ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{ID: ptrfrom.String("4")}, DependencyPackage: &model.PkgSpec{ID: ptrfrom.String("10000")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Invalid Included Package ID and Valid DependencyPackage ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{ID: ptrfrom.String("10000")}, DependencyPackage: &model.PkgSpec{ID: ptrfrom.String("8")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Package Name and DependencyPackage Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Name: &includedPackage1.Name}, DependencyPackage: &model.PkgSpec{Name: &includedPackage2.Name}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Valid Included Package Name and Invalid DependencyPackage Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Name: &includedPackage1.Name}, DependencyPackage: &model.PkgSpec{Name: ptrfrom.String("invalid_name")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Invalid Included Package Name and Valid DependencyPackage Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Package: &model.PkgSpec{Name: ptrfrom.String("invalid_name")}, DependencyPackage: &model.PkgSpec{Name: &includedPackage2.Name}}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included VersionRange", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{VersionRange: &includedDependency1.VersionRange}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included VersionRange", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{VersionRange: ptrfrom.String("invalid_range")}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included DependencyType", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyType: &includedDependency1.DependencyType}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included DependencyType", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{DependencyType: (*model.DependencyType)(ptrfrom.String(string(model.DependencyTypeUnknown)))}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Justification", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Justification: &includedDependency1.Justification}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included Justification", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Justification: ptrfrom.String("invalid_justification")}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Origin", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Origin: &includedDependency1.Origin}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included Origin", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Origin: ptrfrom.String("invalid_origin")}}}, + ExpHS: nil, + }, + { + Name: "IncludedDependencies - Valid Included Collector", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Collector: &includedDependency1.Collector}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedDependencies - Invalid Included Collector", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedDependencies: []*model.IsDependencySpec{{Collector: ptrfrom.String("invalid_collector")}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{ID: ptrfrom.String("21")}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{ID: ptrfrom.String("10000")}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Package ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{ID: ptrfrom.String("4")}}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Package ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{ID: ptrfrom.String("10000")}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Package Namespace", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Namespace: includedPackage1.Namespace}}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Package Namespace", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Namespace: ptrfrom.String("invalid_namespace")}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Package Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Name: ptrfrom.String("p1_name")}}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Package Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Name: ptrfrom.String("invalid_name")}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Package Version", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Version: includedPackage1.Version}}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Package Version", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Version: ptrfrom.String("v1.0.0-invalid-version")}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Package Qualifier", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: includedPackage1QualifierKey, Value: ptrfrom.String(includedPackage1QualifierValue)}}}}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Package Qualifier Key", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: "invalid_qualifier_key", Value: ptrfrom.String(includedPackage1QualifierValue)}}}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Invalid Subject Package Qualifier Value", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Qualifiers: []*model.PackageQualifierSpec{{Key: includedPackage1QualifierKey, Value: ptrfrom.String("invalid_qualifier_value")}}}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Package Subpath", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Subpath: includedPackage1.Subpath}}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Package Subpath", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Package: &model.PkgSpec{Subpath: ptrfrom.String("invalid_subpath")}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Source ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Source: &model.SourceSpec{ID: ptrfrom.String("17")}}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Source ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Source: &model.SourceSpec{ID: ptrfrom.String("10000")}}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Source", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + // TODO (knrc) - source currently needs to be an exact match, does this need to change? + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Source: &model.SourceSpec{ + Type: &includedSource.Type, + Namespace: &includedSource.Namespace, + Name: &includedSource.Name, + Tag: includedSource.Tag, + Commit: includedSource.Commit, + }}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Source Type", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Source: &model.SourceSpec{ + Type: ptrfrom.String("invalid_type"), + Namespace: &includedSource.Namespace, + Name: &includedSource.Name, + Tag: includedSource.Tag, + Commit: includedSource.Commit, + }}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Invalid Included Source Namespace", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Source: &model.SourceSpec{ + Type: &includedSource.Type, + Namespace: ptrfrom.String("invalid_namespace"), + Name: &includedSource.Name, + Tag: includedSource.Tag, + Commit: includedSource.Commit, + }}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Invalid Included Source Name", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Source: &model.SourceSpec{ + Type: &includedSource.Type, + Namespace: &includedSource.Namespace, + Name: ptrfrom.String("invalid_name"), + Tag: includedSource.Tag, + Commit: includedSource.Commit, + }}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Invalid Included Source Tag", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Source: &model.SourceSpec{ + Type: &includedSource.Type, + Namespace: &includedSource.Namespace, + Name: &includedSource.Name, + Tag: ptrfrom.String("invalid_tag"), + Commit: includedSource.Commit, + }}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Invalid Included Source Commit", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Subject: &model.PackageOrSourceSpec{Source: &model.SourceSpec{ + Type: &includedSource.Type, + Namespace: &includedSource.Namespace, + Name: &includedSource.Name, + Tag: includedSource.Tag, + Commit: ptrfrom.String("invalid_commit"), + }}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Artifact ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Artifact: &model.ArtifactSpec{ID: ptrfrom.String("13")}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Artifact ID", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Artifact: &model.ArtifactSpec{ID: ptrfrom.String("10000")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Artifact Algorithm", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Artifact: &model.ArtifactSpec{Algorithm: &includedArtifact1.Algorithm}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Artifact Algorithm", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Artifact: &model.ArtifactSpec{Algorithm: ptrfrom.String("invalid_algorithm")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Artifact Digest", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Artifact: &model.ArtifactSpec{Digest: &includedArtifact1.Digest}}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Artifact Digest", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Artifact: &model.ArtifactSpec{Digest: ptrfrom.String("invalid_digest")}}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Justification", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Justification: &includedOccurrence.Justification}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Justification", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Justification: ptrfrom.String("invalid_justification")}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Origin", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Origin: &includedOccurrence.Origin}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Origin", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Origin: ptrfrom.String("invalid_origin")}}}, + ExpHS: nil, + }, + { + Name: "IncludedOccurrences - Valid Included Collector", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Collector: &includedOccurrence.Collector}}}, + ExpHS: []*model.HasSbom{includedTestExpectedSBOM}, + }, + { + Name: "IncludedOccurrences - Invalid Included Collector", + InPkg: includedPackages, + InArt: includedArtifacts, + InSrc: includedSources, + PkgArt: includedPackageArtifacts, + IsDeps: includedTestDependencies, + IsOccs: includedTestOccurrences, + Calls: []call{{ + Sub: model.PackageOrArtifactInput{ + Package: includedPackage1, + }, + HS: includedHasSBOM, + }}, + Query: &model.HasSBOMSpec{IncludedOccurrences: []*model.IsOccurrenceSpec{{Collector: ptrfrom.String("invalid_collector")}}}, + ExpHS: nil, + }, } ignoreID := cmp.FilterPath(func(p cmp.Path) bool { return strings.Compare(".ID", p[len(p)-1].String()) == 0 @@ -524,8 +2667,50 @@ func TestHasSBOM(t *testing.T) { t.Fatalf("Could not ingest artifact: %v", err) } } + for _, s := range test.InSrc { + if _, err := b.IngestSource(ctx, *s); err != nil { + t.Fatalf("Could not ingest source: %v", err) + } + } + includes := model.HasSBOMIncludesInputSpec{} + if test.PkgArt != nil { + if pkgs, err := b.IngestPackages(ctx, test.PkgArt.Packages); err != nil { + t.Fatalf("Could not ingest package: %v", err) + } else { + for _, pkg := range pkgs { + if id, err := getPackageVersionFromIngestedPackage(pkg); err != nil { + t.Fatalf("Could not determine PackageVersion id: %v", err) + } else { + includes.Software = append(includes.Software, id) + } + } + } + if arts, err := b.IngestArtifacts(ctx, test.PkgArt.Artifacts); err != nil { + t.Fatalf("Could not ingest artifact: %v", err) + } else { + for _, art := range arts { + includes.Software = append(includes.Software, art.ID) + } + } + } + + for _, dep := range test.IsDeps { + if isDep, err := b.IngestDependency(ctx, *dep.pkg, *dep.depPkg, dep.matchType, *dep.isDep); err != nil { + t.Fatalf("Could not ingest dependency: %v", err) + } else { + includes.Dependencies = append(includes.Dependencies, isDep.ID) + } + } + + for _, occ := range test.IsOccs { + if isOcc, err := b.IngestOccurrence(ctx, *occ.Subj, *occ.Art, *occ.isOcc); err != nil { + t.Fatalf("Could not ingest occurrence: %v", err) + } else { + includes.Occurrences = append(includes.Occurrences, isOcc.ID) + } + } for _, o := range test.Calls { - _, err := b.IngestHasSbom(ctx, o.Sub, *o.HS) + _, err := b.IngestHasSbom(ctx, o.Sub, *o.HS, includes) if (err != nil) != test.ExpIngestErr { t.Fatalf("did not get expected ingest error, want: %v, got: %v", test.ExpIngestErr, err) } @@ -556,6 +2741,9 @@ func TestIngestHasSBOMs(t *testing.T) { Name string InPkg []*model.PkgInputSpec InArt []*model.ArtifactInputSpec + PkgArt *model.PackageOrArtifactInputs + IsDeps []testDependency + IsOccs []testOccurrence Calls []call Query *model.HasSBOMSpec ExpHS []*model.HasSbom @@ -565,6 +2753,9 @@ func TestIngestHasSBOMs(t *testing.T) { { Name: "HappyPath", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInputs{ @@ -582,14 +2773,18 @@ func TestIngestHasSBOMs(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: p1out, - URI: "test uri", + Subject: p1out, + URI: "test uri", + IncludedSoftware: []model.PackageOrArtifact{p1out}, }, }, }, { Name: "Ingest same twice", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInputs{ @@ -610,14 +2805,18 @@ func TestIngestHasSBOMs(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: p1out, - URI: "test uri", + Subject: p1out, + URI: "test uri", + IncludedSoftware: []model.PackageOrArtifact{p1out}, }, }, }, { Name: "Query on URI", InPkg: []*model.PkgInputSpec{p1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInputs{ @@ -638,19 +2837,37 @@ func TestIngestHasSBOMs(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: p1out, - URI: "test uri one", + Subject: p1out, + URI: "test uri one", + IncludedSoftware: []model.PackageOrArtifact{p1out}, }, }, }, { Name: "Query on Package", - InPkg: []*model.PkgInputSpec{p1, p2}, + InPkg: []*model.PkgInputSpec{p2, p4}, InArt: []*model.ArtifactInputSpec{a1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p2, p4}, + Artifacts: []*model.ArtifactInputSpec{a1}, + }, + IsDeps: []testDependency{{ + pkg: p2, + depPkg: p4, + matchType: mSpecific, + isDep: &model.IsDependencyInputSpec{ + Justification: "test justification", + }, + }}, + IsOccs: []testOccurrence{{ + Subj: &model.PackageOrSourceInput{Package: p4}, + Art: a1, + isOcc: &model.IsOccurrenceInputSpec{Justification: "test justification"}, + }}, Calls: []call{ { Sub: model.PackageOrArtifactInputs{ - Packages: []*model.PkgInputSpec{p1, p2}, + Packages: []*model.PkgInputSpec{p2, p4}, }, HS: []*model.HasSBOMInputSpec{ { @@ -681,8 +2898,19 @@ func TestIngestHasSBOMs(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: p2out, - URI: "test uri", + Subject: p2out, + URI: "test uri", + IncludedSoftware: []model.PackageOrArtifact{p2out, p4out, a1out}, + IncludedDependencies: []*model.IsDependency{{ + Package: p2out, + DependencyPackage: p4out, + Justification: "test justification", + }}, + IncludedOccurrences: []*model.IsOccurrence{{ + Subject: p4out, + Artifact: a1out, + Justification: "test justification", + }}, }, }, }, @@ -690,6 +2918,15 @@ func TestIngestHasSBOMs(t *testing.T) { Name: "Query on Artifact", InPkg: []*model.PkgInputSpec{p1}, InArt: []*model.ArtifactInputSpec{a1, a2}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + Artifacts: []*model.ArtifactInputSpec{a1, a2}, + }, + IsOccs: []testOccurrence{{ + Subj: &model.PackageOrSourceInput{Package: p1}, + Art: a2, + isOcc: &model.IsOccurrenceInputSpec{Justification: "test justification"}, + }}, Calls: []call{ { Sub: model.PackageOrArtifactInputs{ @@ -724,8 +2961,14 @@ func TestIngestHasSBOMs(t *testing.T) { }, ExpHS: []*model.HasSbom{ { - Subject: a2out, - URI: "test uri", + Subject: a2out, + URI: "test uri", + IncludedSoftware: []model.PackageOrArtifact{p1out, a1out, a2out}, + IncludedOccurrences: []*model.IsOccurrence{{ + Subject: p1out, + Artifact: a2out, + Justification: "test justification", + }}, }, }, }, @@ -750,8 +2993,49 @@ func TestIngestHasSBOMs(t *testing.T) { t.Fatalf("Could not ingest artifact: %v", err) } } + includes := model.HasSBOMIncludesInputSpec{} + if test.PkgArt != nil { + if pkgs, err := b.IngestPackages(ctx, test.PkgArt.Packages); err != nil { + t.Fatalf("Could not ingest package: %v", err) + } else { + for _, pkg := range pkgs { + if id, err := getPackageVersionFromIngestedPackage(pkg); err != nil { + t.Fatalf("Could not determine PackageVersion id: %v", err) + } else { + includes.Software = append(includes.Software, id) + } + } + } + if arts, err := b.IngestArtifacts(ctx, test.PkgArt.Artifacts); err != nil { + t.Fatalf("Could not ingest artifact: %v", err) + } else { + for _, art := range arts { + includes.Software = append(includes.Software, art.ID) + } + } + } + + for _, dep := range test.IsDeps { + if isDep, err := b.IngestDependency(ctx, *dep.pkg, *dep.depPkg, dep.matchType, *dep.isDep); err != nil { + t.Fatalf("Could not ingest dependency: %v", err) + } else { + includes.Dependencies = append(includes.Dependencies, isDep.ID) + } + } + + for _, occ := range test.IsOccs { + if isOcc, err := b.IngestOccurrence(ctx, *occ.Subj, *occ.Art, *occ.isOcc); err != nil { + t.Fatalf("Could not ingest occurrence: %v", err) + } else { + includes.Occurrences = append(includes.Occurrences, isOcc.ID) + } + } for _, o := range test.Calls { - _, err := b.IngestHasSBOMs(ctx, o.Sub, o.HS) + var sbomIncludes []*model.HasSBOMIncludesInputSpec + for count := 0; count < len(o.HS); count++ { + sbomIncludes = append(sbomIncludes, &includes) + } + _, err := b.IngestHasSBOMs(ctx, o.Sub, o.HS, sbomIncludes) if (err != nil) != test.ExpIngestErr { t.Fatalf("did not get expected ingest error, want: %v, got: %v", test.ExpIngestErr, err) } @@ -778,35 +3062,78 @@ func TestHasSBOMNeighbors(t *testing.T) { Sub model.PackageOrArtifactInput HS *model.HasSBOMInputSpec } + tests := []struct { Name string InPkg []*model.PkgInputSpec InArt []*model.ArtifactInputSpec + PkgArt *model.PackageOrArtifactInputs + IsDeps []testDependency + IsOccs []testOccurrence Calls []call ExpNeighbors map[string][]string }{ { Name: "HappyPath", - InPkg: []*model.PkgInputSpec{p1}, + InPkg: []*model.PkgInputSpec{p2, p4}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p2, p4}, + Artifacts: []*model.ArtifactInputSpec{a1}, + }, + IsDeps: []testDependency{{ + pkg: p2, + depPkg: p4, + matchType: mSpecific, + isDep: &model.IsDependencyInputSpec{ + Justification: "test justification", + }, + }}, + IsOccs: []testOccurrence{{ + Subj: &model.PackageOrSourceInput{Package: p4}, + Art: a1, + isOcc: &model.IsOccurrenceInputSpec{Justification: "test justification"}, + }}, Calls: []call{ { Sub: model.PackageOrArtifactInput{ - Package: p1, + Package: p2, }, HS: &model.HasSBOMInputSpec{ URI: "test uri", }, }, }, + /* + * 1 - p2 Package + * 2 - p2 PackageNamespace + * 3 - p2 PackageName + * 4 - p2 PackageVersion + * 5 - p4 Package + * 6 - p4 PackageNamespace + * 7 - p4 PackageName + * 8 - p4 PackageVersion + * 9 - a1 Artifact + * 10 - IsDependency + * 11 - IsOccurrence + * 12 - HasSBOM + */ ExpNeighbors: map[string][]string{ - "4": {"1", "5"}, // pkg version - "5": {"1"}, // hasSBOM + "4": {"3", "10", "12"}, // p2 PackageVersion -> p2 PackageName, IsDependency, HasSBOM + "8": {"7", "10", "11"}, // p4 PackageVersion -> P4 PackageName, IsDependency, IsOccurrence + "9": {"11"}, // a1 Artifact -> IsOccurrence + "10": {"4", "8"}, // IsDependency -> p2 PackageVersion, p4 PackageVersion + "11": {"8", "9"}, // IsOccurrence -> p4 PackageVersion, a1 Artifact + "12": {"4", "8", "9", "10", "11"}, // HasSBOM -> p2 PackageVersion, p4 PackageVersion, a1 Artifact, IsDependency, IsOccurrence }, }, { Name: "Pkg and Artifact", InPkg: []*model.PkgInputSpec{p1}, InArt: []*model.ArtifactInputSpec{a1}, + PkgArt: &model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{p1}, + Artifacts: []*model.ArtifactInputSpec{a1}, + }, Calls: []call{ { Sub: model.PackageOrArtifactInput{ @@ -825,11 +3152,20 @@ func TestHasSBOMNeighbors(t *testing.T) { }, }, }, + /* + * 1 - p1 Package + * 2 - p1 PackageNamespace + * 3 - p1 PackageName + * 4 - p1 PackageVersion + * 5 - a1 Artifact + * 6 - HasSBOM + * 7 - HasSBOM + */ ExpNeighbors: map[string][]string{ - "4": {"1", "6"}, // pkg version -> hs1 - "5": {"7"}, // artifact -> hs2 - "6": {"1"}, // hs1 -> pkg version - "7": {"5"}, // hs2 -> artifact + "4": {"3", "6"}, // p1 PackageVersion -> p1 PackageName, p1 HasSBOM + "5": {"7"}, // artifact -> a1 HasSBOM + "6": {"4", "5"}, // p1 HasSBOM -> p1 PackageVersion, a1 Artifact + "7": {"4", "5"}, // p2 HasSBOM -> p1 PackageVersion, a1 Artifact }, }, } @@ -850,8 +3186,47 @@ func TestHasSBOMNeighbors(t *testing.T) { t.Fatalf("Could not ingest artifact: %v", err) } } + + includes := model.HasSBOMIncludesInputSpec{} + if test.PkgArt != nil { + if pkgs, err := b.IngestPackages(ctx, test.PkgArt.Packages); err != nil { + t.Fatalf("Could not ingest package: %v", err) + } else { + for _, pkg := range pkgs { + if id, err := getPackageVersionFromIngestedPackage(pkg); err != nil { + t.Fatalf("Could not determine PackageVersion id: %v", err) + } else { + includes.Software = append(includes.Software, id) + } + } + } + if arts, err := b.IngestArtifacts(ctx, test.PkgArt.Artifacts); err != nil { + t.Fatalf("Could not ingest artifact: %v", err) + } else { + for _, art := range arts { + includes.Software = append(includes.Software, art.ID) + } + } + } + + for _, dep := range test.IsDeps { + if isDep, err := b.IngestDependency(ctx, *dep.pkg, *dep.depPkg, dep.matchType, *dep.isDep); err != nil { + t.Fatalf("Could not ingest dependency: %v", err) + } else { + includes.Dependencies = append(includes.Dependencies, isDep.ID) + } + } + + for _, occ := range test.IsOccs { + if isOcc, err := b.IngestOccurrence(ctx, *occ.Subj, *occ.Art, *occ.isOcc); err != nil { + t.Fatalf("Could not ingest occurrence: %v", err) + } else { + includes.Occurrences = append(includes.Occurrences, isOcc.ID) + } + } + for _, o := range test.Calls { - if _, err := b.IngestHasSbom(ctx, o.Sub, *o.HS); err != nil { + if _, err := b.IngestHasSbom(ctx, o.Sub, *o.HS, includes); err != nil { t.Fatalf("Could not ingest HasSBOM: %v", err) } } @@ -860,7 +3235,10 @@ func TestHasSBOMNeighbors(t *testing.T) { if err != nil { t.Fatalf("Could not query neighbors: %s", err) } - gotIDs := convNodes(got) + gotIDs, err := getNodeIds(got) + if err != nil { + t.Fatalf("Could not retrieve neighbor ids: %s", err) + } slices.Sort(r) slices.Sort(gotIDs) if diff := cmp.Diff(r, gotIDs); diff != "" { diff --git a/pkg/assembler/backends/inmem/hashEqual.go b/pkg/assembler/backends/inmem/hashEqual.go index 499bc3d2b4..7b0e311283 100644 --- a/pkg/assembler/backends/inmem/hashEqual.go +++ b/pkg/assembler/backends/inmem/hashEqual.go @@ -158,6 +158,9 @@ func (c *demoClient) matchArtifacts(filter []*model.ArtifactSpec, value []uint32 // drop error here if ID is bad if a != nil { matchID = append(matchID, a.id) + } else if aSpec.ID != nil { + // We had an id but it didn't match + return false } else if aSpec.Algorithm != nil || aSpec.Digest != nil { matchPartial = append(matchPartial, aSpec) } diff --git a/pkg/assembler/backends/inmem/isDependency.go b/pkg/assembler/backends/inmem/isDependency.go index 4bc5f28ac2..a12e5bef63 100644 --- a/pkg/assembler/backends/inmem/isDependency.go +++ b/pkg/assembler/backends/inmem/isDependency.go @@ -297,19 +297,7 @@ func (c *demoClient) buildIsDependency(link *isDependencyLink, filter *model.IsD func (c *demoClient) addDepIfMatch(out []*model.IsDependency, filter *model.IsDependencySpec, link *isDependencyLink) ( []*model.IsDependency, error) { - if filter != nil && noMatch(filter.Justification, link.justification) { - return out, nil - } - if filter != nil && noMatch(filter.Origin, link.origin) { - return out, nil - } - if filter != nil && noMatch(filter.Collector, link.collector) { - return out, nil - } - if filter != nil && noMatch(filter.VersionRange, link.versionRange) { - return out, nil - } - if filter != nil && filter.DependencyType != nil && *filter.DependencyType != link.dependencyType { + if noMatchIsDep(filter, link) { return out, nil } @@ -322,3 +310,55 @@ func (c *demoClient) addDepIfMatch(out []*model.IsDependency, } return append(out, foundIsDependency), nil } + +func noMatchIsDep(filter *model.IsDependencySpec, link *isDependencyLink) bool { + if filter != nil { + return noMatch(filter.Justification, link.justification) || + noMatch(filter.Origin, link.origin) || + noMatch(filter.Collector, link.collector) || + noMatch(filter.VersionRange, link.versionRange) || + (filter.DependencyType != nil && *filter.DependencyType != link.dependencyType) + } else { + return false + } +} + +func (c *demoClient) matchDependencies(filters []*model.IsDependencySpec, depLinkIDs []uint32) bool { + var depLinks []*isDependencyLink + if len(filters) > 0 { + for _, depLinkID := range depLinkIDs { + link, err := byID[*isDependencyLink](depLinkID, c) + if err != nil { + return false + } + depLinks = append(depLinks, link) + } + + for _, filter := range filters { + if filter == nil { + continue + } + if filter.ID != nil { + // Check by ID if present + if !c.isIDPresent(*filter.ID, depLinkIDs) { + return false + } + } else { + // Otherwise match spec information + match := false + for _, depLink := range depLinks { + if !noMatchIsDep(filter, depLink) && + (filter.Package == nil || c.matchPackages([]*model.PkgSpec{filter.Package}, []uint32{depLink.packageID})) && + (filter.DependencyPackage == nil || c.matchPackages([]*model.PkgSpec{filter.DependencyPackage}, []uint32{depLink.depPackageID})) { + match = true + break + } + } + if !match { + return false + } + } + } + } + return true +} diff --git a/pkg/assembler/backends/inmem/isOccurrence.go b/pkg/assembler/backends/inmem/isOccurrence.go index cfe83af86b..d6f331d96e 100644 --- a/pkg/assembler/backends/inmem/isOccurrence.go +++ b/pkg/assembler/backends/inmem/isOccurrence.go @@ -384,3 +384,55 @@ func (c *demoClient) addOccIfMatch(out []*model.IsOccurrence, } return append(out, o), nil } + +func (c *demoClient) matchOccurrences(filters []*model.IsOccurrenceSpec, occLinkIDs []uint32 /*, pkgs []uint32, artifacts []uint32*/) bool { + var occLinks []*isOccurrenceStruct + if len(filters) > 0 { + for _, occLinkID := range occLinkIDs { + link, err := byID[*isOccurrenceStruct](occLinkID, c) + if err != nil { + return false + } + occLinks = append(occLinks, link) + } + + for _, filter := range filters { + if filter == nil { + continue + } + if filter.ID != nil { + // Check by ID if present + if !c.isIDPresent(*filter.ID, occLinkIDs) { + return false + } + } else { + // Otherwise match spec information + match := false + for _, link := range occLinks { + if !noMatch(filter.Justification, link.justification) && + !noMatch(filter.Origin, link.origin) && + !noMatch(filter.Collector, link.collector) && + c.matchArtifacts([]*model.ArtifactSpec{filter.Artifact}, []uint32{link.artifact}) { + + if filter.Subject != nil { + if filter.Subject.Package != nil && !c.matchPackages([]*model.PkgSpec{filter.Subject.Package}, []uint32{link.pkg}) { + continue + } else if filter.Subject.Source != nil { + src, err := c.exactSource(filter.Subject.Source) + if err != nil || src == nil { + continue + } + } + } + match = true + break + } + } + if !match { + return false + } + } + } + } + return true +} diff --git a/pkg/assembler/backends/inmem/path_test.go b/pkg/assembler/backends/inmem/path_test.go index bbdf23bcf3..4927c6396b 100644 --- a/pkg/assembler/backends/inmem/path_test.go +++ b/pkg/assembler/backends/inmem/path_test.go @@ -335,21 +335,50 @@ func Test_Nodes(t *testing.T) { }}, }, { name: "hasSBOM", - inPkg: []*model.PkgInputSpec{testdata.P1}, + inPkg: []*model.PkgInputSpec{testdata.P2, testdata.P4}, + inArt: []*model.ArtifactInputSpec{testdata.A1}, + isDepCall: &isDepCall{ + P1: testdata.P2, + P2: testdata.P4, + MF: mSpecific, + ID: &model.IsDependencyInputSpec{ + Justification: "test justification", + }, + }, + isOcurCall: &isOcurCall{ + PkgSrc: model.PackageOrSourceInput{ + Package: testdata.P4, + }, + Artifact: testdata.A1, + Occurrence: &model.IsOccurrenceInputSpec{ + Justification: "test justification", + }, + }, hasSBOMCall: &hasSBOMCall{ - Sub: model.PackageOrArtifactInput{ - Package: testdata.P1, + Package: testdata.P2, }, HS: &model.HasSBOMInputSpec{ DownloadLocation: "location two", }, }, want: []model.Node{&model.HasSbom{ - Subject: testdata.P1out, + Subject: testdata.P2out, DownloadLocation: "location two", + IncludedSoftware: []model.PackageOrArtifact{testdata.P2out, testdata.P4out, testdata.A1out}, + IncludedDependencies: []*model.IsDependency{{ + Package: testdata.P2out, + DependencyPackage: testdata.P4out, + Justification: "test justification", + }}, + IncludedOccurrences: []*model.IsOccurrence{{ + Subject: testdata.P4out, + Artifact: testdata.A1out, + Justification: "test justification", + }}, }}, }, { + name: "hasSLSA", inArt: []*model.ArtifactInputSpec{testdata.A1, testdata.A2}, inBld: []*model.BuilderInputSpec{testdata.B1, testdata.B2}, @@ -501,9 +530,12 @@ func Test_Nodes(t *testing.T) { t.Fatalf("Could not instantiate testing backend: %v", err) } var nodeID string + includes := model.HasSBOMIncludesInputSpec{} for _, p := range tt.inPkg { - if _, err := b.IngestPackage(ctx, *p); err != nil { + if pkg, err := b.IngestPackage(ctx, *p); err != nil { t.Fatalf("Could not ingest package: %v", err) + } else { + includes.Software = append(includes.Software, pkg.Namespaces[0].Names[0].Versions[0].ID) } } for _, s := range tt.inSrc { @@ -512,8 +544,10 @@ func Test_Nodes(t *testing.T) { } } for _, a := range tt.inArt { - if _, err := b.IngestArtifact(ctx, a); err != nil { + if art, err := b.IngestArtifact(ctx, a); err != nil { t.Fatalf("Could not ingest artifact: %v", err) + } else { + includes.Software = append(includes.Software, art.ID) } } for _, bld := range tt.inBld { @@ -538,6 +572,7 @@ func Test_Nodes(t *testing.T) { return } nodeID = ingestedPkg.Namespaces[0].Names[0].Versions[0].ID + includes.Software = append(includes.Software, nodeID) } if tt.artifactInput != nil { ingestedArt, err := b.IngestArtifact(ctx, tt.artifactInput) @@ -658,16 +693,6 @@ func Test_Nodes(t *testing.T) { } nodeID = found.ID } - if tt.hasSBOMCall != nil { - found, err := b.IngestHasSbom(ctx, tt.hasSBOMCall.Sub, *tt.hasSBOMCall.HS) - if (err != nil) != tt.wantErr { - t.Fatalf("did not get expected ingest error, want: %v, got: %v", tt.wantErr, err) - } - if err != nil { - return - } - nodeID = found.ID - } if tt.hasSlsaCall != nil { found, err := b.IngestSLSA(ctx, *tt.hasSlsaCall.Sub, tt.hasSlsaCall.BF, *tt.hasSlsaCall.BB, *tt.hasSlsaCall.SLSA) if (err != nil) != tt.wantErr { @@ -697,6 +722,7 @@ func Test_Nodes(t *testing.T) { return } nodeID = found.ID + includes.Dependencies = append(includes.Dependencies, nodeID) } if tt.isOcurCall != nil { found, err := b.IngestOccurrence(ctx, tt.isOcurCall.PkgSrc, *tt.isOcurCall.Artifact, *tt.isOcurCall.Occurrence) @@ -707,6 +733,18 @@ func Test_Nodes(t *testing.T) { return } nodeID = found.ID + includes.Occurrences = append(includes.Occurrences, nodeID) + } + if tt.hasSBOMCall != nil { + // After isDepCall and isOcurCall so they can set up includes. + found, err := b.IngestHasSbom(ctx, tt.hasSBOMCall.Sub, *tt.hasSBOMCall.HS, includes) + if (err != nil) != tt.wantErr { + t.Fatalf("did not get expected ingest error, want: %v, got: %v", tt.wantErr, err) + } + if err != nil { + return + } + nodeID = found.ID } if tt.pkgEqualCall != nil { found, err := b.IngestPkgEqual(ctx, *tt.pkgEqualCall.P1, *tt.pkgEqualCall.P2, *tt.pkgEqualCall.HE) diff --git a/pkg/assembler/backends/inmem/pkg.go b/pkg/assembler/backends/inmem/pkg.go index 771f6f1601..7a9828f44f 100644 --- a/pkg/assembler/backends/inmem/pkg.go +++ b/pkg/assembler/backends/inmem/pkg.go @@ -725,6 +725,62 @@ func getPackageIDFromInput(c *demoClient, input model.PkgInputSpec, pkgMatchType return packageID, nil } +func (c *demoClient) matchPackages(filter []*model.PkgSpec, pkgs []uint32) bool { + pkgs = slices.Clone(pkgs) + pkgs = sortAndRemoveDups(pkgs) + + for _, pvSpec := range filter { + if pvSpec != nil { + if pvSpec.ID != nil { + // Check by ID if present + if !c.isIDPresent(*pvSpec.ID, pkgs) { + return false + } + } else { + // Otherwise match spec information + match := false + for _, pkgId := range pkgs { + id := pkgId + pkgVersion, err := byID[*pkgVersionNode](id, c) + if err == nil { + if noMatch(pvSpec.Subpath, pkgVersion.subpath) || noMatchQualifiers(pvSpec, pkgVersion.qualifiers) || noMatch(pvSpec.Version, pkgVersion.version) { + continue + } + id = pkgVersion.parent + } + pkgName, err := byID[*pkgVersionStruct](id, c) + if err == nil { + if noMatch(pvSpec.Name, pkgName.name) { + continue + } + id = pkgName.parent + } + pkgNamespace, err := byID[*pkgNameStruct](id, c) + if err == nil { + if noMatch(pvSpec.Namespace, pkgNamespace.namespace) { + continue + } + id = pkgNamespace.parent + } + pkgType, err := byID[*pkgNamespaceStruct](id, c) + if err == nil { + if noMatch(pvSpec.Type, pkgType.typeKey) { + continue + } else { + match = true + break + } + } + } + if !match { + return false + } + } + } + } + return true +} + func getCollectedPackageQualifiers(qualifierMap map[string]string) []*model.PackageQualifier { qualifiers := []*model.PackageQualifier{} for key, val := range qualifierMap { @@ -755,7 +811,7 @@ func getQualifiersFromFilter(qualifiersSpec []*model.PackageQualifierSpec) map[s return qualifiersMap } for _, kv := range qualifiersSpec { - qualifiersMap[kv.Key] = *kv.Value + qualifiersMap[kv.Key] = nilToEmpty(kv.Value) } return qualifiersMap } diff --git a/pkg/assembler/backends/neo4j/hasSBOM.go b/pkg/assembler/backends/neo4j/hasSBOM.go index 285a125740..79624e8643 100644 --- a/pkg/assembler/backends/neo4j/hasSBOM.go +++ b/pkg/assembler/backends/neo4j/hasSBOM.go @@ -187,10 +187,10 @@ func generateModelHasSBOM(subject model.PackageOrArtifact, uri, origin, collecto return &hasSBOM } -func (c *neo4jClient) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec) (*model.HasSbom, error) { +func (c *neo4jClient) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec, includes model.HasSBOMIncludesInputSpec) (*model.HasSbom, error) { panic(fmt.Errorf("not implemented: IngestHasSbom - IngestHasSbom")) } -func (c *neo4jClient) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec) ([]*model.HasSbom, error) { +func (c *neo4jClient) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec, includes []*model.HasSBOMIncludesInputSpec) ([]*model.HasSbom, error) { return []*model.HasSbom{}, fmt.Errorf("not implemented: IngestHasSBOMs") } diff --git a/pkg/assembler/clients/generated/operations.go b/pkg/assembler/clients/generated/operations.go index 042595f677..6112cfdc44 100644 --- a/pkg/assembler/clients/generated/operations.go +++ b/pkg/assembler/clients/generated/operations.go @@ -3076,6 +3076,12 @@ type AllHasSBOMTree struct { Collector string `json:"collector"` // Timestamp for SBOM creation KnownSince time.Time `json:"knownSince"` + // Included packages and artifacts + IncludedSoftware []AllHasSBOMTreeIncludedSoftwarePackageOrArtifact `json:"-"` + // Included dependencies + IncludedDependencies []AllHasSBOMTreeIncludedDependenciesIsDependency `json:"includedDependencies"` + // Included occurrences + IncludedOccurrences []AllHasSBOMTreeIncludedOccurrencesIsOccurrence `json:"includedOccurrences"` } // GetId returns AllHasSBOMTree.Id, and is useful for accessing the field via an interface. @@ -3105,6 +3111,21 @@ func (v *AllHasSBOMTree) GetCollector() string { return v.Collector } // GetKnownSince returns AllHasSBOMTree.KnownSince, and is useful for accessing the field via an interface. func (v *AllHasSBOMTree) GetKnownSince() time.Time { return v.KnownSince } +// GetIncludedSoftware returns AllHasSBOMTree.IncludedSoftware, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTree) GetIncludedSoftware() []AllHasSBOMTreeIncludedSoftwarePackageOrArtifact { + return v.IncludedSoftware +} + +// GetIncludedDependencies returns AllHasSBOMTree.IncludedDependencies, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTree) GetIncludedDependencies() []AllHasSBOMTreeIncludedDependenciesIsDependency { + return v.IncludedDependencies +} + +// GetIncludedOccurrences returns AllHasSBOMTree.IncludedOccurrences, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTree) GetIncludedOccurrences() []AllHasSBOMTreeIncludedOccurrencesIsOccurrence { + return v.IncludedOccurrences +} + func (v *AllHasSBOMTree) UnmarshalJSON(b []byte) error { if string(b) == "null" { @@ -3113,7 +3134,8 @@ func (v *AllHasSBOMTree) UnmarshalJSON(b []byte) error { var firstPass struct { *AllHasSBOMTree - Subject json.RawMessage `json:"subject"` + Subject json.RawMessage `json:"subject"` + IncludedSoftware []json.RawMessage `json:"includedSoftware"` graphql.NoUnmarshalJSON } firstPass.AllHasSBOMTree = v @@ -3135,6 +3157,25 @@ func (v *AllHasSBOMTree) UnmarshalJSON(b []byte) error { } } } + + { + dst := &v.IncludedSoftware + src := firstPass.IncludedSoftware + *dst = make( + []AllHasSBOMTreeIncludedSoftwarePackageOrArtifact, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + if len(src) != 0 && string(src) != "null" { + err = __unmarshalAllHasSBOMTreeIncludedSoftwarePackageOrArtifact( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal AllHasSBOMTree.IncludedSoftware: %w", err) + } + } + } + } return nil } @@ -3155,41 +3196,545 @@ type __premarshalAllHasSBOMTree struct { Collector string `json:"collector"` - KnownSince time.Time `json:"knownSince"` + KnownSince time.Time `json:"knownSince"` + + IncludedSoftware []json.RawMessage `json:"includedSoftware"` + + IncludedDependencies []AllHasSBOMTreeIncludedDependenciesIsDependency `json:"includedDependencies"` + + IncludedOccurrences []AllHasSBOMTreeIncludedOccurrencesIsOccurrence `json:"includedOccurrences"` +} + +func (v *AllHasSBOMTree) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *AllHasSBOMTree) __premarshalJSON() (*__premarshalAllHasSBOMTree, error) { + var retval __premarshalAllHasSBOMTree + + retval.Id = v.Id + { + + dst := &retval.Subject + src := v.Subject + var err error + *dst, err = __marshalAllHasSBOMTreeSubjectPackageOrArtifact( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal AllHasSBOMTree.Subject: %w", err) + } + } + retval.Uri = v.Uri + retval.Algorithm = v.Algorithm + retval.Digest = v.Digest + retval.DownloadLocation = v.DownloadLocation + retval.Origin = v.Origin + retval.Collector = v.Collector + retval.KnownSince = v.KnownSince + { + + dst := &retval.IncludedSoftware + src := v.IncludedSoftware + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalAllHasSBOMTreeIncludedSoftwarePackageOrArtifact( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal AllHasSBOMTree.IncludedSoftware: %w", err) + } + } + } + retval.IncludedDependencies = v.IncludedDependencies + retval.IncludedOccurrences = v.IncludedOccurrences + return &retval, nil +} + +// AllHasSBOMTreeIncludedDependenciesIsDependency includes the requested fields of the GraphQL type IsDependency. +// The GraphQL type's documentation follows. +// +// IsDependency is an attestation to record that a package depends on another. +type AllHasSBOMTreeIncludedDependenciesIsDependency struct { + AllIsDependencyTree `json:"-"` +} + +// GetId returns AllHasSBOMTreeIncludedDependenciesIsDependency.Id, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) GetId() string { + return v.AllIsDependencyTree.Id +} + +// GetJustification returns AllHasSBOMTreeIncludedDependenciesIsDependency.Justification, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) GetJustification() string { + return v.AllIsDependencyTree.Justification +} + +// GetPackage returns AllHasSBOMTreeIncludedDependenciesIsDependency.Package, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) GetPackage() AllIsDependencyTreePackage { + return v.AllIsDependencyTree.Package +} + +// GetDependencyPackage returns AllHasSBOMTreeIncludedDependenciesIsDependency.DependencyPackage, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) GetDependencyPackage() AllIsDependencyTreeDependencyPackage { + return v.AllIsDependencyTree.DependencyPackage +} + +// GetDependencyType returns AllHasSBOMTreeIncludedDependenciesIsDependency.DependencyType, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) GetDependencyType() DependencyType { + return v.AllIsDependencyTree.DependencyType +} + +// GetVersionRange returns AllHasSBOMTreeIncludedDependenciesIsDependency.VersionRange, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) GetVersionRange() string { + return v.AllIsDependencyTree.VersionRange +} + +// GetOrigin returns AllHasSBOMTreeIncludedDependenciesIsDependency.Origin, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) GetOrigin() string { + return v.AllIsDependencyTree.Origin +} + +// GetCollector returns AllHasSBOMTreeIncludedDependenciesIsDependency.Collector, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) GetCollector() string { + return v.AllIsDependencyTree.Collector +} + +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *AllHasSBOMTreeIncludedDependenciesIsDependency + graphql.NoUnmarshalJSON + } + firstPass.AllHasSBOMTreeIncludedDependenciesIsDependency = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.AllIsDependencyTree) + if err != nil { + return err + } + return nil +} + +type __premarshalAllHasSBOMTreeIncludedDependenciesIsDependency struct { + Id string `json:"id"` + + Justification string `json:"justification"` + + Package AllIsDependencyTreePackage `json:"package"` + + DependencyPackage AllIsDependencyTreeDependencyPackage `json:"dependencyPackage"` + + DependencyType DependencyType `json:"dependencyType"` + + VersionRange string `json:"versionRange"` + + Origin string `json:"origin"` + + Collector string `json:"collector"` +} + +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *AllHasSBOMTreeIncludedDependenciesIsDependency) __premarshalJSON() (*__premarshalAllHasSBOMTreeIncludedDependenciesIsDependency, error) { + var retval __premarshalAllHasSBOMTreeIncludedDependenciesIsDependency + + retval.Id = v.AllIsDependencyTree.Id + retval.Justification = v.AllIsDependencyTree.Justification + retval.Package = v.AllIsDependencyTree.Package + retval.DependencyPackage = v.AllIsDependencyTree.DependencyPackage + retval.DependencyType = v.AllIsDependencyTree.DependencyType + retval.VersionRange = v.AllIsDependencyTree.VersionRange + retval.Origin = v.AllIsDependencyTree.Origin + retval.Collector = v.AllIsDependencyTree.Collector + return &retval, nil +} + +// AllHasSBOMTreeIncludedOccurrencesIsOccurrence includes the requested fields of the GraphQL type IsOccurrence. +// The GraphQL type's documentation follows. +// +// IsOccurrence is an attestation to link an artifact to a package or source. +// +// Attestation must occur at the PackageVersion or at the SourceName. +type AllHasSBOMTreeIncludedOccurrencesIsOccurrence struct { + AllIsOccurrencesTree `json:"-"` +} + +// GetId returns AllHasSBOMTreeIncludedOccurrencesIsOccurrence.Id, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedOccurrencesIsOccurrence) GetId() string { + return v.AllIsOccurrencesTree.Id +} + +// GetSubject returns AllHasSBOMTreeIncludedOccurrencesIsOccurrence.Subject, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedOccurrencesIsOccurrence) GetSubject() AllIsOccurrencesTreeSubjectPackageOrSource { + return v.AllIsOccurrencesTree.Subject +} + +// GetArtifact returns AllHasSBOMTreeIncludedOccurrencesIsOccurrence.Artifact, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedOccurrencesIsOccurrence) GetArtifact() AllIsOccurrencesTreeArtifact { + return v.AllIsOccurrencesTree.Artifact +} + +// GetJustification returns AllHasSBOMTreeIncludedOccurrencesIsOccurrence.Justification, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedOccurrencesIsOccurrence) GetJustification() string { + return v.AllIsOccurrencesTree.Justification +} + +// GetOrigin returns AllHasSBOMTreeIncludedOccurrencesIsOccurrence.Origin, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedOccurrencesIsOccurrence) GetOrigin() string { + return v.AllIsOccurrencesTree.Origin +} + +// GetCollector returns AllHasSBOMTreeIncludedOccurrencesIsOccurrence.Collector, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedOccurrencesIsOccurrence) GetCollector() string { + return v.AllIsOccurrencesTree.Collector +} + +func (v *AllHasSBOMTreeIncludedOccurrencesIsOccurrence) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *AllHasSBOMTreeIncludedOccurrencesIsOccurrence + graphql.NoUnmarshalJSON + } + firstPass.AllHasSBOMTreeIncludedOccurrencesIsOccurrence = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.AllIsOccurrencesTree) + if err != nil { + return err + } + return nil +} + +type __premarshalAllHasSBOMTreeIncludedOccurrencesIsOccurrence struct { + Id string `json:"id"` + + Subject json.RawMessage `json:"subject"` + + Artifact AllIsOccurrencesTreeArtifact `json:"artifact"` + + Justification string `json:"justification"` + + Origin string `json:"origin"` + + Collector string `json:"collector"` +} + +func (v *AllHasSBOMTreeIncludedOccurrencesIsOccurrence) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *AllHasSBOMTreeIncludedOccurrencesIsOccurrence) __premarshalJSON() (*__premarshalAllHasSBOMTreeIncludedOccurrencesIsOccurrence, error) { + var retval __premarshalAllHasSBOMTreeIncludedOccurrencesIsOccurrence + + retval.Id = v.AllIsOccurrencesTree.Id + { + + dst := &retval.Subject + src := v.AllIsOccurrencesTree.Subject + var err error + *dst, err = __marshalAllIsOccurrencesTreeSubjectPackageOrSource( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal AllHasSBOMTreeIncludedOccurrencesIsOccurrence.AllIsOccurrencesTree.Subject: %w", err) + } + } + retval.Artifact = v.AllIsOccurrencesTree.Artifact + retval.Justification = v.AllIsOccurrencesTree.Justification + retval.Origin = v.AllIsOccurrencesTree.Origin + retval.Collector = v.AllIsOccurrencesTree.Collector + return &retval, nil +} + +// AllHasSBOMTreeIncludedSoftwareArtifact includes the requested fields of the GraphQL type Artifact. +// The GraphQL type's documentation follows. +// +// Artifact represents an artifact identified by a checksum hash. +// +// The checksum is split into the digest value and the algorithm used to generate +// it. Both fields are mandatory and canonicalized to be lowercase. +// +// If having a checksum Go object, algorithm can be +// strings.ToLower(string(checksum.Algorithm)) and digest can be checksum.Value. +type AllHasSBOMTreeIncludedSoftwareArtifact struct { + Typename *string `json:"__typename"` + AllArtifactTree `json:"-"` +} + +// GetTypename returns AllHasSBOMTreeIncludedSoftwareArtifact.Typename, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedSoftwareArtifact) GetTypename() *string { return v.Typename } + +// GetId returns AllHasSBOMTreeIncludedSoftwareArtifact.Id, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedSoftwareArtifact) GetId() string { return v.AllArtifactTree.Id } + +// GetAlgorithm returns AllHasSBOMTreeIncludedSoftwareArtifact.Algorithm, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedSoftwareArtifact) GetAlgorithm() string { + return v.AllArtifactTree.Algorithm +} + +// GetDigest returns AllHasSBOMTreeIncludedSoftwareArtifact.Digest, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedSoftwareArtifact) GetDigest() string { return v.AllArtifactTree.Digest } + +func (v *AllHasSBOMTreeIncludedSoftwareArtifact) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *AllHasSBOMTreeIncludedSoftwareArtifact + graphql.NoUnmarshalJSON + } + firstPass.AllHasSBOMTreeIncludedSoftwareArtifact = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.AllArtifactTree) + if err != nil { + return err + } + return nil +} + +type __premarshalAllHasSBOMTreeIncludedSoftwareArtifact struct { + Typename *string `json:"__typename"` + + Id string `json:"id"` + + Algorithm string `json:"algorithm"` + + Digest string `json:"digest"` +} + +func (v *AllHasSBOMTreeIncludedSoftwareArtifact) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *AllHasSBOMTreeIncludedSoftwareArtifact) __premarshalJSON() (*__premarshalAllHasSBOMTreeIncludedSoftwareArtifact, error) { + var retval __premarshalAllHasSBOMTreeIncludedSoftwareArtifact + + retval.Typename = v.Typename + retval.Id = v.AllArtifactTree.Id + retval.Algorithm = v.AllArtifactTree.Algorithm + retval.Digest = v.AllArtifactTree.Digest + return &retval, nil +} + +// AllHasSBOMTreeIncludedSoftwarePackage includes the requested fields of the GraphQL type Package. +// The GraphQL type's documentation follows. +// +// Package represents the root of the package trie/tree. +// +// We map package information to a trie, closely matching the pURL specification +// (https://github.com/package-url/purl-spec/blob/0dd92f26f8bb11956ffdf5e8acfcee71e8560407/README.rst), +// but deviating from it where GUAC heuristics allow for better representation of +// package information. Each path in the trie fully represents a package; we split +// the trie based on the pURL components. +// +// This node matches a pkg: partial pURL. The type field matches the +// pURL types but we might also use "guac" for the cases where the pURL +// representation is not complete or when we have custom rules. +// +// Since this node is at the root of the package trie, it is named Package, not +// PackageType. +type AllHasSBOMTreeIncludedSoftwarePackage struct { + Typename *string `json:"__typename"` + AllPkgTree `json:"-"` +} + +// GetTypename returns AllHasSBOMTreeIncludedSoftwarePackage.Typename, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedSoftwarePackage) GetTypename() *string { return v.Typename } + +// GetId returns AllHasSBOMTreeIncludedSoftwarePackage.Id, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedSoftwarePackage) GetId() string { return v.AllPkgTree.Id } + +// GetType returns AllHasSBOMTreeIncludedSoftwarePackage.Type, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedSoftwarePackage) GetType() string { return v.AllPkgTree.Type } + +// GetNamespaces returns AllHasSBOMTreeIncludedSoftwarePackage.Namespaces, and is useful for accessing the field via an interface. +func (v *AllHasSBOMTreeIncludedSoftwarePackage) GetNamespaces() []AllPkgTreeNamespacesPackageNamespace { + return v.AllPkgTree.Namespaces +} + +func (v *AllHasSBOMTreeIncludedSoftwarePackage) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *AllHasSBOMTreeIncludedSoftwarePackage + graphql.NoUnmarshalJSON + } + firstPass.AllHasSBOMTreeIncludedSoftwarePackage = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.AllPkgTree) + if err != nil { + return err + } + return nil +} + +type __premarshalAllHasSBOMTreeIncludedSoftwarePackage struct { + Typename *string `json:"__typename"` + + Id string `json:"id"` + + Type string `json:"type"` + + Namespaces []AllPkgTreeNamespacesPackageNamespace `json:"namespaces"` +} + +func (v *AllHasSBOMTreeIncludedSoftwarePackage) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *AllHasSBOMTreeIncludedSoftwarePackage) __premarshalJSON() (*__premarshalAllHasSBOMTreeIncludedSoftwarePackage, error) { + var retval __premarshalAllHasSBOMTreeIncludedSoftwarePackage + + retval.Typename = v.Typename + retval.Id = v.AllPkgTree.Id + retval.Type = v.AllPkgTree.Type + retval.Namespaces = v.AllPkgTree.Namespaces + return &retval, nil +} + +// AllHasSBOMTreeIncludedSoftwarePackageOrArtifact includes the requested fields of the GraphQL interface PackageOrArtifact. +// +// AllHasSBOMTreeIncludedSoftwarePackageOrArtifact is implemented by the following types: +// AllHasSBOMTreeIncludedSoftwareArtifact +// AllHasSBOMTreeIncludedSoftwarePackage +// The GraphQL type's documentation follows. +// +// PackageOrArtifact is a union of Package and Artifact. +type AllHasSBOMTreeIncludedSoftwarePackageOrArtifact interface { + implementsGraphQLInterfaceAllHasSBOMTreeIncludedSoftwarePackageOrArtifact() + // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). + GetTypename() *string +} + +func (v *AllHasSBOMTreeIncludedSoftwareArtifact) implementsGraphQLInterfaceAllHasSBOMTreeIncludedSoftwarePackageOrArtifact() { +} +func (v *AllHasSBOMTreeIncludedSoftwarePackage) implementsGraphQLInterfaceAllHasSBOMTreeIncludedSoftwarePackageOrArtifact() { } -func (v *AllHasSBOMTree) MarshalJSON() ([]byte, error) { - premarshaled, err := v.__premarshalJSON() +func __unmarshalAllHasSBOMTreeIncludedSoftwarePackageOrArtifact(b []byte, v *AllHasSBOMTreeIncludedSoftwarePackageOrArtifact) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) if err != nil { - return nil, err + return err + } + + switch tn.TypeName { + case "Artifact": + *v = new(AllHasSBOMTreeIncludedSoftwareArtifact) + return json.Unmarshal(b, *v) + case "Package": + *v = new(AllHasSBOMTreeIncludedSoftwarePackage) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing PackageOrArtifact.__typename") + default: + return fmt.Errorf( + `unexpected concrete type for AllHasSBOMTreeIncludedSoftwarePackageOrArtifact: "%v"`, tn.TypeName) } - return json.Marshal(premarshaled) } -func (v *AllHasSBOMTree) __premarshalJSON() (*__premarshalAllHasSBOMTree, error) { - var retval __premarshalAllHasSBOMTree +func __marshalAllHasSBOMTreeIncludedSoftwarePackageOrArtifact(v *AllHasSBOMTreeIncludedSoftwarePackageOrArtifact) ([]byte, error) { - retval.Id = v.Id - { + var typename string + switch v := (*v).(type) { + case *AllHasSBOMTreeIncludedSoftwareArtifact: + typename = "Artifact" - dst := &retval.Subject - src := v.Subject - var err error - *dst, err = __marshalAllHasSBOMTreeSubjectPackageOrArtifact( - &src) + premarshaled, err := v.__premarshalJSON() if err != nil { - return nil, fmt.Errorf( - "unable to marshal AllHasSBOMTree.Subject: %w", err) + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalAllHasSBOMTreeIncludedSoftwareArtifact + }{typename, premarshaled} + return json.Marshal(result) + case *AllHasSBOMTreeIncludedSoftwarePackage: + typename = "Package" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err } + result := struct { + TypeName string `json:"__typename"` + *__premarshalAllHasSBOMTreeIncludedSoftwarePackage + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for AllHasSBOMTreeIncludedSoftwarePackageOrArtifact: "%T"`, v) } - retval.Uri = v.Uri - retval.Algorithm = v.Algorithm - retval.Digest = v.Digest - retval.DownloadLocation = v.DownloadLocation - retval.Origin = v.Origin - retval.Collector = v.Collector - retval.KnownSince = v.KnownSince - return &retval, nil } // AllHasSBOMTreeSubjectArtifact includes the requested fields of the GraphQL type Artifact. @@ -6802,6 +7347,9 @@ const ( EdgeHasMetadataSource Edge = "HAS_METADATA_SOURCE" EdgeHasSbomArtifact Edge = "HAS_SBOM_ARTIFACT" EdgeHasSbomPackage Edge = "HAS_SBOM_PACKAGE" + EdgeHasSbomIncludedSoftware Edge = "HAS_SBOM_INCLUDED_SOFTWARE" + EdgeHasSbomIncludedDependencies Edge = "HAS_SBOM_INCLUDED_DEPENDENCIES" + EdgeHasSbomIncludedOccurrences Edge = "HAS_SBOM_INCLUDED_OCCURRENCES" EdgeHasSlsaBuiltBy Edge = "HAS_SLSA_BUILT_BY" EdgeHasSlsaMaterials Edge = "HAS_SLSA_MATERIALS" EdgeHasSlsaSubject Edge = "HAS_SLSA_SUBJECT" @@ -7380,7 +7928,22 @@ type HasSBOMArtifactsResponse struct { // GetIngestHasSBOMs returns HasSBOMArtifactsResponse.IngestHasSBOMs, and is useful for accessing the field via an interface. func (v *HasSBOMArtifactsResponse) GetIngestHasSBOMs() []string { return v.IngestHasSBOMs } -// HasSBOMInputSpec is the same as HasSBOM but for mutation input. +type HasSBOMIncludesInputSpec struct { + Software []string `json:"software"` + Dependencies []string `json:"dependencies"` + Occurrences []string `json:"occurrences"` +} + +// GetSoftware returns HasSBOMIncludesInputSpec.Software, and is useful for accessing the field via an interface. +func (v *HasSBOMIncludesInputSpec) GetSoftware() []string { return v.Software } + +// GetDependencies returns HasSBOMIncludesInputSpec.Dependencies, and is useful for accessing the field via an interface. +func (v *HasSBOMIncludesInputSpec) GetDependencies() []string { return v.Dependencies } + +// GetOccurrences returns HasSBOMIncludesInputSpec.Occurrences, and is useful for accessing the field via an interface. +func (v *HasSBOMIncludesInputSpec) GetOccurrences() []string { return v.Occurrences } + +// HasSBOMInputSpec is similar to HasSBOM but for mutation input. type HasSBOMInputSpec struct { Uri string `json:"uri"` Algorithm string `json:"algorithm"` @@ -7556,23 +8119,81 @@ type IngestLicensesResponse struct { // GetIngestLicenses returns IngestLicensesResponse.IngestLicenses, and is useful for accessing the field via an interface. func (v *IngestLicensesResponse) GetIngestLicenses() []string { return v.IngestLicenses } +// IngestPackageIngestPackagePackageIDs includes the requested fields of the GraphQL type PackageIDs. +// The GraphQL type's documentation follows. +// +// The IDs of the ingested package +type IngestPackageIngestPackagePackageIDs struct { + PackageTypeID string `json:"packageTypeID"` + PackageNamespaceID string `json:"packageNamespaceID"` + PackageNameID string `json:"packageNameID"` + PackageVersionID string `json:"packageVersionID"` +} + +// GetPackageTypeID returns IngestPackageIngestPackagePackageIDs.PackageTypeID, and is useful for accessing the field via an interface. +func (v *IngestPackageIngestPackagePackageIDs) GetPackageTypeID() string { return v.PackageTypeID } + +// GetPackageNamespaceID returns IngestPackageIngestPackagePackageIDs.PackageNamespaceID, and is useful for accessing the field via an interface. +func (v *IngestPackageIngestPackagePackageIDs) GetPackageNamespaceID() string { + return v.PackageNamespaceID +} + +// GetPackageNameID returns IngestPackageIngestPackagePackageIDs.PackageNameID, and is useful for accessing the field via an interface. +func (v *IngestPackageIngestPackagePackageIDs) GetPackageNameID() string { return v.PackageNameID } + +// GetPackageVersionID returns IngestPackageIngestPackagePackageIDs.PackageVersionID, and is useful for accessing the field via an interface. +func (v *IngestPackageIngestPackagePackageIDs) GetPackageVersionID() string { + return v.PackageVersionID +} + // IngestPackageResponse is returned by IngestPackage on success. type IngestPackageResponse struct { - // Ingests a new package and returns the corresponding package trie path. The returned ID can be empty string. - IngestPackage string `json:"ingestPackage"` + // Ingests a new package and returns a corresponding package hierarchy containing only the IDs. The returned ID can be empty string. + IngestPackage IngestPackageIngestPackagePackageIDs `json:"ingestPackage"` } // GetIngestPackage returns IngestPackageResponse.IngestPackage, and is useful for accessing the field via an interface. -func (v *IngestPackageResponse) GetIngestPackage() string { return v.IngestPackage } +func (v *IngestPackageResponse) GetIngestPackage() IngestPackageIngestPackagePackageIDs { + return v.IngestPackage +} + +// IngestPackagesIngestPackagesPackageIDs includes the requested fields of the GraphQL type PackageIDs. +// The GraphQL type's documentation follows. +// +// The IDs of the ingested package +type IngestPackagesIngestPackagesPackageIDs struct { + PackageTypeID string `json:"packageTypeID"` + PackageNamespaceID string `json:"packageNamespaceID"` + PackageNameID string `json:"packageNameID"` + PackageVersionID string `json:"packageVersionID"` +} + +// GetPackageTypeID returns IngestPackagesIngestPackagesPackageIDs.PackageTypeID, and is useful for accessing the field via an interface. +func (v *IngestPackagesIngestPackagesPackageIDs) GetPackageTypeID() string { return v.PackageTypeID } + +// GetPackageNamespaceID returns IngestPackagesIngestPackagesPackageIDs.PackageNamespaceID, and is useful for accessing the field via an interface. +func (v *IngestPackagesIngestPackagesPackageIDs) GetPackageNamespaceID() string { + return v.PackageNamespaceID +} + +// GetPackageNameID returns IngestPackagesIngestPackagesPackageIDs.PackageNameID, and is useful for accessing the field via an interface. +func (v *IngestPackagesIngestPackagesPackageIDs) GetPackageNameID() string { return v.PackageNameID } + +// GetPackageVersionID returns IngestPackagesIngestPackagesPackageIDs.PackageVersionID, and is useful for accessing the field via an interface. +func (v *IngestPackagesIngestPackagesPackageIDs) GetPackageVersionID() string { + return v.PackageVersionID +} // IngestPackagesResponse is returned by IngestPackages on success. type IngestPackagesResponse struct { - // Bulk ingests packages and returns the list of corresponding package trie path. The returned array of IDs can be a an array of empty string. - IngestPackages []string `json:"ingestPackages"` + // Bulk ingests packages and returns the list of corresponding package hierarchies containing only the IDs. The returned array of IDs can be empty strings. + IngestPackages []IngestPackagesIngestPackagesPackageIDs `json:"ingestPackages"` } // GetIngestPackages returns IngestPackagesResponse.IngestPackages, and is useful for accessing the field via an interface. -func (v *IngestPackagesResponse) GetIngestPackages() []string { return v.IngestPackages } +func (v *IngestPackagesResponse) GetIngestPackages() []IngestPackagesIngestPackagesPackageIDs { + return v.IngestPackages +} // IngestPkgEqualResponse is returned by IngestPkgEqual on success. type IngestPkgEqualResponse struct { @@ -7592,23 +8213,67 @@ type IngestPkgEqualsResponse struct { // GetIngestPkgEquals returns IngestPkgEqualsResponse.IngestPkgEquals, and is useful for accessing the field via an interface. func (v *IngestPkgEqualsResponse) GetIngestPkgEquals() []string { return v.IngestPkgEquals } +// IngestSourceIngestSourceSourceIDs includes the requested fields of the GraphQL type SourceIDs. +// The GraphQL type's documentation follows. +// +// The IDs of the ingested pacsourcekage +type IngestSourceIngestSourceSourceIDs struct { + SourceTypeID string `json:"sourceTypeID"` + SourceNamespaceID string `json:"sourceNamespaceID"` + SourceNameID string `json:"sourceNameID"` +} + +// GetSourceTypeID returns IngestSourceIngestSourceSourceIDs.SourceTypeID, and is useful for accessing the field via an interface. +func (v *IngestSourceIngestSourceSourceIDs) GetSourceTypeID() string { return v.SourceTypeID } + +// GetSourceNamespaceID returns IngestSourceIngestSourceSourceIDs.SourceNamespaceID, and is useful for accessing the field via an interface. +func (v *IngestSourceIngestSourceSourceIDs) GetSourceNamespaceID() string { return v.SourceNamespaceID } + +// GetSourceNameID returns IngestSourceIngestSourceSourceIDs.SourceNameID, and is useful for accessing the field via an interface. +func (v *IngestSourceIngestSourceSourceIDs) GetSourceNameID() string { return v.SourceNameID } + // IngestSourceResponse is returned by IngestSource on success. type IngestSourceResponse struct { // Ingests a new source and returns the corresponding source trie path. The returned ID can be empty string. - IngestSource string `json:"ingestSource"` + IngestSource IngestSourceIngestSourceSourceIDs `json:"ingestSource"` } // GetIngestSource returns IngestSourceResponse.IngestSource, and is useful for accessing the field via an interface. -func (v *IngestSourceResponse) GetIngestSource() string { return v.IngestSource } +func (v *IngestSourceResponse) GetIngestSource() IngestSourceIngestSourceSourceIDs { + return v.IngestSource +} + +// IngestSourcesIngestSourcesSourceIDs includes the requested fields of the GraphQL type SourceIDs. +// The GraphQL type's documentation follows. +// +// The IDs of the ingested pacsourcekage +type IngestSourcesIngestSourcesSourceIDs struct { + SourceTypeID string `json:"sourceTypeID"` + SourceNamespaceID string `json:"sourceNamespaceID"` + SourceNameID string `json:"sourceNameID"` +} + +// GetSourceTypeID returns IngestSourcesIngestSourcesSourceIDs.SourceTypeID, and is useful for accessing the field via an interface. +func (v *IngestSourcesIngestSourcesSourceIDs) GetSourceTypeID() string { return v.SourceTypeID } + +// GetSourceNamespaceID returns IngestSourcesIngestSourcesSourceIDs.SourceNamespaceID, and is useful for accessing the field via an interface. +func (v *IngestSourcesIngestSourcesSourceIDs) GetSourceNamespaceID() string { + return v.SourceNamespaceID +} + +// GetSourceNameID returns IngestSourcesIngestSourcesSourceIDs.SourceNameID, and is useful for accessing the field via an interface. +func (v *IngestSourcesIngestSourcesSourceIDs) GetSourceNameID() string { return v.SourceNameID } // IngestSourcesResponse is returned by IngestSources on success. type IngestSourcesResponse struct { // Bulk ingests sources and returns the list of corresponding source trie path. The returned array of IDs can be a an array of empty string. - IngestSources []string `json:"ingestSources"` + IngestSources []IngestSourcesIngestSourcesSourceIDs `json:"ingestSources"` } // GetIngestSources returns IngestSourcesResponse.IngestSources, and is useful for accessing the field via an interface. -func (v *IngestSourcesResponse) GetIngestSources() []string { return v.IngestSources } +func (v *IngestSourcesResponse) GetIngestSources() []IngestSourcesIngestSourcesSourceIDs { + return v.IngestSources +} // IngestVulnEqualResponse is returned by IngestVulnEqual on success. type IngestVulnEqualResponse struct { @@ -8929,6 +9594,21 @@ func (v *NeighborsNeighborsHasSBOM) GetCollector() string { return v.AllHasSBOMT // GetKnownSince returns NeighborsNeighborsHasSBOM.KnownSince, and is useful for accessing the field via an interface. func (v *NeighborsNeighborsHasSBOM) GetKnownSince() time.Time { return v.AllHasSBOMTree.KnownSince } +// GetIncludedSoftware returns NeighborsNeighborsHasSBOM.IncludedSoftware, and is useful for accessing the field via an interface. +func (v *NeighborsNeighborsHasSBOM) GetIncludedSoftware() []AllHasSBOMTreeIncludedSoftwarePackageOrArtifact { + return v.AllHasSBOMTree.IncludedSoftware +} + +// GetIncludedDependencies returns NeighborsNeighborsHasSBOM.IncludedDependencies, and is useful for accessing the field via an interface. +func (v *NeighborsNeighborsHasSBOM) GetIncludedDependencies() []AllHasSBOMTreeIncludedDependenciesIsDependency { + return v.AllHasSBOMTree.IncludedDependencies +} + +// GetIncludedOccurrences returns NeighborsNeighborsHasSBOM.IncludedOccurrences, and is useful for accessing the field via an interface. +func (v *NeighborsNeighborsHasSBOM) GetIncludedOccurrences() []AllHasSBOMTreeIncludedOccurrencesIsOccurrence { + return v.AllHasSBOMTree.IncludedOccurrences +} + func (v *NeighborsNeighborsHasSBOM) UnmarshalJSON(b []byte) error { if string(b) == "null" { @@ -8974,6 +9654,12 @@ type __premarshalNeighborsNeighborsHasSBOM struct { Collector string `json:"collector"` KnownSince time.Time `json:"knownSince"` + + IncludedSoftware []json.RawMessage `json:"includedSoftware"` + + IncludedDependencies []AllHasSBOMTreeIncludedDependenciesIsDependency `json:"includedDependencies"` + + IncludedOccurrences []AllHasSBOMTreeIncludedOccurrencesIsOccurrence `json:"includedOccurrences"` } func (v *NeighborsNeighborsHasSBOM) MarshalJSON() ([]byte, error) { @@ -9008,6 +9694,26 @@ func (v *NeighborsNeighborsHasSBOM) __premarshalJSON() (*__premarshalNeighborsNe retval.Origin = v.AllHasSBOMTree.Origin retval.Collector = v.AllHasSBOMTree.Collector retval.KnownSince = v.AllHasSBOMTree.KnownSince + { + + dst := &retval.IncludedSoftware + src := v.AllHasSBOMTree.IncludedSoftware + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalAllHasSBOMTreeIncludedSoftwarePackageOrArtifact( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal NeighborsNeighborsHasSBOM.AllHasSBOMTree.IncludedSoftware: %w", err) + } + } + } + retval.IncludedDependencies = v.AllHasSBOMTree.IncludedDependencies + retval.IncludedOccurrences = v.AllHasSBOMTree.IncludedOccurrences return &retval, nil } @@ -12327,6 +13033,21 @@ func (v *NodeNodeHasSBOM) GetCollector() string { return v.AllHasSBOMTree.Collec // GetKnownSince returns NodeNodeHasSBOM.KnownSince, and is useful for accessing the field via an interface. func (v *NodeNodeHasSBOM) GetKnownSince() time.Time { return v.AllHasSBOMTree.KnownSince } +// GetIncludedSoftware returns NodeNodeHasSBOM.IncludedSoftware, and is useful for accessing the field via an interface. +func (v *NodeNodeHasSBOM) GetIncludedSoftware() []AllHasSBOMTreeIncludedSoftwarePackageOrArtifact { + return v.AllHasSBOMTree.IncludedSoftware +} + +// GetIncludedDependencies returns NodeNodeHasSBOM.IncludedDependencies, and is useful for accessing the field via an interface. +func (v *NodeNodeHasSBOM) GetIncludedDependencies() []AllHasSBOMTreeIncludedDependenciesIsDependency { + return v.AllHasSBOMTree.IncludedDependencies +} + +// GetIncludedOccurrences returns NodeNodeHasSBOM.IncludedOccurrences, and is useful for accessing the field via an interface. +func (v *NodeNodeHasSBOM) GetIncludedOccurrences() []AllHasSBOMTreeIncludedOccurrencesIsOccurrence { + return v.AllHasSBOMTree.IncludedOccurrences +} + func (v *NodeNodeHasSBOM) UnmarshalJSON(b []byte) error { if string(b) == "null" { @@ -12372,6 +13093,12 @@ type __premarshalNodeNodeHasSBOM struct { Collector string `json:"collector"` KnownSince time.Time `json:"knownSince"` + + IncludedSoftware []json.RawMessage `json:"includedSoftware"` + + IncludedDependencies []AllHasSBOMTreeIncludedDependenciesIsDependency `json:"includedDependencies"` + + IncludedOccurrences []AllHasSBOMTreeIncludedOccurrencesIsOccurrence `json:"includedOccurrences"` } func (v *NodeNodeHasSBOM) MarshalJSON() ([]byte, error) { @@ -12406,6 +13133,26 @@ func (v *NodeNodeHasSBOM) __premarshalJSON() (*__premarshalNodeNodeHasSBOM, erro retval.Origin = v.AllHasSBOMTree.Origin retval.Collector = v.AllHasSBOMTree.Collector retval.KnownSince = v.AllHasSBOMTree.KnownSince + { + + dst := &retval.IncludedSoftware + src := v.AllHasSBOMTree.IncludedSoftware + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalAllHasSBOMTreeIncludedSoftwarePackageOrArtifact( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal NodeNodeHasSBOM.AllHasSBOMTree.IncludedSoftware: %w", err) + } + } + } + retval.IncludedDependencies = v.AllHasSBOMTree.IncludedDependencies + retval.IncludedOccurrences = v.AllHasSBOMTree.IncludedOccurrences return &retval, nil } @@ -14795,6 +15542,21 @@ func (v *NodesNodesHasSBOM) GetCollector() string { return v.AllHasSBOMTree.Coll // GetKnownSince returns NodesNodesHasSBOM.KnownSince, and is useful for accessing the field via an interface. func (v *NodesNodesHasSBOM) GetKnownSince() time.Time { return v.AllHasSBOMTree.KnownSince } +// GetIncludedSoftware returns NodesNodesHasSBOM.IncludedSoftware, and is useful for accessing the field via an interface. +func (v *NodesNodesHasSBOM) GetIncludedSoftware() []AllHasSBOMTreeIncludedSoftwarePackageOrArtifact { + return v.AllHasSBOMTree.IncludedSoftware +} + +// GetIncludedDependencies returns NodesNodesHasSBOM.IncludedDependencies, and is useful for accessing the field via an interface. +func (v *NodesNodesHasSBOM) GetIncludedDependencies() []AllHasSBOMTreeIncludedDependenciesIsDependency { + return v.AllHasSBOMTree.IncludedDependencies +} + +// GetIncludedOccurrences returns NodesNodesHasSBOM.IncludedOccurrences, and is useful for accessing the field via an interface. +func (v *NodesNodesHasSBOM) GetIncludedOccurrences() []AllHasSBOMTreeIncludedOccurrencesIsOccurrence { + return v.AllHasSBOMTree.IncludedOccurrences +} + func (v *NodesNodesHasSBOM) UnmarshalJSON(b []byte) error { if string(b) == "null" { @@ -14840,6 +15602,12 @@ type __premarshalNodesNodesHasSBOM struct { Collector string `json:"collector"` KnownSince time.Time `json:"knownSince"` + + IncludedSoftware []json.RawMessage `json:"includedSoftware"` + + IncludedDependencies []AllHasSBOMTreeIncludedDependenciesIsDependency `json:"includedDependencies"` + + IncludedOccurrences []AllHasSBOMTreeIncludedOccurrencesIsOccurrence `json:"includedOccurrences"` } func (v *NodesNodesHasSBOM) MarshalJSON() ([]byte, error) { @@ -14874,6 +15642,26 @@ func (v *NodesNodesHasSBOM) __premarshalJSON() (*__premarshalNodesNodesHasSBOM, retval.Origin = v.AllHasSBOMTree.Origin retval.Collector = v.AllHasSBOMTree.Collector retval.KnownSince = v.AllHasSBOMTree.KnownSince + { + + dst := &retval.IncludedSoftware + src := v.AllHasSBOMTree.IncludedSoftware + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalAllHasSBOMTreeIncludedSoftwarePackageOrArtifact( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal NodesNodesHasSBOM.AllHasSBOMTree.IncludedSoftware: %w", err) + } + } + } + retval.IncludedDependencies = v.AllHasSBOMTree.IncludedDependencies + retval.IncludedOccurrences = v.AllHasSBOMTree.IncludedOccurrences return &retval, nil } @@ -18265,6 +19053,21 @@ func (v *PathPathHasSBOM) GetCollector() string { return v.AllHasSBOMTree.Collec // GetKnownSince returns PathPathHasSBOM.KnownSince, and is useful for accessing the field via an interface. func (v *PathPathHasSBOM) GetKnownSince() time.Time { return v.AllHasSBOMTree.KnownSince } +// GetIncludedSoftware returns PathPathHasSBOM.IncludedSoftware, and is useful for accessing the field via an interface. +func (v *PathPathHasSBOM) GetIncludedSoftware() []AllHasSBOMTreeIncludedSoftwarePackageOrArtifact { + return v.AllHasSBOMTree.IncludedSoftware +} + +// GetIncludedDependencies returns PathPathHasSBOM.IncludedDependencies, and is useful for accessing the field via an interface. +func (v *PathPathHasSBOM) GetIncludedDependencies() []AllHasSBOMTreeIncludedDependenciesIsDependency { + return v.AllHasSBOMTree.IncludedDependencies +} + +// GetIncludedOccurrences returns PathPathHasSBOM.IncludedOccurrences, and is useful for accessing the field via an interface. +func (v *PathPathHasSBOM) GetIncludedOccurrences() []AllHasSBOMTreeIncludedOccurrencesIsOccurrence { + return v.AllHasSBOMTree.IncludedOccurrences +} + func (v *PathPathHasSBOM) UnmarshalJSON(b []byte) error { if string(b) == "null" { @@ -18310,6 +19113,12 @@ type __premarshalPathPathHasSBOM struct { Collector string `json:"collector"` KnownSince time.Time `json:"knownSince"` + + IncludedSoftware []json.RawMessage `json:"includedSoftware"` + + IncludedDependencies []AllHasSBOMTreeIncludedDependenciesIsDependency `json:"includedDependencies"` + + IncludedOccurrences []AllHasSBOMTreeIncludedOccurrencesIsOccurrence `json:"includedOccurrences"` } func (v *PathPathHasSBOM) MarshalJSON() ([]byte, error) { @@ -18344,6 +19153,26 @@ func (v *PathPathHasSBOM) __premarshalJSON() (*__premarshalPathPathHasSBOM, erro retval.Origin = v.AllHasSBOMTree.Origin retval.Collector = v.AllHasSBOMTree.Collector retval.KnownSince = v.AllHasSBOMTree.KnownSince + { + + dst := &retval.IncludedSoftware + src := v.AllHasSBOMTree.IncludedSoftware + *dst = make( + []json.RawMessage, + len(src)) + for i, src := range src { + dst := &(*dst)[i] + var err error + *dst, err = __marshalAllHasSBOMTreeIncludedSoftwarePackageOrArtifact( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal PathPathHasSBOM.AllHasSBOMTree.IncludedSoftware: %w", err) + } + } + } + retval.IncludedDependencies = v.AllHasSBOMTree.IncludedDependencies + retval.IncludedOccurrences = v.AllHasSBOMTree.IncludedOccurrences return &retval, nil } @@ -21451,8 +22280,9 @@ func (v *__HasMetadataSrcsInput) GetHasMetadataList() []HasMetadataInputSpec { // __HasSBOMArtifactInput is used internally by genqlient type __HasSBOMArtifactInput struct { - Artifact ArtifactInputSpec `json:"artifact"` - HasSBOM HasSBOMInputSpec `json:"hasSBOM"` + Artifact ArtifactInputSpec `json:"artifact"` + HasSBOM HasSBOMInputSpec `json:"hasSBOM"` + Includes HasSBOMIncludesInputSpec `json:"includes"` } // GetArtifact returns __HasSBOMArtifactInput.Artifact, and is useful for accessing the field via an interface. @@ -21461,10 +22291,14 @@ func (v *__HasSBOMArtifactInput) GetArtifact() ArtifactInputSpec { return v.Arti // GetHasSBOM returns __HasSBOMArtifactInput.HasSBOM, and is useful for accessing the field via an interface. func (v *__HasSBOMArtifactInput) GetHasSBOM() HasSBOMInputSpec { return v.HasSBOM } +// GetIncludes returns __HasSBOMArtifactInput.Includes, and is useful for accessing the field via an interface. +func (v *__HasSBOMArtifactInput) GetIncludes() HasSBOMIncludesInputSpec { return v.Includes } + // __HasSBOMArtifactsInput is used internally by genqlient type __HasSBOMArtifactsInput struct { - Artifacts []ArtifactInputSpec `json:"artifacts"` - HasSBOMs []HasSBOMInputSpec `json:"hasSBOMs"` + Artifacts []ArtifactInputSpec `json:"artifacts"` + HasSBOMs []HasSBOMInputSpec `json:"hasSBOMs"` + Includes []HasSBOMIncludesInputSpec `json:"includes"` } // GetArtifacts returns __HasSBOMArtifactsInput.Artifacts, and is useful for accessing the field via an interface. @@ -21473,10 +22307,14 @@ func (v *__HasSBOMArtifactsInput) GetArtifacts() []ArtifactInputSpec { return v. // GetHasSBOMs returns __HasSBOMArtifactsInput.HasSBOMs, and is useful for accessing the field via an interface. func (v *__HasSBOMArtifactsInput) GetHasSBOMs() []HasSBOMInputSpec { return v.HasSBOMs } +// GetIncludes returns __HasSBOMArtifactsInput.Includes, and is useful for accessing the field via an interface. +func (v *__HasSBOMArtifactsInput) GetIncludes() []HasSBOMIncludesInputSpec { return v.Includes } + // __HasSBOMPkgInput is used internally by genqlient type __HasSBOMPkgInput struct { - Pkg PkgInputSpec `json:"pkg"` - HasSBOM HasSBOMInputSpec `json:"hasSBOM"` + Pkg PkgInputSpec `json:"pkg"` + HasSBOM HasSBOMInputSpec `json:"hasSBOM"` + Includes HasSBOMIncludesInputSpec `json:"includes"` } // GetPkg returns __HasSBOMPkgInput.Pkg, and is useful for accessing the field via an interface. @@ -21485,10 +22323,14 @@ func (v *__HasSBOMPkgInput) GetPkg() PkgInputSpec { return v.Pkg } // GetHasSBOM returns __HasSBOMPkgInput.HasSBOM, and is useful for accessing the field via an interface. func (v *__HasSBOMPkgInput) GetHasSBOM() HasSBOMInputSpec { return v.HasSBOM } +// GetIncludes returns __HasSBOMPkgInput.Includes, and is useful for accessing the field via an interface. +func (v *__HasSBOMPkgInput) GetIncludes() HasSBOMIncludesInputSpec { return v.Includes } + // __HasSBOMPkgsInput is used internally by genqlient type __HasSBOMPkgsInput struct { - Pkgs []PkgInputSpec `json:"pkgs"` - HasSBOMs []HasSBOMInputSpec `json:"hasSBOMs"` + Pkgs []PkgInputSpec `json:"pkgs"` + HasSBOMs []HasSBOMInputSpec `json:"hasSBOMs"` + Includes []HasSBOMIncludesInputSpec `json:"includes"` } // GetPkgs returns __HasSBOMPkgsInput.Pkgs, and is useful for accessing the field via an interface. @@ -21497,6 +22339,9 @@ func (v *__HasSBOMPkgsInput) GetPkgs() []PkgInputSpec { return v.Pkgs } // GetHasSBOMs returns __HasSBOMPkgsInput.HasSBOMs, and is useful for accessing the field via an interface. func (v *__HasSBOMPkgsInput) GetHasSBOMs() []HasSBOMInputSpec { return v.HasSBOMs } +// GetIncludes returns __HasSBOMPkgsInput.Includes, and is useful for accessing the field via an interface. +func (v *__HasSBOMPkgsInput) GetIncludes() []HasSBOMIncludesInputSpec { return v.Includes } + // __IngestArtifactInput is used internally by genqlient type __IngestArtifactInput struct { Artifact ArtifactInputSpec `json:"artifact"` @@ -23543,8 +24388,8 @@ func HasMetadataSrcs( // The query or mutation executed by HasSBOMArtifact. const HasSBOMArtifact_Operation = ` -mutation HasSBOMArtifact ($artifact: ArtifactInputSpec!, $hasSBOM: HasSBOMInputSpec!) { - ingestHasSBOM(subject: {artifact:$artifact}, hasSBOM: $hasSBOM) +mutation HasSBOMArtifact ($artifact: ArtifactInputSpec!, $hasSBOM: HasSBOMInputSpec!, $includes: HasSBOMIncludesInputSpec!) { + ingestHasSBOM(subject: {artifact:$artifact}, hasSBOM: $hasSBOM, includes: $includes) } ` @@ -23553,6 +24398,7 @@ func HasSBOMArtifact( client graphql.Client, artifact ArtifactInputSpec, hasSBOM HasSBOMInputSpec, + includes HasSBOMIncludesInputSpec, ) (*HasSBOMArtifactResponse, error) { req := &graphql.Request{ OpName: "HasSBOMArtifact", @@ -23560,6 +24406,7 @@ func HasSBOMArtifact( Variables: &__HasSBOMArtifactInput{ Artifact: artifact, HasSBOM: hasSBOM, + Includes: includes, }, } var err error @@ -23578,8 +24425,8 @@ func HasSBOMArtifact( // The query or mutation executed by HasSBOMArtifacts. const HasSBOMArtifacts_Operation = ` -mutation HasSBOMArtifacts ($artifacts: [ArtifactInputSpec!]!, $hasSBOMs: [HasSBOMInputSpec!]!) { - ingestHasSBOMs(subjects: {artifacts:$artifacts}, hasSBOMs: $hasSBOMs) +mutation HasSBOMArtifacts ($artifacts: [ArtifactInputSpec!]!, $hasSBOMs: [HasSBOMInputSpec!]!, $includes: [HasSBOMIncludesInputSpec!]!) { + ingestHasSBOMs(subjects: {artifacts:$artifacts}, hasSBOMs: $hasSBOMs, includes: $includes) } ` @@ -23588,6 +24435,7 @@ func HasSBOMArtifacts( client graphql.Client, artifacts []ArtifactInputSpec, hasSBOMs []HasSBOMInputSpec, + includes []HasSBOMIncludesInputSpec, ) (*HasSBOMArtifactsResponse, error) { req := &graphql.Request{ OpName: "HasSBOMArtifacts", @@ -23595,6 +24443,7 @@ func HasSBOMArtifacts( Variables: &__HasSBOMArtifactsInput{ Artifacts: artifacts, HasSBOMs: hasSBOMs, + Includes: includes, }, } var err error @@ -23613,8 +24462,8 @@ func HasSBOMArtifacts( // The query or mutation executed by HasSBOMPkg. const HasSBOMPkg_Operation = ` -mutation HasSBOMPkg ($pkg: PkgInputSpec!, $hasSBOM: HasSBOMInputSpec!) { - ingestHasSBOM(subject: {package:$pkg}, hasSBOM: $hasSBOM) +mutation HasSBOMPkg ($pkg: PkgInputSpec!, $hasSBOM: HasSBOMInputSpec!, $includes: HasSBOMIncludesInputSpec!) { + ingestHasSBOM(subject: {package:$pkg}, hasSBOM: $hasSBOM, includes: $includes) } ` @@ -23623,13 +24472,15 @@ func HasSBOMPkg( client graphql.Client, pkg PkgInputSpec, hasSBOM HasSBOMInputSpec, + includes HasSBOMIncludesInputSpec, ) (*HasSBOMPkgResponse, error) { req := &graphql.Request{ OpName: "HasSBOMPkg", Query: HasSBOMPkg_Operation, Variables: &__HasSBOMPkgInput{ - Pkg: pkg, - HasSBOM: hasSBOM, + Pkg: pkg, + HasSBOM: hasSBOM, + Includes: includes, }, } var err error @@ -23648,8 +24499,8 @@ func HasSBOMPkg( // The query or mutation executed by HasSBOMPkgs. const HasSBOMPkgs_Operation = ` -mutation HasSBOMPkgs ($pkgs: [PkgInputSpec!]!, $hasSBOMs: [HasSBOMInputSpec!]!) { - ingestHasSBOMs(subjects: {packages:$pkgs}, hasSBOMs: $hasSBOMs) +mutation HasSBOMPkgs ($pkgs: [PkgInputSpec!]!, $hasSBOMs: [HasSBOMInputSpec!]!, $includes: [HasSBOMIncludesInputSpec!]!) { + ingestHasSBOMs(subjects: {packages:$pkgs}, hasSBOMs: $hasSBOMs, includes: $includes) } ` @@ -23658,6 +24509,7 @@ func HasSBOMPkgs( client graphql.Client, pkgs []PkgInputSpec, hasSBOMs []HasSBOMInputSpec, + includes []HasSBOMIncludesInputSpec, ) (*HasSBOMPkgsResponse, error) { req := &graphql.Request{ OpName: "HasSBOMPkgs", @@ -23665,6 +24517,7 @@ func HasSBOMPkgs( Variables: &__HasSBOMPkgsInput{ Pkgs: pkgs, HasSBOMs: hasSBOMs, + Includes: includes, }, } var err error @@ -24034,7 +24887,12 @@ func IngestLicenses( // The query or mutation executed by IngestPackage. const IngestPackage_Operation = ` mutation IngestPackage ($pkg: PkgInputSpec!) { - ingestPackage(pkg: $pkg) + ingestPackage(pkg: $pkg) { + packageTypeID + packageNamespaceID + packageNameID + packageVersionID + } } ` @@ -24067,7 +24925,12 @@ func IngestPackage( // The query or mutation executed by IngestPackages. const IngestPackages_Operation = ` mutation IngestPackages ($pkgs: [PkgInputSpec!]!) { - ingestPackages(pkgs: $pkgs) + ingestPackages(pkgs: $pkgs) { + packageTypeID + packageNamespaceID + packageNameID + packageVersionID + } } ` @@ -24174,7 +25037,11 @@ func IngestPkgEquals( // The query or mutation executed by IngestSource. const IngestSource_Operation = ` mutation IngestSource ($source: SourceInputSpec!) { - ingestSource(source: $source) + ingestSource(source: $source) { + sourceTypeID + sourceNamespaceID + sourceNameID + } } ` @@ -24207,7 +25074,11 @@ func IngestSource( // The query or mutation executed by IngestSources. const IngestSources_Operation = ` mutation IngestSources ($sources: [SourceInputSpec!]!) { - ingestSources(sources: $sources) + ingestSources(sources: $sources) { + sourceTypeID + sourceNamespaceID + sourceNameID + } } ` @@ -24930,6 +25801,21 @@ fragment AllHasSBOMTree on HasSBOM { origin collector knownSince + includedSoftware { + __typename + ... on Artifact { + ... AllArtifactTree + } + ... on Package { + ... AllPkgTree + } + } + includedDependencies { + ... AllIsDependencyTree + } + includedOccurrences { + ... AllIsOccurrencesTree + } } fragment AllHasSourceAt on HasSourceAt { id @@ -25392,6 +26278,21 @@ fragment AllHasSBOMTree on HasSBOM { origin collector knownSince + includedSoftware { + __typename + ... on Artifact { + ... AllArtifactTree + } + ... on Package { + ... AllPkgTree + } + } + includedDependencies { + ... AllIsDependencyTree + } + includedOccurrences { + ... AllIsOccurrencesTree + } } fragment AllHasSourceAt on HasSourceAt { id @@ -25852,6 +26753,21 @@ fragment AllHasSBOMTree on HasSBOM { origin collector knownSince + includedSoftware { + __typename + ... on Artifact { + ... AllArtifactTree + } + ... on Package { + ... AllPkgTree + } + } + includedDependencies { + ... AllIsDependencyTree + } + includedOccurrences { + ... AllIsOccurrencesTree + } } fragment AllHasSourceAt on HasSourceAt { id @@ -26541,6 +27457,21 @@ fragment AllHasSBOMTree on HasSBOM { origin collector knownSince + includedSoftware { + __typename + ... on Artifact { + ... AllArtifactTree + } + ... on Package { + ... AllPkgTree + } + } + includedDependencies { + ... AllIsDependencyTree + } + includedOccurrences { + ... AllIsOccurrencesTree + } } fragment AllHasSourceAt on HasSourceAt { id diff --git a/pkg/assembler/clients/helpers/assembler.go b/pkg/assembler/clients/helpers/assembler.go index b7d1810ca1..d7666168b9 100644 --- a/pkg/assembler/clients/helpers/assembler.go +++ b/pkg/assembler/clients/helpers/assembler.go @@ -30,11 +30,14 @@ func GetAssembler(ctx context.Context, gqlclient graphql.Client) func([]assemble logger := logging.FromContext(ctx) return func(preds []assembler.IngestPredicates) error { for _, p := range preds { + var packageAndArtifactIDs []string packages := p.GetPackages(ctx) logger.Infof("assembling Package: %v", len(packages)) for _, v := range packages { - if err := ingestPackage(ctx, gqlclient, v); err != nil { + if id, err := ingestPackage(ctx, gqlclient, v); err != nil { return err + } else { + packageAndArtifactIDs = append(packageAndArtifactIDs, *id) } } @@ -49,15 +52,19 @@ func GetAssembler(ctx context.Context, gqlclient graphql.Client) func([]assemble artifacts := p.GetArtifacts(ctx) logger.Infof("assembling Artifact: %v", len(artifacts)) for _, v := range artifacts { - if err := ingestArtifact(ctx, gqlclient, v); err != nil { + if id, err := ingestArtifact(ctx, gqlclient, v); err != nil { return err + } else { + packageAndArtifactIDs = append(packageAndArtifactIDs, *id) } } materials := p.GetMaterials(ctx) logger.Infof("assembling Materials (Artifact): %v", len(materials)) - if err := ingestArtifacts(ctx, gqlclient, materials); err != nil { + if ids, err := ingestArtifacts(ctx, gqlclient, materials); err != nil { return err + } else { + packageAndArtifactIDs = append(packageAndArtifactIDs, ids...) } builders := p.GetBuilders(ctx) @@ -91,17 +98,23 @@ func GetAssembler(ctx context.Context, gqlclient graphql.Client) func([]assemble } } + var isDependenciesIDs []string logger.Infof("assembling IsDependency: %v", len(p.IsDependency)) for _, v := range p.IsDependency { - if err := ingestIsDependency(ctx, gqlclient, v); err != nil { + if id, err := ingestIsDependency(ctx, gqlclient, v); err != nil { return err + } else { + isDependenciesIDs = append(isDependenciesIDs, *id) } } + var isOccurrencesIDs []string logger.Infof("assembling IsOccurrence: %v", len(p.IsOccurrence)) for _, v := range p.IsOccurrence { - if err := ingestIsOccurrence(ctx, gqlclient, v); err != nil { + if id, err := ingestIsOccurrence(ctx, gqlclient, v); err != nil { return err + } else { + isOccurrencesIDs = append(isOccurrencesIDs, *id) } } @@ -168,8 +181,15 @@ func GetAssembler(ctx context.Context, gqlclient graphql.Client) func([]assemble } } + includes := model.HasSBOMIncludesInputSpec{ + Software: packageAndArtifactIDs, + Dependencies: isDependenciesIDs, + Occurrences: isOccurrencesIDs, + } + logger.Infof("assembling HasSBOM: %v", len(p.HasSBOM)) for _, hb := range p.HasSBOM { + hb.Includes = &includes if err := ingestHasSBOM(ctx, gqlclient, hb); err != nil { return err } @@ -207,9 +227,12 @@ func GetAssembler(ctx context.Context, gqlclient graphql.Client) func([]assemble } } -func ingestPackage(ctx context.Context, client graphql.Client, v *model.PkgInputSpec) error { - _, err := model.IngestPackage(ctx, client, *v) - return err +func ingestPackage(ctx context.Context, client graphql.Client, v *model.PkgInputSpec) (*string, error) { + if result, err := model.IngestPackage(ctx, client, *v); err != nil { + return nil, err + } else { + return &result.IngestPackage.PackageVersionID, nil + } } func ingestSource(ctx context.Context, client graphql.Client, v *model.SourceInputSpec) error { @@ -217,9 +240,12 @@ func ingestSource(ctx context.Context, client graphql.Client, v *model.SourceInp return err } -func ingestArtifact(ctx context.Context, client graphql.Client, v *model.ArtifactInputSpec) error { - _, err := model.IngestArtifact(ctx, client, *v) - return err +func ingestArtifact(ctx context.Context, client graphql.Client, v *model.ArtifactInputSpec) (*string, error) { + if result, err := model.IngestArtifact(ctx, client, *v); err != nil { + return nil, err + } else { + return &result.IngestArtifact, err + } } func ingestBuilder(ctx context.Context, client graphql.Client, v *model.BuilderInputSpec) error { @@ -242,25 +268,34 @@ func ingestCertifyScorecard(ctx context.Context, client graphql.Client, v assemb return err } -func ingestIsDependency(ctx context.Context, client graphql.Client, v assembler.IsDependencyIngest) error { - _, err := model.IsDependency(ctx, client, *v.Pkg, *v.DepPkg, v.DepPkgMatchFlag, *v.IsDependency) - return err +func ingestIsDependency(ctx context.Context, client graphql.Client, v assembler.IsDependencyIngest) (*string, error) { + if response, err := model.IsDependency(ctx, client, *v.Pkg, *v.DepPkg, v.DepPkgMatchFlag, *v.IsDependency); err != nil { + return nil, err + } else { + return &response.IngestDependency, nil + } } -func ingestIsOccurrence(ctx context.Context, client graphql.Client, v assembler.IsOccurrenceIngest) error { +func ingestIsOccurrence(ctx context.Context, client graphql.Client, v assembler.IsOccurrenceIngest) (*string, error) { if v.Pkg != nil && v.Src != nil { - return fmt.Errorf("unable to create IsOccurrence with both Src and Pkg subject specified") + return nil, fmt.Errorf("unable to create IsOccurrence with both Src and Pkg subject specified") } if v.Pkg == nil && v.Src == nil { - return fmt.Errorf("unable to create IsOccurrence without either Src and Pkg subject specified") + return nil, fmt.Errorf("unable to create IsOccurrence without either Src and Pkg subject specified") } if v.Src != nil { - _, err := model.IsOccurrenceSrc(ctx, client, *v.Src, *v.Artifact, *v.IsOccurrence) - return err + if result, err := model.IsOccurrenceSrc(ctx, client, *v.Src, *v.Artifact, *v.IsOccurrence); err != nil { + return nil, err + } else { + return &result.IngestOccurrence, nil + } + } + if result, err := model.IsOccurrencePkg(ctx, client, *v.Pkg, *v.Artifact, *v.IsOccurrence); err != nil { + return nil, err + } else { + return &result.IngestOccurrence, err } - _, err := model.IsOccurrencePkg(ctx, client, *v.Pkg, *v.Artifact, *v.IsOccurrence) - return err } func ingestHasSlsa(ctx context.Context, client graphql.Client, v assembler.HasSlsaIngest) error { @@ -367,10 +402,10 @@ func ingestHasSBOM(ctx context.Context, client graphql.Client, hb assembler.HasS } if hb.Pkg != nil { - _, err := model.HasSBOMPkg(ctx, client, *hb.Pkg, *hb.HasSBOM) + _, err := model.HasSBOMPkg(ctx, client, *hb.Pkg, *hb.HasSBOM, *hb.Includes) return err } - _, err := model.HasSBOMArtifact(ctx, client, *hb.Artifact, *hb.HasSBOM) + _, err := model.HasSBOMArtifact(ctx, client, *hb.Artifact, *hb.HasSBOM, *hb.Includes) return err } diff --git a/pkg/assembler/clients/helpers/bulk.go b/pkg/assembler/clients/helpers/bulk.go index 8a48bd8150..901697e7e5 100644 --- a/pkg/assembler/clients/helpers/bulk.go +++ b/pkg/assembler/clients/helpers/bulk.go @@ -30,6 +30,8 @@ func GetBulkAssembler(ctx context.Context, gqlclient graphql.Client) func([]asse logger := logging.FromContext(ctx) return func(preds []assembler.IngestPredicates) error { for _, p := range preds { + var packageAndArtifactIDs []string + packages := p.GetPackages(ctx) logger.Infof("assembling Package: %v", len(packages)) var collectedPackages []model.PkgInputSpec @@ -37,8 +39,10 @@ func GetBulkAssembler(ctx context.Context, gqlclient graphql.Client) func([]asse for _, v := range packages { collectedPackages = append(collectedPackages, *v) } - if err := ingestPackages(ctx, gqlclient, collectedPackages); err != nil { + if ids, err := ingestPackages(ctx, gqlclient, collectedPackages); err != nil { logger.Errorf("ingestPackages failed with error: %v", err) + } else { + packageAndArtifactIDs = append(packageAndArtifactIDs, ids...) } sources := p.GetSources(ctx) @@ -60,14 +64,18 @@ func GetBulkAssembler(ctx context.Context, gqlclient graphql.Client) func([]asse for _, v := range artifacts { collectedArtifacts = append(collectedArtifacts, *v) } - if err := ingestArtifacts(ctx, gqlclient, collectedArtifacts); err != nil { + if ids, err := ingestArtifacts(ctx, gqlclient, collectedArtifacts); err != nil { logger.Errorf("ingestArtifacts failed with error: %v", err) + } else { + packageAndArtifactIDs = append(packageAndArtifactIDs, ids...) } materials := p.GetMaterials(ctx) logger.Infof("assembling Materials (Artifact): %v", len(materials)) - if err := ingestArtifacts(ctx, gqlclient, materials); err != nil { + if ids, err := ingestArtifacts(ctx, gqlclient, materials); err != nil { logger.Errorf("ingestArtifacts failed with error: %v", err) + } else { + packageAndArtifactIDs = append(packageAndArtifactIDs, ids...) } builders := p.GetBuilders(ctx) @@ -104,13 +112,19 @@ func GetBulkAssembler(ctx context.Context, gqlclient graphql.Client) func([]asse } logger.Infof("assembling IsDependency: %v", len(p.IsDependency)) - if err := ingestIsDependencies(ctx, gqlclient, p.IsDependency); err != nil { + isDependenciesIDs := []string{} + if ingestedIsDependenciesIDs, err := ingestIsDependencies(ctx, gqlclient, p.IsDependency); err != nil { logger.Errorf("ingestIsDependencies failed with error: %v", err) + } else { + isDependenciesIDs = append(isDependenciesIDs, ingestedIsDependenciesIDs...) } logger.Infof("assembling IsOccurrence: %v", len(p.IsOccurrence)) - if err := ingestIsOccurrences(ctx, gqlclient, p.IsOccurrence); err != nil { + isOccurrencesIDs := []string{} + if ingestedIsOccurrencesIDs, err := ingestIsOccurrences(ctx, gqlclient, p.IsOccurrence); err != nil { logger.Errorf("ingestIsOccurrences failed with error: %v", err) + } else { + isOccurrencesIDs = append(isOccurrencesIDs, ingestedIsOccurrencesIDs...) } logger.Infof("assembling HasSLSA: %v", len(p.HasSlsa)) @@ -162,7 +176,11 @@ func GetBulkAssembler(ctx context.Context, gqlclient graphql.Client) func([]asse } logger.Infof("assembling HasSBOM: %v", len(p.HasSBOM)) - if err := ingestHasSBOMs(ctx, gqlclient, p.HasSBOM); err != nil { + if err := ingestHasSBOMs(ctx, gqlclient, p.HasSBOM, model.HasSBOMIncludesInputSpec{ + Software: packageAndArtifactIDs, + Dependencies: isDependenciesIDs, + Occurrences: isOccurrencesIDs, + }); err != nil { logger.Errorf("ingestHasSBOMs failed with error: %v", err) } @@ -190,12 +208,16 @@ func GetBulkAssembler(ctx context.Context, gqlclient graphql.Client) func([]asse } } -func ingestPackages(ctx context.Context, client graphql.Client, v []model.PkgInputSpec) error { - _, err := model.IngestPackages(ctx, client, v) +func ingestPackages(ctx context.Context, client graphql.Client, v []model.PkgInputSpec) ([]string, error) { + response, err := model.IngestPackages(ctx, client, v) if err != nil { - return fmt.Errorf("ingestPackages failed with error: %w", err) + return nil, fmt.Errorf("ingestPackages failed with error: %w", err) } - return nil + var results []string + for _, pkg := range response.IngestPackages { + results = append(results, pkg.PackageVersionID) + } + return results, nil } func ingestSources(ctx context.Context, client graphql.Client, v []model.SourceInputSpec) error { @@ -206,12 +228,12 @@ func ingestSources(ctx context.Context, client graphql.Client, v []model.SourceI return nil } -func ingestArtifacts(ctx context.Context, client graphql.Client, v []model.ArtifactInputSpec) error { - _, err := model.IngestArtifacts(ctx, client, v) +func ingestArtifacts(ctx context.Context, client graphql.Client, v []model.ArtifactInputSpec) ([]string, error) { + response, err := model.IngestArtifacts(ctx, client, v) if err != nil { - return fmt.Errorf("ingestArtifacts failed with error: %w", err) + return nil, fmt.Errorf("ingestArtifacts failed with error: %w", err) } - return nil + return response.IngestArtifacts, nil } func ingestBuilders(ctx context.Context, client graphql.Client, v []model.BuilderInputSpec) error { @@ -400,7 +422,7 @@ func ingestCertifyScorecards(ctx context.Context, client graphql.Client, v []ass return nil } -func ingestIsDependencies(ctx context.Context, client graphql.Client, v []assembler.IsDependencyIngest) error { +func ingestIsDependencies(ctx context.Context, client graphql.Client, v []assembler.IsDependencyIngest) ([]string, error) { var depToVersion, depToName struct { pkgs []model.PkgInputSpec @@ -424,20 +446,23 @@ func ingestIsDependencies(ctx context.Context, client graphql.Client, v []assemb } } + var isDependenciesIDs []string if len(depToVersion.pkgs) > 0 { - _, err := model.IsDependencies(ctx, client, depToVersion.pkgs, depToVersion.depPkgs, depToVersion.depPkgMatchFlag, depToVersion.dependencies) + isDependencies, err := model.IsDependencies(ctx, client, depToVersion.pkgs, depToVersion.depPkgs, depToVersion.depPkgMatchFlag, depToVersion.dependencies) if err != nil { - return fmt.Errorf("isDependencies failed with error: %w", err) + return nil, fmt.Errorf("isDependencies failed with error: %w", err) } + isDependenciesIDs = append(isDependenciesIDs, isDependencies.IngestDependencies...) } if len(depToName.pkgs) > 0 { - _, err := model.IsDependencies(ctx, client, depToName.pkgs, depToName.depPkgs, depToName.depPkgMatchFlag, depToName.dependencies) + isDependencies, err := model.IsDependencies(ctx, client, depToName.pkgs, depToName.depPkgs, depToName.depPkgMatchFlag, depToName.dependencies) if err != nil { - return fmt.Errorf("isDependencies failed with error: %w", err) + return nil, fmt.Errorf("isDependencies failed with error: %w", err) } + isDependenciesIDs = append(isDependenciesIDs, isDependencies.IngestDependencies...) } - return nil + return isDependenciesIDs, nil } func ingestPkgEquals(ctx context.Context, client graphql.Client, v []assembler.PkgEqualIngest) error { @@ -476,11 +501,13 @@ func ingestHashEquals(ctx context.Context, client graphql.Client, v []assembler. return nil } -func ingestHasSBOMs(ctx context.Context, client graphql.Client, v []assembler.HasSBOMIngest) error { +func ingestHasSBOMs(ctx context.Context, client graphql.Client, v []assembler.HasSBOMIngest, includes model.HasSBOMIncludesInputSpec) error { var pkgs []model.PkgInputSpec var artifacts []model.ArtifactInputSpec var pkgSBOMs []model.HasSBOMInputSpec var artSBOMs []model.HasSBOMInputSpec + var pkgIncludes []model.HasSBOMIncludesInputSpec + var artIncludes []model.HasSBOMIncludesInputSpec for _, ingest := range v { if ingest.Pkg != nil && ingest.Artifact != nil { return fmt.Errorf("unable to create hasSBOM with both artifact and Pkg subject specified") @@ -492,19 +519,21 @@ func ingestHasSBOMs(ctx context.Context, client graphql.Client, v []assembler.Ha if ingest.Pkg != nil { pkgs = append(pkgs, *ingest.Pkg) pkgSBOMs = append(pkgSBOMs, *ingest.HasSBOM) + pkgIncludes = append(pkgIncludes, includes) } else { artifacts = append(artifacts, *ingest.Artifact) artSBOMs = append(artSBOMs, *ingest.HasSBOM) + artIncludes = append(artIncludes, includes) } } if len(artifacts) > 0 { - _, err := model.HasSBOMArtifacts(ctx, client, artifacts, artSBOMs) + _, err := model.HasSBOMArtifacts(ctx, client, artifacts, artSBOMs, artIncludes) if err != nil { return fmt.Errorf("hasSBOMArtifacts failed with error: %w", err) } } if len(pkgs) > 0 { - _, err := model.HasSBOMPkgs(ctx, client, pkgs, pkgSBOMs) + _, err := model.HasSBOMPkgs(ctx, client, pkgs, pkgSBOMs, pkgIncludes) if err != nil { return fmt.Errorf("hasSBOMPkgs failed with error: %w", err) } @@ -736,7 +765,7 @@ func ingestCertifyBads(ctx context.Context, client graphql.Client, v []assembler return nil } -func ingestIsOccurrences(ctx context.Context, client graphql.Client, v []assembler.IsOccurrenceIngest) error { +func ingestIsOccurrences(ctx context.Context, client graphql.Client, v []assembler.IsOccurrenceIngest) ([]string, error) { var pkgs []model.PkgInputSpec var sources []model.SourceInputSpec var pkgArtifacts []model.ArtifactInputSpec @@ -746,10 +775,10 @@ func ingestIsOccurrences(ctx context.Context, client graphql.Client, v []assembl for _, ingest := range v { if ingest.Pkg != nil && ingest.Src != nil { - return fmt.Errorf("unable to create IsOccurrence with both Src and Pkg subject specified") + return nil, fmt.Errorf("unable to create IsOccurrence with both Src and Pkg subject specified") } if ingest.Pkg == nil && ingest.Src == nil { - return fmt.Errorf("unable to create IsOccurrence without either Src and Pkg subject specified") + return nil, fmt.Errorf("unable to create IsOccurrence without either Src and Pkg subject specified") } if ingest.Pkg != nil { @@ -762,19 +791,22 @@ func ingestIsOccurrences(ctx context.Context, client graphql.Client, v []assembl srcOccurrences = append(srcOccurrences, *ingest.IsOccurrence) } } + var isOccurrencesIDs []string if len(sources) > 0 { - _, err := model.IsOccurrencesSrc(ctx, client, sources, srcArtifacts, srcOccurrences) + isOccurrences, err := model.IsOccurrencesSrc(ctx, client, sources, srcArtifacts, srcOccurrences) if err != nil { - return fmt.Errorf("isOccurrencesSrc failed with error: %w", err) + return nil, fmt.Errorf("isOccurrencesSrc failed with error: %w", err) } + isOccurrencesIDs = append(isOccurrencesIDs, isOccurrences.IngestOccurrences...) } if len(pkgs) > 0 { - _, err := model.IsOccurrencesPkg(ctx, client, pkgs, pkgArtifacts, pkgOccurrences) + isOccurrences, err := model.IsOccurrencesPkg(ctx, client, pkgs, pkgArtifacts, pkgOccurrences) if err != nil { - return fmt.Errorf("isOccurrencesPkg failed with error: %w", err) + return nil, fmt.Errorf("isOccurrencesPkg failed with error: %w", err) } + isOccurrencesIDs = append(isOccurrencesIDs, isOccurrences.IngestOccurrences...) } - return nil + return isOccurrencesIDs, nil } func ingestCertifyLegals(ctx context.Context, client graphql.Client, v []assembler.CertifyLegalIngest) error { diff --git a/pkg/assembler/clients/operations/hasSBOM.graphql b/pkg/assembler/clients/operations/hasSBOM.graphql index 5c6f2ba8cb..de115ef15e 100644 --- a/pkg/assembler/clients/operations/hasSBOM.graphql +++ b/pkg/assembler/clients/operations/hasSBOM.graphql @@ -17,26 +17,28 @@ # Defines the GraphQL operations to ingest that a package or source has an SBOM (specified by a URI) into GUAC -mutation HasSBOMPkg($pkg: PkgInputSpec!, $hasSBOM: HasSBOMInputSpec!) { - ingestHasSBOM(subject: { package: $pkg }, hasSBOM: $hasSBOM) +mutation HasSBOMPkg($pkg: PkgInputSpec!, $hasSBOM: HasSBOMInputSpec!, $includes: HasSBOMIncludesInputSpec!) { + ingestHasSBOM(subject: { package: $pkg }, hasSBOM: $hasSBOM, includes: $includes) } mutation HasSBOMArtifact( $artifact: ArtifactInputSpec! $hasSBOM: HasSBOMInputSpec! + $includes: HasSBOMIncludesInputSpec! ) { - ingestHasSBOM(subject: { artifact: $artifact }, hasSBOM: $hasSBOM) + ingestHasSBOM(subject: { artifact: $artifact }, hasSBOM: $hasSBOM, includes: $includes) } # Defines the GraphQL operations to bulk ingest hasSBOM information into GUAC -mutation HasSBOMPkgs($pkgs: [PkgInputSpec!]!, $hasSBOMs: [HasSBOMInputSpec!]!) { - ingestHasSBOMs(subjects: { packages: $pkgs }, hasSBOMs: $hasSBOMs) +mutation HasSBOMPkgs($pkgs: [PkgInputSpec!]!, $hasSBOMs: [HasSBOMInputSpec!]!, $includes: [HasSBOMIncludesInputSpec!]!) { + ingestHasSBOMs(subjects: { packages: $pkgs }, hasSBOMs: $hasSBOMs, includes: $includes) } mutation HasSBOMArtifacts( $artifacts: [ArtifactInputSpec!]! $hasSBOMs: [HasSBOMInputSpec!]! + $includes: [HasSBOMIncludesInputSpec!]! ) { - ingestHasSBOMs(subjects: { artifacts: $artifacts }, hasSBOMs: $hasSBOMs) + ingestHasSBOMs(subjects: { artifacts: $artifacts }, hasSBOMs: $hasSBOMs, includes: $includes) } diff --git a/pkg/assembler/clients/operations/package.graphql b/pkg/assembler/clients/operations/package.graphql index 2cf4d2e9d4..c475acc6b1 100644 --- a/pkg/assembler/clients/operations/package.graphql +++ b/pkg/assembler/clients/operations/package.graphql @@ -18,13 +18,23 @@ # Ingest Package mutation IngestPackage($pkg: PkgInputSpec!) { - ingestPackage(pkg: $pkg) + ingestPackage(pkg: $pkg) { + packageTypeID + packageNamespaceID + packageNameID + packageVersionID + } } # Bulk Ingest Packages mutation IngestPackages($pkgs: [PkgInputSpec!]!) { - ingestPackages(pkgs: $pkgs) + ingestPackages(pkgs: $pkgs) { + packageTypeID + packageNamespaceID + packageNameID + packageVersionID + } } # Exposes GraphQL queries to retrieve GUAC packages diff --git a/pkg/assembler/clients/operations/source.graphql b/pkg/assembler/clients/operations/source.graphql index d8e9f6b95e..3adb63829b 100644 --- a/pkg/assembler/clients/operations/source.graphql +++ b/pkg/assembler/clients/operations/source.graphql @@ -18,13 +18,21 @@ # Ingest Source mutation IngestSource($source: SourceInputSpec!) { - ingestSource(source: $source) + ingestSource(source: $source) { + sourceTypeID + sourceNamespaceID + sourceNameID + } } # Bulk Ingest Sources mutation IngestSources($sources: [SourceInputSpec!]!) { - ingestSources(sources: $sources) + ingestSources(sources: $sources) { + sourceTypeID + sourceNamespaceID + sourceNameID + } } # Exposes GraphQL queries to retrieve GUAC sources diff --git a/pkg/assembler/clients/operations/trees.graphql b/pkg/assembler/clients/operations/trees.graphql index 866004363a..c9aeba03a3 100644 --- a/pkg/assembler/clients/operations/trees.graphql +++ b/pkg/assembler/clients/operations/trees.graphql @@ -273,6 +273,21 @@ fragment AllHasSBOMTree on HasSBOM { origin collector knownSince + includedSoftware { + __typename + ... on Artifact { + ...AllArtifactTree + } + ... on Package { + ...AllPkgTree + } + } + includedDependencies { + ...AllIsDependencyTree + } + includedOccurrences { + ...AllIsOccurrencesTree + } } fragment AllHasSourceAt on HasSourceAt { diff --git a/pkg/assembler/graphql/README.md b/pkg/assembler/graphql/README.md index 03e8a3fe3b..9aa69b4c1d 100644 --- a/pkg/assembler/graphql/README.md +++ b/pkg/assembler/graphql/README.md @@ -31,3 +31,7 @@ project. ## GraphQL Examples - `examples`: queries used to test the backend, from the playground + +## GraphQL Helpers + +- `helpers`: utility functions used within the graphql resolvers \ No newline at end of file diff --git a/pkg/assembler/graphql/generated/artifact.generated.go b/pkg/assembler/graphql/generated/artifact.generated.go index 1e9ec2c04e..906c4ea1ff 100644 --- a/pkg/assembler/graphql/generated/artifact.generated.go +++ b/pkg/assembler/graphql/generated/artifact.generated.go @@ -37,8 +37,8 @@ type MutationResolver interface { IngestCertifyVulns(ctx context.Context, pkgs []*model.PkgInputSpec, vulnerabilities []*model.VulnerabilityInputSpec, certifyVulns []*model.ScanMetadataInput) ([]string, error) IngestPointOfContact(ctx context.Context, subject model.PackageSourceOrArtifactInput, pkgMatchType model.MatchFlags, pointOfContact model.PointOfContactInputSpec) (string, error) IngestPointOfContacts(ctx context.Context, subjects model.PackageSourceOrArtifactInputs, pkgMatchType model.MatchFlags, pointOfContacts []*model.PointOfContactInputSpec) ([]string, error) - IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec) (string, error) - IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec) ([]string, error) + IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec, includes model.HasSBOMIncludesInputSpec) (string, error) + IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec, includes []*model.HasSBOMIncludesInputSpec) ([]string, error) IngestSlsa(ctx context.Context, subject model.ArtifactInputSpec, builtFrom []*model.ArtifactInputSpec, builtBy model.BuilderInputSpec, slsa model.SLSAInputSpec) (string, error) IngestSLSAs(ctx context.Context, subjects []*model.ArtifactInputSpec, builtFromList [][]*model.ArtifactInputSpec, builtByList []*model.BuilderInputSpec, slsaList []*model.SLSAInputSpec) ([]string, error) IngestHasSourceAt(ctx context.Context, pkg model.PkgInputSpec, pkgMatchType model.MatchFlags, source model.SourceInputSpec, hasSourceAt model.HasSourceAtInputSpec) (string, error) @@ -53,12 +53,12 @@ type MutationResolver interface { IngestLicenses(ctx context.Context, licenses []*model.LicenseInputSpec) ([]string, error) IngestHasMetadata(ctx context.Context, subject model.PackageSourceOrArtifactInput, pkgMatchType model.MatchFlags, hasMetadata model.HasMetadataInputSpec) (string, error) IngestBulkHasMetadata(ctx context.Context, subjects model.PackageSourceOrArtifactInputs, pkgMatchType model.MatchFlags, hasMetadataList []*model.HasMetadataInputSpec) ([]string, error) - IngestPackage(ctx context.Context, pkg model.PkgInputSpec) (string, error) - IngestPackages(ctx context.Context, pkgs []*model.PkgInputSpec) ([]string, error) + IngestPackage(ctx context.Context, pkg model.PkgInputSpec) (*model.PackageIDs, error) + IngestPackages(ctx context.Context, pkgs []*model.PkgInputSpec) ([]*model.PackageIDs, error) IngestPkgEqual(ctx context.Context, pkg model.PkgInputSpec, otherPackage model.PkgInputSpec, pkgEqual model.PkgEqualInputSpec) (string, error) IngestPkgEquals(ctx context.Context, pkgs []*model.PkgInputSpec, otherPackages []*model.PkgInputSpec, pkgEquals []*model.PkgEqualInputSpec) ([]string, error) - IngestSource(ctx context.Context, source model.SourceInputSpec) (string, error) - IngestSources(ctx context.Context, sources []*model.SourceInputSpec) ([]string, error) + IngestSource(ctx context.Context, source model.SourceInputSpec) (*model.SourceIDs, error) + IngestSources(ctx context.Context, sources []*model.SourceInputSpec) ([]*model.SourceIDs, error) IngestVulnEqual(ctx context.Context, vulnerability model.VulnerabilityInputSpec, otherVulnerability model.VulnerabilityInputSpec, vulnEqual model.VulnEqualInputSpec) (string, error) IngestVulnEquals(ctx context.Context, vulnerabilities []*model.VulnerabilityInputSpec, otherVulnerabilities []*model.VulnerabilityInputSpec, vulnEquals []*model.VulnEqualInputSpec) ([]string, error) IngestVulnerabilityMetadata(ctx context.Context, vulnerability model.VulnerabilityInputSpec, vulnerabilityMetadata model.VulnerabilityMetadataInputSpec) (string, error) @@ -638,6 +638,15 @@ func (ec *executionContext) field_Mutation_ingestHasSBOM_args(ctx context.Contex } } args["hasSBOM"] = arg1 + var arg2 model.HasSBOMIncludesInputSpec + if tmp, ok := rawArgs["includes"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includes")) + arg2, err = ec.unmarshalNHasSBOMIncludesInputSpec2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐHasSBOMIncludesInputSpec(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includes"] = arg2 return args, nil } @@ -662,6 +671,15 @@ func (ec *executionContext) field_Mutation_ingestHasSBOMs_args(ctx context.Conte } } args["hasSBOMs"] = arg1 + var arg2 []*model.HasSBOMIncludesInputSpec + if tmp, ok := rawArgs["includes"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includes")) + arg2, err = ec.unmarshalNHasSBOMIncludesInputSpec2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐHasSBOMIncludesInputSpecᚄ(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includes"] = arg2 return args, nil } @@ -3036,7 +3054,7 @@ func (ec *executionContext) _Mutation_ingestHasSBOM(ctx context.Context, field g }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().IngestHasSbom(rctx, fc.Args["subject"].(model.PackageOrArtifactInput), fc.Args["hasSBOM"].(model.HasSBOMInputSpec)) + return ec.resolvers.Mutation().IngestHasSbom(rctx, fc.Args["subject"].(model.PackageOrArtifactInput), fc.Args["hasSBOM"].(model.HasSBOMInputSpec), fc.Args["includes"].(model.HasSBOMIncludesInputSpec)) }) if err != nil { ec.Error(ctx, err) @@ -3091,7 +3109,7 @@ func (ec *executionContext) _Mutation_ingestHasSBOMs(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().IngestHasSBOMs(rctx, fc.Args["subjects"].(model.PackageOrArtifactInputs), fc.Args["hasSBOMs"].([]*model.HasSBOMInputSpec)) + return ec.resolvers.Mutation().IngestHasSBOMs(rctx, fc.Args["subjects"].(model.PackageOrArtifactInputs), fc.Args["hasSBOMs"].([]*model.HasSBOMInputSpec), fc.Args["includes"].([]*model.HasSBOMIncludesInputSpec)) }) if err != nil { ec.Error(ctx, err) @@ -3928,9 +3946,9 @@ func (ec *executionContext) _Mutation_ingestPackage(ctx context.Context, field g } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*model.PackageIDs) fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) + return ec.marshalNPackageIDs2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageIDs(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Mutation_ingestPackage(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -3940,7 +3958,17 @@ func (ec *executionContext) fieldContext_Mutation_ingestPackage(ctx context.Cont IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") + switch field.Name { + case "packageTypeID": + return ec.fieldContext_PackageIDs_packageTypeID(ctx, field) + case "packageNamespaceID": + return ec.fieldContext_PackageIDs_packageNamespaceID(ctx, field) + case "packageNameID": + return ec.fieldContext_PackageIDs_packageNameID(ctx, field) + case "packageVersionID": + return ec.fieldContext_PackageIDs_packageVersionID(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PackageIDs", field.Name) }, } defer func() { @@ -3983,9 +4011,9 @@ func (ec *executionContext) _Mutation_ingestPackages(ctx context.Context, field } return graphql.Null } - res := resTmp.([]string) + res := resTmp.([]*model.PackageIDs) fc.Result = res - return ec.marshalNID2ᚕstringᚄ(ctx, field.Selections, res) + return ec.marshalNPackageIDs2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageIDsᚄ(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Mutation_ingestPackages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -3995,7 +4023,17 @@ func (ec *executionContext) fieldContext_Mutation_ingestPackages(ctx context.Con IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") + switch field.Name { + case "packageTypeID": + return ec.fieldContext_PackageIDs_packageTypeID(ctx, field) + case "packageNamespaceID": + return ec.fieldContext_PackageIDs_packageNamespaceID(ctx, field) + case "packageNameID": + return ec.fieldContext_PackageIDs_packageNameID(ctx, field) + case "packageVersionID": + return ec.fieldContext_PackageIDs_packageVersionID(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PackageIDs", field.Name) }, } defer func() { @@ -4148,9 +4186,9 @@ func (ec *executionContext) _Mutation_ingestSource(ctx context.Context, field gr } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*model.SourceIDs) fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) + return ec.marshalNSourceIDs2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐSourceIDs(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Mutation_ingestSource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -4160,7 +4198,15 @@ func (ec *executionContext) fieldContext_Mutation_ingestSource(ctx context.Conte IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") + switch field.Name { + case "sourceTypeID": + return ec.fieldContext_SourceIDs_sourceTypeID(ctx, field) + case "sourceNamespaceID": + return ec.fieldContext_SourceIDs_sourceNamespaceID(ctx, field) + case "sourceNameID": + return ec.fieldContext_SourceIDs_sourceNameID(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SourceIDs", field.Name) }, } defer func() { @@ -4203,9 +4249,9 @@ func (ec *executionContext) _Mutation_ingestSources(ctx context.Context, field g } return graphql.Null } - res := resTmp.([]string) + res := resTmp.([]*model.SourceIDs) fc.Result = res - return ec.marshalNID2ᚕstringᚄ(ctx, field.Selections, res) + return ec.marshalNSourceIDs2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐSourceIDsᚄ(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Mutation_ingestSources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -4215,7 +4261,15 @@ func (ec *executionContext) fieldContext_Mutation_ingestSources(ctx context.Cont IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") + switch field.Name { + case "sourceTypeID": + return ec.fieldContext_SourceIDs_sourceTypeID(ctx, field) + case "sourceNamespaceID": + return ec.fieldContext_SourceIDs_sourceNamespaceID(ctx, field) + case "sourceNameID": + return ec.fieldContext_SourceIDs_sourceNameID(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SourceIDs", field.Name) }, } defer func() { @@ -5238,6 +5292,12 @@ func (ec *executionContext) fieldContext_Query_HasSBOM(ctx context.Context, fiel return ec.fieldContext_HasSBOM_collector(ctx, field) case "knownSince": return ec.fieldContext_HasSBOM_knownSince(ctx, field) + case "includedSoftware": + return ec.fieldContext_HasSBOM_includedSoftware(ctx, field) + case "includedDependencies": + return ec.fieldContext_HasSBOM_includedDependencies(ctx, field) + case "includedOccurrences": + return ec.fieldContext_HasSBOM_includedOccurrences(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type HasSBOM", field.Name) }, diff --git a/pkg/assembler/graphql/generated/certifyVEXStatement.generated.go b/pkg/assembler/graphql/generated/certifyVEXStatement.generated.go index 766f2e3e77..696165ce91 100644 --- a/pkg/assembler/graphql/generated/certifyVEXStatement.generated.go +++ b/pkg/assembler/graphql/generated/certifyVEXStatement.generated.go @@ -977,6 +977,50 @@ func (ec *executionContext) marshalNPackageOrArtifact2githubᚗcomᚋguacsecᚋg return ec._PackageOrArtifact(ctx, sel, v) } +func (ec *executionContext) marshalNPackageOrArtifact2ᚕgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageOrArtifactᚄ(ctx context.Context, sel ast.SelectionSet, v []model.PackageOrArtifact) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNPackageOrArtifact2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageOrArtifact(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + func (ec *executionContext) unmarshalNPackageOrArtifactInput2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageOrArtifactInput(ctx context.Context, v interface{}) (model.PackageOrArtifactInput, error) { res, err := ec.unmarshalInputPackageOrArtifactInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -987,6 +1031,23 @@ func (ec *executionContext) unmarshalNPackageOrArtifactInputs2githubᚗcomᚋgua return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) unmarshalNPackageOrArtifactSpec2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageOrArtifactSpec(ctx context.Context, v interface{}) ([]*model.PackageOrArtifactSpec, error) { + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]*model.PackageOrArtifactSpec, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalOPackageOrArtifactSpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageOrArtifactSpec(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + func (ec *executionContext) unmarshalNVexJustification2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐVexJustification(ctx context.Context, v interface{}) (model.VexJustification, error) { var res model.VexJustification err := res.UnmarshalGQL(v) diff --git a/pkg/assembler/graphql/generated/hasSBOM.generated.go b/pkg/assembler/graphql/generated/hasSBOM.generated.go index 529f4d6fbf..160d15e11a 100644 --- a/pkg/assembler/graphql/generated/hasSBOM.generated.go +++ b/pkg/assembler/graphql/generated/hasSBOM.generated.go @@ -5,6 +5,7 @@ package generated import ( "context" "errors" + "fmt" "strconv" "sync" "sync/atomic" @@ -425,10 +426,221 @@ func (ec *executionContext) fieldContext_HasSBOM_knownSince(ctx context.Context, return fc, nil } +func (ec *executionContext) _HasSBOM_includedSoftware(ctx context.Context, field graphql.CollectedField, obj *model.HasSbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_HasSBOM_includedSoftware(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IncludedSoftware, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]model.PackageOrArtifact) + fc.Result = res + return ec.marshalNPackageOrArtifact2ᚕgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageOrArtifactᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_HasSBOM_includedSoftware(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "HasSBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type PackageOrArtifact does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _HasSBOM_includedDependencies(ctx context.Context, field graphql.CollectedField, obj *model.HasSbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_HasSBOM_includedDependencies(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IncludedDependencies, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.IsDependency) + fc.Result = res + return ec.marshalNIsDependency2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐIsDependencyᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_HasSBOM_includedDependencies(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "HasSBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_IsDependency_id(ctx, field) + case "package": + return ec.fieldContext_IsDependency_package(ctx, field) + case "dependencyPackage": + return ec.fieldContext_IsDependency_dependencyPackage(ctx, field) + case "versionRange": + return ec.fieldContext_IsDependency_versionRange(ctx, field) + case "dependencyType": + return ec.fieldContext_IsDependency_dependencyType(ctx, field) + case "justification": + return ec.fieldContext_IsDependency_justification(ctx, field) + case "origin": + return ec.fieldContext_IsDependency_origin(ctx, field) + case "collector": + return ec.fieldContext_IsDependency_collector(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type IsDependency", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _HasSBOM_includedOccurrences(ctx context.Context, field graphql.CollectedField, obj *model.HasSbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_HasSBOM_includedOccurrences(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IncludedOccurrences, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.IsOccurrence) + fc.Result = res + return ec.marshalNIsOccurrence2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐIsOccurrenceᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_HasSBOM_includedOccurrences(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "HasSBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_IsOccurrence_id(ctx, field) + case "subject": + return ec.fieldContext_IsOccurrence_subject(ctx, field) + case "artifact": + return ec.fieldContext_IsOccurrence_artifact(ctx, field) + case "justification": + return ec.fieldContext_IsOccurrence_justification(ctx, field) + case "origin": + return ec.fieldContext_IsOccurrence_origin(ctx, field) + case "collector": + return ec.fieldContext_IsOccurrence_collector(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type IsOccurrence", field.Name) + }, + } + return fc, nil +} + // endregion **************************** field.gotpl ***************************** // region **************************** input.gotpl ***************************** +func (ec *executionContext) unmarshalInputHasSBOMIncludesInputSpec(ctx context.Context, obj interface{}) (model.HasSBOMIncludesInputSpec, error) { + var it model.HasSBOMIncludesInputSpec + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"software", "dependencies", "occurrences"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "software": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("software")) + data, err := ec.unmarshalNID2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + it.Software = data + case "dependencies": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("dependencies")) + data, err := ec.unmarshalNID2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + it.Dependencies = data + case "occurrences": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("occurrences")) + data, err := ec.unmarshalNID2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + it.Occurrences = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputHasSBOMInputSpec(ctx context.Context, obj interface{}) (model.HasSBOMInputSpec, error) { var it model.HasSBOMInputSpec asMap := map[string]interface{}{} @@ -519,7 +731,7 @@ func (ec *executionContext) unmarshalInputHasSBOMSpec(ctx context.Context, obj i asMap[k] = v } - fieldsInOrder := [...]string{"id", "subject", "uri", "algorithm", "digest", "downloadLocation", "origin", "collector", "knownSince"} + fieldsInOrder := [...]string{"id", "subject", "uri", "algorithm", "digest", "downloadLocation", "origin", "collector", "knownSince", "includedSoftware", "includedDependencies", "includedOccurrences"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -607,6 +819,33 @@ func (ec *executionContext) unmarshalInputHasSBOMSpec(ctx context.Context, obj i return it, err } it.KnownSince = data + case "includedSoftware": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includedSoftware")) + data, err := ec.unmarshalNPackageOrArtifactSpec2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageOrArtifactSpec(ctx, v) + if err != nil { + return it, err + } + it.IncludedSoftware = data + case "includedDependencies": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includedDependencies")) + data, err := ec.unmarshalNIsDependencySpec2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐIsDependencySpec(ctx, v) + if err != nil { + return it, err + } + it.IncludedDependencies = data + case "includedOccurrences": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includedOccurrences")) + data, err := ec.unmarshalNIsOccurrenceSpec2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐIsOccurrenceSpec(ctx, v) + if err != nil { + return it, err + } + it.IncludedOccurrences = data } } @@ -677,6 +916,21 @@ func (ec *executionContext) _HasSBOM(ctx context.Context, sel ast.SelectionSet, if out.Values[i] == graphql.Null { out.Invalids++ } + case "includedSoftware": + out.Values[i] = ec._HasSBOM_includedSoftware(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "includedDependencies": + out.Values[i] = ec._HasSBOM_includedDependencies(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "includedOccurrences": + out.Values[i] = ec._HasSBOM_includedOccurrences(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -758,6 +1012,33 @@ func (ec *executionContext) marshalNHasSBOM2ᚖgithubᚗcomᚋguacsecᚋguacᚋp return ec._HasSBOM(ctx, sel, v) } +func (ec *executionContext) unmarshalNHasSBOMIncludesInputSpec2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐHasSBOMIncludesInputSpec(ctx context.Context, v interface{}) (model.HasSBOMIncludesInputSpec, error) { + res, err := ec.unmarshalInputHasSBOMIncludesInputSpec(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalNHasSBOMIncludesInputSpec2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐHasSBOMIncludesInputSpecᚄ(ctx context.Context, v interface{}) ([]*model.HasSBOMIncludesInputSpec, error) { + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]*model.HasSBOMIncludesInputSpec, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNHasSBOMIncludesInputSpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐHasSBOMIncludesInputSpec(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) unmarshalNHasSBOMIncludesInputSpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐHasSBOMIncludesInputSpec(ctx context.Context, v interface{}) (*model.HasSBOMIncludesInputSpec, error) { + res, err := ec.unmarshalInputHasSBOMIncludesInputSpec(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalNHasSBOMInputSpec2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐHasSBOMInputSpec(ctx context.Context, v interface{}) (model.HasSBOMInputSpec, error) { res, err := ec.unmarshalInputHasSBOMInputSpec(ctx, v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/pkg/assembler/graphql/generated/isDependency.generated.go b/pkg/assembler/graphql/generated/isDependency.generated.go index 78679f4f88..e8b3af2e18 100644 --- a/pkg/assembler/graphql/generated/isDependency.generated.go +++ b/pkg/assembler/graphql/generated/isDependency.generated.go @@ -740,6 +740,23 @@ func (ec *executionContext) unmarshalNIsDependencySpec2githubᚗcomᚋguacsecᚋ return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) unmarshalNIsDependencySpec2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐIsDependencySpec(ctx context.Context, v interface{}) ([]*model.IsDependencySpec, error) { + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]*model.IsDependencySpec, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalOIsDependencySpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐIsDependencySpec(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + func (ec *executionContext) unmarshalODependencyType2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐDependencyType(ctx context.Context, v interface{}) (*model.DependencyType, error) { if v == nil { return nil, nil @@ -756,4 +773,12 @@ func (ec *executionContext) marshalODependencyType2ᚖgithubᚗcomᚋguacsecᚋg return v } +func (ec *executionContext) unmarshalOIsDependencySpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐIsDependencySpec(ctx context.Context, v interface{}) (*model.IsDependencySpec, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalInputIsDependencySpec(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) +} + // endregion ***************************** type.gotpl ***************************** diff --git a/pkg/assembler/graphql/generated/isOccurrence.generated.go b/pkg/assembler/graphql/generated/isOccurrence.generated.go index c49ba69335..f9b4f6d21d 100644 --- a/pkg/assembler/graphql/generated/isOccurrence.generated.go +++ b/pkg/assembler/graphql/generated/isOccurrence.generated.go @@ -725,6 +725,23 @@ func (ec *executionContext) unmarshalNIsOccurrenceSpec2githubᚗcomᚋguacsecᚋ return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) unmarshalNIsOccurrenceSpec2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐIsOccurrenceSpec(ctx context.Context, v interface{}) ([]*model.IsOccurrenceSpec, error) { + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]*model.IsOccurrenceSpec, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalOIsOccurrenceSpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐIsOccurrenceSpec(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + func (ec *executionContext) marshalNPackageOrSource2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageOrSource(ctx context.Context, sel ast.SelectionSet, v model.PackageOrSource) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -745,6 +762,14 @@ func (ec *executionContext) unmarshalNPackageOrSourceInputs2githubᚗcomᚋguacs return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) unmarshalOIsOccurrenceSpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐIsOccurrenceSpec(ctx context.Context, v interface{}) (*model.IsOccurrenceSpec, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalInputIsOccurrenceSpec(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalOPackageOrSourceSpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageOrSourceSpec(ctx context.Context, v interface{}) (*model.PackageOrSourceSpec, error) { if v == nil { return nil, nil diff --git a/pkg/assembler/graphql/generated/package.generated.go b/pkg/assembler/graphql/generated/package.generated.go index f5f97034ce..ebe921c80b 100644 --- a/pkg/assembler/graphql/generated/package.generated.go +++ b/pkg/assembler/graphql/generated/package.generated.go @@ -169,6 +169,182 @@ func (ec *executionContext) fieldContext_Package_namespaces(ctx context.Context, return fc, nil } +func (ec *executionContext) _PackageIDs_packageTypeID(ctx context.Context, field graphql.CollectedField, obj *model.PackageIDs) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PackageIDs_packageTypeID(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PackageTypeID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PackageIDs_packageTypeID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PackageIDs", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PackageIDs_packageNamespaceID(ctx context.Context, field graphql.CollectedField, obj *model.PackageIDs) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PackageIDs_packageNamespaceID(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PackageNamespaceID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PackageIDs_packageNamespaceID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PackageIDs", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PackageIDs_packageNameID(ctx context.Context, field graphql.CollectedField, obj *model.PackageIDs) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PackageIDs_packageNameID(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PackageNameID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PackageIDs_packageNameID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PackageIDs", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PackageIDs_packageVersionID(ctx context.Context, field graphql.CollectedField, obj *model.PackageIDs) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PackageIDs_packageVersionID(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PackageVersionID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PackageIDs_packageVersionID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PackageIDs", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _PackageName_id(ctx context.Context, field graphql.CollectedField, obj *model.PackageName) (ret graphql.Marshaler) { fc, err := ec.fieldContext_PackageName_id(ctx, field) if err != nil { @@ -1044,6 +1220,60 @@ func (ec *executionContext) _Package(ctx context.Context, sel ast.SelectionSet, return out } +var packageIDsImplementors = []string{"PackageIDs"} + +func (ec *executionContext) _PackageIDs(ctx context.Context, sel ast.SelectionSet, obj *model.PackageIDs) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, packageIDsImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PackageIDs") + case "packageTypeID": + out.Values[i] = ec._PackageIDs_packageTypeID(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "packageNamespaceID": + out.Values[i] = ec._PackageIDs_packageNamespaceID(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "packageNameID": + out.Values[i] = ec._PackageIDs_packageNameID(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "packageVersionID": + out.Values[i] = ec._PackageIDs_packageVersionID(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var packageNameImplementors = []string{"PackageName"} func (ec *executionContext) _PackageName(ctx context.Context, sel ast.SelectionSet, obj *model.PackageName) graphql.Marshaler { @@ -1298,6 +1528,64 @@ func (ec *executionContext) marshalNPackage2ᚖgithubᚗcomᚋguacsecᚋguacᚋp return ec._Package(ctx, sel, v) } +func (ec *executionContext) marshalNPackageIDs2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageIDs(ctx context.Context, sel ast.SelectionSet, v model.PackageIDs) graphql.Marshaler { + return ec._PackageIDs(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPackageIDs2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageIDsᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.PackageIDs) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNPackageIDs2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageIDs(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNPackageIDs2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageIDs(ctx context.Context, sel ast.SelectionSet, v *model.PackageIDs) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PackageIDs(ctx, sel, v) +} + func (ec *executionContext) marshalNPackageName2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageNameᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.PackageName) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup diff --git a/pkg/assembler/graphql/generated/root_.generated.go b/pkg/assembler/graphql/generated/root_.generated.go index 38f85c604d..9126ecdd92 100644 --- a/pkg/assembler/graphql/generated/root_.generated.go +++ b/pkg/assembler/graphql/generated/root_.generated.go @@ -122,15 +122,18 @@ type ComplexityRoot struct { } HasSBOM struct { - Algorithm func(childComplexity int) int - Collector func(childComplexity int) int - Digest func(childComplexity int) int - DownloadLocation func(childComplexity int) int - ID func(childComplexity int) int - KnownSince func(childComplexity int) int - Origin func(childComplexity int) int - Subject func(childComplexity int) int - URI func(childComplexity int) int + Algorithm func(childComplexity int) int + Collector func(childComplexity int) int + Digest func(childComplexity int) int + DownloadLocation func(childComplexity int) int + ID func(childComplexity int) int + IncludedDependencies func(childComplexity int) int + IncludedOccurrences func(childComplexity int) int + IncludedSoftware func(childComplexity int) int + KnownSince func(childComplexity int) int + Origin func(childComplexity int) int + Subject func(childComplexity int) int + URI func(childComplexity int) int } HasSLSA struct { @@ -202,8 +205,8 @@ type ComplexityRoot struct { IngestDependencies func(childComplexity int, pkgs []*model.PkgInputSpec, depPkgs []*model.PkgInputSpec, depPkgMatchType model.MatchFlags, dependencies []*model.IsDependencyInputSpec) int IngestDependency func(childComplexity int, pkg model.PkgInputSpec, depPkg model.PkgInputSpec, depPkgMatchType model.MatchFlags, dependency model.IsDependencyInputSpec) int IngestHasMetadata func(childComplexity int, subject model.PackageSourceOrArtifactInput, pkgMatchType model.MatchFlags, hasMetadata model.HasMetadataInputSpec) int - IngestHasSBOMs func(childComplexity int, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec) int - IngestHasSbom func(childComplexity int, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec) int + IngestHasSBOMs func(childComplexity int, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec, includes []*model.HasSBOMIncludesInputSpec) int + IngestHasSbom func(childComplexity int, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec, includes model.HasSBOMIncludesInputSpec) int IngestHasSourceAt func(childComplexity int, pkg model.PkgInputSpec, pkgMatchType model.MatchFlags, source model.SourceInputSpec, hasSourceAt model.HasSourceAtInputSpec) int IngestHasSourceAts func(childComplexity int, pkgs []*model.PkgInputSpec, pkgMatchType model.MatchFlags, sources []*model.SourceInputSpec, hasSourceAts []*model.HasSourceAtInputSpec) int IngestHashEqual func(childComplexity int, artifact model.ArtifactInputSpec, otherArtifact model.ArtifactInputSpec, hashEqual model.HashEqualInputSpec) int @@ -239,6 +242,13 @@ type ComplexityRoot struct { Type func(childComplexity int) int } + PackageIDs struct { + PackageNameID func(childComplexity int) int + PackageNamespaceID func(childComplexity int) int + PackageTypeID func(childComplexity int) int + PackageVersionID func(childComplexity int) int + } + PackageName struct { ID func(childComplexity int) int Name func(childComplexity int) int @@ -361,6 +371,12 @@ type ComplexityRoot struct { Type func(childComplexity int) int } + SourceIDs struct { + SourceNameID func(childComplexity int) int + SourceNamespaceID func(childComplexity int) int + SourceTypeID func(childComplexity int) int + } + SourceName struct { Commit func(childComplexity int) int ID func(childComplexity int) int @@ -829,6 +845,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.HasSBOM.ID(childComplexity), true + case "HasSBOM.includedDependencies": + if e.complexity.HasSBOM.IncludedDependencies == nil { + break + } + + return e.complexity.HasSBOM.IncludedDependencies(childComplexity), true + + case "HasSBOM.includedOccurrences": + if e.complexity.HasSBOM.IncludedOccurrences == nil { + break + } + + return e.complexity.HasSBOM.IncludedOccurrences(childComplexity), true + + case "HasSBOM.includedSoftware": + if e.complexity.HasSBOM.IncludedSoftware == nil { + break + } + + return e.complexity.HasSBOM.IncludedSoftware(childComplexity), true + case "HasSBOM.knownSince": if e.complexity.HasSBOM.KnownSince == nil { break @@ -1302,7 +1339,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Mutation.IngestHasSBOMs(childComplexity, args["subjects"].(model.PackageOrArtifactInputs), args["hasSBOMs"].([]*model.HasSBOMInputSpec)), true + return e.complexity.Mutation.IngestHasSBOMs(childComplexity, args["subjects"].(model.PackageOrArtifactInputs), args["hasSBOMs"].([]*model.HasSBOMInputSpec), args["includes"].([]*model.HasSBOMIncludesInputSpec)), true case "Mutation.ingestHasSBOM": if e.complexity.Mutation.IngestHasSbom == nil { @@ -1314,7 +1351,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Mutation.IngestHasSbom(childComplexity, args["subject"].(model.PackageOrArtifactInput), args["hasSBOM"].(model.HasSBOMInputSpec)), true + return e.complexity.Mutation.IngestHasSbom(childComplexity, args["subject"].(model.PackageOrArtifactInput), args["hasSBOM"].(model.HasSBOMInputSpec), args["includes"].(model.HasSBOMIncludesInputSpec)), true case "Mutation.ingestHasSourceAt": if e.complexity.Mutation.IngestHasSourceAt == nil { @@ -1661,6 +1698,34 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Package.Type(childComplexity), true + case "PackageIDs.packageNameID": + if e.complexity.PackageIDs.PackageNameID == nil { + break + } + + return e.complexity.PackageIDs.PackageNameID(childComplexity), true + + case "PackageIDs.packageNamespaceID": + if e.complexity.PackageIDs.PackageNamespaceID == nil { + break + } + + return e.complexity.PackageIDs.PackageNamespaceID(childComplexity), true + + case "PackageIDs.packageTypeID": + if e.complexity.PackageIDs.PackageTypeID == nil { + break + } + + return e.complexity.PackageIDs.PackageTypeID(childComplexity), true + + case "PackageIDs.packageVersionID": + if e.complexity.PackageIDs.PackageVersionID == nil { + break + } + + return e.complexity.PackageIDs.PackageVersionID(childComplexity), true + case "PackageName.id": if e.complexity.PackageName.ID == nil { break @@ -2382,6 +2447,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Source.Type(childComplexity), true + case "SourceIDs.sourceNameID": + if e.complexity.SourceIDs.SourceNameID == nil { + break + } + + return e.complexity.SourceIDs.SourceNameID(childComplexity), true + + case "SourceIDs.sourceNamespaceID": + if e.complexity.SourceIDs.SourceNamespaceID == nil { + break + } + + return e.complexity.SourceIDs.SourceNamespaceID(childComplexity), true + + case "SourceIDs.sourceTypeID": + if e.complexity.SourceIDs.SourceTypeID == nil { + break + } + + return e.complexity.SourceIDs.SourceTypeID(childComplexity), true + case "SourceName.commit": if e.complexity.SourceName.Commit == nil { break @@ -2573,6 +2659,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputCertifyVulnSpec, ec.unmarshalInputHasMetadataInputSpec, ec.unmarshalInputHasMetadataSpec, + ec.unmarshalInputHasSBOMIncludesInputSpec, ec.unmarshalInputHasSBOMInputSpec, ec.unmarshalInputHasSBOMSpec, ec.unmarshalInputHasSLSASpec, @@ -3730,6 +3817,12 @@ type HasSBOM { collector: String! "Timestamp for SBOM creation" knownSince: Time! + "Included packages and artifacts" + includedSoftware: [PackageOrArtifact!]! + "Included dependencies" + includedDependencies: [IsDependency!]! + "Included occurrences" + includedOccurrences: [IsOccurrence!]! } """ @@ -3750,9 +3843,18 @@ input HasSBOMSpec { origin: String collector: String knownSince: Time + includedSoftware: [PackageOrArtifactSpec]! + includedDependencies: [IsDependencySpec]! + includedOccurrences: [IsOccurrenceSpec]! } -"HasSBOMInputSpec is the same as HasSBOM but for mutation input." +input HasSBOMIncludesInputSpec { + software: [ID!]! + dependencies: [ID!]! + occurrences: [ID!]! +} + +"HasSBOMInputSpec is similar to HasSBOM but for mutation input." input HasSBOMInputSpec { uri: String! algorithm: String! @@ -3773,11 +3875,13 @@ extend type Mutation { ingestHasSBOM( subject: PackageOrArtifactInput! hasSBOM: HasSBOMInputSpec! + includes: HasSBOMIncludesInputSpec! ): ID! "Bulk ingest that package or artifact has an SBOM. The returned array of IDs can be a an array of empty string." ingestHasSBOMs( subjects: PackageOrArtifactInputs! hasSBOMs: [HasSBOMInputSpec!]! + includes: [HasSBOMIncludesInputSpec!]! ): [ID!]! } `, BuiltIn: false}, @@ -4596,6 +4700,16 @@ type PackageQualifier { value: String! } +""" +The IDs of the ingested package +""" +type PackageIDs { + packageTypeID: ID! + packageNamespaceID: ID! + packageNameID: ID! + packageVersionID: ID! +} + """ PkgSpec allows filtering the list of sources to return in a query. @@ -4662,10 +4776,10 @@ extend type Query { } extend type Mutation { - "Ingests a new package and returns the corresponding package trie path. The returned ID can be empty string." - ingestPackage(pkg: PkgInputSpec!): ID! - "Bulk ingests packages and returns the list of corresponding package trie path. The returned array of IDs can be a an array of empty string." - ingestPackages(pkgs: [PkgInputSpec!]!): [ID!]! + "Ingests a new package and returns a corresponding package hierarchy containing only the IDs. The returned ID can be empty string." + ingestPackage(pkg: PkgInputSpec!): PackageIDs! + "Bulk ingests packages and returns the list of corresponding package hierarchies containing only the IDs. The returned array of IDs can be empty strings." + ingestPackages(pkgs: [PkgInputSpec!]!): [PackageIDs!]! } `, BuiltIn: false}, {Name: "../schema/path.graphql", Input: `# @@ -4789,6 +4903,9 @@ enum Edge { HAS_METADATA_SOURCE HAS_SBOM_ARTIFACT HAS_SBOM_PACKAGE + HAS_SBOM_INCLUDED_SOFTWARE + HAS_SBOM_INCLUDED_DEPENDENCIES + HAS_SBOM_INCLUDED_OCCURRENCES HAS_SLSA_BUILT_BY HAS_SLSA_MATERIALS HAS_SLSA_SUBJECT @@ -5026,6 +5143,15 @@ type SourceName { commit: String } +""" +The IDs of the ingested pacsourcekage +""" +type SourceIDs { + sourceTypeID: ID! + sourceNamespaceID: ID! + sourceNameID: ID! +} + """ SourceSpec allows filtering the list of sources to return in a query. @@ -5070,9 +5196,9 @@ extend type Query { extend type Mutation { "Ingests a new source and returns the corresponding source trie path. The returned ID can be empty string." - ingestSource(source: SourceInputSpec!): ID! + ingestSource(source: SourceInputSpec!): SourceIDs! "Bulk ingests sources and returns the list of corresponding source trie path. The returned array of IDs can be a an array of empty string." - ingestSources(sources: [SourceInputSpec!]!): [ID!]! + ingestSources(sources: [SourceInputSpec!]!): [SourceIDs!]! } `, BuiltIn: false}, {Name: "../schema/vulnEqual.graphql", Input: `# diff --git a/pkg/assembler/graphql/generated/source.generated.go b/pkg/assembler/graphql/generated/source.generated.go index 23c53ef47f..95094f6245 100644 --- a/pkg/assembler/graphql/generated/source.generated.go +++ b/pkg/assembler/graphql/generated/source.generated.go @@ -169,6 +169,138 @@ func (ec *executionContext) fieldContext_Source_namespaces(ctx context.Context, return fc, nil } +func (ec *executionContext) _SourceIDs_sourceTypeID(ctx context.Context, field graphql.CollectedField, obj *model.SourceIDs) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceIDs_sourceTypeID(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SourceTypeID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SourceIDs_sourceTypeID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SourceIDs", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _SourceIDs_sourceNamespaceID(ctx context.Context, field graphql.CollectedField, obj *model.SourceIDs) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceIDs_sourceNamespaceID(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SourceNamespaceID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SourceIDs_sourceNamespaceID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SourceIDs", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _SourceIDs_sourceNameID(ctx context.Context, field graphql.CollectedField, obj *model.SourceIDs) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SourceIDs_sourceNameID(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SourceNameID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SourceIDs_sourceNameID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SourceIDs", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _SourceName_id(ctx context.Context, field graphql.CollectedField, obj *model.SourceName) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SourceName_id(ctx, field) if err != nil { @@ -688,6 +820,55 @@ func (ec *executionContext) _Source(ctx context.Context, sel ast.SelectionSet, o return out } +var sourceIDsImplementors = []string{"SourceIDs"} + +func (ec *executionContext) _SourceIDs(ctx context.Context, sel ast.SelectionSet, obj *model.SourceIDs) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, sourceIDsImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SourceIDs") + case "sourceTypeID": + out.Values[i] = ec._SourceIDs_sourceTypeID(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "sourceNamespaceID": + out.Values[i] = ec._SourceIDs_sourceNamespaceID(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "sourceNameID": + out.Values[i] = ec._SourceIDs_sourceNameID(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var sourceNameImplementors = []string{"SourceName"} func (ec *executionContext) _SourceName(ctx context.Context, sel ast.SelectionSet, obj *model.SourceName) graphql.Marshaler { @@ -843,6 +1024,64 @@ func (ec *executionContext) marshalNSource2ᚖgithubᚗcomᚋguacsecᚋguacᚋpk return ec._Source(ctx, sel, v) } +func (ec *executionContext) marshalNSourceIDs2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐSourceIDs(ctx context.Context, sel ast.SelectionSet, v model.SourceIDs) graphql.Marshaler { + return ec._SourceIDs(ctx, sel, &v) +} + +func (ec *executionContext) marshalNSourceIDs2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐSourceIDsᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.SourceIDs) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNSourceIDs2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐSourceIDs(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNSourceIDs2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐSourceIDs(ctx context.Context, sel ast.SelectionSet, v *model.SourceIDs) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._SourceIDs(ctx, sel, v) +} + func (ec *executionContext) unmarshalNSourceInputSpec2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐSourceInputSpec(ctx context.Context, v interface{}) (model.SourceInputSpec, error) { res, err := ec.unmarshalInputSourceInputSpec(ctx, v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/pkg/assembler/graphql/helpers/package.go b/pkg/assembler/graphql/helpers/package.go new file mode 100644 index 0000000000..bcc62b24f5 --- /dev/null +++ b/pkg/assembler/graphql/helpers/package.go @@ -0,0 +1,60 @@ +// +// Copyright 2023 The GUAC 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 helpers + +import ( + "github.com/guacsec/guac/pkg/assembler/graphql/model" +) + +// Return a package structure containing only IDs +func GetPackageAsIds(packages []*model.Package) []*model.PackageIDs { + results := []*model.PackageIDs{} + for _, pkg := range packages { + resultPackage := model.PackageIDs{PackageTypeID: pkg.ID} + for _, namespace := range pkg.Namespaces { + resultNamespace := resultPackage + resultNamespace.PackageNamespaceID = namespace.ID + for _, name := range namespace.Names { + resultName := resultNamespace + resultName.PackageNameID = name.ID + for _, version := range name.Versions { + resultVersion := resultName + resultVersion.PackageVersionID = version.ID + results = append(results, &resultVersion) + } + } + } + } + return results +} + +// Return a source structure containing only IDs +func GetSourceAsIds(sources []*model.Source) []*model.SourceIDs { + results := []*model.SourceIDs{} + for _, src := range sources { + resultSource := model.SourceIDs{SourceTypeID: src.ID} + for _, namespace := range src.Namespaces { + resultNamespace := resultSource + resultNamespace.SourceNamespaceID = namespace.ID + for _, name := range namespace.Names { + resultName := resultNamespace + resultName.SourceNameID = name.ID + results = append(results, &resultName) + } + } + } + return results +} diff --git a/pkg/assembler/graphql/helpers/package_test.go b/pkg/assembler/graphql/helpers/package_test.go new file mode 100644 index 0000000000..a353ee10b3 --- /dev/null +++ b/pkg/assembler/graphql/helpers/package_test.go @@ -0,0 +1,253 @@ +// +// Copyright 2023 The GUAC 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 helpers + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/guacsec/guac/pkg/assembler/graphql/model" +) + +func TestIngestCertifyGood(t *testing.T) { + tests := []struct { + Name string + Packages []*model.Package + Want []*model.PackageIDs + }{{ + Name: "Single package", + Packages: []*model.Package{{ + ID: "1", + Namespaces: []*model.PackageNamespace{{ + ID: "2", + Names: []*model.PackageName{{ + ID: "3", + Versions: []*model.PackageVersion{{ + ID: "4", + }}, + }}, + }}, + }}, + Want: []*model.PackageIDs{{ + PackageTypeID: "1", + PackageNamespaceID: "2", + PackageNameID: "3", + PackageVersionID: "4", + }}, + }, { + Name: "Multiple packages", + Packages: []*model.Package{{ + ID: "1", + Namespaces: []*model.PackageNamespace{{ + ID: "2", + Names: []*model.PackageName{{ + ID: "3", + Versions: []*model.PackageVersion{{ + ID: "4", + }}, + }}, + }}, + }, { + ID: "5", + Namespaces: []*model.PackageNamespace{{ + ID: "6", + Names: []*model.PackageName{{ + ID: "7", + Versions: []*model.PackageVersion{{ + ID: "8", + }}, + }}, + }}, + }}, + Want: []*model.PackageIDs{{ + PackageTypeID: "1", + PackageNamespaceID: "2", + PackageNameID: "3", + PackageVersionID: "4", + }, { + PackageTypeID: "5", + PackageNamespaceID: "6", + PackageNameID: "7", + PackageVersionID: "8", + }}, + }, { + Name: "Package Tree", + Packages: []*model.Package{{ + ID: "1", + Namespaces: []*model.PackageNamespace{{ + ID: "2", + Names: []*model.PackageName{{ + ID: "3", + Versions: []*model.PackageVersion{{ + ID: "4", + }, { + ID: "5", + }}, + }, { + ID: "6", + Versions: []*model.PackageVersion{{ + ID: "7", + }, { + ID: "8", + }}, + }}, + }, { + ID: "9", + Names: []*model.PackageName{{ + ID: "10", + Versions: []*model.PackageVersion{{ + ID: "11", + }, { + ID: "12", + }}, + }, { + ID: "13", + Versions: []*model.PackageVersion{{ + ID: "14", + }, { + ID: "15", + }}, + }}, + }}, + }, { + ID: "16", + Namespaces: []*model.PackageNamespace{{ + ID: "17", + Names: []*model.PackageName{{ + ID: "18", + Versions: []*model.PackageVersion{{ + ID: "19", + }, { + ID: "20", + }}, + }, { + ID: "21", + Versions: []*model.PackageVersion{{ + ID: "22", + }, { + ID: "23", + }}, + }}, + }, { + ID: "24", + Names: []*model.PackageName{{ + ID: "25", + Versions: []*model.PackageVersion{{ + ID: "26", + }, { + ID: "27", + }}, + }, { + ID: "28", + Versions: []*model.PackageVersion{{ + ID: "29", + }, { + ID: "30", + }}, + }}, + }}, + }}, + Want: []*model.PackageIDs{{ + PackageTypeID: "1", + PackageNamespaceID: "2", + PackageNameID: "3", + PackageVersionID: "4", + }, { + PackageTypeID: "1", + PackageNamespaceID: "2", + PackageNameID: "3", + PackageVersionID: "5", + }, { + PackageTypeID: "1", + PackageNamespaceID: "2", + PackageNameID: "6", + PackageVersionID: "7", + }, { + PackageTypeID: "1", + PackageNamespaceID: "2", + PackageNameID: "6", + PackageVersionID: "8", + }, { + PackageTypeID: "1", + PackageNamespaceID: "9", + PackageNameID: "10", + PackageVersionID: "11", + }, { + PackageTypeID: "1", + PackageNamespaceID: "9", + PackageNameID: "10", + PackageVersionID: "12", + }, { + PackageTypeID: "1", + PackageNamespaceID: "9", + PackageNameID: "13", + PackageVersionID: "14", + }, { + PackageTypeID: "1", + PackageNamespaceID: "9", + PackageNameID: "13", + PackageVersionID: "15", + }, { + PackageTypeID: "16", + PackageNamespaceID: "17", + PackageNameID: "18", + PackageVersionID: "19", + }, { + PackageTypeID: "16", + PackageNamespaceID: "17", + PackageNameID: "18", + PackageVersionID: "20", + }, { + PackageTypeID: "16", + PackageNamespaceID: "17", + PackageNameID: "21", + PackageVersionID: "22", + }, { + PackageTypeID: "16", + PackageNamespaceID: "17", + PackageNameID: "21", + PackageVersionID: "23", + }, { + PackageTypeID: "16", + PackageNamespaceID: "24", + PackageNameID: "25", + PackageVersionID: "26", + }, { + PackageTypeID: "16", + PackageNamespaceID: "24", + PackageNameID: "25", + PackageVersionID: "27", + }, { + PackageTypeID: "16", + PackageNamespaceID: "24", + PackageNameID: "28", + PackageVersionID: "29", + }, { + PackageTypeID: "16", + PackageNamespaceID: "24", + PackageNameID: "28", + PackageVersionID: "30", + }}, + }} + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + result := GetPackageAsIds(test.Packages) + if diff := cmp.Diff(test.Want, result); diff != "" { + t.Errorf("Unexpected results. (-want +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/assembler/graphql/model/nodes.go b/pkg/assembler/graphql/model/nodes.go index 84aca2b89e..9db8fdf1e3 100644 --- a/pkg/assembler/graphql/model/nodes.go +++ b/pkg/assembler/graphql/model/nodes.go @@ -449,11 +449,23 @@ type HasSbom struct { Collector string `json:"collector"` // Timestamp for SBOM creation KnownSince time.Time `json:"knownSince"` + // Included packages and artifacts + IncludedSoftware []PackageOrArtifact `json:"includedSoftware"` + // Included dependencies + IncludedDependencies []*IsDependency `json:"includedDependencies"` + // Included occurrences + IncludedOccurrences []*IsOccurrence `json:"includedOccurrences"` } func (HasSbom) IsNode() {} -// HasSBOMInputSpec is the same as HasSBOM but for mutation input. +type HasSBOMIncludesInputSpec struct { + Software []string `json:"software"` + Dependencies []string `json:"dependencies"` + Occurrences []string `json:"occurrences"` +} + +// HasSBOMInputSpec is similar to HasSBOM but for mutation input. type HasSBOMInputSpec struct { URI string `json:"uri"` Algorithm string `json:"algorithm"` @@ -471,15 +483,18 @@ type HasSBOMInputSpec struct { // If KnownSince is specified, the returned value will be after or equal to the specified time. // Any nodes time that is before KnownSince is excluded. type HasSBOMSpec struct { - ID *string `json:"id,omitempty"` - Subject *PackageOrArtifactSpec `json:"subject,omitempty"` - URI *string `json:"uri,omitempty"` - Algorithm *string `json:"algorithm,omitempty"` - Digest *string `json:"digest,omitempty"` - DownloadLocation *string `json:"downloadLocation,omitempty"` - Origin *string `json:"origin,omitempty"` - Collector *string `json:"collector,omitempty"` - KnownSince *time.Time `json:"knownSince,omitempty"` + ID *string `json:"id,omitempty"` + Subject *PackageOrArtifactSpec `json:"subject,omitempty"` + URI *string `json:"uri,omitempty"` + Algorithm *string `json:"algorithm,omitempty"` + Digest *string `json:"digest,omitempty"` + DownloadLocation *string `json:"downloadLocation,omitempty"` + Origin *string `json:"origin,omitempty"` + Collector *string `json:"collector,omitempty"` + KnownSince *time.Time `json:"knownSince,omitempty"` + IncludedSoftware []*PackageOrArtifactSpec `json:"includedSoftware"` + IncludedDependencies []*IsDependencySpec `json:"includedDependencies"` + IncludedOccurrences []*IsOccurrenceSpec `json:"includedOccurrences"` } // HasSLSA records that a subject node has a SLSA attestation. @@ -749,6 +764,14 @@ func (Package) IsPackageOrSource() {} func (Package) IsNode() {} +// The IDs of the ingested package +type PackageIDs struct { + PackageTypeID string `json:"packageTypeID"` + PackageNamespaceID string `json:"packageNamespaceID"` + PackageNameID string `json:"packageNameID"` + PackageVersionID string `json:"packageVersionID"` +} + // PackageName is a name for packages. // // In the pURL representation, each PackageName matches the @@ -1255,6 +1278,13 @@ func (Source) IsPackageOrSource() {} func (Source) IsNode() {} +// The IDs of the ingested pacsourcekage +type SourceIDs struct { + SourceTypeID string `json:"sourceTypeID"` + SourceNamespaceID string `json:"sourceNamespaceID"` + SourceNameID string `json:"sourceNameID"` +} + // SourceInputSpec specifies a source for mutations. // // This is different than SourceSpec because we want to encode that all fields @@ -1651,6 +1681,9 @@ const ( EdgeHasMetadataSource Edge = "HAS_METADATA_SOURCE" EdgeHasSbomArtifact Edge = "HAS_SBOM_ARTIFACT" EdgeHasSbomPackage Edge = "HAS_SBOM_PACKAGE" + EdgeHasSbomIncludedSoftware Edge = "HAS_SBOM_INCLUDED_SOFTWARE" + EdgeHasSbomIncludedDependencies Edge = "HAS_SBOM_INCLUDED_DEPENDENCIES" + EdgeHasSbomIncludedOccurrences Edge = "HAS_SBOM_INCLUDED_OCCURRENCES" EdgeHasSlsaBuiltBy Edge = "HAS_SLSA_BUILT_BY" EdgeHasSlsaMaterials Edge = "HAS_SLSA_MATERIALS" EdgeHasSlsaSubject Edge = "HAS_SLSA_SUBJECT" @@ -1725,6 +1758,9 @@ var AllEdge = []Edge{ EdgeHasMetadataSource, EdgeHasSbomArtifact, EdgeHasSbomPackage, + EdgeHasSbomIncludedSoftware, + EdgeHasSbomIncludedDependencies, + EdgeHasSbomIncludedOccurrences, EdgeHasSlsaBuiltBy, EdgeHasSlsaMaterials, EdgeHasSlsaSubject, @@ -1744,7 +1780,7 @@ var AllEdge = []Edge{ func (e Edge) IsValid() bool { switch e { - case EdgeArtifactCertifyBad, EdgeArtifactCertifyGood, EdgeArtifactCertifyVexStatement, EdgeArtifactHashEqual, EdgeArtifactHasMetadata, EdgeArtifactHasSbom, EdgeArtifactHasSlsa, EdgeArtifactIsOccurrence, EdgeArtifactPointOfContact, EdgeBuilderHasSlsa, EdgeLicenseCertifyLegal, EdgePackageCertifyBad, EdgePackageCertifyGood, EdgePackageCertifyLegal, EdgePackageCertifyVexStatement, EdgePackageCertifyVuln, EdgePackageHasMetadata, EdgePackageHasSbom, EdgePackageHasSourceAt, EdgePackageIsDependency, EdgePackageIsOccurrence, EdgePackagePkgEqual, EdgePackagePointOfContact, EdgeSourceCertifyBad, EdgeSourceCertifyGood, EdgeSourceCertifyLegal, EdgeSourceCertifyScorecard, EdgeSourceHasMetadata, EdgeSourceHasSourceAt, EdgeSourceIsOccurrence, EdgeSourcePointOfContact, EdgeVulnerabilityCertifyVexStatement, EdgeVulnerabilityCertifyVuln, EdgeVulnerabilityVulnEqual, EdgeVulnerabilityVulnMetadata, EdgeCertifyBadArtifact, EdgeCertifyBadPackage, EdgeCertifyBadSource, EdgeCertifyGoodArtifact, EdgeCertifyGoodPackage, EdgeCertifyGoodSource, EdgeCertifyLegalLicense, EdgeCertifyLegalPackage, EdgeCertifyLegalSource, EdgeCertifyScorecardSource, EdgeCertifyVexStatementArtifact, EdgeCertifyVexStatementPackage, EdgeCertifyVexStatementVulnerability, EdgeCertifyVulnPackage, EdgeCertifyVulnVulnerability, EdgeHashEqualArtifact, EdgeHasMetadataArtifact, EdgeHasMetadataPackage, EdgeHasMetadataSource, EdgeHasSbomArtifact, EdgeHasSbomPackage, EdgeHasSlsaBuiltBy, EdgeHasSlsaMaterials, EdgeHasSlsaSubject, EdgeHasSourceAtPackage, EdgeHasSourceAtSource, EdgeIsDependencyPackage, EdgeIsOccurrenceArtifact, EdgeIsOccurrencePackage, EdgeIsOccurrenceSource, EdgePkgEqualPackage, EdgePointOfContactArtifact, EdgePointOfContactPackage, EdgePointOfContactSource, EdgeVulnEqualVulnerability, EdgeVulnMetadataVulnerability: + case EdgeArtifactCertifyBad, EdgeArtifactCertifyGood, EdgeArtifactCertifyVexStatement, EdgeArtifactHashEqual, EdgeArtifactHasMetadata, EdgeArtifactHasSbom, EdgeArtifactHasSlsa, EdgeArtifactIsOccurrence, EdgeArtifactPointOfContact, EdgeBuilderHasSlsa, EdgeLicenseCertifyLegal, EdgePackageCertifyBad, EdgePackageCertifyGood, EdgePackageCertifyLegal, EdgePackageCertifyVexStatement, EdgePackageCertifyVuln, EdgePackageHasMetadata, EdgePackageHasSbom, EdgePackageHasSourceAt, EdgePackageIsDependency, EdgePackageIsOccurrence, EdgePackagePkgEqual, EdgePackagePointOfContact, EdgeSourceCertifyBad, EdgeSourceCertifyGood, EdgeSourceCertifyLegal, EdgeSourceCertifyScorecard, EdgeSourceHasMetadata, EdgeSourceHasSourceAt, EdgeSourceIsOccurrence, EdgeSourcePointOfContact, EdgeVulnerabilityCertifyVexStatement, EdgeVulnerabilityCertifyVuln, EdgeVulnerabilityVulnEqual, EdgeVulnerabilityVulnMetadata, EdgeCertifyBadArtifact, EdgeCertifyBadPackage, EdgeCertifyBadSource, EdgeCertifyGoodArtifact, EdgeCertifyGoodPackage, EdgeCertifyGoodSource, EdgeCertifyLegalLicense, EdgeCertifyLegalPackage, EdgeCertifyLegalSource, EdgeCertifyScorecardSource, EdgeCertifyVexStatementArtifact, EdgeCertifyVexStatementPackage, EdgeCertifyVexStatementVulnerability, EdgeCertifyVulnPackage, EdgeCertifyVulnVulnerability, EdgeHashEqualArtifact, EdgeHasMetadataArtifact, EdgeHasMetadataPackage, EdgeHasMetadataSource, EdgeHasSbomArtifact, EdgeHasSbomPackage, EdgeHasSbomIncludedSoftware, EdgeHasSbomIncludedDependencies, EdgeHasSbomIncludedOccurrences, EdgeHasSlsaBuiltBy, EdgeHasSlsaMaterials, EdgeHasSlsaSubject, EdgeHasSourceAtPackage, EdgeHasSourceAtSource, EdgeIsDependencyPackage, EdgeIsOccurrenceArtifact, EdgeIsOccurrencePackage, EdgeIsOccurrenceSource, EdgePkgEqualPackage, EdgePointOfContactArtifact, EdgePointOfContactPackage, EdgePointOfContactSource, EdgeVulnEqualVulnerability, EdgeVulnMetadataVulnerability: return true } return false diff --git a/pkg/assembler/graphql/resolvers/hasSBOM.resolvers.go b/pkg/assembler/graphql/resolvers/hasSBOM.resolvers.go index 9616295655..9a7bbd6bfb 100644 --- a/pkg/assembler/graphql/resolvers/hasSBOM.resolvers.go +++ b/pkg/assembler/graphql/resolvers/hasSBOM.resolvers.go @@ -13,7 +13,7 @@ import ( ) // IngestHasSbom is the resolver for the ingestHasSBOM field. -func (r *mutationResolver) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec) (string, error) { +func (r *mutationResolver) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec, includes model.HasSBOMIncludesInputSpec) (string, error) { funcName := "IngestHasSbom" if err := helper.ValidatePackageOrArtifactInput(&subject, funcName); err != nil { return "", gqlerror.Errorf("%v :: %s", funcName, err) @@ -22,7 +22,7 @@ func (r *mutationResolver) IngestHasSbom(ctx context.Context, subject model.Pack return "", gqlerror.Errorf("hasSbom.KnownSince is a zero time") } - ingestedHasSbom, err := r.Backend.IngestHasSbom(ctx, subject, hasSbom) + ingestedHasSbom, err := r.Backend.IngestHasSbom(ctx, subject, hasSbom, includes) if err != nil { return "", err } @@ -30,7 +30,7 @@ func (r *mutationResolver) IngestHasSbom(ctx context.Context, subject model.Pack } // IngestHasSBOMs is the resolver for the ingestHasSBOMs field. -func (r *mutationResolver) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec) ([]string, error) { +func (r *mutationResolver) IngestHasSBOMs(ctx context.Context, subjects model.PackageOrArtifactInputs, hasSBOMs []*model.HasSBOMInputSpec, includes []*model.HasSBOMIncludesInputSpec) ([]string, error) { funcName := "IngestHasSBOMs" valuesDefined := 0 ingestedHasSBOMSIDS := []string{} @@ -56,7 +56,10 @@ func (r *mutationResolver) IngestHasSBOMs(ctx context.Context, subjects model.Pa } } - ingestedHasSBOMs, err := r.Backend.IngestHasSBOMs(ctx, subjects, hasSBOMs) + if len(hasSBOMs) != len(includes) { + return ingestedHasSBOMSIDS, gqlerror.Errorf("%v :: uneven hasSBOMs and includes for ingestion", funcName) + } + ingestedHasSBOMs, err := r.Backend.IngestHasSBOMs(ctx, subjects, hasSBOMs, includes) if err == nil { for _, hasSBOM := range ingestedHasSBOMs { ingestedHasSBOMSIDS = append(ingestedHasSBOMSIDS, hasSBOM.ID) diff --git a/pkg/assembler/graphql/resolvers/hasSBOM.resolvers_test.go b/pkg/assembler/graphql/resolvers/hasSBOM.resolvers_test.go index a1feed2244..ae10167ae5 100644 --- a/pkg/assembler/graphql/resolvers/hasSBOM.resolvers_test.go +++ b/pkg/assembler/graphql/resolvers/hasSBOM.resolvers_test.go @@ -31,6 +31,7 @@ func TestIngestHasSbom(t *testing.T) { type call struct { Sub model.PackageOrArtifactInput HS *model.HasSBOMInputSpec + Inc *model.HasSBOMIncludesInputSpec } tests := []struct { Name string @@ -48,6 +49,7 @@ func TestIngestHasSbom(t *testing.T) { HS: &model.HasSBOMInputSpec{ DownloadLocation: "location one", }, + Inc: &model.HasSBOMIncludesInputSpec{}, }, }, ExpIngestErr: true, @@ -63,6 +65,7 @@ func TestIngestHasSbom(t *testing.T) { URI: "test uri", KnownSince: ZeroTime, }, + Inc: &model.HasSBOMIncludesInputSpec{}, }, }, ExpIngestErr: false, @@ -81,10 +84,10 @@ func TestIngestHasSbom(t *testing.T) { } b. EXPECT(). - IngestHasSbom(ctx, o.Sub, *o.HS). + IngestHasSbom(ctx, o.Sub, *o.HS, *o.Inc). Return(&model.HasSbom{ID: "a"}, nil). Times(times) - _, err := r.Mutation().IngestHasSbom(ctx, o.Sub, *o.HS) + _, err := r.Mutation().IngestHasSbom(ctx, o.Sub, *o.HS, *o.Inc) if (err != nil) != test.ExpIngestErr { t.Fatalf("did not get expected ingest error, want: %v, got: %v", test.ExpIngestErr, err) } @@ -100,6 +103,7 @@ func TestIngestHasSBOMs(t *testing.T) { type call struct { Sub model.PackageOrArtifactInputs HS []*model.HasSBOMInputSpec + Inc []*model.HasSBOMIncludesInputSpec } tests := []struct { Name string @@ -107,7 +111,7 @@ func TestIngestHasSBOMs(t *testing.T) { ExpIngestErr bool }{ { - Name: "Ingest with two packages and one HasSbom", + Name: "Ingest with two packages, one HasSbom and one includes", Calls: []call{ { Sub: model.PackageOrArtifactInputs{ @@ -118,12 +122,13 @@ func TestIngestHasSBOMs(t *testing.T) { URI: "test uri", }, }, + Inc: []*model.HasSBOMIncludesInputSpec{{}}, }, }, ExpIngestErr: true, }, { - Name: "Ingest with two artifacts and one HasSbom", + Name: "Ingest with two artifacts, one HasSbom and one includes", Calls: []call{ { Sub: model.PackageOrArtifactInputs{ @@ -134,6 +139,24 @@ func TestIngestHasSBOMs(t *testing.T) { URI: "test uri", }, }, + Inc: []*model.HasSBOMIncludesInputSpec{{}}, + }, + }, + ExpIngestErr: true, + }, + { + Name: "Ingest with one package, one HasSbom and two includes", + Calls: []call{ + { + Sub: model.PackageOrArtifactInputs{ + Packages: []*model.PkgInputSpec{testdata.P1, testdata.P2}, + }, + HS: []*model.HasSBOMInputSpec{ + { + URI: "test uri", + }, + }, + Inc: []*model.HasSBOMIncludesInputSpec{{}, {}}, }, }, ExpIngestErr: true, @@ -152,6 +175,7 @@ func TestIngestHasSBOMs(t *testing.T) { KnownSince: ZeroTime, }, }, + Inc: []*model.HasSBOMIncludesInputSpec{{}}, }, }, ExpIngestErr: true, @@ -169,6 +193,7 @@ func TestIngestHasSBOMs(t *testing.T) { KnownSince: ZeroTime, }, }, + Inc: []*model.HasSBOMIncludesInputSpec{{}}, }, }, }, @@ -186,10 +211,10 @@ func TestIngestHasSBOMs(t *testing.T) { } b. EXPECT(). - IngestHasSBOMs(ctx, o.Sub, o.HS). + IngestHasSBOMs(ctx, o.Sub, o.HS, o.Inc). Return([]*model.HasSbom{{ID: "a"}}, nil). Times(times) - _, err := r.Mutation().IngestHasSBOMs(ctx, o.Sub, o.HS) + _, err := r.Mutation().IngestHasSBOMs(ctx, o.Sub, o.HS, o.Inc) if (err != nil) != test.ExpIngestErr { t.Fatalf("did not get expected ingest error, want: %v, got: %v", test.ExpIngestErr, err) } diff --git a/pkg/assembler/graphql/resolvers/package.resolvers.go b/pkg/assembler/graphql/resolvers/package.resolvers.go index 514719993c..5a6c0e3a04 100644 --- a/pkg/assembler/graphql/resolvers/package.resolvers.go +++ b/pkg/assembler/graphql/resolvers/package.resolvers.go @@ -6,29 +6,38 @@ package resolvers import ( "context" + "fmt" + "github.com/guacsec/guac/pkg/assembler/graphql/helpers" "github.com/guacsec/guac/pkg/assembler/graphql/model" ) // IngestPackage is the resolver for the ingestPackage field. -func (r *mutationResolver) IngestPackage(ctx context.Context, pkg model.PkgInputSpec) (string, error) { +func (r *mutationResolver) IngestPackage(ctx context.Context, pkg model.PkgInputSpec) (*model.PackageIDs, error) { + // Return the ids of the package which has been ingested + ingestedPackage, err := r.Backend.IngestPackage(ctx, pkg) if err != nil { - return "", err + return nil, err + } + results := helpers.GetPackageAsIds([]*model.Package{ingestedPackage}) + if len(results) != 1 { + return nil, fmt.Errorf("could no derive correct package ID information for ingested packages, expected to return 1 but have %d", len(results)) } - return ingestedPackage.ID, err + return results[0], nil } // IngestPackages is the resolver for the ingestPackages field. -func (r *mutationResolver) IngestPackages(ctx context.Context, pkgs []*model.PkgInputSpec) ([]string, error) { +func (r *mutationResolver) IngestPackages(ctx context.Context, pkgs []*model.PkgInputSpec) ([]*model.PackageIDs, error) { ingestedPackages, err := r.Backend.IngestPackages(ctx, pkgs) - ingestedPackagesIDS := []string{} if err == nil { - for _, Package := range ingestedPackages { - ingestedPackagesIDS = append(ingestedPackagesIDS, Package.ID) + results := helpers.GetPackageAsIds(ingestedPackages) + if len(results) != len(ingestedPackages) { + return nil, fmt.Errorf("could no derive correct package ID information for ingested packages, expected to return %d but have %d", len(ingestedPackages), len(results)) } + return results, nil } - return ingestedPackagesIDS, err + return nil, err } // Packages is the resolver for the packages field. diff --git a/pkg/assembler/graphql/resolvers/source.resolvers.go b/pkg/assembler/graphql/resolvers/source.resolvers.go index b3b28ee13c..5dc0cfc0c5 100644 --- a/pkg/assembler/graphql/resolvers/source.resolvers.go +++ b/pkg/assembler/graphql/resolvers/source.resolvers.go @@ -6,30 +6,37 @@ package resolvers import ( "context" + "fmt" + "github.com/guacsec/guac/pkg/assembler/graphql/helpers" "github.com/guacsec/guac/pkg/assembler/graphql/model" "github.com/vektah/gqlparser/v2/gqlerror" ) // IngestSource is the resolver for the ingestSource field. -func (r *mutationResolver) IngestSource(ctx context.Context, source model.SourceInputSpec) (string, error) { +func (r *mutationResolver) IngestSource(ctx context.Context, source model.SourceInputSpec) (*model.SourceIDs, error) { ingestedSource, err := r.Backend.IngestSource(ctx, source) if err != nil { - return "", err + return nil, err } - return ingestedSource.ID, err + results := helpers.GetSourceAsIds([]*model.Source{ingestedSource}) + if len(results) != 1 { + return nil, fmt.Errorf("could no derive correct package ID information for ingested packages, expected to return 1 but have %d", len(results)) + } + return results[0], nil } // IngestSources is the resolver for the ingestSources field. -func (r *mutationResolver) IngestSources(ctx context.Context, sources []*model.SourceInputSpec) ([]string, error) { +func (r *mutationResolver) IngestSources(ctx context.Context, sources []*model.SourceInputSpec) ([]*model.SourceIDs, error) { ingestedSources, err := r.Backend.IngestSources(ctx, sources) - ingestedSourcesIDS := []string{} if err == nil { - for _, source := range ingestedSources { - ingestedSourcesIDS = append(ingestedSourcesIDS, source.ID) + results := helpers.GetSourceAsIds(ingestedSources) + if len(results) != len(ingestedSources) { + return nil, fmt.Errorf("could no derive correct package ID information for ingested packages, expected to return 1 but have %d", len(results)) } + return results, nil } - return ingestedSourcesIDS, err + return nil, err } // Sources is the resolver for the sources field. diff --git a/pkg/assembler/graphql/schema/hasSBOM.graphql b/pkg/assembler/graphql/schema/hasSBOM.graphql index 5c1525e30d..c9a9a95305 100644 --- a/pkg/assembler/graphql/schema/hasSBOM.graphql +++ b/pkg/assembler/graphql/schema/hasSBOM.graphql @@ -35,6 +35,12 @@ type HasSBOM { collector: String! "Timestamp for SBOM creation" knownSince: Time! + "Included packages and artifacts" + includedSoftware: [PackageOrArtifact!]! + "Included dependencies" + includedDependencies: [IsDependency!]! + "Included occurrences" + includedOccurrences: [IsOccurrence!]! } """ @@ -55,9 +61,18 @@ input HasSBOMSpec { origin: String collector: String knownSince: Time + includedSoftware: [PackageOrArtifactSpec]! + includedDependencies: [IsDependencySpec]! + includedOccurrences: [IsOccurrenceSpec]! } -"HasSBOMInputSpec is the same as HasSBOM but for mutation input." +input HasSBOMIncludesInputSpec { + software: [ID!]! + dependencies: [ID!]! + occurrences: [ID!]! +} + +"HasSBOMInputSpec is similar to HasSBOM but for mutation input." input HasSBOMInputSpec { uri: String! algorithm: String! @@ -78,10 +93,12 @@ extend type Mutation { ingestHasSBOM( subject: PackageOrArtifactInput! hasSBOM: HasSBOMInputSpec! + includes: HasSBOMIncludesInputSpec! ): ID! "Bulk ingest that package or artifact has an SBOM. The returned array of IDs can be a an array of empty string." ingestHasSBOMs( subjects: PackageOrArtifactInputs! hasSBOMs: [HasSBOMInputSpec!]! + includes: [HasSBOMIncludesInputSpec!]! ): [ID!]! } diff --git a/pkg/assembler/graphql/schema/package.graphql b/pkg/assembler/graphql/schema/package.graphql index cd7484a2ae..c6840b6eaf 100644 --- a/pkg/assembler/graphql/schema/package.graphql +++ b/pkg/assembler/graphql/schema/package.graphql @@ -115,6 +115,16 @@ type PackageQualifier { value: String! } +""" +The IDs of the ingested package +""" +type PackageIDs { + packageTypeID: ID! + packageNamespaceID: ID! + packageNameID: ID! + packageVersionID: ID! +} + """ PkgSpec allows filtering the list of sources to return in a query. @@ -181,8 +191,8 @@ extend type Query { } extend type Mutation { - "Ingests a new package and returns the corresponding package trie path. The returned ID can be empty string." - ingestPackage(pkg: PkgInputSpec!): ID! - "Bulk ingests packages and returns the list of corresponding package trie path. The returned array of IDs can be a an array of empty string." - ingestPackages(pkgs: [PkgInputSpec!]!): [ID!]! + "Ingests a new package and returns a corresponding package hierarchy containing only the IDs. The returned ID can be empty string." + ingestPackage(pkg: PkgInputSpec!): PackageIDs! + "Bulk ingests packages and returns the list of corresponding package hierarchies containing only the IDs. The returned array of IDs can be empty strings." + ingestPackages(pkgs: [PkgInputSpec!]!): [PackageIDs!]! } diff --git a/pkg/assembler/graphql/schema/path.graphql b/pkg/assembler/graphql/schema/path.graphql index e779d2d781..bb61299eb7 100644 --- a/pkg/assembler/graphql/schema/path.graphql +++ b/pkg/assembler/graphql/schema/path.graphql @@ -119,6 +119,9 @@ enum Edge { HAS_METADATA_SOURCE HAS_SBOM_ARTIFACT HAS_SBOM_PACKAGE + HAS_SBOM_INCLUDED_SOFTWARE + HAS_SBOM_INCLUDED_DEPENDENCIES + HAS_SBOM_INCLUDED_OCCURRENCES HAS_SLSA_BUILT_BY HAS_SLSA_MATERIALS HAS_SLSA_SUBJECT diff --git a/pkg/assembler/graphql/schema/source.graphql b/pkg/assembler/graphql/schema/source.graphql index 280c268d8d..5232725a95 100644 --- a/pkg/assembler/graphql/schema/source.graphql +++ b/pkg/assembler/graphql/schema/source.graphql @@ -64,6 +64,15 @@ type SourceName { commit: String } +""" +The IDs of the ingested pacsourcekage +""" +type SourceIDs { + sourceTypeID: ID! + sourceNamespaceID: ID! + sourceNameID: ID! +} + """ SourceSpec allows filtering the list of sources to return in a query. @@ -108,7 +117,7 @@ extend type Query { extend type Mutation { "Ingests a new source and returns the corresponding source trie path. The returned ID can be empty string." - ingestSource(source: SourceInputSpec!): ID! + ingestSource(source: SourceInputSpec!): SourceIDs! "Bulk ingests sources and returns the list of corresponding source trie path. The returned array of IDs can be a an array of empty string." - ingestSources(sources: [SourceInputSpec!]!): [ID!]! + ingestSources(sources: [SourceInputSpec!]!): [SourceIDs!]! }