From 03d51c36d0ccd970c3ec383b10e1c19969d4a7d0 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Mon, 2 May 2022 17:33:40 -0400 Subject: [PATCH 1/7] golang.org/x/crypto upgrade (#979) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 89ae61bd194..8c91c7472d5 100644 --- a/go.mod +++ b/go.mod @@ -319,7 +319,7 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect - golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect + golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect diff --git a/go.sum b/go.sum index 462981be2c3..0fce1755655 100644 --- a/go.sum +++ b/go.sum @@ -2415,8 +2415,8 @@ golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= From 37927b8b23cac234289a73aa3e886675d463e477 Mon Sep 17 00:00:00 2001 From: Dan Luhring Date: Tue, 3 May 2022 09:38:14 -0400 Subject: [PATCH 2/7] reduce logging severity for non-Go binaries (#983) --- syft/pkg/cataloger/golang/scan_bin.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/golang/scan_bin.go b/syft/pkg/cataloger/golang/scan_bin.go index 5663492d638..5d84011a885 100644 --- a/syft/pkg/cataloger/golang/scan_bin.go +++ b/syft/pkg/cataloger/golang/scan_bin.go @@ -41,7 +41,11 @@ func scanFile(reader unionReader, filename string) ([]*debug.BuildInfo, []string } // in this case we could not read the or parse the file, but not explicitly because it is not a // go-compiled binary (though it still might be). - log.Warnf("golang cataloger: failed to read buildinfo (file=%q): %v", filename, err) + // TODO: We should change this back to "warn" eventually. + // But right now it's catching too many cases where the reader IS NOT a Go binary at all. + // It'd be great to see how we can get those cases to be detected and handled above before we get to + // this point in execution. + log.Infof("golang cataloger: unable to read buildinfo (file=%q): %v", filename, err) return nil, nil } From ab289933da82dabf5855c90d4c571c322a42affd Mon Sep 17 00:00:00 2001 From: Jonas Xavier Date: Wed, 4 May 2022 10:47:13 -0700 Subject: [PATCH 3/7] read Go main module version as is - (devel) (#981) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * read Go main module version as is - (devel) Signed-off-by: Jonas Galvão Xavier * fix package test with default (devel) main module Signed-off-by: Jonas Galvão Xavier --- syft/pkg/cataloger/golang/parse_go_bin.go | 3 ++- syft/pkg/cataloger/golang/parse_go_bin_test.go | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/golang/parse_go_bin.go b/syft/pkg/cataloger/golang/parse_go_bin.go index 1fc980d62d7..a736b8f512c 100644 --- a/syft/pkg/cataloger/golang/parse_go_bin.go +++ b/syft/pkg/cataloger/golang/parse_go_bin.go @@ -29,12 +29,13 @@ var ( func makeGoMainPackage(mod *debug.BuildInfo, arch string, location source.Location) pkg.Package { gbs := getBuildSettings(mod.Settings) main := newGoBinaryPackage(&mod.Main, mod.GoVersion, arch, location, gbs) - main.Version = "" + main.Version = "(devel)" if v, ok := gbs["vcs.revision"]; ok { main.Version = v } + main.SetID() return main } diff --git a/syft/pkg/cataloger/golang/parse_go_bin_test.go b/syft/pkg/cataloger/golang/parse_go_bin_test.go index 8ac560d8c39..19888f79df4 100644 --- a/syft/pkg/cataloger/golang/parse_go_bin_test.go +++ b/syft/pkg/cataloger/golang/parse_go_bin_test.go @@ -133,6 +133,7 @@ func TestBuildGoPkgInfo(t *testing.T) { FoundBy: catalogerName, Language: pkg.Go, Type: pkg.GoModulePkg, + Version: "(devel)", Locations: source.NewLocationSet( source.Location{ Coordinates: source.Coordinates{ From 8b6c576d781f8323ace47ee622ea0e8b9b65649f Mon Sep 17 00:00:00 2001 From: Steven Maude Date: Thu, 5 May 2022 01:25:40 +0100 Subject: [PATCH 4/7] Fix `github-json` output option (#967) * Fix "bad output format" for `github-json` output Signed-off-by: Steven Maude * Update formats in README Signed-off-by: Steven Maude * Run `make lint-fix` Signed-off-by: Steven Maude --- README.md | 1 + syft/formats.go | 2 +- syft/formats_test.go | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 280d16b8fc3..fd184197777 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ Where the `formats` available are: - `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). +- `github`: A JSON report conforming to GitHub's dependency snapshot format. - `table`: A columnar summary (default). #### Multiple outputs diff --git a/syft/formats.go b/syft/formats.go index 8623848df40..061f191f15a 100644 --- a/syft/formats.go +++ b/syft/formats.go @@ -74,7 +74,7 @@ func FormatByName(name string) sbom.Format { return FormatByID(cyclonedxxml.ID) case "cyclonedxjson": return FormatByID(cyclonedxjson.ID) - case "github": + case "github", "githubjson": return FormatByID(github.ID) case "spdx", "spdxtv", "spdxtagvalue": return FormatByID(spdx22tagvalue.ID) diff --git a/syft/formats_test.go b/syft/formats_test.go index 555fb08bd39..94e78bdb406 100644 --- a/syft/formats_test.go +++ b/syft/formats_test.go @@ -8,6 +8,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" @@ -169,6 +170,17 @@ func TestFormatByName(t *testing.T) { name: "syft-json", want: syftjson.ID, }, + + // GitHub JSON + { + name: "github", + want: github.ID, + }, + + { + name: "github-json", + want: github.ID, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 2fc344aba47934ce9e13de7d157c285d34735200 Mon Sep 17 00:00:00 2001 From: Jonas Xavier Date: Thu, 5 May 2022 00:01:00 -0700 Subject: [PATCH 5/7] golang cataloger - main module version as is (#986) Signed-off-by: Jonas Xavier --- syft/pkg/cataloger/golang/parse_go_bin.go | 2 -- syft/pkg/cataloger/golang/parse_go_bin_test.go | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/syft/pkg/cataloger/golang/parse_go_bin.go b/syft/pkg/cataloger/golang/parse_go_bin.go index a736b8f512c..3ec13dc2898 100644 --- a/syft/pkg/cataloger/golang/parse_go_bin.go +++ b/syft/pkg/cataloger/golang/parse_go_bin.go @@ -29,13 +29,11 @@ var ( func makeGoMainPackage(mod *debug.BuildInfo, arch string, location source.Location) pkg.Package { gbs := getBuildSettings(mod.Settings) main := newGoBinaryPackage(&mod.Main, mod.GoVersion, arch, location, gbs) - main.Version = "(devel)" if v, ok := gbs["vcs.revision"]; ok { main.Version = v } - main.SetID() return main } diff --git a/syft/pkg/cataloger/golang/parse_go_bin_test.go b/syft/pkg/cataloger/golang/parse_go_bin_test.go index 19888f79df4..9fddb20501c 100644 --- a/syft/pkg/cataloger/golang/parse_go_bin_test.go +++ b/syft/pkg/cataloger/golang/parse_go_bin_test.go @@ -213,7 +213,7 @@ func TestBuildGoPkgInfo(t *testing.T) { arch: archDetails, mod: &debug.BuildInfo{ GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft"}, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ {Key: "GOARCH", Value: archDetails}, {Key: "GOOS", Value: "darwin"}, @@ -227,7 +227,7 @@ func TestBuildGoPkgInfo(t *testing.T) { arch: archDetails, mod: &debug.BuildInfo{ GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft"}, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ {Key: "GOARCH", Value: archDetails}, {Key: "GOOS", Value: "darwin"}, @@ -297,7 +297,7 @@ func TestBuildGoPkgInfo(t *testing.T) { arch: archDetails, mod: &debug.BuildInfo{ GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft"}, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ {Key: "GOARCH", Value: archDetails}, {Key: "GOOS", Value: "darwin"}, From d2f053bc715150a142b667eae7dcac166b439c14 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 5 May 2022 11:12:11 -0400 Subject: [PATCH 6/7] unblock timeout for power-user select CLI tests (#985) * update to use shared secretsFixture to prevent race Signed-off-by: Christopher Phillips --- test/cli/power_user_cmd_test.go | 41 ++++++++++--------- .../image-secrets-dir/Dockerfile | 2 + .../image-secrets-dir/api-key.txt | 1 + 3 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 test/cli/test-fixtures/image-secrets-dir/Dockerfile create mode 100644 test/cli/test-fixtures/image-secrets-dir/api-key.txt diff --git a/test/cli/power_user_cmd_test.go b/test/cli/power_user_cmd_test.go index 95e698cb1f5..8091ebd35d2 100644 --- a/test/cli/power_user_cmd_test.go +++ b/test/cli/power_user_cmd_test.go @@ -6,6 +6,7 @@ import ( ) func TestPowerUserCmdFlags(t *testing.T) { + secretsFixture := getFixtureImage(t, "image-secrets") tests := []struct { name string args []string @@ -33,45 +34,45 @@ func TestPowerUserCmdFlags(t *testing.T) { }, }, { - name: "defaut-secrets-results-w-reveal-values", + name: "content-cataloger-wired-up", + args: []string{"power-user", "docker-archive:" + secretsFixture}, env: map[string]string{ - "SYFT_SECRETS_REVEAL_VALUES": "true", + "SYFT_FILE_CONTENTS_GLOBS": "/api-key.txt", }, - args: []string{"power-user", "docker-archive:" + getFixtureImage(t, "image-secrets")}, assertions: []traitAssertion{ - assertInOutput(`"classification": "generic-api-key"`), // proof of the secrets cataloger finding something - assertInOutput(`"12345A7a901b345678901234567890123456789012345678901234567890"`), // proof of the secrets cataloger finding the api key + assertInOutput(`"contents": "c29tZV9BcEkta0V5ID0gIjEyMzQ1QTdhOTAxYjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MCIK"`), // proof of the content cataloger assertSuccessfulReturnCode, }, }, { - name: "default-secret-results-dont-reveal-values", - args: []string{"power-user", "docker-archive:" + getFixtureImage(t, "image-secrets")}, + name: "default-dir-results-w-pkg-coverage", + args: []string{"power-user", "dir:test-fixtures/image-pkg-coverage"}, assertions: []traitAssertion{ - assertInOutput(`"classification": "generic-api-key"`), // proof of the secrets cataloger finding something - assertNotInOutput(`"12345A7a901b345678901234567890123456789012345678901234567890"`), // proof of the secrets cataloger finding the api key + assertNotInOutput(" command is deprecated"), // only the root command should be deprecated + assertInOutput(`"type": "RegularFile"`), // proof of file-metadata data + assertInOutput(`"algorithm": "sha256"`), // proof of file-metadata default digest algorithm of sha256 + assertInOutput(`"metadataType": "ApkMetadata"`), // proof of package artifacts data assertSuccessfulReturnCode, }, }, { - name: "content-cataloger-wired-up", - args: []string{"power-user", "docker-archive:" + getFixtureImage(t, "image-secrets")}, + name: "default-secrets-results-w-reveal-values", env: map[string]string{ - "SYFT_FILE_CONTENTS_GLOBS": "/api-key.txt", + "SYFT_SECRETS_REVEAL_VALUES": "true", }, + args: []string{"power-user", "docker-archive:" + secretsFixture}, assertions: []traitAssertion{ - assertInOutput(`"contents": "c29tZV9BcEkta0V5ID0gIjEyMzQ1QTdhOTAxYjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MCIK"`), // proof of the content cataloger + assertInOutput(`"classification": "generic-api-key"`), // proof of the secrets cataloger finding something + assertInOutput(`"12345A7a901b345678901234567890123456789012345678901234567890"`), // proof of the secrets cataloger finding the api key assertSuccessfulReturnCode, }, }, { - name: "default-dir-results-w-pkg-coverage", - args: []string{"power-user", "dir:test-fixtures/image-pkg-coverage"}, + name: "default-secret-results-dont-reveal-values", + args: []string{"power-user", "docker-archive:" + secretsFixture}, assertions: []traitAssertion{ - assertNotInOutput(" command is deprecated"), // only the root command should be deprecated - assertInOutput(`"type": "RegularFile"`), // proof of file-metadata data - assertInOutput(`"algorithm": "sha256"`), // proof of file-metadata default digest algorithm of sha256 - assertInOutput(`"metadataType": "ApkMetadata"`), // proof of package artifacts data + assertInOutput(`"classification": "generic-api-key"`), // proof of the secrets cataloger finding something + assertNotInOutput(`"12345A7a901b345678901234567890123456789012345678901234567890"`), // proof of the secrets cataloger finding the api key assertSuccessfulReturnCode, }, }, @@ -80,7 +81,7 @@ func TestPowerUserCmdFlags(t *testing.T) { env: map[string]string{ "SYFT_SECRETS_REVEAL_VALUES": "true", }, - args: []string{"power-user", "dir:test-fixtures/image-secrets"}, + args: []string{"power-user", "dir:test-fixtures/image-secrets-dir"}, assertions: []traitAssertion{ assertInOutput(`"classification": "generic-api-key"`), // proof of the secrets cataloger finding something assertInOutput(`"12345A7a901b345678901234567890123456789012345678901234567890"`), // proof of the secrets cataloger finding the api key diff --git a/test/cli/test-fixtures/image-secrets-dir/Dockerfile b/test/cli/test-fixtures/image-secrets-dir/Dockerfile new file mode 100644 index 00000000000..28a283a0bb3 --- /dev/null +++ b/test/cli/test-fixtures/image-secrets-dir/Dockerfile @@ -0,0 +1,2 @@ +FROM scratch +ADD api-key.txt . \ No newline at end of file diff --git a/test/cli/test-fixtures/image-secrets-dir/api-key.txt b/test/cli/test-fixtures/image-secrets-dir/api-key.txt new file mode 100644 index 00000000000..6cbbd99e3e8 --- /dev/null +++ b/test/cli/test-fixtures/image-secrets-dir/api-key.txt @@ -0,0 +1 @@ +some_ApI-kEy = "12345A7a901b345678901234567890123456789012345678901234567890" From 1cea0ecd5c4faa5cd3e0eb5271c14042e64681b7 Mon Sep 17 00:00:00 2001 From: Christian Kotzbauer Date: Thu, 5 May 2022 21:32:02 +0200 Subject: [PATCH 7/7] feat: add initial dotnet-support (#951) * feat: add initial dotnet-support Signed-off-by: Christian Kotzbauer * fix: add path, sha512 and hashpath Signed-off-by: Christian Kotzbauer * fix: add missing dot Signed-off-by: Christian Kotzbauer * fix: lint warnings Signed-off-by: Christian Kotzbauer * fix CLI test package counts to account for dotnet Signed-off-by: Alex Goodman * fix: updated packagurl-go Signed-off-by: Christian Kotzbauer * tidy go.sum Signed-off-by: Alex Goodman * update json schema Signed-off-by: Alex Goodman Co-authored-by: Alex Goodman --- README.md | 1 + go.mod | 2 +- go.sum | 4 +- internal/constants.go | 2 +- .../formats/common/spdxhelpers/source_info.go | 2 + .../common/spdxhelpers/source_info_test.go | 8 + internal/formats/syftjson/model/package.go | 6 + .../snapshot/TestDirectoryEncoder.golden | 4 +- .../TestEncodeFullJSONDocument.golden | 4 +- .../snapshot/TestImageEncoder.golden | 4 +- schema/json/generate.go | 1 + schema/json/schema-3.2.3.json | 1298 +++++++++++++++++ syft/pkg/cataloger/cataloger.go | 4 + syft/pkg/cataloger/dotnet/cataloger.go | 14 + .../pkg/cataloger/dotnet/parse_dotnet_deps.go | 72 + .../dotnet/parse_dotnet_deps_test.go | 199 +++ .../test-fixtures/TestLibrary.deps.json | 235 +++ syft/pkg/dotnet_deps_metadata.go | 27 + syft/pkg/language.go | 4 + syft/pkg/language_test.go | 4 + syft/pkg/metadata.go | 3 + syft/pkg/type.go | 6 + syft/pkg/type_test.go | 5 + syft/pkg/url_test.go | 14 + test/cli/packages_cmd_test.go | 2 +- .../catalog_packages_cases_test.go | 19 + test/integration/catalog_packages_test.go | 2 + .../pkgs/dotnet/TestLibrary.deps.json | 235 +++ 28 files changed, 2170 insertions(+), 11 deletions(-) create mode 100644 schema/json/schema-3.2.3.json create mode 100644 syft/pkg/cataloger/dotnet/cataloger.go create mode 100644 syft/pkg/cataloger/dotnet/parse_dotnet_deps.go create mode 100644 syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go create mode 100644 syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json create mode 100644 syft/pkg/dotnet_deps_metadata.go create mode 100644 test/integration/test-fixtures/image-pkg-coverage/pkgs/dotnet/TestLibrary.deps.json diff --git a/README.md b/README.md index fd184197777..7abac912518 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ A CLI tool and Go library for generating a Software Bill of Materials (SBOM) fro - Alpine (apk) - Dart (pubs) - Debian (dpkg) +- Dotnet (deps.json) - Go (go.mod, Go binaries) - Java (jar, ear, war, par, sar) - JavaScript (npm, yarn) diff --git a/go.mod b/go.mod index 8c91c7472d5..9272bdc8860 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,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.1.1-0.20220314153042-1bcd40e5206b + github.com/anchore/packageurl-go v0.1.1-0.20220428202044-a072fa3cb6d7 github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270 github.com/antihax/optional v1.0.0 github.com/bmatcuk/doublestar/v4 v4.0.2 diff --git a/go.sum b/go.sum index 0fce1755655..083c0c621fd 100644 --- a/go.sum +++ b/go.sum @@ -272,8 +272,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.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/packageurl-go v0.1.1-0.20220428202044-a072fa3cb6d7 h1:kDrYkTSM9uIxaX/P9s0F4nKYNM+hnSgLJdLpqvsaQ/g= +github.com/anchore/packageurl-go v0.1.1-0.20220428202044-a072fa3cb6d7/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270 h1:NmxPDR6vo3xjwCL6o+tpF1vUad/BVo+WaVSwueB9W9w= github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270/go.mod h1:yoCLUZY0k/pYLNIy0L80p2Ko0PKVNXm8rHtgxp4OiSc= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= diff --git a/internal/constants.go b/internal/constants.go index da098e08910..bb241541d00 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -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.2.2" + JSONSchemaVersion = "3.2.3" ) diff --git a/internal/formats/common/spdxhelpers/source_info.go b/internal/formats/common/spdxhelpers/source_info.go index 1806f72dfb1..7099ed7dd9f 100644 --- a/internal/formats/common/spdxhelpers/source_info.go +++ b/internal/formats/common/spdxhelpers/source_info.go @@ -17,6 +17,8 @@ func SourceInfo(p pkg.Package) string { answer = "acquired package info from pubspec manifest" case pkg.DebPkg: answer = "acquired package info from DPKG DB" + case pkg.DotnetPkg: + answer = "acquired package info from dotnet project assets file" case pkg.NpmPkg: answer = "acquired package info from installed node module manifest file" case pkg.PythonPkg: diff --git a/internal/formats/common/spdxhelpers/source_info_test.go b/internal/formats/common/spdxhelpers/source_info_test.go index 4016ad2be51..591c15db0d6 100644 --- a/internal/formats/common/spdxhelpers/source_info_test.go +++ b/internal/formats/common/spdxhelpers/source_info_test.go @@ -134,6 +134,14 @@ func Test_SourceInfo(t *testing.T) { "from pubspec manifest", }, }, + { + input: pkg.Package{ + Type: pkg.DotnetPkg, + }, + expected: []string{ + "from dotnet project assets file", + }, + }, } var pkgTypes []pkg.Type for _, test := range tests { diff --git a/internal/formats/syftjson/model/package.go b/internal/formats/syftjson/model/package.go index 9ae55ee51cd..101df068665 100644 --- a/internal/formats/syftjson/model/package.go +++ b/internal/formats/syftjson/model/package.go @@ -136,6 +136,12 @@ func (p *Package) UnmarshalJSON(b []byte) error { return err } p.Metadata = payload + case pkg.DotnetDepsMetadataType: + var payload pkg.DotnetDepsMetadata + if err := json.Unmarshal(unpacker.Metadata, &payload); err != nil { + return err + } + p.Metadata = payload default: log.Warnf("unknown package metadata type=%q for packageID=%q", p.MetadataType, p.ID) } diff --git a/internal/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/internal/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index ae51ed71720..cd168776269 100644 --- a/internal/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/internal/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -88,7 +88,7 @@ } }, "schema": { - "version": "3.2.2", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.2.json" + "version": "3.2.3", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.3.json" } } diff --git a/internal/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/internal/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index 2fe4ba0dcb7..95e55a9ce3d 100644 --- a/internal/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/internal/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -184,7 +184,7 @@ } }, "schema": { - "version": "3.2.2", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.2.json" + "version": "3.2.3", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.3.json" } } diff --git a/internal/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/internal/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index 10a5ced626f..ca164b40bf7 100644 --- a/internal/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/internal/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -111,7 +111,7 @@ } }, "schema": { - "version": "3.2.2", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.2.json" + "version": "3.2.3", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.3.json" } } diff --git a/schema/json/generate.go b/schema/json/generate.go index a2ff68e57d2..b6c8cf76123 100644 --- a/schema/json/generate.go +++ b/schema/json/generate.go @@ -38,6 +38,7 @@ type artifactMetadataContainer struct { Go pkg.GolangBinMetadata Php pkg.PhpComposerJSONMetadata Dart pkg.DartPubMetadata + Dotnet pkg.DotnetDepsMetadata } func main() { diff --git a/schema/json/schema-3.2.3.json b/schema/json/schema-3.2.3.json new file mode 100644 index 00000000000..3018f70c97f --- /dev/null +++ b/schema/json/schema-3.2.3.json @@ -0,0 +1,1298 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Document", + "definitions": { + "ApkFileRecord": { + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Digest" + } + }, + "additionalProperties": true, + "type": "object" + }, + "ApkMetadata": { + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "license", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ], + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "type": "string" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/ApkFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "CargoPackageMetadata": { + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Classification": { + "required": [ + "class", + "metadata" + ], + "properties": { + "class": { + "type": "string" + }, + "metadata": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Coordinates": { + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "DartPubMetadata": { + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Descriptor": { + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": { + "additionalProperties": true + } + }, + "additionalProperties": true, + "type": "object" + }, + "Digest": { + "required": [ + "algorithm", + "value" + ], + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Document": { + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ], + "properties": { + "artifacts": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Secrets" + }, + "type": "array" + }, + "source": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Source" + }, + "distro": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/LinuxRelease" + }, + "descriptor": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Descriptor" + }, + "schema": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Schema" + } + }, + "additionalProperties": true, + "type": "object" + }, + "DotnetDepsMetadata": { + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "DpkgFileRecord": { + "required": [ + "path", + "isConfigFile" + ], + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/definitions/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "additionalProperties": true, + "type": "object" + }, + "DpkgMetadata": { + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ], + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/DpkgFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "File": { + "required": [ + "id", + "location" + ], + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/definitions/Coordinates" + }, + "metadata": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Digest" + }, + "type": "array" + }, + "classifications": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Classification" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "FileMetadataEntry": { + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType" + ], + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "GemMetadata": { + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "GolangBinMetadata": { + "required": [ + "goCompiledVersion", + "architecture" + ], + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object" + }, + "JavaMetadata": { + "required": [ + "virtualPath" + ], + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/JavaManifest" + }, + "pomProperties": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PomProperties" + }, + "pomProject": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PomProject" + }, + "digest": { + "items": { + "$ref": "#/definitions/Digest" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "items": { + "type": "string" + }, + "type": "array" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "NpmPackageJSONMetadata": { + "required": [ + "name", + "version", + "author", + "licenses", + "homepage", + "description", + "url" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "author": { + "type": "string" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Package": { + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Coordinates" + }, + "type": "array" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/definitions/ApkMetadata" + }, + { + "$ref": "#/definitions/CargoPackageMetadata" + }, + { + "$ref": "#/definitions/DartPubMetadata" + }, + { + "$ref": "#/definitions/DotnetDepsMetadata" + }, + { + "$ref": "#/definitions/DpkgMetadata" + }, + { + "$ref": "#/definitions/GemMetadata" + }, + { + "$ref": "#/definitions/GolangBinMetadata" + }, + { + "$ref": "#/definitions/JavaMetadata" + }, + { + "$ref": "#/definitions/NpmPackageJSONMetadata" + }, + { + "$ref": "#/definitions/PhpComposerJSONMetadata" + }, + { + "$ref": "#/definitions/PythonPackageMetadata" + }, + { + "$ref": "#/definitions/RpmdbMetadata" + } + ] + } + }, + "additionalProperties": true, + "type": "object" + }, + "PhpComposerAuthors": { + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PhpComposerExternalReference": { + "required": [ + "type", + "url", + "reference" + ], + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PhpComposerJSONMetadata": { + "required": [ + "name", + "version", + "source", + "dist" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/definitions/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PomParent": { + "required": [ + "groupId", + "artifactId", + "version" + ], + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PomProject": { + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ], + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PomProperties": { + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version", + "extraFields" + ], + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonDirectURLOriginInfo": { + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonFileDigest": { + "required": [ + "algorithm", + "value" + ], + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonFileRecord": { + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonPackageMetadata": { + "required": [ + "name", + "version", + "license", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PythonDirectURLOriginInfo" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Relationship": { + "required": [ + "parent", + "child", + "type" + ], + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": { + "additionalProperties": true + } + }, + "additionalProperties": true, + "type": "object" + }, + "RpmdbFileRecord": { + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ], + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/definitions/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "RpmdbMetadata": { + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "license", + "vendor", + "files" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "license": { + "type": "string" + }, + "vendor": { + "type": "string" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/RpmdbFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Schema": { + "required": [ + "version", + "url" + ], + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "SearchResult": { + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ], + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Secrets": { + "required": [ + "location", + "secrets" + ], + "properties": { + "location": { + "$ref": "#/definitions/Coordinates" + }, + "secrets": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/SearchResult" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Source": { + "required": [ + "type", + "target" + ], + "properties": { + "type": { + "type": "string" + }, + "target": { + "additionalProperties": true + } + }, + "additionalProperties": true, + "type": "object" + } + } +} diff --git a/syft/pkg/cataloger/cataloger.go b/syft/pkg/cataloger/cataloger.go index 8e10bf5d626..149091a95ce 100644 --- a/syft/pkg/cataloger/cataloger.go +++ b/syft/pkg/cataloger/cataloger.go @@ -11,6 +11,7 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/apkdb" "github.com/anchore/syft/syft/pkg/cataloger/dart" "github.com/anchore/syft/syft/pkg/cataloger/deb" + "github.com/anchore/syft/syft/pkg/cataloger/dotnet" "github.com/anchore/syft/syft/pkg/cataloger/golang" "github.com/anchore/syft/syft/pkg/cataloger/java" "github.com/anchore/syft/syft/pkg/cataloger/javascript" @@ -44,6 +45,7 @@ func ImageCatalogers(cfg Config) []Cataloger { java.NewJavaCataloger(cfg.Java()), apkdb.NewApkdbCataloger(), golang.NewGoModuleBinaryCataloger(), + dotnet.NewDotnetDepsCataloger(), } } @@ -63,6 +65,7 @@ func DirectoryCatalogers(cfg Config) []Cataloger { golang.NewGoModFileCataloger(), rust.NewCargoLockCataloger(), dart.NewPubspecLockCataloger(), + dotnet.NewDotnetDepsCataloger(), } } @@ -83,5 +86,6 @@ func AllCatalogers(cfg Config) []Cataloger { golang.NewGoModFileCataloger(), rust.NewCargoLockCataloger(), dart.NewPubspecLockCataloger(), + dotnet.NewDotnetDepsCataloger(), } } diff --git a/syft/pkg/cataloger/dotnet/cataloger.go b/syft/pkg/cataloger/dotnet/cataloger.go new file mode 100644 index 00000000000..e5907e9ec42 --- /dev/null +++ b/syft/pkg/cataloger/dotnet/cataloger.go @@ -0,0 +1,14 @@ +package dotnet + +import ( + "github.com/anchore/syft/syft/pkg/cataloger/common" +) + +// NewDotnetDepsCataloger returns a new Dotnet cataloger object base on deps json files. +func NewDotnetDepsCataloger() *common.GenericCataloger { + globParsers := map[string]common.ParserFn{ + "**/*.deps.json": parseDotnetDeps, + } + + return common.NewGenericCataloger(nil, globParsers, "dotnet-deps-cataloger") +} diff --git a/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go b/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go new file mode 100644 index 00000000000..c6128197592 --- /dev/null +++ b/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go @@ -0,0 +1,72 @@ +package dotnet + +import ( + "encoding/json" + "fmt" + "io" + "strings" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/common" +) + +// integrity check +var _ common.ParserFn = parseDotnetDeps + +type dotnetDeps struct { + Libraries map[string]dotnetDepsLibrary `json:"libraries"` +} + +type dotnetDepsLibrary struct { + Type string `json:"type"` + Path string `json:"path"` + Sha512 string `json:"sha512"` + HashPath string `json:"hashPath"` +} + +func parseDotnetDeps(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) { + var packages []*pkg.Package + + dec := json.NewDecoder(reader) + + var p dotnetDeps + if err := dec.Decode(&p); err != nil { + return nil, nil, fmt.Errorf("failed to parse deps.json file: %w", err) + } + + for nameVersion, lib := range p.Libraries { + dotnetPkg := newDotnetDepsPackage(nameVersion, lib) + + if dotnetPkg != nil { + packages = append(packages, dotnetPkg) + } + } + + return packages, nil, nil +} + +func newDotnetDepsPackage(nameVersion string, lib dotnetDepsLibrary) *pkg.Package { + if lib.Type != "package" { + return nil + } + + splitted := strings.Split(nameVersion, "/") + name := splitted[0] + version := splitted[1] + + return &pkg.Package{ + Name: name, + Version: version, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: &pkg.DotnetDepsMetadata{ + Name: name, + Version: version, + Path: lib.Path, + Sha512: lib.Sha512, + HashPath: lib.HashPath, + }, + } +} diff --git a/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go b/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go new file mode 100644 index 00000000000..2907c362891 --- /dev/null +++ b/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go @@ -0,0 +1,199 @@ +package dotnet + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/syft/pkg" +) + +func assertPackagesEqual(t *testing.T, actual []*pkg.Package, expected map[string]*pkg.Package) { + assert.Len(t, actual, len(expected)) +} + +func TestParseDotnetDeps(t *testing.T) { + expected := map[string]*pkg.Package{ + "AWSSDK.Core": { + Name: "AWSSDK.Core", + Version: "3.7.10.6", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "AWSSDK.Core", + Version: "3.7.10.6", + Sha512: "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==", + Path: "awssdk.core/3.7.10.6", + HashPath: "awssdk.core.3.7.10.6.nupkg.sha512", + }, + }, + "Microsoft.Extensions.DependencyInjection": { + Name: "Microsoft.Extensions.DependencyInjection", + Version: "6.0.0", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.DependencyInjection", + Version: "6.0.0", + Sha512: "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + Path: "microsoft.extensions.dependencyinjection/6.0.0", + HashPath: "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512", + }, + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + Name: "Microsoft.Extensions.DependencyInjection.Abstractions", + Version: "6.0.0", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.DependencyInjection", + Version: "6.0.0", + Sha512: "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==", + Path: "microsoft.extensions.dependencyinjection.abstractions/6.0.0", + HashPath: "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512", + }, + }, + "Microsoft.Extensions.Logging": { + Name: "Microsoft.Extensions.Logging", + Version: "6.0.0", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Logging", + Version: "6.0.0", + Sha512: "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + Path: "microsoft.extensions.logging/6.0.0", + HashPath: "microsoft.extensions.logging.6.0.0.nupkg.sha512", + }, + }, + "Microsoft.Extensions.Logging.Abstractions": { + Name: "Microsoft.Extensions.Logging.Abstractions", + Version: "6.0.0", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Logging", + Version: "6.0.0", + Sha512: "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==", + Path: "microsoft.extensions.logging.abstractions/6.0.0", + HashPath: "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512", + }, + }, + "Microsoft.Extensions.Options": { + Name: "Microsoft.Extensions.Options", + Version: "6.0.0", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Options", + Version: "6.0.0", + Sha512: "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + Path: "microsoft.extensions.options/6.0.0", + HashPath: "microsoft.extensions.options.6.0.0.nupkg.sha512", + }, + }, + "Microsoft.Extensions.Primitives": { + Name: "Microsoft.Extensions.Primitives", + Version: "6.0.0", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Primitives", + Version: "6.0.0", + Sha512: "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + Path: "microsoft.extensions.primitives/6.0.0", + HashPath: "microsoft.extensions.primitives.6.0.0.nupkg.sha512", + }, + }, + "Newtonsoft.Json": { + Name: "Newtonsoft.Json", + Version: "13.0.1", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Newtonsoft.Json", + Version: "13.0.1", + Sha512: "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==", + Path: "newtonsoft.json/13.0.1", + HashPath: "newtonsoft.json.13.0.1.nupkg.sha512", + }, + }, + "Serilog": { + Name: "Serilog", + Version: "2.10.0", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Serilog", + Version: "2.10.0", + Sha512: "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==", + Path: "serilog/2.10.0", + HashPath: "serilog.2.10.0.nupkg.sha512", + }, + }, + "Serilog.Sinks.Console": { + Name: "Serilog.Sinks.Console", + Version: "4.0.1", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Serilog.Sinks.Console", + Version: "4.0.1", + Sha512: "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==", + Path: "serilog.sinks.console/4.0.1", + HashPath: "serilog.sinks.console.4.0.1.nupkg.sha512", + }, + }, + "System.Diagnostics.DiagnosticSource": { + Name: "System.Diagnostics.DiagnosticSource", + Version: "6.0.0", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "System.Diagnostics.DiagnosticSource", + Version: "6.0.0", + Sha512: "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + Path: "system.diagnostics.diagnosticsource/6.0.0", + HashPath: "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512", + }, + }, + "System.Runtime.CompilerServices.Unsafe": { + Name: "System.Runtime.CompilerServices.Unsafe", + Version: "6.0.0", + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "System.Runtime.CompilerServices.Unsafe", + Version: "6.0.0", + Sha512: "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==", + Path: "system.runtime.compilerservices.unsafe/6.0.0", + HashPath: "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512", + }, + }, + } + + fixture, err := os.Open("test-fixtures/TestLibrary.deps.json") + if err != nil { + t.Fatalf("failed to open fixture: %+v", err) + } + + actual, _, err := parseDotnetDeps(fixture.Name(), fixture) + if err != nil { + t.Fatalf("failed to parse deps.json: %+v", err) + } + + assertPackagesEqual(t, actual, expected) +} diff --git a/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json b/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json new file mode 100644 index 00000000000..a429ec4094d --- /dev/null +++ b/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json @@ -0,0 +1,235 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v6.0", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v6.0": { + "TestLibrary/1.0.0": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Newtonsoft.Json": "13.0.1", + "Serilog": "2.10.0", + "Serilog.Sinks.Console": "4.0.1", + "TestCommon": "1.0.0" + }, + "runtime": { + "TestLibrary.dll": {} + } + }, + "AWSSDK.Core/3.7.10.6": { + "runtime": { + "lib/netcoreapp3.1/AWSSDK.Core.dll": { + "assemblyVersion": "3.3.0.0", + "fileVersion": "3.7.10.6" + } + } + }, + "Microsoft.Extensions.DependencyInjection/6.0.0": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + }, + "runtime": { + "lib/net6.0/Microsoft.Extensions.DependencyInjection.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0": { + "runtime": { + "lib/net6.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Microsoft.Extensions.Logging/6.0.0": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + }, + "runtime": { + "lib/netstandard2.1/Microsoft.Extensions.Logging.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Microsoft.Extensions.Logging.Abstractions/6.0.0": { + "runtime": { + "lib/net6.0/Microsoft.Extensions.Logging.Abstractions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Microsoft.Extensions.Options/6.0.0": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + }, + "runtime": { + "lib/netstandard2.1/Microsoft.Extensions.Options.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Microsoft.Extensions.Primitives/6.0.0": { + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + }, + "runtime": { + "lib/net6.0/Microsoft.Extensions.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Newtonsoft.Json/13.0.1": { + "runtime": { + "lib/netstandard2.0/Newtonsoft.Json.dll": { + "assemblyVersion": "13.0.0.0", + "fileVersion": "13.0.1.25517" + } + } + }, + "Serilog/2.10.0": { + "runtime": { + "lib/netstandard2.1/Serilog.dll": { + "assemblyVersion": "2.0.0.0", + "fileVersion": "2.10.0.0" + } + } + }, + "Serilog.Sinks.Console/4.0.1": { + "dependencies": { + "Serilog": "2.10.0" + }, + "runtime": { + "lib/net5.0/Serilog.Sinks.Console.dll": { + "assemblyVersion": "4.0.1.0", + "fileVersion": "4.0.1.0" + } + } + }, + "System.Diagnostics.DiagnosticSource/6.0.0": { + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe/6.0.0": {}, + "TestCommon/1.0.0": { + "dependencies": { + "AWSSDK.Core": "3.7.10.6" + }, + "runtime": { + "TestCommon.dll": {} + } + } + } + }, + "libraries": { + "TestLibrary/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "AWSSDK.Core/3.7.10.6": { + "type": "package", + "serviceable": true, + "sha512": "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==", + "path": "awssdk.core/3.7.10.6", + "hashPath": "awssdk.core.3.7.10.6.nupkg.sha512" + }, + "Microsoft.Extensions.DependencyInjection/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "path": "microsoft.extensions.dependencyinjection/6.0.0", + "hashPath": "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==", + "path": "microsoft.extensions.dependencyinjection.abstractions/6.0.0", + "hashPath": "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Logging/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "path": "microsoft.extensions.logging/6.0.0", + "hashPath": "microsoft.extensions.logging.6.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Logging.Abstractions/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==", + "path": "microsoft.extensions.logging.abstractions/6.0.0", + "hashPath": "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Options/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "path": "microsoft.extensions.options/6.0.0", + "hashPath": "microsoft.extensions.options.6.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Primitives/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "path": "microsoft.extensions.primitives/6.0.0", + "hashPath": "microsoft.extensions.primitives.6.0.0.nupkg.sha512" + }, + "Newtonsoft.Json/13.0.1": { + "type": "package", + "serviceable": true, + "sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==", + "path": "newtonsoft.json/13.0.1", + "hashPath": "newtonsoft.json.13.0.1.nupkg.sha512" + }, + "Serilog/2.10.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==", + "path": "serilog/2.10.0", + "hashPath": "serilog.2.10.0.nupkg.sha512" + }, + "Serilog.Sinks.Console/4.0.1": { + "type": "package", + "serviceable": true, + "sha512": "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==", + "path": "serilog.sinks.console/4.0.1", + "hashPath": "serilog.sinks.console.4.0.1.nupkg.sha512" + }, + "System.Diagnostics.DiagnosticSource/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "path": "system.diagnostics.diagnosticsource/6.0.0", + "hashPath": "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512" + }, + "System.Runtime.CompilerServices.Unsafe/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==", + "path": "system.runtime.compilerservices.unsafe/6.0.0", + "hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512" + }, + "TestCommon/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/syft/pkg/dotnet_deps_metadata.go b/syft/pkg/dotnet_deps_metadata.go new file mode 100644 index 00000000000..faac92839da --- /dev/null +++ b/syft/pkg/dotnet_deps_metadata.go @@ -0,0 +1,27 @@ +package pkg + +import ( + "github.com/anchore/packageurl-go" + "github.com/anchore/syft/syft/linux" +) + +type DotnetDepsMetadata struct { + Name string `mapstructure:"name" json:"name"` + Version string `mapstructure:"version" json:"version"` + Path string `mapstructure:"path" json:"path"` + Sha512 string `mapstructure:"sha512" json:"sha512"` + HashPath string `mapstructure:"hashPath" json:"hashPath"` +} + +func (m DotnetDepsMetadata) PackageURL(_ *linux.Release) string { + var qualifiers packageurl.Qualifiers + + return packageurl.NewPackageURL( + packageurl.TypeDotnet, + "", + m.Name, + m.Version, + qualifiers, + "", + ).ToString() +} diff --git a/syft/pkg/language.go b/syft/pkg/language.go index d9c4905b1dd..ad05670cd60 100644 --- a/syft/pkg/language.go +++ b/syft/pkg/language.go @@ -20,6 +20,7 @@ const ( Go Language = "go" Rust Language = "rust" Dart Language = "dart" + Dotnet Language = "dotnet" ) // AllLanguages is a set of all programming languages detected by syft. @@ -32,6 +33,7 @@ var AllLanguages = []Language{ Go, Rust, Dart, + Dotnet, } // String returns the string representation of the language. @@ -66,6 +68,8 @@ func LanguageByName(name string) Language { return Rust case packageurl.TypePub, string(Dart): return Dart + case packageurl.TypeDotnet: + return Dotnet default: return UnknownLanguage } diff --git a/syft/pkg/language_test.go b/syft/pkg/language_test.go index 2e1d5bc9abe..0f2a8a544e4 100644 --- a/syft/pkg/language_test.go +++ b/syft/pkg/language_test.go @@ -34,6 +34,10 @@ func TestLanguageFromPURL(t *testing.T) { purl: "pkg:pub/util@1.2.34", want: Dart, }, + { + purl: "pkg:dotnet/Microsoft.CodeAnalysis.Razor@2.2.0", + want: Dotnet, + }, { purl: "pkg:cargo/clap@2.33.0", want: Rust, diff --git a/syft/pkg/metadata.go b/syft/pkg/metadata.go index efd11e3b026..e96f9c8f8a6 100644 --- a/syft/pkg/metadata.go +++ b/syft/pkg/metadata.go @@ -18,6 +18,7 @@ const ( NpmPackageJSONMetadataType MetadataType = "NpmPackageJsonMetadata" RpmdbMetadataType MetadataType = "RpmdbMetadata" DartPubMetadataType MetadataType = "DartPubMetadata" + DotnetDepsMetadataType MetadataType = "DotnetDepsMetadata" PythonPackageMetadataType MetadataType = "PythonPackageMetadata" RustCargoPackageMetadataType MetadataType = "RustCargoPackageMetadata" KbPackageMetadataType MetadataType = "KbPackageMetadata" @@ -33,6 +34,7 @@ var AllMetadataTypes = []MetadataType{ NpmPackageJSONMetadataType, RpmdbMetadataType, DartPubMetadataType, + DotnetDepsMetadataType, PythonPackageMetadataType, RustCargoPackageMetadataType, KbPackageMetadataType, @@ -48,6 +50,7 @@ var MetadataTypeByName = map[MetadataType]reflect.Type{ NpmPackageJSONMetadataType: reflect.TypeOf(NpmPackageJSONMetadata{}), RpmdbMetadataType: reflect.TypeOf(RpmdbMetadata{}), DartPubMetadataType: reflect.TypeOf(DartPubMetadata{}), + DotnetDepsMetadataType: reflect.TypeOf(DotnetDepsMetadata{}), PythonPackageMetadataType: reflect.TypeOf(PythonPackageMetadata{}), RustCargoPackageMetadataType: reflect.TypeOf(CargoMetadata{}), KbPackageMetadataType: reflect.TypeOf(KbPackageMetadata{}), diff --git a/syft/pkg/type.go b/syft/pkg/type.go index ffa965bb958..01f87b161bd 100644 --- a/syft/pkg/type.go +++ b/syft/pkg/type.go @@ -21,6 +21,7 @@ const ( RustPkg Type = "rust-crate" KbPkg Type = "msrc-kb" DartPubPkg Type = "dart-pub" + DotnetPkg Type = "dotnet" ) // AllPkgs represents all supported package types @@ -38,6 +39,7 @@ var AllPkgs = []Type{ RustPkg, KbPkg, DartPubPkg, + DotnetPkg, } // PackageURLType returns the PURL package type for the current package. @@ -65,6 +67,8 @@ func (t Type) PackageURLType() string { return "cargo" case DartPubPkg: return packageurl.TypePub + case DotnetPkg: + return packageurl.TypeDotnet default: // TODO: should this be a "generic" purl type instead? return "" @@ -104,6 +108,8 @@ func TypeByName(name string) Type { return RustPkg case packageurl.TypePub: return DartPubPkg + case packageurl.TypeDotnet: + return DotnetPkg default: return UnknownPkg } diff --git a/syft/pkg/type_test.go b/syft/pkg/type_test.go index ade1f0913c1..48e25a69ca7 100644 --- a/syft/pkg/type_test.go +++ b/syft/pkg/type_test.go @@ -51,6 +51,11 @@ func TestTypeFromPURL(t *testing.T) { purl: "pkg:pub/util@1.2.34?hosted_url=pub.hosted.org", expected: DartPubPkg, }, + + { + purl: "pkg:dotnet/Microsoft.CodeAnalysis.Razor@2.2.0", + expected: DotnetPkg, + }, { purl: "pkg:composer/laravel/laravel@5.5.0", expected: PhpComposerPkg, diff --git a/syft/pkg/url_test.go b/syft/pkg/url_test.go index 7d1ca3381e4..91321e0300a 100644 --- a/syft/pkg/url_test.go +++ b/syft/pkg/url_test.go @@ -39,6 +39,20 @@ func TestPackageURL(t *testing.T) { }, expected: "pkg:pub/name@0.2.0?hosted_url=pub.hosted.org", }, + + { + name: "dotnet", + pkg: Package{ + Name: "Microsoft.CodeAnalysis.Razor", + Version: "2.2.0", + Type: DotnetPkg, + Metadata: DotnetDepsMetadata{ + Name: "Microsoft.CodeAnalysis.Razor", + Version: "2.2.0", + }, + }, + expected: "pkg:dotnet/Microsoft.CodeAnalysis.Razor@2.2.0", + }, { name: "python", pkg: Package{ diff --git a/test/cli/packages_cmd_test.go b/test/cli/packages_cmd_test.go index 898727af0fc..c7ec797bcaa 100644 --- a/test/cli/packages_cmd_test.go +++ b/test/cli/packages_cmd_test.go @@ -96,7 +96,7 @@ func TestPackagesCmdFlags(t *testing.T) { name: "squashed-scope-flag", args: []string{"packages", "-o", "json", "-s", "squashed", coverageImage}, assertions: []traitAssertion{ - assertPackageCount(20), + assertPackageCount(32), assertSuccessfulReturnCode, }, }, diff --git a/test/integration/catalog_packages_cases_test.go b/test/integration/catalog_packages_cases_test.go index 8d566afcb86..60b2005acee 100644 --- a/test/integration/catalog_packages_cases_test.go +++ b/test/integration/catalog_packages_cases_test.go @@ -199,6 +199,25 @@ var dirOnlyTestCases = []testCase{ "analyzer": "0.40.7", }, }, + { + name: "find dotnet packages", + pkgType: pkg.DotnetPkg, + pkgLanguage: pkg.Dotnet, + pkgInfo: map[string]string{ + "AWSSDK.Core": "3.7.10.6", + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0", + "Newtonsoft.Json": "13.0.1", + "Serilog": "2.10.0", + "Serilog.Sinks.Console": "4.0.1", + "System.Diagnostics.DiagnosticSource": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + }, + }, } var commonTestCases = []testCase{ diff --git a/test/integration/catalog_packages_test.go b/test/integration/catalog_packages_test.go index 6c701a72ef5..69557582533 100644 --- a/test/integration/catalog_packages_test.go +++ b/test/integration/catalog_packages_test.go @@ -66,6 +66,7 @@ func TestPkgCoverageImage(t *testing.T) { definedLanguages.Remove(pkg.Go.String()) definedLanguages.Remove(pkg.Rust.String()) definedLanguages.Remove(pkg.Dart.String()) + definedLanguages.Remove(pkg.Dotnet.String()) observedPkgs := internal.NewStringSet() definedPkgs := internal.NewStringSet() @@ -78,6 +79,7 @@ func TestPkgCoverageImage(t *testing.T) { definedPkgs.Remove(string(pkg.GoModulePkg)) definedPkgs.Remove(string(pkg.RustPkg)) definedPkgs.Remove(string(pkg.DartPubPkg)) + definedPkgs.Remove(string(pkg.DotnetPkg)) var cases []testCase cases = append(cases, commonTestCases...) diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/dotnet/TestLibrary.deps.json b/test/integration/test-fixtures/image-pkg-coverage/pkgs/dotnet/TestLibrary.deps.json new file mode 100644 index 00000000000..a429ec4094d --- /dev/null +++ b/test/integration/test-fixtures/image-pkg-coverage/pkgs/dotnet/TestLibrary.deps.json @@ -0,0 +1,235 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v6.0", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v6.0": { + "TestLibrary/1.0.0": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Newtonsoft.Json": "13.0.1", + "Serilog": "2.10.0", + "Serilog.Sinks.Console": "4.0.1", + "TestCommon": "1.0.0" + }, + "runtime": { + "TestLibrary.dll": {} + } + }, + "AWSSDK.Core/3.7.10.6": { + "runtime": { + "lib/netcoreapp3.1/AWSSDK.Core.dll": { + "assemblyVersion": "3.3.0.0", + "fileVersion": "3.7.10.6" + } + } + }, + "Microsoft.Extensions.DependencyInjection/6.0.0": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + }, + "runtime": { + "lib/net6.0/Microsoft.Extensions.DependencyInjection.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0": { + "runtime": { + "lib/net6.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Microsoft.Extensions.Logging/6.0.0": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + }, + "runtime": { + "lib/netstandard2.1/Microsoft.Extensions.Logging.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Microsoft.Extensions.Logging.Abstractions/6.0.0": { + "runtime": { + "lib/net6.0/Microsoft.Extensions.Logging.Abstractions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Microsoft.Extensions.Options/6.0.0": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + }, + "runtime": { + "lib/netstandard2.1/Microsoft.Extensions.Options.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Microsoft.Extensions.Primitives/6.0.0": { + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + }, + "runtime": { + "lib/net6.0/Microsoft.Extensions.Primitives.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, + "Newtonsoft.Json/13.0.1": { + "runtime": { + "lib/netstandard2.0/Newtonsoft.Json.dll": { + "assemblyVersion": "13.0.0.0", + "fileVersion": "13.0.1.25517" + } + } + }, + "Serilog/2.10.0": { + "runtime": { + "lib/netstandard2.1/Serilog.dll": { + "assemblyVersion": "2.0.0.0", + "fileVersion": "2.10.0.0" + } + } + }, + "Serilog.Sinks.Console/4.0.1": { + "dependencies": { + "Serilog": "2.10.0" + }, + "runtime": { + "lib/net5.0/Serilog.Sinks.Console.dll": { + "assemblyVersion": "4.0.1.0", + "fileVersion": "4.0.1.0" + } + } + }, + "System.Diagnostics.DiagnosticSource/6.0.0": { + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe/6.0.0": {}, + "TestCommon/1.0.0": { + "dependencies": { + "AWSSDK.Core": "3.7.10.6" + }, + "runtime": { + "TestCommon.dll": {} + } + } + } + }, + "libraries": { + "TestLibrary/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "AWSSDK.Core/3.7.10.6": { + "type": "package", + "serviceable": true, + "sha512": "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==", + "path": "awssdk.core/3.7.10.6", + "hashPath": "awssdk.core.3.7.10.6.nupkg.sha512" + }, + "Microsoft.Extensions.DependencyInjection/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "path": "microsoft.extensions.dependencyinjection/6.0.0", + "hashPath": "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==", + "path": "microsoft.extensions.dependencyinjection.abstractions/6.0.0", + "hashPath": "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Logging/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "path": "microsoft.extensions.logging/6.0.0", + "hashPath": "microsoft.extensions.logging.6.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Logging.Abstractions/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==", + "path": "microsoft.extensions.logging.abstractions/6.0.0", + "hashPath": "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Options/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "path": "microsoft.extensions.options/6.0.0", + "hashPath": "microsoft.extensions.options.6.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Primitives/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "path": "microsoft.extensions.primitives/6.0.0", + "hashPath": "microsoft.extensions.primitives.6.0.0.nupkg.sha512" + }, + "Newtonsoft.Json/13.0.1": { + "type": "package", + "serviceable": true, + "sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==", + "path": "newtonsoft.json/13.0.1", + "hashPath": "newtonsoft.json.13.0.1.nupkg.sha512" + }, + "Serilog/2.10.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==", + "path": "serilog/2.10.0", + "hashPath": "serilog.2.10.0.nupkg.sha512" + }, + "Serilog.Sinks.Console/4.0.1": { + "type": "package", + "serviceable": true, + "sha512": "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==", + "path": "serilog.sinks.console/4.0.1", + "hashPath": "serilog.sinks.console.4.0.1.nupkg.sha512" + }, + "System.Diagnostics.DiagnosticSource/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "path": "system.diagnostics.diagnosticsource/6.0.0", + "hashPath": "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512" + }, + "System.Runtime.CompilerServices.Unsafe/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==", + "path": "system.runtime.compilerservices.unsafe/6.0.0", + "hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512" + }, + "TestCommon/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file