Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3678448
commit 3fa5902
Showing
2 changed files
with
235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
package github | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/google/go-github/v45/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 tableGitHubDependabotAlert() *plugin.Table { | ||
return &plugin.Table{ | ||
Name: "github_dependabot_alert", | ||
Description: "", | ||
List: &plugin.ListConfig{ | ||
KeyColumns: []*plugin.KeyColumn{ | ||
{ | ||
Name: "repository_full_name", | ||
Require: plugin.Required, | ||
}, | ||
{ | ||
Name: "state", | ||
Require: plugin.Optional, | ||
}, | ||
}, | ||
ShouldIgnoreError: isNotFoundError([]string{"404"}), | ||
Hydrate: tableGitHubDependabotAlertList, | ||
}, | ||
Get: &plugin.GetConfig{ | ||
KeyColumns: plugin.AllColumns([]string{"repository_full_name", "dependabot_number"}), | ||
ShouldIgnoreError: isNotFoundError([]string{"404"}), | ||
Hydrate: tableGitHubDependabotAlertGet, | ||
}, | ||
Columns: []*plugin.Column{ | ||
{ | ||
Name: "repository_full_name", | ||
Type: proto.ColumnType_STRING, | ||
Transform: transform.FromQual("repository_full_name"), | ||
Description: "The full name of the repository (login/repo-name).", | ||
}, | ||
{ | ||
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: "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), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
//// LIST FUNCTION | ||
|
||
func tableGitHubDependabotAlertList(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { | ||
quals := d.KeyColumnQuals | ||
|
||
fullName := quals["repository_full_name"].GetStringValue() | ||
owner, repo := parseRepoFullName(fullName) | ||
|
||
opt := &github.ListAlertsOptions{ | ||
ListCursorOptions: github.ListCursorOptions{First: 100}, | ||
} | ||
|
||
if quals["state"] != nil { | ||
state := quals["state"].GetStringValue() | ||
opt.State = &state | ||
} | ||
|
||
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.ListRepoAlerts(ctx, owner, repo, 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 | ||
} | ||
|
||
//// HYDRATE FUNCTIONS | ||
|
||
func tableGitHubDependabotAlertGet(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { | ||
var owner, repo string | ||
var alertNumber int | ||
|
||
logger := plugin.Logger(ctx) | ||
quals := d.KeyColumnQuals | ||
|
||
alertNumber = int(d.KeyColumnQuals["dependabot_number"].GetInt64Value()) | ||
fullName := quals["repository_full_name"].GetStringValue() | ||
owner, repo = parseRepoFullName(fullName) | ||
logger.Trace("tableGitHubDependabotAlertGet", "owner", owner, "repo", repo, "alertNumber", alertNumber) | ||
|
||
client := connect(ctx, d) | ||
|
||
type GetResponse struct { | ||
alert *github.DependabotAlert | ||
resp *github.Response | ||
} | ||
|
||
getDetails := func(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { | ||
alert, resp, err := client.Dependabot.GetRepoAlert(ctx, owner, repo, alertNumber) | ||
return GetResponse{ | ||
alert: alert, | ||
resp: resp, | ||
}, err | ||
} | ||
|
||
getResponse, err := retryHydrate(ctx, d, h, getDetails) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
getResp := getResponse.(GetResponse) | ||
alert := getResp.alert | ||
|
||
return alert, nil | ||
} |