Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# METADATA
# scope: package
# title: Access rules using XPath should only contain attributes and associations with Read access
# description: If an XPath uses attributes or associations with read and write access, it may cause a security breach
# authors:
# - Bart Zantingh <bart.zantingh@nl.abnamro.com>
# custom:
# category: Security
# rulename: AccessRuleXpathConstraintsWithReadWrite
# severity: HIGH
# rulenumber: "002_0010"
# remediation: Ensure that all attributes and associations used in XPath constraints have read-only access
# input: "*/DomainModels$DomainModel.yaml"
package app.mendix.domain_model.access_rules_with_read_write_in_xpath

import rego.v1

annotation := rego.metadata.chain()[1].annotations

default allow := false

allow if count(errors) == 0

# check XPaths for attributes with ReadWrite access
errors contains error if {
some entity in input.Entities
entity_name := entity.Name

attributes := [attribute |
some attribute in entity.Attributes
attribute["$Type"] == "DomainModels$Attribute"
]

some attribute in attributes
has_read_write_access_to_attribute(entity, attribute.Name)
used_in_xpath(entity, attribute.Name)

error := sprintf(
"[%v, %v, %v] Entity %v has an XPath constraint that uses one or more attributes with read/write access",
[
annotation.custom.severity,
annotation.custom.category,
annotation.custom.rulenumber,
entity_name,
],
)
}

# check XPaths for associations with ReadWrite access
errors contains error if {
some association in input.Associations

some entity in input.Entities
has_read_write_access_to_association(entity, association.Name)
used_in_xpath(entity, association.Name)

entity_name := entity.Name

error := sprintf(
"[%v, %v, %v] Entity %v has an XPath constraint that uses one or more assocations with read/write access",
[
annotation.custom.severity,
annotation.custom.category,
annotation.custom.rulenumber,
entity_name,
],
)
}

has_read_write_access_to_attribute(entity, attribute_name) if {
some access_rule in entity.AccessRules

some member_access in access_rule.MemberAccesses
member_access.AccessRights == "ReadWrite"
contains(member_access.Attribute, attribute_name)
}

has_read_write_access_to_association(entity, association_name) if {
some access_rule in entity.AccessRules

some member_access in access_rule.MemberAccesses
member_access.AccessRights == "ReadWrite"
contains(member_access.Association, association_name)
}

