diff --git a/demo/graphql/queries-trustification.gql b/demo/graphql/queries-trustification.gql index 251b745a36..1a5d870893 100644 --- a/demo/graphql/queries-trustification.gql +++ b/demo/graphql/queries-trustification.gql @@ -465,3 +465,19 @@ query TC_1814_FindVulnerabilityBySbomURI { } } } + +query TC_1847_FindDependentProduct_source { + findDependentProduct ( + purl: "pkg:generic/openssl@3.0.7?checksum=sha256:83049d042a260e696f62406ac5c08bf706fd84383f945cf21bd61e9ed95c396e&download_url=https://openssl.org/source/openssl-3.0.7.tar.gz" + ) { + ...allHasSBOMTree + } +} + +query TC_1847_FindDependentProduct_rpm { + findDependentProduct ( + purl: "pkg:rpm/redhat/openssl-perl@3.0.7-18.el9_2?arch=aarch64&repository_id=rhel-9-for-aarch64-baseos-eus-rpms" + ) { + ...allHasSBOMTree + } +} diff --git a/internal/testing/e2e-trustification/e2e b/internal/testing/e2e-trustification/e2e index 3376fb4efc..0058a8e46d 100755 --- a/internal/testing/e2e-trustification/e2e +++ b/internal/testing/e2e-trustification/e2e @@ -154,4 +154,12 @@ diff -u "${SCRIPT_DIR}/expectTC_1814_HasSBOM.json" "${GUAC_DIR}/gotTC_1814_HasSB cat "$queries" | gql-cli http://localhost:8080/query -o TC_1814_FindVulnerabilityBySbomURI | jq ' .findVulnerabilityBySbomURI ' > "${GUAC_DIR}/gotTC_1814_FindVulnerabilityBySbomURI.json" diff -u "${SCRIPT_DIR}/expectTC_1814_FindVulnerabilityBySbomURI.json" "${GUAC_DIR}/gotTC_1814_FindVulnerabilityBySbomURI.json" +echo @@@@ Running TC-1847 queries and validating output +# the SBOM is the same one used for TC_1757, i.e. TC_1757_openssl-3.0.7-18.el9_2.spdx.json, so need to ingest it again +cat "$queries" | gql-cli http://localhost:8080/query -o TC_1847_FindDependentProduct_source | jq 'del(.. | .id?) | del(.. | .downloadLocation?) | del(.. | .origin?) | .findDependentProduct[].subject.namespaces[]?.names[]?.versions[]?.qualifiers? |= sort | .findDependentProduct' > "${GUAC_DIR}/gotTC_1847_FindDependentProduct_source.json" +diff -u "${SCRIPT_DIR}/expectTC_1847_FindDependentProduct_source.json" "${GUAC_DIR}/gotTC_1847_FindDependentProduct_source.json" + +cat "$queries" | gql-cli http://localhost:8080/query -o TC_1847_FindDependentProduct_rpm | jq 'del(.. | .id?) | del(.. | .downloadLocation?) | del(.. | .origin?) | .findDependentProduct[].subject.namespaces[]?.names[]?.versions[]?.qualifiers? |= sort | .findDependentProduct' > "${GUAC_DIR}/gotTC_1847_FindDependentProduct_rpm.json" +diff -u "${SCRIPT_DIR}/expectTC_1847_FindDependentProduct_rpm.json" "${GUAC_DIR}/gotTC_1847_FindDependentProduct_rpm.json" + # Note: graphql_playground is left running, CI will clean it up diff --git a/internal/testing/e2e-trustification/expectTC_1847_FindDependentProduct_rpm.json b/internal/testing/e2e-trustification/expectTC_1847_FindDependentProduct_rpm.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/internal/testing/e2e-trustification/expectTC_1847_FindDependentProduct_rpm.json @@ -0,0 +1 @@ +[] diff --git a/internal/testing/e2e-trustification/expectTC_1847_FindDependentProduct_source.json b/internal/testing/e2e-trustification/expectTC_1847_FindDependentProduct_source.json new file mode 100644 index 0000000000..a307fbbd02 --- /dev/null +++ b/internal/testing/e2e-trustification/expectTC_1847_FindDependentProduct_source.json @@ -0,0 +1,13 @@ +[ + { + "subject": { + "__typename": "Artifact", + "algorithm": "sha256", + "digest": "31b5079268339cff7ba65a0aee77930560c5adef4b1b3f8f5927a43ee46a56d9" + }, + "uri": "https://www.redhat.com/openssl-3.0.7-18.el9_2.spdx.json", + "algorithm": "sha256", + "digest": "b9bc355df9867cddb41208fac6327f94cc32099ed2bbb7fe4848651326d6a000", + "collector": "FileCollector" + } +] diff --git a/pkg/assembler/backends/ent/backend/search.go b/pkg/assembler/backends/ent/backend/search.go index 1ea9138f23..62fb5eb71b 100644 --- a/pkg/assembler/backends/ent/backend/search.go +++ b/pkg/assembler/backends/ent/backend/search.go @@ -544,6 +544,7 @@ func (b *EntBackend) FindDependentProduct(ctx context.Context, purl string, offs }), ). WithPackage(withPackageVersionTree()). + WithArtifact(). Order(billofmaterials.ByID(), ent.Desc()) if offset != nil { sbomQuery.Offset(*offset) diff --git a/pkg/ingestor/parser/spdx/parse_spdx.go b/pkg/ingestor/parser/spdx/parse_spdx.go index 1f8d8b68d3..ea52575524 100644 --- a/pkg/ingestor/parser/spdx/parse_spdx.go +++ b/pkg/ingestor/parser/spdx/parse_spdx.go @@ -414,8 +414,9 @@ func (s *spdxParser) GetPredicates(ctx context.Context) *assembler.IngestPredica func isDependency(rel string) bool { return map[string]bool{ - spdx_common.TypeRelationshipContains: true, - spdx_common.TypeRelationshipDependsOn: true, + spdx_common.TypeRelationshipContains: true, + spdx_common.TypeRelationshipDependsOn: true, + spdx_common.TypeRelationshipGeneratedFrom: true, }[rel] } @@ -423,6 +424,7 @@ func isDependent(rel string) bool { return map[string]bool{ spdx_common.TypeRelationshipContainedBy: true, spdx_common.TypeRelationshipDependencyOf: true, + spdx_common.TypeRelationshipGenerates: true, }[rel] } diff --git a/pkg/ingestor/parser/spdx/parse_spdx_test.go b/pkg/ingestor/parser/spdx/parse_spdx_test.go index 8e2f4edaff..29cedb7c38 100644 --- a/pkg/ingestor/parser/spdx/parse_spdx_test.go +++ b/pkg/ingestor/parser/spdx/parse_spdx_test.go @@ -1575,6 +1575,260 @@ func Test_spdxParser(t *testing.T) { }, }, wantErr: false, + }, { + name: "SPDX with GENERATED_FROM relationship", + additionalOpts: []cmp.Option{ + cmpopts.IgnoreFields(assembler.HasSBOMIngest{}, + "HasSBOM"), + }, + doc: &processor.Document{ + Blob: []byte(` + { + "spdxVersion": "SPDX-2.3", + "SPDXID":"SPDXRef-DOCUMENT", + "name":"sbom-sha256:a743268cd3c56f921f3fb706cc0425c8ab78119fd433e38bb7c5dcd5635b0d10", + "creationInfo": { "created": "2022-09-24T17:27:55.556104Z" }, + "packages":[ + { + "SPDXID": "SPDXRef-SRPM", + "name": "openssl", + "versionInfo": "3.0.7-18.el9_2", + "downloadLocation": "NOASSERTION", + "packageFileName": "openssl-3.0.7-18.el9_2.src.rpm", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "31b5079268339cff7ba65a0aee77930560c5adef4b1b3f8f5927a43ee46a56d9" + } + ] + }, + { + "SPDXID": "SPDXRef-aarch64-openssl-perl", + "name": "openssl-perl", + "versionInfo": "3.0.7-18.el9_2", + "downloadLocation": "NOASSERTION", + "packageFileName": "openssl-perl-3.0.7-18.el9_2.aarch64.rpm", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "96e53b2da90ce5ad109ba659ce3ed1b5a819b108c95fc493f84847429898b2ed" + } + ] + } + ], + "relationships":[ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBES", + "relatedSpdxElement": "SPDXRef-SRPM" + }, + { + "spdxElementId": "SPDXRef-aarch64-openssl-perl", + "relationshipType": "GENERATED_FROM", + "relatedSpdxElement": "SPDXRef-SRPM" + } + ] + } + `), + Format: processor.FormatJSON, + Type: processor.DocumentSPDX, + SourceInformation: processor.SourceInformation{ + Collector: "TestCollector", + Source: "TestSource", + }, + }, + wantPredicates: &assembler.IngestPredicates{ + IsDependency: []assembler.IsDependencyIngest{ + { + Pkg: &generated.PkgInputSpec{ + Type: "guac", + Namespace: ptrfrom.String("pkg"), + Name: "openssl-perl", + Version: ptrfrom.String("3.0.7-18.el9_2"), + Subpath: &packageOfEmptyString, + }, + DepPkg: &generated.PkgInputSpec{ + Type: "guac", + Namespace: ptrfrom.String("pkg"), + Name: "openssl", + Version: ptrfrom.String("3.0.7-18.el9_2"), + Subpath: &packageOfEmptyString, + }, + DepPkgMatchFlag: generated.MatchFlags{Pkg: "SPECIFIC_VERSION"}, + IsDependency: &generated.IsDependencyInputSpec{ + VersionRange: "3.0.7-18.el9_2", + DependencyType: "UNKNOWN", + Justification: "Derived from SPDX GENERATED_FROM relationship", + }, + }, + }, + IsOccurrence: []assembler.IsOccurrenceIngest{ + { + Pkg: &generated.PkgInputSpec{ + Type: "guac", + Namespace: ptrfrom.String("pkg"), + Name: "openssl", + Version: ptrfrom.String("3.0.7-18.el9_2"), + Subpath: &packageOfEmptyString, + }, + Artifact: &generated.ArtifactInputSpec{ + Algorithm: "sha256", + Digest: "31b5079268339cff7ba65a0aee77930560c5adef4b1b3f8f5927a43ee46a56d9", + }, + IsOccurrence: &generated.IsOccurrenceInputSpec{Justification: "spdx package with checksum"}, + }, + { + Pkg: &generated.PkgInputSpec{ + Type: "guac", + Namespace: ptrfrom.String("pkg"), + Name: "openssl-perl", + Version: ptrfrom.String("3.0.7-18.el9_2"), + Subpath: &packageOfEmptyString, + }, + Artifact: &generated.ArtifactInputSpec{ + Algorithm: "sha256", + Digest: "96e53b2da90ce5ad109ba659ce3ed1b5a819b108c95fc493f84847429898b2ed", + }, + IsOccurrence: &generated.IsOccurrenceInputSpec{Justification: "spdx package with checksum"}, + }, + }, + HasSBOM: []assembler.HasSBOMIngest{ + { + Artifact: &generated.ArtifactInputSpec{ + Algorithm: "sha256", + Digest: "31b5079268339cff7ba65a0aee77930560c5adef4b1b3f8f5927a43ee46a56d9", + }, + }, + }, + }, + wantErr: false, + }, { + name: "SPDX with GENERATES relationship", + additionalOpts: []cmp.Option{ + cmpopts.IgnoreFields(assembler.HasSBOMIngest{}, + "HasSBOM"), + }, + doc: &processor.Document{ + Blob: []byte(` + { + "spdxVersion": "SPDX-2.3", + "SPDXID":"SPDXRef-DOCUMENT", + "name":"sbom-sha256:a743268cd3c56f921f3fb706cc0425c8ab78119fd433e38bb7c5dcd5635b0d10", + "creationInfo": { "created": "2022-09-24T17:27:55.556104Z" }, + "packages":[ + { + "SPDXID": "SPDXRef-SRPM", + "name": "openssl", + "versionInfo": "3.0.7-18.el9_2", + "downloadLocation": "NOASSERTION", + "packageFileName": "openssl-3.0.7-18.el9_2.src.rpm", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "31b5079268339cff7ba65a0aee77930560c5adef4b1b3f8f5927a43ee46a56d9" + } + ] + }, + { + "SPDXID": "SPDXRef-aarch64-openssl-perl", + "name": "openssl-perl", + "versionInfo": "3.0.7-18.el9_2", + "downloadLocation": "NOASSERTION", + "packageFileName": "openssl-perl-3.0.7-18.el9_2.aarch64.rpm", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "96e53b2da90ce5ad109ba659ce3ed1b5a819b108c95fc493f84847429898b2ed" + } + ] + } + ], + "relationships":[ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBES", + "relatedSpdxElement": "SPDXRef-SRPM" + }, + { + "spdxElementId": "SPDXRef-SRPM", + "relationshipType": "GENERATES", + "relatedSpdxElement": "SPDXRef-aarch64-openssl-perl" + } + ] + } + `), + Format: processor.FormatJSON, + Type: processor.DocumentSPDX, + SourceInformation: processor.SourceInformation{ + Collector: "TestCollector", + Source: "TestSource", + }, + }, + wantPredicates: &assembler.IngestPredicates{ + IsDependency: []assembler.IsDependencyIngest{ + { + Pkg: &generated.PkgInputSpec{ + Type: "guac", + Namespace: ptrfrom.String("pkg"), + Name: "openssl-perl", + Version: ptrfrom.String("3.0.7-18.el9_2"), + Subpath: &packageOfEmptyString, + }, + DepPkg: &generated.PkgInputSpec{ + Type: "guac", + Namespace: ptrfrom.String("pkg"), + Name: "openssl", + Version: ptrfrom.String("3.0.7-18.el9_2"), + Subpath: &packageOfEmptyString, + }, + DepPkgMatchFlag: generated.MatchFlags{Pkg: "SPECIFIC_VERSION"}, + IsDependency: &generated.IsDependencyInputSpec{ + VersionRange: "3.0.7-18.el9_2", + DependencyType: "UNKNOWN", + Justification: "Derived from SPDX GENERATES relationship", + }, + }, + }, + IsOccurrence: []assembler.IsOccurrenceIngest{ + { + Pkg: &generated.PkgInputSpec{ + Type: "guac", + Namespace: ptrfrom.String("pkg"), + Name: "openssl", + Version: ptrfrom.String("3.0.7-18.el9_2"), + Subpath: &packageOfEmptyString, + }, + Artifact: &generated.ArtifactInputSpec{ + Algorithm: "sha256", + Digest: "31b5079268339cff7ba65a0aee77930560c5adef4b1b3f8f5927a43ee46a56d9", + }, + IsOccurrence: &generated.IsOccurrenceInputSpec{Justification: "spdx package with checksum"}, + }, + { + Pkg: &generated.PkgInputSpec{ + Type: "guac", + Namespace: ptrfrom.String("pkg"), + Name: "openssl-perl", + Version: ptrfrom.String("3.0.7-18.el9_2"), + Subpath: &packageOfEmptyString, + }, + Artifact: &generated.ArtifactInputSpec{ + Algorithm: "sha256", + Digest: "96e53b2da90ce5ad109ba659ce3ed1b5a819b108c95fc493f84847429898b2ed", + }, + IsOccurrence: &generated.IsOccurrenceInputSpec{Justification: "spdx package with checksum"}, + }, + }, + HasSBOM: []assembler.HasSBOMIngest{ + { + Artifact: &generated.ArtifactInputSpec{ + Algorithm: "sha256", + Digest: "31b5079268339cff7ba65a0aee77930560c5adef4b1b3f8f5927a43ee46a56d9", + }, + }, + }, + }, + wantErr: false, }, } for _, tt := range tests {