Compare commits

...
5 Commits
Author SHA1 Message Date
Alex Goodman da0b17b719 Add cataloging of macho multi-architecture binaries (#657)
* add cataloging within universal binaries

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update json test fixtures

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add comments + correct 32 bit multi arch magic check

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2021-12-08 16:25:24 -05:00
Alex Goodman c7752f0f6c fix panic from reading nonexistent reader (#658)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2021-12-08 20:28:47 +00:00
Bala Raman 7a359dc16b Adding AlmaLinux OS Support (#652)
* Adding AlmaLinux Support

Signed-off-by: Bala Raman <srbala@gmail.com>

* Adding AlmaLinux Support

Signed-off-by: Bala Raman <srbala@gmail.com>
2021-12-07 15:59:31 -05:00
Dan Luhring 7957294b26 Add section for community meetings (#655)
Signed-off-by: Dan Luhring <dan+github@luhrings.com>
2021-12-06 12:18:34 -05:00
Jonas Galvão Xavier 5374a1dc6f add cyclone-json output format (#635)
* add cyclone json format

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* adapt format to sbom.SBOM structure

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* cycloneDX json output with official lib

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* add cycloneDX 1.3 schema output in xml

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* fix lints errors

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* tidying go mod

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* remove cycloneDX 1.2 format

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* update cycloneDX xml schema

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* fix cyclone according to schema

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* use RFC 2141 URN form of uuid for serial number

add schema validation for cycloneDX 1.3 JSON output

add yajsv cli for JSON schema validation during tests

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* tidying go mod up

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* go get json schema validator

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* install yajsv without mess with go mod

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* reuse code between cycloneDX json & xml encoders

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* add output options for cyclone XML

add bom.json to .gitignore

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* add cyclone json format

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* adapt format to sbom.SBOM structure

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* cycloneDX json output with official lib

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* add cycloneDX 1.3 schema output in xml

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* fix lints errors

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* tidying go mod

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* remove cycloneDX 1.2 format

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* update cycloneDX xml schema

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* fix cyclone according to schema

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* use RFC 2141 URN form of uuid for serial number

add schema validation for cycloneDX 1.3 JSON output

add yajsv cli for JSON schema validation during tests

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* tidying go mod up

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* go get json schema validator

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* install yajsv without mess with go mod

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* reuse code between cycloneDX json & xml encoders

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* add output options for cyclone XML

add bom.json to .gitignore

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* fix cyclone12xml removal

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* feedback changes

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* go mod tidy

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>
2021-12-03 17:06:23 -08:00
52 changed files with 4071 additions and 1443 deletions
+1 -1
View File
@@ -82,7 +82,7 @@ help:
.PHONY: ci-bootstrap
ci-bootstrap:
DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y bc jq libxml2-utils
DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y bc jq libxml2-utils && go install github.com/neilpa/yajsv@latest
.PHONY:
ci-bootstrap-mac:
+6
View File
@@ -11,6 +11,12 @@
A CLI tool and Go library for generating a Software Bill of Materials (SBOM) from container images and filesystems. Exceptional for vulnerability detection when used with a scanner tool like [Grype](https://github.com/anchore/grype).
### Join our community meetings!
- Calendar: https://calendar.google.com/calendar/u/0/r?cid=Y182OTM4dGt0MjRtajI0NnNzOThiaGtnM29qNEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t
- Agenda: https://docs.google.com/document/d/1ZtSAa6fj2a6KRWviTn3WoJm09edvrNUp4Iz_dOjjyY8/edit?usp=sharing (join [this group](https://groups.google.com/g/anchore-oss-community) for write access)
- All are welcome!
![syft-demo](https://user-images.githubusercontent.com/590471/90277200-2a253000-de33-11ea-893f-32c219eea11a.gif)
## Features
+1
View File
@@ -3,6 +3,7 @@ module github.com/anchore/syft
go 1.16
require (
github.com/CycloneDX/cyclonedx-go v0.4.0
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/adrg/xdg v0.2.1
github.com/alecthomas/jsonschema v0.0.0-20210301060011-54c507b6f074
+4
View File
@@ -60,6 +60,8 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CycloneDX/cyclonedx-go v0.4.0 h1:Wz4QZ9B4RXGWIWTypVLEOVJgOdFfy5mcS5PGNzUkZxU=
github.com/CycloneDX/cyclonedx-go v0.4.0/go.mod h1:rmRcf//gT7PIzovatusbWi377xqCg1FS4jyST0GH20E=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
@@ -138,6 +140,8 @@ github.com/bmatcuk/doublestar/v2 v2.0.4/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQm
github.com/bmatcuk/doublestar/v4 v4.0.2 h1:X0krlUVAVmtr2cRoTqR8aDMrDqnB36ht8wpWTiQ3jsA=
github.com/bmatcuk/doublestar/v4 v4.0.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs=
github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+1 -1
View File
@@ -6,5 +6,5 @@ const (
// JSONSchemaVersion is the current schema version output by the JSON presenter
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "2.0.0"
JSONSchemaVersion = "2.0.1"
)
@@ -0,0 +1,93 @@
package cyclonedxhelpers
import (
"time"
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/version"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
"github.com/google/uuid"
)
func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
cdxBOM := cyclonedx.NewBOM()
versionInfo := version.FromBuild()
// NOTE(jonasagx): cycloneDX requires URN uuids (URN returns the RFC 2141 URN form of uuid):
// https://github.com/CycloneDX/specification/blob/master/schema/bom-1.3-strict.schema.json#L36
// "pattern": "^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
cdxBOM.SerialNumber = uuid.New().URN()
cdxBOM.Metadata = toBomDescriptor(internal.ApplicationName, versionInfo.Version, s.Source)
packages := s.Artifacts.PackageCatalog.Sorted()
components := make([]cyclonedx.Component, len(packages))
for i, p := range packages {
components[i] = toComponent(p)
}
cdxBOM.Components = &components
return cdxBOM
}
// NewBomDescriptor returns a new BomDescriptor tailored for the current time and "syft" tool details.
func toBomDescriptor(name, version string, srcMetadata source.Metadata) *cyclonedx.Metadata {
return &cyclonedx.Metadata{
Timestamp: time.Now().Format(time.RFC3339),
Tools: &[]cyclonedx.Tool{
{
Vendor: "anchore",
Name: name,
Version: version,
},
},
Component: toBomDescriptorComponent(srcMetadata),
}
}
func toComponent(p pkg.Package) cyclonedx.Component {
return cyclonedx.Component{
Type: cyclonedx.ComponentTypeLibrary,
Name: p.Name,
Version: p.Version,
PackageURL: p.PURL,
Licenses: toLicenses(p.Licenses),
}
}
func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component {
switch srcMetadata.Scheme {
case source.ImageScheme:
return &cyclonedx.Component{
Type: cyclonedx.ComponentTypeContainer,
Name: srcMetadata.ImageMetadata.UserInput,
Version: srcMetadata.ImageMetadata.ManifestDigest,
}
case source.DirectoryScheme, source.FileScheme:
return &cyclonedx.Component{
Type: cyclonedx.ComponentTypeFile,
Name: srcMetadata.Path,
}
}
return nil
}
func toLicenses(ls []string) *cyclonedx.Licenses {
if len(ls) == 0 {
return nil
}
lc := make(cyclonedx.Licenses, len(ls))
for i, licenseName := range ls {
lc[i] = cyclonedx.LicenseChoice{
License: &cyclonedx.License{
Name: licenseName,
},
}
}
return &lc
}
@@ -87,6 +87,8 @@ func AssertPresenterAgainstGoldenSnapshot(t *testing.T, pres presenter.Presenter
if !bytes.Equal(expected, actual) {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(string(expected), string(actual), true)
t.Logf("len: %d\nexpected: %s", len(expected), expected)
t.Logf("len: %d\nactual: %s", len(actual), actual)
t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs))
}
}
@@ -1,26 +0,0 @@
package cyclonedx12xml
import (
"encoding/xml"
"io"
"github.com/anchore/syft/syft/sbom"
)
func encoder(output io.Writer, s sbom.SBOM) error {
enc := xml.NewEncoder(output)
enc.Indent("", " ")
_, err := output.Write([]byte(xml.Header))
if err != nil {
return err
}
err = enc.Encode(toFormatModel(s))
if err != nil {
return err
}
_, err = output.Write([]byte("\n"))
return err
}
@@ -1,31 +0,0 @@
package model
import (
"encoding/xml"
)
// Source: https://cyclonedx.org/ext/bom-descriptor/
// BomDescriptor represents all metadata surrounding the BOM report (such as when the BOM was made, with which tool, and the item being cataloged).
type BomDescriptor struct {
XMLName xml.Name `xml:"metadata"`
Timestamp string `xml:"timestamp,omitempty"` // The date and time (timestamp) when the document was created
Tools []BomDescriptorTool `xml:"tools>tool"` // The tool used to create the BOM.
Component *BomDescriptorComponent `xml:"component"` // The component that the BOM describes.
}
// BomDescriptorTool represents the tool that created the BOM report.
type BomDescriptorTool struct {
XMLName xml.Name `xml:"tool"`
Vendor string `xml:"vendor,omitempty"` // The vendor of the tool used to create the BOM.
Name string `xml:"name,omitempty"` // The name of the tool used to create the BOM.
Version string `xml:"version,omitempty"` // The version of the tool used to create the BOM.
// TODO: hashes, author, manufacture, supplier
// TODO: add user-defined fields for the remaining build/version parameters
}
// BomDescriptorComponent represents the software/package being cataloged.
type BomDescriptorComponent struct {
XMLName xml.Name `xml:"component"`
Component
}
@@ -1,20 +0,0 @@
package model
import "encoding/xml"
// Component represents a single element in the CycloneDX BOM
type Component struct {
XMLName xml.Name `xml:"component"`
Type string `xml:"type,attr"` // Required; Describes if the component is a library, framework, application, container, operating system, firmware, hardware device, or file
Supplier string `xml:"supplier,omitempty"` // The organization that supplied the component. The supplier may often be the manufacture, but may also be a distributor or repackager.
Author string `xml:"author,omitempty"` // The person(s) or organization(s) that authored the component
Publisher string `xml:"publisher,omitempty"` // The person(s) or organization(s) that published the component
Group string `xml:"group,omitempty"` // The high-level classification that a project self-describes as. This will often be a shortened, single name of the company or project that produced the component, or the source package or domain name.
Name string `xml:"name"` // Required; The name of the component as defined by the project
Version string `xml:"version"` // Required; The version of the component as defined by the project
Description string `xml:"description,omitempty"` // A description of the component
Licenses *[]License `xml:"licenses>license"` // A node describing zero or more license names, SPDX license IDs or expressions
PackageURL string `xml:"purl,omitempty"` // Specifies the package-url (PackageURL). The purl, if specified, must be valid and conform to the specification defined at: https://github.com/package-url/purl-spec
// TODO: source, hashes, copyright, cpe, purl, swid, modified, pedigree, externalReferences
// TODO: add user-defined parameters for syft-specific values (image layer index, cataloger, location path, etc.)
}
@@ -1,17 +0,0 @@
package model
import (
"encoding/xml"
)
// Source: https://github.com/CycloneDX/specification
// Document represents a CycloneDX BOM Document.
type Document struct {
XMLName xml.Name `xml:"bom"`
XMLNs string `xml:"xmlns,attr"`
Version int `xml:"version,attr"`
SerialNumber string `xml:"serialNumber,attr"`
BomDescriptor *BomDescriptor `xml:"metadata"` // The BOM descriptor extension
Components []Component `xml:"components>component"` // The BOM contents
}
@@ -1,10 +0,0 @@
package model
import "encoding/xml"
// License represents a single software license for a Component
type License struct {
XMLName xml.Name `xml:"license"`
ID string `xml:"id,omitempty"` // A valid SPDX license ID
Name string `xml:"name,omitempty"` // If SPDX does not define the license used, this field may be used to provide the license name
}
@@ -1,95 +0,0 @@
package cyclonedx12xml
import (
"encoding/xml"
"time"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/formats/cyclonedx12xml/model"
"github.com/anchore/syft/internal/version"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
"github.com/google/uuid"
)
// toFormatModel creates and populates a new in-memory representation of a CycloneDX 1.2 document
func toFormatModel(s sbom.SBOM) model.Document {
versionInfo := version.FromBuild()
doc := model.Document{
XMLNs: "http://cyclonedx.org/schema/bom/1.2",
Version: 1,
SerialNumber: uuid.New().URN(),
BomDescriptor: toBomDescriptor(internal.ApplicationName, versionInfo.Version, s.Source),
}
// attach components
for _, p := range s.Artifacts.PackageCatalog.Sorted() {
doc.Components = append(doc.Components, toComponent(p))
}
return doc
}
func toComponent(p pkg.Package) model.Component {
return model.Component{
Type: "library", // TODO: this is not accurate
Name: p.Name,
Version: p.Version,
PackageURL: p.PURL,
Licenses: toLicenses(p.Licenses),
}
}
// NewBomDescriptor returns a new BomDescriptor tailored for the current time and "syft" tool details.
func toBomDescriptor(name, version string, srcMetadata source.Metadata) *model.BomDescriptor {
return &model.BomDescriptor{
XMLName: xml.Name{},
Timestamp: time.Now().Format(time.RFC3339),
Tools: []model.BomDescriptorTool{
{
Vendor: "anchore",
Name: name,
Version: version,
},
},
Component: toBomDescriptorComponent(srcMetadata),
}
}
func toBomDescriptorComponent(srcMetadata source.Metadata) *model.BomDescriptorComponent {
switch srcMetadata.Scheme {
case source.ImageScheme:
return &model.BomDescriptorComponent{
Component: model.Component{
Type: "container",
Name: srcMetadata.ImageMetadata.UserInput,
Version: srcMetadata.ImageMetadata.ManifestDigest,
},
}
case source.DirectoryScheme, source.FileScheme:
return &model.BomDescriptorComponent{
Component: model.Component{
Type: "file",
Name: srcMetadata.Path,
},
}
}
return nil
}
func toLicenses(licenses []string) *[]model.License {
if len(licenses) == 0 {
return nil
}
var result []model.License
for _, licenseName := range licenses {
result = append(result, model.License{
Name: licenseName,
})
}
return &result
}
@@ -0,0 +1,18 @@
package cyclonedx13json
import (
"io"
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/syft/internal/formats/common/cyclonedxhelpers"
"github.com/anchore/syft/syft/sbom"
)
func encoder(output io.Writer, s sbom.SBOM) error {
bom := cyclonedxhelpers.ToFormatModel(s)
enc := cyclonedx.NewBOMEncoder(output, cyclonedx.BOMFileFormatJSON)
enc.SetPretty(true)
err := enc.Encode(bom)
return err
}
@@ -0,0 +1,39 @@
package cyclonedx13json
import (
"flag"
"regexp"
"testing"
"github.com/anchore/syft/internal/formats/common/testutils"
)
var updateCycloneDx = flag.Bool("update-cyclonedx", false, "update the *.golden files for cyclone-dx presenters")
func TestCycloneDxDirectoryPresenter(t *testing.T) {
testutils.AssertPresenterAgainstGoldenSnapshot(t,
Format().Presenter(testutils.DirectoryInput(t)),
*updateCycloneDx,
cycloneDxRedactor,
)
}
func TestCycloneDxImagePresenter(t *testing.T) {
testImage := "image-simple"
testutils.AssertPresenterAgainstGoldenImageSnapshot(t,
Format().Presenter(testutils.ImageInput(t, testImage)),
testImage,
*updateCycloneDx,
cycloneDxRedactor,
)
}
func cycloneDxRedactor(s []byte) []byte {
serialPattern := regexp.MustCompile(`urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`)
rfc3339Pattern := regexp.MustCompile(`([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))`)
for _, pattern := range []*regexp.Regexp{serialPattern, rfc3339Pattern} {
s = pattern.ReplaceAll(s, []byte("redacted"))
}
return s
}
@@ -0,0 +1,12 @@
package cyclonedx13json
import "github.com/anchore/syft/syft/format"
func Format() format.Format {
return format.NewFormat(
format.CycloneDxJSONOption,
encoder,
nil,
nil,
)
}
@@ -0,0 +1,42 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"serialNumber": "urn:uuid:a81dc685-cf22-48e0-bda5-65ea1a8bca5b",
"version": 1,
"metadata": {
"timestamp": "2021-12-03T13:17:26-08:00",
"tools": [
{
"vendor": "anchore",
"name": "syft",
"version": "[not provided]"
}
],
"component": {
"type": "file",
"name": "/some/path",
"version": ""
}
},
"components": [
{
"type": "library",
"name": "package-1",
"version": "1.0.1",
"licenses": [
{
"license": {
"name": "MIT"
}
}
],
"purl": "a-purl-2"
},
{
"type": "library",
"name": "package-2",
"version": "2.0.1",
"purl": "a-purl-2"
}
]
}
@@ -0,0 +1,42 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.3",
"serialNumber": "urn:uuid:2156ac1f-c838-4e93-8dc5-a3874ffeb967",
"version": 1,
"metadata": {
"timestamp": "2021-12-03T13:17:26-08:00",
"tools": [
{
"vendor": "anchore",
"name": "syft",
"version": "[not provided]"
}
],
"component": {
"type": "container",
"name": "user-image-input",
"version": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
}
},
"components": [
{
"type": "library",
"name": "package-1",
"version": "1.0.1",
"licenses": [
{
"license": {
"name": "MIT"
}
}
],
"purl": "a-purl-1"
},
{
"type": "library",
"name": "package-2",
"version": "2.0.1",
"purl": "a-purl-2"
}
]
}
@@ -0,0 +1,18 @@
package cyclonedx13xml
import (
"io"
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/syft/internal/formats/common/cyclonedxhelpers"
"github.com/anchore/syft/syft/sbom"
)
func encoder(output io.Writer, s sbom.SBOM) error {
bom := cyclonedxhelpers.ToFormatModel(s)
enc := cyclonedx.NewBOMEncoder(output, cyclonedx.BOMFileFormatXML)
enc.SetPretty(true)
err := enc.Encode(bom)
return err
}
@@ -1,4 +1,4 @@
package cyclonedx12xml
package cyclonedx13xml
import (
"flag"
@@ -1,10 +1,10 @@
package cyclonedx12xml
package cyclonedx13xml
import "github.com/anchore/syft/syft/format"
func Format() format.Format {
return format.NewFormat(
format.CycloneDxOption,
format.CycloneDxXMLOption,
encoder,
nil,
nil,
@@ -0,0 +1,4 @@
# Note: changes to this file will result in updating several test values. Consider making a new image fixture instead of editing this one.
FROM scratch
ADD file-1.txt /somefile-1.txt
ADD file-2.txt /somefile-2.txt
@@ -0,0 +1 @@
this file has contents
@@ -0,0 +1 @@
file-2 contents!
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" version="1" serialNumber="urn:uuid:5404937f-72d6-44a2-8e9b-954305ecb4f6">
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:7b1c3b1d-ea3b-4022-9dcc-80f4b4cbce36" version="1">
<metadata>
<timestamp>2021-06-23T13:40:33-04:00</timestamp>
<timestamp>2021-12-03T13:16:45-08:00</timestamp>
<tools>
<tool>
<vendor>anchore</vendor>
@@ -31,4 +31,4 @@
<purl>a-purl-2</purl>
</component>
</components>
</bom>
</bom>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" version="1" serialNumber="urn:uuid:e34bad2e-cd27-483c-86dc-f4e26d6103b0">
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:66bda3e1-888a-4d43-b906-7fd96d428753" version="1">
<metadata>
<timestamp>2021-06-23T13:40:33-04:00</timestamp>
<timestamp>2021-12-03T13:16:45-08:00</timestamp>
<tools>
<tool>
<vendor>anchore</vendor>
@@ -31,4 +31,4 @@
<purl>a-purl-2</purl>
</component>
</components>
</bom>
</bom>
+4 -2
View File
@@ -3,7 +3,8 @@ package formats
import (
"bytes"
"github.com/anchore/syft/internal/formats/cyclonedx12xml"
"github.com/anchore/syft/internal/formats/cyclonedx13json"
"github.com/anchore/syft/internal/formats/cyclonedx13xml"
"github.com/anchore/syft/internal/formats/spdx22json"
"github.com/anchore/syft/internal/formats/spdx22tagvalue"
"github.com/anchore/syft/internal/formats/syftjson"
@@ -17,7 +18,8 @@ func All() []format.Format {
return []format.Format{
syftjson.Format(),
table.Format(),
cyclonedx12xml.Format(),
cyclonedx13xml.Format(),
cyclonedx13json.Format(),
spdx22json.Format(),
spdx22tagvalue.Format(),
text.Format(),
@@ -83,7 +83,7 @@
}
},
"schema": {
"version": "2.0.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.0.json"
"version": "2.0.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.1.json"
}
}
@@ -179,7 +179,7 @@
}
},
"schema": {
"version": "2.0.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.0.json"
"version": "2.0.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.1.json"
}
}
@@ -104,7 +104,7 @@
}
},
"schema": {
"version": "2.0.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.0.json"
"version": "2.0.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.1.json"
}
}
+2 -1
View File
@@ -1 +1,2 @@
bom.xml
bom.xml
bom.json
+3 -1
View File
@@ -2,4 +2,6 @@
.PHONY: validate-schema
validate-schema:
go run ../../main.go ubuntu:latest -vv -o cyclonedx > bom.xml
xmllint --noout --schema ./cyclonedx.xsd bom.xml
xmllint --noout --schema ./cyclonedx.xsd bom.xml
go run ../../main.go ubuntu:latest -vv -o cyclonedx-json > bom.json
yajsv -s bom-1.3.schema.json bom.json
File diff suppressed because it is too large Load Diff
+269 -56
View File
@@ -16,13 +16,13 @@ limitations under the License.
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
xmlns:bom="http://cyclonedx.org/schema/bom/1.2"
xmlns:bom="http://cyclonedx.org/schema/bom/1.3"
xmlns:spdx="http://cyclonedx.org/schema/spdx"
elementFormDefault="qualified"
targetNamespace="http://cyclonedx.org/schema/bom/1.2"
targetNamespace="http://cyclonedx.org/schema/bom/1.3"
vc:minVersion="1.0"
vc:maxVersion="1.1"
version="1.2">
version="1.3">
<xs:import namespace="http://cyclonedx.org/schema/spdx" schemaLocation="spdx.xsd"/>
@@ -32,9 +32,6 @@ limitations under the License.
<url>https://cyclonedx.org/</url>
<license uri="http://www.apache.org/licenses/LICENSE-2.0"
version="2.0">Apache License, Version 2.0</license>
<authors>
<author>Steve Springett</author>
</authors>
</xs:documentation>
</xs:annotation>
@@ -71,15 +68,23 @@ limitations under the License.
<xs:documentation>The component that the BOM describes.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="manufacture" type="bom:organizationalEntity" minOccurs="0" maxOccurs="unbounded">
<xs:element name="manufacture" type="bom:organizationalEntity" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>The organization that manufactured the component that the BOM describes.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="supplier" type="bom:organizationalEntity" minOccurs="0" maxOccurs="unbounded">
<xs:element name="supplier" type="bom:organizationalEntity" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>The organization that supplied the component that the BOM describes. The
supplier may often be the manufacture, but may also be a distributor or repackager.</xs:documentation>
supplier may often be the manufacturer, but may also be a distributor or repackager.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="licenses" type="bom:licenseChoiceType" minOccurs="0" maxOccurs="1"/>
<xs:element name="properties" type="bom:propertiesType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Provides the ability to document properties in a key/value store.
This provides flexibility to include data not officially supported in the standard
without having to use additional namespaces or create extensions.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
@@ -181,14 +186,14 @@ limitations under the License.
<xs:documentation>The name of the contact</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="email" type="xs:normalizedString" minOccurs="0" maxOccurs="unbounded">
<xs:element name="email" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>The email address of the contact. Multiple email addresses are allowed.</xs:documentation>
<xs:documentation>The email address of the contact.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="phone" type="xs:normalizedString" minOccurs="0" maxOccurs="unbounded">
<xs:element name="phone" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>The phone number of the contact. Multiple phone numbers are allowed.</xs:documentation>
<xs:documentation>The phone number of the contact.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
@@ -231,7 +236,7 @@ limitations under the License.
<xs:element name="supplier" type="bom:organizationalEntity" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>The organization that supplied the component. The supplier may often
be the manufacture, but may also be a distributor or repackager.</xs:documentation>
be the manufacturer, but may also be a distributor or repackager.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="author" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
@@ -282,19 +287,7 @@ limitations under the License.
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="licenses" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:choice>
<xs:element name="license" type="bom:licenseType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="expression" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>A valid SPDX license expression.
Refer to https://spdx.org/specifications for syntax requirements</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
<xs:element name="licenses" type="bom:licenseChoiceType" minOccurs="0" maxOccurs="1"/>
<xs:element name="copyright" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>An optional copyright notice informing users of the underlying claims to
@@ -349,6 +342,13 @@ limitations under the License.
component or to the project the component describes.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="properties" type="bom:propertiesType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Provides the ability to document properties in a key/value store.
This provides flexibility to include data not officially supported in the standard
without having to use additional namespaces or create extensions.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="components" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
@@ -370,6 +370,11 @@ limitations under the License.
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="evidence" type="bom:componentEvidenceType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Provides the ability to document evidence collected through various forms of extraction or analysis.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
@@ -512,45 +517,45 @@ limitations under the License.
<xs:simpleType name="classification">
<xs:restriction base="xs:string">
<xs:enumeration value="application">
<xs:annotation>
<xs:documentation>A software application. Refer to https://en.wikipedia.org/wiki/Application_software
<xs:annotation>
<xs:documentation>A software application. Refer to https://en.wikipedia.org/wiki/Application_software
for information about applications.</xs:documentation>
</xs:annotation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="framework">
<xs:annotation>
<xs:documentation>A software framework. Refer to https://en.wikipedia.org/wiki/Software_framework
for information on how frameworks vary slightly from libraries.</xs:documentation>
for information on how frameworks vary slightly from libraries.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="library">
<xs:annotation>
<xs:documentation>A software library. Refer to https://en.wikipedia.org/wiki/Library_(computing)
for information about libraries. All third-party and open source reusable components will likely
be a library. If the library also has key features of a framework, then it should be classified
as a framework. If not, or is unknown, then specifying library is recommended.</xs:documentation>
for information about libraries. All third-party and open source reusable components will likely
be a library. If the library also has key features of a framework, then it should be classified
as a framework. If not, or is unknown, then specifying library is recommended.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="container">
<xs:annotation>
<xs:documentation>A packaging and/or runtime format, not specific to any particular technology,
which isolates software inside the container from software outside of a container through
virtualization technology. Refer to https://en.wikipedia.org/wiki/OS-level_virtualization</xs:documentation>
which isolates software inside the container from software outside of a container through
virtualization technology. Refer to https://en.wikipedia.org/wiki/OS-level_virtualization</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="operating-system">
<xs:annotation>
<xs:documentation>A software operating system without regard to deployment model
(i.e. installed on physical hardware, virtual machine, image, etc) Refer to
https://en.wikipedia.org/wiki/Operating_system</xs:documentation>
(i.e. installed on physical hardware, virtual machine, image, etc) Refer to
https://en.wikipedia.org/wiki/Operating_system</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="device">
<xs:annotation>
<xs:documentation>A hardware device such as a processor, or chip-set. A hardware device
containing firmware should include a component for the physical hardware itself, and another
component of type 'firmware' or 'operating-system' (whichever is relevant), describing
information about the software running on the device.</xs:documentation>
containing firmware should include a component for the physical hardware itself, and another
component of type 'firmware' or 'operating-system' (whichever is relevant), describing
information about the software running on the device.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="firmware">
@@ -562,7 +567,7 @@ limitations under the License.
<xs:enumeration value="file">
<xs:annotation>
<xs:documentation>A computer file. Refer to https://en.wikipedia.org/wiki/Computer_file
for information about files.</xs:documentation>
for information about files.</xs:documentation>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
@@ -782,6 +787,13 @@ limitations under the License.
<xs:documentation xml:lang="en">An optional comment describing the external reference</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="hashes" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="hash" type="bom:hashType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="type" type="bom:externalReferenceType" use="required">
<xs:annotation>
@@ -927,7 +939,7 @@ limitations under the License.
<xs:enumeration value="backport">
<xs:annotation>
<xs:documentation>A patch which takes code from a newer version of software and applies
it to older versions of the same software. Refer to https://en.wikipedia.org/wiki/Backporting</xs:documentation>
it to older versions of the same software. Refer to https://en.wikipedia.org/wiki/Backporting</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="cherry-pick">
@@ -1255,24 +1267,19 @@ limitations under the License.
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="licenses" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:choice>
<xs:element name="license" type="bom:licenseType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="expression" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>A valid SPDX license expression.
Refer to https://spdx.org/specifications for syntax requirements</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
<xs:element name="licenses" type="bom:licenseChoiceType" minOccurs="0" maxOccurs="1"/>
<xs:element name="externalReferences" type="bom:externalReferences" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Provides the ability to document external references related to the service.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="properties" type="bom:propertiesType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Provides the ability to document properties in a key/value store.
This provides flexibility to include data not officially supported in the standard
without having to use additional namespaces or create extensions.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="services" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
@@ -1349,6 +1356,199 @@ limitations under the License.
</xs:restriction>
</xs:simpleType>
<xs:complexType name="licenseChoiceType">
<xs:choice>
<xs:element name="license" type="bom:licenseType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="expression" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>A valid SPDX license expression.
Refer to https://spdx.org/specifications for syntax requirements</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice>
</xs:complexType>
<xs:complexType name="copyrightsType">
<xs:sequence>
<xs:element name="text" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="componentEvidenceType">
<xs:sequence>
<xs:element name="licenses" type="bom:licenseChoiceType" minOccurs="0" maxOccurs="1"/>
<xs:element name="copyright" type="bom:copyrightsType" minOccurs="0" maxOccurs="1"/>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Allows any undeclared elements as long as the elements are placed in a different namespace.
</xs:documentation>
</xs:annotation>
</xs:any>
</xs:sequence>
<xs:anyAttribute namespace="##any" processContents="lax">
<xs:annotation>
<xs:documentation>User-defined attributes may be used on this element as long as they
do not have the same name as an existing attribute used by the schema.</xs:documentation>
</xs:annotation>
</xs:anyAttribute>
</xs:complexType>
<xs:complexType name="compositionsType">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="composition" type="bom:compositionType"/>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Allows any undeclared elements as long as the elements are placed in a different namespace.
</xs:documentation>
</xs:annotation>
</xs:any>
</xs:sequence>
<xs:anyAttribute namespace="##any" processContents="lax">
<xs:annotation>
<xs:documentation>User-defined attributes may be used on this element as long as they
do not have the same name as an existing attribute used by the schema.</xs:documentation>
</xs:annotation>
</xs:anyAttribute>
</xs:complexType>
<xs:complexType name="compositionType">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="aggregate" type="bom:aggregateType" default="not_specified">
<xs:annotation>
<xs:documentation>Specifies an aggregate type that describe how complete a relationship is.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="assemblies" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
The bom-ref identifiers of the components or services being described. Assemblies refer to
nested relationships whereby a constituent part may include other constituent parts. References
do not cascade to child parts. References are explicit for the specified constituent part only.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="assembly" type="bom:bomReferenceType"/>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Allows any undeclared elements as long as the elements are placed in a different namespace.
</xs:documentation>
</xs:annotation>
</xs:any>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="dependencies" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
The bom-ref identifiers of the components or services being described. Dependencies refer to a
relationship whereby an independent constituent part requires another independent constituent
part. References do not cascade to transitive dependencies. References are explicit for the
specified dependency only.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="dependency" type="bom:bomReferenceType"/>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Allows any undeclared elements as long as the elements are placed in a different namespace.
</xs:documentation>
</xs:annotation>
</xs:any>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="aggregateType">
<xs:restriction base="xs:string">
<xs:enumeration value="complete">
<xs:annotation>
<xs:documentation>The relationship is complete. No further relationships including constituent components, services, or dependencies exist.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="incomplete">
<xs:annotation>
<xs:documentation>The relationship is incomplete. Additional relationships exist and may include constituent components, services, or dependencies.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="incomplete_first_party_only">
<xs:annotation>
<xs:documentation>The relationship is incomplete. Only relationships for first-party components, services, or their dependencies are represented.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="incomplete_third_party_only">
<xs:annotation>
<xs:documentation>The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are represented.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="unknown">
<xs:annotation>
<xs:documentation>The relationship may be complete or incomplete. This usually signifies a 'best-effort' to obtain constituent components, services, or dependencies but the completeness is inconclusive.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="not_specified">
<xs:annotation>
<xs:documentation>The relationship completeness is not specified.</xs:documentation>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="bomReferenceType">
<xs:attribute name="ref" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>References a component or service by the its bom-ref attribute</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:anyAttribute namespace="##other" processContents="lax">
<xs:annotation>
<xs:documentation>User-defined attributes may be used on this element as long as they
do not have the same name as an existing attribute used by the schema.</xs:documentation>
</xs:annotation>
</xs:anyAttribute>
</xs:complexType>
<xs:complexType name="propertiesType">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="property" type="bom:propertyType"/>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Allows any undeclared elements as long as the elements are placed in a different namespace.
</xs:documentation>
</xs:annotation>
</xs:any>
</xs:sequence>
<xs:anyAttribute namespace="##any" processContents="lax">
<xs:annotation>
<xs:documentation>User-defined attributes may be used on this element as long as they
do not have the same name as an existing attribute used by the schema.</xs:documentation>
</xs:annotation>
</xs:anyAttribute>
</xs:complexType>
<xs:complexType name="propertyType">
<xs:annotation>
<xs:documentation>Specifies an individual property with a name and value.</xs:documentation>
</xs:annotation>
<xs:simpleContent>
<xs:extension base="xs:normalizedString">
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The name of the property. Duplicate names are allowed, each potentially having a different value.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:element name="bom">
<xs:complexType>
<xs:sequence>
@@ -1378,6 +1578,19 @@ limitations under the License.
<xs:documentation>Provides the ability to document dependency relationships.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="compositions" type="bom:compositionsType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Compositions describe constituent parts (including components, services, and dependency relationships) and their completeness.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="properties" type="bom:propertiesType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Provides the ability to document properties in a name-value store.
This provides flexibility to include data not officially supported in the standard
without having to use additional namespaces or create extensions. Unlike key-value
stores, properties support duplicate names, each potentially having different values.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
+1226 -1146
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -35,6 +35,7 @@ type artifactMetadataContainer struct {
Python pkg.PythonPackageMetadata
Rpm pkg.RpmdbMetadata
Cargo pkg.CargoPackageMetadata
Go pkg.GolangBinMetadata
}
func main() {
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -6,10 +6,9 @@ import (
"os"
"testing"
hashiVer "github.com/hashicorp/go-version"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/source"
hashiVer "github.com/hashicorp/go-version"
"github.com/stretchr/testify/assert"
)
@@ -128,6 +127,11 @@ func TestIdentifyDistro(t *testing.T) {
Type: RockyLinux,
Version: "8.4.0",
},
{
fixture: "test-fixtures/os/almalinux",
Type: AlmaLinux,
Version: "8.4.0",
},
}
observedDistros := internal.NewStringSet()
@@ -0,0 +1,15 @@
NAME="AlmaLinux"
VERSION="8.4 (Electric Cheetah)"
ID="almalinux"
ID_LIKE="rhel centos fedora"
VERSION_ID="8.4"
PLATFORM_ID="platform:el8"
PRETTY_NAME="AlmaLinux 8.4 (Electric Cheetah)"
ANSI_COLOR="0;34"
CPE_NAME="cpe:/o:almalinux:almalinux:8.4:GA"
HOME_URL="https://almalinux.org/"
DOCUMENTATION_URL="https://wiki.almalinux.org/"
BUG_REPORT_URL="https://bugs.almalinux.org/"
ALMALINUX_MANTISBT_PROJECT="AlmaLinux-8"
ALMALINUX_MANTISBT_PROJECT_VERSION="8.4"
+3
View File
@@ -22,6 +22,7 @@ const (
Windows Type = "windows"
Mariner Type = "mariner"
RockyLinux Type = "rockylinux"
AlmaLinux Type = "almalinux"
)
// All contains all Linux distribution options
@@ -42,6 +43,7 @@ var All = []Type{
Windows,
Mariner,
RockyLinux,
AlmaLinux,
}
// IDMapping connects a distro ID like "ubuntu" to a Distro type
@@ -62,6 +64,7 @@ var IDMapping = map[string]Type{
"windows": Windows,
"mariner": Mariner,
"rocky": RockyLinux,
"almalinux": AlmaLinux,
}
// String returns the string representation of the given Linux distribution.
+10 -4
View File
@@ -7,7 +7,8 @@ const (
JSONOption Option = "json"
TextOption Option = "text"
TableOption Option = "table"
CycloneDxOption Option = "cyclonedx"
CycloneDxXMLOption Option = "cyclonedx"
CycloneDxJSONOption Option = "cyclonedx-json"
SPDXTagValueOption Option = "spdx-tag-value"
SPDXJSONOption Option = "spdx-json"
)
@@ -16,7 +17,8 @@ var AllOptions = []Option{
JSONOption,
TextOption,
TableOption,
CycloneDxOption,
CycloneDxXMLOption,
CycloneDxJSONOption,
SPDXTagValueOption,
SPDXJSONOption,
}
@@ -31,8 +33,12 @@ func ParseOption(userStr string) Option {
return TextOption
case string(TableOption):
return TableOption
case string(CycloneDxOption), "cyclone", "cyclone-dx":
return CycloneDxOption
case string(CycloneDxXMLOption), "cyclone", "cyclone-dx", "cyclone-dx-xml", "cyclone-xml":
// NOTE(jonasagx): setting "cyclone" to XML by default for retro-compatibility.
// If we want to show no preference between XML and JSON please remove it.
return CycloneDxXMLOption
case string(CycloneDxJSONOption), "cyclone-json", "cyclone-dx-json":
return CycloneDxJSONOption
case string(SPDXTagValueOption), "spdx", "spdx-tagvalue", "spdxtagvalue", "spdx-tv", "spdxtv":
return SPDXTagValueOption
case string(SPDXJSONOption), "spdxjson":
+1 -1
View File
@@ -138,7 +138,7 @@ func getAdditionalFileListing(resolver source.FileResolver, dbLocation source.Lo
if conffilesReader != nil && conffilesLocation != nil {
defer internal.CloseAndLogError(conffilesReader, conffilesLocation.VirtualPath)
// attach the file list
files = append(files, parseDpkgConffileInfo(md5Reader)...)
files = append(files, parseDpkgConffileInfo(conffilesReader)...)
// keep a record of the file where this was discovered
locations = append(locations, *conffilesLocation)
+87 -4
View File
@@ -14,6 +14,7 @@ import (
"debug/pe"
"fmt"
"io"
"strings"
)
// An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF).
@@ -24,6 +25,9 @@ type exe interface {
// ReadData reads and returns up to size byte starting at virtual address addr.
ReadData(addr, size uint64) ([]byte, error)
// ArchName returns a string that represents the CPU architecture of the executable.
ArchName() string
// DataStart returns the writable data segment start address.
DataStart() uint64
}
@@ -32,7 +36,7 @@ type exe interface {
// we changed this signature from accpeting a string
// to a ReadCloser so we could adapt the code to the
// stereoscope api. We removed the file open methods.
func openExe(file io.ReadCloser) (exe, error) {
func openExe(file io.ReadCloser) ([]exe, error) {
/*
f, err := os.Open(file)
if err != nil {
@@ -57,7 +61,7 @@ func openExe(file io.ReadCloser) (exe, error) {
return nil, err
}
return &elfExe{file, e}, nil
return []exe{&elfExe{file, e}}, nil
}
if bytes.HasPrefix(data, []byte("MZ")) {
@@ -65,7 +69,7 @@ func openExe(file io.ReadCloser) (exe, error) {
if err != nil {
return nil, err
}
return &peExe{file, e}, nil
return []exe{&peExe{file, e}}, nil
}
if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) {
@@ -73,7 +77,20 @@ func openExe(file io.ReadCloser) (exe, error) {
if err != nil {
return nil, err
}
return &machoExe{file, e}, nil
return []exe{&machoExe{file, e}}, nil
}
// adding macho multi-architecture support (both for 64bit and 32 bit)... this case is not in the stdlib yet
if bytes.HasPrefix(data, []byte("\xCA\xFE\xBA\xBE")) || bytes.HasPrefix(data, []byte("\xCA\xFE\xBA\xBF")) {
fatExe, err := macho.NewFatFile(f)
if err != nil {
return nil, err
}
var exes []exe
for _, arch := range fatExe.Arches {
exes = append(exes, &machoExe{file, arch.File})
}
return exes, nil
}
return nil, fmt.Errorf("unrecognized executable format")
@@ -90,6 +107,14 @@ func (x *elfExe) Close() error {
return x.os.Close()
}
func (x *elfExe) ArchName() string {
return cleanElfArch(x.f.Machine)
}
func cleanElfArch(machine elf.Machine) string {
return strings.TrimPrefix(strings.ToLower(machine.String()), "em_")
}
func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) {
for _, prog := range x.f.Progs {
if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 {
@@ -132,6 +157,56 @@ func (x *peExe) Close() error {
return x.os.Close()
}
func (x *peExe) ArchName() string {
// from: debug/pe/pe.go
switch x.f.Machine {
case pe.IMAGE_FILE_MACHINE_AM33:
return "amd33"
case pe.IMAGE_FILE_MACHINE_AMD64:
return "amd64"
case pe.IMAGE_FILE_MACHINE_ARM:
return "arm"
case pe.IMAGE_FILE_MACHINE_ARMNT:
return "armnt"
case pe.IMAGE_FILE_MACHINE_ARM64:
return "arm64"
case pe.IMAGE_FILE_MACHINE_EBC:
return "ebc"
case pe.IMAGE_FILE_MACHINE_I386:
return "i386"
case pe.IMAGE_FILE_MACHINE_IA64:
return "ia64"
case pe.IMAGE_FILE_MACHINE_M32R:
return "m32r"
case pe.IMAGE_FILE_MACHINE_MIPS16:
return "mips16"
case pe.IMAGE_FILE_MACHINE_MIPSFPU:
return "mipsfpu"
case pe.IMAGE_FILE_MACHINE_MIPSFPU16:
return "mipsfpu16"
case pe.IMAGE_FILE_MACHINE_POWERPC:
return "ppc"
case pe.IMAGE_FILE_MACHINE_POWERPCFP:
return "ppcfp"
case pe.IMAGE_FILE_MACHINE_R4000:
return "r4000"
case pe.IMAGE_FILE_MACHINE_SH3:
return "sh3"
case pe.IMAGE_FILE_MACHINE_SH3DSP:
return "sh3dsp"
case pe.IMAGE_FILE_MACHINE_SH4:
return "sh4"
case pe.IMAGE_FILE_MACHINE_SH5:
return "sh5"
case pe.IMAGE_FILE_MACHINE_THUMB:
return "thumb"
case pe.IMAGE_FILE_MACHINE_WCEMIPSV2:
return "wcemipsv2"
default:
return fmt.Sprintf("unknown-pe-machine-%d", x.f.Machine)
}
}
func (x *peExe) imageBase() uint64 {
switch oh := x.f.OptionalHeader.(type) {
case *pe.OptionalHeader32:
@@ -193,6 +268,14 @@ func (x *machoExe) Close() error {
return x.os.Close()
}
func (x *machoExe) ArchName() string {
return cleanMachoArch(x.f.Cpu)
}
func cleanMachoArch(cpu macho.Cpu) string {
return strings.TrimPrefix(strings.ToLower(cpu.String()), "cpu")
}
func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) {
for _, load := range x.f.Loads {
seg, ok := load.(*macho.Segment)
+43
View File
@@ -0,0 +1,43 @@
package golang
import (
"debug/elf"
"debug/macho"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_cleanElfArch(t *testing.T) {
tests := []struct {
machine elf.Machine
want string
}{
{
machine: elf.EM_X86_64,
want: "x86_64",
},
}
for _, test := range tests {
t.Run(test.machine.String(), func(t *testing.T) {
assert.Equalf(t, test.want, cleanElfArch(test.machine), "cleanElfArch(%v)", test.machine)
})
}
}
func Test_cleanMachoArch(t *testing.T) {
tests := []struct {
cpu macho.Cpu
want string
}{
{
cpu: macho.CpuAmd64,
want: "amd64",
},
}
for _, test := range tests {
t.Run(test.cpu.String(), func(t *testing.T) {
assert.Equalf(t, test.want, cleanMachoArch(test.cpu), "cleanMachoArch(%v)", test.cpu)
})
}
}
+9 -5
View File
@@ -16,17 +16,20 @@ const (
func parseGoBin(location source.Location, reader io.ReadCloser) ([]pkg.Package, error) {
// Identify if bin was compiled by go
x, err := openExe(reader)
exes, err := openExe(reader)
if err != nil {
return nil, err
}
goVersion, mod := findVers(x)
return buildGoPkgInfo(location, mod, goVersion), nil
var pkgs []pkg.Package
for _, x := range exes {
goVersion, mod := findVers(x)
pkgs = append(pkgs, buildGoPkgInfo(location, mod, goVersion, x.ArchName())...)
}
return pkgs, nil
}
func buildGoPkgInfo(location source.Location, mod, goVersion string) []pkg.Package {
func buildGoPkgInfo(location source.Location, mod, goVersion, arch string) []pkg.Package {
pkgsSlice := make([]pkg.Package, 0)
scanner := bufio.NewScanner(strings.NewReader(mod))
@@ -52,6 +55,7 @@ func buildGoPkgInfo(location source.Location, mod, goVersion string) []pkg.Packa
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goVersion,
H1Digest: fields[3],
Architecture: arch,
},
})
}
+10 -3
View File
@@ -8,9 +8,11 @@ import (
"github.com/stretchr/testify/assert"
)
const goCompiledVersion = "1.17"
func TestBuildGoPkgInfo(t *testing.T) {
const (
goCompiledVersion = "1.17"
archDetails = "amd64"
)
tests := []struct {
name string
mod string
@@ -43,6 +45,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
H1Digest: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
},
},
@@ -62,6 +65,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
H1Digest: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
},
},
@@ -92,6 +96,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
H1Digest: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=",
},
},
@@ -111,6 +116,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
H1Digest: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
},
},
@@ -130,6 +136,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
H1Digest: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
},
},
@@ -146,7 +153,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
FileSystemID: "layer-id",
},
}
pkgs := buildGoPkgInfo(location, tt.mod, goCompiledVersion)
pkgs := buildGoPkgInfo(location, tt.mod, goCompiledVersion, archDetails)
assert.Equal(t, tt.expected, pkgs)
})
}
+3 -2
View File
@@ -2,6 +2,7 @@ package pkg
// GolangBinMetadata represents all captured data for a Golang Binary
type GolangBinMetadata struct {
GoCompiledVersion string
H1Digest string
GoCompiledVersion string `json:"goCompiledVersion"`
Architecture string `json:"architecture"`
H1Digest string `json:"h1Digest"`
}