From e8e7896d8039e2223eada1d6218b714384c8e50f Mon Sep 17 00:00:00 2001 From: Mustafa Abban Date: Thu, 28 Apr 2022 10:08:59 -0500 Subject: [PATCH 1/2] Add support for list Code Scan Alerts by Org --- github/code-scanning.go | 28 +++++ github/code-scanning_test.go | 206 +++++++++++++++++++++++++++++++- github/github-accessors.go | 8 ++ github/github-accessors_test.go | 7 ++ 4 files changed, 248 insertions(+), 1 deletion(-) diff --git a/github/code-scanning.go b/github/code-scanning.go index e09964ba17..48d7295230 100644 --- a/github/code-scanning.go +++ b/github/code-scanning.go @@ -68,6 +68,7 @@ type Tool struct { // GitHub API docs: https://docs.github.com/en/rest/reference/code-scanning#list-code-scanning-alerts-for-a-repository type Alert struct { Number *int `json:"number,omitempty"` + Repository *Repository `json:"repository,omitempty"` RuleID *string `json:"rule_id,omitempty"` RuleSeverity *string `json:"rule_severity,omitempty"` RuleDescription *string `json:"rule_description,omitempty"` @@ -175,6 +176,33 @@ type SarifID struct { URL *string `json:"url,omitempty"` } +// ListAlertsForOrg lists code scanning alerts for an org. +// +// You must use an access token with the security_events scope to use this endpoint. GitHub Apps must have the security_events +// read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/en/rest/code-scanning#list-code-scanning-alerts-for-an-organization +func (s *CodeScanningService) ListAlertsForOrg(ctx context.Context, org string, opts *AlertListOptions) ([]*Alert, *Response, error) { + u := fmt.Sprintf("orgs/%v/code-scanning/alerts", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var alerts []*Alert + resp, err := s.client.Do(ctx, req, &alerts) + if err != nil { + return nil, resp, err + } + + return alerts, resp, nil +} + // ListAlertsForRepo lists code scanning alerts for a repository. // // Lists all open code scanning alerts for the default branch (usually master) and protected branches in a repository. diff --git a/github/code-scanning_test.go b/github/code-scanning_test.go index 0be58153f0..fd8cd257b8 100644 --- a/github/code-scanning_test.go +++ b/github/code-scanning_test.go @@ -89,6 +89,210 @@ func TestCodeScanningService_UploadSarif(t *testing.T) { }) } +func TestCodeScanningService_ListAlertsForOrg(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/code-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "open", "ref": "heads/master"}) + fmt.Fprint(w, `[{ + "rule_id":"js/trivial-conditional", + "rule_severity":"warning", + "rule_description":"Useless conditional", + "tool": { + "name": "CodeQL", + "guid": null, + "version": "1.4.0" + }, + "rule": { + "id": "js/trivial-conditional", + "severity": "warning", + "description": "Useless conditional", + "name": "js/trivial-conditional", + "full_description": "Expression has no effect", + "help": "Expression has no effect" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "state": "open", + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + }, + "created_at":"2020-05-06T12:00:00Z", + "state":"open", + "closed_by":null, + "closed_at":null, + "url":"https://api.github.com/repos/o/r/code-scanning/alerts/25", + "html_url":"https://github.com/o/r/security/code-scanning/25" + }, + { + "rule_id":"js/useless-expression", + "rule_severity":"warning", + "rule_description":"Expression has no effect", + "tool": { + "name": "CodeQL", + "guid": null, + "version": "1.4.0" + }, + "repository": { + "id": 1, + "name": "n", + "url": "url" + }, + "rule": { + "id": "js/useless-expression", + "severity": "warning", + "description": "Expression has no effect", + "name": "js/useless-expression", + "full_description": "Expression has no effect", + "help": "Expression has no effect" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "state": "open", + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + }, + "created_at":"2020-05-06T12:00:00Z", + "state":"open", + "closed_by":null, + "closed_at":null, + "url":"https://api.github.com/repos/o/r/code-scanning/alerts/88", + "html_url":"https://github.com/o/r/security/code-scanning/88" + }]`) + }) + + opts := &AlertListOptions{State: "open", Ref: "heads/master"} + ctx := context.Background() + alerts, _, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts) + if err != nil { + t.Errorf("CodeScanning.ListAlertsForOrg returned error: %v", err) + } + + date := Timestamp{time.Date(2020, time.May, 06, 12, 00, 00, 0, time.UTC)} + want := []*Alert{ + { + Repository: &Repository{ + ID: Int64(1), + URL: String("url"), + Name: String("n"), + }, + RuleID: String("js/trivial-conditional"), + RuleSeverity: String("warning"), + RuleDescription: String("Useless conditional"), + Tool: &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")}, + Rule: &Rule{ + ID: String("js/trivial-conditional"), + Severity: String("warning"), + Description: String("Useless conditional"), + Name: String("js/trivial-conditional"), + FullDescription: String("Expression has no effect"), + Help: String("Expression has no effect"), + }, + CreatedAt: &date, + State: String("open"), + ClosedBy: nil, + ClosedAt: nil, + URL: String("https://api.github.com/repos/o/r/code-scanning/alerts/25"), + HTMLURL: String("https://github.com/o/r/security/code-scanning/25"), + MostRecentInstance: &MostRecentInstance{ + Ref: String("refs/heads/main"), + State: String("open"), + CommitSHA: String("abcdefg12345"), + Message: &Message{ + Text: String("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: String("spec-main/api-session-spec.ts"), + StartLine: Int(917), + EndLine: Int(917), + StartColumn: Int(7), + EndColumn: Int(18), + }, + Classifications: []string{"test"}, + }, + }, + { + RuleID: String("js/useless-expression"), + RuleSeverity: String("warning"), + RuleDescription: String("Expression has no effect"), + Tool: &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")}, + Rule: &Rule{ + ID: String("js/useless-expression"), + Severity: String("warning"), + Description: String("Expression has no effect"), + Name: String("js/useless-expression"), + FullDescription: String("Expression has no effect"), + Help: String("Expression has no effect"), + }, + CreatedAt: &date, + State: String("open"), + ClosedBy: nil, + ClosedAt: nil, + URL: String("https://api.github.com/repos/o/r/code-scanning/alerts/88"), + HTMLURL: String("https://github.com/o/r/security/code-scanning/88"), + MostRecentInstance: &MostRecentInstance{ + Ref: String("refs/heads/main"), + State: String("open"), + CommitSHA: String("abcdefg12345"), + Message: &Message{ + Text: String("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: String("spec-main/api-session-spec.ts"), + StartLine: Int(917), + EndLine: Int(917), + StartColumn: Int(7), + EndColumn: Int(18), + }, + Classifications: []string{"test"}, + }, + }, + } + if !cmp.Equal(alerts, want) { + t.Errorf("CodeScanning.ListAlertsForOrg returned %+v, want %+v", alerts, want) + } + + const methodName = "ListAlertsForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.ListAlertsForOrg(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestCodeScanningService_ListAlertsForRepo(t *testing.T) { client, mux, _, teardown := setup() defer teardown() @@ -322,7 +526,7 @@ func TestCodeScanningService_GetAlert(t *testing.T) { "classifications": [ "test" ] - }, + }, "created_at":"2019-01-02T15:04:05Z", "state":"open", "closed_by":null, diff --git a/github/github-accessors.go b/github/github-accessors.go index d016a36bcb..0144466378 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -342,6 +342,14 @@ func (a *Alert) GetNumber() int { return *a.Number } +// GetRepository returns the Repository field. +func (a *Alert) GetRepository() *Repository { + if a == nil { + return nil + } + return a.Repository +} + // GetRule returns the Rule field. func (a *Alert) GetRule() *Rule { if a == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 3e2108dfc3..8fdb12e59c 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -380,6 +380,13 @@ func TestAlert_GetNumber(tt *testing.T) { a.GetNumber() } +func TestAlert_GetRepository(tt *testing.T) { + a := &Alert{} + a.GetRepository() + a = nil + a.GetRepository() +} + func TestAlert_GetRule(tt *testing.T) { a := &Alert{} a.GetRule() From a10da90cca03d63d2f8ae82c4c0225ecd0c48efe Mon Sep 17 00:00:00 2001 From: Mustafa Abban Date: Fri, 29 Apr 2022 17:52:13 -0500 Subject: [PATCH 2/2] update ListAlertsForOrg test --- github/code-scanning_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/github/code-scanning_test.go b/github/code-scanning_test.go index fd8cd257b8..6e3fc6bb1a 100644 --- a/github/code-scanning_test.go +++ b/github/code-scanning_test.go @@ -97,6 +97,11 @@ func TestCodeScanningService_ListAlertsForOrg(t *testing.T) { testMethod(t, r, "GET") testFormValues(t, r, values{"state": "open", "ref": "heads/master"}) fmt.Fprint(w, `[{ + "repository": { + "id": 1, + "name": "n", + "url": "url" + }, "rule_id":"js/trivial-conditional", "rule_severity":"warning", "rule_description":"Useless conditional", @@ -147,11 +152,6 @@ func TestCodeScanningService_ListAlertsForOrg(t *testing.T) { "guid": null, "version": "1.4.0" }, - "repository": { - "id": 1, - "name": "n", - "url": "url" - }, "rule": { "id": "js/useless-expression", "severity": "warning", @@ -275,7 +275,7 @@ func TestCodeScanningService_ListAlertsForOrg(t *testing.T) { }, } if !cmp.Equal(alerts, want) { - t.Errorf("CodeScanning.ListAlertsForOrg returned %+v, want %+v", alerts, want) + t.Errorf("CodeScanning.ListAlertsForOrg returned %+v, want %+v", *&alerts, *&want) } const methodName = "ListAlertsForOrg"