From 91dcd98e416fab5f02c85422229a7cbaee174421 Mon Sep 17 00:00:00 2001 From: Forest Eckhardt Date: Tue, 2 Aug 2022 17:34:47 +0000 Subject: [PATCH 1/3] Makes CycloneDX SBOM Reproducible - Removes the serial number and timestamp in the FormatterReader.Read() function --- sbom/formatted_reader.go | 25 +++++++++++++++++++++++++ sbom/formatted_reader_test.go | 5 +++++ sbom/sbom_outputs_test.go | 8 +++++--- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/sbom/formatted_reader.go b/sbom/formatted_reader.go index 540a19ab..54027e29 100644 --- a/sbom/formatted_reader.go +++ b/sbom/formatted_reader.go @@ -2,6 +2,7 @@ package sbom import ( "bytes" + "encoding/json" "fmt" "io" "sync" @@ -53,6 +54,30 @@ func (f *FormattedReader) Read(b []byte) (int, error) { return 0, fmt.Errorf("failed to format sbom: %w", err) } + // Makes CycloneDX SBOM more reproducible, see + // https://github.com/paketo-buildpacks/packit/issues/367 for more details. + if f.format.ID() == "cyclonedx-1.3-json" || f.format.ID() == "cyclonedx-1-json" { + var cycloneDXOutput map[string]interface{} + err = json.Unmarshal(output, &cycloneDXOutput) + if err != nil { + return 0, fmt.Errorf("failed to modify SPDX SBOM for reproducibility: %w", err) + } + for k := range cycloneDXOutput { + if k == "metadata" { + metadata := cycloneDXOutput[k].(map[string]interface{}) + delete(metadata, "timestamp") + cycloneDXOutput[k] = metadata + } + if k == "serialNumber" { + delete(cycloneDXOutput, k) + } + } + output, err = json.Marshal(cycloneDXOutput) + if err != nil { + return 0, fmt.Errorf("failed to modify CycloneDX SBOM for reproducibility: %w", err) + } + } + f.reader = bytes.NewBuffer(output) } diff --git a/sbom/formatted_reader_test.go b/sbom/formatted_reader_test.go index 71db8867..e087095d 100644 --- a/sbom/formatted_reader_test.go +++ b/sbom/formatted_reader_test.go @@ -39,7 +39,10 @@ func testFormattedReader(t *testing.T, context spec.G, it spec.S) { Expect(cdxOutput.BOMFormat).To(Equal("CycloneDX"), buffer.String()) Expect(cdxOutput.SpecVersion).To(Equal("1.3"), buffer.String()) + Expect(cdxOutput.SerialNumber).To(Equal(""), buffer.String()) + Expect(cdxOutput.Metadata.Timestamp).To(Equal(""), buffer.String()) + Expect(cdxOutput.Metadata.Component.Type).To(Equal("file"), buffer.String()) Expect(cdxOutput.Metadata.Component.Type).To(Equal("file"), buffer.String()) Expect(cdxOutput.Metadata.Component.Name).To(Equal("testdata/"), buffer.String()) Expect(cdxOutput.Components[0].Name).To(Equal("collapse-white-space"), buffer.String()) @@ -62,7 +65,9 @@ func testFormattedReader(t *testing.T, context spec.G, it spec.S) { Expect(cdxOutput.BOMFormat).To(Equal("CycloneDX"), buffer.String()) Expect(cdxOutput.SpecVersion).To(Equal("1.4"), buffer.String()) + Expect(cdxOutput.SerialNumber).To(Equal(""), buffer.String()) + Expect(cdxOutput.Metadata.Timestamp).To(Equal(""), buffer.String()) Expect(cdxOutput.Metadata.Component.Type).To(Equal("file"), buffer.String()) Expect(cdxOutput.Metadata.Component.Name).To(Equal("testdata/"), buffer.String()) Expect(cdxOutput.Components[0].Name).To(Equal("collapse-white-space"), buffer.String()) diff --git a/sbom/sbom_outputs_test.go b/sbom/sbom_outputs_test.go index 93c236e7..dc861119 100644 --- a/sbom/sbom_outputs_test.go +++ b/sbom/sbom_outputs_test.go @@ -17,9 +17,11 @@ type component struct { } type cdxOutput struct { - BOMFormat string `json:"bomFormat"` - SpecVersion string `json:"specVersion"` - Metadata struct { + BOMFormat string `json:"bomFormat"` + SpecVersion string `json:"specVersion"` + SerialNumber string `json:"serialNumber"` + Metadata struct { + Timestamp string `json:"timestamp"` Component struct { Type string `json:"type"` Name string `json:"name"` From c7aace6766c52230630d1d3bf09c21e592663cad Mon Sep 17 00:00:00 2001 From: Forest Eckhardt Date: Wed, 3 Aug 2022 15:04:54 +0000 Subject: [PATCH 2/3] Adds format verification check --- sbom/formatted_reader_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sbom/formatted_reader_test.go b/sbom/formatted_reader_test.go index e087095d..69f6e094 100644 --- a/sbom/formatted_reader_test.go +++ b/sbom/formatted_reader_test.go @@ -32,6 +32,9 @@ func testFormattedReader(t *testing.T, context spec.G, it spec.S) { _, err := io.Copy(buffer, sbom.NewFormattedReader(bom, sbom.CycloneDXFormat)) Expect(err).NotTo(HaveOccurred()) + format := syft.IdentifyFormat(buffer.Bytes()) + Expect(format.ID()).To(Equal(syft.CycloneDxJSONFormatID)) + var cdxOutput cdxOutput err = json.Unmarshal(buffer.Bytes(), &cdxOutput) @@ -58,6 +61,9 @@ func testFormattedReader(t *testing.T, context spec.G, it spec.S) { _, err := io.Copy(buffer, sbom.NewFormattedReader(bom, sbom.Format(syft.CycloneDxJSONFormatID))) Expect(err).NotTo(HaveOccurred()) + format := syft.IdentifyFormat(buffer.Bytes()) + Expect(format.ID()).To(Equal(syft.CycloneDxJSONFormatID)) + var cdxOutput cdxOutput err = json.Unmarshal(buffer.Bytes(), &cdxOutput) From d8528d4dab0f52860a5f69948b405bbe867b3df6 Mon Sep 17 00:00:00 2001 From: Forest Eckhardt Date: Wed, 3 Aug 2022 11:06:43 -0400 Subject: [PATCH 3/3] Update sbom/formatted_reader.go Co-authored-by: Sophie Wigmore --- sbom/formatted_reader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbom/formatted_reader.go b/sbom/formatted_reader.go index 54027e29..1ffa33b4 100644 --- a/sbom/formatted_reader.go +++ b/sbom/formatted_reader.go @@ -60,7 +60,7 @@ func (f *FormattedReader) Read(b []byte) (int, error) { var cycloneDXOutput map[string]interface{} err = json.Unmarshal(output, &cycloneDXOutput) if err != nil { - return 0, fmt.Errorf("failed to modify SPDX SBOM for reproducibility: %w", err) + return 0, fmt.Errorf("failed to modify CycloneDX SBOM for reproducibility: %w", err) } for k := range cycloneDXOutput { if k == "metadata" {