From ec4f1464c5ac17cfb3f22ceccb2c2fe658956a36 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Tue, 2 May 2023 13:29:30 -0400 Subject: [PATCH 1/8] Add R cataloger Add a cataloger that detects installed R packages by looking for DESCRIPTION files. The base R package is now picked up in coverageImage tests in test/cli/packages_cmd_test.go, so increment expected package counts for the tests that use that image. Signed-off-by: Will Murphy --- .../formats/common/spdxhelpers/source_info.go | 2 + .../common/spdxhelpers/source_info_test.go | 8 ++ syft/pkg/cataloger/cataloger.go | 3 + syft/pkg/cataloger/r/cataloger.go | 13 ++ syft/pkg/cataloger/r/cataloger_test.go | 60 ++++++++ syft/pkg/cataloger/r/package.go | 38 +++++ syft/pkg/cataloger/r/parse_description.go | 134 ++++++++++++++++++ .../pkg/cataloger/r/parse_description_test.go | 73 ++++++++++ .../pkg/cataloger/r/test-fixtures/DESCRIPTION | 46 ++++++ .../test-fixtures/installed/base/DESCRIPTION | 11 ++ .../installed/stringr/DESCRIPTION | 46 ++++++ .../cataloger/r/test-fixtures/map-parse/bad | 3 + .../r/test-fixtures/map-parse/eof-multiline | 6 + .../r/test-fixtures/map-parse/multiline | 8 ++ .../r/test-fixtures/map-parse/simple | 4 + syft/pkg/language.go | 4 + syft/pkg/language_test.go | 8 ++ syft/pkg/metadata.go | 3 + syft/pkg/r_package_metadata.go | 21 +++ syft/pkg/type.go | 6 + syft/pkg/type_test.go | 4 + test/cli/packages_cmd_test.go | 8 +- .../catalog_packages_cases_test.go | 8 ++ test/integration/catalog_packages_test.go | 2 + .../pkgs/r/base/DESCRIPTION | 11 ++ 25 files changed, 526 insertions(+), 4 deletions(-) create mode 100644 syft/pkg/cataloger/r/cataloger.go create mode 100644 syft/pkg/cataloger/r/cataloger_test.go create mode 100644 syft/pkg/cataloger/r/package.go create mode 100644 syft/pkg/cataloger/r/parse_description.go create mode 100644 syft/pkg/cataloger/r/parse_description_test.go create mode 100644 syft/pkg/cataloger/r/test-fixtures/DESCRIPTION create mode 100644 syft/pkg/cataloger/r/test-fixtures/installed/base/DESCRIPTION create mode 100644 syft/pkg/cataloger/r/test-fixtures/installed/stringr/DESCRIPTION create mode 100644 syft/pkg/cataloger/r/test-fixtures/map-parse/bad create mode 100644 syft/pkg/cataloger/r/test-fixtures/map-parse/eof-multiline create mode 100644 syft/pkg/cataloger/r/test-fixtures/map-parse/multiline create mode 100644 syft/pkg/cataloger/r/test-fixtures/map-parse/simple create mode 100644 syft/pkg/r_package_metadata.go create mode 100644 test/integration/test-fixtures/image-pkg-coverage/pkgs/r/base/DESCRIPTION diff --git a/syft/formats/common/spdxhelpers/source_info.go b/syft/formats/common/spdxhelpers/source_info.go index 71007150ccc..8aeb5b356aa 100644 --- a/syft/formats/common/spdxhelpers/source_info.go +++ b/syft/formats/common/spdxhelpers/source_info.go @@ -52,6 +52,8 @@ func SourceInfo(p pkg.Package) string { answer = "acquired package info from linux kernel module files" case pkg.NixPkg: answer = "acquired package info from nix store path" + case pkg.Rpkg: + answer = "acquired package info from R-package DESCRIPTION file" default: answer = "acquired package info from the following paths" } diff --git a/syft/formats/common/spdxhelpers/source_info_test.go b/syft/formats/common/spdxhelpers/source_info_test.go index bb4eee7e89d..a56efff9338 100644 --- a/syft/formats/common/spdxhelpers/source_info_test.go +++ b/syft/formats/common/spdxhelpers/source_info_test.go @@ -223,6 +223,14 @@ func Test_SourceInfo(t *testing.T) { "from nix store path", }, }, + { + input: pkg.Package{ + Type: pkg.Rpkg, + }, + expected: []string{ + "acquired package info from R-package DESCRIPTION file", + }, + }, } var pkgTypes []pkg.Type for _, test := range tests { diff --git a/syft/pkg/cataloger/cataloger.go b/syft/pkg/cataloger/cataloger.go index 1c25e48ac64..78a995843ad 100644 --- a/syft/pkg/cataloger/cataloger.go +++ b/syft/pkg/cataloger/cataloger.go @@ -28,6 +28,7 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/php" "github.com/anchore/syft/syft/pkg/cataloger/portage" "github.com/anchore/syft/syft/pkg/cataloger/python" + "github.com/anchore/syft/syft/pkg/cataloger/r" "github.com/anchore/syft/syft/pkg/cataloger/rpm" "github.com/anchore/syft/syft/pkg/cataloger/ruby" "github.com/anchore/syft/syft/pkg/cataloger/rust" @@ -53,6 +54,7 @@ func ImageCatalogers(cfg Config) []pkg.Cataloger { php.NewComposerInstalledCataloger(), portage.NewPortageCataloger(), python.NewPythonPackageCataloger(), + r.NewPackageCataloger(), rpm.NewRpmDBCataloger(), ruby.NewGemSpecCataloger(), sbom.NewSBOMCataloger(), @@ -121,6 +123,7 @@ func AllCatalogers(cfg Config) []pkg.Cataloger { portage.NewPortageCataloger(), python.NewPythonIndexCataloger(), python.NewPythonPackageCataloger(), + r.NewPackageCataloger(), rpm.NewFileCataloger(), rpm.NewRpmDBCataloger(), ruby.NewGemFileLockCataloger(), diff --git a/syft/pkg/cataloger/r/cataloger.go b/syft/pkg/cataloger/r/cataloger.go new file mode 100644 index 00000000000..8cb4774a4af --- /dev/null +++ b/syft/pkg/cataloger/r/cataloger.go @@ -0,0 +1,13 @@ +package r + +import ( + "github.com/anchore/syft/syft/pkg/cataloger/generic" +) + +const catalogerName = "r-package-cataloger" + +// NewPackageCataloger returns a new R cataloger object based on detection of R package DESCRIPTION files. +func NewPackageCataloger() *generic.Cataloger { + return generic.NewCataloger(catalogerName). + WithParserByGlobs(parseDescriptionFile, "**/DESCRIPTION") +} diff --git a/syft/pkg/cataloger/r/cataloger_test.go b/syft/pkg/cataloger/r/cataloger_test.go new file mode 100644 index 00000000000..c1744804d91 --- /dev/null +++ b/syft/pkg/cataloger/r/cataloger_test.go @@ -0,0 +1,60 @@ +package r + +import ( + "testing" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" + "github.com/anchore/syft/syft/source" +) + +func TestRPackageCataloger(t *testing.T) { + expectedPkgs := []pkg.Package{ + { + Name: "base", + Version: "4.3.0", + FoundBy: "r-package-cataloger", + Locations: source.NewLocationSet(source.NewLocation("base/DESCRIPTION")), + Licenses: []string{"Part of R 4.3.0"}, + Language: "R", + Type: "R-package", + PURL: "pkg:cran/base@4.3.0", + MetadataType: "RDescriptionFileMetadataType", + Metadata: pkg.RDescriptionFileMetadata{ + Title: "The R Base Package", + Description: "Base R functions.", + Author: "R Core Team and contributors worldwide", + Maintainer: "R Core Team ", + Built: "R 4.3.0; ; 2023-04-21 11:33:09 UTC; unix", + Suggests: []string{"methods"}, + }, + }, + { + Name: "stringr", + Version: "1.5.0.9000", + FoundBy: "r-package-cataloger", + Locations: source.NewLocationSet(source.NewLocation("stringr/DESCRIPTION")), + Licenses: []string{"MIT + file LICENSE"}, + Language: "R", + Type: "R-package", + PURL: "pkg:cran/stringr@1.5.0.9000", + MetadataType: "RDescriptionFileMetadataType", + Metadata: pkg.RDescriptionFileMetadata{ + Title: "Simple, Consistent Wrappers for Common String Operations", + Description: "A consistent, simple and easy to use set of wrappers around the fantastic 'stringi' package. All function and argument names (and positions) are consistent, all functions deal with \"NA\"'s and zero length vectors in the same way, and the output from one function is easy to feed into the input of another.", + URL: []string{"https://stringr.tidyverse.org", "https://github.com/tidyverse/stringr"}, + Imports: []string{ + "cli", "glue (>= 1.6.1)", "lifecycle (>= 1.0.3)", "magrittr", + "rlang (>= 1.0.0)", "stringi (>= 1.5.3)", "vctrs (>= 0.4.0)", + }, + Depends: []string{"R (>= 3.3)"}, + Suggests: []string{"covr", "dplyr", "gt", "htmltools", "htmlwidgets", "knitr", "rmarkdown", "testthat (>= 3.0.0)", "tibble"}, + }, + }, + } + // TODO: relationships are not under test yet + var expectedRelationships []artifact.Relationship + + pkgtest.NewCatalogTester().FromDirectory(t, "test-fixtures/installed").Expects(expectedPkgs, expectedRelationships).TestCataloger(t, NewPackageCataloger()) +} diff --git a/syft/pkg/cataloger/r/package.go b/syft/pkg/cataloger/r/package.go new file mode 100644 index 00000000000..891088ba0ca --- /dev/null +++ b/syft/pkg/cataloger/r/package.go @@ -0,0 +1,38 @@ +package r + +import ( + "github.com/anchore/packageurl-go" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +func newPackage(pd parseData, locations ...source.Location) pkg.Package { + locationSet := source.NewLocationSet() + for _, loc := range locations { + locationSet.Add(loc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)) + } + result := pkg.Package{ + Name: pd.Package, + Version: pd.Version, + FoundBy: catalogerName, + Locations: locationSet, + Licenses: []string{pd.License}, + Language: "R", + Type: pkg.Rpkg, + PURL: packageURL(pd), + MetadataType: pkg.RDescriptionFileMetadataType, + Metadata: pd.RDescriptionFileMetadata, + } + + result.Language = "R" + result.FoundBy = catalogerName + + result.Licenses = []string{pd.License} + result.Version = pd.Version + result.SetID() + return result +} + +func packageURL(m parseData) string { + return packageurl.NewPackageURL("cran", "", m.Package, m.Version, nil, "").ToString() +} diff --git a/syft/pkg/cataloger/r/parse_description.go b/syft/pkg/cataloger/r/parse_description.go new file mode 100644 index 00000000000..c0e5f762746 --- /dev/null +++ b/syft/pkg/cataloger/r/parse_description.go @@ -0,0 +1,134 @@ +package r + +import ( + "bufio" + "io" + "regexp" + "strings" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" + "github.com/anchore/syft/syft/source" +) + +/* some examples of license strings found in DESCRIPTION files: +find /usr/local/lib/R -name DESCRIPTION | xargs cat | grep 'License:' | sort | uniq +License: GPL +License: GPL (>= 2) +License: GPL (>=2) +License: GPL(>=2) +License: GPL (>= 2) | file LICENCE +License: GPL-2 | GPL-3 +License: GPL-3 +License: LGPL (>= 2) +License: LGPL (>= 2.1) +License: MIT + file LICENSE +License: Part of R 4.3.0 +License: Unlimited +*/ + +func parseDescriptionFile(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + values := extractFieldsFromDescriptionFile(reader) + m := parseDataFromDescriptionMap(values) + return []pkg.Package{newPackage(m, []source.Location{reader.Location}...)}, nil, nil +} + +type parseData struct { + Package string + Version string + License string + pkg.RDescriptionFileMetadata +} + +func parseDataFromDescriptionMap(values map[string]string) parseData { + return parseData{ + License: values["License"], + Package: values["Package"], + Version: values["Version"], + RDescriptionFileMetadata: pkg.RDescriptionFileMetadata{ + Title: values["Title"], + Description: cleanMultiLineValue(values["Description"]), + Maintainer: values["Maintainer"], + URL: commaSeparatedList(values["URL"]), + Depends: commaSeparatedList(values["Depends"]), + Imports: commaSeparatedList(values["Imports"]), + Suggests: commaSeparatedList(values["Suggests"]), + NeedsCompilation: yesNoToBool(values["NeedsCompilation"]), + Author: values["Author"], + Repository: values["Repository"], + Built: values["Built"], + }, + } +} + +func yesNoToBool(s string) bool { + return strings.EqualFold(s, "yes") +} + +func commaSeparatedList(s string) []string { + var result []string + split := strings.Split(s, ",") + for _, piece := range split { + value := strings.TrimSpace(piece) + if value == "" { + continue + } + result = append(result, value) + } + return result +} + +var space = regexp.MustCompile(`\s+`) + +func cleanMultiLineValue(s string) string { + return space.ReplaceAllString(s, " ") +} + +func extractFieldsFromDescriptionFile(reader io.Reader) map[string]string { + result := make(map[string]string) + key := "" + var valueFragment strings.Builder + scanner := bufio.NewScanner(reader) + + for scanner.Scan() { + line := scanner.Text() + // line is like Key: Value -> start capturing value; close out previous value + // line is like \t\t continued value -> append to existing value + if len(line) == 0 { + continue + } + if startsWithWhitespace(line) { + // we're continuing a value + if key == "" { + continue + } + valueFragment.WriteByte('\n') + valueFragment.WriteString(strings.TrimSpace(line)) + } else { + if key != "" { + // capture previous value + result[key] = valueFragment.String() + key = "" + valueFragment = strings.Builder{} + } + parts := strings.SplitN(line, ":", 2) + if len(parts) != 2 { + continue + } + key = parts[0] + valueFragment.WriteString(strings.TrimSpace(parts[1])) + } + } + if key != "" { + result[key] = valueFragment.String() + } + return result +} + +func startsWithWhitespace(s string) bool { + if s == "" { + return false + } + return s[0] == ' ' || s[0] == '\t' +} diff --git a/syft/pkg/cataloger/r/parse_description_test.go b/syft/pkg/cataloger/r/parse_description_test.go new file mode 100644 index 00000000000..82f70df6a3a --- /dev/null +++ b/syft/pkg/cataloger/r/parse_description_test.go @@ -0,0 +1,73 @@ +package r + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_extractFieldsFromDescriptionFile(t *testing.T) { + tests := []struct { + name string + fixture string + want map[string]string + }{ + { + name: "go case", + fixture: "test-fixtures/map-parse/simple", + want: map[string]string{ + "Package": "base", + "Version": "4.3.0", + "Suggests": "methods", + "Built": "R 4.3.0; ; 2023-04-21 11:33:09 UTC; unix", + }, + }, + { + name: "bad cases", + fixture: "test-fixtures/map-parse/bad", + want: map[string]string{ + "Key": "", + "Whitespace": "", + }, + }, + { + name: "multiline key-value", + fixture: "test-fixtures/map-parse/multiline", + want: map[string]string{ + "Description": `A consistent, simple and easy to use set of wrappers around +the fantastic 'stringi' package. All function and argument names (and +positions) are consistent, all functions deal with "NA"'s and zero +length vectors in the same way, and the output from one function is +easy to feed into the input of another.`, + "License": "MIT + file LICENSE", + "Key": "value", + }, + }, + { + name: "eof multiline", + fixture: "test-fixtures/map-parse/eof-multiline", + want: map[string]string{ + "License": "MIT + file LICENSE", + "Description": `A consistent, simple and easy to use set of wrappers around +the fantastic 'stringi' package. All function and argument names (and +positions) are consistent, all functions deal with "NA"'s and zero +length vectors in the same way, and the output from one function is +easy to feed into the input of another.`, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + file, err := os.Open(test.fixture) + require.NoError(t, err) + + result := extractFieldsFromDescriptionFile(file) + + assert.Equal(t, test.want, result) + }) + } + +} diff --git a/syft/pkg/cataloger/r/test-fixtures/DESCRIPTION b/syft/pkg/cataloger/r/test-fixtures/DESCRIPTION new file mode 100644 index 00000000000..ccc61029894 --- /dev/null +++ b/syft/pkg/cataloger/r/test-fixtures/DESCRIPTION @@ -0,0 +1,46 @@ +Package: stringr +Title: Simple, Consistent Wrappers for Common String Operations +Version: 1.5.0.9000 +Authors@R: + c(person(given = "Hadley", + family = "Wickham", + role = c("aut", "cre", "cph"), + email = "hadley@rstudio.com"), + person(given = "RStudio", + role = c("cph", "fnd"))) +Description: A consistent, simple and easy to use set of wrappers around + the fantastic 'stringi' package. All function and argument names (and + positions) are consistent, all functions deal with "NA"'s and zero + length vectors in the same way, and the output from one function is + easy to feed into the input of another. +License: MIT + file LICENSE +URL: https://stringr.tidyverse.org, https://github.com/tidyverse/stringr +BugReports: https://github.com/tidyverse/stringr/issues +Depends: + R (>= 3.3) +Imports: + cli, + glue (>= 1.6.1), + lifecycle (>= 1.0.3), + magrittr, + rlang (>= 1.0.0), + stringi (>= 1.5.3), + vctrs (>= 0.4.0) +Suggests: + covr, + dplyr, + gt, + htmltools, + htmlwidgets, + knitr, + rmarkdown, + testthat (>= 3.0.0), + tibble +VignetteBuilder: + knitr +Config/Needs/website: tidyverse/tidytemplate +Config/testthat/edition: 3 +Encoding: UTF-8 +LazyData: true +Roxygen: list(markdown = TRUE) +RoxygenNote: 7.2.1 diff --git a/syft/pkg/cataloger/r/test-fixtures/installed/base/DESCRIPTION b/syft/pkg/cataloger/r/test-fixtures/installed/base/DESCRIPTION new file mode 100644 index 00000000000..f47edbdd0db --- /dev/null +++ b/syft/pkg/cataloger/r/test-fixtures/installed/base/DESCRIPTION @@ -0,0 +1,11 @@ +Package: base +Version: 4.3.0 +Priority: base +Title: The R Base Package +Author: R Core Team and contributors worldwide +Maintainer: R Core Team +Contact: R-help mailing list +Description: Base R functions. +License: Part of R 4.3.0 +Suggests: methods +Built: R 4.3.0; ; 2023-04-21 11:33:09 UTC; unix \ No newline at end of file diff --git a/syft/pkg/cataloger/r/test-fixtures/installed/stringr/DESCRIPTION b/syft/pkg/cataloger/r/test-fixtures/installed/stringr/DESCRIPTION new file mode 100644 index 00000000000..ccc61029894 --- /dev/null +++ b/syft/pkg/cataloger/r/test-fixtures/installed/stringr/DESCRIPTION @@ -0,0 +1,46 @@ +Package: stringr +Title: Simple, Consistent Wrappers for Common String Operations +Version: 1.5.0.9000 +Authors@R: + c(person(given = "Hadley", + family = "Wickham", + role = c("aut", "cre", "cph"), + email = "hadley@rstudio.com"), + person(given = "RStudio", + role = c("cph", "fnd"))) +Description: A consistent, simple and easy to use set of wrappers around + the fantastic 'stringi' package. All function and argument names (and + positions) are consistent, all functions deal with "NA"'s and zero + length vectors in the same way, and the output from one function is + easy to feed into the input of another. +License: MIT + file LICENSE +URL: https://stringr.tidyverse.org, https://github.com/tidyverse/stringr +BugReports: https://github.com/tidyverse/stringr/issues +Depends: + R (>= 3.3) +Imports: + cli, + glue (>= 1.6.1), + lifecycle (>= 1.0.3), + magrittr, + rlang (>= 1.0.0), + stringi (>= 1.5.3), + vctrs (>= 0.4.0) +Suggests: + covr, + dplyr, + gt, + htmltools, + htmlwidgets, + knitr, + rmarkdown, + testthat (>= 3.0.0), + tibble +VignetteBuilder: + knitr +Config/Needs/website: tidyverse/tidytemplate +Config/testthat/edition: 3 +Encoding: UTF-8 +LazyData: true +Roxygen: list(markdown = TRUE) +RoxygenNote: 7.2.1 diff --git a/syft/pkg/cataloger/r/test-fixtures/map-parse/bad b/syft/pkg/cataloger/r/test-fixtures/map-parse/bad new file mode 100644 index 00000000000..a2ea102351b --- /dev/null +++ b/syft/pkg/cataloger/r/test-fixtures/map-parse/bad @@ -0,0 +1,3 @@ +MissingColon +Whitespace: +Key: \ No newline at end of file diff --git a/syft/pkg/cataloger/r/test-fixtures/map-parse/eof-multiline b/syft/pkg/cataloger/r/test-fixtures/map-parse/eof-multiline new file mode 100644 index 00000000000..ecc14bf7d61 --- /dev/null +++ b/syft/pkg/cataloger/r/test-fixtures/map-parse/eof-multiline @@ -0,0 +1,6 @@ +License: MIT + file LICENSE +Description: A consistent, simple and easy to use set of wrappers around + the fantastic 'stringi' package. All function and argument names (and + positions) are consistent, all functions deal with "NA"'s and zero + length vectors in the same way, and the output from one function is + easy to feed into the input of another. \ No newline at end of file diff --git a/syft/pkg/cataloger/r/test-fixtures/map-parse/multiline b/syft/pkg/cataloger/r/test-fixtures/map-parse/multiline new file mode 100644 index 00000000000..35e019c43e2 --- /dev/null +++ b/syft/pkg/cataloger/r/test-fixtures/map-parse/multiline @@ -0,0 +1,8 @@ +Key: value + +Description: A consistent, simple and easy to use set of wrappers around + the fantastic 'stringi' package. All function and argument names (and + positions) are consistent, all functions deal with "NA"'s and zero + length vectors in the same way, and the output from one function is + easy to feed into the input of another. +License: MIT + file LICENSE \ No newline at end of file diff --git a/syft/pkg/cataloger/r/test-fixtures/map-parse/simple b/syft/pkg/cataloger/r/test-fixtures/map-parse/simple new file mode 100644 index 00000000000..74bb84692a2 --- /dev/null +++ b/syft/pkg/cataloger/r/test-fixtures/map-parse/simple @@ -0,0 +1,4 @@ +Package: base +Version: 4.3.0 +Suggests: methods +Built: R 4.3.0; ; 2023-04-21 11:33:09 UTC; unix diff --git a/syft/pkg/language.go b/syft/pkg/language.go index 2ce12163e4e..bb2902bbf60 100644 --- a/syft/pkg/language.go +++ b/syft/pkg/language.go @@ -23,6 +23,7 @@ const ( JavaScript Language = "javascript" PHP Language = "php" Python Language = "python" + R Language = "R" Ruby Language = "ruby" Rust Language = "rust" Swift Language = "swift" @@ -41,6 +42,7 @@ var AllLanguages = []Language{ JavaScript, PHP, Python, + R, Ruby, Rust, Swift, @@ -91,6 +93,8 @@ func LanguageByName(name string) Language { // answer: no. We want this to definitively answer "which language does this package represent?" // which might not be possible in all cases. See for more context: https://github.com/package-url/purl-spec/pull/178 return UnknownLanguage + case packageurl.TypeCran, "r": + return R default: return UnknownLanguage } diff --git a/syft/pkg/language_test.go b/syft/pkg/language_test.go index 8e84b975eb1..36740d66ca4 100644 --- a/syft/pkg/language_test.go +++ b/syft/pkg/language_test.go @@ -66,6 +66,10 @@ func TestLanguageFromPURL(t *testing.T) { purl: "pkg:hex/hpax/hpax@0.1.1", want: UnknownLanguage, }, + { + purl: "pkg:cran/base@4.3.0", + want: R, + }, } var languages []string @@ -231,6 +235,10 @@ func TestLanguageByName(t *testing.T) { name: "haskell", language: Haskell, }, + { + name: "R", + language: R, + }, } for _, test := range tests { diff --git a/syft/pkg/metadata.go b/syft/pkg/metadata.go index c6d4a036ec6..5d43e9911de 100644 --- a/syft/pkg/metadata.go +++ b/syft/pkg/metadata.go @@ -38,6 +38,7 @@ const ( PythonPipfileLockMetadataType MetadataType = "PythonPipfileLockMetadata" PythonRequirementsMetadataType MetadataType = "PythonRequirementsMetadata" RebarLockMetadataType MetadataType = "RebarLockMetadataType" + RDescriptionFileMetadataType MetadataType = "RDescriptionFileMetadataType" RpmMetadataType MetadataType = "RpmMetadata" RustCargoPackageMetadataType MetadataType = "RustCargoPackageMetadata" ) @@ -69,6 +70,7 @@ var AllMetadataTypes = []MetadataType{ PythonPackageMetadataType, PythonPipfileLockMetadataType, PythonRequirementsMetadataType, + RDescriptionFileMetadataType, RebarLockMetadataType, RpmMetadataType, RustCargoPackageMetadataType, @@ -101,6 +103,7 @@ var MetadataTypeByName = map[MetadataType]reflect.Type{ PythonPackageMetadataType: reflect.TypeOf(PythonPackageMetadata{}), PythonPipfileLockMetadataType: reflect.TypeOf(PythonPipfileLockMetadata{}), PythonRequirementsMetadataType: reflect.TypeOf(PythonRequirementsMetadata{}), + RDescriptionFileMetadataType: reflect.TypeOf(RDescriptionFileMetadata{}), RebarLockMetadataType: reflect.TypeOf(RebarLockMetadata{}), RpmMetadataType: reflect.TypeOf(RpmMetadata{}), RustCargoPackageMetadataType: reflect.TypeOf(CargoPackageMetadata{}), diff --git a/syft/pkg/r_package_metadata.go b/syft/pkg/r_package_metadata.go new file mode 100644 index 00000000000..2a3ad3204d1 --- /dev/null +++ b/syft/pkg/r_package_metadata.go @@ -0,0 +1,21 @@ +package pkg + +type RDescriptionFileMetadata struct { + /* + Fields chosen by: + docker run --rm -it rocker/r-ver bash + $ install2.r ggplot2 # has a lot of dependencies + $ find /usr/local/lib/R -name DESCRIPTION | xargs cat | grep -v '^\s' | cut -d ':' -f 1 | sort | uniq -c | sort -nr + */ + Title string `json:"title,omitempty"` + Description string `json:"description,omitempty"` + Author string `json:"author,omitempty"` + Maintainer string `json:"maintainer,omitempty"` + URL []string `json:"url,omitempty"` + Repository string `json:"repository,omitempty"` + Built string `json:"built,omitempty"` + NeedsCompilation bool `json:"needsCompilation,omitempty"` + Imports []string `json:"imports,omitempty"` + Depends []string `json:"depends,omitempty"` + Suggests []string `json:"suggests,omitempty"` +} diff --git a/syft/pkg/type.go b/syft/pkg/type.go index 952ec2e9930..760b3232931 100644 --- a/syft/pkg/type.go +++ b/syft/pkg/type.go @@ -33,6 +33,7 @@ const ( PhpComposerPkg Type = "php-composer" PortagePkg Type = "portage" PythonPkg Type = "python" + Rpkg Type = "R-package" RpmPkg Type = "rpm" RustPkg Type = "rust-crate" ) @@ -61,6 +62,7 @@ var AllPkgs = []Type{ PhpComposerPkg, PortagePkg, PythonPkg, + Rpkg, RpmPkg, RustPkg, } @@ -106,6 +108,8 @@ func (t Type) PackageURLType() string { return "nix" case NpmPkg: return packageurl.TypeNPM + case Rpkg: + return packageurl.TypeCran case RpmPkg: return packageurl.TypeRPM case RustPkg: @@ -173,6 +177,8 @@ func TypeByName(name string) Type { return LinuxKernelModulePkg case "nix": return NixPkg + case packageurl.TypeCran: + return Rpkg default: return UnknownPkg } diff --git a/syft/pkg/type_test.go b/syft/pkg/type_test.go index cfcd4b1d9dd..e5c7a687fd8 100644 --- a/syft/pkg/type_test.go +++ b/syft/pkg/type_test.go @@ -91,6 +91,10 @@ func TestTypeFromPURL(t *testing.T) { purl: "pkg:nix/glibc@2.34?hash=h0cnbmfcn93xm5dg2x27ixhag1cwndga", expected: NixPkg, }, + { + purl: "pkg:cran/base@4.3.0", + expected: Rpkg, + }, } var pkgTypes []string diff --git a/test/cli/packages_cmd_test.go b/test/cli/packages_cmd_test.go index 08ce77a1798..0cd68ffed37 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(35), + assertPackageCount(36), assertSuccessfulReturnCode, }, }, @@ -213,7 +213,7 @@ func TestPackagesCmdFlags(t *testing.T) { // the application config in the log matches that of what we expect to have been configured. assertInOutput("parallelism: 2"), assertInOutput("parallelism=2"), - assertPackageCount(35), + assertPackageCount(36), assertSuccessfulReturnCode, }, }, @@ -224,7 +224,7 @@ func TestPackagesCmdFlags(t *testing.T) { // the application config in the log matches that of what we expect to have been configured. assertInOutput("parallelism: 1"), assertInOutput("parallelism=1"), - assertPackageCount(35), + assertPackageCount(36), assertSuccessfulReturnCode, }, }, @@ -238,7 +238,7 @@ func TestPackagesCmdFlags(t *testing.T) { assertions: []traitAssertion{ assertNotInOutput("secret_password"), assertNotInOutput("secret_key_path"), - assertPackageCount(35), + assertPackageCount(36), assertSuccessfulReturnCode, }, }, diff --git a/test/integration/catalog_packages_cases_test.go b/test/integration/catalog_packages_cases_test.go index bc3acbdf2fd..39a7e1c79d2 100644 --- a/test/integration/catalog_packages_cases_test.go +++ b/test/integration/catalog_packages_cases_test.go @@ -69,6 +69,14 @@ var imageOnlyTestCases = []testCase{ "joda-time": "2.9.2", }, }, + { + name: "find R packages", + pkgType: pkg.Rpkg, + pkgLanguage: "R", + pkgInfo: map[string]string{ + "base": "4.3.0", + }, + }, } var dirOnlyTestCases = []testCase{ diff --git a/test/integration/catalog_packages_test.go b/test/integration/catalog_packages_test.go index 8b65ed9a9ba..c700484cd72 100644 --- a/test/integration/catalog_packages_test.go +++ b/test/integration/catalog_packages_test.go @@ -163,12 +163,14 @@ func TestPkgCoverageDirectory(t *testing.T) { for _, l := range pkg.AllLanguages { definedLanguages.Add(l.String()) } + definedLanguages.Remove(string(pkg.R)) observedPkgs := internal.NewStringSet() definedPkgs := internal.NewStringSet() for _, p := range pkg.AllPkgs { definedPkgs.Add(string(p)) } + definedPkgs.Remove(string(pkg.Rpkg)) var cases []testCase cases = append(cases, commonTestCases...) diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/r/base/DESCRIPTION b/test/integration/test-fixtures/image-pkg-coverage/pkgs/r/base/DESCRIPTION new file mode 100644 index 00000000000..f47edbdd0db --- /dev/null +++ b/test/integration/test-fixtures/image-pkg-coverage/pkgs/r/base/DESCRIPTION @@ -0,0 +1,11 @@ +Package: base +Version: 4.3.0 +Priority: base +Title: The R Base Package +Author: R Core Team and contributors worldwide +Maintainer: R Core Team +Contact: R-help mailing list +Description: Base R functions. +License: Part of R 4.3.0 +Suggests: methods +Built: R 4.3.0; ; 2023-04-21 11:33:09 UTC; unix \ No newline at end of file From 029671f2a29af14012a91c9d7d2d41c609b20950 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Fri, 5 May 2023 16:45:38 -0400 Subject: [PATCH 2/8] Use R language constant more consistently Signed-off-by: Will Murphy --- syft/pkg/cataloger/r/cataloger_test.go | 12 ++++++------ syft/pkg/cataloger/r/package.go | 3 +-- test/integration/catalog_packages_cases_test.go | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/syft/pkg/cataloger/r/cataloger_test.go b/syft/pkg/cataloger/r/cataloger_test.go index c1744804d91..671affb43f8 100644 --- a/syft/pkg/cataloger/r/cataloger_test.go +++ b/syft/pkg/cataloger/r/cataloger_test.go @@ -17,10 +17,10 @@ func TestRPackageCataloger(t *testing.T) { FoundBy: "r-package-cataloger", Locations: source.NewLocationSet(source.NewLocation("base/DESCRIPTION")), Licenses: []string{"Part of R 4.3.0"}, - Language: "R", - Type: "R-package", + Language: pkg.R, + Type: pkg.Rpkg, PURL: "pkg:cran/base@4.3.0", - MetadataType: "RDescriptionFileMetadataType", + MetadataType: pkg.RustCargoPackageMetadataType, Metadata: pkg.RDescriptionFileMetadata{ Title: "The R Base Package", Description: "Base R functions.", @@ -36,10 +36,10 @@ func TestRPackageCataloger(t *testing.T) { FoundBy: "r-package-cataloger", Locations: source.NewLocationSet(source.NewLocation("stringr/DESCRIPTION")), Licenses: []string{"MIT + file LICENSE"}, - Language: "R", - Type: "R-package", + Language: pkg.R, + Type: pkg.Rpkg, PURL: "pkg:cran/stringr@1.5.0.9000", - MetadataType: "RDescriptionFileMetadataType", + MetadataType: pkg.RustCargoPackageMetadataType, Metadata: pkg.RDescriptionFileMetadata{ Title: "Simple, Consistent Wrappers for Common String Operations", Description: "A consistent, simple and easy to use set of wrappers around the fantastic 'stringi' package. All function and argument names (and positions) are consistent, all functions deal with \"NA\"'s and zero length vectors in the same way, and the output from one function is easy to feed into the input of another.", diff --git a/syft/pkg/cataloger/r/package.go b/syft/pkg/cataloger/r/package.go index 891088ba0ca..5d82e20d7a8 100644 --- a/syft/pkg/cataloger/r/package.go +++ b/syft/pkg/cataloger/r/package.go @@ -17,14 +17,13 @@ func newPackage(pd parseData, locations ...source.Location) pkg.Package { FoundBy: catalogerName, Locations: locationSet, Licenses: []string{pd.License}, - Language: "R", + Language: pkg.R, Type: pkg.Rpkg, PURL: packageURL(pd), MetadataType: pkg.RDescriptionFileMetadataType, Metadata: pd.RDescriptionFileMetadata, } - result.Language = "R" result.FoundBy = catalogerName result.Licenses = []string{pd.License} diff --git a/test/integration/catalog_packages_cases_test.go b/test/integration/catalog_packages_cases_test.go index 39a7e1c79d2..815ccd3e4e2 100644 --- a/test/integration/catalog_packages_cases_test.go +++ b/test/integration/catalog_packages_cases_test.go @@ -72,7 +72,7 @@ var imageOnlyTestCases = []testCase{ { name: "find R packages", pkgType: pkg.Rpkg, - pkgLanguage: "R", + pkgLanguage: pkg.R, pkgInfo: map[string]string{ "base": "4.3.0", }, From abdd0c0f4b27c1eed8df6dbb9f99f6d7cf0f0ca3 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Mon, 8 May 2023 09:32:20 -0400 Subject: [PATCH 3/8] Fix feedback Mostly, don't return packages that have no name or version. Signed-off-by: Will Murphy --- syft/pkg/cataloger/r/package.go | 4 -- syft/pkg/cataloger/r/package_test.go | 14 +++++ syft/pkg/cataloger/r/parse_description.go | 6 +- .../pkg/cataloger/r/parse_description_test.go | 58 +++++++++++++++++++ .../r/test-fixtures/map-parse/no-name | 2 + .../r/test-fixtures/map-parse/no-version | 1 + test/integration/catalog_packages_test.go | 4 +- 7 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 syft/pkg/cataloger/r/package_test.go create mode 100644 syft/pkg/cataloger/r/test-fixtures/map-parse/no-name create mode 100644 syft/pkg/cataloger/r/test-fixtures/map-parse/no-version diff --git a/syft/pkg/cataloger/r/package.go b/syft/pkg/cataloger/r/package.go index 5d82e20d7a8..96382dc3be0 100644 --- a/syft/pkg/cataloger/r/package.go +++ b/syft/pkg/cataloger/r/package.go @@ -24,10 +24,6 @@ func newPackage(pd parseData, locations ...source.Location) pkg.Package { Metadata: pd.RDescriptionFileMetadata, } - result.FoundBy = catalogerName - - result.Licenses = []string{pd.License} - result.Version = pd.Version result.SetID() return result } diff --git a/syft/pkg/cataloger/r/package_test.go b/syft/pkg/cataloger/r/package_test.go new file mode 100644 index 00000000000..cccebf67993 --- /dev/null +++ b/syft/pkg/cataloger/r/package_test.go @@ -0,0 +1,14 @@ +package r + +import "testing" + +func Test_newPackage(t *testing.T) { + testCases := []struct { + name string + }{} + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + }) + } +} diff --git a/syft/pkg/cataloger/r/parse_description.go b/syft/pkg/cataloger/r/parse_description.go index c0e5f762746..50ae0ec9d7f 100644 --- a/syft/pkg/cataloger/r/parse_description.go +++ b/syft/pkg/cataloger/r/parse_description.go @@ -31,7 +31,11 @@ License: Unlimited func parseDescriptionFile(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { values := extractFieldsFromDescriptionFile(reader) m := parseDataFromDescriptionMap(values) - return []pkg.Package{newPackage(m, []source.Location{reader.Location}...)}, nil, nil + p := newPackage(m, []source.Location{reader.Location}...) + if p.Name == "" || p.Version == "" { + return nil, nil, nil + } + return []pkg.Package{p}, nil, nil } type parseData struct { diff --git a/syft/pkg/cataloger/r/parse_description_test.go b/syft/pkg/cataloger/r/parse_description_test.go index 82f70df6a3a..4263995240d 100644 --- a/syft/pkg/cataloger/r/parse_description_test.go +++ b/syft/pkg/cataloger/r/parse_description_test.go @@ -2,12 +2,70 @@ package r import ( "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" ) +func Test_parseDescriptionFile(t *testing.T) { + type packageAssertions []func(*testing.T, []pkg.Package) + tests := []struct { + name string + assertions packageAssertions + fixture string + }{ + { + name: "no package is returned if no version found", + fixture: filepath.Join("test-fixtures", "map-parse", "no-version"), + assertions: packageAssertions{ + func(t *testing.T, p []pkg.Package) { + assert.Empty(t, p) + }, + }, + }, + { + name: "no package is returned if no package name found", + fixture: filepath.Join("test-fixtures", "map-parse", "no-name"), + assertions: packageAssertions{ + func(t *testing.T, p []pkg.Package) { + assert.Empty(t, p) + }, + }, + }, + { + name: "package return if both name and version found", + fixture: filepath.Join("test-fixtures", "map-parse", "simple"), + assertions: packageAssertions{ + func(t *testing.T, p []pkg.Package) { + assert.Equal(t, 1, len(p)) + assert.Equal(t, "base", p[0].Name) + assert.Equal(t, "4.3.0", p[0].Version) + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.fixture) + input := source.LocationReadCloser{ + Location: source.NewLocation(tt.fixture), + ReadCloser: f, + } + got, _, err := parseDescriptionFile(nil, nil, input) + assert.NoError(t, err) + for _, assertion := range tt.assertions { + assertion(t, got) + } + }) + } +} + func Test_extractFieldsFromDescriptionFile(t *testing.T) { tests := []struct { name string diff --git a/syft/pkg/cataloger/r/test-fixtures/map-parse/no-name b/syft/pkg/cataloger/r/test-fixtures/map-parse/no-name new file mode 100644 index 00000000000..4f4dbb6ffc9 --- /dev/null +++ b/syft/pkg/cataloger/r/test-fixtures/map-parse/no-name @@ -0,0 +1,2 @@ +Version: 1.2.3 +Description: a package with no name diff --git a/syft/pkg/cataloger/r/test-fixtures/map-parse/no-version b/syft/pkg/cataloger/r/test-fixtures/map-parse/no-version new file mode 100644 index 00000000000..e901d883a5d --- /dev/null +++ b/syft/pkg/cataloger/r/test-fixtures/map-parse/no-version @@ -0,0 +1 @@ +Package: foo diff --git a/test/integration/catalog_packages_test.go b/test/integration/catalog_packages_test.go index c700484cd72..2c88c0615fe 100644 --- a/test/integration/catalog_packages_test.go +++ b/test/integration/catalog_packages_test.go @@ -163,14 +163,12 @@ func TestPkgCoverageDirectory(t *testing.T) { for _, l := range pkg.AllLanguages { definedLanguages.Add(l.String()) } - definedLanguages.Remove(string(pkg.R)) observedPkgs := internal.NewStringSet() definedPkgs := internal.NewStringSet() for _, p := range pkg.AllPkgs { definedPkgs.Add(string(p)) } - definedPkgs.Remove(string(pkg.Rpkg)) var cases []testCase cases = append(cases, commonTestCases...) @@ -222,10 +220,12 @@ func TestPkgCoverageDirectory(t *testing.T) { observedLanguages.Remove(pkg.UnknownLanguage.String()) definedLanguages.Remove(pkg.UnknownLanguage.String()) + definedLanguages.Remove(pkg.R.String()) observedPkgs.Remove(string(pkg.UnknownPkg)) definedPkgs.Remove(string(pkg.BinaryPkg)) definedPkgs.Remove(string(pkg.LinuxKernelPkg)) definedPkgs.Remove(string(pkg.LinuxKernelModulePkg)) + definedPkgs.Remove(string(pkg.Rpkg)) definedPkgs.Remove(string(pkg.UnknownPkg)) // for directory scans we should not expect to see any of the following package types From e8a9f24eb0d3f70e1f96e7535a995d6898ba00f5 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Mon, 8 May 2023 09:57:27 -0400 Subject: [PATCH 4/8] Document what NeedsCompilation values were found Signed-off-by: Will Murphy --- syft/pkg/cataloger/r/cataloger_test.go | 4 ++-- syft/pkg/cataloger/r/parse_description.go | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/syft/pkg/cataloger/r/cataloger_test.go b/syft/pkg/cataloger/r/cataloger_test.go index 671affb43f8..82e6c2b2747 100644 --- a/syft/pkg/cataloger/r/cataloger_test.go +++ b/syft/pkg/cataloger/r/cataloger_test.go @@ -20,7 +20,7 @@ func TestRPackageCataloger(t *testing.T) { Language: pkg.R, Type: pkg.Rpkg, PURL: "pkg:cran/base@4.3.0", - MetadataType: pkg.RustCargoPackageMetadataType, + MetadataType: pkg.RDescriptionFileMetadataType, Metadata: pkg.RDescriptionFileMetadata{ Title: "The R Base Package", Description: "Base R functions.", @@ -39,7 +39,7 @@ func TestRPackageCataloger(t *testing.T) { Language: pkg.R, Type: pkg.Rpkg, PURL: "pkg:cran/stringr@1.5.0.9000", - MetadataType: pkg.RustCargoPackageMetadataType, + MetadataType: pkg.RDescriptionFileMetadataType, Metadata: pkg.RDescriptionFileMetadata{ Title: "Simple, Consistent Wrappers for Common String Operations", Description: "A consistent, simple and easy to use set of wrappers around the fantastic 'stringi' package. All function and argument names (and positions) are consistent, all functions deal with \"NA\"'s and zero length vectors in the same way, and the output from one function is easy to feed into the input of another.", diff --git a/syft/pkg/cataloger/r/parse_description.go b/syft/pkg/cataloger/r/parse_description.go index 50ae0ec9d7f..b062b039559 100644 --- a/syft/pkg/cataloger/r/parse_description.go +++ b/syft/pkg/cataloger/r/parse_description.go @@ -67,6 +67,15 @@ func parseDataFromDescriptionMap(values map[string]string) parseData { } func yesNoToBool(s string) bool { + /* + $ docker run --rm -it rocker/r-ver bash + $ install2.r ggplot2 dplyr mlr3 caret # just some packages for a larger sample + $ find /usr/local/lib/R -name DESCRIPTION | xargs cat | grep 'NeedsCompilation:' | sort | uniq + NeedsCompilation: no + NeedsCompilation: yes + $ find /usr/local/lib/R -name DESCRIPTION | xargs cat | grep 'NeedsCompilation:' | wc -l + 105 + */ return strings.EqualFold(s, "yes") } From 07a4ef5ad6e1d985cf0f2bcb7b819fa2d17004f1 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Mon, 8 May 2023 10:36:51 -0400 Subject: [PATCH 5/8] Don't set FoundBy in cataloger Apparently this is set elsewhere. Signed-off-by: Will Murphy --- syft/pkg/cataloger/r/package.go | 1 - 1 file changed, 1 deletion(-) diff --git a/syft/pkg/cataloger/r/package.go b/syft/pkg/cataloger/r/package.go index 96382dc3be0..e0802831777 100644 --- a/syft/pkg/cataloger/r/package.go +++ b/syft/pkg/cataloger/r/package.go @@ -14,7 +14,6 @@ func newPackage(pd parseData, locations ...source.Location) pkg.Package { result := pkg.Package{ Name: pd.Package, Version: pd.Version, - FoundBy: catalogerName, Locations: locationSet, Licenses: []string{pd.License}, Language: pkg.R, From 88c3c577f145820a21279a81e8c17df4a1b840bd Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Tue, 9 May 2023 11:02:38 -0400 Subject: [PATCH 6/8] Bump JSON schema version Because the new R package metadata type is a change to the JSON that can be written, bump the schema. Signed-off-by: Will Murphy --- internal/constants.go | 2 +- schema/json/generate.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/constants.go b/internal/constants.go index 5adf784b7a0..177d316c3ab 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 = "7.1.5" + JSONSchemaVersion = "7.1.6" ) diff --git a/schema/json/generate.go b/schema/json/generate.go index 9197c4d3fe7..bf3daea1301 100644 --- a/schema/json/generate.go +++ b/schema/json/generate.go @@ -56,6 +56,7 @@ type artifactMetadataContainer struct { PythonPackage pkg.PythonPackageMetadata PythonPipfilelock pkg.PythonPipfileLockMetadata PythonRequirements pkg.PythonRequirementsMetadata + R pkg.RDescriptionFileMetadata Rebar pkg.RebarLockMetadata Rpm pkg.RpmMetadata RustCargo pkg.CargoPackageMetadata From 484cbe8558b94cda2049acbf3b644bd684b2291d Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Tue, 9 May 2023 11:05:53 -0400 Subject: [PATCH 7/8] Actually track new json schema Signed-off-by: Will Murphy --- schema/json/schema-7.1.6.json | 1863 +++++++++++++++++++++++++++++++++ 1 file changed, 1863 insertions(+) create mode 100644 schema/json/schema-7.1.6.json diff --git a/schema/json/schema-7.1.6.json b/schema/json/schema-7.1.6.json new file mode 100644 index 00000000000..88ec0181444 --- /dev/null +++ b/schema/json/schema-7.1.6.json @@ -0,0 +1,1863 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/anchore/syft/syft/formats/syftjson/model/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "AlpmMetadata": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "license": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "license", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "ApkMetadata": { + "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": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "license", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "BinaryMetadata": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CargoPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoapodsMetadata": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "ConanLockMetadata": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "type": "string" + }, + "build_requires": { + "type": "string" + }, + "py_requires": { + "type": "string" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ConanMetadata": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$ref": "#/$defs/Secrets" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "DpkgMetadata": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GemMetadata": { + "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" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "GolangBinMetadata": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GolangModMetadata": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaMetadata": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/PomProperties" + }, + "pomProject": { + "$ref": "#/$defs/PomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "KbPackageMetadata": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "LinuxKernelMetadata": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModuleMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NixStoreMetadata": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "licenses", + "homepage", + "description", + "url", + "private" + ] + }, + "NpmPackageLockJSONMetadata": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "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": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelModuleMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NixStoreMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/PythonRequirementsMetadata" + }, + { + "$ref": "#/$defs/RDescriptionFileMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/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": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "PomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "PomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PortageMetadata": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "license", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipfileLockMetadata": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonRequirementsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "name", + "extras", + "versionConstraint", + "url", + "markers" + ] + }, + "RDescriptionFileMetadata": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "RebarLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmMetadata": { + "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" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmdbFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "license", + "vendor", + "modularityLabel", + "files" + ] + }, + "RpmdbFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "SearchResult": { + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ] + }, + "Secrets": { + "properties": { + "location": { + "$ref": "#/$defs/Coordinates" + }, + "secrets": { + "items": { + "$ref": "#/$defs/SearchResult" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "location", + "secrets" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "target": true + }, + "type": "object", + "required": [ + "id", + "type", + "target" + ] + } + } +} From 469d0a2b683ef6968611e955efdb95c12c4d4aaf Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Tue, 9 May 2023 11:31:29 -0400 Subject: [PATCH 8/8] rename R to RDescriptionFile in generator Signed-off-by: Will Murphy --- schema/json/generate.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/schema/json/generate.go b/schema/json/generate.go index bf3daea1301..e81cff02030 100644 --- a/schema/json/generate.go +++ b/schema/json/generate.go @@ -29,6 +29,9 @@ can be extended to include specific package metadata struct shapes in the future // not matter as long as it is exported. // TODO: this should be generated from reflection of whats in the pkg package +// Should be created during generation below; use reflection's ability to +// create types at runtime. +// should be same name as struct minus metadata type artifactMetadataContainer struct { Alpm pkg.AlpmMetadata Apk pkg.ApkMetadata @@ -56,7 +59,7 @@ type artifactMetadataContainer struct { PythonPackage pkg.PythonPackageMetadata PythonPipfilelock pkg.PythonPipfileLockMetadata PythonRequirements pkg.PythonRequirementsMetadata - R pkg.RDescriptionFileMetadata + RDescriptionFile pkg.RDescriptionFileMetadata Rebar pkg.RebarLockMetadata Rpm pkg.RpmMetadata RustCargo pkg.CargoPackageMetadata