diff --git a/github/orgs_rules_test.go b/github/orgs_rules_test.go index 66cf6bd5c80..353705febad 100644 --- a/github/orgs_rules_test.go +++ b/github/orgs_rules_test.go @@ -202,6 +202,18 @@ func TestOrganizationsService_CreateOrganizationRuleset_RepoNames(t *testing.T) "operator": "contains", "pattern": "github" } + }, + { + "type": "code_scanning", + "parameters": { + "code_scanning_tools": [ + { + "tool": "CodeQL", + "security_alerts_threshold": "high_or_higher", + "alerts_threshold": "errors" + } + ] + } } ] }`) @@ -288,6 +300,15 @@ func TestOrganizationsService_CreateOrganizationRuleset_RepoNames(t *testing.T) Operator: "contains", Pattern: "github", }), + NewRequiredCodeScanningRule(&RequiredCodeScanningRuleParameters{ + RequiredCodeScanningTools: []*RuleRequiredCodeScanningTools{ + { + Tool: "CodeQL", + SecurityAlertsThreshold: "high_or_higher", + AlertsThreshold: "errors", + }, + }, + }), }, }) if err != nil { @@ -374,6 +395,15 @@ func TestOrganizationsService_CreateOrganizationRuleset_RepoNames(t *testing.T) Operator: "contains", Pattern: "github", }), + NewRequiredCodeScanningRule(&RequiredCodeScanningRuleParameters{ + RequiredCodeScanningTools: []*RuleRequiredCodeScanningTools{ + { + Tool: "CodeQL", + SecurityAlertsThreshold: "high_or_higher", + AlertsThreshold: "errors", + }, + }, + }), }, } if !cmp.Equal(ruleset, want) { @@ -524,6 +554,18 @@ func TestOrganizationsService_CreateOrganizationRuleset_RepoProperty(t *testing. "operator": "contains", "pattern": "github" } + }, + { + "type": "code_scanning", + "parameters": { + "code_scanning_tools": [ + { + "tool": "CodeQL", + "security_alerts_threshold": "high_or_higher", + "alerts_threshold": "errors" + } + ] + } } ] }`) @@ -617,6 +659,15 @@ func TestOrganizationsService_CreateOrganizationRuleset_RepoProperty(t *testing. Operator: "contains", Pattern: "github", }), + NewRequiredCodeScanningRule(&RequiredCodeScanningRuleParameters{ + RequiredCodeScanningTools: []*RuleRequiredCodeScanningTools{ + { + Tool: "CodeQL", + SecurityAlertsThreshold: "high_or_higher", + AlertsThreshold: "errors", + }, + }, + }), }, }) if err != nil { @@ -710,6 +761,15 @@ func TestOrganizationsService_CreateOrganizationRuleset_RepoProperty(t *testing. Operator: "contains", Pattern: "github", }), + NewRequiredCodeScanningRule(&RequiredCodeScanningRuleParameters{ + RequiredCodeScanningTools: []*RuleRequiredCodeScanningTools{ + { + Tool: "CodeQL", + SecurityAlertsThreshold: "high_or_higher", + AlertsThreshold: "errors", + }, + }, + }), }, } if !cmp.Equal(ruleset, want) { @@ -852,6 +912,18 @@ func TestOrganizationsService_CreateOrganizationRuleset_RepoIDs(t *testing.T) { "operator": "contains", "pattern": "github" } + }, + { + "type": "code_scanning", + "parameters": { + "code_scanning_tools": [ + { + "tool": "CodeQL", + "security_alerts_threshold": "high_or_higher", + "alerts_threshold": "errors" + } + ] + } } ] }`) @@ -936,6 +1008,15 @@ func TestOrganizationsService_CreateOrganizationRuleset_RepoIDs(t *testing.T) { Operator: "contains", Pattern: "github", }), + NewRequiredCodeScanningRule(&RequiredCodeScanningRuleParameters{ + RequiredCodeScanningTools: []*RuleRequiredCodeScanningTools{ + { + Tool: "CodeQL", + SecurityAlertsThreshold: "high_or_higher", + AlertsThreshold: "errors", + }, + }, + }), }, }) if err != nil { @@ -1020,6 +1101,15 @@ func TestOrganizationsService_CreateOrganizationRuleset_RepoIDs(t *testing.T) { Operator: "contains", Pattern: "github", }), + NewRequiredCodeScanningRule(&RequiredCodeScanningRuleParameters{ + RequiredCodeScanningTools: []*RuleRequiredCodeScanningTools{ + { + Tool: "CodeQL", + SecurityAlertsThreshold: "high_or_higher", + AlertsThreshold: "errors", + }, + }, + }), }, } if !cmp.Equal(ruleset, want) { diff --git a/github/repos_rules.go b/github/repos_rules.go index b812bd0b984..bb8aa121f81 100644 --- a/github/repos_rules.go +++ b/github/repos_rules.go @@ -159,6 +159,18 @@ type RequiredWorkflowsRuleParameters struct { RequiredWorkflows []*RuleRequiredWorkflow `json:"workflows"` } +// RuleRequiredCodeScanningTools represents the RequiredCodeScanningTools for the RequiredCodeScanningParameters object. +type RuleRequiredCodeScanningTools struct { + AlertsThreshold string `json:"alerts_threshold"` + SecurityAlertsThreshold string `json:"security_alerts_threshold"` + Tool string `json:"tool"` +} + +// RequiredCodeScanningRuleParameters represents the code_scanning rule parameters. +type RequiredCodeScanningRuleParameters struct { + RequiredCodeScanningTools []*RuleRequiredCodeScanningTools `json:"code_scanning_tools"` +} + // RepositoryRule represents a GitHub Rule. type RepositoryRule struct { Type string `json:"type"` @@ -491,6 +503,15 @@ func (r *RepositoryRule) UnmarshalJSON(data []byte) error { bytes, _ := json.Marshal(params) rawParams := json.RawMessage(bytes) + r.Parameters = &rawParams + case "code_scanning": + params := RequiredCodeScanningRuleParameters{} + if err := json.Unmarshal(*RepositoryRule.Parameters, ¶ms); err != nil { + return err + } + bytes, _ := json.Marshal(params) + rawParams := json.RawMessage(bytes) + r.Parameters = &rawParams case "max_file_path_length": params := RuleMaxFilePathLengthParameters{} @@ -705,6 +726,18 @@ func NewRequiredWorkflowsRule(params *RequiredWorkflowsRuleParameters) (rule *Re } } +// NewRequiredCodeScanningRule creates a rule to require which tools must provide code scanning results before the reference is updated. +func NewRequiredCodeScanningRule(params *RequiredCodeScanningRuleParameters) (rule *RepositoryRule) { + bytes, _ := json.Marshal(params) + + rawParams := json.RawMessage(bytes) + + return &RepositoryRule{ + Type: "code_scanning", + Parameters: &rawParams, + } +} + // NewFilePathRestrictionRule creates a rule to restrict file paths from being pushed to. func NewFilePathRestrictionRule(params *RuleFileParameters) (rule *RepositoryRule) { bytes, _ := json.Marshal(params) diff --git a/github/repos_rules_test.go b/github/repos_rules_test.go index e7faf7e7b2c..4af486aaa86 100644 --- a/github/repos_rules_test.go +++ b/github/repos_rules_test.go @@ -294,7 +294,7 @@ func TestRepositoryRule_UnmarshalJSON(t *testing.T) { }, wantErr: true, }, - "Required workflows params": { + "Valid Required workflows params": { data: `{"type":"workflows","parameters":{"workflows":[{"path": ".github/workflows/test.yml", "repository_id": 1}]}}`, want: NewRequiredWorkflowsRule(&RequiredWorkflowsRuleParameters{ RequiredWorkflows: []*RuleRequiredWorkflow{ @@ -305,6 +305,34 @@ func TestRepositoryRule_UnmarshalJSON(t *testing.T) { }, }), }, + "Invalid Required workflows params": { + data: `{"type":"workflows","parameters":{"workflows":[{"path": ".github/workflows/test.yml", "repository_id": "test"}]}}`, + want: &RepositoryRule{ + Type: "workflows", + Parameters: nil, + }, + wantErr: true, + }, + "Valid Required code_scanning params": { + data: `{"type":"code_scanning","parameters":{"code_scanning_tools":[{"tool": "CodeQL", "security_alerts_threshold": "high_or_higher", "alerts_threshold": "errors"}]}}`, + want: NewRequiredCodeScanningRule(&RequiredCodeScanningRuleParameters{ + RequiredCodeScanningTools: []*RuleRequiredCodeScanningTools{ + { + Tool: "CodeQL", + SecurityAlertsThreshold: "high_or_higher", + AlertsThreshold: "errors", + }, + }, + }), + }, + "Invalid Required code_scanning params": { + data: `{"type":"code_scanning","parameters":{"code_scanning_tools":[{"tool": 1}]}}`, + want: &RepositoryRule{ + Type: "code_scanning", + Parameters: nil, + }, + wantErr: true, + }, "Invalid type": { data: `{"type":"unknown"}`, want: &RepositoryRule{