Compare commits

...
5 Commits
11 changed files with 187 additions and 39 deletions
@@ -0,0 +1,51 @@
name: PR for latest Stereoscope release
on:
schedule:
- cron: "0 8 * * *" # 3 AM EST
workflow_dispatch:
env:
GO_VERSION: "1.18.x"
GO_STABLE_VERSION: true
jobs:
upgrade-stereoscope:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
stable: ${{ env.GO_STABLE_VERSION }}
- run: |
LATEST_VERSION=$(git ls-remote https://github.com/anchore/stereoscope main | head -n1 | awk '{print $1;}')
# update go.mod
go get github.com/anchore/stereoscope@$LATEST_VERSION
go mod tidy
# export the version for use with create-pull-request
echo "::set-output name=LATEST_VERSION::$LATEST_VERSION"
id: latest-version
- uses: tibdex/github-app-token@v1
id: generate-token
with:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@v4
with:
signoff: true
delete-branch: true
branch: auto/latest
labels: dependencies
commit-message: "Update Stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}"
title: "Update Stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}"
body: |
Update Stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}
token: ${{ steps.generate-token.outputs.token }}
+3 -3
View File
@@ -156,9 +156,9 @@ syft <image> -o <format>
Where the `formats` available are:
- `json`: Use this to get as much information out of Syft as possible!
- `text`: A row-oriented, human-and-machine-friendly output.
- `cyclonedx`: A XML report conforming to the [CycloneDX 1.3 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-json`: A JSON report conforming to the [CycloneDX 1.3 specification](https://cyclonedx.org/specification/overview/).
- `spdx`: A tag-value formatted report conforming to the [SPDX 2.2 specification](https://spdx.github.io/spdx-spec/).
- `cyclonedx-xml`: A XML report conforming to the [CycloneDX 1.4 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-json`: A JSON report conforming to the [CycloneDX 1.4 specification](https://cyclonedx.org/specification/overview/).
- `spdx-tag-value`: A tag-value formatted report conforming to the [SPDX 2.2 specification](https://spdx.github.io/spdx-spec/).
- `spdx-json`: A JSON report conforming to the [SPDX 2.2 JSON Schema](https://github.com/spdx/spdx-spec/blob/v2.2/schemas/spdx-schema.json).
- `table`: A columnar summary (default).
@@ -0,0 +1,13 @@
package spdxhelpers
import (
"regexp"
)
var expr = regexp.MustCompile("[^a-zA-Z0-9.-]")
// SPDX spec says SPDXID must be:
// "SPDXRef-"[idstring] where [idstring] is a unique string containing letters, numbers, ., and/or -
func SanitizeElementID(id string) string {
return expr.ReplaceAllString(id, "-")
}
@@ -0,0 +1,39 @@
package spdxhelpers
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_SanitizeElementID(t *testing.T) {
tests := []struct {
input string
expected string
}{
{
input: "letters",
expected: "letters",
},
{
input: "ssl-client",
expected: "ssl-client",
},
{
input: "ssl_client",
expected: "ssl-client",
},
{
input: "go-module-sigs.k8s.io/structured-merge-diff/v3",
expected: "go-module-sigs.k8s.io-structured-merge-diff-v3",
},
}
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {
actual := SanitizeElementID(test.input)
assert.Equal(t, test.expected, actual)
})
}
}
+1 -1
View File
@@ -130,7 +130,7 @@ func toGithubManifests(s *sbom.SBOM) Manifests {
name := dependencyName(p)
manifest.Resolved[name] = DependencyNode{
Purl: p.PURL,
PackageURL: p.PURL,
Metadata: toDependencyMetadata(p),
Relationship: toDependencyRelationshipType(p),
Scope: toDependencyScope(p),
+3 -3
View File
@@ -104,12 +104,12 @@ func Test_toGithubModel(t *testing.T) {
},
Resolved: DependencyGraph{
"pkg:generic/pkg-1@1.0.1": DependencyNode{
Purl: "pkg:generic/pkg-1@1.0.1",
PackageURL: "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",
PackageURL: "pkg:generic/pkg-2@2.0.2",
Scope: DependencyScopeRuntime,
Relationship: DependencyRelationshipDirect,
},
@@ -125,7 +125,7 @@ func Test_toGithubModel(t *testing.T) {
},
Resolved: DependencyGraph{
"pkg:generic/pkg-3@3.0.3": DependencyNode{
Purl: "pkg:generic/pkg-3@3.0.3",
PackageURL: "pkg:generic/pkg-3@3.0.3",
Scope: DependencyScopeRuntime,
Relationship: DependencyRelationshipDirect,
},
@@ -14,9 +14,9 @@ type DependencySnapshot struct {
}
type Job struct {
Name string `json:"name,omitempty"` // !omitempty
ID string `json:"id,omitempty"` // !omitempty
HTMLURL string `json:"html_url,omitempty"`
Correlator string `json:"correlator,omitempty"` // !omitempty
ID string `json:"id,omitempty"` // !omitempty
HTMLURL string `json:"html_url,omitempty"`
}
type DetectorMetadata struct {
@@ -62,7 +62,7 @@ const (
)
type DependencyNode struct {
Purl string `json:"purl,omitempty"`
PackageURL string `json:"package_url,omitempty"`
Metadata Metadata `json:"metadata,omitempty"`
Relationship DependencyRelationship `json:"relationship,omitempty"`
Scope DependencyScope `json:"scope,omitempty"`
@@ -1,5 +1,7 @@
package model
import "github.com/anchore/syft/internal/formats/common/spdxhelpers"
// ElementID represents the identifier string portion of an SPDX element
// identifier. DocElementID should be used for any attributes which can
// contain identifiers defined in a different SPDX document.
@@ -7,31 +9,5 @@ package model
type ElementID string
func (e ElementID) String() string {
return "SPDXRef-" + string(e)
}
// DocElementID represents an SPDX element identifier that could be defined
// in a different SPDX document, and therefore could have a "DocumentRef-"
// portion, such as Relationship and Annotations.
// ElementID is used for attributes in which a "DocumentRef-" portion cannot
// appear, such as a Package or File definition (since it is necessarily
// being defined in the present document).
// DocumentRefID will be the empty string for elements defined in the
// present document.
// DocElementIDs should NOT contain the mandatory 'DocumentRef-' or
// 'SPDXRef-' portions.
type DocElementID struct {
DocumentRefID string
ElementRefID ElementID
}
// RenderDocElementID takes a DocElementID and returns the string equivalent,
// with the SPDXRef- prefix (and, if applicable, the DocumentRef- prefix)
// reinserted.
func (d DocElementID) String() string {
prefix := ""
if d.DocumentRefID != "" {
prefix = "DocumentRef-" + d.DocumentRefID + ":"
}
return prefix + d.ElementRefID.String()
return "SPDXRef-" + spdxhelpers.SanitizeElementID(string(e))
}
@@ -6,6 +6,9 @@ import (
"testing"
"github.com/anchore/syft/internal/formats/common/testutils"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
var updateSpdxTagValue = flag.Bool("update-spdx-tv", false, "update the *.golden files for spdx-tv encoders")
@@ -31,6 +34,32 @@ func TestSPDXTagValueImageEncoder(t *testing.T) {
)
}
func TestSPDXJSONSPDXIDs(t *testing.T) {
var pkgs []pkg.Package
for _, name := range []string{"some/slashes", "@at-sign", "under_scores"} {
p := pkg.Package{
Name: name,
}
p.SetID()
pkgs = append(pkgs, p)
}
testutils.AssertEncoderAgainstGoldenSnapshot(t,
Format(),
sbom.SBOM{
Artifacts: sbom.Artifacts{
PackageCatalog: pkg.NewCatalog(pkgs...),
},
Relationships: nil,
Source: source.Metadata{
Scheme: source.DirectoryScheme,
},
Descriptor: sbom.Descriptor{},
},
true,
spdxTagValueRedactor,
)
}
func spdxTagValueRedactor(s []byte) []byte {
// each SBOM reports the time it was generated, which is not useful during snapshot testing
s = regexp.MustCompile(`Created: .*`).ReplaceAll(s, []byte("redacted"))
@@ -0,0 +1,40 @@
SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: .
DocumentNamespace: https://anchore.com/syft/dir/e69056a9-935e-4f00-b85f-9467f5d99a92
LicenseListVersion: 3.16
Creator: Organization: Anchore, Inc
Creator: Tool: syft-[not provided]
Created: 2022-04-13T16:38:03Z
##### Package: @at-sign
PackageName: @at-sign
SPDXID: SPDXRef-Package---at-sign-739e4f0d93fb8298
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageLicenseConcluded: NONE
PackageLicenseDeclared: NONE
PackageCopyrightText: NOASSERTION
##### Package: some/slashes
PackageName: some/slashes
SPDXID: SPDXRef-Package--some-slashes-26db06648b24bff9
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageLicenseConcluded: NONE
PackageLicenseDeclared: NONE
PackageCopyrightText: NOASSERTION
##### Package: under_scores
PackageName: under_scores
SPDXID: SPDXRef-Package--under-scores-250cbfefcdea318b
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageLicenseConcluded: NONE
PackageLicenseDeclared: NONE
PackageCopyrightText: NOASSERTION
@@ -96,7 +96,7 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2
for _, p := range catalog.Sorted() {
// name should be guaranteed to be unique, but semantically useful and stable
id := fmt.Sprintf("Package-%+v-%s-%s", p.Type, p.Name, p.ID())
id := spdxhelpers.SanitizeElementID(fmt.Sprintf("Package-%+v-%s-%s", p.Type, p.Name, p.ID()))
// If the Concluded License is not the same as the Declared License, a written explanation should be provided
// in the Comments on License field (section 3.16). With respect to NOASSERTION, a written explanation in