diff --git a/.github/workflows/.goreleaser.yml b/.github/workflows/.goreleaser.yml new file mode 100644 index 0000000..819eee7 --- /dev/null +++ b/.github/workflows/.goreleaser.yml @@ -0,0 +1,34 @@ +name: goreleaser + +on: + push: + tags: + - '*' + +permissions: + contents: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - + name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.22.3' + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@v5 + with: + distribution: goreleaser + # 'latest', 'nightly', or a semver + version: '~> v1' + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..33013df --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +tidelift-sbom-info +bin/* + +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..e3fff2b --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,64 @@ +# This is an example .goreleaser.yml file with some sensible defaults. +# Make sure to check the documentation at https://goreleaser.com + +# The lines below are called `modelines`. See `:help modeline` +# Feel free to remove those if you don't want/need to use them. +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json +# vim: set ts=2 sw=2 tw=0 fo=cnqoj + +version: 1 + +before: + hooks: + # You may remove this if you don't use go modules. + - go mod tidy + # you may remove this if you don't need go generate + # - go generate ./... + +builds: + - main: ./cmd/tidelift-sbom-analyzer + id: "tidelift-sbom-analyzer" + binary: tidelift-sbom-analyzer + env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + - main: ./cmd/tidelift-sbom-vulnerability-reporter + id: "tidelift-sbom-vulnerability-reporter" + binary: tidelift-sbom-vulnerability-reporter + env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + +archives: + - format: tar.gz + wrap_in_directory: false + # this name template makes the OS and Arch compatible with the results of `uname`. + name_template: >- + {{ .ProjectName }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} + {{- if .Arm }}v{{ .Arm }}{{ end }} + # use zip for windows archives + format_overrides: + - goos: windows + format: zip + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" + +release: + draft: true + replace_existing_draft: true + replace_existing_artifacts: true \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bf2b565 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Tidelift, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..70fc9f9 --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +CMDS = $(shell find cmd/ -mindepth 1 -maxdepth 1 -type d) +SRC = $(shell find . -type f -name '*.go') + +GOLANGCI_LINT:=$(shell command -v golangci-lint 2> /dev/null) + +all: build + +all-cross: build-windows build-linux-x86 build-linux-arm build-mac-arm + +build: $(SRC) + for d in $(CMDS) ; do pushd $$d > /dev/null ; CGO_ENABLED=0 go build -o ../../bin/ ; popd > /dev/null ; done + +build-windows: $(SRC) + mkdir -p bin/windows || /bin/true + for d in $(CMDS) ; do pushd $$d > /dev/null ; GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o ../../bin/windows ; popd > /dev/null ; done + +build-linux-x86: $(SRC) + mkdir -p bin/linux-x86 || /bin/true + for d in $(CMDS) ; do pushd $$d > /dev/null ; GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ../../bin/linux-x86 ; popd > /dev/null ; done + +build-linux-arm: $(SRC) + mkdir -p bin/linux-arm || /bin/true + for d in $(CMDS) ; do pushd $$d > /dev/null ; GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o ../../bin/linux-arm ; popd > /dev/null ; done + +build-mac-arm: $(SRC) + mkdir -p bin/mac-arm || /bin/true + for d in $(CMDS) ; do pushd $$d > /dev/null ; GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -o ../../bin/mac-arm ; popd > /dev/null ; done + +clean: + go clean + rm -f bin/* + +linter_installed: +ifndef GOLANGCI_LINT + $(error "golangci-lint is not available, please install https://github.com/golangci/golangci-lint") +endif + +lint: linter_installed + # https://github.com/golangci/golangci-lint#disabled-by-default-linters--e--enable + golangci-lint run + +fix: linter_installed + golangci-lint run --fix \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..53da15e --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +This repository contains some examples of using the Tidelift APIs to gather +information about open source library components. + +The examples all use a cyclonedx sbom as their entry point and then provide +different outputs depending on the specific example. + +Tagged releases are published on GitHub at +https://github.com/tidelift/tidelift-sbom-info/releases and can be downloaded and +run in your environment. + +## Current Commands + +* `tidelift-sbom-analyzer`: This takes a cyclonedx file as the first argument and +then outputs a CSV file with Tidelift's recommendations about the packages in the +SBOM. Takes an optional argument of `-o output.csv` to write the output to a file. + +* `tidelift-sbom-vulnerability-reporter`: This takes a cyclonedx file as the first +argument and then outputs a JSON file with information about any known vulnerabilities +in releases that are listed in the SBOM. Takes an optional argument of +`-o output.json` to write the output to a file. + +## Contributing + +While this is primarily intended to guide others in the use of the Tidelift API, +contributions to adapt and enhance the existing tools are always welcome. Additional +commands to provide different types of data are also welcome. + +## Building from source + +If you want to build from source, you can do so by running `make build`. The commands +then all live in the `bin/` subdirectory. + +If you need/want to build for an architecture that you're not running on, you can +do any of `make build-windows`, `make linux-x86`, `make linux-arm`, `make mac-arm`, +or `make all-cross` to build binaries for a different OS (or all) which then live +in the named subdirectories of the `bin/` subdirectory. diff --git a/cmd/tidelift-sbom-analyzer/main.go b/cmd/tidelift-sbom-analyzer/main.go new file mode 100644 index 0000000..43a9799 --- /dev/null +++ b/cmd/tidelift-sbom-analyzer/main.go @@ -0,0 +1,116 @@ +package main + +//go:generate oapi-codegen --package=tidelift_api_gen -generate=types -include-tags Packages,Releases,Vulnerabilities -o ../../internal/tidelift-api-gen/tidelift.gen.go https://tidelift.com/api/depci/subscriber-api.json + +import ( + "encoding/csv" + "flag" + "fmt" + "os" + + "github.com/package-url/packageurl-go" + log "github.com/sirupsen/logrus" + cyclonedx "github.com/tidelift/tidelift-sbom-info/internal/cyclonedx" + api "github.com/tidelift/tidelift-sbom-info/internal/tidelift-api-gen" + utils "github.com/tidelift/tidelift-sbom-info/internal/utils" +) + +func main() { + var debug bool + var outputFile string + flag.BoolVar(&debug, "debug", false, "Show debug logging") + flag.StringVar(&outputFile, "output", "", "Write output to a file (defaults to stdout)") + + flag.Usage = func() { + fmt.Fprintln(flag.CommandLine.Output(), "Display a CSV containing recommendations from Tidelift for the packages in an SBOM.") + fmt.Fprintln(flag.CommandLine.Output(), "") + fmt.Fprintln(flag.CommandLine.Output(), "Usage:") + fmt.Fprintln(flag.CommandLine.Output(), " tidelift-sbom-analyzer [SOURCE]") + fmt.Fprintln(flag.CommandLine.Output(), "") + fmt.Fprintln(flag.CommandLine.Output(), "Flags:") + flag.PrintDefaults() + } + + flag.Parse() + + if _, keyExists := os.LookupEnv("TIDELIFT_API_KEY"); !keyExists { + log.Fatalf("Error: TIDELIFT_API_KEY environment variable is required.") + } + + if flag.NArg() != 1 { + fmt.Fprintln(os.Stderr, "Error: need to pass cyclonedx file as argument") + flag.Usage() + os.Exit(1) + } + + if debug { + log.SetLevel(log.DebugLevel) + } else { + log.SetLevel(log.WarnLevel) + } + + purls, err := cyclonedx.SupportedPurlsFromBomFile(flag.Arg(0)) + if err != nil { + log.Fatalf("Error: %s", err) + } + + packageInfo, missingPackages := utils.GetPackageInfo(purls) + releaseInfo, missingReleases := utils.GetReleaseInfo(purls) + log.Debug(fmt.Sprintf("Unable to look up %d packages and %d releases (may be internal packages)", len(missingPackages), len(missingReleases))) + + if err := writeContentsReport(outputFile, purls, packageInfo, releaseInfo); err != nil { + log.Fatalf("Error: %s", err) + } +} + +func writeContentsReport(outputFile string, purls []packageurl.PackageURL, packageInfo []api.PackageDetail, releaseInfo []api.ReleaseDetail) error { + var writer *csv.Writer + if outputFile != "" { + f, err := os.Create(outputFile) + if err != nil { + return err + } + writer = csv.NewWriter(f) + defer f.Close() + } else { + writer = csv.NewWriter(os.Stdout) + } + + if err := writer.Write([]string{"platform", "name", "version", "purl", "license", "appears_maintained", "tidelift_recommended", "nearest_recommended_version"}); err != nil { + return err + } + + for _, purl := range purls { + var isRecommended = "" + var recommendedRelease = "" + var license = "" + releasePurlString := purl.ToString() + pkgPurlString := utils.ReleasePurlToPackagePurl(purl).String() + + for _, r := range releaseInfo { + if *r.Purl == releasePurlString { + if r.NearestRecommendedRelease != nil { + recommendedRelease = string(*r.NearestRecommendedRelease.Version) + } + license = *r.License.Expression + isRecommended = string(*r.TideliftRecommendation) + break + } + } + + var isMaintained = "" + for _, p := range packageInfo { + if p.Purl == pkgPurlString { + isMaintained = string(p.QualityChecks.PackageAppearsMaintained.Status) + break + } + } + + if err := writer.Write([]string{utils.TideliftPlatformFromPurl(purl), utils.TideliftPackageNameFromPurl(purl), purl.Version, releasePurlString, license, isMaintained, isRecommended, recommendedRelease}); err != nil { + return err + } + } + + writer.Flush() + return nil +} diff --git a/cmd/tidelift-sbom-vulnerability-reporter/main.go b/cmd/tidelift-sbom-vulnerability-reporter/main.go new file mode 100644 index 0000000..e37d950 --- /dev/null +++ b/cmd/tidelift-sbom-vulnerability-reporter/main.go @@ -0,0 +1,186 @@ +package main + +//go:generate oapi-codegen --package=tidelift_api_gen -generate=types -include-tags Packages,Releases,Vulnerabilities -o ../../internal/tidelift-api-gen/tidelift.gen.go https://tidelift.com/api/depci/subscriber-api.json + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + + requests "github.com/carlmjohnson/requests" + "github.com/package-url/packageurl-go" + log "github.com/sirupsen/logrus" + cyclonedx "github.com/tidelift/tidelift-sbom-info/internal/cyclonedx" + api "github.com/tidelift/tidelift-sbom-info/internal/tidelift-api-gen" + utils "github.com/tidelift/tidelift-sbom-info/internal/utils" +) + +type RecommendationDetails struct { + RealIssue bool `json:"real_issue"` + FalsePositiveReason *string `json:"false_positive_reason"` + ImpactScore int `json:"impact_score"` + ImpactDescription string `json:"impact_description"` + WorkaroundAvailable bool `json:"workaround_available"` + WorkaroundDescription *string `json:"workaround_description"` +} + +type VulnerabilityDetails struct { + VulnerabilityId string `json:"vulnerability_id"` + NistUrl string `json:"nist_url"` + CvssScore string `json:"cvss_score"` + Platform string `json:"platform"` + Name string `json:"name"` + Version string `json:"version"` + Purl string `json:"purl"` + Recommendation string `json:"recommendation"` + RecommendationDetails *RecommendationDetails `json:"recommendation_details"` +} + +func main() { + var debug bool + var outputFile string + + flag.BoolVar(&debug, "debug", false, "Show debug logging") + flag.StringVar(&outputFile, "output", "", "Write output to a file (defaults to stdout)") + + flag.Usage = func() { + fmt.Fprintln(flag.CommandLine.Output(), "Display a JSON file containing vulnerabilities from Tidelift for the packages in an SBOM.") + fmt.Fprintln(flag.CommandLine.Output(), "") + fmt.Fprintln(flag.CommandLine.Output(), "Usage:") + fmt.Fprintln(flag.CommandLine.Output(), " tidelift-sbom-vulnerability-reporter [SOURCE]") + fmt.Fprintln(flag.CommandLine.Output(), "") + fmt.Fprintln(flag.CommandLine.Output(), "Flags:") + flag.PrintDefaults() + } + + flag.Parse() + + if _, keyExists := os.LookupEnv("TIDELIFT_API_KEY"); !keyExists { + log.Fatalf("Error: TIDELIFT_API_KEY environment variable is required.") + } + + if flag.NArg() != 1 { + fmt.Fprintln(os.Stderr, "Error: need to pass cyclonedx file as argument") + flag.Usage() + os.Exit(1) + } + + if debug { + log.SetLevel(log.DebugLevel) + } else { + log.SetLevel(log.WarnLevel) + } + + purls, err := cyclonedx.SupportedPurlsFromBomFile(flag.Arg(0)) + if err != nil { + log.Fatalf("Error: %s", err) + } + + releaseInfo, missingReleases := utils.GetReleaseInfo(purls) + + if len(missingReleases) > 0 { + log.Debug(fmt.Sprintf("Unable to look up %d releases (may be internal packages)", len(missingReleases))) + } + + if err := writeVulnerabilitiesReport(outputFile, purls, releaseInfo); err != nil { + log.Fatalf("Error: %s", err) + } +} + +func writeVulnerabilitiesReport(outputFile string, purls []packageurl.PackageURL, releaseInfo []api.ReleaseDetail) error { + var vulnerabilties []VulnerabilityDetails + + for _, purl := range purls { + releasePurlString := purl.ToString() + pkgPurlString := utils.ReleasePurlToPackagePurl(purl).String() + for _, r := range releaseInfo { + if *r.Purl == releasePurlString { + if len(*r.Violations) == 0 { + break + } + for _, violation := range *r.Violations { + if *violation.CatalogStandard == "vulnerabilities" { + // oapi spec doesn't give definition of the additional properties but this is how vuln ids come through + id, ok := violation.AdditionalProperties["vulnerability"].(map[string]interface{})["id"].(string) + if !ok { + jsonStr, err := json.MarshalIndent(violation, "", " ") + if err != nil { + return err + } + log.Warn(fmt.Sprintf("Unable to parse vulnerability info: %s", jsonStr)) + continue + } + var vulnRsp api.Vulnerability + log.Debug(fmt.Sprintf("Getting vulnerability info for %s", id)) + if err := requests.URL(fmt.Sprintf("https://api.tidelift.com/external-api/v1/vulnerabilities/%s", id)). + ToJSON(&vulnRsp). + ContentType("application/json"). + // TODO: could use the full auth setup that we do in the tidelift cli + Header("Authorization", "Bearer "+os.Getenv("TIDELIFT_API_KEY")). + Fetch(context.Background()); err != nil { + log.Warn(fmt.Sprintf("problem fetching vulnerability info %s ", err)) + continue + } + for _, a := range vulnRsp.AffectedPackages { + if a.Purl == pkgPurlString { + v := VulnerabilityDetails{ + VulnerabilityId: id, + NistUrl: *vulnRsp.NistUrl, + Platform: *r.Platform, + Name: *r.Name, + Version: *r.Version, + Purl: *r.Purl, + Recommendation: string(a.Recommendation), + } + + if vulnRsp.CvssScore != nil { + v.CvssScore = *vulnRsp.CvssScore + } + + if a.RecommendationDetails != nil { + r := RecommendationDetails{ + RealIssue: a.RecommendationDetails.RealIssue, + FalsePositiveReason: a.RecommendationDetails.FalsePositiveReason, + ImpactScore: *a.RecommendationDetails.ImpactScore, + ImpactDescription: *a.RecommendationDetails.ImpactDescription, + WorkaroundAvailable: *a.RecommendationDetails.WorkaroundAvailable, + WorkaroundDescription: a.RecommendationDetails.WorkaroundDescription, + } + v.RecommendationDetails = &r + } + vulnerabilties = append(vulnerabilties, v) + break + } + } + } + } + } + } + } + + if outputFile != "" { + f, err := os.Create(outputFile) + if err != nil { + return err + } + jsonStr, err := json.Marshal(vulnerabilties) + if err != nil { + return err + } + _, err = f.Write(jsonStr) + if err != nil { + return err + } + f.Close() + } else { + jsonStr, err := json.MarshalIndent(vulnerabilties, "", " ") + if err != nil { + return err + } + fmt.Print(string(jsonStr)) + } + + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..74a71d9 --- /dev/null +++ b/go.mod @@ -0,0 +1,21 @@ +module github.com/tidelift/tidelift-sbom-info + +go 1.22 + +require github.com/CycloneDX/cyclonedx-go v0.8.0 // direct + +require github.com/carlmjohnson/requests v0.23.5 // direct + +require github.com/oapi-codegen/runtime v1.1.0 // direct + +require github.com/package-url/packageurl-go v0.1.2 // direct + +require github.com/sirupsen/logrus v1.9.3 // direct + +require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/google/uuid v1.4.0 // indirect + golang.org/x/exp v0.0.0-20240529005216-23cca8864a10 + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0e2e486 --- /dev/null +++ b/go.sum @@ -0,0 +1,49 @@ +github.com/CycloneDX/cyclonedx-go v0.8.0 h1:FyWVj6x6hoJrui5uRQdYZcSievw3Z32Z88uYzG/0D6M= +github.com/CycloneDX/cyclonedx-go v0.8.0/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= +github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= +github.com/carlmjohnson/requests v0.23.5 h1:NPANcAofwwSuC6SIMwlgmHry2V3pLrSqRiSBKYbNHHA= +github.com/carlmjohnson/requests v0.23.5/go.mod h1:zG9P28thdRnN61aD7iECFhH5iGGKX2jIjKQD9kqYH+o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/oapi-codegen/runtime v1.1.0 h1:rJpoNUawn5XTvekgfkvSZr0RqEnoYpFkyvrzfWeFKWM= +github.com/oapi-codegen/runtime v1.1.0/go.mod h1:BeSfBkWWWnAnGdyS+S/GnlbmHKzf8/hwkvelJZDeKA8= +github.com/package-url/packageurl-go v0.1.2 h1:0H2DQt6DHd/NeRlVwW4EZ4oEI6Bn40XlNPRqegcxuo4= +github.com/package-url/packageurl-go v0.1.2/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo= +github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +golang.org/x/exp v0.0.0-20240529005216-23cca8864a10 h1:vpzMC/iZhYFAjJzHU0Cfuq+w1vLLsF2vLkDrPjzKYck= +golang.org/x/exp v0.0.0-20240529005216-23cca8864a10/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/cyclonedx/cyclonedx.go b/internal/cyclonedx/cyclonedx.go new file mode 100644 index 0000000..caf76ee --- /dev/null +++ b/internal/cyclonedx/cyclonedx.go @@ -0,0 +1,86 @@ +// This has all of the code to read a cyclonedx sbom and convert to a list of +// platform, name, version tuples as needed by the current Tidelift APIs + +package cyclonedx + +import ( + "fmt" + "os" + "slices" + + cdx "github.com/CycloneDX/cyclonedx-go" + "github.com/package-url/packageurl-go" + log "github.com/sirupsen/logrus" + "github.com/tidelift/tidelift-sbom-info/internal/utils" +) + +func SupportedPurlsFromBomFile(filename string) ([]packageurl.PackageURL, error) { + bom, err := decodeCyclonedx(filename) + if err != nil { + return nil, err + } + + purls, err := extractSupportedPurls(bom) + if err != nil { + return nil, err + } + + return purls, nil +} + +func decodeCyclonedx(filename string) (*cdx.BOM, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + + bom := new(cdx.BOM) + decoder := cdx.NewBOMDecoder(file, cdx.BOMFileFormatJSON) + if err = decoder.Decode(bom); err != nil { + return nil, err + } + return bom, nil +} + +func extractSupportedPurls(bom *cdx.BOM) ([]packageurl.PackageURL, error) { + var purls []packageurl.PackageURL + + if bom.Components == nil { + log.Warn("CycloneDX file does not have any components") + return make([]packageurl.PackageURL, 0), nil + } + + for _, comp := range *bom.Components { + if comp.Type != "library" { + logBadPurl(comp, "Skipping non-library component in SBOM") + continue + } + if comp.PackageURL == "" { + logBadPurl(comp, "Skipping component in SBOM without purl") + continue + } + + purl, err := packageurl.FromString(comp.PackageURL) + if err != nil { + logBadPurl(comp, "Skipping malformed purl") + continue + } + if !slices.Contains(utils.SupportedPurlTypes, purl.Type) { + logBadPurl(comp, "Skipping component in SBOM with unsupported purl type") + continue + } + + purls = append(purls, purl) + } + + log.Debug(fmt.Sprintf("Found %d purls\n", len(purls))) + return purls, nil +} + +func logBadPurl(comp cdx.Component, msg string) { + log.WithFields(log.Fields{ + "type": comp.Type, + "name": comp.Name, + "purl": comp.PackageURL, + }).Debug(msg) +} diff --git a/internal/tidelift-api-gen/tidelift.gen.go b/internal/tidelift-api-gen/tidelift.gen.go new file mode 100644 index 0000000..9165cdf --- /dev/null +++ b/internal/tidelift-api-gen/tidelift.gen.go @@ -0,0 +1,1346 @@ +// Package tidelift_api_gen provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0 DO NOT EDIT. +package tidelift_api_gen + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/oapi-codegen/runtime" + openapi_types "github.com/oapi-codegen/runtime/types" +) + +const ( + BearerAuthScopes = "BearerAuth.Scopes" +) + +// Defines values for CycloneDxJsonBomFormat. +const ( + CycloneDX CycloneDxJsonBomFormat = "CycloneDX" +) + +// Defines values for GenericQualityCheckStatus. +const ( + GenericQualityCheckStatusNoassertion GenericQualityCheckStatus = "noassertion" + GenericQualityCheckStatusNotPassed GenericQualityCheckStatus = "not_passed" + GenericQualityCheckStatusPassed GenericQualityCheckStatus = "passed" + GenericQualityCheckStatusPending GenericQualityCheckStatus = "pending" +) + +// Defines values for PackageAppearsMaintainedAdditionalDataIsLiftedReason. +const ( + PackageIsLifted PackageAppearsMaintainedAdditionalDataIsLiftedReason = "package_is_lifted" +) + +// Defines values for PackageAppearsMaintainedAdditionalDataMaintenanceAppearanceReason. +const ( + PackageAppearsMaintained PackageAppearsMaintainedAdditionalDataMaintenanceAppearanceReason = "package_appears_maintained" + PackageAppearsUnmaintained PackageAppearsMaintainedAdditionalDataMaintenanceAppearanceReason = "package_appears_unmaintained" +) + +// Defines values for PackageAppearsMaintainedAdditionalDataMaintenanceAssessmentReason. +const ( + PackageIsMaintainedAssessedFalse PackageAppearsMaintainedAdditionalDataMaintenanceAssessmentReason = "package_is_maintained_assessed_false" + PackageIsMaintainedAssessedTrue PackageAppearsMaintainedAdditionalDataMaintenanceAssessmentReason = "package_is_maintained_assessed_true" +) + +// Defines values for PackageAppearsMaintainedQualityCheckStatus. +const ( + PackageAppearsMaintainedQualityCheckStatusNoassertion PackageAppearsMaintainedQualityCheckStatus = "noassertion" + PackageAppearsMaintainedQualityCheckStatusNotPassed PackageAppearsMaintainedQualityCheckStatus = "not_passed" + PackageAppearsMaintainedQualityCheckStatusPassed PackageAppearsMaintainedQualityCheckStatus = "passed" + PackageAppearsMaintainedQualityCheckStatusPending PackageAppearsMaintainedQualityCheckStatus = "pending" +) + +// Defines values for PackageAttestationStatementIncomeStreams0. +const ( + NOASSERTION PackageAttestationStatementIncomeStreams0 = "NOASSERTION" +) + +// Defines values for PackageDetailAlternativePackagesType. +const ( + Alternative PackageDetailAlternativePackagesType = "alternative" + Rename PackageDetailAlternativePackagesType = "rename" +) + +// Defines values for PackageDetailLicenseSource. +const ( + PackageDetailLicenseSourceCorrectedByTidelift PackageDetailLicenseSource = "corrected_by_tidelift" + PackageDetailLicenseSourceOverriddenByOrganization PackageDetailLicenseSource = "overridden_by_organization" + PackageDetailLicenseSourceResearchedByTidelift PackageDetailLicenseSource = "researched_by_tidelift" + PackageDetailLicenseSourceTideliftResearching PackageDetailLicenseSource = "tidelift_researching" + PackageDetailLicenseSourceUnknown PackageDetailLicenseSource = "unknown" + PackageDetailLicenseSourceValidSpdx PackageDetailLicenseSource = "valid_spdx" + PackageDetailLicenseSourceVerifiedByMaintainer PackageDetailLicenseSource = "verified_by_maintainer" +) + +// Defines values for PackageDetailQualityChecksDiscoverableSecurityPolicyStatus. +const ( + PackageDetailQualityChecksDiscoverableSecurityPolicyStatusNoassertion PackageDetailQualityChecksDiscoverableSecurityPolicyStatus = "noassertion" + PackageDetailQualityChecksDiscoverableSecurityPolicyStatusNotPassed PackageDetailQualityChecksDiscoverableSecurityPolicyStatus = "not_passed" + PackageDetailQualityChecksDiscoverableSecurityPolicyStatusPassed PackageDetailQualityChecksDiscoverableSecurityPolicyStatus = "passed" + PackageDetailQualityChecksDiscoverableSecurityPolicyStatusPending PackageDetailQualityChecksDiscoverableSecurityPolicyStatus = "pending" +) + +// Defines values for PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus. +const ( + PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatusNoassertion PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus = "noassertion" + PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatusNotPassed PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus = "not_passed" + PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatusPassed PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus = "passed" + PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatusPending PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus = "pending" +) + +// Defines values for PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus. +const ( + PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatusNoassertion PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus = "noassertion" + PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatusNotPassed PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus = "not_passed" + PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatusPassed PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus = "passed" + PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatusPending PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus = "pending" +) + +// Defines values for PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus. +const ( + PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatusNoassertion PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus = "noassertion" + PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatusNotPassed PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus = "not_passed" + PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatusPassed PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus = "passed" + PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatusPending PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus = "pending" +) + +// Defines values for PackageDetailQualityChecksPackageIsNotDeprecatedStatus. +const ( + PackageDetailQualityChecksPackageIsNotDeprecatedStatusNoassertion PackageDetailQualityChecksPackageIsNotDeprecatedStatus = "noassertion" + PackageDetailQualityChecksPackageIsNotDeprecatedStatusNotPassed PackageDetailQualityChecksPackageIsNotDeprecatedStatus = "not_passed" + PackageDetailQualityChecksPackageIsNotDeprecatedStatusPassed PackageDetailQualityChecksPackageIsNotDeprecatedStatus = "passed" + PackageDetailQualityChecksPackageIsNotDeprecatedStatusPending PackageDetailQualityChecksPackageIsNotDeprecatedStatus = "pending" +) + +// Defines values for PackageDetailQualityChecksReleaseManagersAreReviewedStatus. +const ( + PackageDetailQualityChecksReleaseManagersAreReviewedStatusNoassertion PackageDetailQualityChecksReleaseManagersAreReviewedStatus = "noassertion" + PackageDetailQualityChecksReleaseManagersAreReviewedStatusNotPassed PackageDetailQualityChecksReleaseManagersAreReviewedStatus = "not_passed" + PackageDetailQualityChecksReleaseManagersAreReviewedStatusPassed PackageDetailQualityChecksReleaseManagersAreReviewedStatus = "passed" + PackageDetailQualityChecksReleaseManagersAreReviewedStatusPending PackageDetailQualityChecksReleaseManagersAreReviewedStatus = "pending" +) + +// Defines values for PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatus. +const ( + PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatusNoassertion PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatus = "noassertion" + PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatusNotPassed PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatus = "not_passed" + PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatusPassed PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatus = "passed" + PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatusPending PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatus = "pending" +) + +// Defines values for PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus. +const ( + PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatusNoassertion PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus = "noassertion" + PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatusNotPassed PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus = "not_passed" + PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatusPassed PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus = "passed" + PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatusPending PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus = "pending" +) + +// Defines values for PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatus. +const ( + PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatusNoassertion PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatus = "noassertion" + PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatusNotPassed PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatus = "not_passed" + PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatusPassed PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatus = "passed" + PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatusPending PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatus = "pending" +) + +// Defines values for PackageDetailRepositorySource. +const ( + PackageDetailRepositorySourceHumanVerified PackageDetailRepositorySource = "human_verified" + PackageDetailRepositorySourcePackageManager PackageDetailRepositorySource = "package_manager" +) + +// Defines values for PackageDetailRepositoryStatisticsStatus. +const ( + Active PackageDetailRepositoryStatisticsStatus = "Active" + Removed PackageDetailRepositoryStatisticsStatus = "Removed" + Unmaintained PackageDetailRepositoryStatisticsStatus = "Unmaintained" +) + +// Defines values for PackageDetailVersioningScheme. +const ( + Calver PackageDetailVersioningScheme = "calver" + Maven PackageDetailVersioningScheme = "maven" + Osgi PackageDetailVersioningScheme = "osgi" + Other PackageDetailVersioningScheme = "other" + Pep440 PackageDetailVersioningScheme = "pep440" + Semver PackageDetailVersioningScheme = "semver" +) + +// Defines values for PackageIsNotEOLQualityCheckAdditionalDataReason. +const ( + PackageHasFutureEndOfLife PackageIsNotEOLQualityCheckAdditionalDataReason = "package_has_future_end_of_life" + PackageHasNoKnownEndOfLife PackageIsNotEOLQualityCheckAdditionalDataReason = "package_has_no_known_end_of_life" + PackageIsRenamed PackageIsNotEOLQualityCheckAdditionalDataReason = "package_is_renamed" + PackageIsUnknown PackageIsNotEOLQualityCheckAdditionalDataReason = "package_is_unknown" + PackageRepositoryIsUnmaintained PackageIsNotEOLQualityCheckAdditionalDataReason = "package_repository_is_unmaintained" +) + +// Defines values for PackageIsNotEOLQualityCheckStatus. +const ( + PackageIsNotEOLQualityCheckStatusNoassertion PackageIsNotEOLQualityCheckStatus = "noassertion" + PackageIsNotEOLQualityCheckStatusNotPassed PackageIsNotEOLQualityCheckStatus = "not_passed" + PackageIsNotEOLQualityCheckStatusPassed PackageIsNotEOLQualityCheckStatus = "passed" + PackageIsNotEOLQualityCheckStatusPending PackageIsNotEOLQualityCheckStatus = "pending" +) + +// Defines values for QualityChecksDiscoverableSecurityPolicyStatus. +const ( + QualityChecksDiscoverableSecurityPolicyStatusNoassertion QualityChecksDiscoverableSecurityPolicyStatus = "noassertion" + QualityChecksDiscoverableSecurityPolicyStatusNotPassed QualityChecksDiscoverableSecurityPolicyStatus = "not_passed" + QualityChecksDiscoverableSecurityPolicyStatusPassed QualityChecksDiscoverableSecurityPolicyStatus = "passed" + QualityChecksDiscoverableSecurityPolicyStatusPending QualityChecksDiscoverableSecurityPolicyStatus = "pending" +) + +// Defines values for QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus. +const ( + QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatusNoassertion QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus = "noassertion" + QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatusNotPassed QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus = "not_passed" + QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatusPassed QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus = "passed" + QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatusPending QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus = "pending" +) + +// Defines values for QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus. +const ( + QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatusNoassertion QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus = "noassertion" + QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatusNotPassed QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus = "not_passed" + QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatusPassed QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus = "passed" + QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatusPending QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus = "pending" +) + +// Defines values for QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus. +const ( + QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatusNoassertion QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus = "noassertion" + QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatusNotPassed QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus = "not_passed" + QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatusPassed QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus = "passed" + QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatusPending QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus = "pending" +) + +// Defines values for QualityChecksPackageIsNotDeprecatedStatus. +const ( + QualityChecksPackageIsNotDeprecatedStatusNoassertion QualityChecksPackageIsNotDeprecatedStatus = "noassertion" + QualityChecksPackageIsNotDeprecatedStatusNotPassed QualityChecksPackageIsNotDeprecatedStatus = "not_passed" + QualityChecksPackageIsNotDeprecatedStatusPassed QualityChecksPackageIsNotDeprecatedStatus = "passed" + QualityChecksPackageIsNotDeprecatedStatusPending QualityChecksPackageIsNotDeprecatedStatus = "pending" +) + +// Defines values for QualityChecksReleaseManagersAreReviewedStatus. +const ( + QualityChecksReleaseManagersAreReviewedStatusNoassertion QualityChecksReleaseManagersAreReviewedStatus = "noassertion" + QualityChecksReleaseManagersAreReviewedStatusNotPassed QualityChecksReleaseManagersAreReviewedStatus = "not_passed" + QualityChecksReleaseManagersAreReviewedStatusPassed QualityChecksReleaseManagersAreReviewedStatus = "passed" + QualityChecksReleaseManagersAreReviewedStatusPending QualityChecksReleaseManagersAreReviewedStatus = "pending" +) + +// Defines values for QualityChecksReleasesAreDiscoverableUpstreamStatus. +const ( + QualityChecksReleasesAreDiscoverableUpstreamStatusNoassertion QualityChecksReleasesAreDiscoverableUpstreamStatus = "noassertion" + QualityChecksReleasesAreDiscoverableUpstreamStatusNotPassed QualityChecksReleasesAreDiscoverableUpstreamStatus = "not_passed" + QualityChecksReleasesAreDiscoverableUpstreamStatusPassed QualityChecksReleasesAreDiscoverableUpstreamStatus = "passed" + QualityChecksReleasesAreDiscoverableUpstreamStatusPending QualityChecksReleasesAreDiscoverableUpstreamStatus = "pending" +) + +// Defines values for QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus. +const ( + QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatusNoassertion QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus = "noassertion" + QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatusNotPassed QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus = "not_passed" + QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatusPassed QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus = "passed" + QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatusPending QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus = "pending" +) + +// Defines values for QualityChecksTwoFactorAuthenticationForPackageManagerStatus. +const ( + Noassertion QualityChecksTwoFactorAuthenticationForPackageManagerStatus = "noassertion" + NotPassed QualityChecksTwoFactorAuthenticationForPackageManagerStatus = "not_passed" + Passed QualityChecksTwoFactorAuthenticationForPackageManagerStatus = "passed" + Pending QualityChecksTwoFactorAuthenticationForPackageManagerStatus = "pending" +) + +// Defines values for ReleaseDetailLicenseSource. +const ( + ReleaseDetailLicenseSourceCorrectedByTidelift ReleaseDetailLicenseSource = "corrected_by_tidelift" + ReleaseDetailLicenseSourceOverriddenByOrganization ReleaseDetailLicenseSource = "overridden_by_organization" + ReleaseDetailLicenseSourceResearchedByTidelift ReleaseDetailLicenseSource = "researched_by_tidelift" + ReleaseDetailLicenseSourceTideliftResearching ReleaseDetailLicenseSource = "tidelift_researching" + ReleaseDetailLicenseSourceValidSpdx ReleaseDetailLicenseSource = "valid_spdx" + ReleaseDetailLicenseSourceVerifiedByMaintainer ReleaseDetailLicenseSource = "verified_by_maintainer" +) + +// Defines values for ReleaseDetailRepositorySource. +const ( + ReleaseDetailRepositorySourceHumanVerified ReleaseDetailRepositorySource = "human_verified" + ReleaseDetailRepositorySourcePackageManager ReleaseDetailRepositorySource = "package_manager" +) + +// Defines values for TideliftPackageRecommendation. +const ( + TideliftPackageRecommendationCautionAdvised TideliftPackageRecommendation = "caution_advised" + TideliftPackageRecommendationNeutral TideliftPackageRecommendation = "neutral" + TideliftPackageRecommendationNotAssessed TideliftPackageRecommendation = "not_assessed" + TideliftPackageRecommendationNotRecommended TideliftPackageRecommendation = "not_recommended" + TideliftPackageRecommendationRecommended TideliftPackageRecommendation = "recommended" +) + +// Defines values for TideliftReleaseRecommendation. +const ( + TideliftReleaseRecommendationNotAssessed TideliftReleaseRecommendation = "not_assessed" + TideliftReleaseRecommendationNotRecommended TideliftReleaseRecommendation = "not_recommended" + TideliftReleaseRecommendationRecommended TideliftReleaseRecommendation = "recommended" +) + +// Defines values for VulnerabilityAffectedPackagesRecommendation. +const ( + Ignore VulnerabilityAffectedPackagesRecommendation = "ignore" + Upgrade VulnerabilityAffectedPackagesRecommendation = "upgrade" + UpgradeOrWorkaround VulnerabilityAffectedPackagesRecommendation = "upgrade_or_workaround" +) + +// Defines values for SbomTypeParam. +const ( + SbomTypeParamCyclonedx SbomTypeParam = "cyclonedx" + SbomTypeParamSpdx SbomTypeParam = "spdx" +) + +// Defines values for ReleaseSbomParamsSbomType. +const ( + ReleaseSbomParamsSbomTypeCyclonedx ReleaseSbomParamsSbomType = "cyclonedx" + ReleaseSbomParamsSbomTypeSpdx ReleaseSbomParamsSbomType = "spdx" +) + +// CycloneDxJson defines model for CycloneDxJson. +type CycloneDxJson struct { + BomFormat *CycloneDxJsonBomFormat `json:"bomFormat,omitempty"` + Components *[]map[string]interface{} `json:"components,omitempty"` + Dependencies *[]map[string]interface{} `json:"dependencies,omitempty"` + Metadata *map[string]interface{} `json:"metadata,omitempty"` + SerialNumber *string `json:"serialNumber,omitempty"` + SpecVersion *string `json:"specVersion,omitempty"` +} + +// CycloneDxJsonBomFormat defines model for CycloneDxJson.BomFormat. +type CycloneDxJsonBomFormat string + +// CycloneDxXml defines model for CycloneDxXml. +type CycloneDxXml struct { + Components *[]map[string]interface{} `json:"components,omitempty"` + Dependencies *[]map[string]interface{} `json:"dependencies,omitempty"` + Metadata *map[string]interface{} `json:"metadata,omitempty"` +} + +// GenericQualityCheck defines model for GenericQualityCheck. +type GenericQualityCheck struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status GenericQualityCheckStatus `json:"status"` +} + +// GenericQualityCheckStatus defines model for GenericQualityCheck.Status. +type GenericQualityCheckStatus string + +// Package defines model for Package. +type Package struct { + Name string `json:"name"` + Platform string `json:"platform"` +} + +// PackageAppearsMaintainedAdditionalDataIsLifted defines model for PackageAppearsMaintainedAdditionalData--is-lifted. +type PackageAppearsMaintainedAdditionalDataIsLifted struct { + // Reason Tidelift is partnering with the maintainer of this package to ensure its continued maintenance. + Reason PackageAppearsMaintainedAdditionalDataIsLiftedReason `json:"reason"` +} + +// PackageAppearsMaintainedAdditionalDataIsLiftedReason Tidelift is partnering with the maintainer of this package to ensure its continued maintenance. +type PackageAppearsMaintainedAdditionalDataIsLiftedReason string + +// PackageAppearsMaintainedAdditionalDataMaintenanceAppearance defines model for PackageAppearsMaintainedAdditionalData--maintenance-appearance. +type PackageAppearsMaintainedAdditionalDataMaintenanceAppearance struct { + // ContributorsInPastYear Whether there has been any contributors to the package's source repository in the past year. + ContributorsInPastYear bool `json:"contributors_in_past_year"` + + // FrequentPackageActivity Whether at least 1/3 of issues and pull requests must have been closed in the past year. + FrequentPackageActivity bool `json:"frequent_package_activity"` + + // Reason Tidelift checks upstream source repository and release activity to determine maintenace activity for the package. + // If the source repository has been marked archived or unmaintained, it fails this check. + // Otherwise, Tidelift considers a package to appear unmaintained if all of the following are `false`: `contributors_in_past_year` + // `frequent_package_activity`, `recent_commits`, and `recent_latest_release`. + Reason PackageAppearsMaintainedAdditionalDataMaintenanceAppearanceReason `json:"reason"` + + // RecentCommits Whether there has been a commit to the package's source repository in the past year. + RecentCommits bool `json:"recent_commits"` + + // RecentLatestRelease Whether there has been a release of the package in the past 6 months. + RecentLatestRelease bool `json:"recent_latest_release"` + + // RepositoryMarkedUnmaintained Whether the package's source repository been archived or otherwise marked unmaintained. + RepositoryMarkedUnmaintained *bool `json:"repository_marked_unmaintained,omitempty"` +} + +// PackageAppearsMaintainedAdditionalDataMaintenanceAppearanceReason Tidelift checks upstream source repository and release activity to determine maintenace activity for the package. +// If the source repository has been marked archived or unmaintained, it fails this check. +// Otherwise, Tidelift considers a package to appear unmaintained if all of the following are `false`: `contributors_in_past_year` +// `frequent_package_activity`, `recent_commits`, and `recent_latest_release`. +type PackageAppearsMaintainedAdditionalDataMaintenanceAppearanceReason string + +// PackageAppearsMaintainedAdditionalDataMaintenanceAssessment defines model for PackageAppearsMaintainedAdditionalData--maintenance-assessment. +type PackageAppearsMaintainedAdditionalDataMaintenanceAssessment struct { + // IsMaintained The manual assessment of the package's maintenance status. + IsMaintained bool `json:"is_maintained"` + + // IsMaintainedUpdatedAt The time at which the package's maintenance status was manually assessed. + IsMaintainedUpdatedAt time.Time `json:"is_maintained_updated_at"` + + // Reason Tidelift has manually assessed this package's maintenance status. + Reason PackageAppearsMaintainedAdditionalDataMaintenanceAssessmentReason `json:"reason"` +} + +// PackageAppearsMaintainedAdditionalDataMaintenanceAssessmentReason Tidelift has manually assessed this package's maintenance status. +type PackageAppearsMaintainedAdditionalDataMaintenanceAssessmentReason string + +// PackageAppearsMaintainedQualityCheck The project lacks auto-detectable evidence of recent activity +type PackageAppearsMaintainedQualityCheck struct { + AdditionalData PackageAppearsMaintainedQualityCheck_AdditionalData `json:"additional_data"` + Status PackageAppearsMaintainedQualityCheckStatus `json:"status"` +} + +// PackageAppearsMaintainedQualityCheckAdditionalData3 defines model for . +type PackageAppearsMaintainedQualityCheckAdditionalData3 = map[string]interface{} + +// PackageAppearsMaintainedQualityCheck_AdditionalData defines model for PackageAppearsMaintainedQualityCheck.AdditionalData. +type PackageAppearsMaintainedQualityCheck_AdditionalData struct { + union json.RawMessage +} + +// PackageAppearsMaintainedQualityCheckStatus defines model for PackageAppearsMaintainedQualityCheck.Status. +type PackageAppearsMaintainedQualityCheckStatus string + +// PackageAttestationStatement defines model for PackageAttestationStatement. +type PackageAttestationStatement struct { + CleanReleaseAvailable string `json:"CleanReleaseAvailable"` + CodeReviewPractice string `json:"CodeReviewPractice"` + DependenciesAreManaged string `json:"DependenciesAreManaged"` + DocumentedBuildPractices string `json:"DocumentedBuildPractices"` + FixedVulnerabilities string `json:"FixedVulnerabilities"` + FuzzingPractice string `json:"FuzzingPractice"` + IncomeStreams PackageAttestationStatement_IncomeStreams `json:"IncomeStreams"` + KnownReleasesURL string `json:"KnownReleasesURL"` + KnownVulnerabilitiesURL string `json:"KnownVulnerabilitiesURL"` + LatestStableRelease string `json:"LatestStableRelease"` + MultipleMaintainers string `json:"MultipleMaintainers"` + NoBinariesInRepository string `json:"NoBinariesInRepository"` + PURL string `json:"PURL"` + PackageManagerMFAEnabled string `json:"PackageManagerMFAEnabled"` + PackageName string `json:"PackageName"` + PackagePlatform string `json:"PackagePlatform"` + PackageSecurityContact string `json:"PackageSecurityContact"` + PackageSecurityPolicyURL string `json:"PackageSecurityPolicyURL"` + PackageStatus struct { + LastModifiedDateTimeUTC time.Time `json:"LastModifiedDateTimeUTC"` + Status string `json:"Status"` + } `json:"PackageStatus"` + ReleasesDigitallySigned *string `json:"ReleasesDigitallySigned,omitempty"` + ReleasesInUse []string `json:"ReleasesInUse"` + SBOM []struct { + DigitalSignatureURL string `json:"DigitalSignatureURL"` + Type string `json:"Type"` + URL string `json:"URL"` + } `json:"SBOM"` + SDLCEvidenceDataURL string `json:"SDLCEvidenceDataURL"` + SDLCPolicyURL string `json:"SDLCPolicyURL"` + SPDXLicenseLatestRelease string `json:"SPDXLicenseLatestRelease"` + SecurityResponsive string `json:"SecurityResponsive"` + SourceRepoMFAEnabled string `json:"SourceRepoMFAEnabled"` + SuccessionPlan string `json:"SuccessionPlan"` + UpstreamRepositoryURL string `json:"UpstreamRepositoryURL"` +} + +// PackageAttestationStatementIncomeStreams0 defines model for PackageAttestationStatement.IncomeStreams.0. +type PackageAttestationStatementIncomeStreams0 string + +// PackageAttestationStatementIncomeStreams1 defines model for . +type PackageAttestationStatementIncomeStreams1 = []struct { + EvidenceURL string `json:"EvidenceURL"` + LastModifiedDateTimeUTC time.Time `json:"LastModifiedDateTimeUTC"` + Type string `json:"Type"` +} + +// PackageAttestationStatement_IncomeStreams defines model for PackageAttestationStatement.IncomeStreams. +type PackageAttestationStatement_IncomeStreams struct { + union json.RawMessage +} + +// PackageDetail defines model for PackageDetail. +type PackageDetail struct { + // AlternativePackages List of alternative packages that could be used in place of this package + AlternativePackages []struct { + // Name Name of the other package + Name string `json:"name"` + + // Type How the alternative package relates to the original package + Type PackageDetailAlternativePackagesType `json:"type"` + } `json:"alternative_packages"` + + // ContributorsCount Number of contributors to the upstream source repository, if available + ContributorsCount *int `json:"contributors_count"` + + // Description Package description taken from the upstream package manager data + Description *string `json:"description"` + + // IsLifted Whether Tidelift partners with the maintainers of this package + IsLifted bool `json:"is_lifted"` + + // LastChangedOn When the package record last was updated by new information + LastChangedOn *openapi_types.Date `json:"last_changed_on"` + LatestRecommendedRelease struct { + PublishedAt *time.Time `json:"published_at"` + Version *string `json:"version"` + } `json:"latest_recommended_release"` + LatestRelease struct { + PublishedAt *time.Time `json:"published_at"` + Version *string `json:"version"` + } `json:"latest_release"` + LatestStableRelease struct { + PublishedAt *time.Time `json:"published_at"` + Version *string `json:"version"` + } `json:"latest_stable_release"` + License struct { + // Expression An [SPDX expression](https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/) + Expression *string `json:"expression"` + + // Source The source of the license information, if the license is known. + Source *PackageDetailLicenseSource `json:"source"` + } `json:"license"` + + // Name The canonical name of the package (may be adjusted for casing or hyphen vs underscore for example) + Name string `json:"name"` + + // PackageManagerUrl Link to this package on the package manager website + PackageManagerUrl *string `json:"package_manager_url"` + + // Platform The package manager platform, all lowercase. + Platform string `json:"platform"` + + // Purl An identifier for the package following the [package URL (purl) specification](https://github.com/package-url/purl-spec) + Purl string `json:"purl"` + QualityChecks struct { + DiscoverableSecurityPolicy struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status PackageDetailQualityChecksDiscoverableSecurityPolicyStatus `json:"status"` + } `json:"discoverable_security_policy"` + NoKnownIssuesInDependenciesForLatestRelease struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus `json:"status"` + } `json:"no_known_issues_in_dependencies_for_latest_release"` + NoKnownVulnerabilitiesOnLatestRelease struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus `json:"status"` + } `json:"no_known_vulnerabilities_on_latest_release"` + + // PackageAppearsMaintained The project lacks auto-detectable evidence of recent activity + PackageAppearsMaintained PackageAppearsMaintainedQualityCheck `json:"package_appears_maintained"` + PackageHasAStableReleaseGreaterThanTwoYearsOld struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus `json:"status"` + } `json:"package_has_a_stable_release_greater_than_two_years_old"` + PackageIsNotDeprecated struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status PackageDetailQualityChecksPackageIsNotDeprecatedStatus `json:"status"` + } `json:"package_is_not_deprecated"` + + // PackageIsNotEol The project does not have an active end-of-life notification. + PackageIsNotEol PackageIsNotEOLQualityCheck `json:"package_is_not_eol"` + ReleaseManagersAreReviewed struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status PackageDetailQualityChecksReleaseManagersAreReviewedStatus `json:"status"` + } `json:"release_managers_are_reviewed"` + ReleasesAreDiscoverableUpstream struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatus `json:"status"` + } `json:"releases_are_discoverable_upstream"` + TwoFactorAuthenticationAtSourceRepository struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus `json:"status"` + } `json:"two_factor_authentication_at_source_repository"` + TwoFactorAuthenticationForPackageManager struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatus `json:"status"` + } `json:"two_factor_authentication_for_package_manager"` + } `json:"quality_checks"` + + // Releases An array of all known public releases of the package + Releases []struct { + // PublishedAt Time that the release was published to the package manager + PublishedAt *time.Time `json:"published_at"` + TideliftRecommendation *TideliftReleaseRecommendation `json:"tidelift_recommendation"` + + // Version The release's name + Version string `json:"version"` + } `json:"releases"` + + // Repository The source code repository where the package is maintained + Repository struct { + Source PackageDetailRepositorySource `json:"source"` + Url *string `json:"url"` + } `json:"repository"` + + // RepositoryStatistics Statistics about the package's source code repository that can be used to assess upstream activity and maintenance + RepositoryStatistics *struct { + // Last52WeeksContributors Number of distinct contributors in the past year + Last52WeeksContributors *int `json:"last_52_weeks_contributors"` + + // LastCommitAt Date of the last commit + LastCommitAt *openapi_types.Date `json:"last_commit_at"` + + // OneYearClosedIssues How many issues have been closed in the last year + OneYearClosedIssues *int `json:"one_year_closed_issues"` + + // OneYearClosedPullRequests How many pull requests have been closed in the past year + OneYearClosedPullRequests *int `json:"one_year_closed_pull_requests"` + + // OneYearTotalIssues How many issues were opened in the last year + OneYearTotalIssues *int `json:"one_year_total_issues"` + + // OneYearTotalPullRequests How many pull requests were opened in the last year + OneYearTotalPullRequests *int `json:"one_year_total_pull_requests"` + + // Status Source repositories may be active, archived (Unmaintained), or deleted (Removed); Tidelift may add other statuses in the future + Status PackageDetailRepositoryStatisticsStatus `json:"status"` + } `json:"repository_statistics"` + + // SdlcEvidence If available, a URL that may help figure out the project's software development lifecycle policies + SdlcEvidence *string `json:"sdlc_evidence"` + + // SdlcPolicy If available, a URL where the project explains its software development lifecycle policy (for example, which release streams are still receiving security updates) + SdlcPolicy *string `json:"sdlc_policy"` + + // SecurityPolicyUrl If available, a url for the package's upstream security reporting policy + SecurityPolicyUrl *string `json:"security_policy_url"` + TideliftRecommendation *TideliftPackageRecommendation `json:"tidelift_recommendation"` + TideliftRecommendationReasons *TideliftPackageRecommendationReasons `json:"tidelift_recommendation_reasons,omitempty"` + + // VersioningScheme The version numbering rules that the package intends to use; Tidelift may add new possible values for this field over time + VersioningScheme PackageDetailVersioningScheme `json:"versioning_scheme"` +} + +// PackageDetailAlternativePackagesType How the alternative package relates to the original package +type PackageDetailAlternativePackagesType string + +// PackageDetailLicenseSource The source of the license information, if the license is known. +type PackageDetailLicenseSource string + +// PackageDetailQualityChecksDiscoverableSecurityPolicyStatus defines model for PackageDetail.QualityChecks.DiscoverableSecurityPolicy.Status. +type PackageDetailQualityChecksDiscoverableSecurityPolicyStatus string + +// PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus defines model for PackageDetail.QualityChecks.NoKnownIssuesInDependenciesForLatestRelease.Status. +type PackageDetailQualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus string + +// PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus defines model for PackageDetail.QualityChecks.NoKnownVulnerabilitiesOnLatestRelease.Status. +type PackageDetailQualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus string + +// PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus defines model for PackageDetail.QualityChecks.PackageHasAStableReleaseGreaterThanTwoYearsOld.Status. +type PackageDetailQualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus string + +// PackageDetailQualityChecksPackageIsNotDeprecatedStatus defines model for PackageDetail.QualityChecks.PackageIsNotDeprecated.Status. +type PackageDetailQualityChecksPackageIsNotDeprecatedStatus string + +// PackageDetailQualityChecksReleaseManagersAreReviewedStatus defines model for PackageDetail.QualityChecks.ReleaseManagersAreReviewed.Status. +type PackageDetailQualityChecksReleaseManagersAreReviewedStatus string + +// PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatus defines model for PackageDetail.QualityChecks.ReleasesAreDiscoverableUpstream.Status. +type PackageDetailQualityChecksReleasesAreDiscoverableUpstreamStatus string + +// PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus defines model for PackageDetail.QualityChecks.TwoFactorAuthenticationAtSourceRepository.Status. +type PackageDetailQualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus string + +// PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatus defines model for PackageDetail.QualityChecks.TwoFactorAuthenticationForPackageManager.Status. +type PackageDetailQualityChecksTwoFactorAuthenticationForPackageManagerStatus string + +// PackageDetailRepositorySource defines model for PackageDetail.Repository.Source. +type PackageDetailRepositorySource string + +// PackageDetailRepositoryStatisticsStatus Source repositories may be active, archived (Unmaintained), or deleted (Removed); Tidelift may add other statuses in the future +type PackageDetailRepositoryStatisticsStatus string + +// PackageDetailVersioningScheme The version numbering rules that the package intends to use; Tidelift may add new possible values for this field over time +type PackageDetailVersioningScheme string + +// PackageDetailRelease defines model for PackageDetailRelease. +type PackageDetailRelease struct { + PublishedAt *time.Time `json:"published_at"` + Version *string `json:"version"` +} + +// PackageIsNotEOLQualityCheck The project does not have an active end-of-life notification. +type PackageIsNotEOLQualityCheck struct { + AdditionalData struct { + // EffectiveOn The ISO 8601 formatted date at which the package will be considered end-of-life. + EffectiveOn *openapi_types.Date `json:"effective_on"` + + // EolReferenceFound If Tidelift has a known end-of-life effective date from a published source. + EolReferenceFound bool `json:"eol_reference_found"` + + // NoPlannedMaintenance If the package's deprecation status and repository activity indicates no further updates should be expected. + NoPlannedMaintenance bool `json:"no_planned_maintenance"` + + // PackageRenamedTo The packages that this package was renamed to. + PackageRenamedTo []struct { + Name *string `json:"name,omitempty"` + Platform *string `json:"platform,omitempty"` + } `json:"package_renamed_to"` + + // Reason The reason Tidelift has decided the package's end-of-life status. + Reason PackageIsNotEOLQualityCheckAdditionalDataReason `json:"reason"` + + // ReferenceUrl The URL which indicates that the package will be end-of-life. + ReferenceUrl *string `json:"reference_url"` + } `json:"additional_data"` + Status PackageIsNotEOLQualityCheckStatus `json:"status"` +} + +// PackageIsNotEOLQualityCheckAdditionalDataReason The reason Tidelift has decided the package's end-of-life status. +type PackageIsNotEOLQualityCheckAdditionalDataReason string + +// PackageIsNotEOLQualityCheckStatus defines model for PackageIsNotEOLQualityCheck.Status. +type PackageIsNotEOLQualityCheckStatus string + +// PackageList defines model for PackageList. +type PackageList struct { + Results *[]PackageWithPurl `json:"results,omitempty"` +} + +// PackageWithHint defines model for PackageWithHint. +type PackageWithHint struct { + Hint *string `json:"hint,omitempty"` + Name string `json:"name"` + Platform string `json:"platform"` +} + +// PackageWithPurl defines model for PackageWithPurl. +type PackageWithPurl struct { + Name string `json:"name"` + Platform string `json:"platform"` + Purl *string `json:"purl,omitempty"` +} + +// PaginationEnvelope defines model for PaginationEnvelope. +type PaginationEnvelope struct { + CurrentPage int `json:"current_page"` + NextPage *int `json:"next_page"` + PerPage int `json:"per_page"` + PrevPage *int `json:"prev_page"` + TotalCount int `json:"total_count"` + TotalPages int `json:"total_pages"` +} + +// QualityChecks defines model for QualityChecks. +type QualityChecks struct { + DiscoverableSecurityPolicy struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status QualityChecksDiscoverableSecurityPolicyStatus `json:"status"` + } `json:"discoverable_security_policy"` + NoKnownIssuesInDependenciesForLatestRelease struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus `json:"status"` + } `json:"no_known_issues_in_dependencies_for_latest_release"` + NoKnownVulnerabilitiesOnLatestRelease struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus `json:"status"` + } `json:"no_known_vulnerabilities_on_latest_release"` + + // PackageAppearsMaintained The project lacks auto-detectable evidence of recent activity + PackageAppearsMaintained PackageAppearsMaintainedQualityCheck `json:"package_appears_maintained"` + PackageHasAStableReleaseGreaterThanTwoYearsOld struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus `json:"status"` + } `json:"package_has_a_stable_release_greater_than_two_years_old"` + PackageIsNotDeprecated struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status QualityChecksPackageIsNotDeprecatedStatus `json:"status"` + } `json:"package_is_not_deprecated"` + + // PackageIsNotEol The project does not have an active end-of-life notification. + PackageIsNotEol PackageIsNotEOLQualityCheck `json:"package_is_not_eol"` + ReleaseManagersAreReviewed struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status QualityChecksReleaseManagersAreReviewedStatus `json:"status"` + } `json:"release_managers_are_reviewed"` + ReleasesAreDiscoverableUpstream struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status QualityChecksReleasesAreDiscoverableUpstreamStatus `json:"status"` + } `json:"releases_are_discoverable_upstream"` + TwoFactorAuthenticationAtSourceRepository struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus `json:"status"` + } `json:"two_factor_authentication_at_source_repository"` + TwoFactorAuthenticationForPackageManager struct { + AdditionalData *map[string]interface{} `json:"additional_data"` + Status QualityChecksTwoFactorAuthenticationForPackageManagerStatus `json:"status"` + } `json:"two_factor_authentication_for_package_manager"` +} + +// QualityChecksDiscoverableSecurityPolicyStatus defines model for QualityChecks.DiscoverableSecurityPolicy.Status. +type QualityChecksDiscoverableSecurityPolicyStatus string + +// QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus defines model for QualityChecks.NoKnownIssuesInDependenciesForLatestRelease.Status. +type QualityChecksNoKnownIssuesInDependenciesForLatestReleaseStatus string + +// QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus defines model for QualityChecks.NoKnownVulnerabilitiesOnLatestRelease.Status. +type QualityChecksNoKnownVulnerabilitiesOnLatestReleaseStatus string + +// QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus defines model for QualityChecks.PackageHasAStableReleaseGreaterThanTwoYearsOld.Status. +type QualityChecksPackageHasAStableReleaseGreaterThanTwoYearsOldStatus string + +// QualityChecksPackageIsNotDeprecatedStatus defines model for QualityChecks.PackageIsNotDeprecated.Status. +type QualityChecksPackageIsNotDeprecatedStatus string + +// QualityChecksReleaseManagersAreReviewedStatus defines model for QualityChecks.ReleaseManagersAreReviewed.Status. +type QualityChecksReleaseManagersAreReviewedStatus string + +// QualityChecksReleasesAreDiscoverableUpstreamStatus defines model for QualityChecks.ReleasesAreDiscoverableUpstream.Status. +type QualityChecksReleasesAreDiscoverableUpstreamStatus string + +// QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus defines model for QualityChecks.TwoFactorAuthenticationAtSourceRepository.Status. +type QualityChecksTwoFactorAuthenticationAtSourceRepositoryStatus string + +// QualityChecksTwoFactorAuthenticationForPackageManagerStatus defines model for QualityChecks.TwoFactorAuthenticationForPackageManager.Status. +type QualityChecksTwoFactorAuthenticationForPackageManagerStatus string + +// Release defines model for Release. +type Release struct { + Name string `json:"name"` + Platform string `json:"platform"` + Version string `json:"version"` +} + +// ReleaseDetail defines model for ReleaseDetail. +type ReleaseDetail struct { + License *struct { + Expression *string `json:"expression,omitempty"` + Source *ReleaseDetailLicenseSource `json:"source,omitempty"` + } `json:"license,omitempty"` + Name *string `json:"name,omitempty"` + NearestRecommendedRelease *struct { + PublishedAt *time.Time `json:"published_at"` + Version *string `json:"version,omitempty"` + } `json:"nearest_recommended_release"` + Platform *string `json:"platform,omitempty"` + PublishedAt *string `json:"published_at"` + Purl *string `json:"purl,omitempty"` + Repository *struct { + Source *ReleaseDetailRepositorySource `json:"source,omitempty"` + Url *string `json:"url"` + } `json:"repository,omitempty"` + TideliftRecommendation *TideliftReleaseRecommendation `json:"tidelift_recommendation"` + Version *string `json:"version,omitempty"` + Violations *[]ReleaseDetail_Violations_Item `json:"violations,omitempty"` +} + +// ReleaseDetailLicenseSource defines model for ReleaseDetail.License.Source. +type ReleaseDetailLicenseSource string + +// ReleaseDetailRepositorySource defines model for ReleaseDetail.Repository.Source. +type ReleaseDetailRepositorySource string + +// ReleaseDetail_Violations_Item defines model for ReleaseDetail.violations.Item. +type ReleaseDetail_Violations_Item struct { + CatalogStandard *string `json:"catalog_standard,omitempty"` + Title *string `json:"title,omitempty"` + ViolationId *string `json:"violation_id,omitempty"` + AdditionalProperties map[string]interface{} `json:"-"` +} + +// ReleaseRequirement defines model for ReleaseRequirement. +type ReleaseRequirement struct { + Name *string `json:"name,omitempty"` + Platform *string `json:"platform,omitempty"` + Purl *string `json:"purl,omitempty"` + Requirement *string `json:"requirement,omitempty"` + Scope *string `json:"scope,omitempty"` +} + +// StandardError The standard error format +type StandardError struct { + // Details An optional object with extra helpful details about the error. + Details *map[string]interface{} `json:"details,omitempty"` + + // Error An error code representing the error + Error string `json:"error"` + + // Message A human-readable error message representing the error + Message string `json:"message"` +} + +// TideliftPackageRecommendation defines model for TideliftPackageRecommendation. +type TideliftPackageRecommendation string + +// TideliftPackageRecommendationReasons defines model for TideliftPackageRecommendationReasons. +type TideliftPackageRecommendationReasons = []string + +// TideliftReleaseRecommendation defines model for TideliftReleaseRecommendation. +type TideliftReleaseRecommendation string + +// Vulnerability defines model for Vulnerability. +type Vulnerability struct { + AffectedPackages []struct { + AffectedVersions *[]string `json:"affected_versions,omitempty"` + Name string `json:"name"` + Package string `json:"package"` + Platform string `json:"platform"` + Purl string `json:"purl"` + Recommendation VulnerabilityAffectedPackagesRecommendation `json:"recommendation"` + RecommendationDetails *struct { + CreatedAt time.Time `json:"created_at"` + + // FalsePositiveReason Detailed explanation of the false positive result. + FalsePositiveReason *string `json:"false_positive_reason"` + Id string `json:"id"` + + // ImpactDescription Detailed explanation of the impact score. + ImpactDescription *string `json:"impact_description"` + + // ImpactScore When using this package as intended, how likely are users to be affected? A general score out of 10. + ImpactScore *int `json:"impact_score"` + + // IncludesDev Does this vulnerability apply when the package is only used within a Development or CI environment? + IncludesDev *bool `json:"includes_dev"` + + // OtherConditions Are there any other conditions that users should check for to determine if they're vulnerable? + OtherConditions *bool `json:"other_conditions"` + + // OtherConditionsDescription Detailed explanation of the other conditions result. + OtherConditionsDescription *string `json:"other_conditions_description"` + + // RealIssue Is this a real vulnerability or a false positive? + RealIssue bool `json:"real_issue"` + + // SpecificMethodsAffected Does this vulnerability apply only if certain methods, classes, or functionality are in use? + SpecificMethodsAffected *bool `json:"specific_methods_affected"` + + // SpecificMethodsDescription Detailed explanation of the specific methods result. + SpecificMethodsDescription *string `json:"specific_methods_description"` + UpdatedAt time.Time `json:"updated_at"` + + // WorkaroundAvailable For users who are unable to upgrade to a supported release, is there a workaround available? + WorkaroundAvailable *bool `json:"workaround_available"` + + // WorkaroundDescription Detailed explanation of the workaround. + WorkaroundDescription *string `json:"workaround_description"` + } `json:"recommendation_details"` + UnaffectedVersions *string `json:"unaffected_versions"` + } `json:"affected_packages"` + CvssScore *string `json:"cvss_score"` + Description *string `json:"description"` + NistUrl *string `json:"nist_url"` + Severity *string `json:"severity"` + UpdatedAt *string `json:"updated_at,omitempty"` + Url *string `json:"url"` + VulnId *string `json:"vuln_id"` +} + +// VulnerabilityAffectedPackagesRecommendation defines model for Vulnerability.AffectedPackages.Recommendation. +type VulnerabilityAffectedPackagesRecommendation string + +// PackageNameParam defines model for packageNameParam. +type PackageNameParam = string + +// PackagePlatformParam defines model for packagePlatformParam. +type PackagePlatformParam = string + +// PagePaginationParam defines model for pagePaginationParam. +type PagePaginationParam = int + +// PerPagePaginationParam defines model for perPagePaginationParam. +type PerPagePaginationParam = int + +// ReleaseVersionParam defines model for releaseVersionParam. +type ReleaseVersionParam = string + +// SbomTypeParam defines model for sbomTypeParam. +type SbomTypeParam string + +// VulnerabilityIdParam The id of the vulnerability. +type VulnerabilityIdParam = string + +// N401Error The standard error format +type N401Error = StandardError + +// N404Error The standard error format +type N404Error = StandardError + +// GetPackagesParams defines parameters for GetPackages. +type GetPackagesParams struct { + // Lifted Filter parameter. Filters packages to only those that are lifted or non-lifted. + Lifted *bool `form:"lifted,omitempty" json:"lifted,omitempty"` + + // SourceRepositoryUrl Filter parameter. Filters packages to only those associated with the url-encoded source repository url. + SourceRepositoryUrl *string `form:"source_repository_url,omitempty" json:"source_repository_url,omitempty"` + + // SearchableName Filter parameter. Filters packages to those whose name contains the given string. + SearchableName *string `form:"searchable_name,omitempty" json:"searchable_name,omitempty"` + + // Platform Filter parameter. Filters packages to those matching the given platform. + Platform *string `form:"platform,omitempty" json:"platform,omitempty"` + Page *PagePaginationParam `form:"page,omitempty" json:"page,omitempty"` + PerPage *PerPagePaginationParam `form:"per_page,omitempty" json:"per_page,omitempty"` +} + +// EolPackagesJSONBody defines parameters for EolPackages. +type EolPackagesJSONBody struct { + union json.RawMessage +} + +// EolPackagesJSONBody0 defines parameters for EolPackages. +type EolPackagesJSONBody0 struct { + // Packages The packages being looked up + Packages []Package `json:"packages"` +} + +// EolPackagesJSONBody1 defines parameters for EolPackages. +type EolPackagesJSONBody1 struct { + // Purls Array of properly formatted PURLs + Purls []string `json:"purls"` +} + +// LookupJSONBody defines parameters for Lookup. +type LookupJSONBody struct { + union json.RawMessage +} + +// LookupJSONBody0 defines parameters for Lookup. +type LookupJSONBody0 struct { + // Packages The packages being looked up + Packages []Package `json:"packages"` +} + +// LookupJSONBody1 defines parameters for Lookup. +type LookupJSONBody1 struct { + // Purls Array of properly formatted PURLs + Purls []string `json:"purls"` +} + +// UpdatesSinceParams defines parameters for UpdatesSince. +type UpdatesSinceParams struct { + Page *PagePaginationParam `form:"page,omitempty" json:"page,omitempty"` + PerPage *PerPagePaginationParam `form:"per_page,omitempty" json:"per_page,omitempty"` + + // Date The date to search for changes on or after + Date openapi_types.Date `form:"date" json:"date"` +} + +// ReleaseDependenciesParams defines parameters for ReleaseDependencies. +type ReleaseDependenciesParams struct { + Page *PagePaginationParam `form:"page,omitempty" json:"page,omitempty"` + PerPage *PerPagePaginationParam `form:"per_page,omitempty" json:"per_page,omitempty"` +} + +// ReleaseSbomParams defines parameters for ReleaseSbom. +type ReleaseSbomParams struct { + // SbomType The type the SBOM to generate. Required, unless older sbom_format parameter is passed. + SbomType *ReleaseSbomParamsSbomType `form:"sbom_type,omitempty" json:"sbom_type,omitempty"` +} + +// ReleaseSbomParamsSbomType defines parameters for ReleaseSbom. +type ReleaseSbomParamsSbomType string + +// LookupReleasesJSONBody defines parameters for LookupReleases. +type LookupReleasesJSONBody struct { + union json.RawMessage +} + +// LookupReleasesJSONBody0 defines parameters for LookupReleases. +type LookupReleasesJSONBody0 struct { + // Releases The releases being looked up. + Releases []Release `json:"releases"` +} + +// LookupReleasesJSONBody1 defines parameters for LookupReleases. +type LookupReleasesJSONBody1 struct { + // Purls Array of properly formatted PURLs + Purls []string `json:"purls"` +} + +// EolPackagesJSONRequestBody defines body for EolPackages for application/json ContentType. +type EolPackagesJSONRequestBody EolPackagesJSONBody + +// LookupJSONRequestBody defines body for Lookup for application/json ContentType. +type LookupJSONRequestBody LookupJSONBody + +// LookupReleasesJSONRequestBody defines body for LookupReleases for application/json ContentType. +type LookupReleasesJSONRequestBody LookupReleasesJSONBody + +// Getter for additional properties for ReleaseDetail_Violations_Item. Returns the specified +// element and whether it was found +func (a ReleaseDetail_Violations_Item) Get(fieldName string) (value interface{}, found bool) { + if a.AdditionalProperties != nil { + value, found = a.AdditionalProperties[fieldName] + } + return +} + +// Setter for additional properties for ReleaseDetail_Violations_Item +func (a *ReleaseDetail_Violations_Item) Set(fieldName string, value interface{}) { + if a.AdditionalProperties == nil { + a.AdditionalProperties = make(map[string]interface{}) + } + a.AdditionalProperties[fieldName] = value +} + +// Override default JSON handling for ReleaseDetail_Violations_Item to handle AdditionalProperties +func (a *ReleaseDetail_Violations_Item) UnmarshalJSON(b []byte) error { + object := make(map[string]json.RawMessage) + err := json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["catalog_standard"]; found { + err = json.Unmarshal(raw, &a.CatalogStandard) + if err != nil { + return fmt.Errorf("error reading 'catalog_standard': %w", err) + } + delete(object, "catalog_standard") + } + + if raw, found := object["title"]; found { + err = json.Unmarshal(raw, &a.Title) + if err != nil { + return fmt.Errorf("error reading 'title': %w", err) + } + delete(object, "title") + } + + if raw, found := object["violation_id"]; found { + err = json.Unmarshal(raw, &a.ViolationId) + if err != nil { + return fmt.Errorf("error reading 'violation_id': %w", err) + } + delete(object, "violation_id") + } + + if len(object) != 0 { + a.AdditionalProperties = make(map[string]interface{}) + for fieldName, fieldBuf := range object { + var fieldVal interface{} + err := json.Unmarshal(fieldBuf, &fieldVal) + if err != nil { + return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err) + } + a.AdditionalProperties[fieldName] = fieldVal + } + } + return nil +} + +// Override default JSON handling for ReleaseDetail_Violations_Item to handle AdditionalProperties +func (a ReleaseDetail_Violations_Item) MarshalJSON() ([]byte, error) { + var err error + object := make(map[string]json.RawMessage) + + if a.CatalogStandard != nil { + object["catalog_standard"], err = json.Marshal(a.CatalogStandard) + if err != nil { + return nil, fmt.Errorf("error marshaling 'catalog_standard': %w", err) + } + } + + if a.Title != nil { + object["title"], err = json.Marshal(a.Title) + if err != nil { + return nil, fmt.Errorf("error marshaling 'title': %w", err) + } + } + + if a.ViolationId != nil { + object["violation_id"], err = json.Marshal(a.ViolationId) + if err != nil { + return nil, fmt.Errorf("error marshaling 'violation_id': %w", err) + } + } + + for fieldName, field := range a.AdditionalProperties { + object[fieldName], err = json.Marshal(field) + if err != nil { + return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err) + } + } + return json.Marshal(object) +} + +// AsPackageAppearsMaintainedAdditionalDataMaintenanceAppearance returns the union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData as a PackageAppearsMaintainedAdditionalDataMaintenanceAppearance +func (t PackageAppearsMaintainedQualityCheck_AdditionalData) AsPackageAppearsMaintainedAdditionalDataMaintenanceAppearance() (PackageAppearsMaintainedAdditionalDataMaintenanceAppearance, error) { + var body PackageAppearsMaintainedAdditionalDataMaintenanceAppearance + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromPackageAppearsMaintainedAdditionalDataMaintenanceAppearance overwrites any union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData as the provided PackageAppearsMaintainedAdditionalDataMaintenanceAppearance +func (t *PackageAppearsMaintainedQualityCheck_AdditionalData) FromPackageAppearsMaintainedAdditionalDataMaintenanceAppearance(v PackageAppearsMaintainedAdditionalDataMaintenanceAppearance) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergePackageAppearsMaintainedAdditionalDataMaintenanceAppearance performs a merge with any union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData, using the provided PackageAppearsMaintainedAdditionalDataMaintenanceAppearance +func (t *PackageAppearsMaintainedQualityCheck_AdditionalData) MergePackageAppearsMaintainedAdditionalDataMaintenanceAppearance(v PackageAppearsMaintainedAdditionalDataMaintenanceAppearance) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsPackageAppearsMaintainedAdditionalDataMaintenanceAssessment returns the union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData as a PackageAppearsMaintainedAdditionalDataMaintenanceAssessment +func (t PackageAppearsMaintainedQualityCheck_AdditionalData) AsPackageAppearsMaintainedAdditionalDataMaintenanceAssessment() (PackageAppearsMaintainedAdditionalDataMaintenanceAssessment, error) { + var body PackageAppearsMaintainedAdditionalDataMaintenanceAssessment + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromPackageAppearsMaintainedAdditionalDataMaintenanceAssessment overwrites any union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData as the provided PackageAppearsMaintainedAdditionalDataMaintenanceAssessment +func (t *PackageAppearsMaintainedQualityCheck_AdditionalData) FromPackageAppearsMaintainedAdditionalDataMaintenanceAssessment(v PackageAppearsMaintainedAdditionalDataMaintenanceAssessment) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergePackageAppearsMaintainedAdditionalDataMaintenanceAssessment performs a merge with any union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData, using the provided PackageAppearsMaintainedAdditionalDataMaintenanceAssessment +func (t *PackageAppearsMaintainedQualityCheck_AdditionalData) MergePackageAppearsMaintainedAdditionalDataMaintenanceAssessment(v PackageAppearsMaintainedAdditionalDataMaintenanceAssessment) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsPackageAppearsMaintainedAdditionalDataIsLifted returns the union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData as a PackageAppearsMaintainedAdditionalDataIsLifted +func (t PackageAppearsMaintainedQualityCheck_AdditionalData) AsPackageAppearsMaintainedAdditionalDataIsLifted() (PackageAppearsMaintainedAdditionalDataIsLifted, error) { + var body PackageAppearsMaintainedAdditionalDataIsLifted + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromPackageAppearsMaintainedAdditionalDataIsLifted overwrites any union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData as the provided PackageAppearsMaintainedAdditionalDataIsLifted +func (t *PackageAppearsMaintainedQualityCheck_AdditionalData) FromPackageAppearsMaintainedAdditionalDataIsLifted(v PackageAppearsMaintainedAdditionalDataIsLifted) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergePackageAppearsMaintainedAdditionalDataIsLifted performs a merge with any union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData, using the provided PackageAppearsMaintainedAdditionalDataIsLifted +func (t *PackageAppearsMaintainedQualityCheck_AdditionalData) MergePackageAppearsMaintainedAdditionalDataIsLifted(v PackageAppearsMaintainedAdditionalDataIsLifted) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsPackageAppearsMaintainedQualityCheckAdditionalData3 returns the union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData as a PackageAppearsMaintainedQualityCheckAdditionalData3 +func (t PackageAppearsMaintainedQualityCheck_AdditionalData) AsPackageAppearsMaintainedQualityCheckAdditionalData3() (PackageAppearsMaintainedQualityCheckAdditionalData3, error) { + var body PackageAppearsMaintainedQualityCheckAdditionalData3 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromPackageAppearsMaintainedQualityCheckAdditionalData3 overwrites any union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData as the provided PackageAppearsMaintainedQualityCheckAdditionalData3 +func (t *PackageAppearsMaintainedQualityCheck_AdditionalData) FromPackageAppearsMaintainedQualityCheckAdditionalData3(v PackageAppearsMaintainedQualityCheckAdditionalData3) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergePackageAppearsMaintainedQualityCheckAdditionalData3 performs a merge with any union data inside the PackageAppearsMaintainedQualityCheck_AdditionalData, using the provided PackageAppearsMaintainedQualityCheckAdditionalData3 +func (t *PackageAppearsMaintainedQualityCheck_AdditionalData) MergePackageAppearsMaintainedQualityCheckAdditionalData3(v PackageAppearsMaintainedQualityCheckAdditionalData3) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +func (t PackageAppearsMaintainedQualityCheck_AdditionalData) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *PackageAppearsMaintainedQualityCheck_AdditionalData) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsPackageAttestationStatementIncomeStreams0 returns the union data inside the PackageAttestationStatement_IncomeStreams as a PackageAttestationStatementIncomeStreams0 +func (t PackageAttestationStatement_IncomeStreams) AsPackageAttestationStatementIncomeStreams0() (PackageAttestationStatementIncomeStreams0, error) { + var body PackageAttestationStatementIncomeStreams0 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromPackageAttestationStatementIncomeStreams0 overwrites any union data inside the PackageAttestationStatement_IncomeStreams as the provided PackageAttestationStatementIncomeStreams0 +func (t *PackageAttestationStatement_IncomeStreams) FromPackageAttestationStatementIncomeStreams0(v PackageAttestationStatementIncomeStreams0) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergePackageAttestationStatementIncomeStreams0 performs a merge with any union data inside the PackageAttestationStatement_IncomeStreams, using the provided PackageAttestationStatementIncomeStreams0 +func (t *PackageAttestationStatement_IncomeStreams) MergePackageAttestationStatementIncomeStreams0(v PackageAttestationStatementIncomeStreams0) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsPackageAttestationStatementIncomeStreams1 returns the union data inside the PackageAttestationStatement_IncomeStreams as a PackageAttestationStatementIncomeStreams1 +func (t PackageAttestationStatement_IncomeStreams) AsPackageAttestationStatementIncomeStreams1() (PackageAttestationStatementIncomeStreams1, error) { + var body PackageAttestationStatementIncomeStreams1 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromPackageAttestationStatementIncomeStreams1 overwrites any union data inside the PackageAttestationStatement_IncomeStreams as the provided PackageAttestationStatementIncomeStreams1 +func (t *PackageAttestationStatement_IncomeStreams) FromPackageAttestationStatementIncomeStreams1(v PackageAttestationStatementIncomeStreams1) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergePackageAttestationStatementIncomeStreams1 performs a merge with any union data inside the PackageAttestationStatement_IncomeStreams, using the provided PackageAttestationStatementIncomeStreams1 +func (t *PackageAttestationStatement_IncomeStreams) MergePackageAttestationStatementIncomeStreams1(v PackageAttestationStatementIncomeStreams1) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +func (t PackageAttestationStatement_IncomeStreams) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *PackageAttestationStatement_IncomeStreams) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} diff --git a/internal/utils/purls.go b/internal/utils/purls.go new file mode 100644 index 0000000..44964d8 --- /dev/null +++ b/internal/utils/purls.go @@ -0,0 +1,47 @@ +package utils + +import ( + "strings" + + "github.com/package-url/packageurl-go" + "golang.org/x/exp/maps" +) + +var purlTypeToPlatform = map[string]string{ + "cargo": "cargo", + "composer": "packagist", + "gem": "rubygems", + "golang": "go", + "maven": "maven", + "npm": "npm", + "nuget": "nuget", + "pypi": "pypi", +} + +var SupportedPurlTypes = maps.Keys(purlTypeToPlatform) + +type PackageRelease struct { + Platform string `json:"platform"` + Name string `json:"name"` + Version string `json:"version"` + Purl string +} + +func ReleasePurlToPackagePurl(purl packageurl.PackageURL) packageurl.PackageURL { + return *packageurl.NewPackageURL(purl.Type, purl.Namespace, purl.Name, "", nil, "") +} + +func TideliftPlatformFromPurl(purl packageurl.PackageURL) string { + return purlTypeToPlatform[purl.Type] +} + +func TideliftPackageNameFromPurl(purl packageurl.PackageURL) string { + if purl.Namespace == "" { + return purl.Name + } + if purl.Type == "maven" { + return strings.Join([]string{purl.Namespace, purl.Name}, ":") + } else { + return strings.Join([]string{purl.Namespace, purl.Name}, "/") + } +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go new file mode 100644 index 0000000..ca4fbd6 --- /dev/null +++ b/internal/utils/utils.go @@ -0,0 +1,93 @@ +package utils + +import ( + "context" + "encoding/json" + "fmt" + "math" + "os" + + requests "github.com/carlmjohnson/requests" + "github.com/package-url/packageurl-go" + log "github.com/sirupsen/logrus" + api "github.com/tidelift/tidelift-sbom-info/internal/tidelift-api-gen" +) + +const CHUNK_SIZE = 500 + +type packageLookupResponse struct { + Packages []api.PackageDetail `json:"packages"` + MissingResults []api.Package `json:"missing_results"` +} + +type purlLookupRequest struct { + Purls []string `json:"purls"` +} + +type releaseLookupResponse struct { + Releases []api.ReleaseDetail `json:"releases"` + MissingResults []PackageRelease `json:"missing_results"` +} + +func GetPackageInfo(purls []packageurl.PackageURL) ([]api.PackageDetail, []api.Package) { + var packageInfo []api.PackageDetail + var missingPackages []api.Package + + for start := 0; start < len(purls); start += CHUNK_SIZE { + purlStrings := chunkOfPurlStrings(purls, start, CHUNK_SIZE) + + var packageRsp packageLookupResponse + if err := requests.URL("https://api.tidelift.com/external-api/v1/packages/lookup"). + BodyJSON(purlLookupRequest{purlStrings}). + ToJSON(&packageRsp). + ContentType("application/json"). + // TODO: could use the full auth setup that we do in the tidelift cli + Header("Authorization", "Bearer "+os.Getenv("TIDELIFT_API_KEY")). + Fetch(context.Background()); err != nil { + log.Warn(fmt.Sprintf("problem fetching package info %s ", err)) + } + packageInfo = append(packageInfo, packageRsp.Packages...) + missingPackages = append(missingPackages, packageRsp.MissingResults...) + } + return packageInfo, missingPackages +} + +func GetReleaseInfo(purls []packageurl.PackageURL) ([]api.ReleaseDetail, []PackageRelease) { + var releaseInfo []api.ReleaseDetail + var missingReleases []PackageRelease + for start := 0; start < len(purls); start += CHUNK_SIZE { + purlStrings := chunkOfPurlStrings(purls, start, CHUNK_SIZE) + + var releaseRsp releaseLookupResponse + if err := requests.URL("https://api.tidelift.com/external-api/v1/releases/lookup"). + BodyJSON(purlLookupRequest{purlStrings}). + ToJSON(&releaseRsp). + ContentType("application/json"). + // TODO: could use the full auth setup that we do in the tidelift cli + Header("Authorization", "Bearer "+os.Getenv("TIDELIFT_API_KEY")). + Fetch(context.Background()); err != nil { + log.Warn(fmt.Sprintf("problem fetching package info %s ", err)) + } + + releaseInfo = append(releaseInfo, releaseRsp.Releases...) + missingReleases = append(missingReleases, releaseRsp.MissingResults...) + } + return releaseInfo, missingReleases +} + +func DebugJsonPrint(toPrint any) { + jsonStr, err := json.MarshalIndent(toPrint, "", " ") + if err != nil { + panic(err) + } + log.Debug(string(jsonStr)) +} + +func chunkOfPurlStrings(purls []packageurl.PackageURL, start int, chunk_size int) []string { + end := int(math.Min(float64(start+chunk_size), float64(len(purls)))) + var purlStrings []string + for _, purl := range purls[start:end:end] { + purlStrings = append(purlStrings, purl.ToString()) + } + return purlStrings +}