Skip to content

Commit

Permalink
Add table github_dependabot_alert
Browse files Browse the repository at this point in the history
  • Loading branch information
francois2metz committed Dec 5, 2022
1 parent 1142afe commit 4ce1d9a
Show file tree
Hide file tree
Showing 5 changed files with 494 additions and 9 deletions.
2 changes: 2 additions & 0 deletions github/plugin.go
Expand Up @@ -39,10 +39,12 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"github_my_team": tableGitHubMyTeam(),
"github_organization": tableGitHubOrganization(),
"github_organization_member": tableGitHubOrganizationMember(),
"github_organization_dependabot_alert": tableGitHubOrganizationDependabotAlert(),
"github_pull_request": tableGitHubPullRequest(),
"github_rate_limit": tableGitHubRateLimit(ctx),
"github_release": tableGitHubRelease(ctx),
"github_repository": tableGitHubRepository(),
"github_repository_dependabot_alert": tableGitHubRepositoryDependabotAlert(),
"github_search_code": tableGitHubSearchCode(ctx),
"github_search_commit": tableGitHubSearchCommit(ctx),
"github_search_issue": tableGitHubSearchIssue(ctx),
Expand Down
299 changes: 299 additions & 0 deletions github/table_github_organization_dependabot_alert.go
@@ -0,0 +1,299 @@
package github

import (
"context"

"github.com/google/go-github/v48/github"
"github.com/turbot/steampipe-plugin-sdk/v4/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/v4/plugin"
"github.com/turbot/steampipe-plugin-sdk/v4/plugin/transform"
)

//// TABLE DEFINITION

func gitHubDependabotAlertColumns() []*plugin.Column {
return []*plugin.Column{
{
Name: "dependabot_number",
Type: proto.ColumnType_INT,
Description: "The security alert number.",
Transform: transform.FromField("Number"),
},
{
Name: "state",
Type: proto.ColumnType_STRING,
Description: "The state of the Dependabot alert.",
},
{
Name: "dependency_package_ecosystem",
Type: proto.ColumnType_STRING,
Description: "The package's language or package management ecosystem.",
Transform: transform.FromField("Dependency.Package.Ecosystem"),
},
{
Name: "dependency_package_name",
Type: proto.ColumnType_STRING,
Description: "The unique package name within its ecosystem.",
Transform: transform.FromField("Dependency.Package.Name"),
},
{
Name: "dependency_manifest_path",
Type: proto.ColumnType_STRING,
Description: "The unique package name within its ecosystem.",
Transform: transform.FromField("Dependency.ManifestPath"),
},
{
Name: "dependency_scope",
Type: proto.ColumnType_STRING,
Description: "The execution scope of the vulnerable dependency.",
Transform: transform.FromField("Dependency.Scope"),
},
{
Name: "security_advisory_ghsaid",
Type: proto.ColumnType_STRING,
Description: "The unique GitHub Security Advisory ID assigned to the advisory.",
Transform: transform.FromField("SecurityAdvisory.GHSAID"),
},
{
Name: "security_advisory_cveid",
Type: proto.ColumnType_STRING,
Description: "The unique CVE ID assigned to the advisory.",
Transform: transform.FromField("SecurityAdvisory.CVEID"),
},
{
Name: "security_advisory_summary",
Type: proto.ColumnType_STRING,
Description: "A short, plain text summary of the advisory.",
Transform: transform.FromField("SecurityAdvisory.Summary"),
},
{
Name: "security_advisory_description",
Type: proto.ColumnType_STRING,
Description: "A long-form Markdown-supported description of the advisory.",
Transform: transform.FromField("SecurityAdvisory.Description"),
},
{
Name: "security_advisory_severity",
Type: proto.ColumnType_STRING,
Description: "The severity of the advisory.",
Transform: transform.FromField("SecurityAdvisory.Severity"),
},
{
Name: "security_advisory_cvss_score",
Type: proto.ColumnType_DOUBLE,
Description: "The overall CVSS score of the advisory.",
Transform: transform.FromField("SecurityAdvisory.CVSs.Score"),
},
{
Name: "security_advisory_cvss_vector_string",
Type: proto.ColumnType_STRING,
Description: "The full CVSS vector string for the advisory.",
Transform: transform.FromField("SecurityAdvisory.CVSs.VectorString"),
},
{
Name: "security_advisory_cwes_cweid",
Type: proto.ColumnType_STRING,
Description: "The unique CWE ID.",
Transform: transform.FromField("SecurityAdvisory.CWEs.CWEID"),
},
{
Name: "security_advisory_cwes_name",
Type: proto.ColumnType_STRING,
Description: "The short, plain text name of the CWE.",
Transform: transform.FromField("SecurityAdvisory.CWEs.Name"),
},
{
Name: "security_advisory_published_at",
Type: proto.ColumnType_TIMESTAMP,
Description: "The time that the advisory was published.",
Transform: transform.FromField("SecurityAdvisory.PublishedAt").NullIfZero().Transform(convertTimestamp),
},
{
Name: "security_advisory_updated_at",
Type: proto.ColumnType_TIMESTAMP,
Description: "The time that the advisory was last modified.",
Transform: transform.FromField("SecurityAdvisory.UpdatedAt").NullIfZero().Transform(convertTimestamp),
},
{
Name: "security_advisory_withdrawn_at",
Type: proto.ColumnType_TIMESTAMP,
Description: "The time that the advisory was withdrawn.",
Transform: transform.FromField("SecurityAdvisory.WithdrawnAt").NullIfZero().Transform(convertTimestamp),
},
{
Name: "url",
Type: proto.ColumnType_STRING,
Description: "The REST API URL of the alert resource.",
},
{
Name: "html_url",
Type: proto.ColumnType_STRING,
Description: "The GitHub URL of the alert resource.",
},
{
Name: "created_at",
Type: proto.ColumnType_TIMESTAMP,
Description: "The time that the alert was created.",
Transform: transform.FromField("CreatedAt").Transform(convertTimestamp),
},
{
Name: "updated_at",
Type: proto.ColumnType_TIMESTAMP,
Description: "The time that the alert was last updated.",
Transform: transform.FromField("UpdatedAt").Transform(convertTimestamp),
},
{
Name: "dismissed_at",
Type: proto.ColumnType_TIMESTAMP,
Description: "The time that the alert was dismissed.",
Transform: transform.FromField("DismissedAt").NullIfZero().Transform(convertTimestamp),
},
{
Name: "dismissed_reason",
Type: proto.ColumnType_STRING,
Description: "The reason that the alert was dismissed.",
},
{
Name: "dismissed_comment",
Type: proto.ColumnType_STRING,
Description: "An optional comment associated with the alert's dismissal.",
},
{
Name: "fixed_at",
Type: proto.ColumnType_TIMESTAMP,
Description: "The time that the alert was no longer detected and was considered fixed.",
Transform: transform.FromField("FixedAt").NullIfZero().Transform(convertTimestamp),
},
}
}

