From 3dbb24488316d4b8f3d90f7aa0d13aa052fa4f57 Mon Sep 17 00:00:00 2001 From: Viktor Date: Mon, 23 Sep 2024 19:28:18 +0200 Subject: [PATCH] lint rules with regal --- .github/workflows/ci.yml | 11 ++ .regal/config.yaml | 9 ++ .../001_0001_anonymous_disabled.rego | 24 ++-- .../001_0001_anonymous_disabled_test.rego | 11 +- .../001_0002_demo_users_disabled.rego | 26 ++-- .../001_0002_demo_users_disabled_test.rego | 11 +- .../001_0003_security_checks.rego | 43 ++++--- .../001_0003_security_checks_test.rego | 14 ++- .../001_0004_strong_password.rego | 87 +++++++------ .../001_0004_strong_password_test.rego | 48 ++++--- .../002_0001_number_of_entities.rego | 34 ++--- .../002_0001_number_of_entities_test.rego | 26 ++-- .../002_0002_number_of_attributes.rego | 42 ++++--- .../002_0002_number_of_attributes_test.rego | 50 ++++---- ...3_inherit_from_administration_account.rego | 31 +++-- ...erit_from_administration_account_test.rego | 35 +++--- .../002_0004_inherit_from_non_system.rego | 32 +++-- ...002_0004_inherit_from_non_system_test.rego | 35 +++--- ..._0005_avoid_system_entity_association.rego | 32 +++-- ..._avoid_system_entity_association_test.rego | 24 ++-- ...006_avoid_too_many_virtual_attributes.rego | 37 +++--- ...void_too_many_virtual_attributes_test.rego | 80 +++++------- ...002_0007_avoid_using_validation_rules.rego | 30 ++--- ...007_avoid_using_validation_rules_test.rego | 72 +++++------ .../003_0001_number_of_modules.rego | 32 ++--- .../003_0001_number_of_modules_test.rego | 29 ++--- .../004_0001_inline_style_property_used.rego | 37 +++--- ..._0001_inline_style_property_used_test.rego | 118 +++++++----------- ..._0001_empty_string_check_not_complete.rego | 36 +++--- ..._empty_string_check_not_complete_test.rego | 41 +++--- scripts/run-policy-tests.sh | 2 +- 31 files changed, 589 insertions(+), 550 deletions(-) create mode 100644 .regal/config.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e60445..6e9c08a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,17 @@ on: branches: [ main ] jobs: + + Lint-Rego: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: StyraInc/setup-regal@v1 + with: + version: latest + + - name: Lint + run: regal lint --format=github ./rules Rules: runs-on: ubuntu-latest diff --git a/.regal/config.yaml b/.regal/config.yaml new file mode 100644 index 0000000..c72c998 --- /dev/null +++ b/.regal/config.yaml @@ -0,0 +1,9 @@ +rules: + style: + prefer-some-in-iteration: + level: ignore + + idiomatic: + directory-package-mismatch: + level: ignore + \ No newline at end of file diff --git a/rules/001_project_settings/001_0001_anonymous_disabled.rego b/rules/001_project_settings/001_0001_anonymous_disabled.rego index 9c61de6..b1e5afd 100644 --- a/rules/001_project_settings/001_0001_anonymous_disabled.rego +++ b/rules/001_project_settings/001_0001_anonymous_disabled.rego @@ -12,20 +12,24 @@ # remediation: Disable anonymous/guest access in Project Security # input: Security$ProjectSecurity.yaml package app.mendix.project_settings.anonymous_disabled + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 errors contains error if { - input.EnableGuestAccess == true - error := sprintf("[%v, %v, %v] %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - annotation.title, - ] - ) -} \ No newline at end of file + input.EnableGuestAccess == true + error := sprintf( + "[%v, %v, %v] %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + annotation.title, + ], + ) +} diff --git a/rules/001_project_settings/001_0001_anonymous_disabled_test.rego b/rules/001_project_settings/001_0001_anonymous_disabled_test.rego index e03bac8..c165599 100644 --- a/rules/001_project_settings/001_0001_anonymous_disabled_test.rego +++ b/rules/001_project_settings/001_0001_anonymous_disabled_test.rego @@ -1,10 +1,13 @@ -package app.mendix.project_settings.anonymous_disabled +package app.mendix.project_settings.anonymous_disabled_test + +import data.app.mendix.project_settings.anonymous_disabled import rego.v1 # Test cases test_allow if { - allow with input as {"EnableGuestAccess": false} + anonymous_disabled.allow with input as {"EnableGuestAccess": false} } + test_no_allow if { - not allow with input as {"EnableGuestAccess": true} -} \ No newline at end of file + not anonymous_disabled.allow with input as {"EnableGuestAccess": true} +} diff --git a/rules/001_project_settings/001_0002_demo_users_disabled.rego b/rules/001_project_settings/001_0002_demo_users_disabled.rego index a9836c4..ae0b020 100644 --- a/rules/001_project_settings/001_0002_demo_users_disabled.rego +++ b/rules/001_project_settings/001_0002_demo_users_disabled.rego @@ -1,7 +1,7 @@ # METADATA # scope: package # title: Business apps should disable demo users -# description: No demo users +# description: No demo users # authors: # - Xiwen Cheng # custom: @@ -12,20 +12,24 @@ # remediation: Disable demo users in Project Security # input: Security$ProjectSecurity.yaml package app.mendix.project_settings.demo_users_disabled + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 errors contains error if { - input.EnableDemoUsers == true - error := sprintf("[%v, %v, %v] %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - annotation.title, - ] - ) -} \ No newline at end of file + input.EnableDemoUsers == true + error := sprintf( + "[%v, %v, %v] %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + annotation.title, + ], + ) +} diff --git a/rules/001_project_settings/001_0002_demo_users_disabled_test.rego b/rules/001_project_settings/001_0002_demo_users_disabled_test.rego index 7a8d14f..b7e5eb5 100644 --- a/rules/001_project_settings/001_0002_demo_users_disabled_test.rego +++ b/rules/001_project_settings/001_0002_demo_users_disabled_test.rego @@ -1,10 +1,13 @@ -package app.mendix.project_settings.demo_users_disabled +package app.mendix.project_settings.demo_users_disabled_test + +import data.app.mendix.project_settings.demo_users_disabled import rego.v1 # Test cases test_allow if { - allow with input as {"EnableDemoUsers": false} + demo_users_disabled.allow with input as {"EnableDemoUsers": false} } + test_no_allow if { - not allow with input as {"EnableDemoUsers": true} -} \ No newline at end of file + not demo_users_disabled.allow with input as {"EnableDemoUsers": true} +} diff --git a/rules/001_project_settings/001_0003_security_checks.rego b/rules/001_project_settings/001_0003_security_checks.rego index 74ad3b2..34d2dc5 100644 --- a/rules/001_project_settings/001_0003_security_checks.rego +++ b/rules/001_project_settings/001_0003_security_checks.rego @@ -12,32 +12,37 @@ # remediation: Set Security check to production in Project Security # input: Security$ProjectSecurity.yaml package app.mendix.project_settings.security_checks + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 errors contains error if { - input.CheckSecurity == false - error := sprintf("[%v, %v, %v] %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - "Security check is not enabled in Project Security", - ] - ) + input.CheckSecurity == false + error := sprintf( + "[%v, %v, %v] %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + "Security check is not enabled in Project Security", + ], + ) } errors contains error if { - input.SecurityLevel != "CheckEverything" - error := sprintf("[%v, %v, %v] %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - "Security check is not set to Production in Project Security", - ] - ) -} \ No newline at end of file + input.SecurityLevel != "CheckEverything" + error := sprintf( + "[%v, %v, %v] %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + "Security check is not set to Production in Project Security", + ], + ) +} diff --git a/rules/001_project_settings/001_0003_security_checks_test.rego b/rules/001_project_settings/001_0003_security_checks_test.rego index 58ecf46..0d29e3f 100644 --- a/rules/001_project_settings/001_0003_security_checks_test.rego +++ b/rules/001_project_settings/001_0003_security_checks_test.rego @@ -1,22 +1,26 @@ -package app.mendix.project_settings.security_checks +package app.mendix.project_settings.security_checks_test + +import data.app.mendix.project_settings.security_checks import rego.v1 # Test cases test_allow if { - allow with input as { + security_checks.allow with input as { "CheckSecurity": true, "SecurityLevel": "CheckEverything", } } + test_no_allow_1 if { - not allow with input as { + not security_checks.allow with input as { "CheckSecurity": false, "SecurityLevel": "CheckEverything", } } + test_no_allow_2 if { - not allow with input as { + not security_checks.allow with input as { "CheckSecurity": true, "SecurityLevel": "unknown", } -} \ No newline at end of file +} diff --git a/rules/001_project_settings/001_0004_strong_password.rego b/rules/001_project_settings/001_0004_strong_password.rego index 5c23d7c..c90ddae 100644 --- a/rules/001_project_settings/001_0004_strong_password.rego +++ b/rules/001_project_settings/001_0004_strong_password.rego @@ -13,61 +13,68 @@ # remediation: Ensure minimum password length of at least 8 characters and must use all character classes. # input: Security$ProjectSecurity.yaml package app.mendix.project_settings.strong_password + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 min_password_length := 8 errors contains error if { - my_password_length := input.PasswordPolicySettings.MinimumLength - my_password_length < min_password_length - error := sprintf("[%v, %v, %v] Password length of %v is not enough. It must be at least %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - my_password_length, - min_password_length, - ] - ) + my_password_length := input.PasswordPolicySettings.MinimumLength + my_password_length < min_password_length + error := sprintf( + "[%v, %v, %v] Password length of %v is not enough. It must be at least %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + my_password_length, + min_password_length, + ], + ) } errors contains error if { - input.PasswordPolicySettings.RequireDigit == false - error := sprintf("[%v, %v, %v] %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - "Password must require digits", - ] - ) + input.PasswordPolicySettings.RequireDigit == false + error := sprintf( + "[%v, %v, %v] %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + "Password must require digits", + ], + ) } errors contains error if { - input.PasswordPolicySettings.RequireMixedCase == false - input.PasswordPolicySettings.RequireSymbol == false - error := sprintf("[%v, %v, %v] %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - "Password must require mixed case characters", - ] - ) + input.PasswordPolicySettings.RequireMixedCase == false + input.PasswordPolicySettings.RequireSymbol == false + error := sprintf( + "[%v, %v, %v] %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + "Password must require mixed case characters", + ], + ) } errors contains error if { - input.PasswordPolicySettings.RequireSymbol == false - error := sprintf("[%v, %v, %v] %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - "Password must require symbols", - ] - ) -} \ No newline at end of file + input.PasswordPolicySettings.RequireSymbol == false + error := sprintf( + "[%v, %v, %v] %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + "Password must require symbols", + ], + ) +} diff --git a/rules/001_project_settings/001_0004_strong_password_test.rego b/rules/001_project_settings/001_0004_strong_password_test.rego index ca12cd5..866f931 100644 --- a/rules/001_project_settings/001_0004_strong_password_test.rego +++ b/rules/001_project_settings/001_0004_strong_password_test.rego @@ -1,36 +1,32 @@ -package app.mendix.project_settings.strong_password +package app.mendix.project_settings.strong_password_test + +import data.app.mendix.project_settings.strong_password import rego.v1 # Test cases test_allow if { - allow with input as { - "PasswordPolicySettings": { - "MinimumLength": 9, - "RequireDigit": true, - "RequireSymbol": true, - "RequireMixedCase": true, - } - } + strong_password.allow with input as {"PasswordPolicySettings": { + "MinimumLength": 9, + "RequireDigit": true, + "RequireSymbol": true, + "RequireMixedCase": true, + }} } test_no_allow_password_length if { - not allow with input as { - "PasswordPolicySettings": { - "MinimumLength": 3, - "RequireDigit": true, - "RequireSymbol": true, - "RequireMixedCase": true, - } - } + not strong_password.allow with input as {"PasswordPolicySettings": { + "MinimumLength": 3, + "RequireDigit": true, + "RequireSymbol": true, + "RequireMixedCase": true, + }} } test_no_allow_simple if { - not allow with input as { - "PasswordPolicySettings": { - "MinimumLength": 3, - "RequireDigit": false, - "RequireSymbol": true, - "RequireMixedCase": false, - } - } -} \ No newline at end of file + not strong_password.allow with input as {"PasswordPolicySettings": { + "MinimumLength": 3, + "RequireDigit": false, + "RequireSymbol": true, + "RequireMixedCase": false, + }} +} diff --git a/rules/002_domain_model/002_0001_number_of_entities.rego b/rules/002_domain_model/002_0001_number_of_entities.rego index 28c659c..55509ea 100644 --- a/rules/002_domain_model/002_0001_number_of_entities.rego +++ b/rules/002_domain_model/002_0001_number_of_entities.rego @@ -1,7 +1,9 @@ # METADATA # scope: package # title: No more than 15 persistent entities within one domain model -# description: The bigger the domain models, the harder they will be to maintain. It adds complexity to your security model as well. The smaller the modules, the easier to reuse. +# description: The bigger the domain models, the harder they will be to maintain. +# It adds complexity to your security model as well. The smaller the modules, +# the easier to reuse. # authors: # - Xiwen Cheng # custom: @@ -12,25 +14,29 @@ # remediation: Split domain model into multiple modules. # input: "*/DomainModels$DomainModel.yaml" package app.mendix.domain_model.number_of_entities + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 max_entities := 15 errors contains error if { - not input.Entities == null - count_entities := count(input.Entities) - count_entities > max_entities - error := sprintf("[%v, %v, %v] There are %v entities which is more than %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - count_entities, - max_entities - ] - ) -} \ No newline at end of file + not input.Entities == null + count_entities := count(input.Entities) + count_entities > max_entities + error := sprintf( + "[%v, %v, %v] There are %v entities which is more than %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + count_entities, + max_entities, + ], + ) +} diff --git a/rules/002_domain_model/002_0001_number_of_entities_test.rego b/rules/002_domain_model/002_0001_number_of_entities_test.rego index 488c126..f2a8f1c 100644 --- a/rules/002_domain_model/002_0001_number_of_entities_test.rego +++ b/rules/002_domain_model/002_0001_number_of_entities_test.rego @@ -1,32 +1,30 @@ -package app.mendix.domain_model.number_of_entities -import rego.v1 +package app.mendix.domain_model.number_of_entities_test +import data.app.mendix.domain_model.number_of_entities +import rego.v1 # Test data -entity_attr_0 = { - "Name": "Entity1", -} - +entity_attr_0 := {"Name": "Entity1"} twenty := numbers.range(1, 20) -entities_20 = [ - { "Name": entity_attr_0.Name } | n := twenty[_] -] +entities_20 := [ +{"Name": entity_attr_0.Name} | n := twenty[_]] # Test cases + test_no_entities if { - allow with input as {"Entities": null} + number_of_entities.allow with input as {"Entities": null} } test_1_entity if { - allow with input as {"Entities": [entity_attr_0]} + number_of_entities.allow with input as {"Entities": [entity_attr_0]} } test_2_entities if { - allow with input as {"Entities": [entity_attr_0, entity_attr_0]} + number_of_entities.allow with input as {"Entities": [entity_attr_0, entity_attr_0]} } test_20_entities if { - not allow with input as {"Entities": entities_20} -} \ No newline at end of file + not number_of_entities.allow with input as {"Entities": entities_20} +} diff --git a/rules/002_domain_model/002_0002_number_of_attributes.rego b/rules/002_domain_model/002_0002_number_of_attributes.rego index 0d02856..d06a730 100644 --- a/rules/002_domain_model/002_0002_number_of_attributes.rego +++ b/rules/002_domain_model/002_0002_number_of_attributes.rego @@ -1,7 +1,9 @@ # METADATA # scope: package # title: No more that 35 attributes in an entity -# description: The bigger the entities, the slower your application will become when handling the data. This is because Mendix is using SELECT * queries a lot and will retrieve a lot of unnecessary data. +# description: The bigger the entities, the slower your application will become +# when handling the data. This is because Mendix is using SELECT * queries a lot +# and will retrieve a lot of unnecessary data. # authors: # - Xiwen Cheng # custom: @@ -9,30 +11,36 @@ # rulename: NumberOfAttributes # severity: MEDIUM # rulenumber: 002_0002 -# remediation: Normalize your datamodel. Split your object into multiple objects. If the attributes really belong to each other in a one-to-one relation, just draw a one-to-one relation between the objects. +# remediation: Normalize your datamodel. Split your object into multiple objects. +# If the attributes really belong to each other in a one-to-one relation, +# just draw a one-to-one relation between the objects. # input: "*/DomainModels$DomainModel.yaml" package app.mendix.domain_model.number_of_attributes + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 max_attributes := 35 errors contains error if { - entity := input.Entities[_] - not entity.Attributes == null - count_attributes := count(entity.Attributes) - count_attributes > max_attributes - error := sprintf("[%v, %v, %v] Entity %v has %v attributes which is more than %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - entity.Name, - count_attributes, - max_attributes - ] - ) -} \ No newline at end of file + entity := input.Entities[_] + not entity.Attributes == null + count_attributes := count(entity.Attributes) + count_attributes > max_attributes + error := sprintf( + "[%v, %v, %v] Entity %v has %v attributes which is more than %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + entity.Name, + count_attributes, + max_attributes, + ], + ) +} diff --git a/rules/002_domain_model/002_0002_number_of_attributes_test.rego b/rules/002_domain_model/002_0002_number_of_attributes_test.rego index eaf0433..28695b5 100644 --- a/rules/002_domain_model/002_0002_number_of_attributes_test.rego +++ b/rules/002_domain_model/002_0002_number_of_attributes_test.rego @@ -1,56 +1,52 @@ -package app.mendix.domain_model.number_of_attributes -import rego.v1 +package app.mendix.domain_model.number_of_attributes_test +import data.app.mendix.domain_model.number_of_attributes +import rego.v1 # Test data -attribute1 = { - "Name": "Attribute1" -} +attribute1 := {"Name": "Attribute1"} -entity_attr_0 = { - "Name": "Entity1", - "Attributes": null, +entity_attr_0 := { + "Name": "Entity1", + "Attributes": null, } -entity_attr_1 = { - "Name": "Entity1", - "Attributes": [ - attribute1 - ] +entity_attr_1 := { + "Name": "Entity1", + "Attributes": [attribute1], } forty := numbers.range(1, 40) -attributes_40 = [ - { "Name": attribute1.Name } | n := forty[_] -] -entity_1_attr_40 = { - "Name": "Entity1", - "Attributes": attributes_40, -} +attributes_40 := [ +{"Name": attribute1.Name} | n := forty[_]] +entity_1_attr_40 := { + "Name": "Entity1", + "Attributes": attributes_40, +} # Test cases test_no_entities if { - allow with input as {"Entities": null} + number_of_attributes.allow with input as {"Entities": null} } test_1_entity_1_attribute if { - allow with input as {"Entities": [entity_attr_1]} + number_of_attributes.allow with input as {"Entities": [entity_attr_1]} } test_2_entities if { - allow with input as {"Entities": [entity_attr_1, entity_attr_1]} + number_of_attributes.allow with input as {"Entities": [entity_attr_1, entity_attr_1]} } test_3_entities_1_empty if { - allow with input as {"Entities": [entity_attr_1, entity_attr_1, entity_attr_0]} + number_of_attributes.allow with input as {"Entities": [entity_attr_1, entity_attr_1, entity_attr_0]} } test_1_entity_40_attributes_not_allowed if { - not allow with input as {"Entities": [entity_1_attr_40]} + not number_of_attributes.allow with input as {"Entities": [entity_1_attr_40]} } test_2_entity_40_attributes_1_empty_not_allowed if { - not allow with input as {"Entities": [entity_1_attr_40, entity_1_attr_40, entity_attr_0]} -} \ No newline at end of file + not number_of_attributes.allow with input as {"Entities": [entity_1_attr_40, entity_1_attr_40, entity_attr_0]} +} diff --git a/rules/002_domain_model/002_0003_inherit_from_administration_account.rego b/rules/002_domain_model/002_0003_inherit_from_administration_account.rego index 3feeb94..bd0f6ab 100644 --- a/rules/002_domain_model/002_0003_inherit_from_administration_account.rego +++ b/rules/002_domain_model/002_0003_inherit_from_administration_account.rego @@ -1,7 +1,9 @@ # METADATA # scope: package # title: Inherit from Administration.Account -# description: There is no need to inherit from administration.account. Administration.account may simply be extended, this is not a system module. Avoid unnecessary inheritance as this has a negative effect on performance. +# description: There is no need to inherit from administration.account. +# Administration.account may simply be extended, this is not a system module. +# Avoid unnecessary inheritance as this has a negative effect on performance. # authors: # - Xiwen Cheng # custom: @@ -12,21 +14,26 @@ # remediation: Inherit from system.user instead or adapt Administration.Account so it fits your needs. # input: "*/DomainModels$DomainModel.yaml" package app.mendix.domain_model.inherit_from_administration_account + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 errors contains error if { - some i - input.Entities[i].MaybeGeneralization.Generalization == "Administration.Account" - error := sprintf("[%v, %v, %v] Entity %v has generaralization of %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - [input.Entities[i].Name], - "Administration.Account", - ]) -} \ No newline at end of file + some i + input.Entities[i].MaybeGeneralization.Generalization == "Administration.Account" + error := sprintf( + "[%v, %v, %v] Entity %v has generaralization of %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + [input.Entities[i].Name], + "Administration.Account", + ], + ) +} diff --git a/rules/002_domain_model/002_0003_inherit_from_administration_account_test.rego b/rules/002_domain_model/002_0003_inherit_from_administration_account_test.rego index ee63918..943de30 100644 --- a/rules/002_domain_model/002_0003_inherit_from_administration_account_test.rego +++ b/rules/002_domain_model/002_0003_inherit_from_administration_account_test.rego @@ -1,41 +1,40 @@ -package app.mendix.domain_model.inherit_from_administration_account -import rego.v1 +package app.mendix.domain_model.inherit_from_administration_account_test +import data.app.mendix.domain_model.inherit_from_administration_account +import rego.v1 # Test data -entity_negative = { - "Name": "Entity1", +entity_negative := { + "Name": "Entity1", "MaybeGeneralization": { "Type": "DomainModels$Generalization", - "Generalization": "System.FileDocument" - } + "Generalization": "System.FileDocument", + }, } -entity_positive = { - "Name": "Entity2", +entity_positive := { + "Name": "Entity2", "MaybeGeneralization": { "Type": "DomainModels$Generalization", - "Generalization": "Administration.Account" - } + "Generalization": "Administration.Account", + }, } - -entities_mixed = [entity_negative, entity_positive] - +entities_mixed := [entity_negative, entity_positive] # Test cases test_no_entities if { - allow with input as {"Entities": null} + inherit_from_administration_account.allow with input as {"Entities": null} } test_entity_negative if { - allow with input as {"Entities": [entity_negative]} + inherit_from_administration_account.allow with input as {"Entities": [entity_negative]} } test_entity_positive if { - not allow with input as {"Entities": [entity_positive]} + not inherit_from_administration_account.allow with input as {"Entities": [entity_positive]} } test_entities_mixed if { - not allow with input as {"Entities": entities_mixed} -} \ No newline at end of file + not inherit_from_administration_account.allow with input as {"Entities": entities_mixed} +} diff --git a/rules/002_domain_model/002_0004_inherit_from_non_system.rego b/rules/002_domain_model/002_0004_inherit_from_non_system.rego index 54cb6a9..e0ce4aa 100644 --- a/rules/002_domain_model/002_0004_inherit_from_non_system.rego +++ b/rules/002_domain_model/002_0004_inherit_from_non_system.rego @@ -1,7 +1,8 @@ # METADATA # scope: package # title: Inherit from non System module is discouraged -# description: Inheritance, except from system module, is strongly discouraged because of the negative performance side effects. +# description: Inheritance, except from system module, is strongly discouraged +# because of the negative performance side effects. # authors: # - Xiwen Cheng # custom: @@ -9,23 +10,30 @@ # rulename: AvoidInheritanceFromNonSystem # severity: MEDIUM # rulenumber: 002_0004 -# remediation: Instead of inheritance, just use separate objects which are associated to the main object. As an alternative, you can add the child’s attributes to the super entity and add an ObjectType enumeration. +# remediation: Instead of inheritance, just use separate objects which are +# associated to the main object. As an alternative, you can add the child’s +# attributes to the super entity and add an ObjectType enumeration. # input: "*/DomainModels$DomainModel.yaml" package app.mendix.domain_model.inherit_from_non_system + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 errors contains error if { - some i - not startswith(input.Entities[i].MaybeGeneralization.Generalization, "System.") - error := sprintf("[%v, %v, %v] Entity %v has generaralization of non-System", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - [input.Entities[i].Name], - ]) -} \ No newline at end of file + some i + not startswith(input.Entities[i].MaybeGeneralization.Generalization, "System.") + error := sprintf( + "[%v, %v, %v] Entity %v has generaralization of non-System", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + [input.Entities[i].Name], + ], + ) +} diff --git a/rules/002_domain_model/002_0004_inherit_from_non_system_test.rego b/rules/002_domain_model/002_0004_inherit_from_non_system_test.rego index 362babf..4fb41f6 100644 --- a/rules/002_domain_model/002_0004_inherit_from_non_system_test.rego +++ b/rules/002_domain_model/002_0004_inherit_from_non_system_test.rego @@ -1,41 +1,40 @@ -package app.mendix.domain_model.inherit_from_non_system -import rego.v1 +package app.mendix.domain_model.inherit_from_non_system_test +import data.app.mendix.domain_model.inherit_from_non_system +import rego.v1 # Test data -entity_negative = { - "Name": "Entity1", +entity_negative := { + "Name": "Entity1", "MaybeGeneralization": { "Type": "DomainModels$Generalization", - "Generalization": "System.FileDocument" - } + "Generalization": "System.FileDocument", + }, } -entity_positive = { - "Name": "Entity2", +entity_positive := { + "Name": "Entity2", "MaybeGeneralization": { "Type": "DomainModels$Generalization", - "Generalization": "Administration.Account" - } + "Generalization": "Administration.Account", + }, } - -entities_mixed = [entity_negative, entity_positive] - +entities_mixed := [entity_negative, entity_positive] # Test cases test_no_entities if { - allow with input as {"Entities": null} + inherit_from_non_system.allow with input as {"Entities": null} } test_entity_negative if { - allow with input as {"Entities": [entity_negative]} + inherit_from_non_system.allow with input as {"Entities": [entity_negative]} } test_entity_positive if { - not allow with input as {"Entities": [entity_positive]} + not inherit_from_non_system.allow with input as {"Entities": [entity_positive]} } test_entities_mixed if { - not allow with input as {"Entities": entities_mixed} -} \ No newline at end of file + not inherit_from_non_system.allow with input as {"Entities": entities_mixed} +} diff --git a/rules/002_domain_model/002_0005_avoid_system_entity_association.rego b/rules/002_domain_model/002_0005_avoid_system_entity_association.rego index 99c0c6b..1227773 100644 --- a/rules/002_domain_model/002_0005_avoid_system_entity_association.rego +++ b/rules/002_domain_model/002_0005_avoid_system_entity_association.rego @@ -1,7 +1,9 @@ # METADATA # scope: package # title: Avoid using system storage objects directly -# description: Always inherit for filedocuments and images. Never implement direct assocations to the System Domain Model, because of limits on the configuration of security. +# description: Always inherit for filedocuments and images. +# Never implement direct assocations to the System Domain Model, +# because of limits on the configuration of security. # authors: # - Xiwen Cheng # custom: @@ -9,23 +11,29 @@ # rulename: AvoidSystemEntityAssociation # severity: HIGH # rulenumber: 002_0005 -# remediation: Remove direct associations with the System Domain Model. Use inheritance instead (i.e. Generalization in the entity properties). +# remediation: Remove direct associations with the System Domain Model. +# Use inheritance instead (i.e. Generalization in the entity properties). # input: "*/DomainModels$DomainModel.yaml" package app.mendix.domain_model.avoid_system_entity_association + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 errors contains error if { - some i - startswith(input.CrossAssociations[i].Child, "System.") - error := sprintf("[%v, %v, %v] Entity association %v refers to a System entity with limited security configuration.", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - [input.CrossAssociations[i].Name], - ]) -} \ No newline at end of file + some i + startswith(input.CrossAssociations[i].Child, "System.") + error := sprintf( + "[%v, %v, %v] Entity association %v refers to a System entity with limited security configuration.", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + input.CrossAssociations[i].Name, + ], + ) +} diff --git a/rules/002_domain_model/002_0005_avoid_system_entity_association_test.rego b/rules/002_domain_model/002_0005_avoid_system_entity_association_test.rego index c2be3c0..2b2c7f4 100644 --- a/rules/002_domain_model/002_0005_avoid_system_entity_association_test.rego +++ b/rules/002_domain_model/002_0005_avoid_system_entity_association_test.rego @@ -1,33 +1,33 @@ -package app.mendix.domain_model.avoid_system_entity_association -import rego.v1 +package app.mendix.domain_model.avoid_system_entity_association_test +import data.app.mendix.domain_model.avoid_system_entity_association +import rego.v1 # Test data -negative = { - "Name": "HELLO_THERE1", +negative := { + "Name": "HELLO_THERE1", "Child": "SomeModule.FileDocument", } -positive = { - "Name": "HELLO_THERE2", +positive := { + "Name": "HELLO_THERE2", "Child": "System.FileDocument", } - # Test cases test_no_cross_associations if { - allow with input as {"CrossAssociations": null} + avoid_system_entity_association.allow with input as {"CrossAssociations": null} } test_negative if { - allow with input as {"CrossAssociations": [negative]} + avoid_system_entity_association.allow with input as {"CrossAssociations": [negative]} } test_positive if { - not allow with input as {"CrossAssociations": [positive]} + not avoid_system_entity_association.allow with input as {"CrossAssociations": [positive]} } test_mixed if { - not allow with input as {"CrossAssociations": [negative, positive]} -} \ No newline at end of file + not avoid_system_entity_association.allow with input as {"CrossAssociations": [negative, positive]} +} diff --git a/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes.rego b/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes.rego index dcaf50c..b6d0c73 100644 --- a/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes.rego +++ b/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes.rego @@ -14,28 +14,31 @@ package app.mendix.domain_model.avoid_too_many_virtual_attributes import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 max_virtual_attributes := 10 errors contains error if { - not input.Entities == null - - entity := input.Entities[_] - entity_name := entity.Name - attr_count := count([attr | attr := entity.Attributes[_]; attr.Value["$Type"] == "DomainModels$CalculatedValue"]) - attr_count > max_virtual_attributes - error := sprintf("[%v, %v, %v] There are %v Virtual Attributes in entity %v which is more than %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - attr_count, - entity_name, - max_virtual_attributes - ] - ) -} \ No newline at end of file + not input.Entities == null + + entity := input.Entities[_] + entity_name := entity.Name + attr_count := count([attr | attr := entity.Attributes[_]; attr.Value["$Type"] == "DomainModels$CalculatedValue"]) + attr_count > max_virtual_attributes + error := sprintf( + "[%v, %v, %v] There are %v Virtual Attributes in entity %v which is more than %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + attr_count, + entity_name, + max_virtual_attributes, + ], + ) +} diff --git a/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes_test.rego b/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes_test.rego index 6174e0c..231aa64 100644 --- a/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes_test.rego +++ b/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes_test.rego @@ -1,66 +1,50 @@ -package app.mendix.domain_model.avoid_too_many_virtual_attributes +package app.mendix.domain_model.avoid_too_many_virtual_attributes_test +import data.app.mendix.domain_model.avoid_too_many_virtual_attributes import rego.v1 - # Test data - attr_0 := { - "$Type": "DomainModels$Attribute", - "Name": "VA_age", - "Value": { - "$Type": "DomainModels$CalculatedValue" - } + "$Type": "DomainModels$Attribute", + "Name": "VA_age", + "Value": {"$Type": "DomainModels$CalculatedValue"}, } - twenty := numbers.range(1, 20) -attr_20 = [ - { "Name": attr_0.Name, "Value": attr_0.Value } | n := twenty[_] -] -positive := { - "Entities": [ - { - "$Type": "DomainModels$EntityImpl", - "Attributes": [ - { - "$Type": "DomainModels$Attribute", - "Name": "VA_age", - "Value": { - "$Type": "DomainModels$CalculatedValue" - } - }, - { - "$Type": "DomainModels$Attribute", - "Name": "Year", - "Value": { - "$Type": "DomainModels$StoredValue" - } - } - ], - "Name": "Bike" - } - ] -} - -negative := { - "Entities": [ - { - "$Type": "DomainModels$EntityImpl", - "Attributes": attr_20, - "Name": "Bike" - } - ] -} +attr_20 := [ +{"Name": attr_0.Name, "Value": attr_0.Value} | n := twenty[_]] + +positive := {"Entities": [{ + "$Type": "DomainModels$EntityImpl", + "Attributes": [ + { + "$Type": "DomainModels$Attribute", + "Name": "VA_age", + "Value": {"$Type": "DomainModels$CalculatedValue"}, + }, + { + "$Type": "DomainModels$Attribute", + "Name": "Year", + "Value": {"$Type": "DomainModels$StoredValue"}, + }, + ], + "Name": "Bike", +}]} + +negative := {"Entities": [{ + "$Type": "DomainModels$EntityImpl", + "Attributes": attr_20, + "Name": "Bike", +}]} # Test cases test_positive if { - allow with input as positive + avoid_too_many_virtual_attributes.allow with input as positive } test_negative if { - not allow with input as negative + not avoid_too_many_virtual_attributes.allow with input as negative } diff --git a/rules/002_domain_model/002_0007_avoid_using_validation_rules.rego b/rules/002_domain_model/002_0007_avoid_using_validation_rules.rego index c064ec8..80ce7f9 100644 --- a/rules/002_domain_model/002_0007_avoid_using_validation_rules.rego +++ b/rules/002_domain_model/002_0007_avoid_using_validation_rules.rego @@ -14,6 +14,7 @@ package app.mendix.domain_model.avoid_using_validation_rules import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false @@ -21,18 +22,19 @@ default allow := false allow if count(errors) == 0 errors contains error if { - entity := input.Entities[_] - entity_name := entity.Name - rules_count := count([rule | rule := entity.ValidationRules[_]]) - rules_count > 0 - - error := sprintf("[%v, %v, %v] Validation rules %v in entity %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - rules_count, - entity_name - ] - ) + entity := input.Entities[_] + entity_name := entity.Name + rules_count := count([rule | rule := entity.ValidationRules[_]]) + rules_count > 0 + + error := sprintf( + "[%v, %v, %v] Validation rules %v in entity %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + rules_count, + entity_name, + ], + ) } diff --git a/rules/002_domain_model/002_0007_avoid_using_validation_rules_test.rego b/rules/002_domain_model/002_0007_avoid_using_validation_rules_test.rego index ddf6684..8f52ef5 100644 --- a/rules/002_domain_model/002_0007_avoid_using_validation_rules_test.rego +++ b/rules/002_domain_model/002_0007_avoid_using_validation_rules_test.rego @@ -1,55 +1,43 @@ -package app.mendix.domain_model.avoid_using_validation_rules +package app.mendix.domain_model.avoid_using_validation_rules_test +import data.app.mendix.domain_model.avoid_using_validation_rules import rego.v1 - # Test data -positive := { - "Entities": [ - { - "ValidationRules": [], - "Name": "Bike" - } - ] -} - -negative := { - "Entities": [ - { - "ValidationRules": [ - { - "$Type": "DomainModels$ValidationRule", - "Attribute": "MyFirstModule.Bike.Name", - "Message": { - "$Type": "Texts$Text", - "Items": [ - { - "$Type": "Texts$Translation", - "LanguageCode": "en_US", - "Text": "Not a good name" - } - ] - }, - "RuleInfo": { - "$Type": "DomainModels$EqualsToRuleInfo", - "EqualsToAttribute": "", - "UseValue": true, - "Value": "admin" - } - } - ], - "Name": "Bike" - } - ] -} +positive := {"Entities": [{ + "ValidationRules": [], + "Name": "Bike", +}]} + +negative := {"Entities": [{ + "ValidationRules": [{ + "$Type": "DomainModels$ValidationRule", + "Attribute": "MyFirstModule.Bike.Name", + "Message": { + "$Type": "Texts$Text", + "Items": [{ + "$Type": "Texts$Translation", + "LanguageCode": "en_US", + "Text": "Not a good name", + }], + }, + "RuleInfo": { + "$Type": "DomainModels$EqualsToRuleInfo", + "EqualsToAttribute": "", + "UseValue": true, + "Value": "admin", + }, + }], + "Name": "Bike", +}]} # Test cases test_positive if { - allow with input as positive + avoid_using_validation_rules.allow with input as positive } test_negative if { - not allow with input as negative + not avoid_using_validation_rules.allow with input as negative } diff --git a/rules/003_modules/003_0001_number_of_modules.rego b/rules/003_modules/003_0001_number_of_modules.rego index 86f5019..34107c0 100644 --- a/rules/003_modules/003_0001_number_of_modules.rego +++ b/rules/003_modules/003_0001_number_of_modules.rego @@ -12,27 +12,31 @@ # remediation: Consider a multi-app stategy to avoid creating one big (unmaintainable) monstrous application. # input: "Metadata.yaml" package app.mendix.modules.number_of_modules + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 max_count := 20 errors contains error if { - not input.Modules == null - some i - user_modules := [item | input.Modules[i].Attributes.FromAppStore == false ; item := input.Modules[i]] - count_items = count(user_modules) - count_items > max_count - error := sprintf("[%v, %v, %v] There are %v user modules which is more than %v", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - count_items, - max_count - ] - ) + not input.Modules == null + some i + user_modules := [item | input.Modules[i].Attributes.FromAppStore == false; item := input.Modules[i]] + count_items = count(user_modules) + count_items > max_count + error := sprintf( + "[%v, %v, %v] There are %v user modules which is more than %v", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + count_items, + max_count, + ], + ) } diff --git a/rules/003_modules/003_0001_number_of_modules_test.rego b/rules/003_modules/003_0001_number_of_modules_test.rego index 70740ec..149c1fc 100644 --- a/rules/003_modules/003_0001_number_of_modules_test.rego +++ b/rules/003_modules/003_0001_number_of_modules_test.rego @@ -1,35 +1,32 @@ -package app.mendix.modules.number_of_modules -import rego.v1 +package app.mendix.modules.number_of_modules_test +import data.app.mendix.modules.number_of_modules +import rego.v1 # Test data -module = { - "Name": "Module", - "Attributes": { - "FromAppStore": false, - } +module := { + "Name": "Module", + "Attributes": {"FromAppStore": false}, } - thirty := numbers.range(1, 30) -modules_30 = [ - module | n := thirty[_] -] +modules_30 := [ +module | n := thirty[_]] # Test cases test_empty if { - allow with input as {"Modules": null} + number_of_modules.allow with input as {"Modules": null} } test_1_module if { - allow with input as {"Modules": [module]} + number_of_modules.allow with input as {"Modules": [module]} } test_2_modules if { - allow with input as {"Modules": [module, module]} + number_of_modules.allow with input as {"Modules": [module, module]} } test_30_modules if { - not allow with input as {"Modules": modules_30} -} \ No newline at end of file + not number_of_modules.allow with input as {"Modules": modules_30} +} diff --git a/rules/004_pages/004_0001_inline_style_property_used.rego b/rules/004_pages/004_0001_inline_style_property_used.rego index 5f4235a..bb76265 100644 --- a/rules/004_pages/004_0001_inline_style_property_used.rego +++ b/rules/004_pages/004_0001_inline_style_property_used.rego @@ -1,7 +1,9 @@ # METADATA # scope: package # title: Inline style property used -# description: Avoid using the style property, because this will make the life of your UI designer a lot more complicated. It will be harder to overrule styles from CSS file level. +# description: Avoid using the style property, because this will make the life of your +# UI designer a lot more complicated. It will be harder to overrule styles from +# CSS file level. # authors: # - Xiwen Cheng # custom: @@ -12,25 +14,30 @@ # remediation: Use generic classes instead, defined by the theme. # input: "*/**/*$Page.yaml" package app.mendix.pages.inline_style_property_used + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 errors contains error if { - [p, v] := walk(input) - # Check if the path ends with "Style" and value is not an empty string - last := array.slice(p, count(p) - 1, count(p))[0] - last == "Style" - v != "" - error := sprintf("[%v, %v, %v] Form with name '%v' has inlined style property with value '%v'", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - input.Name, - v, - ] - ) + [p, v] := walk(input) + + # Check if the path ends with "Style" and value is not an empty string + last := array.slice(p, count(p) - 1, count(p))[0] + last == "Style" + v != "" + error := sprintf( + "[%v, %v, %v] Form with name '%v' has inlined style property with value '%v'", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + input.Name, + v, + ], + ) } diff --git a/rules/004_pages/004_0001_inline_style_property_used_test.rego b/rules/004_pages/004_0001_inline_style_property_used_test.rego index f6cdecb..cf07fe3 100644 --- a/rules/004_pages/004_0001_inline_style_property_used_test.rego +++ b/rules/004_pages/004_0001_inline_style_property_used_test.rego @@ -1,9 +1,10 @@ -package app.mendix.pages.inline_style_property_used -import rego.v1 +package app.mendix.pages.inline_style_property_used_test +import data.app.mendix.pages.inline_style_property_used +import rego.v1 # Test data -form_simple = { +form_simple := { "$Type": "Forms$Page", "Name": "Page1", "Appearance": { @@ -14,7 +15,8 @@ form_simple = { "Style": "", }, } -form_simple_negative = { + +form_simple_negative := { "$Type": "Forms$Page", "Name": "Page1", "Appearance": { @@ -26,87 +28,61 @@ form_simple_negative = { }, } -form_nested = { +form_nested := { "Name": "Page1", - "FormCall": { - "Arguments": [ - { - "Widgets": [ - { - "$Type": "Forms$LayoutGrid", - "Name": "layoutGrid2", - "Rows": [ - { - "$Type": "Forms$LayoutGridRow", - "Columns": [ - { - "$Type": "Forms$LayoutGridColumn", - "Appearance": { - "$Type": "Forms$Appearance", - "Class": "", - "DesignProperties": null, - "DynamicClasses": "", - "Style": "", - } - }, - ], - }, - ], - }, - ], - }, - ], - }, + "FormCall": {"Arguments": [{"Widgets": [{ + "$Type": "Forms$LayoutGrid", + "Name": "layoutGrid2", + "Rows": [{ + "$Type": "Forms$LayoutGridRow", + "Columns": [{ + "$Type": "Forms$LayoutGridColumn", + "Appearance": { + "$Type": "Forms$Appearance", + "Class": "", + "DesignProperties": null, + "DynamicClasses": "", + "Style": "", + }, + }], + }], + }]}]}, } -form_nested_negative = { +form_nested_negative := { "Name": "Page1", - "FormCall": { - "Arguments": [ - { - "Widgets": [ - { - "$Type": "Forms$LayoutGrid", - "Name": "layoutGrid2", - "Rows": [ - { - "$Type": "Forms$LayoutGridRow", - "Columns": [ - { - "$Type": "Forms$LayoutGridColumn", - "Appearance": { - "$Type": "Forms$Appearance", - "Class": "", - "DesignProperties": null, - "DynamicClasses": "", - "Style": "color: orange;", - } - }, - ], - }, - ], - }, - ], - }, - ], - }, + "FormCall": {"Arguments": [{"Widgets": [{ + "$Type": "Forms$LayoutGrid", + "Name": "layoutGrid2", + "Rows": [{ + "$Type": "Forms$LayoutGridRow", + "Columns": [{ + "$Type": "Forms$LayoutGridColumn", + "Appearance": { + "$Type": "Forms$Appearance", + "Class": "", + "DesignProperties": null, + "DynamicClasses": "", + "Style": "color: orange;", + }, + }], + }], + }]}]}, } - - # Test cases test_simple if { - allow with input as form_simple + inline_style_property_used.allow with input as form_simple } test_simple_negative if { - not allow with input as form_simple_negative + not inline_style_property_used.allow with input as form_simple_negative } test_nested if { - allow with input as form_nested + inline_style_property_used.allow with input as form_nested } test_nested_negative if { - not allow with input as form_nested_negative -} \ No newline at end of file + not inline_style_property_used.allow with input as form_nested_negative +} diff --git a/rules/005_microflows/005_0001_empty_string_check_not_complete.rego b/rules/005_microflows/005_0001_empty_string_check_not_complete.rego index a0569f5..1e2f643 100644 --- a/rules/005_microflows/005_0001_empty_string_check_not_complete.rego +++ b/rules/005_microflows/005_0001_empty_string_check_not_complete.rego @@ -9,28 +9,34 @@ # rulename: EmptyStringCheckNotComplete # severity: MEDIUM # rulenumber: 005_0001 -# remediation: Always check a string for empty based on != empty and != "". The first one equals database NULL value, the latter one indicates a truncated string. +# remediation: Always check a string for empty based on != empty and != "". +# The first one equals database NULL value, the latter one indicates a +# truncated string. # input: "*/**/*$Microflow.yaml" package app.mendix.microflows.empty_string_check_not_complete + import rego.v1 + annotation := rego.metadata.chain()[1].annotations default allow := false + allow if count(errors) == 0 errors contains error if { - [p, v] := walk(input) - last := array.slice(p, count(p) - 1, count(p))[0] - last == "Expression" - contains(replace(v, " ", ""), "!=''") - not contains(replace(v, " ", ""), "!=empty") - error := sprintf("[%v, %v, %v] Expression in Microflow '%v' has incomplete empty string check '%v'", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - input.Name, - v, - ] - ) + [p, v] := walk(input) + last := array.slice(p, count(p) - 1, count(p))[0] + last == "Expression" + contains(replace(v, " ", ""), "!=''") + not contains(replace(v, " ", ""), "!=empty") + error := sprintf( + "[%v, %v, %v] Expression in Microflow '%v' has incomplete empty string check '%v'", + [ + annotation.custom.severity, + annotation.custom.category, + annotation.custom.rulenumber, + input.Name, + v, + ], + ) } diff --git a/rules/005_microflows/005_0001_empty_string_check_not_complete_test.rego b/rules/005_microflows/005_0001_empty_string_check_not_complete_test.rego index b10f767..297bb6f 100644 --- a/rules/005_microflows/005_0001_empty_string_check_not_complete_test.rego +++ b/rules/005_microflows/005_0001_empty_string_check_not_complete_test.rego @@ -1,47 +1,44 @@ -package app.mendix.microflows.empty_string_check_not_complete -import rego.v1 +package app.mendix.microflows.empty_string_check_not_complete_test +import data.app.mendix.microflows.empty_string_check_not_complete +import rego.v1 # Test data -microflow_good = { +microflow_good := { "$Type": "Microflow$Page", "Name": "mf1", "ObjectCollection": { "$Type": "Microflows$MicroflowObjectCollection", - "Objects": [ - { - "$Type": "Microflows$ExclusiveSplit", - "SplitCondition": { - "$Type": "Microflows$ExpressionSplitCondition", - "Expression": "$Variable != empty and $Variable != ''", - }, + "Objects": [{ + "$Type": "Microflows$ExclusiveSplit", + "SplitCondition": { + "$Type": "Microflows$ExpressionSplitCondition", + "Expression": "$Variable != empty and $Variable != ''", }, - ], + }], }, } -microflow_bad = { +microflow_bad := { "$Type": "Microflow$Page", "Name": "mf1", "ObjectCollection": { "$Type": "Microflows$MicroflowObjectCollection", - "Objects": [ - { - "$Type": "Microflows$ExclusiveSplit", - "SplitCondition": { - "$Type": "Microflows$ExpressionSplitCondition", - "Expression": "$Variable != ''", - }, + "Objects": [{ + "$Type": "Microflows$ExclusiveSplit", + "SplitCondition": { + "$Type": "Microflows$ExpressionSplitCondition", + "Expression": "$Variable != ''", }, - ], + }], }, } # Test cases test_simple if { - allow with input as microflow_good + empty_string_check_not_complete.allow with input as microflow_good } test_simple_negative if { - not allow with input as microflow_bad + not empty_string_check_not_complete.allow with input as microflow_bad } diff --git a/scripts/run-policy-tests.sh b/scripts/run-policy-tests.sh index 9b39c48..9a23b19 100755 --- a/scripts/run-policy-tests.sh +++ b/scripts/run-policy-tests.sh @@ -23,4 +23,4 @@ if [ ! -f "$OPA" ]; then chmod +x "$OPA" fi -$OPA test -v policies +$OPA test -v rules