mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
Use the json schema as input for templating (#2542)
* use the json schema as input for templating Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix cli tests Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
11c0b1c234
commit
a32b8d7fc6
17
README.md
17
README.md
@ -555,6 +555,7 @@ select-catalogers: []
|
|||||||
format:
|
format:
|
||||||
|
|
||||||
# default value for all formats that support the "pretty" option (default is unset)
|
# default value for all formats that support the "pretty" option (default is unset)
|
||||||
|
# SYFT_FORMAT_PRETTY env var
|
||||||
pretty:
|
pretty:
|
||||||
|
|
||||||
# all syft-json format options
|
# all syft-json format options
|
||||||
@ -562,6 +563,7 @@ format:
|
|||||||
|
|
||||||
# include space indention and newlines (inherits default value from 'format.pretty' or 'false' if parent is unset)
|
# include space indention and newlines (inherits default value from 'format.pretty' or 'false' if parent is unset)
|
||||||
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
|
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
|
||||||
|
# SYFT_FORMAT_JSON_PRETTY env var
|
||||||
pretty: false
|
pretty: false
|
||||||
|
|
||||||
# transform any syft-json output to conform to an approximation of the v11.0.1 schema. This includes:
|
# transform any syft-json output to conform to an approximation of the v11.0.1 schema. This includes:
|
||||||
@ -571,21 +573,30 @@ format:
|
|||||||
# that output might not strictly be json schema v11 compliant, however, for consumers that require time to port
|
# that output might not strictly be json schema v11 compliant, however, for consumers that require time to port
|
||||||
# over to the final syft 1.0 json output this option can be used to ease the transition.
|
# over to the final syft 1.0 json output this option can be used to ease the transition.
|
||||||
#
|
#
|
||||||
# Note: long term support for this option is not guaranteed.
|
# Note: long term support for this option is not guaranteed (it may change or break at any time).
|
||||||
|
# SYFT_FORMAT_JSON_LEGACY env var
|
||||||
legacy: false
|
legacy: false
|
||||||
|
|
||||||
# all template format options
|
# all template format options
|
||||||
template:
|
template:
|
||||||
# path to the template file to use when rendering the output with the `template` output format.
|
# path to the template file to use when rendering the output with the `template` output format.
|
||||||
# Note that all template paths are based on the current syft-json schema.
|
# Note that all template paths are based on the current syft-json schema.
|
||||||
# SYFT_TEMPLATE_PATH env var / -t flag
|
# SYFT_FORMAT_TEMPLATE_PATH env var / -t flag
|
||||||
path: ""
|
path: ""
|
||||||
|
|
||||||
|
# if true, uses the go structs for the syft-json format for templating.
|
||||||
|
# if false, uses the syft-json output for templating (which follows the syft JSON schema exactly).
|
||||||
|
#
|
||||||
|
# Note: long term support for this option is not guaranteed (it may change or break at any time).
|
||||||
|
# SYFT_FORMAT_TEMPLATE_LEGACY env var
|
||||||
|
legacy: false
|
||||||
|
|
||||||
# all spdx-json format options
|
# all spdx-json format options
|
||||||
spdx-json:
|
spdx-json:
|
||||||
|
|
||||||
# include space indention and newlines
|
# include space indention and newlines
|
||||||
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
|
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
|
||||||
|
# SYFT_FORMAT_SPDX_JSON_PRETTY env var
|
||||||
pretty: false
|
pretty: false
|
||||||
|
|
||||||
# all cyclonedx-json format options
|
# all cyclonedx-json format options
|
||||||
@ -593,6 +604,7 @@ format:
|
|||||||
|
|
||||||
# include space indention and newlines
|
# include space indention and newlines
|
||||||
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
|
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
|
||||||
|
# SYFT_FORMAT_CYCLONEDX_JSON_PRETTY env var
|
||||||
pretty: false
|
pretty: false
|
||||||
|
|
||||||
# all cyclonedx-xml format options
|
# all cyclonedx-xml format options
|
||||||
@ -600,6 +612,7 @@ format:
|
|||||||
|
|
||||||
# include space indention
|
# include space indention
|
||||||
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
|
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
|
||||||
|
# SYFT_FORMAT_CYCLONEDX_XML_PRETTY env var
|
||||||
pretty: false
|
pretty: false
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ var _ clio.FlagAdder = (*FormatTemplate)(nil)
|
|||||||
type FormatTemplate struct {
|
type FormatTemplate struct {
|
||||||
Enabled bool `yaml:"-" json:"-" mapstructure:"-"`
|
Enabled bool `yaml:"-" json:"-" mapstructure:"-"`
|
||||||
Path string `yaml:"path" json:"path" mapstructure:"path"` // -t template file to use for output
|
Path string `yaml:"path" json:"path" mapstructure:"path"` // -t template file to use for output
|
||||||
|
Legacy bool `yaml:"legacy" json:"legacy" mapstructure:"legacy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultFormatTemplate() FormatTemplate {
|
func DefaultFormatTemplate() FormatTemplate {
|
||||||
@ -28,5 +29,6 @@ func (o *FormatTemplate) AddFlags(flags clio.FlagSet) {
|
|||||||
func (o FormatTemplate) config() template.EncoderConfig {
|
func (o FormatTemplate) config() template.EncoderConfig {
|
||||||
return template.EncoderConfig{
|
return template.EncoderConfig{
|
||||||
TemplatePath: o.Path,
|
TemplatePath: o.Path,
|
||||||
|
Legacy: o.Legacy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,9 +72,7 @@ func (o EncodersConfig) Encoders() ([]sbom.FormatEncoder, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o EncodersConfig) templateEncoders() ([]sbom.FormatEncoder, error) {
|
func (o EncodersConfig) templateEncoders() ([]sbom.FormatEncoder, error) {
|
||||||
enc, err := template.NewFormatEncoder(template.EncoderConfig{
|
enc, err := template.NewFormatEncoder(o.Template)
|
||||||
TemplatePath: o.Template.TemplatePath,
|
|
||||||
})
|
|
||||||
return []sbom.FormatEncoder{enc}, err
|
return []sbom.FormatEncoder{enc}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package template
|
package template
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -19,6 +21,7 @@ const ID sbom.FormatID = "template"
|
|||||||
|
|
||||||
type EncoderConfig struct {
|
type EncoderConfig struct {
|
||||||
TemplatePath string
|
TemplatePath string
|
||||||
|
Legacy bool
|
||||||
syftjson.EncoderConfig
|
syftjson.EncoderConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +93,28 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
|
|||||||
return fmt.Errorf("unable to parse template: %w", err)
|
return fmt.Errorf("unable to parse template: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
doc := syftjson.ToFormatModel(s, e.cfg.EncoderConfig)
|
var doc any
|
||||||
|
if e.cfg.Legacy {
|
||||||
|
doc = syftjson.ToFormatModel(s, e.cfg.EncoderConfig)
|
||||||
|
} else {
|
||||||
|
enc, err := syftjson.NewFormatEncoderWithConfig(e.cfg.EncoderConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create json encoder: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
if err = enc.Encode(&buff, s); err != nil {
|
||||||
|
return fmt.Errorf("unable to encode json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializedDoc := make(map[string]any)
|
||||||
|
|
||||||
|
if err = json.Unmarshal(buff.Bytes(), &deserializedDoc); err != nil {
|
||||||
|
return fmt.Errorf("unable to unmarshal json for template: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
doc = deserializedDoc
|
||||||
|
}
|
||||||
|
|
||||||
return tmpl.Execute(writer, doc)
|
return tmpl.Execute(writer, doc)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,42 @@ import (
|
|||||||
|
|
||||||
var updateSnapshot = flag.Bool("update-template", false, "update the *.golden files for json encoders")
|
var updateSnapshot = flag.Bool("update-template", false, "update the *.golden files for json encoders")
|
||||||
|
|
||||||
|
func TestFormatWithOption_Legacy(t *testing.T) {
|
||||||
|
f, err := NewFormatEncoder(EncoderConfig{
|
||||||
|
TemplatePath: "test-fixtures/legacy/csv.template",
|
||||||
|
Legacy: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||||
|
testutil.EncoderSnapshotTestConfig{
|
||||||
|
Subject: testutil.DirectoryInput(t, t.TempDir()),
|
||||||
|
Format: f,
|
||||||
|
UpdateSnapshot: *updateSnapshot,
|
||||||
|
PersistRedactionsInSnapshot: true,
|
||||||
|
IsJSON: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatWithOptionAndHasField_Legacy(t *testing.T) {
|
||||||
|
f, err := NewFormatEncoder(EncoderConfig{
|
||||||
|
TemplatePath: "test-fixtures/legacy/csv-hasField.template",
|
||||||
|
Legacy: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||||
|
testutil.EncoderSnapshotTestConfig{
|
||||||
|
Subject: testutil.DirectoryInputWithAuthorField(t),
|
||||||
|
Format: f,
|
||||||
|
UpdateSnapshot: *updateSnapshot,
|
||||||
|
PersistRedactionsInSnapshot: true,
|
||||||
|
IsJSON: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func TestFormatWithOption(t *testing.T) {
|
func TestFormatWithOption(t *testing.T) {
|
||||||
f, err := NewFormatEncoder(EncoderConfig{
|
f, err := NewFormatEncoder(EncoderConfig{
|
||||||
TemplatePath: "test-fixtures/csv.template",
|
TemplatePath: "test-fixtures/csv.template",
|
||||||
@ -44,7 +80,6 @@ func TestFormatWithOptionAndHasField(t *testing.T) {
|
|||||||
IsJSON: false,
|
IsJSON: false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFormatWithoutOptions(t *testing.T) {
|
func TestFormatWithoutOptions(t *testing.T) {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
"Package","Version Installed","Found by","Author"
|
"Package","Version Installed","Found by","Author"
|
||||||
{{- range .Artifacts}}
|
{{- range .artifacts}}
|
||||||
"{{.Name}}","{{.Version}}","{{.FoundBy}}","{{ if hasField .Metadata "Author" }}{{.Metadata.Author}}{{ else }}NO AUTHOR SUPPLIED{{end}}"
|
"{{.name}}","{{.version}}","{{.foundBy}}","{{ if index .metadata "author" }}{{.metadata.author}}{{ else }}NO AUTHOR SUPPLIED{{end}}"
|
||||||
{{- end}}
|
{{- end}}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
"Package","Version Installed", "Found by"
|
"Package","Version Installed", "Found by"
|
||||||
{{- range .Artifacts}}
|
{{- range .artifacts}}
|
||||||
"{{.Name}}","{{.Version}}","{{.FoundBy}}"
|
"{{.name}}","{{.version}}","{{.foundBy}}"
|
||||||
{{- end}}
|
{{- end}}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
"Package","Version Installed","Found by","Author"
|
||||||
|
{{- range .Artifacts}}
|
||||||
|
"{{.Name}}","{{.Version}}","{{.FoundBy}}","{{ if hasField .Metadata "Author" }}{{.Metadata.Author}}{{ else }}NO AUTHOR SUPPLIED{{end}}"
|
||||||
|
{{- end}}
|
||||||
4
syft/format/template/test-fixtures/legacy/csv.template
Normal file
4
syft/format/template/test-fixtures/legacy/csv.template
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
"Package","Version Installed", "Found by"
|
||||||
|
{{- range .Artifacts}}
|
||||||
|
"{{.Name}}","{{.Version}}","{{.FoundBy}}"
|
||||||
|
{{- end}}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
"Package","Version Installed","Found by","Author"
|
||||||
|
"package-1","1.0.1","the-cataloger-1","test-author"
|
||||||
|
"package-2","2.0.1","the-cataloger-2","NO AUTHOR SUPPLIED"
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
"Package","Version Installed", "Found by"
|
||||||
|
"package-1","1.0.1","the-cataloger-1"
|
||||||
|
"package-2","2.0.1","the-cataloger-2"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
"Package","Version Installed", "Found by"
|
"Package","Version Installed", "Found by"
|
||||||
{{- range .Artifacts}}
|
{{- range .artifacts}}
|
||||||
"{{.Name}}","{{.Version}}","{{.FoundBy}}"
|
"{{.name}}","{{.version}}","{{.foundBy}}"
|
||||||
{{- end}}
|
{{- end}}
|
||||||
Loading…
x
Reference in New Issue
Block a user