func tableGitHubOrganizationDependabotAlert() *plugin.Table {
return &plugin.Table{
Name: "github_organization_dependabot_alert",
Description: "Dependabot alerts from an organization.",
List: &plugin.ListConfig{
KeyColumns: []*plugin.KeyColumn{
{
Name: "organization",
Require: plugin.Required,
},
{
Name: "state",
Require: plugin.Optional,
},
{
Name: "security_advisory_severity",
Require: plugin.Optional,
},
{
Name: "dependency_package_ecosystem",
Require: plugin.Optional,
},
{
Name: "dependency_package_name",
Require: plugin.Optional,
},
{
Name: "dependency_scope",
Require: plugin.Optional,
},
},
ShouldIgnoreError: isNotFoundError([]string{"404"}),
Hydrate: tableGitHubOrganizationDependabotAlertList,
},
Columns: append(
gitHubDependabotAlertColumns(),
[]*plugin.Column{
{
Name: "organization",
Type: proto.ColumnType_STRING,
Description: "The login name of the organization.",
Transform: transform.FromQual("organization"),
},
}...,
),
}
}

//// LIST FUNCTION

func tableGitHubOrganizationDependabotAlertList(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
quals := d.KeyColumnQuals

org := quals["organization"].GetStringValue()

opt := &github.ListAlertsOptions{
ListCursorOptions: github.ListCursorOptions{First: 100},
}

if quals["state"] != nil {
state := quals["state"].GetStringValue()
opt.State = &state
}
if quals["security_advisory_severity"] != nil {
severity := quals["security_advisory_severity"].GetStringValue()
opt.Severity = &severity
}
if quals["dependency_package_ecosystem"] != nil {
ecosystem := quals["dependency_package_ecosystem"].GetStringValue()
opt.Ecosystem = &ecosystem
}
if quals["dependency_package_name"] != nil {
package_name := quals["dependency_package_name"].GetStringValue()
opt.Package = &package_name
}
if quals["dependency_scope"] != nil {
scope := quals["dependency_scope"].GetStringValue()
opt.Scope = &scope
}

type ListPageResponse struct {
alerts []*github.DependabotAlert
resp *github.Response
}

client := connect(ctx, d)

limit := d.QueryContext.Limit
if limit != nil {
if *limit < int64(opt.ListCursorOptions.First) {
opt.ListCursorOptions.First = int(*limit)
}
}

listPage := func(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
alerts, resp, err := client.Dependabot.ListOrgAlerts(ctx, org, opt)
return ListPageResponse{
alerts: alerts,
resp: resp,
}, err
}
for {
listPageResponse, err := retryHydrate(ctx, d, h, listPage)

if err != nil {
return nil, err
}

listResponse := listPageResponse.(ListPageResponse)
alerts := listResponse.alerts
resp := listResponse.resp

for _, i := range alerts {
d.StreamListItem(ctx, i)

// Context can be cancelled due to manual cancellation or the limit has been hit
if d.QueryStatus.RowsRemaining(ctx) == 0 {
return nil, nil
}
}

if resp.After == "" {
break
}

opt.ListCursorOptions.After = resp.After
}

return nil, nil
}

0 comments on commit 4ce1d9a

Please sign in to comment.