Skip to content
Open
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
2 changes: 0 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,13 @@ linters:
# TODO: fix and remove these exceptions.
body-allowed-pointer-types:
- AuthorizationRequest
- CodeScanningAlertState
- ConfigApplyOptions
- ConfigSettings
- CreateOrUpdateIssueTypesOptions
- CreateUpdateEnvironment
- CreateWorkflowDispatchEventRequest
- CustomDeploymentProtectionRuleRequest
- CustomProperty
- DependabotAlertState
- EncryptedSecret
- EnterpriseSecurityAnalysisSettings
- Hook
Expand Down
63 changes: 36 additions & 27 deletions github/code_scanning.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,28 +71,31 @@ type Tool struct {
//
// GitHub API docs: https://docs.github.com/rest/code-scanning?apiVersion=2022-11-28
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"`
Rule *Rule `json:"rule,omitempty"`
Tool *Tool `json:"tool,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
FixedAt *Timestamp `json:"fixed_at,omitempty"`
State *string `json:"state,omitempty"`
ClosedBy *User `json:"closed_by,omitempty"`
ClosedAt *Timestamp `json:"closed_at,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
MostRecentInstance *MostRecentInstance `json:"most_recent_instance,omitempty"`
Instances []*MostRecentInstance `json:"instances,omitempty"`
DismissedBy *User `json:"dismissed_by,omitempty"`
DismissedAt *Timestamp `json:"dismissed_at,omitempty"`
DismissedReason *string `json:"dismissed_reason,omitempty"`
DismissedComment *string `json:"dismissed_comment,omitempty"`
InstancesURL *string `json:"instances_url,omitempty"`
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"`
Rule *Rule `json:"rule,omitempty"`
Tool *Tool `json:"tool,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
FixedAt *Timestamp `json:"fixed_at,omitempty"`
State *string `json:"state,omitempty"`
ClosedBy *User `json:"closed_by,omitempty"`
ClosedAt *Timestamp `json:"closed_at,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
MostRecentInstance *MostRecentInstance `json:"most_recent_instance,omitempty"`
Instances []*MostRecentInstance `json:"instances,omitempty"`
DismissedBy *User `json:"dismissed_by,omitempty"`
DismissedAt *Timestamp `json:"dismissed_at,omitempty"`
DismissedReason *string `json:"dismissed_reason,omitempty"`
DismissedComment *string `json:"dismissed_comment,omitempty"`
DismissalApprovedBy *User `json:"dismissal_approved_by,omitempty"`
InstancesURL *string `json:"instances_url,omitempty"`
Assignees []*User `json:"assignees,omitempty"`
LinkedPullRequests []*PullRequest `json:"linked_pull_requests,omitempty"`
}

// ID returns the ID associated with an alert. It is the number at the end of the security alert's URL.
Expand Down Expand Up @@ -218,20 +221,26 @@ type SarifAnalysis struct {
Validate *bool `json:"validate,omitempty"`
}

