From 3690f979b3ea776e9701bc71d496410576953422 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:56:03 -0500 Subject: [PATCH] feat: update spdx format model to produce valid spdx json documents (#1418) --- .../common/spdxhelpers/relationship_type.go | 5 ++ .../common/spdxhelpers/to_format_model.go | 29 +++++++++- .../TestSPDXJSONDirectoryEncoder.golden | 13 +++-- .../snapshot/TestSPDXJSONImageEncoder.golden | 13 +++-- .../snapshot/TestSPDXRelationshipOrder.golden | 51 +++++++++++++++--- .../stereoscope-fixture-image-simple.golden | Bin 15360 -> 15360 bytes .../snapshot/TestSPDXJSONSPDXIDs.golden | 10 ++-- .../snapshot/TestSPDXRelationshipOrder.golden | 13 +++-- .../TestSPDXTagValueDirectoryEncoder.golden | 10 ++-- .../TestSPDXTagValueImageEncoder.golden | 10 ++-- .../stereoscope-fixture-image-simple.golden | Bin 15360 -> 15360 bytes test/cli/spdx_tooling_validation_test.go | 33 +++++++++++- 12 files changed, 159 insertions(+), 28 deletions(-) diff --git a/syft/formats/common/spdxhelpers/relationship_type.go b/syft/formats/common/spdxhelpers/relationship_type.go index 564dd32ec..a3234c873 100644 --- a/syft/formats/common/spdxhelpers/relationship_type.go +++ b/syft/formats/common/spdxhelpers/relationship_type.go @@ -8,6 +8,11 @@ const ( // Example: The package 'WildFly' is described by SPDX document WildFly.spdx. DescribedByRelationship RelationshipType = "DESCRIBED_BY" + // Describes is to be used when SPDXRef-DOCUMENT describes SPDXRef-A. + // Example: An SPDX document WildFly.spdx describes package ‘WildFly’. + // Note this is a logical relationship to help organize related items within an SPDX document that is mandatory if more than one package or set of files (not in a package) is present. + DescribesRelationship RelationshipType = "DESCRIBES" + // ContainsRelationship is to be used when SPDXRef-A contains SPDXRef-B. // Example: An ARCHIVE file bar.tgz contains a SOURCE file foo.c. ContainsRelationship RelationshipType = "CONTAINS" diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index 798bd1a27..655c9cce6 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -33,6 +33,24 @@ const ( //nolint:funlen func ToFormatModel(s sbom.SBOM) *spdx.Document { name, namespace := DocumentNameAndNamespace(s.Source) + relationships := toRelationships(s.RelationshipsSorted()) + + // for valid SPDX we need a document describes relationship + // TODO: remove this placeholder after deciding on correct behavior + // for the primary package purpose field: + // https://spdx.github.io/spdx-spec/v2.3/package-information/#724-primary-package-purpose-field + documentDescribesRelationship := &spdx.Relationship{ + RefA: common.DocElementID{ + ElementRefID: "DOCUMENT", + }, + Relationship: string(DescribesRelationship), + RefB: common.DocElementID{ + ElementRefID: "DOCUMENT", + }, + RelationshipComment: "", + } + + relationships = append(relationships, documentDescribesRelationship) return &spdx.Document{ // 6.1: SPDX Version; should be in the format "SPDX-x.x" @@ -107,7 +125,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document { }, Packages: toPackages(s.Artifacts.PackageCatalog, s), Files: toFiles(s), - Relationships: toRelationships(s.RelationshipsSorted()), + Relationships: relationships, } } @@ -407,6 +425,15 @@ func toFiles(s sbom.SBOM) (results []*spdx.File) { digests = digestsForLocation } + // if we don't have any metadata or digests for this location + // then the file is most likely a symlink or non-regular file + // for now we include a 0 sha1 digest as requested by the spdx spec + // TODO: update location code in core SBOM so that we can map complex links + // back to their real file digest location. + if len(digests) == 0 { + digests = append(digests, file.Digest{Algorithm: "sha1", Value: "0000000000000000000000000000000000000000"}) + } + // TODO: add file classifications (?) and content as a snippet var comment string diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden index bbe863815..59bdc67c9 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden @@ -3,14 +3,14 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "/some/path", - "documentNamespace": "https://anchore.com/syft/dir/some/path-4bf54cdd-0a0f-4560-bf4f-39cac2ef7a5b", + "documentNamespace": "https://anchore.com/syft/dir/some/path-116e86ba-a976-48ed-909d-9278807ee7fe", "creationInfo": { - "licenseListVersion": "3.18", + "licenseListVersion": "3.19", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2022-11-19T13:46:57Z", + "created": "2022-12-21T03:39:05Z", "comment": "" }, "packages": [ @@ -62,5 +62,12 @@ } ] } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBES" + } ] } diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden index 5462bcdf1..8e9ca38cc 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden @@ -3,14 +3,14 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-102ca7dc-3d1e-46d2-b130-28968831ebcc", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-006b9b96-66f1-4de3-897f-6583b4358c87", "creationInfo": { - "licenseListVersion": "3.18", + "licenseListVersion": "3.19", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2022-11-19T13:46:57Z", + "created": "2022-12-21T03:39:05Z", "comment": "" }, "packages": [ @@ -62,5 +62,12 @@ } ] } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBES" + } ] } diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 451f76e9b..e88d0f5e6 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -3,14 +3,14 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-ace88a38-4633-4bff-8fa3-8ae929dab37d", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-552a9cfc-49ee-4706-81cb-723fd7146b4f", "creationInfo": { "licenseListVersion": "3.19", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2022-12-14T18:21:40Z", + "created": "2022-12-21T03:39:05Z", "comment": "" }, "packages": [ @@ -70,7 +70,12 @@ "fileTypes": [ "OTHER" ], - "checksums": [], + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], "licenseConcluded": "NOASSERTION", "copyrightText": "" }, @@ -80,7 +85,12 @@ "fileTypes": [ "OTHER" ], - "checksums": [], + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], "licenseConcluded": "NOASSERTION", "copyrightText": "" }, @@ -90,7 +100,12 @@ "fileTypes": [ "OTHER" ], - "checksums": [], + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], "licenseConcluded": "NOASSERTION", "copyrightText": "" }, @@ -100,7 +115,12 @@ "fileTypes": [ "OTHER" ], - "checksums": [], + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], "licenseConcluded": "NOASSERTION", "copyrightText": "" }, @@ -110,7 +130,12 @@ "fileTypes": [ "OTHER" ], - "checksums": [], + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], "licenseConcluded": "NOASSERTION", "copyrightText": "" }, @@ -120,7 +145,12 @@ "fileTypes": [ "OTHER" ], - "checksums": [], + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], "licenseConcluded": "NOASSERTION", "copyrightText": "" } @@ -155,6 +185,11 @@ "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", "relatedSpdxElement": "SPDXRef-f9e49132a4b96ccd", "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBES" } ] } diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/spdxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index aca6612022dd7bc15ae55b156bb7897f9e4c54ef..b9ddf6e33390ab196d24a941bdda1dc969b30a0e 100644 GIT binary patch literal 15360 zcmeHOZExE)5ccQ&3QzkQ+a$#|8Q6!eDbNDNQnXnItSAb;CR%ODkmMpkkpI4u>^QNz zR!d|nDMrD7#kV8%9PfNQJ&u_WL{sC4wG4YAF?W_&oG8wCEIH;@I^mcSRx68*XAI#) zS|o9zEfbSL`-Koig0TTwUAy0P?Eur5Au0rp;{hTFBRm+gcG2B?EXzt2u%xb%b|0>* zyq?C#UG@L|#oKqUPJaL1azUGjQ@X47n=&95@8}=nSPX`pb@IJM-L(DB^^eEBpnQXN z_6o6%e<^IHFq!`dK~9-=x|&9qjbfp`(yV%`5m_9dET%h~KG zdOn9|Tiw2ztF!e&-{-UC+?6*if9A#KG&?&^*Qb25t zwjO@nAoKL`_z=B$cKY(y$+9dav)riJq||9Py;I&4ZyTDbRS|BZj}HNK0z2crh^oSz zrA6l z|6xLR&wm8LYrt^+>tS15Mc*Jb+_GsJ-pZbM8e5!}nu4)KVW~+gn^R;f^9LfADldS8 z^Wmyx7?D6RgT~UWm<1Z+>K5ARYHgoA;7t*|kzq624`JM{_M$DZvKrLZwZg1@1)1d9wZf_kg5$BsxsiS)4r zwt4jN`iCRb^kB35oMNuB)Vs3!FMj-?^S1Fn7z57#?bYxRh5KIPz;`}h^CqLc z!~$UbZ`~q4Rc9-oQn|wArO6j=*SvY_orI``@723)J?{d9eROsbfp-RaCZEXdN|lHqt#X%&)JXrUh-m P843Y`fIvXthe6;Ux}S+Y literal 15360 zcmeHOZBOGk5bo#u6}{Y7+S-m^E8PcfS!q{V9j9GxPdbE<~R}c=-EM%S9L=PU*VdZ_9vOyrzE~6Vd6m*2z;Gd z?6lx9jbly{k{E3f(h3PKG{JGos1zzqgh-KaOk-}WAX-ZavP@}pZT-R^6d*lJdPp8& zHbi7dSzk&JqDV~s_Fap-)>&@LQ@C>3Sv0(kJ``1*>#>FL(Bq&OLx;QapOr1=E&Fw? z&H0x)&GKmI^r*6X(Xy_X!e-VhZ3#mcl}Gi}^7?RM^Hn*s(qV335sUus^Ka2GpN>X* z(YrD1ZGQQ2tj`t;{aTEsV_V&}{8f|}S$=kqEmr$Jugj}Rk$I5kV-NG+rrBsI>XT0F zXfga|hRoZ?<2d?b|LDW7gK1R`MupL%L6v0r@LqXYyla?M&5LjuoxB9l0o)nSMO2sO zJgcp#r=?e!9;cjwm;x}5Mp-_+jBYk!lsgpC*6;@-ePzqOkD(v^H28n|F#abP(cu3d z1$2WARWY_M8`&P|*O&DM1>Y0ZpnnACJs%QxsqVV*t ze%{_pL2^JKAP^7;2)rc(+8)-nlK;hmGamB4ySPtU>whcxUnFpd|97GOPRRf61m-t8 zNo)IWpZ`Ui;I;QZh*9wWJ9#tlgTMb-1^Q`LnBw+VkSIacuPNoi% z1Ox&C0fB(PD{?bi6!R8{X{Qiv)xh9Mct>MyvFVHF$7RRwPj>i_PG)Ys6QIaa@oKAtYD=ARG z3E?7zW~{Wah;su{7m^rGX`Cp`3Aa?@*a{F!m{Km$DCNijy_`^O6~zYl<|$Sf{uDD* zS&58_PHw(CLe2I~cB3sx}Zstlf^nXR@pX_NiKzZI?9Kj*>zx2291xz|zEV4*&0Xl^QNz zR!d|nDMrD7#kV8%9PfNQJ&u_WL{sC4wG4YAF?W_&oG8wCEIH;@I^mcSRx68*XAI#) zS|o9zEfbSL`-Koig0TTwUAy0P?Eur5Au0rp;{hTFBRm+gcG2B?EXzt2u%xb%b|0>* zyq?C#UG@L|#oKqUPJaL1azUGjQ@X47n=&95@8}=nSPX`pb@IJM-L(DB^^eEBpnQXN z_6o6%e<^IHFq!`dK~9-=x|&9qjbfp`(yV%`5m_9dET%h~KG zdOn9|Tiw2ztF!e&-{-UC+?6*if9A#KG&?&^*Qb25t zwjO@nAoKL`_z=B$cKY(y$+9dav)riJq||9Py;I&4ZyTDbRS|BZj}HNK0z2crh^oSz zrA6l z|6xLR&wm8LYrt^+>tS15Mc*Jb+_GsJ-pZbM8e5!}nu4)KVW~+gn^R;f^9LfADldS8 z^Wmyx7?D6RgT~UWm<1Z+>K5ARYHgoA;7t*|kzq624`JM{_M$DZvKrLZwZg1@1)1d9wZf_kg5$BsxsiS)4r zwt4jN`iCRb^kB35oMNuB)Vs3!FMj-?^S1Fn7z57#?bYxRh5KIPz;`}h^CqLc z!~$UbZ`~q4Rc9-oQn|wArO6j=*SvY_orI``@723)J?{d9eROsbfp-RaCZEXdN|lHqt#X%&)JXrUh-m P843Y`fIvXthe6;Ux}S+Y literal 15360 zcmeHOZExE)5YFfQ3QzkQ+kB^JU>~xkKnoO0(Pka6peQK5BwB3AkmRC4kpI4u?Krl( zL`@_c$wtGl`FcD)r<3k@Cy5P`Tr=aC@SgZ2HYUbI3u`Q6PFw4}v;qpyELBXolU90< zluLN5(D0!7gAju?_yJmcw*Twa1I#GGk{}__0U}5udC=p{q}z{}m6a}lrM8r|H`kWl zp61K8^8fVu!^gKLzdyt-!Uze*w&i|X2ju9M{IO*Gpx0U^4>5Jy_dl0E9_$6>TeQRf zGee-Y{{LO(S;L+h^dEDwegDT?ilF}uuy)dY9i=uJMQtyG=rEe@DZywiePNf;F(A;#H+Vaqhpv4-b9^N2V{jR#t$aJq6eV+0Y>M{%F0SQ3 zvxNfTfIvVXAP^9ERtO~Apv3DqFd;S+`S>*-z z;Ahtj)1;u3B1-7|z+kb$mjq@;N5{v#xe0`8^S3u#nZt|vFTJF8urJdJST*@%vN++m zZ5-I#T)LtrF!+k1o5LnXHcu}8m65p}zQ)L+{zyi^BVKb#52GT_D-T}kbyQtVYu9z| zwUw|bn|l8xwa|AKl^2?DF#^T6*d&}Qs%$J12o4bip&n_G<`i+2BvdkDh!vb-CKz=D zei68m9%*8M>MbQMm6FDLrDG~ERX7%es>FIHCE*Cla5jW2a#l&prNaorTHL|yg;A`c z&o?j5q27zucDG2|!@3>C|9Bn$3Gu(~y}&pMdl(bh!!xZ4C<5fIvVX zAP^Av&ml0;S?XO`{YV_=she+l{&P;(a={`0AM!tN$p3jN+>e?Ev6ENJyveAOSRiH?>0~-~qnJbPr{NgIabhiz%u5|Br<8-& zMihb=q%cf5NxV0Tb9gbt!0RYD_Q(-pcv5Hoy{mK3=F4^G&cHsp%Z-CT#^;|NC6|2- zE