diff --git a/.github/workflows/opa.yml b/.github/workflows/opa.yml index 143edf7..b5d6e5d 100644 --- a/.github/workflows/opa.yml +++ b/.github/workflows/opa.yml @@ -1,5 +1,6 @@ name: Run OPA Tests on: [push, workflow_dispatch] + jobs: Run-OPA-Tests: runs-on: ubuntu-latest @@ -12,5 +13,13 @@ jobs: with: version: latest + - name: Setup Regal + uses: StyraInc/setup-regal@v0.2.0 + with: + version: latest + - name: Run OPA Tests run: opa test src -v + + - name: Lint OPA Policies + run: regal lint --format github ./src diff --git a/.regal/config.yaml b/.regal/config.yaml new file mode 100644 index 0000000..ee64004 --- /dev/null +++ b/.regal/config.yaml @@ -0,0 +1,5 @@ +rules: + idiomatic: + no-defined-entrypoint: + # This repo consists of a set of library functions, which therefore have no entrypoint. + level: ignore diff --git a/src/abbey/functions/expire_after.rego b/src/abbey/functions/expire_after.rego index 76b32bf..578241d 100644 --- a/src/abbey/functions/expire_after.rego +++ b/src/abbey/functions/expire_after.rego @@ -1,16 +1,22 @@ -package abbey.functions +package abbey.functions_test import future.keywords.if -# Function that checks if the time at `ts` has expired, relative to the time at `approved_at`. -# The `ts` input is a string that can be parsed by Rego's native `time.parse_duration_ns` function. -# Valid string values are derived from https://pkg.go.dev/time#ParseDuration. -# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". -# This function compares against data under the `system.abbey.target` namespace. +# METADATA +# title: Expire After +# description: | +# Function that checks if the time at `ts` has expired, relative to the time at `approved_at`. +# The `ts` input is a string that can be parsed by Rego's native `time.parse_duration_ns` function. +# Valid string values are derived from https://pkg.go.dev/time#ParseDuration. +# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". +# This function compares against data under the `system.abbey.target` namespace. +# related_resources: +# - ref: https://docs.abbey.io/use-cases/time-based-access/expire-after-a-duration +# entrypoint: false expire_after(ts) := live if { expires_after := time.parse_duration_ns(ts) approved_at := time.parse_rfc3339_ns(data.system.abbey.target.grant.approved_at) expires_at := approved_at + expires_after now := time.now_ns() live := (now - expires_at) < 0 -} \ No newline at end of file +} diff --git a/src/abbey/functions/expire_after_test.rego b/src/abbey/functions/expire_after_test.rego index f058d9f..85addf6 100644 --- a/src/abbey/functions/expire_after_test.rego +++ b/src/abbey/functions/expire_after_test.rego @@ -1,18 +1,18 @@ -package abbey.functions +package abbey.functions_test import future.keywords.if -test_expired if { - not expire_after("1m") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" - with time.now_ns as 1672534900000000000 +test_after_expired_duration if { + not expire_after("1m") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" + with time.now_ns as 1672534900000000000 } -test_expired if { - not expire_after("1m") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" - with time.now_ns as 1672534860000000000 +test_at_expired_duration if { + not expire_after("1m") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" + with time.now_ns as 1672534860000000000 } -test_not_expired if { - expire_after("1m") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" - with time.now_ns as 0 +test_before_expired_duration if { + expire_after("1m") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" + with time.now_ns as 0 } diff --git a/src/abbey/functions/expire_at.rego b/src/abbey/functions/expire_at.rego index ad7f6fd..bececf6 100644 --- a/src/abbey/functions/expire_at.rego +++ b/src/abbey/functions/expire_at.rego @@ -1,10 +1,16 @@ -package abbey.functions +package abbey.functions_test import future.keywords.if -# Function that checks if the time at `ts` has expired, relative to the time at `approved_at`. -# The `ts` input is a string representing the RFC 3339 date time format. -# This function compares against data under the `system.abbey.target` namespace. +# METADATA +# title: Expire At +# description: | +# Function that checks if the time at `ts` has expired, relative to the time at `approved_at`. +# The `ts` input is a string representing the RFC 3339 date time format. +# This function compares against data under the `system.abbey.target` namespace. +# related_resources: +# - ref: https://docs.abbey.io/use-cases/time-based-access/expire-at-a-specific-time +# entrypoint: false expire_at(ts) := live if { expires_at := time.parse_rfc3339_ns(ts) now := time.now_ns() diff --git a/src/abbey/functions/expire_at_test.rego b/src/abbey/functions/expire_at_test.rego index a741d17..058117f 100644 --- a/src/abbey/functions/expire_at_test.rego +++ b/src/abbey/functions/expire_at_test.rego @@ -1,18 +1,18 @@ -package abbey.functions +package abbey.functions_test import future.keywords.if -test_expired if { - not expire_at("2023-01-01T02:00:00Z") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" - with time.now_ns as 1672538500000000000 +test_after_expired_at_threshold if { + not expire_at("2023-01-01T02:00:00Z") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" + with time.now_ns as 1672538500000000000 } -test_expired if { - not expire_at("2023-01-01T01:00:00Z") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" - with time.now_ns as 1672538400000000000 +test_on_expired_at_threshold if { + not expire_at("2023-01-01T01:00:00Z") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" + with time.now_ns as 1672538400000000000 } -test_not_expired if { - expire_at("2023-01-01T01:00:00Z") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" - with time.now_ns as 0 -} \ No newline at end of file +test_before_expired_at_threshold if { + expire_at("2023-01-01T01:00:00Z") with data.system.abbey.target.grant.approved_at as "2023-01-01T01:00:00Z" + with time.now_ns as 0 +} diff --git a/src/abbey/functions/has_attribute.rego b/src/abbey/functions/has_attribute.rego deleted file mode 100644 index d60d7ed..0000000 --- a/src/abbey/functions/has_attribute.rego +++ /dev/null @@ -1,10 +0,0 @@ -package abbey.functions - -import future.keywords.if - -# Function which checks whether a user has a given attribute. -# Attributes are found under a custom_attributes object under the -# abbey.identities object. -has_attribute(name, value) := true if { - data.system.abbey.identities.directory_sync_users.custom_attributes[name] == value -} diff --git a/src/abbey/functions/has_attribute_test.rego b/src/abbey/functions/has_attribute_test.rego deleted file mode 100644 index 8069927..0000000 --- a/src/abbey/functions/has_attribute_test.rego +++ /dev/null @@ -1,19 +0,0 @@ -package abbey.functions - -import future.keywords.if - -test_has_cost_center_engineering if { - has_attribute("cost_center_name", "Engineering") with data.system.abbey.identities.directory_sync_users.custom_attributes as {"department_name": "Engineering", "cost_center_name": "Engineering", "employee_type": "Manager"} -} - -test_has_department_engineering if { - has_attribute("department_name", "Engineering") with data.system.abbey.identities.directory_sync_users.custom_attributes as {"department_name": "Engineering", "cost_center_name": "Engineering", "employee_type": "Manager"} -} - -test_does_not_have_department_marketing if { - not has_attribute("department_name", "Marketing") with data.system.abbey.identities.directory_sync_users.custom_attributes as {"department_name": "Engineering", "cost_center_name": "Engineering", "employee_type": "Manager"} -} - -test_does_not_have_employee_type_ic if { - not has_attribute("employee_type", "IC") with data.system.abbey.identities.directory_sync_users.custom_attributes as {"department_name": "Engineering", "cost_center_name": "Engineering", "employee_type": "Manager"} -} diff --git a/src/abbey/functions/in_group.rego b/src/abbey/functions/in_group.rego deleted file mode 100644 index 0404df9..0000000 --- a/src/abbey/functions/in_group.rego +++ /dev/null @@ -1,11 +0,0 @@ -package abbey.functions - -import future.keywords.if -import future.keywords.in - -# Function which checks whether a user is in a given group. -# Groups are kept within an object called group_memberships in the -# system.abbey object -in_group(group_name) := true if { - group_name in data.system.abbey.group_memberships -} diff --git a/src/abbey/functions/in_group_test.rego b/src/abbey/functions/in_group_test.rego deleted file mode 100644 index 408059d..0000000 --- a/src/abbey/functions/in_group_test.rego +++ /dev/null @@ -1,11 +0,0 @@ -package abbey.functions - -import future.keywords.if - -test_in_group_engineering if { - in_group("Engineering") with data.system.abbey.group_memberships as ["Engineering", "R&D"] -} - -test_not_in_group_marketing if { - not in_group("Marketing") with data.system.abbey.group_memberships as ["Engineering", "R&D"] -} diff --git a/src/abbey/soc2/security/dcf_10/system_access_control.rego b/src/abbey/soc2/security/dcf_10/system_access_control.rego deleted file mode 100644 index 0b66de8..0000000 --- a/src/abbey/soc2/security/dcf_10/system_access_control.rego +++ /dev/null @@ -1,11 +0,0 @@ -# Requires annual access control reviews to be conducted and -# access request forms be filled out for new hires and employee transfers. -# -# Does the organization enforce controls related to the granting or revoking -# of access to sensitive systems and/or applications? -# -# 1. New hire checklist includes access request form -# 2. Employee role changes require new access request form -# -# Covers CC4.1, CC6.2, CC6.3. -package abbey.soc2.security.dcf_10 \ No newline at end of file diff --git a/src/abbey/soc2/security/dcf_2/least_privilege.rego b/src/abbey/soc2/security/dcf_2/least_privilege.rego deleted file mode 100644 index 1c7c915..0000000 --- a/src/abbey/soc2/security/dcf_2/least_privilege.rego +++ /dev/null @@ -1,11 +0,0 @@ -# Authorizes access to information resources, including data and the systems that -# store or process sensitive data, based on the principle of least privilege. -# -# Does the organization utilize the concept of least privilege, -# allowing only authorized access to processes necessary to accomplish assigned tasks -# in accordance with organizational business functions? -# -# Authorize access to information resources based on least-privilege policy. -# -# Covers CC2.1, CC5.2. -package abbey.soc2.security.dcf_2 \ No newline at end of file diff --git a/src/abbey/soc2/security/dcf_59/role_based_security.rego b/src/abbey/soc2/security/dcf_59/role_based_security.rego deleted file mode 100644 index 6fdc2d3..0000000 --- a/src/abbey/soc2/security/dcf_59/role_based_security.rego +++ /dev/null @@ -1,6 +0,0 @@ -# Role-based security is in place for internal and external users, including super admin users. -# -# Does the organization enforce a Role-Based Access Control (RBAC) policy over users and resources? -# -# Covers CC1.1, CC6.1, CC6.3, PI1.4. -package abbey.soc2.security.dcf_59