// CodeScanningAlertState specifies the state of a code scanning alert.
// UpdateCodeScanningAlertRequest represents a request to update a code scanning alert.
//
// GitHub API docs: https://docs.github.com/rest/code-scanning?apiVersion=2022-11-28
type CodeScanningAlertState struct {
// State sets the state of the code scanning alert and is a required field.
type UpdateCodeScanningAlertRequest struct {
// State sets the state of the code scanning alert.
// Either State or Assignees must be provided.
// You must also provide DismissedReason when you set the state to "dismissed".
// State can be one of: "open", "dismissed".
State string `json:"state"`
State *string `json:"state,omitempty"`
// 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"`
// CreateRequest, if true, attempts to create an alert dismissal request.
CreateRequest *bool `json:"create_request,omitempty"`
// Assignees replaces the users assigned to the alert.
// Either State or Assignees must be provided. An empty slice removes all assignees.
Assignees []string `json:"assignees,omitzero"`
}

// SarifID identifies a sarif analysis upload.
Expand Down Expand Up @@ -338,7 +347,7 @@ func (s *CodeScanningService) GetAlert(ctx context.Context, owner, repo string,
// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#update-a-code-scanning-alert
//
//meta:operation PATCH /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}
func (s *CodeScanningService) UpdateAlert(ctx context.Context, owner, repo string, id int64, body *CodeScanningAlertState) (*Alert, *Response, error) {
func (s *CodeScanningService) UpdateAlert(ctx context.Context, owner, repo string, id int64, body UpdateCodeScanningAlertRequest) (*Alert, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/code-scanning/alerts/%v", owner, repo, id)

req, err := s.client.NewRequest(ctx, "PATCH", u, body)
Expand Down
29 changes: 25 additions & 4 deletions github/code_scanning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,8 +709,8 @@ func TestCodeScanningService_UpdateAlert(t *testing.T) {
dismissedComment := new("This alert is not actually correct as sanitizer is used")
dismissedReason := new("false positive")
state := new("dismissed")
stateInfo := &CodeScanningAlertState{State: *state, DismissedReason: dismissedReason, DismissedComment: dismissedComment}
alert, _, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo)
body := UpdateCodeScanningAlertRequest{State: state, DismissedReason: dismissedReason, DismissedComment: dismissedComment}
alert, _, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, body)
if err != nil {
t.Errorf("CodeScanning.UpdateAlert returned error: %v", err)
}
Expand Down Expand Up @@ -759,19 +759,40 @@ func TestCodeScanningService_UpdateAlert(t *testing.T) {

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

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

func TestUpdateCodeScanningAlertRequest_Marshal(t *testing.T) {
t.Parallel()

dismissInput := UpdateCodeScanningAlertRequest{
State: new("dismissed"),
DismissedReason: new("false positive"),
CreateRequest: new(true),
}
testJSONMarshal(t, dismissInput, `{"state":"dismissed","dismissed_reason":"false positive","create_request":true}`)

assignInput := UpdateCodeScanningAlertRequest{
Assignees: []string{"octocat"},
}
testJSONMarshal(t, assignInput, `{"assignees":["octocat"]}`)

unassignInput := UpdateCodeScanningAlertRequest{
Assignees: []string{},
}
testJSONMarshal(t, unassignInput, `{"assignees":[]}`)
}

func TestCodeScanningService_ListAlertInstances(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)
Expand Down
62 changes: 43 additions & 19 deletions github/dependabot_alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,34 +67,58 @@ type DependabotSecurityAdvisory struct {

// DependabotAlert represents a Dependabot alert.
type DependabotAlert struct {
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Dependency *Dependency `json:"dependency,omitempty"`
SecurityAdvisory *DependabotSecurityAdvisory `json:"security_advisory,omitempty"`
SecurityVulnerability *AdvisoryVulnerability `json:"security_vulnerability,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
DismissedAt *Timestamp `json:"dismissed_at,omitempty"`
DismissedBy *User `json:"dismissed_by,omitempty"`
DismissedReason *string `json:"dismissed_reason,omitempty"`
DismissedComment *string `json:"dismissed_comment,omitempty"`
FixedAt *Timestamp `json:"fixed_at,omitempty"`
AutoDismissedAt *Timestamp `json:"auto_dismissed_at,omitempty"`
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Dependency *Dependency `json:"dependency,omitempty"`
SecurityAdvisory *DependabotSecurityAdvisory `json:"security_advisory,omitempty"`
SecurityVulnerability *AdvisoryVulnerability `json:"security_vulnerability,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
DismissedAt *Timestamp `json:"dismissed_at,omitempty"`
DismissedBy *User `json:"dismissed_by,omitempty"`
DismissedReason *string `json:"dismissed_reason,omitempty"`
DismissedComment *string `json:"dismissed_comment,omitempty"`
FixedAt *Timestamp `json:"fixed_at,omitempty"`
AutoDismissedAt *Timestamp `json:"auto_dismissed_at,omitempty"`
Assignees []*User `json:"assignees,omitempty"`
DismissalRequest *DependabotAlertDismissalRequest `json:"dismissal_request,omitempty"`
// The repository is always empty for events
Repository *Repository `json:"repository,omitempty"`
}

// DependabotAlertState represents the state of a Dependabot alert to update.
type DependabotAlertState struct {
// DependabotAlertDismissalRequest represents a request to dismiss a Dependabot alert.
type DependabotAlertDismissalRequest struct {
ID *int64 `json:"id,omitempty"`
Status *string `json:"status,omitempty"`
Requester *User `json:"requester,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
URL *string `json:"url,omitempty"`
}

// UpdateDependabotAlertRequest represents a request to update a Dependabot alert.
type UpdateDependabotAlertRequest struct {
// The state of the Dependabot alert. A dismissed_reason must be provided when setting the state to dismissed.
State string `json:"state"`
// Either State or Assignees must be provided.
State *string `json:"state,omitempty"`
// Required when state is dismissed. A reason for dismissing the alert.
// Can be one of: fix_started, inaccurate, no_bandwidth, not_used, tolerable_risk
DismissedReason *string `json:"dismissed_reason,omitempty"`
// An optional comment associated with dismissing the alert.
DismissedComment *string `json:"dismissed_comment,omitempty"`
// Usernames to assign to the alert, replacing the current assignees.
// Either State or Assignees must be provided. An empty slice removes all assignees.
Assignees []string `json:"assignees,omitzero"`
// Parameters for AI agent assignment. Only used when an agent bot login is included in Assignees.
AgentAssignment *DependabotAlertAgentAssignment `json:"agent_assignment,omitempty"`
}

// DependabotAlertAgentAssignment represents the parameters for assigning an AI agent to a Dependabot alert.
type DependabotAlertAgentAssignment struct {
CustomInstructions *string `json:"custom_instructions,omitempty"`
CustomAgent *string `json:"custom_agent,omitempty"`
Model *string `json:"model,omitempty"`
}

// ListAlertsOptions specifies the optional parameters to the DependabotService.ListRepoAlerts
Expand Down Expand Up @@ -178,7 +202,7 @@ func (s *DependabotService) GetRepoAlert(ctx context.Context, owner, repo string
// GitHub API docs: https://docs.github.com/rest/dependabot/alerts?apiVersion=2022-11-28#update-a-dependabot-alert
//
//meta:operation PATCH /repos/{owner}/{repo}/dependabot/alerts/{alert_number}
func (s *DependabotService) UpdateAlert(ctx context.Context, owner, repo string, number int, body *DependabotAlertState) (*DependabotAlert, *Response, error) {
func (s *DependabotService) UpdateAlert(ctx context.Context, owner, repo string, number int, body UpdateDependabotAlertRequest) (*DependabotAlert, *Response, error) {
url := fmt.Sprintf("repos/%v/%v/dependabot/alerts/%v", owner, repo, number)
req, err := s.client.NewRequest(ctx, "PATCH", url, body)
if err != nil {
Expand Down
32 changes: 28 additions & 4 deletions github/dependabot_alerts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,15 @@ func TestDependabotService_UpdateAlert(t *testing.T) {
dismissedReason := new("no_bandwidth")
dismissedComment := new("no time to fix this")

alertState := &DependabotAlertState{State: *state, DismissedReason: dismissedReason, DismissedComment: dismissedComment}
body := UpdateDependabotAlertRequest{State: state, DismissedReason: dismissedReason, DismissedComment: dismissedComment}

mux.HandleFunc("/repos/o/r/dependabot/alerts/42", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PATCH")
fmt.Fprint(w, `{"number":42,"state":"dismissed","dismissed_reason":"no_bandwidth","dismissed_comment":"no time to fix this"}`)
})

ctx := t.Context()
alert, _, err := client.Dependabot.UpdateAlert(ctx, "o", "r", 42, alertState)
alert, _, err := client.Dependabot.UpdateAlert(ctx, "o", "r", 42, body)
if err != nil {
t.Errorf("Dependabot.UpdateAlert returned error: %v", err)
}
Expand All @@ -164,15 +164,39 @@ func TestDependabotService_UpdateAlert(t *testing.T) {

const methodName = "UpdateAlert"
testBadOptions(t, methodName, func() (err error) {
_, _, err = client.Dependabot.UpdateAlert(ctx, "\n", "\n", 0, alertState)
_, _, err = client.Dependabot.UpdateAlert(ctx, "\n", "\n", 0, body)
return err
})

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

func TestUpdateDependabotAlertRequest_Marshal(t *testing.T) {
t.Parallel()

dismissInput := UpdateDependabotAlertRequest{
State: new("dismissed"),
DismissedReason: new("no_bandwidth"),
}
testJSONMarshal(t, dismissInput, `{"state":"dismissed","dismissed_reason":"no_bandwidth"}`)

assignInput := UpdateDependabotAlertRequest{
Assignees: []string{"copilot-swe-agent[bot]"},
AgentAssignment: &DependabotAlertAgentAssignment{
CustomInstructions: new("Upgrade the dependency"),
Model: new("m"),
},
}
testJSONMarshal(t, assignInput, `{"assignees":["copilot-swe-agent[bot]"],"agent_assignment":{"custom_instructions":"Upgrade the dependency","model":"m"}}`)

unassignInput := UpdateDependabotAlertRequest{
Assignees: []string{},
}
testJSONMarshal(t, unassignInput, `{"assignees":[]}`)
}
Loading
Loading