mirror of
https://github.com/anchore/syft.git
synced 2026-11-01 06:56:40 +01:00
130 lines
2.9 KiB
Go
130 lines
2.9 KiB
Go
package formats
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/anchore/syft/internal/log"
|
|
"github.com/anchore/syft/syft/formats/cyclonedxjson"
|
|
"github.com/anchore/syft/syft/formats/cyclonedxxml"
|
|
"github.com/anchore/syft/syft/formats/github"
|
|
"github.com/anchore/syft/syft/formats/spdxjson"
|
|
"github.com/anchore/syft/syft/formats/spdxtagvalue"
|
|
"github.com/anchore/syft/syft/formats/syftjson"
|
|
"github.com/anchore/syft/syft/formats/table"
|
|
"github.com/anchore/syft/syft/formats/template"
|
|
"github.com/anchore/syft/syft/formats/text"
|
|
"github.com/anchore/syft/syft/sbom"
|
|
)
|
|
|
|
func Formats() []sbom.Format {
|
|
return []sbom.Format{
|
|
syftjson.Format(),
|
|
cyclonedxxml.Format(),
|
|
cyclonedxjson.Format(),
|
|
github.Format(),
|
|
spdxtagvalue.Format(),
|
|
spdxjson.Format(),
|
|
table.Format(),
|
|
text.Format(),
|
|
template.Format(),
|
|
}
|
|
}
|
|
|
|
func Identify(by []byte) sbom.Format {
|
|
for _, f := range Formats() {
|
|
if err := f.Validate(bytes.NewReader(by)); err != nil {
|
|
if !errors.Is(err, sbom.ErrValidationNotSupported) {
|
|
log.Debugf("format %s returned err: %+v", f.ID(), err)
|
|
}
|
|
continue
|
|
}
|
|
return f
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ByName(name string) sbom.Format {
|
|
cleanName := cleanFormatName(name)
|
|
for _, f := range Formats() {
|
|
if cleanFormatName(string(f.ID())) == cleanName {
|
|
return f
|
|
}
|
|
}
|
|
|
|
// handle any aliases for any supported format
|
|
switch cleanName {
|
|
case "json", "syftjson":
|
|
return ByID(syftjson.ID)
|
|
case "cyclonedx", "cyclone", "cyclonedxxml":
|
|
return ByID(cyclonedxxml.ID)
|
|
case "cyclonedxjson":
|
|
return ByID(cyclonedxjson.ID)
|
|
case "github", "githubjson":
|
|
return ByID(github.ID)
|
|
case "spdx", "spdxtv", "spdxtagvalue":
|
|
return ByID(spdxtagvalue.ID)
|
|
case "spdxjson":
|
|
return ByID(spdxjson.ID)
|
|
case "table":
|
|
return ByID(table.ID)
|
|
case "text":
|
|
return ByID(text.ID)
|
|
case "template":
|
|
ByID(template.ID)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func IDs() (ids []sbom.FormatID) {
|
|
for _, f := range Formats() {
|
|
ids = append(ids, f.ID())
|
|
}
|
|
return ids
|
|
}
|
|
|
|
func ByID(id sbom.FormatID) sbom.Format {
|
|
for _, f := range Formats() {
|
|
if f.ID() == id {
|
|
return f
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cleanFormatName(name string) string {
|
|
r := strings.NewReplacer("-", "", "_", "")
|
|
return strings.ToLower(r.Replace(name))
|
|
}
|
|
|
|
// Encode takes all SBOM elements and a format option and encodes an SBOM document.
|
|
func Encode(s sbom.SBOM, f sbom.Format) ([]byte, error) {
|
|
buff := bytes.Buffer{}
|
|
|
|
if err := f.Encode(&buff, s); err != nil {
|
|
return nil, fmt.Errorf("unable to encode sbom: %w", err)
|
|
}
|
|
|
|
return buff.Bytes(), nil
|
|
}
|
|
|
|
// Decode takes a reader for an SBOM and generates all internal SBOM elements.
|
|
func Decode(reader io.Reader) (*sbom.SBOM, sbom.Format, error) {
|
|
by, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to read sbom: %w", err)
|
|
}
|
|
|
|
f := Identify(by)
|
|
if f == nil {
|
|
return nil, nil, fmt.Errorf("unable to identify format")
|
|
}
|
|
|
|
s, err := f.Decode(bytes.NewReader(by))
|
|
return s, f, err
|
|
}
|