Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions github/code-scanning.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,22 @@ type SarifAnalysis struct {
ToolName *string `json:"tool_name,omitempty"`
}

// CodeScanningAlertState specifies the state of a code scanning alert.
//
// GitHub API docs: https://docs.github.com/en/rest/code-scanning
type CodeScanningAlertState struct {
// State sets the state of the code scanning alert and is a required field.
// You must also provide DismissedReason when you set the state to "dismissed".
// State can be one of: "open", "dismissed".
State string `json:"state"`
// DismissedReason represents the reason for dismissing or closing the alert.
// It is required when the state is "dismissed".
// It can be one of: "false positive", "won't fix", "used in tests".
DismissedReason *string `json:"dismissed_reason,omitempty"`
// DismissedComment is associated with the dismissal of the alert.
DismissedComment *string `json:"dismissed_comment,omitempty"`
}

// SarifID identifies a sarif analysis upload.
//
// GitHub API docs: https://docs.github.com/en/rest/code-scanning
Expand Down Expand Up @@ -261,6 +277,31 @@ func (s *CodeScanningService) GetAlert(ctx context.Context, owner, repo string,
return a, resp, nil
}

// UpdateAlert updates the state of a single code scanning alert for a repository.
//
// 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.
//
// The security alert_id is the number at the end of the security alert's URL.
//
// GitHub API docs: https://docs.github.com/en/rest/code-scanning?apiVersion=2022-11-28#update-a-code-scanning-alert
func (s *CodeScanningService) UpdateAlert(ctx context.Context, owner, repo string, id int64, stateInfo *CodeScanningAlertState) (*Alert, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/code-scanning/alerts/%v", owner, repo, id)

req, err := s.client.NewRequest("PATCH", u, stateInfo)
if err != nil {
return nil, nil, err
}

a := new(Alert)
resp, err := s.client.Do(ctx, req, a)
if err != nil {
return nil, resp, err
}

return a, resp, nil
}

// UploadSarif uploads the result of code scanning job to GitHub.
//
// For the parameter sarif, you must first compress your SARIF file using gzip and then translate the contents of the file into a Base64 encoding string.
Expand Down
117 changes: 117 additions & 0 deletions github/code-scanning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,123 @@ func TestCodeScanningService_ListAlertsForRepo(t *testing.T) {
})
}

func TestCodeScanningService_UpdateAlert(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/code-scanning/alerts/88", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PATCH")
fmt.Fprint(w, `{"rule_id":"js/useless-expression",
"rule_severity":"warning",
"rule_description":"Expression has no effect",
"tool": {
"name": "CodeQL",
"guid": null,
"version": "1.4.0"
},
"rule": {
"id": "useless expression",
"severity": "warning",
"description": "Expression has no effect",
"name": "useless expression",
"full_description": "Expression has no effect",
"help": "Expression has no effect"
},
"most_recent_instance": {
"ref": "refs/heads/main",
"state": "dismissed",
"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":"2019-01-02T15:04:05Z",
"state":"dismissed",
"dismissed_reason": "false positive",
"dismissed_comment": "This alert is not actually correct as sanitizer is used",
"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"}`)
})

ctx := context.Background()
dismissedComment := String("This alert is not actually correct as sanitizer is used")
dismissedReason := String("false positive")
state := String("dismissed")
stateInfo := &CodeScanningAlertState{State: *state, DismissedReason: dismissedReason, DismissedComment: dismissedComment}
alert, _, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo)
if err != nil {
t.Errorf("CodeScanning.UpdateAlert returned error: %v", err)
}

date := Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}
want := &Alert{
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("useless expression"),
Severity: String("warning"),
Description: String("Expression has no effect"),
Name: String("useless expression"),
FullDescription: String("Expression has no effect"),
Help: String("Expression has no effect"),
},
CreatedAt: &date,
State: state,
DismissedReason: dismissedReason,
DismissedComment: dismissedComment,
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("dismissed"),
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(alert, want) {
t.Errorf("CodeScanning.UpdateAlert returned %+v, want %+v", alert, want)
}

const methodName = "UpdateAlert"
testBadOptions(t, methodName, func() (err error) {
_, _, err = client.CodeScanning.UpdateAlert(ctx, "\n", "\n", -88, stateInfo)
return err
})

testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}

func TestCodeScanningService_GetAlert(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
Expand Down
16 changes: 16 additions & 0 deletions github/github-accessors.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions github/github-accessors_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.