diff --git a/ b/ new file mode 100755 index 0000000..5e3070a Binary files /dev/null and b/ differ diff --git a/rules/001_project_settings/001_0007_hash_algorithm_test.rego b/rules/001_project_settings/001_0007_hash_algorithm_test.rego deleted file mode 100644 index 5c2ed2f..0000000 --- a/rules/001_project_settings/001_0007_hash_algorithm_test.rego +++ /dev/null @@ -1,36 +0,0 @@ -package app.mendix.project_settings.hash_algorithm_test - -import data.app.mendix.project_settings.hash_algorithm -import rego.v1 - -# Test data -bcrypt := {"Settings": { - "$Type": "Settings$ModelSettings", - "HashAlgorithm": "BCrypt", -}} - -ssha256 := {"Settings": { - "$Type": "Settings$ModelSettings", - "HashAlgorithm": "SSHA256", -}} - -sha256 := {"Settings": { - "$Type": "Settings$ModelSettings", - "HashAlgorithm": "SHA256", -}} - -md5 := {"Settings": { - "$Type": "Settings$ModelSettings", - "HashAlgorithm": "MD5", -}} - -# Test cases -test_should_allow_when_build_version_in_allowed_list if { - hash_algorithm.allow with input as bcrypt - hash_algorithm.allow with input as ssha256 -} - -test_should_deny_when_build_version_not_in_allowed_list if { - not hash_algorithm.allow with input as sha256 - not hash_algorithm.allow with input as md5 -} diff --git a/rules/001_project_settings/001_0007_hash_algorithm_test.yaml b/rules/001_project_settings/001_0007_hash_algorithm_test.yaml new file mode 100644 index 0000000..16aa74c --- /dev/null +++ b/rules/001_project_settings/001_0007_hash_algorithm_test.yaml @@ -0,0 +1,25 @@ +TestCases: +- name: allow BCrypt hash algorithm + allow: true + input: + Settings: + $Type: Settings$ModelSettings + HashAlgorithm: BCrypt +- name: allow SSHA256 hash algorithm + allow: true + input: + Settings: + $Type: Settings$ModelSettings + HashAlgorithm: SSHA256 +- name: do not allow SHA256 hash algorithm + allow: false + input: + Settings: + $Type: Settings$ModelSettings + HashAlgorithm: SHA256 +- name: do not allow MD5 hash algorithm + allow: false + input: + Settings: + $Type: Settings$ModelSettings + HashAlgorithm: MD5 diff --git a/rules/001_project_settings/001_0008_check_security_on_user_roles_test.rego b/rules/001_project_settings/001_0008_check_security_on_user_roles_test.rego deleted file mode 100644 index ca72a4f..0000000 --- a/rules/001_project_settings/001_0008_check_security_on_user_roles_test.rego +++ /dev/null @@ -1,24 +0,0 @@ -package app.mendix.project_settings.check_security_on_user_roles_test - -import data.app.mendix.project_settings.check_security_on_user_roles -import rego.v1 - -# Test data -check_for_security := {"UserRoles": [{ - "CheckSecurity": true, - "Name": "Administrator", -}]} - -not_check_for_security := {"UserRoles": [{ - "CheckSecurity": false, - "Name": "Administrator", -}]} - -# Test cases -test_should_allow_when_checking_user_roles_for_security if { - check_security_on_user_roles.allow with input as check_for_security -} - -test_should_deny_when_not_checking_user_roles_for_security if { - not check_security_on_user_roles.allow with input as not_check_for_security -} diff --git a/rules/001_project_settings/001_0008_check_security_on_user_roles_test.yaml b/rules/001_project_settings/001_0008_check_security_on_user_roles_test.yaml new file mode 100644 index 0000000..4ae3441 --- /dev/null +++ b/rules/001_project_settings/001_0008_check_security_on_user_roles_test.yaml @@ -0,0 +1,13 @@ +TestCases: +- name: allow user role that is checked for security + allow: true + input: + UserRoles: + - Name: Administrator + CheckSecurity: true +- name: do not allow user role that is not checked for security + allow: false + input: + UserRoles: + - Name: Administrator + CheckSecurity: false diff --git a/rules/001_project_settings/001_0009_strict_mode_with_react_client_test.yaml b/rules/001_project_settings/001_0009_strict_mode_with_react_client_test.yaml index 3219eb3..2e2c279 100644 --- a/rules/001_project_settings/001_0009_strict_mode_with_react_client_test.yaml +++ b/rules/001_project_settings/001_0009_strict_mode_with_react_client_test.yaml @@ -1,28 +1,26 @@ +# The rule reads Security$ProjectSecurity.yaml from the working directory. +# During tests the working directory is this rule's folder, which contains a +# fixture Security$ProjectSecurity.yaml with StrictMode disabled. Cases that +# enable the optimized (React) client therefore expect a violation. TestCases: - - name: allow when UseOptimizedClient is false + - name: allow when optimized client is disabled allow: true input: - UseOptimizedClient: false - files: - "Security$ProjectSecurity.yaml": - StrictMode: false - - name: allow when both UseOptimizedClient and StrictMode are true + $Type: Settings$ProjectSettings + Settings: + - $Type: Forms$WebUIProjectSettingsPart + UseOptimizedClient: "No" + - name: allow when optimized client setting is absent allow: true input: - UseOptimizedClient: true - files: - "Security$ProjectSecurity.yaml": - StrictMode: true - - name: do not allow UseOptimizedClient true but StrictMode false + $Type: Settings$ProjectSettings + Settings: + - $Type: Settings$ModelSettings + HashAlgorithm: BCrypt + - name: do not allow optimized client enabled while strict mode is disabled allow: false input: - UseOptimizedClient: true - files: - "Security$ProjectSecurity.yaml": - StrictMode: false - - name: do not allow UseOptimizedClient true but StrictMode missing - allow: false - input: - UseOptimizedClient: true - files: - "Security$ProjectSecurity.yaml": {} + $Type: Settings$ProjectSettings + Settings: + - $Type: Forms$WebUIProjectSettingsPart + UseOptimizedClient: "Yes" diff --git a/rules/001_project_settings/Security$ProjectSecurity.yaml b/rules/001_project_settings/Security$ProjectSecurity.yaml new file mode 100644 index 0000000..fd45bd1 --- /dev/null +++ b/rules/001_project_settings/Security$ProjectSecurity.yaml @@ -0,0 +1,3 @@ +# Fixture for 001_0009_strict_mode_with_react_client_test.yaml (ignored by rule scanner). +$Type: Security$ProjectSecurity +StrictMode: false diff --git a/rules/005_microflows/005_0002_commit_actions_with_a_loop.js b/rules/005_microflows/005_0002_commit_actions_with_a_loop.js new file mode 100644 index 0000000..4d567c5 --- /dev/null +++ b/rules/005_microflows/005_0002_commit_actions_with_a_loop.js @@ -0,0 +1,112 @@ +const metadata = { + scope: "package", + title: "Commit actions with a loop", + description: "Commiting objects within a loop will fire a SQL Update query for each iteration. This applies whether the commit sits directly in the loop or is reached via a called microflow.", + authors: [ + "Viktor Berlov ", + "Jurre Tanja " + ], + custom: { + category: "Microflows", + rulename: "AvoidCommitInLoop", + severity: "MEDIUM", + rulenumber: "005_0002", + remediation: "Consider committing objects outside the loop. Within the loop, add them to a list.", + input: ".*\\$Microflow\\.yaml" + } +}; + +function rule(input = {}) { + const errors = []; + + function collect(node, predicate, acc) { + acc = acc || []; + if (!node || typeof node !== "object") return acc; + if (predicate(node)) acc.push(node); + for (const key of Object.keys(node)) { + const v = node[key]; + if (Array.isArray(v)) { + for (const item of v) collect(item, predicate, acc); + } else if (v && typeof v === "object") { + collect(v, predicate, acc); + } + } + return acc; + } + + const isLoop = n => n["$Type"] === "Microflows$LoopedActivity"; + + const isCall = n => + n["$Type"] === "Microflows$ActionActivity" && + n.Action && + n.Action["$Type"] === "Microflows$MicroflowCallAction" && + n.Action.MicroflowCall && + n.Action.MicroflowCall.Microflow; + + const isCommit = n => + n["$Type"] === "Microflows$ActionActivity" && + n.Action && ( + n.Action["$Type"] === "Microflows$CommitAction" || + (n.Action["$Type"] === "Microflows$ChangeAction" && n.Action.Commit === "Yes") || + (n.Action["$Type"] === "Microflows$CreateChangeAction" && n.Action.Commit === "Yes") + ); + + function refToPath(ref) { + const parts = ref.split("."); + if (parts.length !== 2) return null; + return parts[0] + "/" + parts[1] + ".Microflows$Microflow.yaml"; + } + + const pathPrefixes = ["", "rules/005_microflows/", "./rules/005_microflows/"]; + + function tryReadYaml(relPath) { + for (const prefix of pathPrefixes) { + try { + const doc = mxlint.io.readYaml(prefix + relPath); + if (doc) return doc; + } catch (e) { + // try next prefix + } + } + return null; + } + + function commitsTransitively(ref, visited) { + if (visited.has(ref)) return false; + visited.add(ref); + const path = refToPath(ref); + if (!path) return false; + const doc = tryReadYaml(path); + if (!doc) return false; + if (collect(doc, isCommit).length > 0) return true; + for (const call of collect(doc, isCall)) { + if (commitsTransitively(call.Action.MicroflowCall.Microflow, visited)) return true; + } + return false; + } + + const prefix = "[" + metadata.custom.severity + ", " + metadata.custom.category + ", " + metadata.custom.rulenumber + "] "; + const flowName = input.Name || "unknown"; + + const loops = collect(input, isLoop); + const reportedCommitNodes = new Set(); + const reportedIndirectRefs = new Set(); + + for (const loop of loops) { + for (const c of collect(loop, isCommit)) { + if (reportedCommitNodes.has(c)) continue; + reportedCommitNodes.add(c); + errors.push(prefix + c.Action["$Type"] + " inside " + flowName + " loop"); + } + for (const call of collect(loop, isCall)) { + const ref = call.Action.MicroflowCall.Microflow; + if (reportedIndirectRefs.has(ref)) continue; + if (commitsTransitively(ref, new Set())) { + reportedIndirectRefs.add(ref); + errors.push(prefix + "Microflow " + ref + " called from " + flowName + " loop commits (directly or transitively)"); + } + } + } + + return { allow: errors.length === 0, errors }; +} diff --git a/rules/005_microflows/005_0002_commit_actions_with_a_loop.rego b/rules/005_microflows/005_0002_commit_actions_with_a_loop.rego deleted file mode 100644 index dc53f39..0000000 --- a/rules/005_microflows/005_0002_commit_actions_with_a_loop.rego +++ /dev/null @@ -1,62 +0,0 @@ -# METADATA -# scope: package -# title: Commit actions with a loop -# description: Commiting objects within a loop will fire a SQL Update query for each iteration. -# authors: -# - Viktor Berlov -# custom: -# category: Microflows -# rulename: AvoidCommitInLoop -# severity: MEDIUM -# rulenumber: 005_0002 -# remediation: Consider committing objects outside the loop. Within the loop, add them to a list. -# input: .*\$Microflow\.yaml -package app.mendix.microflows.commit_actions_with_a_loop - -import rego.v1 - -annotation := rego.metadata.chain()[1].annotations - -default allow := false - -allow if count(errors) == 0 - -errors contains error if { - name := input.Name - main_function := input.MainFunction - - some attr in main_function - attr.Attributes["$Type"] == "Microflows$LoopedActivity" - some commit_action in attr.Attributes.ObjectCollection.Objects - commit_action.Action["$Type"] == "Microflows$CommitAction" - - error := sprintf( - "[%v, %v, %v] Commit actions inside %v loop", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - name, - ], - ) -} - -errors contains error if { - name := input.Name - main_function := input.MainFunction - some attr in main_function - attr.Attributes["$Type"] == "Microflows$LoopedActivity" - some change_action in attr.Attributes.ObjectCollection.Objects - change_action.Action["$Type"] == "Microflows$ChangeAction" - change_action.Action.Commit == "Yes" - - error := sprintf( - "[%v, %v, %v] Commit set to Yes for Change actions inside %v loop", - [ - annotation.custom.severity, - annotation.custom.category, - annotation.custom.rulenumber, - name, - ], - ) -} diff --git a/rules/005_microflows/005_0002_commit_actions_with_a_loop_test.yaml b/rules/005_microflows/005_0002_commit_actions_with_a_loop_test.yaml index 32d7a6c..98a442b 100644 --- a/rules/005_microflows/005_0002_commit_actions_with_a_loop_test.yaml +++ b/rules/005_microflows/005_0002_commit_actions_with_a_loop_test.yaml @@ -1,24 +1,95 @@ +# Fixture microflows for transitive lookup live under ./ModuleA/*.yaml +# and follow the type-scoped naming convention (Name.Microflows$Microflow.yaml) +# used by other rule fixtures in this repo. TestCases: -- name: allow loop without objects +# --- allowed: structural / defensive --- +- name: allow microflow with no looped activity allow: true input: - $Type: Microflow$Microflow - Name: MicroflowForLoop - MainFunction: - - Attributes: - $Type: Microflows$LoopedActivity + $Type: Microflows$Microflow + Name: NoLoop + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$RetrieveAction + +- name: allow looped activity with an empty body + allow: true + input: + $Type: Microflows$Microflow + Name: EmptyLoop + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: [] + +- name: allow committing sub-microflow when the call sits outside any loop + allow: true + input: + $Type: Microflows$Microflow + Name: CommitOutsideLoop + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.CommitInLoop + +- name: allow loop calling a microflow reference that does not resolve to a file + allow: true + input: + $Type: Microflows$Microflow + Name: LoopCallsMissing + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity ObjectCollection: $Type: Microflows$MicroflowObjectCollection - Objects: null + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: NonExistentModule.NonExistentFlow -- name: allow loop with change action without commit +- name: allow loop calling a microflow reference without a module prefix allow: true input: - $Type: Microflow$Microflow - Name: MicroflowForLoop - MainFunction: - - Attributes: - $Type: Microflows$LoopedActivity + $Type: Microflows$Microflow + Name: LoopCallsMalformed + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: nodots + +# --- allowed: direct action inside the loop that does NOT commit --- +- name: allow loop that directly contains a ChangeAction with Commit=No + allow: true + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity ObjectCollection: $Type: Microflows$MicroflowObjectCollection Objects: @@ -27,14 +98,104 @@ TestCases: $Type: Microflows$ChangeAction Commit: "No" -- name: do not allow loop with commit action +- name: allow loop that directly contains a CreateChangeAction with Commit=No + allow: true + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$CreateChangeAction + Commit: "No" + +# --- allowed: loop -> sub that does NOT commit --- +- name: allow loop calling sub-microflow that contains a ChangeAction with Commit=No + allow: true + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.ChangeNoCommit + +- name: allow loop calling sub-microflow that contains a CreateChangeAction with Commit=No + allow: true + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.CreateNoCommit + +- name: allow loop calling sub-microflow that transitively reaches a ChangeAction with Commit=No + allow: true + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.CallsChangeNoCommit + +- name: allow loop calling a self-recursive sub-microflow that never commits + allow: true + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.RecursiveNoCommit + +# --- disallowed: direct commit-capable action inside the loop --- +- name: do not allow loop that directly contains a CommitAction allow: false input: - $Type: Microflow$Microflow - Name: MicroflowForLoop - MainFunction: - - Attributes: - $Type: Microflows$LoopedActivity + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity ObjectCollection: $Type: Microflows$MicroflowObjectCollection Objects: @@ -42,14 +203,14 @@ TestCases: Action: $Type: Microflows$CommitAction -- name: do not allow loop with change action with commit +- name: do not allow loop that directly contains a ChangeAction with Commit=Yes allow: false input: - $Type: Microflow$Microflow - Name: MicroflowForLoop - MainFunction: - - Attributes: - $Type: Microflows$LoopedActivity + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity ObjectCollection: $Type: Microflows$MicroflowObjectCollection Objects: @@ -58,21 +219,146 @@ TestCases: $Type: Microflows$ChangeAction Commit: "Yes" -- name: do not allow loop with both commit and change actions +- name: do not allow loop that directly contains a CreateChangeAction with Commit=Yes allow: false input: - $Type: Microflow$Microflow - Name: MicroflowForLoop - MainFunction: - - Attributes: - $Type: Microflows$LoopedActivity + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity ObjectCollection: $Type: Microflows$MicroflowObjectCollection Objects: - $Type: Microflows$ActionActivity Action: - $Type: Microflows$CommitAction + $Type: Microflows$CreateChangeAction + Commit: "Yes" + +# --- disallowed: loop -> sub with a commit-capable action --- +- name: do not allow loop calling sub-microflow that contains a CommitAction + allow: false + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: - $Type: Microflows$ActionActivity Action: - $Type: Microflows$ChangeAction - Commit: "Yes" + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.CommitInLoop + +- name: do not allow loop calling sub-microflow that contains a ChangeAction with Commit=Yes + allow: false + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.ChangeWithCommit + +- name: do not allow loop calling sub-microflow that contains a CreateChangeAction with Commit=Yes + allow: false + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.CreateWithCommit + +# --- disallowed: loop -> SUB -> sub with a commit-capable action --- +- name: do not allow loop calling sub-microflow that transitively reaches a CommitAction + allow: false + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.CallsCommit + +- name: do not allow loop calling sub-microflow that transitively reaches a ChangeAction with Commit=Yes + allow: false + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.CallsChangeWithCommit + +- name: do not allow loop calling sub-microflow that transitively reaches a CreateChangeAction with Commit=Yes + allow: false + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.CallsCreateWithCommit + +- name: do not allow loop calling a self-recursive sub-microflow that also contains a CommitAction + allow: false + input: + $Type: Microflows$Microflow + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$LoopedActivity + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.RecursiveCommit diff --git a/rules/005_microflows/005_0005_nested_if_statements_test.rego b/rules/005_microflows/005_0005_nested_if_statements_test.rego deleted file mode 100644 index 00362f0..0000000 --- a/rules/005_microflows/005_0005_nested_if_statements_test.rego +++ /dev/null @@ -1,76 +0,0 @@ -package app.mendix.microflows.nested_if_statements_test - -import data.app.mendix.microflows.nested_if_statements -import rego.v1 - -# Test data -one_exclusive_split_with_no_nested_ifs := {"ObjectCollection": { - "$Type": "Microflows$MicroflowObjectCollection", - "Objects": [{ - "$Type": "Microflows$ExpressionSplitCondition", - "Caption": "no nested ifs", - "SplitCondition": {"Expression": "if $Variable then a else b"}, - }], -}} - -multiple_exclusive_splits_with_no_nested_ifs := {"ObjectCollection": { - "$Type": "Microflows$MicroflowObjectCollection", - "Objects": [ - { - "$Type": "Microflows$ExpressionSplitCondition", - "Caption": "ex spit 1 with no nested ifs", - "SplitCondition": {"Expression": "if $Variable then a else b"}, - }, - { - "$Type": "Microflows$ExpressionSplitCondition", - "Caption": "ex split 2 with no nested ifs", - "SplitCondition": {"Expression": "if $Variable then c else d"}, - }, - ], -}} - -one_exclusive_split_with_else_if := {"ObjectCollection": { - "$Type": "Microflows$MicroflowObjectCollection", - "Objects": [{ - "$Type": "Microflows$ExclusiveSplit", - "Caption": "ex split with else-if", - "SplitCondition": {"Expression": "if true then\n\tfalse\nelse if false then\n\ttrue\nelse false"}, - }], -}} - -one_exclusive_split_with_then_if := {"ObjectCollection": { - "$Type": "Microflows$MicroflowObjectCollection", - "Objects": [{ - "$Type": "Microflows$ExclusiveSplit", - "Caption": "ex split with then-if", - "SplitCondition": {"Expression": "if a then if b then c else d else e"}, - }], -}} - -multiple_exclusive_splits_with_one_nested_if := {"ObjectCollection": { - "$Type": "Microflows$MicroflowObjectCollection", - "Objects": [ - { - "$Type": "Microflows$ExclusiveSplit", - "Caption": "no nested ifs", - "SplitCondition": {"Expression": "if $Variable then a else b"}, - }, - { - "$Type": "Microflows$ExclusiveSplit", - "Caption": "ex split with then-if", - "SplitCondition": {"Expression": "if a then b else if c then d else e"}, - }, - ], -}} - -# Test cases -test_should_allow_when_no_exclusive_splits_with_nested_ifs if { - nested_if_statements.allow with input as one_exclusive_split_with_no_nested_ifs - nested_if_statements.allow with input as multiple_exclusive_splits_with_no_nested_ifs -} - -test_should_deny_when_exclusive_splits_with_nested_ifs if { - not nested_if_statements.allow with input as one_exclusive_split_with_else_if - not nested_if_statements.allow with input as one_exclusive_split_with_then_if - not nested_if_statements.allow with input as multiple_exclusive_splits_with_one_nested_if -} diff --git a/rules/005_microflows/005_0005_nested_if_statements_test.yaml b/rules/005_microflows/005_0005_nested_if_statements_test.yaml new file mode 100644 index 0000000..52ab587 --- /dev/null +++ b/rules/005_microflows/005_0005_nested_if_statements_test.yaml @@ -0,0 +1,63 @@ +TestCases: +- name: allow one exclusive split without nested if-statements + allow: true + input: + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ExclusiveSplit + Caption: no nested ifs + SplitCondition: + Expression: if $Variable then a else b + +- name: allow multiple exclusive splits without nested if-statements + allow: true + input: + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ExclusiveSplit + Caption: ex split 1 with no nested ifs + SplitCondition: + Expression: if $Variable then a else b + - $Type: Microflows$ExclusiveSplit + Caption: ex split 2 with no nested ifs + SplitCondition: + Expression: if $Variable then c else d + +- name: do not allow exclusive split with an else-if + allow: false + input: + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ExclusiveSplit + Caption: ex split with else-if + SplitCondition: + Expression: "if true then\n\tfalse\nelse if false then\n\ttrue\nelse false" + +- name: do not allow exclusive split with a then-if + allow: false + input: + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ExclusiveSplit + Caption: ex split with then-if + SplitCondition: + Expression: if a then if b then c else d else e + +- name: do not allow multiple exclusive splits where one has a nested if + allow: false + input: + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ExclusiveSplit + Caption: no nested ifs + SplitCondition: + Expression: if $Variable then a else b + - $Type: Microflows$ExclusiveSplit + Caption: ex split with then-if + SplitCondition: + Expression: if a then b else if c then d else e diff --git a/rules/005_microflows/ModuleA/CallsChangeNoCommit.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/CallsChangeNoCommit.Microflows$Microflow.yaml new file mode 100644 index 0000000..f9f9013 --- /dev/null +++ b/rules/005_microflows/ModuleA/CallsChangeNoCommit.Microflows$Microflow.yaml @@ -0,0 +1,11 @@ +$Type: Microflows$Microflow +Name: CallsChangeNoCommit +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.ChangeNoCommit diff --git a/rules/005_microflows/ModuleA/CallsChangeWithCommit.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/CallsChangeWithCommit.Microflows$Microflow.yaml new file mode 100644 index 0000000..a0c8144 --- /dev/null +++ b/rules/005_microflows/ModuleA/CallsChangeWithCommit.Microflows$Microflow.yaml @@ -0,0 +1,11 @@ +$Type: Microflows$Microflow +Name: CallsChangeWithCommit +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.ChangeWithCommit diff --git a/rules/005_microflows/ModuleA/CallsCommit.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/CallsCommit.Microflows$Microflow.yaml new file mode 100644 index 0000000..e8c3b4d --- /dev/null +++ b/rules/005_microflows/ModuleA/CallsCommit.Microflows$Microflow.yaml @@ -0,0 +1,11 @@ +$Type: Microflows$Microflow +Name: CallsCommit +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.CommitInLoop diff --git a/rules/005_microflows/ModuleA/CallsCreateWithCommit.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/CallsCreateWithCommit.Microflows$Microflow.yaml new file mode 100644 index 0000000..5c6d96c --- /dev/null +++ b/rules/005_microflows/ModuleA/CallsCreateWithCommit.Microflows$Microflow.yaml @@ -0,0 +1,11 @@ +$Type: Microflows$Microflow +Name: CallsCreateWithCommit +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.CreateWithCommit diff --git a/rules/005_microflows/ModuleA/ChangeNoCommit.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/ChangeNoCommit.Microflows$Microflow.yaml new file mode 100644 index 0000000..18e2a2a --- /dev/null +++ b/rules/005_microflows/ModuleA/ChangeNoCommit.Microflows$Microflow.yaml @@ -0,0 +1,9 @@ +$Type: Microflows$Microflow +Name: ChangeNoCommit +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$ChangeAction + Commit: "No" diff --git a/rules/005_microflows/ModuleA/ChangeWithCommit.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/ChangeWithCommit.Microflows$Microflow.yaml new file mode 100644 index 0000000..00657fa --- /dev/null +++ b/rules/005_microflows/ModuleA/ChangeWithCommit.Microflows$Microflow.yaml @@ -0,0 +1,9 @@ +$Type: Microflows$Microflow +Name: ChangeWithCommit +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$ChangeAction + Commit: "Yes" diff --git a/rules/005_microflows/ModuleA/CommitInLoop.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/CommitInLoop.Microflows$Microflow.yaml new file mode 100644 index 0000000..52bb8d9 --- /dev/null +++ b/rules/005_microflows/ModuleA/CommitInLoop.Microflows$Microflow.yaml @@ -0,0 +1,8 @@ +$Type: Microflows$Microflow +Name: CommitInLoop +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$CommitAction diff --git a/rules/005_microflows/ModuleA/CreateNoCommit.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/CreateNoCommit.Microflows$Microflow.yaml new file mode 100644 index 0000000..a01b0cc --- /dev/null +++ b/rules/005_microflows/ModuleA/CreateNoCommit.Microflows$Microflow.yaml @@ -0,0 +1,9 @@ +$Type: Microflows$Microflow +Name: CreateNoCommit +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$CreateChangeAction + Commit: "No" diff --git a/rules/005_microflows/ModuleA/CreateWithCommit.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/CreateWithCommit.Microflows$Microflow.yaml new file mode 100644 index 0000000..755b7fd --- /dev/null +++ b/rules/005_microflows/ModuleA/CreateWithCommit.Microflows$Microflow.yaml @@ -0,0 +1,9 @@ +$Type: Microflows$Microflow +Name: CreateWithCommit +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$CreateChangeAction + Commit: "Yes" diff --git a/rules/005_microflows/ModuleA/RecursiveCommit.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/RecursiveCommit.Microflows$Microflow.yaml new file mode 100644 index 0000000..936f0f6 --- /dev/null +++ b/rules/005_microflows/ModuleA/RecursiveCommit.Microflows$Microflow.yaml @@ -0,0 +1,14 @@ +$Type: Microflows$Microflow +Name: RecursiveCommit +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.RecursiveCommit + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$CommitAction diff --git a/rules/005_microflows/ModuleA/RecursiveNoCommit.Microflows$Microflow.yaml b/rules/005_microflows/ModuleA/RecursiveNoCommit.Microflows$Microflow.yaml new file mode 100644 index 0000000..caaf9d7 --- /dev/null +++ b/rules/005_microflows/ModuleA/RecursiveNoCommit.Microflows$Microflow.yaml @@ -0,0 +1,15 @@ +$Type: Microflows$Microflow +Name: RecursiveNoCommit +ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$MicroflowCallAction + MicroflowCall: + $Type: Microflows$MicroflowCall + Microflow: ModuleA.RecursiveNoCommit + - $Type: Microflows$ActionActivity + Action: + $Type: Microflows$ChangeAction + Commit: "No" diff --git a/scripts/run-policy-tests.sh b/scripts/run-policy-tests.sh index 0a84b25..08b000e 100755 --- a/scripts/run-policy-tests.sh +++ b/scripts/run-policy-tests.sh @@ -28,8 +28,13 @@ if [ ! -f "$MXLINT" ]; then fi +# copy rules into the default cache location that test-rules reads from +mkdir -p .mendix-cache/rules +cp -R ./rules/. .mendix-cache/rules/ +trap 'rm -rf .mendix-cache/' EXIT + # capture all output to a file with tee -$MXLINT test-rules --rules ./rules 2>&1 | tee /tmp/mxlint-test-rules.log +$MXLINT test-rules 2>&1 | tee /tmp/mxlint-test-rules.log # grep for FAIL in the log file if grep -q "FAIL" /tmp/mxlint-test-rules.log; then