diff --git a/go.mod b/go.mod index 62137fa5..98e42757 100644 --- a/go.mod +++ b/go.mod @@ -70,10 +70,11 @@ require ( github.com/golang-jwt/jwt/v4 v4.4.3 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-containerregistry v0.12.1 // indirect github.com/google/go-github/v38 v38.1.0 // indirect github.com/google/go-github/v45 v45.2.0 // indirect + github.com/google/go-github/v57 v57.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/osv-scanner v1.2.1-0.20230302232134-592acbc2539b // indirect github.com/google/uuid v1.3.0 // indirect diff --git a/go.sum b/go.sum index 6e3cfe4e..4eb66ed8 100644 --- a/go.sum +++ b/go.sum @@ -1164,6 +1164,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= github.com/google/go-containerregistry v0.12.1 h1:W1mzdNUTx4Zla4JaixCRLhORcR7G6KxE5hHl5fkPsp8= github.com/google/go-containerregistry v0.12.1/go.mod h1:sdIK+oHQO7B93xI8UweYdl887YhuIwg9vz8BSLH3+8k= @@ -1173,6 +1175,8 @@ github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FC github.com/google/go-github/v45 v45.2.0/go.mod h1:FObaZJEDSTa/WGCzZ2Z3eoCDXWJKMenWWTrd8jrta28= github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI= github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao= +github.com/google/go-github/v57 v57.0.0 h1:L+Y3UPTY8ALM8x+TV0lg+IEBI+upibemtBD8Q9u7zHs= +github.com/google/go-github/v57 v57.0.0/go.mod h1:s0omdnye0hvK/ecLvpsGfJMiRt85PimQh4oygmLIxHw= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= diff --git a/internal/clients/github/client.go b/internal/clients/github/client.go index 50934438..8cccf49c 100644 --- a/internal/clients/github/client.go +++ b/internal/clients/github/client.go @@ -599,12 +599,23 @@ func (c *Client) GetRulesForBranch(organization, repository, branch string) ([]* return nil, err } - var p []*types.RepositoryRule - _, err = c.client.Do(c.context, req, &p) + var rules []*types.RepositoryRule + _, err = c.client.Do(c.context, req, &rules) if err != nil { return nil, err } - return p, nil + + for _, rule := range rules { + specific, _, err := c.Client().Repositories.GetRuleset(c.context, organization, repository, rule.Id, true) + if err != nil { + continue + } + + rule.Ruleset = specific + } + + return rules, nil + } func (c *Client) GetSecurityAndAnalysisForEnterprise(enterprise string) (*types.AnalysisAndSecurityPolicies, error) { diff --git a/internal/clients/github/types/types.go b/internal/clients/github/types/types.go index 1d7762b0..f8d01a78 100644 --- a/internal/clients/github/types/types.go +++ b/internal/clients/github/types/types.go @@ -1,6 +1,9 @@ package types -import "encoding/json" +import ( + "encoding/json" + "github.com/google/go-github/v53/github" +) type TokenPermissions struct { DefaultWorkflowPermissions *string `json:"default_workflow_permissions,omitempty"` @@ -10,6 +13,8 @@ type TokenPermissions struct { type RepositoryRule struct { Type string `json:"type"` Parameters *json.RawMessage `json:"parameters,omitempty"` + Id int64 `json:"ruleset_id"` + Ruleset *github.Ruleset `json:"ruleset"` } type AnalysisAndSecurityPolicies struct { @@ -18,4 +23,4 @@ type AnalysisAndSecurityPolicies struct { SecretScanningEnabledForNewRepositories bool `json:"secret_scanning_enabled_for_new_repositories"` SecretScanningPushProtectionEnabledForNewRepos bool `json:"secret_scanning_push_protection_enabled_for_new_repositories"` SecretScanningPushProtectionCustomLink string `json:"secret_scanning_push_protection_custom_link"` -} +} diff --git a/internal/collected/github/repository.go b/internal/collected/github/repository.go index f8c3ee61..970181ff 100644 --- a/internal/collected/github/repository.go +++ b/internal/collected/github/repository.go @@ -70,7 +70,7 @@ type Repository struct { Collaborators []*github.User `json:"collaborators,omitempty"` ActionsTokenPermissions *types.TokenPermissions `json:"actions_token_permissions"` DependencyGraphManifests *GitHubQLDependencyGraphManifests `json:"dependency_graph_manifests"` - RulesSet []*types.RepositoryRule `json:"rules_set"` + RulesSet []*types.RepositoryRule `json:"rules_set,omitempty"` } func (r Repository) ViolationEntityType() string { diff --git a/policies/github/repository.rego b/policies/github/repository.rego index 535ce039..47b31948 100644 --- a/policies/github/repository.rego +++ b/policies/github/repository.rego @@ -513,3 +513,29 @@ default actions_can_approve_pull_requests := true actions_can_approve_pull_requests := false{ not input.actions_token_permissions.can_approve_pull_request_reviews } + +# METADATA +# scope: rule +# title: Users Are Allowed To Bypass Ruleset Rules +# description: Rulesets rules are not enforced for some users. When defining rulesets it is recommended to make sure that no one is allowed to bypass these rules in order to avoid inadvertent or intentional alterations to critical code which can lead to potential errors or vulnerabilities in the software. +# custom: +# remediationSteps: +# - Go to the repository settings page +# - Under "Code and automation", select "Rules -> Rulesets" +# - Find the relevant ruleset +# - Empty the "Bypass list" +# - Press "Save Changes" +# severity: MEDIUM +# requiredScopes: [repo] +# threat: Attackers that gain access to a user that can bypass the ruleset rules can compromise the codebase without anyone noticing, introducing malicious code that would go straight ahead to production. +default users_allowed_to_bypass_ruleset := true + +users_allowed_to_bypass_ruleset := false { + count(input.rules_set) == 0 +} + +users_allowed_to_bypass_ruleset := false { + some index + rule := input.rules_set[index] + count(rule.ruleset.bypass_actors) == 0 +} \ No newline at end of file