mirror of
https://github.com/ossf/scorecard.git
synced 2024-09-19 13:07:17 +03:00
✨ Add a Vulnerabilities check. (#628)
Uses OSV to check this. Fixes #52.
This commit is contained in:
parent
18b53076d6
commit
34621504fb
@ -59,6 +59,7 @@ SAST | Does the project use static code analysis tools, e.g. [Code
|
||||
Active | Did the project get any commits in the last 90 days?
|
||||
Branch-Protection | Does the project use [Branch Protection](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/about-protected-branches) ?
|
||||
Packaging | Does the project build and publish official packages from CI/CD, e.g. [GitHub Publishing](https://docs.github.com/en/free-pro-team@latest/actions/guides/about-packaging-with-github-actions#workflows-for-publishing-packages) ?
|
||||
Vulnerabilities | Does the project have unfixed vulnerabilities? Uses the [OSV service](https://osv.dev).
|
||||
|
||||
To see detailed information about each check and remediation steps, check out
|
||||
the [checks documentation page](checks/checks.md).
|
||||
|
@ -140,3 +140,10 @@ This check tries to determine if a project's GitHub workflows follow the princip
|
||||
**Remediation steps**
|
||||
- Set permissions as `read-all` or `contents: read` as described in GitHub's [documentation](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#permissions).
|
||||
|
||||
## Vulnerabilities
|
||||
|
||||
This check determines whether if there are open, unfixed vulnerabilities in the project using the [OSV](https://osv.dev) service.
|
||||
|
||||
**Remediation steps**
|
||||
- Fix the vulnerabilities. The details of each vulnerability can be found on <https://osv.dev>.
|
||||
|
||||
|
@ -251,3 +251,11 @@ checks:
|
||||
- >-
|
||||
For GitHub, check out the steps
|
||||
[here](https://docs.github.com/en/github/administering-a-repository/managing-a-branch-protection-rule).
|
||||
Vulnerabilities:
|
||||
description: >-
|
||||
This check determines whether if there are open, unfixed vulnerabilities
|
||||
in the project using the [OSV](https://osv.dev) service.
|
||||
remediation:
|
||||
- >-
|
||||
Fix the vulnerabilities. The details of each vulnerability can be found
|
||||
on <https://osv.dev>.
|
||||
|
108
checks/vulnerabilities.go
Normal file
108
checks/vulnerabilities.go
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2021 Security Scorecard Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package checks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v32/github"
|
||||
|
||||
"github.com/ossf/scorecard/checker"
|
||||
)
|
||||
|
||||
const (
|
||||
// CheckVulnerabilities is the registered name for the OSV check.
|
||||
CheckVulnerabilities = "Vulnerabilities"
|
||||
osvQueryEndpoint = "https://api.osv.dev/v1/query"
|
||||
)
|
||||
|
||||
// ErrNoCommits is the error for when there are no commits found.
|
||||
var ErrNoCommits = errors.New("no commits found")
|
||||
|
||||
type osvQuery struct {
|
||||
Commit string `json:"commit"`
|
||||
}
|
||||
|
||||
type osvResponse struct {
|
||||
Vulns []struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"vulns"`
|
||||
}
|
||||
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
registerCheck(CheckVulnerabilities, HasUnfixedVulnerabilities)
|
||||
}
|
||||
|
||||
func (resp *osvResponse) getVulnerabilities() []string {
|
||||
ids := make([]string, 0, len(resp.Vulns))
|
||||
for _, vuln := range resp.Vulns {
|
||||
ids = append(ids, vuln.ID)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func HasUnfixedVulnerabilities(c *checker.CheckRequest) checker.CheckResult {
|
||||
commits, _, err := c.Client.Repositories.ListCommits(c.Ctx, c.Owner, c.Repo, &github.CommitsListOptions{
|
||||
ListOptions: github.ListOptions{
|
||||
PerPage: 1,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return checker.MakeRetryResult(CheckVulnerabilities, err)
|
||||
}
|
||||
|
||||
if len(commits) != 1 || commits[0].SHA == nil {
|
||||
return checker.MakeInconclusiveResult(CheckVulnerabilities, ErrNoCommits)
|
||||
}
|
||||
|
||||
query, err := json.Marshal(&osvQuery{
|
||||
Commit: *commits[0].SHA,
|
||||
})
|
||||
if err != nil {
|
||||
panic("!! failed to marshal OSV query.")
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(c.Ctx, http.MethodPost, osvQueryEndpoint, bytes.NewReader(query))
|
||||
if err != nil {
|
||||
return checker.MakeRetryResult(CheckVulnerabilities, err)
|
||||
}
|
||||
|
||||
// Use our own http client as the one from CheckRequest adds GitHub tokens to the headers.
|
||||
httpClient := &http.Client{}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return checker.MakeRetryResult(CheckVulnerabilities, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var osvResp osvResponse
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
if err := decoder.Decode(&osvResp); err != nil {
|
||||
return checker.MakeRetryResult(CheckVulnerabilities, err)
|
||||
}
|
||||
|
||||
vulnIDs := osvResp.getVulnerabilities()
|
||||
if len(vulnIDs) > 0 {
|
||||
c.Logf("HEAD is vulnerable to %s", strings.Join(vulnIDs, ", "))
|
||||
return checker.MakeFailResult(CheckVulnerabilities, nil)
|
||||
}
|
||||
|
||||
return checker.MakePassResult(CheckVulnerabilities)
|
||||
}
|
64
e2e/vulnerabilities_test.go
Normal file
64
e2e/vulnerabilities_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2021 Security Scorecard Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//nolint:dupl // repeating test cases that are slightly different is acceptable
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/ossf/scorecard/checker"
|
||||
"github.com/ossf/scorecard/checks"
|
||||
)
|
||||
|
||||
var _ = Describe("E2E TEST:Vulnerabilities", func() {
|
||||
Context("E2E TEST:Validating vulnerabilities status", func() {
|
||||
It("Should return that there are no vulnerabilities", func() {
|
||||
l := log{}
|
||||
checkRequest := checker.CheckRequest{
|
||||
Ctx: context.Background(),
|
||||
Client: ghClient,
|
||||
HTTPClient: httpClient,
|
||||
RepoClient: nil,
|
||||
Owner: "ossf",
|
||||
Repo: "scorecard",
|
||||
GraphClient: graphClient,
|
||||
Logf: l.Logf,
|
||||
}
|
||||
result := checks.HasUnfixedVulnerabilities(&checkRequest)
|
||||
Expect(result.Error).Should(BeNil())
|
||||
Expect(result.Pass).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Should return that there are vulnerabilities", func() {
|
||||
l := log{}
|
||||
checkRequest := checker.CheckRequest{
|
||||
Ctx: context.Background(),
|
||||
Client: ghClient,
|
||||
HTTPClient: httpClient,
|
||||
RepoClient: nil,
|
||||
Owner: "oliverchang",
|
||||
Repo: "open62541",
|
||||
GraphClient: graphClient,
|
||||
Logf: l.Logf,
|
||||
}
|
||||
result := checks.HasUnfixedVulnerabilities(&checkRequest)
|
||||
Expect(result.Error).Should(BeNil())
|
||||
Expect(result.Pass).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user