Skip to content

Commit

Permalink
Merge pull request #1012 from stacklok/912-support-go-dependency-scan…
Browse files Browse the repository at this point in the history
…ning-for-pr

Support Go dependency scanning for pull requests
  • Loading branch information
jhrozek authored Sep 26, 2023
2 parents c1913aa + 4b347c1 commit acdf4ea
Show file tree
Hide file tree
Showing 9 changed files with 751 additions and 602 deletions.
1 change: 1 addition & 0 deletions docs/docs/protodocs/proto.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions examples/github/policies/pr_vuln_check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ pull_request:
vulnerability_database_endpoint: https://api.osv.dev/v1/query
package_repository:
url: https://registry.npmjs.org
- name: go
- name: Go
vulnerability_database_type: osv
vulnerability_database_endpoint: https://vuln.go.dev
vulnerability_database_endpoint: https://api.osv.dev/v1/query
package_repository:
url: https://proxy.golang.org
2 changes: 2 additions & 0 deletions examples/github/rule-types/pr_vulnerability_check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ def:
ecosystems:
- name: npm
depfile: package-lock.json
- name: go
depfile: go.sum
# Defines the configuration for evaluating data ingested against the given policy
eval:
type: vulncheck
Expand Down
2 changes: 2 additions & 0 deletions internal/engine/eval/vulncheck/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ func pbEcosystemAsString(ecosystem pb.DepEcosystem) string {
switch ecosystem {
case pb.DepEcosystem_DEP_ECOSYSTEM_NPM:
return "npm"
case pb.DepEcosystem_DEP_ECOSYSTEM_GO:
return "Go"
case pb.DepEcosystem_DEP_ECOSYSTEM_UNSPECIFIED:
// this shouldn't happen
return ""
Expand Down
2 changes: 2 additions & 0 deletions internal/engine/ingester/diff/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type DependencyEcosystem string
const (
// DepEcosystemNPM is the npm dependency ecosystem
DepEcosystemNPM DependencyEcosystem = "npm"
// DepEcosystemGo is the go dependency ecosystem
DepEcosystemGo DependencyEcosystem = "go"
// DepEcosystemNone is the fallback value
DepEcosystemNone DependencyEcosystem = ""
)
Expand Down
38 changes: 35 additions & 3 deletions internal/engine/ingester/diff/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package diff

import (
"bufio"
"encoding/json"
"fmt"
"strings"
Expand All @@ -26,16 +27,47 @@ import (
type ecosystemParser func(string) ([]*pb.Dependency, error)

func newEcosystemParser(eco DependencyEcosystem) ecosystemParser {
switch eco {
case DepEcosystemNPM:
switch strings.ToLower(string(eco)) {
case string(DepEcosystemNPM):
return npmParse
case DepEcosystemNone:
case string(DepEcosystemGo):
return goParse
case string(DepEcosystemNone):
return nil
default:
return nil
}
}

func goParse(patch string) ([]*pb.Dependency, error) {
scanner := bufio.NewScanner(strings.NewReader(patch))
var deps []*pb.Dependency

for scanner.Scan() {
line := scanner.Text()

if strings.HasPrefix(line, "+") {
fields := strings.Split(line, " ")
if len(fields) > 2 && !strings.HasSuffix(fields[1], "/go.mod") {
name, version := fields[0], fields[1]
dep := &pb.Dependency{
Ecosystem: pb.DepEcosystem_DEP_ECOSYSTEM_GO,
Name: name[1:],
Version: version,
}

deps = append(deps, dep)
}
}
}

if err := scanner.Err(); err != nil {
return nil, err
}

return deps, nil
}

type npmDependency struct {
Version string `json:"version"`
Resolved string `json:"resolved"`
Expand Down
104 changes: 104 additions & 0 deletions internal/engine/ingester/diff/parse_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2023 Stacklok, Inc.
//
// 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 diff provides the diff rule data ingest engine
package diff

import (
"testing"

"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/proto"

pb "github.com/stacklok/mediator/pkg/api/protobuf/go/mediator/v1"
)

func TestGoParse(t *testing.T) {
t.Parallel()

tests := []struct {
description string
content string
expectedCount int
expectedDependencies []*pb.Dependency
}{
{
description: "Single addition",
content: `
+cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
+cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=`,
expectedCount: 1,
expectedDependencies: []*pb.Dependency{
{
Ecosystem: pb.DepEcosystem_DEP_ECOSYSTEM_GO,
Name: "cloud.google.com/go/compute",
Version: "v1.23.0",
},
},
},
{
description: "Single removal",
content: `
-cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
-cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=`,
expectedCount: 0,
expectedDependencies: nil,
},
{
description: "Mixed additions and removals",
content: `
-cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
-cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
+cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
+cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
+dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
+dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=`,
expectedCount: 2,
expectedDependencies: []*pb.Dependency{
{
Ecosystem: pb.DepEcosystem_DEP_ECOSYSTEM_GO,
Name: "cloud.google.com/go/compute/metadata",
Version: "v0.2.3",
},
{
Ecosystem: pb.DepEcosystem_DEP_ECOSYSTEM_GO,
Name: "dario.cat/mergo",
Version: "v1.0.0",
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.description, func(t *testing.T) {
t.Parallel()
got, err := goParse(tt.content)
if err != nil {
t.Fatalf("goParse() returned error: %v", err)
}

assert.Equal(t, tt.expectedCount, len(got), "mismatched dependency count")

for i, expectedDep := range tt.expectedDependencies {
if !proto.Equal(expectedDep, got[i]) {
t.Errorf("mismatch at index %d: expected %v, got %v", i, expectedDep, got[i])
}
}
})
}
}
Loading

0 comments on commit acdf4ea

Please sign in to comment.