Skip to content

Commit

Permalink
policy: Add Regal for linting Rego (#257)
Browse files Browse the repository at this point in the history
This PR introduces [Regal](https://github.com/styrainc/regal) for linting
the Rego files of the project. The Rego found here is generally in a good
shape, so only a few rules have been either ignored or fixed. The few rules
fixed include:
* [rule-shadows-builtin](https://docs.styra.com/regal/rules/bugs/rule-shadows-builtin)
  as there is already an `is_null` function in the standard lib
* [custom-has-key-construct](https://docs.styra.com/regal/rules/idiomatic/custom-has-key-construct)
  as this is handled by `object.keys` and `in` in modern Rego
* [use-assignment-operator](https://docs.styra.com/regal/rules/style/use-assignment-operator)
  as `:=` is preferred over `=` for rule assignment, and using this does
  not change the semantics of the code in any way.

Included is also a new job for checking/linting Rego code as part of CI.

Signed-off-by: Anders Eknert <anders@styra.com>
  • Loading branch information
anderseknert committed Oct 31, 2023
1 parent cd0c200 commit 72b5bd3
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 201 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/build_and_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,16 @@ jobs:
run: go test -count=1 -shuffle=on -timeout=10m -race $(go list ./... | grep -vE '^github.com/Legit-Labs/legitify/e2e')
- name: Vet
run: go vet -v ./...
check_rego:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: open-policy-agent/setup-opa@9501844990f7dcfd09b17a8d97c794d294620f37 # v2.1.0
with:
version: v0.55.0
- uses: styrainc/setup-regal@94ad2891f53efdb7ebe7c6836bc25ecc9504aec1 # v0.2.0
with:
version: v0.11.0
- run: opa check --strict policies/github
- run: opa check --strict policies/gitlab
- run: regal lint --format=github policies
23 changes: 23 additions & 0 deletions .regal/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
rules:
idiomatic:
no-defined-entrypoint:
# OK to ignore for libraries
level: ignore
style:
file-length:
# Override default of 500 as one file has 515 lines
max-file-length: 550
line-length:
# Violations here mostly from metadata annotation values.
# These could be fixed by using |> and newlines, but we'll
# ignore this for now.
level: ignore
opa-fmt:
# Would mostly changes spaces -> tabs. Safe to ignore.
level: ignore
prefer-snake-case:
# Only a few violations here, so this would be easy to fix.
level: ignore
prefer-some-in-iteration:
# This is mostly a style preference, so safe to ignore.
level: ignore
16 changes: 8 additions & 8 deletions policies/github/actions.rego
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ package actions
# - "4. Attacker trigger the workflow"
# - "5. Attacker receives all organization secrets and uses them maliciously"
# requiredScopes: [admin:org]
default all_repositories_can_run_github_actions = true
default all_repositories_can_run_github_actions := true

all_repositories_can_run_github_actions = false {
all_repositories_can_run_github_actions := false {
input.actions_permissions.enabled_repositories != "all"
}

Expand All @@ -37,9 +37,9 @@ all_repositories_can_run_github_actions = false {
# - "1. Attacker creates a repository with a tempting but malicious custom GitHub Action"
# - "2. An innocent developer / DevOps engineer uses this malicious action"
# - "3. The malicious action has access to the developer repository and could steal its secrets or modify its content"
default all_github_actions_are_allowed = true
default all_github_actions_are_allowed := true

all_github_actions_are_allowed = false {
all_github_actions_are_allowed := false {
input.actions_permissions.allowed_actions != "all"
}

Expand All @@ -59,9 +59,9 @@ all_github_actions_are_allowed = false {
# severity: MEDIUM
# requiredScopes: [admin:org]
# threat: In case of token compromise (due to a vulnerability or malicious third-party GitHub actions), an attacker can use this token to sabotage various assets in your CI/CD pipeline, such as packages, pull-requests, deployments, and more.
default token_default_permissions_is_read_write = true
default token_default_permissions_is_read_write := true

token_default_permissions_is_read_write = false {
token_default_permissions_is_read_write := false {
input.token_permissions.default_workflow_permissions == "read"
}

Expand All @@ -81,8 +81,8 @@ token_default_permissions_is_read_write = false {
# severity: HIGH
# requiredScopes: [admin:org]
# threat: Attackers can exploit this misconfiguration to bypass code-review restrictions by creating a workflow that approves their own pull request and then merging the pull request without anyone noticing, introducing malicious code that would go straight ahead to production.
default actions_can_approve_pull_requests = true
default actions_can_approve_pull_requests := true

actions_can_approve_pull_requests = false {
actions_can_approve_pull_requests := false {
not input.token_permissions.can_approve_pull_request_reviews
}
10 changes: 4 additions & 6 deletions policies/github/common/webhooks.rego
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package common.webhooks

has_key(x, k) {
_ = x[k]
}
import future.keywords.in

ssl_enabled(hook) {
has_key(hook.config, "insecure_ssl")
"insecure_ssl" in object.keys(hook.config)
hook.config.insecure_ssl == "0"
}

has_secret(hook) {
has_key(hook.config, "secret")
}
"secret" in object.keys(hook.config)
}
46 changes: 23 additions & 23 deletions policies/github/enterprise.rego
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ package enterprise
# requiredScopes: [admin:enterprise]
# threat:
# - "A member of the organization could inadvertently or maliciously make public an internal repository exposing confidential data."
default enterprise_not_using_visibility_change_disable_policy = true
default enterprise_not_using_visibility_change_disable_policy := true

enterprise_not_using_visibility_change_disable_policy = false {
enterprise_not_using_visibility_change_disable_policy := false {
input.members_can_change_repository_visibility == "DISABLED"
}

Expand All @@ -27,9 +27,9 @@ enterprise_not_using_visibility_change_disable_policy = false {
# requiredScopes: [admin:enterprise]
# threat:
# - Forking to external namespaces could result in loss of control over proprietary information and potentially expose the organization to security risks, such as data leaks.
default enterprise_allows_forking_repos = true
default enterprise_allows_forking_repos := true

enterprise_allows_forking_repos = false {
enterprise_allows_forking_repos := false {
input.repositories_forking_policy == "DISABLED"
}

Expand All @@ -44,9 +44,9 @@ enterprise_allows_forking_repos = false {
# requiredScopes: [admin:enterprise]
# threat:
# - Users can accidentaly create public repositories and expose source code.
default enterprise_allows_creating_public_repos = true
default enterprise_allows_creating_public_repos := true

enterprise_allows_creating_public_repos = false {
enterprise_allows_creating_public_repos := false {
input.members_can_create_public_repositories == false
}

Expand All @@ -61,9 +61,9 @@ enterprise_allows_creating_public_repos = false {
# requiredScopes: [admin:enterprise]
# threat:
# - Inviting external collaborators could result in a loss of control over proprietary information and potentially expose the organization to security risks, such as data leaks.
default enterprise_allows_inviting_externals_collaborators = true
default enterprise_allows_inviting_externals_collaborators := true

enterprise_allows_inviting_externals_collaborators = false {
enterprise_allows_inviting_externals_collaborators := false {
input.external_collaborators_invite_policy == "DISABLED"
}

Expand All @@ -77,9 +77,9 @@ enterprise_allows_inviting_externals_collaborators = false {
# requiredScopes: [admin:enterprise]
# threat:
# - If an attacker gets the valid credentials for one of the enterprise’s users they can authenticate to your GitHub enterprise.
default enterprise_enforce_two_factor_authentication = true
default enterprise_enforce_two_factor_authentication := true

enterprise_enforce_two_factor_authentication = false {
enterprise_enforce_two_factor_authentication := false {
input.two_factor_required_setting == "ENABLED"
}

Expand All @@ -93,24 +93,24 @@ enterprise_enforce_two_factor_authentication = false {
# remediationSteps: [Make sure you are an enterprise owner, Go to the Settings page, Go to the Authentication security tab, Toggle on "Enable SAML authentication", Fill in the remaining SSO configuration as instructed on the screen, Click "Save"]
# requiredScopes: [admin:enterprise]
# threat: Not using an SSO solution makes it more difficult to track a potentially compromised user's actions across different systems, prevents common password policy throughout the enterprise, and makes it challenging to audit different aspects of the user's behavior.
default enterprise_not_using_single_sign_on = true
default enterprise_not_using_single_sign_on := true

enterprise_not_using_single_sign_on = false {
enterprise_not_using_single_sign_on := false {
input.saml_enabled
}

# METADATA
# scope: rule
# title: Enterprise Should Define Base Permissions As “No Permission” For All Members
# title: Enterprise Should Define Base Permissions As “No Permission” For All Members
# description: Collaborators in your organizations should receive access to specific organizations and repositories as necessary, and not have read and write access to all repositories across the enterprise.
# custom:
# severity: MEDIUM
# remediationSteps: [Make sure you are an enterprise owner, Go to the Settings page, Under the ‘Policies’ tab, choose ‘Repositories’, Under ‘Base Permission’ choose ‘No Permission’]
# requiredScopes: [admin:enterprise]
# threat: An adversary will have access to all repositories in the enterprise, instead of just a part of them.
default repository_no_permission_enforced_by_default = true
default repository_no_permission_enforced_by_default := true

repository_no_permission_enforced_by_default = false {
repository_no_permission_enforced_by_default := false {
input.default_repository_no_permission_enforced == "NONE"
}

Expand All @@ -123,9 +123,9 @@ repository_no_permission_enforced_by_default = false {
# remediationSteps: [Make sure you are an enterprise owner, Go to the Enterprise Settings page, Under the ‘Policies’ tab choose ‘Repositories’, Go to the ‘Admin repository permissions' section, under ‘Repository deletion and transfer' and select 'Disabled']
# requiredScopes: [admin:enterprise]
# threat: A member of the organization could inadvertently or maliciously transfer a repository to an external namespace and expose confidential data.
default memberes_allowed_repository_move_or_deletion = true
default memberes_allowed_repository_move_or_deletion := true

memberes_allowed_repository_move_or_deletion = false {
memberes_allowed_repository_move_or_deletion := false {
input.member_can_delete_repository == "DISABLED"
}

Expand All @@ -138,9 +138,9 @@ memberes_allowed_repository_move_or_deletion = false {
# custom:
# remediationSteps: [Make sure you are an enterprise owner, Go to the Enterprise Settings page, Under the ‘Settings’ tab choose ‘Code security and analysis’, Check 'Automatically enable for new repositories']
# requiredScopes: [admin:enterprise]
default enable_ghas_for_new_orgs = true
default enable_ghas_for_new_orgs := true

enable_ghas_for_new_orgs = false {
enable_ghas_for_new_orgs := false {
input.code_analysis_and_security_policies.advanced_security_enabled_for_new_repositories == true
}

Expand All @@ -152,9 +152,9 @@ enable_ghas_for_new_orgs = false {
# severity: MEDIUM
# remediationSteps: [Make sure you are an enterprise owner, Go to the Enterprise Settings page, Under the ‘Settings’ tab choose ‘Code security and analysis’, Check 'Automatically enable for new repositories with Advanced Security enabled']
# requiredScopes: [admin:enterprise]
default enable_secret_scanning_for_new_orgs = true
default enable_secret_scanning_for_new_orgs := true

enable_secret_scanning_for_new_orgs = false {
enable_secret_scanning_for_new_orgs := false {
input.code_analysis_and_security_policies.secret_scanning_enabled_for_new_repositories == true
}

Expand All @@ -166,8 +166,8 @@ enable_secret_scanning_for_new_orgs = false {
# severity: MEDIUM
# remediationSteps: [Make sure you are an enterprise owner, Go to the Enterprise Settings page, Under the ‘Settings’ tab choose ‘Code security and analysis’, Check 'Automatically enable for repositories added to secret scanning']
# requiredScopes: [admin:enterprise]
default enable_push_protection_secret_scanning = true
default enable_push_protection_secret_scanning := true

enable_push_protection_secret_scanning = false {
enable_push_protection_secret_scanning := false {
input.code_analysis_and_security_policies.secret_scanning_push_protection_enabled_for_new_repositories == true
}
8 changes: 4 additions & 4 deletions policies/github/member.rego
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ package member
# - "1. An organization has a permissive attitude and provides an owner role to all developers."
# - "2. One of the developers has decided to collaborate with an evil ransomware gang, and uses his high privileges to add a malicious external collaborator"
# - "3. The malicious collaborator, being an owner, has a wide range of destructive operations he can do (e.g. remove security settings)"
default organization_has_too_many_admins = true
default organization_has_too_many_admins := true

organization_has_too_many_admins = false {
organization_has_too_many_admins := false {
admins := count({member | member := input.members[_]; member.is_admin == true})
admins <= 3
}
Expand All @@ -31,7 +31,7 @@ organization_has_too_many_admins = false {
# prerequisites: [premium]
# threat:
# - "Stale members are most likely not managed and monitored, increasing the possibility of being compromised."
stale_member_found[mem] = true {
stale_member_found[mem] := true {
some member
mem := input.members[member]
mem.is_admin == false
Expand All @@ -51,7 +51,7 @@ stale_member_found[mem] = true {
# prerequisites: [premium]
# threat:
# - "Stale admins are most likely not managed and monitored, increasing the possibility of being compromised."
stale_admin_found[mem] = true {
stale_admin_found[mem] := true {
some member
mem := input.members[member]
mem.is_admin == true
Expand Down
22 changes: 11 additions & 11 deletions policies/github/organization.rego
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import data.common.webhooks as webhookUtils
# severity: LOW
# remediationSteps: [Make sure you have admin permissions, Go to the organization settings page, Select "Webhooks", Press on the insecure webhook, Configure a secret , Click "Update webhook"]
# requiredScopes: [admin:org_hook]
# threat:
# threat:
# - "Not using a webhook secret makes the service receiving the webhook unable to determine the authenticity of the request."
# - "This allows attackers to masquerade as your organization, potentially creating an unstable or insecure state in other systems."
organization_webhook_no_secret[violated] = true {
organization_webhook_no_secret[violated] := true {
some index
hook := input.hooks[index]
not webhookUtils.has_secret(hook)
Expand All @@ -36,7 +36,7 @@ organization_webhook_no_secret[violated] = true {
# threat:
# - "If SSL verification is disabled, any party with access to the target DNS domain can masquerade as your designated payload URL, allowing it freely read and affect the response of any webhook request."
# - "In the case of GitHub Enterprise Server instances, it may be sufficient only to control the DNS configuration of the network where the instance is deployed, as an attacker can redirect traffic to the target domain in your internal network directly to them, and this is often much easier than compromising an internet-facing domain."
organization_webhook_doesnt_require_ssl[violated] = true {
organization_webhook_doesnt_require_ssl[violated] := true {
some index
hook := input.hooks[index]
not webhookUtils.ssl_enabled(hook)
Expand All @@ -56,9 +56,9 @@ organization_webhook_doesnt_require_ssl[violated] = true {
# requiredScopes: [admin:org]
# threat:
# - If an attacker gets the valid credentials for one of the organization’s users they can authenticate to your GitHub organization.
default two_factor_authentication_not_required_for_org = true
default two_factor_authentication_not_required_for_org := true

two_factor_authentication_not_required_for_org = false {
two_factor_authentication_not_required_for_org := false {
input.organization.two_factor_requirement_enabled
}

Expand All @@ -73,9 +73,9 @@ two_factor_authentication_not_required_for_org = false {
# requiredScopes: [read:org]
# threat:
# - "A member of the organization could inadvertently or maliciously make public an internal repository exposing confidential data."
default non_admins_can_create_public_repositories = true
default non_admins_can_create_public_repositories := true

non_admins_can_create_public_repositories = false {
non_admins_can_create_public_repositories := false {
not input.organization.members_can_create_public_repositories
}

Expand All @@ -89,9 +89,9 @@ non_admins_can_create_public_repositories = false {
# requiredScopes: [read:enterprise]
# threat:
# - "Organization members can see the content of freshly created repositories, even if they should be restricted."
default default_repository_permission_is_not_none = true
default default_repository_permission_is_not_none := true

default_repository_permission_is_not_none = false {
default_repository_permission_is_not_none := false {
input.organization.default_repository_permission == "none"
}

Expand All @@ -105,8 +105,8 @@ default_repository_permission_is_not_none = false {
# remediationSteps: [Make sure you have admin permissions, Go to the organization settings page, Enter "Authentication security" tab, Toggle on "Enable SAML authentication", Fill in the remaining SSO configuration as instructed on the screen, Click "Save"]
# requiredScopes: [admin:org]
# threat: Not using an SSO solution makes it more difficult to track a potentially compromised user's actions accross different systems, prevents the organization from defining a common password policy, and makes it challenging to audit different aspects of the user's behavior.
default organization_not_using_single_sign_on = true
default organization_not_using_single_sign_on := true

organization_not_using_single_sign_on = false {
organization_not_using_single_sign_on := false {
input.saml_enabled
}
Loading

0 comments on commit 72b5bd3

Please sign in to comment.