# check if there are XPath constraints that contain the name of the attribute or association
used_in_xpath(entity, search_term) if {
some access_rule in entity.AccessRules
contains(access_rule.XPathConstraint, search_term)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package app.mendix.domain_model.access_rules_with_read_write_in_xpath_test

import data.app.mendix.domain_model.access_rules_with_read_write_in_xpath
import rego.v1

# Test data
attribute_with_read_used_in_xpath := {"Entities": [{
"AccessRules": [{
"MemberAccesses": [{
"AccessRights": "Read",
"Attribute": "MxLintTest.TestEntity.Attribute1",
"Association": "",
}],
"XPathConstraint": "[Attribute1 = true]",
}],
"Attributes": [{
"$Type": "DomainModels$Attribute",
"Name": "Attribute1",
}],
"Name": "TestEntity",
}]}

attribute_with_read_write_used_in_xpath := {"Entities": [{
"AccessRules": [{
"MemberAccesses": [{
"AccessRights": "ReadWrite",
"Attribute": "MxLintTest.TestEntity.Attribute1",
"Association": "",
}],
"XPathConstraint": "[Attribute1 = true]",
}],
"Attributes": [{
"$Type": "DomainModels$Attribute",
"Name": "Attribute1",
}],
"Name": "TestEntity",
}]}

association_with_read_used_in_xpath := {
"Associations": [{"Name": "TestEntity_Entity"}],
"Entities": [{
"AccessRules": [{
"MemberAccesses": [{
"AccessRights": "Read",
"Attribute": "",
"Association": "MxLintTest.TestEntity_Entity",
}],
"XPathConstraint": "[MxLintTest.TestEntity_Entity/MxLintTest.Entity]",
}],
"Name": "TestEntity",
}],
}

association_with_read_write_used_in_xpath := {
"Associations": [{"Name": "TestEntity_Entity"}],
"Entities": [{
"AccessRules": [{
"MemberAccesses": [{
"AccessRights": "ReadWrite",
"Attribute": "",
"Association": "MxLintTest.TestEntity_Entity",
}],
"XPathConstraint": "[MxLintTest.TestEntity_Entity/MxLintTest.Entity]",
}],
"Name": "TestEntity",
}],
}

# Test cases
test_should_allow_when_default_access_rights_none_or_read if {
access_rules_with_read_write_in_xpath.allow with input as attribute_with_read_used_in_xpath
access_rules_with_read_write_in_xpath.allow with input as association_with_read_used_in_xpath
}

test_should_deny_when_attribute_with_read_write_in_xpath if {
not access_rules_with_read_write_in_xpath.allow with input as attribute_with_read_write_used_in_xpath
}

test_should_deny_when_association_with_read_write_in_xpath if {
not access_rules_with_read_write_in_xpath.allow with input as association_with_read_write_used_in_xpath
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# METADATA
# scope: package
# title: Unlimited length string attributes should not be editable by anonymous users
# description: A malicious agent could set a very long value for the attribute causing the database to run out of space
# authors:
# - Bart Zantingh <bart.zantingh@nl.abnamro.com>
# custom:
# category: Security
# rulename: UnlimitedLengthAttributesEditableByAnonymous
# severity: CRITICAL
# rulenumber: "002_0011"
# remediation: Ensure that anonymous users have, at most, only read access to attributes with unlimited length
# input: "*/DomainModels$DomainModel.yaml"
package app.mendix.domain_model.unlimited_length_attributes_editable_by_anonymous

import rego.v1

annotation := rego.metadata.chain()[1].annotations

default allow := false

allow if count(errors) == 0

errors contains error if {
some entity in input.Entities

anon_has_access(entity)

attributes := [attribute |
some attribute in entity.Attributes
attribute["$Type"] == "DomainModels$Attribute"
attribute.NewType["$Type"] == "DomainModels$StringAttributeType"
attribute.NewType.Length == 0
]

some access_rule in entity.AccessRules
some member_access in access_rule.MemberAccesses
member_access.AccessRights == "ReadWrite"

some attribute in attributes
contains(member_access.Attribute, attribute.Name)

error := sprintf(
"[%v, %v, %v] String attribute %v in entity %v has unlimited length and seems to be editable by anonymous users",
[
annotation.custom.severity,
annotation.custom.category,
annotation.custom.rulenumber,
attribute.Name,
entity.Name,
],
)
}

anon_has_access(entity) if {
some access_rule in entity.AccessRules
some module_role in access_rule.AllowedModuleRoles
contains(lower(module_role), "anon")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package app.mendix.domain_model.unlimited_length_attributes_editable_by_anonymous_test

import data.app.mendix.domain_model.unlimited_length_attributes_editable_by_anonymous
import rego.v1

# Test data
anon_with_readonly_access := {"Entities": [{
"AccessRules": [{
"AllowedModuleRoles": ["MxLintTest.Anonymous"],
"MemberAccesses": [{
"$Type": "DomainModels$MemberAccess",
"AccessRights": "ReadOnly",
"Attribute": "MxLintTest.TestEntity.Attribute",
}],
}],
"Attributes": [{
"$Type": "DomainModels$Attribute",
"Name": "Attribute",
"NewType": {
"$Type": "DomainModels$StringAttributeType",
"Length": 0,
},
}],
"Name": "TestEntity",
}]}

anon_with_readwrite_access := {"Entities": [{
"AccessRules": [{
"AllowedModuleRoles": ["MxLintTest.Anonymous"],
"MemberAccesses": [{
"$Type": "DomainModels$MemberAccess",
"AccessRights": "ReadWrite",
"Attribute": "MxLintTest.TestEntity.Attribute",
}],
}],
"Attributes": [{
"$Type": "DomainModels$Attribute",
"Name": "Attribute",
"NewType": {
"$Type": "DomainModels$StringAttributeType",
"Length": 0,
},
}],
"Name": "TestEntity",
}]}

# Test cases
test_should_allow_when_anon_has_readonly_access if {
unlimited_length_attributes_editable_by_anonymous.allow with input as anon_with_readonly_access
}

test_should_deny_when_anon_has_readwrite_access if {
not unlimited_length_attributes_editable_by_anonymous.allow with input as anon_with_readwrite_access
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# METADATA
# scope: package
# title: Anonymous users should not be allowed to create persistent entities
# description: Anonymous users with create access to persistent entities can pose security risks
# authors:
# - Bart Zantingh <bart.zantingh@nl.abnamro.com>
# custom:
# category: Security
# rulename: AnonymousUsersWithCreateAccess
# severity: HIGH
# rulenumber: "002_0012"
# remediation: Remove create access or make entity non-persistent
# input: "*/DomainModels$DomainModel.yaml"
package app.mendix.domain_model.anonymous_users_with_create_access

import rego.v1

annotation := rego.metadata.chain()[1].annotations

default allow := false

allow if count(errors) == 0

errors contains error if {
some entity in input.Entities
entity.MaybeGeneralization.Persistable == true
entity_name := entity.Name

some access_rule in entity.AccessRules
access_rule.AllowCreate == true

some module_role in access_rule.AllowedModuleRoles
contains(lower(module_role), "anon") # converts module role name to lower case so it catches all variants of spelling

module_name := split(module_role, ".")[0]
module_role_name := split(module_role, ".")[1]

error := sprintf(
"[%v, %v, %v] Module role %v in module %v seems to be for anonymous users, but has Create access to persistent entity %v",
[
annotation.custom.severity,
annotation.custom.category,
annotation.custom.rulenumber,
module_role_name,
module_name,
entity_name,
],
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package app.mendix.domain_model.anonymous_users_with_create_access_test

import data.app.mendix.domain_model.anonymous_users_with_create_access
import rego.v1

# Test data
anon_role_with_create_access_to_persistent := {
"$Type": "DomainModels$DomainModel",
"Entities": [{
"AccessRules": [{
"AllowCreate": true,
"AllowedModuleRoles": ["MyFirstModule.Anonymous"],
}],
"MaybeGeneralization": {"Persistable": true},
"Name": "Entity",
}],
}

anon_role_without_create_access_to_persistent := {
"$Type": "DomainModels$DomainModel",
"Entities": [{
"AccessRules": [{
"AllowCreate": false,
"AllowedModuleRoles": ["MyFirstModule.Anonymous"],
}],
"MaybeGeneralization": {"Persistable": true},
"Name": "Entity",
}],
}

anon_role_with_create_access_to_nonpersistent := {
"$Type": "DomainModels$DomainModel",
"Entities": [{
"AccessRules": [{
"AllowCreate": true,
"AllowedModuleRoles": ["MyFirstModule.Anonymous"],
}],
"MaybeGeneralization": {"Persistable": false},
"Name": "Entity",
}],
}

# Test cases
test_should_allow_when_anon_role_has_no_create_access_to_persistent_entity if {
anonymous_users_with_create_access.allow with input as anon_role_without_create_access_to_persistent
}

test_should_allow_when_anon_role_has_create_access_to_nonpersistent_entity if {
anonymous_users_with_create_access.allow with input as anon_role_with_create_access_to_nonpersistent
}

test_should_deny_when_anon_role_has_create_access_to_persistent_entity if {
not anonymous_users_with_create_access.allow with input as anon_role_with_create_access_to_persistent
}
Loading