Skip to content

Commit

Permalink
Merge pull request #92 from underdog-tech/feat/report-project-link
Browse files Browse the repository at this point in the history
feat: Display project links in the Slack team reports
  • Loading branch information
tarkatronic authored Nov 8, 2023
2 parents 76f1575 + 7a58c8e commit 9cfdf55
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 75 deletions.
3 changes: 3 additions & 0 deletions .golangci.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ enable = [
"prealloc",
"zerologlint",
]

[linters-settings.cyclop]
max-complexity = 11
5 changes: 3 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"github.com/rs/zerolog"
"github.com/underdog-tech/vulnbot/config"
"github.com/underdog-tech/vulnbot/logger"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -47,7 +46,9 @@ func NewRootCommand() *cobra.Command {

// Set up Viper config
_ = viper.BindPFlags(pflags)
config.SetConfigDefaults()
// tarkatronic(2023-11-07): This appears to be no longer necessary.
// Leaving it commented out for the time being. Viper is confusing.
// config.SetConfigDefaults()

return rootCmd
}
Expand Down
17 changes: 13 additions & 4 deletions querying/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ func (gh *GithubDataSource) CollectFindings(projects *ProjectCollection, wg *syn
func (gh *GithubDataSource) processRepoFindings(projects *ProjectCollection, repo orgRepo) error {
log := logger.Get()
project := projects.GetProject(repo.Name)
project.Links["GitHub"] = repo.Url

// Link directly to Dependabot findings.
// There doesn't appear to be a GraphQL property for this link.
project.Links["GitHub"] = repo.Url + "/security/dependabot"

log.Debug().Str("project", project.Name).Msg("Processing findings for project.")

for _, vuln := range repo.VulnerabilityAlerts.Nodes {
Expand Down Expand Up @@ -204,7 +208,9 @@ type orgTeam struct {
Edges []struct {
Permission string
Node struct {
Name string
Name string
IsFork bool
IsArchived bool
}
}
} `graphql:"repositories(orderBy: {field: NAME, direction: ASC}, first: 100, after: $repoCursor)"`
Expand Down Expand Up @@ -235,8 +241,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)
if err != nil {
if err := gh.GhClient.Query(gh.ctx, &ownerQuery, queryVars); err != nil {
log.Fatal().Err(err).Msg("Failed to query GitHub for repository ownership.")
}
for _, team := range ownerQuery.Organization.Teams.Nodes {
Expand All @@ -247,6 +252,10 @@ func (gh *GithubDataSource) gatherRepoOwners(projects *ProjectCollection) {
}
// TODO: Handle pagination of repositories owned by a team
for _, repo := range team.Repositories.Edges {
if repo.Node.IsArchived || repo.Node.IsFork {
log.Debug().Str("Repo", repo.Node.Name).Bool("IsFork", repo.Node.IsFork).Bool("IsArchived", repo.Node.IsArchived).Msg("Skipping untracked repository.")
continue
}
switch repo.Permission {
case "ADMIN", "MAINTAIN":
project := projects.GetProject(repo.Node.Name)
Expand Down
2 changes: 1 addition & 1 deletion querying/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func getTestProject() querying.ProjectCollection {
{
Name: "zaphod",
Links: map[string]string{
"GitHub": "https://heart-of-gold/zaphod",
"GitHub": "https://heart-of-gold/zaphod/security/dependabot",
},
Findings: []*querying.Finding{
{
Expand Down
12 changes: 10 additions & 2 deletions reporting/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,16 @@ func (s *SlackReporter) BuildTeamRepositoryReport(
severityIcon = DEFAULT_SLACK_ICON
}
}
projLinks := make([]string, 0)
for title, link := range repoReport.Project.Links {
projLinks = append(projLinks, fmt.Sprintf("[<%s|%s>]", link, title))
}
projName := fmt.Sprintf("%s *%s*", severityIcon, repoReport.Project.Name)
if len(projLinks) > 0 {
projName = fmt.Sprintf("%s · %s", projName, strings.Join(projLinks, " "))
}
fields := []*slack.TextBlockObject{
slack.NewTextBlockObject(slack.MarkdownType, fmt.Sprintf("%s *%s*", severityIcon, repoReport.Name), false, false),
slack.NewTextBlockObject(slack.MarkdownType, projName, false, false),
slack.NewTextBlockObject(slack.MarkdownType, strings.Join(vulnCounts, " | "), false, false),
}
return slack.NewSectionBlock(nil, fields, nil)
Expand All @@ -172,7 +180,7 @@ func (s *SlackReporter) BuildTeamReport(
sort.Sort(repos)
var summaryReport *ProjectFindingSummary
for _, repo := range repos {
if repo.Name == SUMMARY_KEY {
if repo.Project.Name == SUMMARY_KEY {
summaryReport = repo
continue
}
Expand Down
22 changes: 13 additions & 9 deletions reporting/slack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/underdog-tech/vulnbot/config"
"github.com/underdog-tech/vulnbot/querying"
"github.com/underdog-tech/vulnbot/reporting"
)

Expand Down Expand Up @@ -205,8 +206,11 @@ func TestSendSlackSummaryReportSendsSingleMessage(t *testing.T) {

func TestBuildSlackTeamRepositoryReport(t *testing.T) {
reporter := reporting.SlackReporter{Config: &config.Config{}}

report := reporting.NewProjectFindingSummary("foo")
proj := querying.NewProject("foo")
proj.Links = map[string]string{
"GitHub": "https://github.com/bar/foo",
}
report := reporting.NewProjectFindingSummary(proj)
report.VulnsByEcosystem[config.FindingEcosystemPython] = 15
report.VulnsBySeverity[config.FindingSeverityCritical] = 2
report.VulnsBySeverity[config.FindingSeverityHigh] = 3
Expand All @@ -217,7 +221,7 @@ func TestBuildSlackTeamRepositoryReport(t *testing.T) {
"fields": []map[string]interface{}{
{
"type": "mrkdwn",
"text": " *foo*",
"text": " *foo* · [<https://github.com/bar/foo|GitHub>]",
},
{
"type": "mrkdwn",
Expand All @@ -240,16 +244,16 @@ func TestBuildSlackTeamReport(t *testing.T) {
}
reporter := reporting.SlackReporter{Config: &cfg}

repo1Report := reporting.NewProjectFindingSummary("repo1")
repo1Report := reporting.NewProjectFindingSummary(querying.NewProject("repo1"))
repo1Report.VulnsByEcosystem[config.FindingEcosystemPython] = 10
repo1Report.VulnsBySeverity[config.FindingSeverityLow] = 10

repo2Report := reporting.NewProjectFindingSummary("repo2")
repo2Report := reporting.NewProjectFindingSummary(querying.NewProject("repo2"))
repo2Report.VulnsByEcosystem[config.FindingEcosystemPython] = 5
repo2Report.VulnsBySeverity[config.FindingSeverityCritical] = 1
repo2Report.VulnsBySeverity[config.FindingSeverityModerate] = 4

summaryReport := reporting.NewProjectFindingSummary(reporting.SUMMARY_KEY)
summaryReport := reporting.NewProjectFindingSummary(querying.NewProject(reporting.SUMMARY_KEY))
summaryReport.AffectedRepos = 2
summaryReport.TotalCount = 15

Expand Down Expand Up @@ -336,9 +340,9 @@ func TestSendSlackTeamReportsSendsMessagePerTeam(t *testing.T) {
}
mockClient := new(MockSlackClient)
reporter := reporting.SlackReporter{Config: &cfg, Client: mockClient}
repo1Report := reporting.NewProjectFindingSummary("repo1")
repo2Report := reporting.NewProjectFindingSummary("repo2")
summaryReport := reporting.NewProjectFindingSummary(reporting.SUMMARY_KEY)
repo1Report := reporting.NewProjectFindingSummary(querying.NewProject("repo1"))
repo2Report := reporting.NewProjectFindingSummary(querying.NewProject("repo2"))
summaryReport := reporting.NewProjectFindingSummary(querying.NewProject(reporting.SUMMARY_KEY))
teamReports := map[config.TeamConfig]reporting.TeamProjectCollection{
teamFoo: {
&repo1Report,
Expand Down
14 changes: 7 additions & 7 deletions reporting/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type FindingSummary struct {
type ProjectFindingSummary struct {
FindingSummary

Name string
Project *querying.Project
}

// GetHighestCriticality looks for the severity level of the most critical
Expand All @@ -75,9 +75,9 @@ func NewFindingSummary() FindingSummary {
}
}

func NewProjectFindingSummary(name string) ProjectFindingSummary {
func NewProjectFindingSummary(project *querying.Project) ProjectFindingSummary {
summary := NewFindingSummary()
return ProjectFindingSummary{Name: name, FindingSummary: summary}
return ProjectFindingSummary{Project: project, FindingSummary: summary}
}

func SummarizeFindings(projects *querying.ProjectCollection) (FindingSummary, []ProjectFindingSummary) {
Expand All @@ -86,7 +86,7 @@ func SummarizeFindings(projects *querying.ProjectCollection) (FindingSummary, []
projectReportCollection := []ProjectFindingSummary{}

for _, project := range projects.Projects {
projectReport := NewProjectFindingSummary(project.Name)
projectReport := NewProjectFindingSummary(project)
if numFindings := len(project.Findings); numFindings > 0 {
affectedRepos += 1
vulnCount += numFindings
Expand Down Expand Up @@ -129,7 +129,7 @@ func (r TeamProjectCollection) Less(i, j int) bool {
if sevOne != sevTwo {
return sevOne < sevTwo
}
return r[i].Name < r[j].Name
return r[i].Project.Name < r[j].Project.Name
}

// GroupTeamFindings gathers a map of each team and the summaries of the projects
Expand All @@ -140,7 +140,7 @@ func GroupTeamFindings(projects *querying.ProjectCollection, summaries []Project
for _, project := range projects.Projects {
projectSummary := ProjectFindingSummary{}
for _, sum := range summaries {
if sum.Name == project.Name {
if sum.Project == project {
projectSummary = sum
break
}
Expand All @@ -152,7 +152,7 @@ func GroupTeamFindings(projects *querying.ProjectCollection, summaries []Project
}
// We also need a summary report for each team
for team, projects := range teamProjects {
summaryReport := NewProjectFindingSummary(SUMMARY_KEY)
summaryReport := NewProjectFindingSummary(querying.NewProject(SUMMARY_KEY))
for _, project := range projects {
summaryReport.TotalCount += project.TotalCount
}
Expand Down
Loading

0 comments on commit 9cfdf55

Please sign in to comment.