From 8fba10514a9da7504d2c2cdea9159c215cb9ac5d Mon Sep 17 00:00:00 2001 From: Joey Wilhelm Date: Tue, 1 Aug 2023 16:29:47 -0600 Subject: [PATCH 1/5] feat: Add the beginning of the GitHub data source --- querying/datasource.go | 9 +++++ querying/github.go | 89 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 querying/github.go diff --git a/querying/datasource.go b/querying/datasource.go index ad15823..1a8dfb6 100644 --- a/querying/datasource.go +++ b/querying/datasource.go @@ -1 +1,10 @@ package querying + +import "sync" + +type DataSource interface { + CollectFindings( + *ProjectCollection, + *sync.WaitGroup, + ) error +} diff --git a/querying/github.go b/querying/github.go new file mode 100644 index 0000000..f89efa2 --- /dev/null +++ b/querying/github.go @@ -0,0 +1,89 @@ +package querying + +import ( + "context" + "sync" + + "github.com/shurcooL/githubv4" + "github.com/underdog-tech/vulnbot/logger" +) + +type githubClient interface { + Query(context.Context, interface{}, map[string]interface{}) error +} + +// GithubDataSource is used to pull Dependabot alerts for an individual organization. +type GithubDataSource struct { + ghClient githubClient + orgName string +} + +type orgVulnerabilityQuery struct { + Organization struct { + Name string + Login string + Repositories struct { + TotalCount int + PageInfo struct { + EndCursor githubv4.String + HasNextPage bool + } + Nodes struct { + Name string + VulnerabilityAlerts struct { + TotalCount int + PageInfo struct { + EndCursor githubv4.String + HasNextPage bool + } + Nodes struct { + Id string + Number int + SecurityAdvisory struct { + Description string + Identifiers struct { + Type string + Value string + } + } + SecurityVulnerability struct { + Severity string + Package struct { + Ecosystem string + Name string + } + } + } + } `graphql:"vulnerabilityAlerts(states: OPEN, first: 100, after: $alertCursor)"` + } + } `graphql:"repositories(orderBy: {field: NAME, direction: ASC}, isFork: false, isArchived: false, first: 100, after: $repoCursor)"` + } `graphql:"organization(login: $login)"` +} + +func (gh *GithubDataSource) CollectFindings(projects *ProjectCollection, wg *sync.WaitGroup) { + var alertQuery orgVulnerabilityQuery + log := logger.Get() + defer wg.Done() + + queryVars := map[string]interface{}{ + "login": githubv4.String(gh.orgName), + "repoCursor": (*githubv4.String)(nil), // We pass nil/null to get the first page + "alertCursor": (*githubv4.String)(nil), + } + + for { + log.Info().Msg("Querying GitHub API for vulnerable repositories.") + err := gh.ghClient.Query(context.Background(), &alertQuery, queryVars) + if err != nil { + log.Error().Err(err).Msg("Failed to query GitHub!") + } + // TODO: Process the repositories... + // I'm thinking a goroutine that takes in projects, repoCursor, and alertCursor + // then it can handle pagination of the alerts if necessary + + if !alertQuery.Organization.Repositories.PageInfo.HasNextPage { + break + } + queryVars["reposCursor"] = githubv4.NewString(alertQuery.Organization.Repositories.PageInfo.EndCursor) + } +} From b7db6f6cef380686d5389e9495334fe7e2456271 Mon Sep 17 00:00:00 2001 From: Joey Wilhelm Date: Wed, 2 Aug 2023 14:23:15 -0600 Subject: [PATCH 2/5] fix: Tune the repo info log down to trace --- api/github.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/github.go b/api/github.go index bc2c343..8a95880 100644 --- a/api/github.go +++ b/api/github.go @@ -141,6 +141,6 @@ func QueryGithubOrgRepositoryOwners(ghOrgLogin string, ghClient githubv4.Client) } queryVars["teamsCursor"] = githubv4.NewString(ownerQuery.Organization.Teams.PageInfo.EndCursor) } - log.Debug().Any("repos", allRepos).Msg("Repositories loaded.") + log.Trace().Any("repos", allRepos).Msg("Repositories loaded.") return allRepos } From 456e05c4a8537e1a96cfe349dee835f82343d4e3 Mon Sep 17 00:00:00 2001 From: Joey Wilhelm Date: Wed, 2 Aug 2023 14:25:58 -0600 Subject: [PATCH 3/5] feat: Build out the GithubDataSource to populate a ProjectCollection --- go.mod | 3 +- go.sum | 2 + internal/scan.go | 42 +++++++-- querying/github.go | 212 ++++++++++++++++++++++++++++++++++++-------- querying/project.go | 4 + 5 files changed, 217 insertions(+), 46 deletions(-) diff --git a/go.mod b/go.mod index cf307f7..227a505 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/underdog-tech/vulnbot go 1.20 require ( + github.com/deckarep/golang-set/v2 v2.3.0 github.com/gookit/color v1.5.3 github.com/rs/zerolog v1.29.1 github.com/shurcooL/githubv4 v0.0.0-20230424031643-6cea62ecd5a9 @@ -13,6 +14,7 @@ require ( github.com/stretchr/testify v1.8.3 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 golang.org/x/oauth2 v0.8.0 + golang.org/x/text v0.9.0 ) require ( @@ -37,7 +39,6 @@ require ( github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index fcd4f89..5557a4a 100644 --- a/go.sum +++ b/go.sum @@ -45,6 +45,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t 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/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= +github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= diff --git a/internal/scan.go b/internal/scan.go index 5e33bb6..eb86b02 100644 --- a/internal/scan.go +++ b/internal/scan.go @@ -6,14 +6,14 @@ import ( "sync" "time" + "github.com/shurcooL/githubv4" "github.com/underdog-tech/vulnbot/api" "github.com/underdog-tech/vulnbot/config" "github.com/underdog-tech/vulnbot/logger" "github.com/underdog-tech/vulnbot/reporting" + "golang.org/x/oauth2" - "github.com/shurcooL/githubv4" "github.com/spf13/cobra" - "golang.org/x/oauth2" ) func Scan(cmd *cobra.Command, args []string) { @@ -41,14 +41,32 @@ func Scan(cmd *cobra.Command, args []string) { log.Error().Err(err).Msg("Failed to load ENV file.") } - ghTokenSource := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: env.GithubToken}, - ) - ghOrgLogin := env.GithubOrg - slackToken := env.SlackAuthToken + /**** + * NOTE: This is working code at the moment, but will remain commented out + * until the collating and reporting has been updated to accept the new format. + dataSources := []querying.DataSource{} - httpClient := oauth2.NewClient(context.Background(), ghTokenSource) - ghClient := githubv4.NewClient(httpClient) + if env.GithubToken != "" { + ghds := querying.NewGithubDataSource(userConfig, env) + dataSources = append(dataSources, &ghds) + } + + dswg := new(sync.WaitGroup) + projects := querying.NewProjectCollection() + for _, ds := range dataSources { + dswg.Add(1) + go func(currentDS querying.DataSource) { + err := currentDS.CollectFindings(projects, dswg) + if err != nil { + log.Error().Err(err).Type("datasource", currentDS).Msg("Failed to query datasource") + } + }(ds) + } + dswg.Wait() + log.Trace().Any("projects", projects).Msg("Gathered project information.") + */ + + slackToken := env.SlackAuthToken reporters := []reporting.Reporter{} @@ -64,6 +82,12 @@ func Scan(cmd *cobra.Command, args []string) { reporters = append(reporters, &reporting.ConsoleReporter{Config: userConfig}) reportTime := time.Now().UTC() + ghTokenSource := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: env.GithubToken}, + ) + httpClient := oauth2.NewClient(context.Background(), ghTokenSource) + ghClient := githubv4.NewClient(httpClient) + ghOrgLogin := env.GithubOrg ghOrgName, allRepos := api.QueryGithubOrgVulnerabilities(ghOrgLogin, *ghClient) repositoryOwners := api.QueryGithubOrgRepositoryOwners(ghOrgLogin, *ghClient) // Count our vulnerabilities diff --git a/querying/github.go b/querying/github.go index f89efa2..320e0eb 100644 --- a/querying/github.go +++ b/querying/github.go @@ -5,7 +5,9 @@ import ( "sync" "github.com/shurcooL/githubv4" + "github.com/underdog-tech/vulnbot/config" "github.com/underdog-tech/vulnbot/logger" + "golang.org/x/oauth2" ) type githubClient interface { @@ -16,6 +18,51 @@ type githubClient interface { type GithubDataSource struct { ghClient githubClient orgName string + conf config.Config + ctx context.Context +} + +func NewGithubDataSource(conf config.Config, env config.Env) GithubDataSource { + ghTokenSource := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: env.GithubToken}, + ) + httpClient := oauth2.NewClient(context.Background(), ghTokenSource) + ghClient := githubv4.NewClient(httpClient) + + return GithubDataSource{ + ghClient: ghClient, + orgName: env.GithubOrg, + conf: conf, + ctx: context.Background(), + } +} + +type orgRepo struct { + Name string + Url string + VulnerabilityAlerts struct { + TotalCount int + PageInfo struct { + EndCursor githubv4.String + HasNextPage bool + } + Nodes []struct { + SecurityAdvisory struct { + Description string + Identifiers []struct { + Type string + Value string + } + } + SecurityVulnerability struct { + Severity string + Package struct { + Ecosystem string + Name string + } + } + } + } `graphql:"vulnerabilityAlerts(states: OPEN, first: 100, after: $alertCursor)"` } type orgVulnerabilityQuery struct { @@ -28,39 +75,28 @@ type orgVulnerabilityQuery struct { EndCursor githubv4.String HasNextPage bool } - Nodes struct { - Name string - VulnerabilityAlerts struct { - TotalCount int - PageInfo struct { - EndCursor githubv4.String - HasNextPage bool - } - Nodes struct { - Id string - Number int - SecurityAdvisory struct { - Description string - Identifiers struct { - Type string - Value string - } - } - SecurityVulnerability struct { - Severity string - Package struct { - Ecosystem string - Name string - } - } - } - } `graphql:"vulnerabilityAlerts(states: OPEN, first: 100, after: $alertCursor)"` - } + Nodes []orgRepo } `graphql:"repositories(orderBy: {field: NAME, direction: ASC}, isFork: false, isArchived: false, first: 100, after: $repoCursor)"` } `graphql:"organization(login: $login)"` } -func (gh *GithubDataSource) CollectFindings(projects *ProjectCollection, wg *sync.WaitGroup) { +// Ref: https://docs.github.com/en/graphql/reference/enums#securityadvisoryecosystem +var githubEcosystems = map[string]FindingEcosystemType{ + "ACTIONS": FindingEcosystemGHA, + "COMPOSER": FindingEcosystemPHP, + "ERLANG": FindingEcosystemErlang, + "GO": FindingEcosystemGo, + "MAVEN": FindingEcosystemJava, + "NPM": FindingEcosystemJS, + "NUGET": FindingEcosystemCSharp, + "PIP": FindingEcosystemPython, + "PUB": FindingEcosystemDart, + "RUBYGEMS": FindingEcosystemRuby, + "RUST": FindingEcosystemRust, + "SWIFT": FindingEcosystemSwift, +} + +func (gh *GithubDataSource) CollectFindings(projects *ProjectCollection, wg *sync.WaitGroup) error { var alertQuery orgVulnerabilityQuery log := logger.Get() defer wg.Done() @@ -72,18 +108,122 @@ func (gh *GithubDataSource) CollectFindings(projects *ProjectCollection, wg *syn } for { - log.Info().Msg("Querying GitHub API for vulnerable repositories.") - err := gh.ghClient.Query(context.Background(), &alertQuery, queryVars) + log.Info().Any("repoCursor", queryVars["repoCursor"]).Msg("Querying GitHub API for repositories with vulnerabilities.") + err := gh.ghClient.Query(gh.ctx, &alertQuery, queryVars) if err != nil { - log.Error().Err(err).Msg("Failed to query GitHub!") + log.Error().Err(err).Msg("GitHub repository query failed!") + return err + } + for _, repo := range alertQuery.Organization.Repositories.Nodes { + err := gh.processRepoFindings(projects, repo, queryVars["repoCursor"].(*githubv4.String)) + if err != nil { + log.Warn().Err(err).Str("repository", repo.Name).Msg("Failed to process findings for repository.") + } } - // TODO: Process the repositories... - // I'm thinking a goroutine that takes in projects, repoCursor, and alertCursor - // then it can handle pagination of the alerts if necessary if !alertQuery.Organization.Repositories.PageInfo.HasNextPage { break } - queryVars["reposCursor"] = githubv4.NewString(alertQuery.Organization.Repositories.PageInfo.EndCursor) + queryVars["repoCursor"] = githubv4.NewString(alertQuery.Organization.Repositories.PageInfo.EndCursor) + } + gh.gatherRepoOwners(projects) + return nil +} + +func (gh *GithubDataSource) processRepoFindings(projects *ProjectCollection, repo orgRepo, repoCursor *githubv4.String) error { + log := logger.Get() + project := projects.GetProject(repo.Name) + project.Links["GitHub"] = repo.Url + log.Debug().Str("project", project.Name).Msg("Processing findings for project.") + // TODO: Handle pagination of vulnerabilityAlerts + for _, vuln := range repo.VulnerabilityAlerts.Nodes { + identifiers := FindingIdentifierMap{} + for _, id := range vuln.SecurityAdvisory.Identifiers { + identifiers[FindingIdentifierType(id.Type)] = id.Value + } + log.Debug().Any("identifiers", identifiers).Msg("Processing finding.") + // Utilizing a lambda to account for locks/deferrals + func() { + finding := project.GetFinding(identifiers) + finding.mu.Lock() + defer finding.mu.Unlock() + + if finding.Description == "" { + finding.Description = vuln.SecurityAdvisory.Description + } + if finding.Ecosystem == "" { + finding.Ecosystem = githubEcosystems[vuln.SecurityVulnerability.Package.Ecosystem] + } + if finding.PackageName == "" { + finding.PackageName = vuln.SecurityVulnerability.Package.Name + } + }() + } + return nil +} + +type orgTeam struct { + Name string + Slug string + Repositories struct { + PageInfo struct { + EndCursor githubv4.String + HasNextPage bool + } + Edges []struct { + Permission string + Node struct { + Name string + } + } + } `graphql:"repositories(orderBy: {field: NAME, direction: ASC}, first: 100, after: $repoCursor)"` +} + +type orgRepoOwnerQuery struct { + Organization struct { + Teams struct { + TotalCount int + PageInfo struct { + EndCursor githubv4.String + HasNextPage bool + } + Nodes []orgTeam + } `graphql:"teams(orderBy: {field: NAME, direction: ASC}, first: 100, after: $teamCursor)"` + } `graphql:"organization(login: $login)"` +} + +func (gh *GithubDataSource) gatherRepoOwners(projects *ProjectCollection) { + var ownerQuery orgRepoOwnerQuery + log := logger.Get() + + queryVars := map[string]interface{}{ + "login": githubv4.String(gh.orgName), + "repoCursor": (*githubv4.String)(nil), // We pass nil/null to get the first page + "teamCursor": (*githubv4.String)(nil), + } + + for { + log.Info().Msg("Querying GitHub API for repository ownership information.") + err := gh.ghClient.Query(gh.ctx, &ownerQuery, queryVars) + if err != nil { + log.Fatal().Err(err).Msg("Failed to query GitHub for repository ownership.") + } + for _, team := range ownerQuery.Organization.Teams.Nodes { + teamConfig, _ := config.GetTeamConfigBySlug(team.Slug, gh.conf.Team) + // TODO: Handle pagination of repositories owned by a team + for _, repo := range team.Repositories.Edges { + switch repo.Permission { + case "ADMIN", "MAINTAIN": + project := projects.GetProject(repo.Node.Name) + project.Owners.Add(teamConfig) + default: + continue + } + } + } + if !ownerQuery.Organization.Teams.PageInfo.HasNextPage { + break + } + queryVars["teamCursor"] = githubv4.NewString(ownerQuery.Organization.Teams.PageInfo.EndCursor) } } diff --git a/querying/project.go b/querying/project.go index 04032be..c2b501a 100644 --- a/querying/project.go +++ b/querying/project.go @@ -5,6 +5,8 @@ import ( "strings" "sync" + mapset "github.com/deckarep/golang-set/v2" + "github.com/underdog-tech/vulnbot/config" "golang.org/x/exp/maps" ) @@ -17,6 +19,7 @@ type Project struct { Name string Findings []*Finding Links map[string]string + Owners mapset.Set[config.TeamConfig] mu sync.Mutex } @@ -26,6 +29,7 @@ func NewProject(name string) *Project { Name: name, Findings: []*Finding{}, Links: map[string]string{}, + Owners: mapset.NewSet[config.TeamConfig](), } } From af55100e3c5ecbd36a4fedf2b11ddff0be30b512 Mon Sep 17 00:00:00 2001 From: Joey Wilhelm Date: Fri, 4 Aug 2023 12:57:08 -0600 Subject: [PATCH 4/5] test: Add an initial test for collecting github findings --- querying/finding.go | 4 +- querying/github.go | 8 +-- querying/github_test.go | 70 +++++++++++++++++++ .../single_project_single_finding_owners.json | 11 +++ .../single_project_single_finding_vulns.json | 37 ++++++++++ 5 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 querying/github_test.go create mode 100644 querying/testdata/single_project_single_finding_owners.json create mode 100644 querying/testdata/single_project_single_finding_vulns.json diff --git a/querying/finding.go b/querying/finding.go index b5d4775..de4bd23 100644 --- a/querying/finding.go +++ b/querying/finding.go @@ -6,8 +6,8 @@ type FindingIdentifierType string type FindingIdentifierMap map[FindingIdentifierType]string const ( - FindingIdentifierCVE FindingIdentifierType = "cve" - FindingIdentifierGHSA FindingIdentifierType = "ghsa" + FindingIdentifierCVE FindingIdentifierType = "CVE" + FindingIdentifierGHSA FindingIdentifierType = "GHSA" ) type Finding struct { diff --git a/querying/github.go b/querying/github.go index 320e0eb..6329b4b 100644 --- a/querying/github.go +++ b/querying/github.go @@ -16,7 +16,7 @@ type githubClient interface { // GithubDataSource is used to pull Dependabot alerts for an individual organization. type GithubDataSource struct { - ghClient githubClient + GhClient githubClient orgName string conf config.Config ctx context.Context @@ -30,7 +30,7 @@ func NewGithubDataSource(conf config.Config, env config.Env) GithubDataSource { ghClient := githubv4.NewClient(httpClient) return GithubDataSource{ - ghClient: ghClient, + GhClient: ghClient, orgName: env.GithubOrg, conf: conf, ctx: context.Background(), @@ -109,7 +109,7 @@ func (gh *GithubDataSource) CollectFindings(projects *ProjectCollection, wg *syn for { log.Info().Any("repoCursor", queryVars["repoCursor"]).Msg("Querying GitHub API for repositories with vulnerabilities.") - err := gh.ghClient.Query(gh.ctx, &alertQuery, queryVars) + err := gh.GhClient.Query(gh.ctx, &alertQuery, queryVars) if err != nil { log.Error().Err(err).Msg("GitHub repository query failed!") return err @@ -204,7 +204,7 @@ func (gh *GithubDataSource) gatherRepoOwners(projects *ProjectCollection) { for { log.Info().Msg("Querying GitHub API for repository ownership information.") - err := gh.ghClient.Query(gh.ctx, &ownerQuery, queryVars) + err := gh.GhClient.Query(gh.ctx, &ownerQuery, queryVars) if err != nil { log.Fatal().Err(err).Msg("Failed to query GitHub for repository ownership.") } diff --git a/querying/github_test.go b/querying/github_test.go new file mode 100644 index 0000000..12b1cb4 --- /dev/null +++ b/querying/github_test.go @@ -0,0 +1,70 @@ +package querying_test + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "os" + "strings" + "sync" + "testing" + + mapset "github.com/deckarep/golang-set/v2" + "github.com/shurcooL/githubv4" + "github.com/stretchr/testify/assert" + "github.com/underdog-tech/vulnbot/config" + "github.com/underdog-tech/vulnbot/querying" +) + +func TestCollectFindingsSingleProjectSingleFinding(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var bodyJson map[string]string + var data []byte + _ = json.NewDecoder(r.Body).Decode(&bodyJson) + vulnQuery := strings.Contains(bodyJson["query"], "vulnerabilityAlerts") + if vulnQuery { + data, _ = os.ReadFile("testdata/single_project_single_finding_vulns.json") + } else { + data, _ = os.ReadFile("testdata/single_project_single_finding_owners.json") + } + w.WriteHeader(http.StatusOK) + w.Write([]byte(data)) + })) + defer server.Close() + + conf := config.Config{} + env := config.Env{} + env.GithubOrg = "heart-of-gold" + env.GithubToken = "pangalactic-gargleblaster" + + ds := querying.NewGithubDataSource(conf, env) + ds.GhClient = githubv4.NewEnterpriseClient(server.URL, &http.Client{}) + + projects := querying.NewProjectCollection() + wg := new(sync.WaitGroup) + wg.Add(1) + ds.CollectFindings(projects, wg) + expected := querying.ProjectCollection{ + Projects: []*querying.Project{ + { + Name: "zaphod", + Links: map[string]string{ + "GitHub": "https://heart-of-gold/zaphod", + }, + Findings: []*querying.Finding{ + { + Ecosystem: querying.FindingEcosystemGo, + Severity: querying.FindingSeverityCritical, + Description: "The Improbability Drive is far too improbable.", + PackageName: "improbability-drive", + Identifiers: querying.FindingIdentifierMap{ + querying.FindingIdentifierCVE: "CVE-42", + }, + }, + }, + Owners: mapset.NewSet[config.TeamConfig](), + }, + }, + } + assert.Equal(t, &expected, projects) +} diff --git a/querying/testdata/single_project_single_finding_owners.json b/querying/testdata/single_project_single_finding_owners.json new file mode 100644 index 0000000..713dcf3 --- /dev/null +++ b/querying/testdata/single_project_single_finding_owners.json @@ -0,0 +1,11 @@ +{ + "data": { + "organization": { + "teams": { + "totalCount": 0, + "pageInfo": { "hasNextPage": false }, + "nodes": [] + } + } + } +} diff --git a/querying/testdata/single_project_single_finding_vulns.json b/querying/testdata/single_project_single_finding_vulns.json new file mode 100644 index 0000000..8d94fc9 --- /dev/null +++ b/querying/testdata/single_project_single_finding_vulns.json @@ -0,0 +1,37 @@ +{ + "data": { + "organization": { + "repositories": { + "totalCount": 1, + "pageInfo": { "hasNextPage": false }, + "nodes": [ + { + "name": "zaphod", + "url": "https://heart-of-gold/zaphod", + "vulnerabilityAlerts": { + "totalCount": 1, + "pageInfo": { "hasNextPage": false }, + "nodes": [ + { + "securityAdvisory": { + "description": "The Improbability Drive is far too improbable.", + "identifiers": [ + { "type": "CVE", "value": "CVE-42" } + ] + }, + "securityVulnerability": { + "severity": "CRITICAL", + "package": { + "ecosystem": "GO", + "name": "improbability-drive" + } + } + } + ] + } + } + ] + } + } + } +} From 8ed98b9269149dba11c245374cfb840967c67e2d Mon Sep 17 00:00:00 2001 From: Joey Wilhelm Date: Fri, 4 Aug 2023 12:59:44 -0600 Subject: [PATCH 5/5] fix: Fix linting errors in the test --- querying/github_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/querying/github_test.go b/querying/github_test.go index 12b1cb4..23fb205 100644 --- a/querying/github_test.go +++ b/querying/github_test.go @@ -28,7 +28,7 @@ func TestCollectFindingsSingleProjectSingleFinding(t *testing.T) { data, _ = os.ReadFile("testdata/single_project_single_finding_owners.json") } w.WriteHeader(http.StatusOK) - w.Write([]byte(data)) + _, _ = w.Write([]byte(data)) })) defer server.Close() @@ -43,7 +43,10 @@ func TestCollectFindingsSingleProjectSingleFinding(t *testing.T) { projects := querying.NewProjectCollection() wg := new(sync.WaitGroup) wg.Add(1) - ds.CollectFindings(projects, wg) + err := ds.CollectFindings(projects, wg) + if err != nil { + t.Error(err) + } expected := querying.ProjectCollection{ Projects: []*querying.Project{ {