diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..acdc928 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,49 @@ +name: build + +on: + push: + branches: + - main + pull_request: + branches: + - main + release: + types: + - published + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Install go + uses: actions/setup-go@v3 + with: + go-version: 1.19.x + - name: Check out source code + uses: actions/checkout@v3 + - name: Cache go dependencies + id: cache-go-dependencies + uses: actions/cache@v3 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - name: Install go dependencies + if: steps.cache-go-dependencies.outputs.cache-hit != 'true' + run: go mod download + - name: Build + run: + make + - if: github.event_name == 'release' && github.event.action == 'published' + name: Publish artifacts on releases + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: bin/osv + tag: ${{ github.ref }} + overwrite: true + file_glob: true diff --git a/.github/workflows/cloc.yml b/.github/workflows/cloc.yml new file mode 100644 index 0000000..ba0bc76 --- /dev/null +++ b/.github/workflows/cloc.yml @@ -0,0 +1,37 @@ +name: "Lines of code statistics" +on: + push: + branches: + - main + pull_request: + branches: + - main + release: + types: + - published + +permissions: read-all + +jobs: + loc: + name: Lines of code + runs-on: ubuntu-latest + steps: + - name: Install go + uses: actions/setup-go@v3 + with: + go-version: 1.19.x + - name: Check out source code + uses: actions/checkout@v3 + - name: Install dependencies + run: | + cd $GITHUB_WORKSPACE + go install github.com/hhatto/gocloc/cmd/gocloc@latest + - name: All sources + run: | + cd $GITHUB_WORKSPACE + gocloc . + - name: All sources (except tests) + run: | + cd $GITHUB_WORKSPACE + gocloc --not-match='.*_test.go' . diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..541ac3c --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,106 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '17 11 * * 0' + +permissions: read-all + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + env: + CGO_ENABLED: 0 + GOFLAGS: "-tags=sync,search,scrub,metrics,containers_image_openpgp" + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install go + uses: actions/setup-go@v3 + with: + go-version: 1.19.x + + - name: Install dependencies + run: | + cd $GITHUB_WORKSPACE + go install github.com/swaggo/swag/cmd/swag@latest + go mod download + go install github.com/wadey/gocovmerge@latest + go get -u github.com/swaggo/swag/cmd/swag + go mod download + sudo apt-get update + sudo apt-get -y install rpm uidmap + # install skopeo + . /etc/os-release + echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list + curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key | sudo apt-key add - + sudo apt-get update + sudo apt-get -y upgrade + sudo apt-get -y install skopeo + # install notation + curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v0.7.1-alpha.1/notation_0.7.1-alpha.1_linux_amd64.tar.gz + sudo tar xvzf notation.tar.gz -C /usr/bin notation + # install oras + curl -LO https://github.com/oras-project/oras/releases/download/v0.14.0/oras_0.14.0_linux_amd64.tar.gz + mkdir -p oras-install/ + tar -zxf oras_0.14.0_*.tar.gz -C oras-install/ + sudo mv oras-install/oras /usr/bin/ + rm -rf oras_0.14.0_*.tar.gz oras-install/ + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/commit-msg.yaml b/.github/workflows/commit-msg.yaml new file mode 100644 index 0000000..2dda471 --- /dev/null +++ b/.github/workflows/commit-msg.yaml @@ -0,0 +1,38 @@ +name: 'Check commit message style' +on: + pull_request: + types: + - opened + - edited + - reopened + - synchronize + push: + branches: + - main + +jobs: + check-commit-message-style: + name: Check commit message style + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Check Commit Type + uses: gsactions/commit-message-checker@v2 + with: + pattern: '^((build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(.+\))?(!)?(: (.*\s*)*))' + flags: 'gm' + error: 'Your first line has to the Conventional Commits specification.' + excludeDescription: 'true' # optional: this excludes the description body of a pull request + excludeTitle: 'true' # optional: this excludes the title of a pull request + checkAllCommitMessages: 'true' + accessToken: ${{ secrets.GITHUB_TOKEN }} + - name: Check Line Length + uses: gsactions/commit-message-checker@v2 + with: + pattern: '^[^#].{1,74}' + error: 'The maximum line length of 74 characters is exceeded.' + excludeDescription: 'true' # optional: this excludes the description body of a pull request + excludeTitle: 'true' # optional: this excludes the title of a pull request + checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request + accessToken: ${{ secrets.GITHUB_TOKEN }} # github access token is only required if checkAllCommitMessages is true diff --git a/.github/workflows/dco.yml b/.github/workflows/dco.yml new file mode 100644 index 0000000..5123a88 --- /dev/null +++ b/.github/workflows/dco.yml @@ -0,0 +1,24 @@ +# .github/workflows/dco.yml +name: DCO +on: + pull_request: + branches: + - main + +permissions: read-all + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.x + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Check DCO + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + pip3 install -U dco-check + dco-check diff --git a/.github/workflows/golangci-lint.yaml b/.github/workflows/golangci-lint.yaml new file mode 100644 index 0000000..a0af8f7 --- /dev/null +++ b/.github/workflows/golangci-lint.yaml @@ -0,0 +1,46 @@ +name: golangci-lint +on: + push: + tags: + - v* + branches: + - master + - main + pull_request: +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + # pull-requests: read +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v3 + with: + go-version: '1.19' + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: v1.49.0 + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + args: --config ./golangcilint.yaml --enable-all --build-tags debug,needprivileges,sync,scrub,search,metrics,containers_image_openpgp,lint ./cmd/... ./pkg/... + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the action will use pre-installed Go. + skip-go-installation: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true diff --git a/.github/workflows/license.yaml b/.github/workflows/license.yaml new file mode 100644 index 0000000..b9ec649 --- /dev/null +++ b/.github/workflows/license.yaml @@ -0,0 +1,29 @@ +name: "Software License Check" +on: + push: + paths: + - 'go.mod' + branches: + - main + pull_request: + paths: + - 'go.mod' + # The branches below must be a subset of the branches above + branches: [main] + +permissions: read-all + +jobs: + license-check: + runs-on: ubuntu-latest + name: License Check + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: 1.19.x + - name: Install go-licenses + run: go install github.com/google/go-licenses@latest + - name: Check for forbidden licenses + run: + echo "disabled for now" diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml new file mode 100644 index 0000000..db56621 --- /dev/null +++ b/.github/workflows/scorecards.yml @@ -0,0 +1,62 @@ +name: Scorecards supply-chain security +on: + # Only the default branch is supported. + branch_protection_rule: + schedule: + - cron: '40 23 * * 1' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecards analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Used to receive a badge. (Upcoming feature) + id-token: write + # Needs for private repositories. + contents: read + actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@v3 # v3.0.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # v1.1.1 + with: + results_file: results.sarif + results_format: sarif + # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecards on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} + + # Publish the results for public repositories to enable scorecard badges. For more details, see + # https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories, `publish_results` will automatically be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@v3 # v3.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26 + with: + sarif_file: results.sarif diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml new file mode 100644 index 0000000..b080652 --- /dev/null +++ b/.github/workflows/stale.yaml @@ -0,0 +1,29 @@ +name: 'Close stale issues and PRs' +on: + schedule: + - cron: '30 1 * * *' + +permissions: + contents: read + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v6 + with: + stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' + stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.' + close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.' + close-pr-message: 'This PR was closed because it has been stalled for 10 days with no activity.' + days-before-issue-stale: 30 + days-before-pr-stale: 45 + days-before-issue-close: 5 + days-before-pr-close: 10 + stale-issue-label: 'no-issue-activity' + exempt-issue-labels: 'awaiting-approval,work-in-progress' + stale-pr-label: 'no-pr-activity' + exempt-pr-labels: 'awaiting-approval,work-in-progress' + only-labels: 'awaiting-feedback,awaiting-answers' diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7d0a446 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +TOOLS_DIR := hack/tools +TOOLS_BIN_DIR := $(TOOLS_DIR)/bin +BIN_DIR := bin + +GOLANGCI_LINT := $(TOOLS_BIN_DIR)/golangci-lint + +.PHONY: all +all: build check binary + +.PHONY: binary +binary: + mkdir -p $(BIN_DIR) + go build -v -gcflags all='-N -l' -o $(BIN_DIR)/osv ./cmd/osv/... + +.PHONY: build +build: + go build -v ./... + +$(GOLANGCI_LINT): + mkdir -p $(TOOLS_BIN_DIR) + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TOOLS_BIN_DIR) v1.50.1 + +.PHONY: check +check: $(GOLANGCI_LINT) + $(GOLANGCI_LINT) run -c golangcilint.yaml ./... + +.PHONY: clean +clean: + rm -rf $(BIN_DIR) + rm -rf $(TOOLS_DIR) diff --git a/README.md b/README.md index 2540093..66677ff 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,22 @@ # go-osv Golang library to interact with OSV db + +# Build + +``` +make +``` + +# Usage + +## Lookup by package + +``` +osv -p jinja2 -r 2.4.1 +``` + +## Lookup by commit hash + +``` +osv -c 6879efc2c1596d11a6a6ad296f80063b558d5e0f +``` diff --git a/cmd/osv/main.go b/cmd/osv/main.go new file mode 100644 index 0000000..a47ba1d --- /dev/null +++ b/cmd/osv/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/spf13/cobra" + "zotregistry.io/go-osv/pkg/osv" +) + +const cmdName = "osv" + +// NewRootCmd returns a new cli root cmd. +func NewRootCmd() *cobra.Command { + showVersion := false + pkg := "" + pkgVersion := "" + ecosystem := "" + commit := "" + + rootCmd := &cobra.Command{ + Use: cmdName, + Short: cmdName, + Long: cmdName, + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("args: %v\n", args) + // cannot have version without the pkg + if pkg == "" && pkgVersion != "" { + _ = cmd.Usage() + os.Exit(1) + } + + // cannot have both + if pkg != "" && commit != "" { + _ = cmd.Usage() + os.Exit(1) + } + + if pkg != "" { + if _, err := osv.LookupPackage(context.TODO(), pkg, pkgVersion); err != nil { + os.Exit(1) + } + } else if commit != "" { + if _, err := osv.LookupCommitHash(context.TODO(), commit); err != nil { + os.Exit(1) + } + } + }, + } + + // lookup pkg + rootCmd.Flags().StringVarP(&pkg, "pkg", "p", "", "Lookup specified package") + rootCmd.Flags().StringVarP(&pkgVersion, "pkgver", "r", "", "Lookup specified package version,"+ + " package name must be specified") + rootCmd.Flags().StringVarP(&ecosystem, "ecosystem", "e", "", "Lookup specified package/version in this ecosystem:"+ + "[Alpine,Android,crates.io,Debian,Go,GSD,Linux,Maven,npm,NuGet,OSS-Fuzz,Packagist,PyPI,RubyGems]") + // lookup commit hash + rootCmd.Flags().StringVarP(&commit, "commit", "c", "", "Lookup specified commit") + + // "version" + rootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "Show the version and exit") + + return rootCmd +} + +func main() { + if err := NewRootCmd().Execute(); err != nil { + os.Exit(1) + } +} diff --git a/errors/errors.go b/errors/errors.go new file mode 100644 index 0000000..9289c5c --- /dev/null +++ b/errors/errors.go @@ -0,0 +1,5 @@ +package errors + +import "errors" + +var ErrBadParam = errors.New("invalid parameter") diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d425541 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module zotregistry.io/go-osv + +go 1.19 + +require ( + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/spf13/cobra v1.6.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/vuln v0.0.0-20221025230227-995372c58a16 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7bf87f9 --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/vuln v0.0.0-20221025230227-995372c58a16 h1:/H6ddBUaKrFDOBFz0Y3l1/Ppbx19f/rK11jABxiqKFw= +golang.org/x/vuln v0.0.0-20221025230227-995372c58a16/go.mod h1:F12iebNzxRMpJsm4W7ape+r/KdnXiSy3VC94WsyCG68= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/golangcilint.yaml b/golangcilint.yaml new file mode 100644 index 0000000..f140f62 --- /dev/null +++ b/golangcilint.yaml @@ -0,0 +1,77 @@ +run: + deadline: 60m + skip-dirs: + - "internal" + +linters: + enable-all: true + disable: funlen,gocognit,exhaustivestruct,paralleltest,forbidigo,ireturn,wrapcheck,exhaustive,maintidx,exhaustruct,nosnakecase,interfacer,structcheck,varcheck,deadcode,ifshort,golint,scopelint,maligned,rowserrcheck,sqlclosecheck + +linters-settings: + dupl: + threshold: 200 + nestif: + min-complexity: 26 + cyclop: + max-complexity: 40 + skip-tests: true + varnamelen: + check-return: true + ignore-type-assert-ok: true + ignore-map-index-ok: true + ignore-chan-recv-ok: true + ignore-names: + - err + - ok + - gc + - wg + ignore-decls: + - n int + - i int + - r *os.File + - w *os.File + - to int64 + - l *ldap.Conn + gci: + sections: + - standard + - default + - prefix(zotregistry.io/zot) + wsl: + allow-assign-and-anything: true + enforce-err-cuddling: true + nolintlint: + allow-unused: true + gomnd: + settings: + mnd: + checks: argument,case,condition,operation,return,assign + ignored-numbers: 10,64 + gomoddirectives: + replace-allow-list: + - github.com/aquasecurity/fanal + - github.com/aquasecurity/trivy + - github.com/aquasecurity/trivy-db + - github.com/containers/image/v5 + - github.com/opencontainers/image-spec + - github.com/open-policy-agent/opa + - github.com/vektah/gqlparser/v2 + - go.opentelemetry.io/otel + - go.opentelemetry.io/otel/exporters/otlp + - go.opentelemetry.io/otel/metric + - go.opentelemetry.io/otel/sdk + - github.com/hashicorp/go-getter + - github.com/opencontainers/runc + - github.com/theupdateframework/go-tuf + - go.etcd.io/etcd/v3 + - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc + - go.opentelemetry.io/otel/exporters/otlp/otlptrace + - go.opentelemetry.io/otel/trace + +issues: + exclude-rules: + - path: pkg/extensions/search/schema.resolvers.go + linters: + - lll + - varnamelen + - gci diff --git a/pkg/osv/constants.go b/pkg/osv/constants.go new file mode 100644 index 0000000..9f4456d --- /dev/null +++ b/pkg/osv/constants.go @@ -0,0 +1,34 @@ +package osv + +type Ecosystem string + +const ( + Alpine Ecosystem = "Alpine" + Android Ecosystem = "Android" +) + +/* +"Alpine" +"Android" +"crates.io" +"Debian" +"DWF" +"GitHub Actions" +"Go" +"GSD" +"Hex" +"Linux" +"Maven" +"npm" +"NuGet" +"OSS-Fuzz" +"Packagist" +"Pub" +"PyPI" +"RubyGems" +"UVI" +*/ + +func (e *Ecosystem) Lookup() string { + return "" +} diff --git a/pkg/osv/osv.go b/pkg/osv/osv.go new file mode 100644 index 0000000..9ac2d3a --- /dev/null +++ b/pkg/osv/osv.go @@ -0,0 +1,121 @@ +package osv + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + + "golang.org/x/vuln/osv" + "zotregistry.io/go-osv/errors" +) + +/* +curl -X POST -d \ + '{"version": "2.4.1", + "package": {"name": "jinja2", "ecosystem": "PyPI"}}' \ + "https://api.osv.dev/v1/query" +*/ + +const ( + QueryHost = "api.osv.dev" + V1QueryURL = "/v1/query" +) + +type V1Query struct { + Commit string `json:"commit,omitempty"` + Version string `json:"version,omitempty"` + Package osv.Package `json:"package,omitempty"` +} + +type V1Response struct { + Vulns []osv.Entry `json:"vulns,omitempty"` +} + +func lookup(ctx context.Context, osvData *V1Query) (*V1Response, error) { + body, err := json.Marshal(osvData) + if err != nil { + return nil, err + } + + requestURL := "https://" + QueryHost + V1QueryURL + + request, err := http.NewRequestWithContext(ctx, http.MethodPost, requestURL, bytes.NewBuffer(body)) + if err != nil { + return nil, err + } + + client := &http.Client{} + + response, err := client.Do(request) + if err != nil { + return nil, err + } + + if response.StatusCode != http.StatusOK { + return nil, errors.ErrBadParam + } + + defer response.Body.Close() + + data, err := io.ReadAll(response.Body) + if err != nil { + return nil, err + } + + fmt.Printf("%s\v", string(data)) + + var resp V1Response + if err := json.Unmarshal(data, &resp); err != nil { + return nil, err + } + + return &resp, nil +} + +func LookupPackage(ctx context.Context, name, version string, ecosystems ...string) ([]osv.Entry, error) { + // input validation + if name == "" || version == "" { + return nil, errors.ErrBadParam + } + + if len(ecosystems) > 1 { + return nil, errors.ErrBadParam + } + + var ecosystem osv.Ecosystem + if len(ecosystems) == 1 { + ecosystem = osv.Ecosystem(ecosystems[0]) + } + + osvData := &V1Query{Package: osv.Package{Name: name, Ecosystem: ecosystem}, Version: version} + + resp, err := lookup(ctx, osvData) + if err != nil { + return nil, err + } + + // fmt.Printf("%v\n", resp) + + return resp.Vulns, nil +} + +func LookupCommitHash(ctx context.Context, commit string) ([]osv.Entry, error) { + // input validation + if commit == "" { + return nil, errors.ErrBadParam + } + + osvData := &V1Query{Commit: commit} + + resp, err := lookup(ctx, osvData) + if err != nil { + return nil, err + } + + // fmt.Printf("%v\n", resp) + + return resp.Vulns, nil +}