mirror of
https://github.com/anchore/syft.git
synced 2026-11-03 07:56:36 +01:00
feat: add cyclonedx schema version selection (#2123)
--------- Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
This commit is contained in:
@@ -9,11 +9,40 @@ import (
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
func encoder(output io.Writer, s sbom.SBOM) error {
|
||||
func encoderV1_0(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_0)
|
||||
}
|
||||
|
||||
func encoderV1_1(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_1)
|
||||
}
|
||||
|
||||
func encoderV1_2(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_2)
|
||||
}
|
||||
|
||||
func encoderV1_3(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_3)
|
||||
}
|
||||
|
||||
func encoderV1_4(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_4)
|
||||
}
|
||||
|
||||
func encoderV1_5(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_5)
|
||||
}
|
||||
|
||||
func buildEncoder(output io.Writer, s sbom.SBOM) (cyclonedx.BOMEncoder, *cyclonedx.BOM) {
|
||||
bom := cyclonedxhelpers.ToFormatModel(s)
|
||||
enc := cyclonedx.NewBOMEncoder(output, cyclonedx.BOMFileFormatJSON)
|
||||
enc.SetPretty(true)
|
||||
enc.SetEscapeHTML(false)
|
||||
err := enc.Encode(bom)
|
||||
return err
|
||||
return enc, bom
|
||||
}
|
||||
|
||||
@@ -9,10 +9,62 @@ import (
|
||||
|
||||
const ID sbom.FormatID = "cyclonedx-json"
|
||||
|
||||
func Format() sbom.Format {
|
||||
var Format = Format1_4
|
||||
|
||||
func Format1_0() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
sbom.AnyVersion,
|
||||
encoder,
|
||||
cyclonedx.SpecVersion1_0.String(),
|
||||
encoderV1_0,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON),
|
||||
ID,
|
||||
)
|
||||
}
|
||||
|
||||
func Format1_1() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
cyclonedx.SpecVersion1_1.String(),
|
||||
encoderV1_1,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON),
|
||||
ID,
|
||||
)
|
||||
}
|
||||
|
||||
func Format1_2() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
cyclonedx.SpecVersion1_2.String(),
|
||||
encoderV1_2,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON),
|
||||
ID,
|
||||
)
|
||||
}
|
||||
|
||||
func Format1_3() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
cyclonedx.SpecVersion1_3.String(),
|
||||
encoderV1_3,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON),
|
||||
ID,
|
||||
)
|
||||
}
|
||||
|
||||
func Format1_4() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
cyclonedx.SpecVersion1_4.String(),
|
||||
encoderV1_4,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON),
|
||||
ID,
|
||||
)
|
||||
}
|
||||
|
||||
func Format1_5() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
cyclonedx.SpecVersion1_5.String(),
|
||||
encoderV1_5,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON),
|
||||
ID,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package cyclonedxjson
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
)
|
||||
|
||||
func TestFormatVersions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expectedVersion string
|
||||
}{
|
||||
{
|
||||
|
||||
"cyclonedx-json should default to v1.4",
|
||||
cyclonedx.SpecVersion1_4.String(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range tests {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
sbomFormat := Format()
|
||||
if sbomFormat.ID() != ID {
|
||||
t.Errorf("expected ID %q, got %q", ID, sbomFormat.ID())
|
||||
}
|
||||
|
||||
if sbomFormat.Version() != c.expectedVersion {
|
||||
t.Errorf("expected version %q, got %q", c.expectedVersion, sbomFormat.Version())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,40 @@ import (
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
func encoder(output io.Writer, s sbom.SBOM) error {
|
||||
func encoderV1_0(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_0)
|
||||
}
|
||||
|
||||
func encoderV1_1(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_1)
|
||||
}
|
||||
|
||||
func encoderV1_2(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_2)
|
||||
}
|
||||
|
||||
func encoderV1_3(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_3)
|
||||
}
|
||||
|
||||
func encoderV1_4(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_4)
|
||||
}
|
||||
|
||||
func encoderV1_5(output io.Writer, s sbom.SBOM) error {
|
||||
enc, bom := buildEncoder(output, s)
|
||||
return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_5)
|
||||
}
|
||||
|
||||
func buildEncoder(output io.Writer, s sbom.SBOM) (cyclonedx.BOMEncoder, *cyclonedx.BOM) {
|
||||
bom := cyclonedxhelpers.ToFormatModel(s)
|
||||
enc := cyclonedx.NewBOMEncoder(output, cyclonedx.BOMFileFormatXML)
|
||||
enc.SetPretty(true)
|
||||
|
||||
err := enc.Encode(bom)
|
||||
return err
|
||||
enc.SetEscapeHTML(false)
|
||||
return enc, bom
|
||||
}
|
||||
|
||||
@@ -9,10 +9,62 @@ import (
|
||||
|
||||
const ID sbom.FormatID = "cyclonedx-xml"
|
||||
|
||||
func Format() sbom.Format {
|
||||
var Format = Format1_4
|
||||
|
||||
func Format1_0() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
sbom.AnyVersion,
|
||||
encoder,
|
||||
cyclonedx.SpecVersion1_0.String(),
|
||||
encoderV1_0,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML),
|
||||
ID, "cyclonedx", "cyclone",
|
||||
)
|
||||
}
|
||||
|
||||
func Format1_1() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
cyclonedx.SpecVersion1_1.String(),
|
||||
encoderV1_1,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML),
|
||||
ID, "cyclonedx", "cyclone",
|
||||
)
|
||||
}
|
||||
|
||||
func Format1_2() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
cyclonedx.SpecVersion1_2.String(),
|
||||
encoderV1_2,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML),
|
||||
ID, "cyclonedx", "cyclone",
|
||||
)
|
||||
}
|
||||
|
||||
func Format1_3() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
cyclonedx.SpecVersion1_3.String(),
|
||||
encoderV1_3,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML),
|
||||
ID, "cyclonedx", "cyclone",
|
||||
)
|
||||
}
|
||||
|
||||
func Format1_4() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
cyclonedx.SpecVersion1_4.String(),
|
||||
encoderV1_4,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML),
|
||||
ID, "cyclonedx", "cyclone",
|
||||
)
|
||||
}
|
||||
|
||||
func Format1_5() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
cyclonedx.SpecVersion1_5.String(),
|
||||
encoderV1_5,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML),
|
||||
ID, "cyclonedx", "cyclone",
|
||||
|
||||
+26
-6
@@ -26,17 +26,27 @@ import (
|
||||
func Formats() []sbom.Format {
|
||||
return []sbom.Format{
|
||||
syftjson.Format(),
|
||||
cyclonedxxml.Format(),
|
||||
cyclonedxjson.Format(),
|
||||
github.Format(),
|
||||
table.Format(),
|
||||
text.Format(),
|
||||
template.Format(),
|
||||
cyclonedxxml.Format1_0(),
|
||||
cyclonedxxml.Format1_1(),
|
||||
cyclonedxxml.Format1_2(),
|
||||
cyclonedxxml.Format1_3(),
|
||||
cyclonedxxml.Format1_4(),
|
||||
cyclonedxxml.Format1_5(),
|
||||
cyclonedxjson.Format1_0(),
|
||||
cyclonedxjson.Format1_1(),
|
||||
cyclonedxjson.Format1_2(),
|
||||
cyclonedxjson.Format1_3(),
|
||||
cyclonedxjson.Format1_4(),
|
||||
cyclonedxjson.Format1_5(),
|
||||
spdxtagvalue.Format2_1(),
|
||||
spdxtagvalue.Format2_2(),
|
||||
spdxtagvalue.Format2_3(),
|
||||
spdxjson.Format2_2(),
|
||||
spdxjson.Format2_3(),
|
||||
table.Format(),
|
||||
text.Format(),
|
||||
template.Format(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +65,7 @@ func Identify(by []byte) sbom.Format {
|
||||
|
||||
// ByName accepts a name@version string, such as:
|
||||
//
|
||||
// spdx-json@2.1 or cyclonedx@2
|
||||
// spdx-json@2.1 or cyclonedx@1.5
|
||||
func ByName(name string) sbom.Format {
|
||||
parts := strings.SplitN(name, "@", 2)
|
||||
version := sbom.AnyVersion
|
||||
@@ -71,6 +81,16 @@ func ByNameAndVersion(name string, version string) sbom.Format {
|
||||
for _, f := range Formats() {
|
||||
for _, n := range f.IDs() {
|
||||
if cleanFormatName(string(n)) == name && versionMatches(f.Version(), version) {
|
||||
// if the version is not specified and the format is cyclonedx, then we want to return the most recent version up to 1.4
|
||||
// If more aliases like cdx are added this will not catch those - we want to eventually provide a way for
|
||||
// formats to inform this function what their default version is
|
||||
// TODO: remove this check when 1.5 is stable or default formats are designed. PR below should be merged.
|
||||
// https://github.com/CycloneDX/cyclonedx-go/pull/90
|
||||
if version == sbom.AnyVersion && strings.Contains(string(n), "cyclone") {
|
||||
if f.Version() == "1.5" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if mostRecentFormat == nil || f.Version() > mostRecentFormat.Version() {
|
||||
mostRecentFormat = f
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ func TestFormats_EmptyInput(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestByName(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
want sbom.FormatID
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package syftjson
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
)
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
version string
|
||||
}{
|
||||
{
|
||||
name: "default version should use latest internal version",
|
||||
version: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range tests {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
sbomFormat := Format()
|
||||
if sbomFormat.ID() != ID {
|
||||
t.Errorf("expected ID %q, got %q", ID, sbomFormat.ID())
|
||||
}
|
||||
|
||||
if sbomFormat.Version() != internal.JSONSchemaVersion {
|
||||
t.Errorf("expected version %q, got %q", c.version, sbomFormat.Version())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user