Compare commits

...
11 Commits
Author SHA1 Message Date
Keith Zantow ee0a1d172c panic parsing cyclonedx (#892) 2022-03-16 09:10:44 -04:00
Keith Zantow b9b3ccecf9 Update register link text (#891) 2022-03-15 17:02:33 +00:00
mikey strauss 95271fb10d NPM PURLs are invalid (#832)
Signed-off-by: houdini91 <mdstrauss91@gmail.com>
2022-03-15 11:54:33 -04:00
Christopher Angelo Phillips 93d2d57cd3 update README with OSS Meetup information (#890)
Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2022-03-15 11:37:56 -04:00
Kenny Moens 7cd3201fe9 Support the .be top-level domain (#873)
Signed-off-by: Kenny Moens <kenny.moens@cipalschaubroeck.be>
2022-03-15 10:59:13 -04:00
Keith Zantow c7cf8b0b26 Brew install gon failed (#885) 2022-03-11 13:17:57 -05:00
Frankie G-J 44a6e00f7a Include vendored modules in Go Module package list (#883)
* include vendored modules in package slice

Signed-off-by: Frankie Gallina-Jones <frankieg@vmware.com>

* add explanatory comments

Signed-off-by: Frankie Gallina-Jones <frankieg@vmware.com>
2022-03-11 12:57:33 -05:00
Keith Zantow 6c8102bf28 Correct CycloneDX distro decoding (#745) 2022-03-11 09:27:18 -05:00
Keith Zantow 7789506dc6 Experimental GitHub export (#836) 2022-03-10 22:38:12 -05:00
Christopher Angelo Phillips fa03723617 Upgrade vault api from v1.3.1 to v1.4.1 (#878)
* move v1.3.1 => v1.4.1
* go mod tidy

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2022-03-09 15:11:07 -05:00
Alex Goodman 2946813a74 RPM Epoch should be optional in the json schema (#880) 2022-03-09 14:51:43 -05:00
26 changed files with 1844 additions and 61 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ notarize() {
fi
# install gon
which gon || (brew tap mitchellh/gon && brew install mitchellh/gon/gon)
which gon || (go install github.com/mitchellh/gon/cmd/gon@latest)
# create config (note: json via stdin with gon is broken, can only use HCL from file)
hcl_file=$(mktemp).hcl
+8
View File
@@ -11,6 +11,14 @@
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 Virtual OSS Meetup!
You are invited to join us on March 23rd, 11AM-Noon PT for our virtual open source meetup.
Hosts Priyanka “Pinky” Ravi of Weaveworks and Dan Luhring from Anchore OSS will explore how open source project Flux is utilizing Syft-generated SBOMs to secure the software supply chain.
[Register here!](https://get.anchore.com/anchore-oss-meetup-mar-23-2022/)
### Join our community meetings!
- Calendar: https://calendar.google.com/calendar/u/0/r?cid=Y182OTM4dGt0MjRtajI0NnNzOThiaGtnM29qNEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t
+2
View File
@@ -22,6 +22,8 @@ func formatAliases(ids ...sbom.FormatID) (aliases []string) {
aliases = append(aliases, "cyclonedx-xml")
case syft.CycloneDxJSONFormatID:
aliases = append(aliases, "cyclonedx-json")
case syft.GitHubID:
aliases = append(aliases, "github", "github-json")
default:
aliases = append(aliases, string(id))
}
+2 -2
View File
@@ -12,7 +12,7 @@ require (
github.com/anchore/go-rpmdb v0.0.0-20210914181456-a9c52348da63
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.0.0-20210922164639-b3fa992ebd29
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b
github.com/anchore/stereoscope v0.0.0-20220307154759-8a5a70c227d3
github.com/antihax/optional v1.0.0
github.com/bmatcuk/doublestar/v4 v4.0.2
@@ -171,7 +171,7 @@ require (
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/go-version v1.4.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/vault/api v1.3.1 // indirect
github.com/hashicorp/vault/api v1.4.1 // indirect
github.com/hashicorp/vault/sdk v0.4.1 // indirect
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
github.com/imdario/mergo v0.3.12 // indirect
+4 -3
View File
@@ -282,8 +282,8 @@ github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0v
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ=
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods=
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.0.0-20210922164639-b3fa992ebd29 h1:K9LfnxwhqvihqU0+MF325FNy7fsKV9EGaUxdfR4gnWk=
github.com/anchore/packageurl-go v0.0.0-20210922164639-b3fa992ebd29/go.mod h1:Oc1UkGaJwY6ND6vtAqPSlYrptKRJngHwkwB6W7l1uP0=
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b h1:YJWYt/6KQXR9JR46lLHrTTYi8rcye42tKcyjREA/hvA=
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
github.com/anchore/stereoscope v0.0.0-20220307154759-8a5a70c227d3 h1:Kx2jlMdENAf4cVjYGYLI+fiavVhzhtmU89GUYDITJ1w=
github.com/anchore/stereoscope v0.0.0-20220307154759-8a5a70c227d3/go.mod h1:XESZQTgFETDBatmyoet6XZ0zVknoIMDSAhj2INj2a5w=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
@@ -1349,8 +1349,9 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hashicorp/vault/api v1.3.0/go.mod h1:EabNQLI0VWbWoGlA+oBLC8PXmR9D60aUVgQGvangFWQ=
github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM=
github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw=
github.com/hashicorp/vault/api v1.4.1 h1:mWLfPT0RhxBitjKr6swieCEP2v5pp/M//t70S3kMLRo=
github.com/hashicorp/vault/api v1.4.1/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM=
github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo=
github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
+1 -1
View File
@@ -6,5 +6,5 @@ const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// 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 = "3.0.1"
JSONSchemaVersion = "3.1.0"
)
@@ -47,8 +47,10 @@ func hasMetadata(p pkg.Package) bool {
func decodeComponent(c *cyclonedx.Component) *pkg.Package {
values := map[string]string{}
for _, p := range *c.Properties {
values[p.Name] = p.Value
if c.Properties != nil {
for _, p := range *c.Properties {
values[p.Name] = p.Value
}
}
p := &pkg.Package{
@@ -5,6 +5,8 @@ import (
"io"
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/syft/internal/formats/common"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
@@ -157,6 +159,14 @@ func linuxReleaseFromOSComponent(component *cyclonedx.Component) *linux.Release
}
}
if component.Properties != nil {
values := map[string]string{}
for _, p := range *component.Properties {
values[p.Name] = p.Value
}
common.DecodeInto(&rel, values, "syft:distro", CycloneDXFields)
}
return rel
}
+183
View File
@@ -0,0 +1,183 @@
package github
import (
"fmt"
"strings"
"time"
"github.com/mholt/archiver/v3"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/version"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
// toGithubModel converts the provided SBOM to a GitHub dependency model
func toGithubModel(s *sbom.SBOM) DependencySnapshot {
scanTime := time.Now().Format(time.RFC3339) // TODO is there a record of this somewhere?
v := version.FromBuild().Version
if v == "[not provided]" {
v = "0.0.0-dev"
}
return DependencySnapshot{
Version: 0,
// TODO allow property input to specify the Job, Sha, and Ref
Detector: DetectorMetadata{
Name: internal.ApplicationName,
URL: "https://github.com/anchore/syft",
Version: v,
},
Metadata: toSnapshotMetadata(s),
Manifests: toGithubManifests(s),
Scanned: scanTime,
}
}
// toSnapshotMetadata captures the linux distribution information and other metadata
func toSnapshotMetadata(s *sbom.SBOM) Metadata {
out := Metadata{}
if s.Artifacts.LinuxDistribution != nil {
d := s.Artifacts.LinuxDistribution
qualifiers := packageurl.Qualifiers{}
if len(d.IDLike) > 0 {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "like",
Value: strings.Join(d.IDLike, ","),
})
}
purl := packageurl.NewPackageURL("generic", "", d.ID, d.VersionID, qualifiers, "")
out["syft:distro"] = purl.ToString()
}
return out
}
func filesystem(p pkg.Package) string {
if len(p.Locations) > 0 {
return p.Locations[0].FileSystemID
}
return ""
}
// isArchive returns true if the path appears to be an archive
func isArchive(path string) bool {
_, err := archiver.ByExtension(path)
return err == nil
}
// toPath Generates a string representation of the package location, optionally including the layer hash
func toPath(s source.Metadata, p pkg.Package) string {
inputPath := strings.TrimPrefix(s.Path, "./")
if inputPath == "." {
inputPath = ""
}
if len(p.Locations) > 0 {
location := p.Locations[0]
packagePath := location.RealPath
if location.VirtualPath != "" {
packagePath = location.VirtualPath
}
packagePath = strings.TrimPrefix(packagePath, "/")
switch s.Scheme {
case source.ImageScheme:
image := strings.ReplaceAll(s.ImageMetadata.UserInput, ":/", "//")
return fmt.Sprintf("%s:/%s", image, packagePath)
case source.FileScheme:
if isArchive(inputPath) {
return fmt.Sprintf("%s:/%s", inputPath, packagePath)
}
return inputPath
case source.DirectoryScheme:
if inputPath != "" {
return fmt.Sprintf("%s/%s", inputPath, packagePath)
}
return packagePath
}
}
return fmt.Sprintf("%s%s", inputPath, s.ImageMetadata.UserInput)
}
// toGithubManifests manifests, each of which represents a specific location that has dependencies
func toGithubManifests(s *sbom.SBOM) Manifests {
manifests := map[string]*Manifest{}
for _, p := range s.Artifacts.PackageCatalog.Sorted() {
path := toPath(s.Source, p)
manifest, ok := manifests[path]
if !ok {
manifest = &Manifest{
Name: path,
File: FileInfo{
SourceLocation: path,
},
Resolved: DependencyGraph{},
}
fs := filesystem(p)
if fs != "" {
manifest.Metadata = Metadata{
"syft:filesystem": fs,
}
}
manifests[path] = manifest
}
name := dependencyName(p)
manifest.Resolved[name] = DependencyNode{
Purl: p.PURL,
Metadata: toDependencyMetadata(p),
Relationship: toDependencyRelationshipType(p),
Scope: toDependencyScope(p),
Dependencies: toDependencies(s, p),
}
}
out := Manifests{}
for k, v := range manifests {
out[k] = *v
}
return out
}
// dependencyName to make things a little nicer to read; this might end up being lossy
func dependencyName(p pkg.Package) string {
purl, err := packageurl.FromString(p.PURL)
if err != nil {
log.Warnf("Invalid PURL for package: '%s' PURL: '%s' (%w)", p.Name, p.PURL, err)
return ""
}
// don't use qualifiers for this
purl.Qualifiers = nil
return purl.ToString()
}
func toDependencyScope(_ pkg.Package) DependencyScope {
return DependencyScopeRuntime
}
func toDependencyRelationshipType(_ pkg.Package) DependencyRelationship {
return DependencyRelationshipDirect
}
func toDependencyMetadata(_ pkg.Package) Metadata {
// We have limited properties: up to 8 with reasonably small values
// For now, we are encoding the location as part of the key, we are encoding PURLs with most
// of the other information Grype might need; and the distro information at the top level
// so we don't need anything here yet
return Metadata{}
}
func toDependencies(s *sbom.SBOM, p pkg.Package) (out []string) {
for _, r := range s.Relationships {
if r.From.ID() == p.ID() {
if p, ok := r.To.(pkg.Package); ok {
out = append(out, dependencyName(p))
}
}
}
return
}
+161
View File
@@ -0,0 +1,161 @@
package github
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
func Test_toGithubModel(t *testing.T) {
s := sbom.SBOM{
Source: source.Metadata{
Scheme: source.ImageScheme,
ImageMetadata: source.ImageMetadata{
UserInput: "ubuntu:18.04",
Architecture: "amd64",
},
},
Artifacts: sbom.Artifacts{
LinuxDistribution: &linux.Release{
ID: "ubuntu",
VersionID: "18.04",
IDLike: []string{"debian"},
},
PackageCatalog: pkg.NewCatalog(),
},
}
for _, p := range []pkg.Package{
{
Name: "pkg-1",
Version: "1.0.1",
Locations: []source.Location{{
Coordinates: source.Coordinates{
RealPath: "/usr/lib",
FileSystemID: "fsid-1",
},
}},
},
{
Name: "pkg-2",
Version: "2.0.2",
Locations: []source.Location{{
Coordinates: source.Coordinates{
RealPath: "/usr/lib",
FileSystemID: "fsid-1",
},
}},
},
{
Name: "pkg-3",
Version: "3.0.3",
Locations: []source.Location{{
Coordinates: source.Coordinates{
RealPath: "/etc",
FileSystemID: "fsid-1",
},
}},
},
} {
p.PURL = packageurl.NewPackageURL(
"generic",
"",
p.Name,
p.Version,
nil,
"",
).ToString()
s.Artifacts.PackageCatalog.Add(p)
}
actual := toGithubModel(&s)
expected := DependencySnapshot{
Version: 0,
Detector: DetectorMetadata{
Name: "syft",
Version: "0.0.0-dev",
URL: "https://github.com/anchore/syft",
},
Metadata: Metadata{
"syft:distro": "pkg:generic/ubuntu@18.04?like=debian",
},
Scanned: actual.Scanned,
Manifests: Manifests{
"ubuntu:18.04:/usr/lib": Manifest{
Name: "ubuntu:18.04:/usr/lib",
File: FileInfo{
SourceLocation: "ubuntu:18.04:/usr/lib",
},
Metadata: Metadata{
"syft:filesystem": "fsid-1",
},
Resolved: DependencyGraph{
"pkg:generic/pkg-1@1.0.1": DependencyNode{
Purl: "pkg:generic/pkg-1@1.0.1",
Scope: DependencyScopeRuntime,
Relationship: DependencyRelationshipDirect,
},
"pkg:generic/pkg-2@2.0.2": DependencyNode{
Purl: "pkg:generic/pkg-2@2.0.2",
Scope: DependencyScopeRuntime,
Relationship: DependencyRelationshipDirect,
},
},
},
"ubuntu:18.04:/etc": Manifest{
Name: "ubuntu:18.04:/etc",
File: FileInfo{
SourceLocation: "ubuntu:18.04:/etc",
},
Metadata: Metadata{
"syft:filesystem": "fsid-1",
},
Resolved: DependencyGraph{
"pkg:generic/pkg-3@3.0.3": DependencyNode{
Purl: "pkg:generic/pkg-3@3.0.3",
Scope: DependencyScopeRuntime,
Relationship: DependencyRelationshipDirect,
},
},
},
},
}
// just using JSONEq because it gives a comprehensible diff
s1, _ := json.Marshal(expected)
s2, _ := json.Marshal(actual)
assert.JSONEq(t, string(s1), string(s2))
// Just test the other schemes:
s.Source.Path = "."
s.Source.Scheme = source.DirectoryScheme
actual = toGithubModel(&s)
assert.Equal(t, "etc", actual.Manifests["etc"].Name)
s.Source.Path = "./artifacts"
s.Source.Scheme = source.DirectoryScheme
actual = toGithubModel(&s)
assert.Equal(t, "artifacts/etc", actual.Manifests["artifacts/etc"].Name)
s.Source.Path = "/artifacts"
s.Source.Scheme = source.DirectoryScheme
actual = toGithubModel(&s)
assert.Equal(t, "/artifacts/etc", actual.Manifests["/artifacts/etc"].Name)
s.Source.Path = "./executable"
s.Source.Scheme = source.FileScheme
actual = toGithubModel(&s)
assert.Equal(t, "executable", actual.Manifests["executable"].Name)
s.Source.Path = "./archive.tar.gz"
s.Source.Scheme = source.FileScheme
actual = toGithubModel(&s)
assert.Equal(t, "archive.tar.gz:/etc", actual.Manifests["archive.tar.gz:/etc"].Name)
}
+29
View File
@@ -0,0 +1,29 @@
package github
import (
"encoding/json"
"io"
"github.com/anchore/syft/syft/sbom"
)
const ID sbom.FormatID = "github-0-json"
func Format() sbom.Format {
return sbom.NewFormat(
ID,
func(writer io.Writer, sbom sbom.SBOM) error {
bom := toGithubModel(&sbom)
bytes, err := json.MarshalIndent(bom, "", " ")
if err != nil {
return err
}
_, err = writer.Write(bytes)
return err
},
nil,
nil,
)
}
@@ -0,0 +1,78 @@
package github
// Derived from: https://gist.github.com/reiddraper/fdab2883db0f372c146d1a750fc1c43f
type DependencySnapshot struct {
Version int `json:"version"`
Job Job `json:"job,omitempty"` // !omitempty
Sha string `json:"sha,omitempty"` // !omitempty sha of the Git commit
Ref string `json:"ref,omitempty"` // !omitempty ref of the Git commit example "refs/heads/main"
Detector DetectorMetadata `json:"detector,omitempty"`
Metadata Metadata `json:"metadata,omitempty"`
Manifests Manifests `json:"manifests,omitempty"`
Scanned ISO8601Date `json:"scanned,omitempty"`
}
type Job struct {
Name string `json:"name,omitempty"` // !omitempty
ID string `json:"id,omitempty"` // !omitempty
HTMLURL string `json:"html_url,omitempty"`
}
type DetectorMetadata struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
Version string `json:"version,omitempty"`
}
type Manifests map[string]Manifest
// Manifest A collection of related dependencies, either declared in a file,
// or representing a logical group of dependencies.
type Manifest struct {
Name string `json:"name"`
File FileInfo `json:"file"`
Metadata Metadata `json:"metadata,omitempty"`
Resolved DependencyGraph `json:"resolved,omitempty"`
}
type FileInfo struct {
SourceLocation string `json:"source_location,omitempty"`
}
// DependencyRelationship A notation of whether a dependency is requested directly
// by this manifest, or is a dependency of another dependency.
type DependencyRelationship string
const (
DependencyRelationshipDirect DependencyRelationship = "direct"
DependencyRelationshipIndirect DependencyRelationship = "indirect"
)
// DependencyScope A notation of whether the dependency is required for the primary
// build artifact (runtime), or is only used for development.
// Future versions of this specification may allow for more granular
// scopes, like `runtimeserver`, `runtimeshipped`,
// `developmenttest`, `developmentbenchmark`.
type DependencyScope string
const (
DependencyScopeRuntime DependencyScope = "runtime"
DependencyScopeDevelopment DependencyScope = "development"
)
type DependencyNode struct {
Purl string `json:"purl,omitempty"`
Metadata Metadata `json:"metadata,omitempty"`
Relationship DependencyRelationship `json:"relationship,omitempty"`
Scope DependencyScope `json:"scope,omitempty"`
Dependencies []string `json:"dependencies,omitempty"`
}
type DependencyGraph map[string]DependencyNode
type ISO8601Date = string
type Scalar interface{} // should be: null | boolean | string | number
type Metadata map[string]Scalar
@@ -88,7 +88,7 @@
}
},
"schema": {
"version": "3.0.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.0.1.json"
"version": "3.1.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.1.0.json"
}
}
@@ -184,7 +184,7 @@
}
},
"schema": {
"version": "3.0.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.0.1.json"
"version": "3.1.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.1.0.json"
}
}
@@ -111,7 +111,7 @@
}
},
"schema": {
"version": "3.0.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.0.1.json"
"version": "3.1.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.1.0.json"
}
}
File diff suppressed because it is too large Load Diff
+5
View File
@@ -6,6 +6,7 @@ import (
"github.com/anchore/syft/internal/formats/cyclonedxjson"
"github.com/anchore/syft/internal/formats/cyclonedxxml"
"github.com/anchore/syft/internal/formats/github"
"github.com/anchore/syft/internal/formats/spdx22json"
"github.com/anchore/syft/internal/formats/spdx22tagvalue"
"github.com/anchore/syft/internal/formats/syftjson"
@@ -21,6 +22,7 @@ const (
TableFormatID = table.ID
CycloneDxXMLFormatID = cyclonedxxml.ID
CycloneDxJSONFormatID = cyclonedxjson.ID
GitHubID = github.ID
SPDXTagValueFormatID = spdx22tagvalue.ID
SPDXJSONFormatID = spdx22json.ID
)
@@ -32,6 +34,7 @@ func init() {
syftjson.Format(),
cyclonedxxml.Format(),
cyclonedxjson.Format(),
github.Format(),
spdx22tagvalue.Format(),
spdx22json.Format(),
table.Format(),
@@ -71,6 +74,8 @@ func FormatByName(name string) sbom.Format {
return FormatByID(cyclonedxxml.ID)
case "cyclonedxjson":
return FormatByID(cyclonedxjson.ID)
case "github":
return FormatByID(github.ID)
case "spdx", "spdxtv", "spdxtagvalue":
return FormatByID(spdx22tagvalue.ID)
case "spdxjson":
+1
View File
@@ -17,6 +17,7 @@ var (
"org",
"net",
"io",
"be",
}
primaryJavaManifestGroupIDFields = []string{
+15 -7
View File
@@ -70,18 +70,26 @@ func buildGoPkgInfo(location source.Location, mod, goVersion, arch string) []pkg
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
// must have dep, name, version, sha
if len(fields) < 4 {
// must have dep, name, version
if len(fields) < 3 {
continue
}
if fields[0] == packageIdentifier || fields[0] == replaceIdentifier {
name := fields[1]
version := fields[2]
h1Digest := fields[3]
name := fields[1]
version := fields[2]
h1Digest := ""
// if dep is *not* vendored, it'll also have h1digest
if len(fields) >= 4 {
h1Digest = fields[3]
}
if fields[0] == packageIdentifier {
pkgsSlice = append(pkgsSlice, newGoBinaryPackage(name, version, h1Digest, goVersion, arch, location))
}
if fields[0] == replaceIdentifier {
// replace the previous entry in the package slice
pkgsSlice[len(pkgsSlice)-1] = newGoBinaryPackage(name, version, h1Digest, goVersion, arch, location)
}
}
return pkgsSlice
}
+42 -2
View File
@@ -28,7 +28,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
name: "buildGoPkgInfo parses a populated mod string and returns packages but no source info",
mod: `path github.com/anchore/syft mod github.com/anchore/syft (devel)
dep github.com/adrg/xdg v0.2.1 h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=
dep github.com/anchore/client-go v0.0.0-20210222170800-9c70f9b80bcf h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=`,
dep github.com/anchore/client-go v0.0.0-20210222170800-9c70f9b80bcf h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=
dep github.com/anchore/client-go v1.2.3`,
expected: []pkg.Package{
{
Name: "github.com/adrg/xdg",
@@ -70,6 +71,25 @@ func TestBuildGoPkgInfo(t *testing.T) {
H1Digest: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
},
},
{
Name: "github.com/anchore/client-go",
Version: "v1.2.3",
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: []source.Location{
{
Coordinates: source.Coordinates{
RealPath: "/a-path",
FileSystemID: "layer-id",
},
},
},
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
},
},
},
},
{
@@ -79,7 +99,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
dep golang.org/x/net v0.0.0-20211006190231-62292e806868 h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=
dep golang.org/x/sys v0.0.0-20211006194710-c8a6f5223071 h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=
dep golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
=> golang.org/x/term v0.0.0-20210916214954-140adaaadfaf h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=`,
=> golang.org/x/term v0.0.0-20210916214954-140adaaadfaf h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=
dep github.com/anchore/client-go v1.2.3`,
expected: []pkg.Package{
{
Name: "golang.org/x/net",
@@ -141,6 +162,25 @@ func TestBuildGoPkgInfo(t *testing.T) {
H1Digest: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
},
},
{
Name: "github.com/anchore/client-go",
Version: "v1.2.3",
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: []source.Location{
{
Coordinates: source.Coordinates{
RealPath: "/a-path",
FileSystemID: "layer-id",
},
},
},
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
},
},
},
},
}
+1 -1
View File
@@ -75,7 +75,7 @@ func TestDpkgMetadata_pURL(t *testing.T) {
Version: "v",
SourceVersion: "2.3",
},
expected: "pkg:deb/debian/p@v?upstream=s@2.3&distro=debian-11",
expected: "pkg:deb/debian/p@v?upstream=s%402.3&distro=debian-11",
},
}
+26 -6
View File
@@ -1,18 +1,21 @@
package pkg
import (
"fmt"
"testing"
"github.com/anchore/packageurl-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
func TestNpmPackageJSONMetadata_PackageURL(t *testing.T) {
tests := []struct {
name string
metadata NpmPackageJSONMetadata
expected string
name string
metadata NpmPackageJSONMetadata
expected string
namespace string
}{
{
name: "no namespace",
@@ -24,19 +27,36 @@ func TestNpmPackageJSONMetadata_PackageURL(t *testing.T) {
},
{
name: "split by namespace",
metadata: NpmPackageJSONMetadata{
Name: "npmcli/arborist",
Version: "2.6.2",
},
expected: "pkg:npm/npmcli/arborist@2.6.2",
namespace: "npmcli",
},
{
name: "encoding @ symobl",
metadata: NpmPackageJSONMetadata{
Name: "@npmcli/arborist",
Version: "2.6.2",
},
expected: "pkg:npm/@npmcli/arborist@2.6.2",
expected: "pkg:npm/%40npmcli/arborist@2.6.2",
namespace: "@npmcli",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := tt.metadata.PackageURL(nil)
assert.Equal(t, tt.expected, actual)
_, err := packageurl.FromString(actual)
decoded, err := packageurl.FromString(actual)
require.NoError(t, err)
assert.Equal(t, tt.namespace, decoded.Namespace)
if decoded.Namespace != "" {
assert.Equal(t, tt.metadata.Name, fmt.Sprintf("%s/%s", decoded.Namespace, decoded.Name))
} else {
assert.Equal(t, tt.metadata.Name, decoded.Name)
}
assert.Equal(t, tt.metadata.Version, decoded.Version)
})
}
}
+1 -1
View File
@@ -27,7 +27,7 @@ func TestPythonPackageMetadata_pURL(t *testing.T) {
CommitID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
},
expected: "pkg:pypi/name@v0.1.0?vcs_url=git+https:%2F%2Fgithub.com%2Ftest%2Ftest.git@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
expected: "pkg:pypi/name@v0.1.0?vcs_url=git+https://github.com/test/test.git%40aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
{
name: "should not respond to release info",
+3 -5
View File
@@ -5,12 +5,10 @@ import (
"sort"
"strconv"
"github.com/anchore/syft/syft/file"
"github.com/scylladb/go-set/strset"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/linux"
"github.com/scylladb/go-set/strset"
)
const RpmDBGlob = "**/var/lib/rpm/Packages"
@@ -24,7 +22,7 @@ var (
type RpmdbMetadata struct {
Name string `json:"name"`
Version string `json:"version"`
Epoch *int `json:"epoch" cyclonedx:"epoch"`
Epoch *int `json:"epoch" cyclonedx:"epoch" jsonschema:"nullable"`
Arch string `json:"architecture"`
Release string `json:"release" cyclonedx:"release"`
SourceRpm string `json:"sourceRpm" cyclonedx:"sourceRpm"`
+6 -1
View File
@@ -50,8 +50,13 @@ func URL(p Package, release *linux.Release) string {
fields := re.Split(p.Name, -1)
namespace = fields[0]
name = strings.TrimPrefix(p.Name, namespace+"/")
case p.Type == NpmPkg:
fields := strings.SplitN(p.Name, "/", 2)
if len(fields) > 1 {
namespace = fields[0]
name = fields[1]
}
}
// generate a purl from the package data
return packageurl.NewPackageURL(
purlType,
+26 -23
View File
@@ -54,35 +54,38 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
for _, test := range tests {
t.Run(string(test.formatOption), func(t *testing.T) {
originalSBOM, _ := catalogFixtureImage(t, "image-pkg-coverage")
// use second image for relationships
for _, image := range []string{"image-pkg-coverage", "image-owning-package"} {
originalSBOM, _ := catalogFixtureImage(t, image)
format := syft.FormatByID(test.formatOption)
require.NotNil(t, format)
format := syft.FormatByID(test.formatOption)
require.NotNil(t, format)
by1, err := syft.Encode(originalSBOM, format)
assert.NoError(t, err)
by1, err := syft.Encode(originalSBOM, format)
assert.NoError(t, err)
newSBOM, newFormat, err := syft.Decode(bytes.NewReader(by1))
assert.NoError(t, err)
assert.Equal(t, format.ID(), newFormat.ID())
newSBOM, newFormat, err := syft.Decode(bytes.NewReader(by1))
assert.NoError(t, err)
assert.Equal(t, format.ID(), newFormat.ID())
by2, err := syft.Encode(*newSBOM, format)
assert.NoError(t, err)
by2, err := syft.Encode(*newSBOM, format)
assert.NoError(t, err)
if test.redactor != nil {
by1 = test.redactor(by1)
by2 = test.redactor(by2)
}
if test.redactor != nil {
by1 = test.redactor(by1)
by2 = test.redactor(by2)
}
if test.json {
s1 := string(by1)
s2 := string(by2)
assert.JSONEq(t, s1, s2)
} else {
if !assert.True(t, bytes.Equal(by1, by2)) {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(string(by1), string(by2), true)
t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
if test.json {
s1 := string(by1)
s2 := string(by2)
assert.JSONEq(t, s1, s2)
} else {
if !assert.True(t, bytes.Equal(by1, by2)) {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(string(by1), string(by2), true)
t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
}
}
}
})