diff --git a/glitch/rego/queries/design/design_avoid_comments.rego b/glitch/rego/queries/design/design_avoid_comments.rego index 9fd1fa42..17532849 100644 --- a/glitch/rego/queries/design/design_avoid_comments.rego +++ b/glitch/rego/queries/design/design_avoid_comments.rego @@ -1,35 +1,35 @@ package glitch +import rego.v1 + import data.glitch_lib -get_first_line(elements) = line { - count(elements) > 0 - line = { elements[0].line } +get_first_line(elements) := line if { + count(elements) > 0 + line = {elements[0].line} } -get_first_line(elements) = set() { - count(elements) == 0 + +get_first_line(elements) := set() if { + count(elements) == 0 } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - count(parent.comments) > 0 + count(parent.comments) > 0 - lines := { parent.line | parent.line > 0 } - | get_first_line(parent.atomic_units) - | get_first_line(parent.statements) - | get_first_line(parent.unit_blocks) + lines := (({parent.line | parent.line > 0} | get_first_line(parent.atomic_units)) | get_first_line(parent.statements)) | get_first_line(parent.unit_blocks) - count(lines) > 0 - line := min(lines) + count(lines) > 0 + line := min(lines) - comment := parent.comments[_] - comment.line >= line + comment := parent.comments[_] + comment.line >= line - result := {{ + result := { "type": "design_avoid_comments", "element": comment, "path": parent.path, - "description": "Avoid comments - Comments may lead to bad code or be used as a way to justify bad code." - }} + "description": "Avoid comments - Comments may lead to bad code or be used as a way to justify bad code.", + } } diff --git a/glitch/rego/queries/design/design_imperative_abstraction.rego b/glitch/rego/queries/design/design_imperative_abstraction.rego index fed0c425..344bac2d 100644 --- a/glitch/rego/queries/design/design_imperative_abstraction.rego +++ b/glitch/rego/queries/design/design_imperative_abstraction.rego @@ -1,24 +1,26 @@ package glitch +import rego.v1 + import data.glitch_lib -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - resources := count(glitch_lib.all_atomic_units(parent)) - executions := count({au | - au := glitch_lib.all_atomic_units(parent)[_] - au.type == data.design.exec_atomic_units[_] - }) + resources := count(glitch_lib.all_atomic_units(parent)) + executions := count({au | + au := glitch_lib.all_atomic_units(parent)[_] + au.type == data.design.exec_atomic_units[_] + }) - executions > 2 - (executions / resources) > 0.2 + executions > 2 + executions / resources > 0.2 - result := {{ + result := { "type": "design_imperative_abstraction", "element": parent, "path": parent.path, - "description": "Imperative abstraction - The presence of imperative statements defies the purpose of IaC declarative languages." - }} + "description": "Imperative abstraction - The presence of imperative statements defies the purpose of IaC declarative languages.", + } } diff --git a/glitch/rego/queries/design/design_long_resource.rego b/glitch/rego/queries/design/design_long_resource.rego index ea413829..d56f7f65 100644 --- a/glitch/rego/queries/design/design_long_resource.rego +++ b/glitch/rego/queries/design/design_long_resource.rego @@ -1,27 +1,29 @@ package glitch +import rego.v1 + import data.glitch_lib -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - atomic_units := glitch_lib.all_atomic_units(parent) - node := atomic_units[_] - node.type == data.design.exec_atomic_units[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + atomic_units := glitch_lib.all_atomic_units(parent) + node := atomic_units[_] + node.type == data.design.exec_atomic_units[_] - lines := [ - line | - attr := node.attributes[_] - line := split(attr.code, "\n")[_] - not regex.match("^\\s*$", line) - ] + lines := [ + line | + attr := node.attributes[_] + line := split(attr.code, "\n")[_] + not regex.match("^\\s*$", line) + ] - count(lines) > 7 + count(lines) > 7 - result := {{ + result := { "type": "design_long_resource", "element": node, "path": parent.path, - "description": "Long Resource - Long resources may decrease the readability and maintainability of the code." - }} + "description": "Long Resource - Long resources may decrease the readability and maintainability of the code.", + } } diff --git a/glitch/rego/queries/design/design_misplaced_attribute.rego b/glitch/rego/queries/design/design_misplaced_attribute.rego index b9ffb0c6..30f00e5a 100644 --- a/glitch/rego/queries/design/design_misplaced_attribute.rego +++ b/glitch/rego/queries/design/design_misplaced_attribute.rego @@ -1,86 +1,88 @@ package glitch +import rego.v1 + import data.glitch_lib # Second option for Puppet was not tested in real code due to lack of test data -chef_priority(attr) = index { - attr == "source" - index = 1 -} else = index { - attr == "owner" - index = 2 -} else = index { - attr == "group" - index = 2 -} else = index { - attr == "mode" - index = 3 -} else = index { - attr == "action" - index = 4 +chef_priority(attr) := index if { + attr == "source" + index = 1 +} else := index if { + attr == "owner" + index = 2 +} else := index if { + attr == "group" + index = 2 +} else := index if { + attr == "mode" + index = 3 +} else := index if { + attr == "action" + index = 4 } # Chef -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - endswith(parent.name, ".rb") +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + endswith(parent.name, ".rb") - atomic_units := glitch_lib.all_atomic_units(parent) - node := atomic_units[_] + atomic_units := glitch_lib.all_atomic_units(parent) + node := atomic_units[_] - attr_names := ["source", "owner", "group", "mode", "action"] - order := [ - chef_priority(attr) | - attr := node.attributes[_].name - attr = attr_names[_] - ] + attr_names := ["source", "owner", "group", "mode", "action"] + order := [ + chef_priority(attr) | + attr := node.attributes[_].name + attr = attr_names[_] + ] - order != sort(order) + order != sort(order) - result := {{ + result := {{ "type": "design_misplaced_attribute", "element": node, "path": parent.path, - "description": "Misplaced attribute - The developers should try to follow the languages' style guides. These style guides define the expected attribute order." + "description": "Misplaced attribute - The developers should try to follow the languages' style guides. These style guides define the expected attribute order.", }} } # Puppet -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - endswith(parent.name, ".pp") - atomic_units := glitch_lib.all_atomic_units(parent) - node := atomic_units[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + endswith(parent.name, ".pp") + atomic_units := glitch_lib.all_atomic_units(parent) + node := atomic_units[_] - some n - n > 0 - node.attributes[n].name == "ensure" + some n + n > 0 + node.attributes[n].name == "ensure" - result := {{ + result := {{ "type": "design_misplaced_attribute", "element": node, "path": parent.path, - "description": "Misplaced attribute - The developers should try to follow the languages' style guides. These style guides define the expected attribute order." + "description": "Misplaced attribute - The developers should try to follow the languages' style guides. These style guides define the expected attribute order.", }} } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - endswith(parent.name, ".pp") +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + endswith(parent.name, ".pp") - some i - i >= 0 - i < count(parent.attributes) - 1 - parent.attributes[i].value != {} + some i + i >= 0 + i < count(parent.attributes) - 1 + parent.attributes[i].value != {} - result := {{ + result := { "type": "design_misplaced_attribute", "element": parent, "path": parent.path, - "description": "Misplaced attribute - The developers should try to follow the languages' style guides. These style guides define the expected attribute order." - }} + "description": "Misplaced attribute - The developers should try to follow the languages' style guides. These style guides define the expected attribute order.", + } } diff --git a/glitch/rego/queries/design/design_multifaceted_abstraction.rego b/glitch/rego/queries/design/design_multifaceted_abstraction.rego index 9120cc23..88ff4b6e 100644 --- a/glitch/rego/queries/design/design_multifaceted_abstraction.rego +++ b/glitch/rego/queries/design/design_multifaceted_abstraction.rego @@ -1,32 +1,36 @@ package glitch +import rego.v1 + import data.glitch_lib -checker(node) { - regex.match("(&&|;|\\|)", node.name) +checker(node) if { + regex.match("(&&|;|\\|)", node.name) } -checker(node) { - attr := node.attributes[_] - regex.match("(&&|;|\\|)", attr.value.value) + +checker(node) if { + attr := node.attributes[_] + regex.match("(&&|;|\\|)", attr.value.value) } -checker(node) { - attr := node.attributes[_] - regex.match("(&&|;|\\|)", attr.value.code) + +checker(node) if { + attr := node.attributes[_] + regex.match("(&&|;|\\|)", attr.value.code) } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - atomic_units := glitch_lib.all_atomic_units(parent) - node := atomic_units[_] - node.type == data.design.exec_atomic_units[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + atomic_units := glitch_lib.all_atomic_units(parent) + node := atomic_units[_] + node.type == data.design.exec_atomic_units[_] - checker(node) + checker(node) - result := {{ + result := { "type": "design_multifaceted_abstraction", "element": node, "path": parent.path, - "description": "Multifaceted Abstraction - Each block should only specify the properties of a single piece of software." - }} + "description": "Multifaceted Abstraction - Each block should only specify the properties of a single piece of software.", + } } diff --git a/glitch/rego/queries/design/implementation_too_many_variables.rego b/glitch/rego/queries/design/implementation_too_many_variables.rego index bae50cc6..3ba9ec36 100644 --- a/glitch/rego/queries/design/implementation_too_many_variables.rego +++ b/glitch/rego/queries/design/implementation_too_many_variables.rego @@ -1,23 +1,25 @@ package glitch +import rego.v1 + import data.glitch_lib -ALLOWED_TYPES = ["unkown", "script", "tasks"] +ALLOWED_TYPES := ["unkown", "script", "tasks"] -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - type := ALLOWED_TYPES[_] - parent.type = type +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + type := ALLOWED_TYPES[_] + parent.type = type - vars := glitch_lib.count_nodes_with_irtype(parent, "Variable") + vars := glitch_lib.count_nodes_with_irtype(parent, "Variable") - (vars / parent.lines) > 0.3 + vars / parent.lines > 0.3 - result := {{ + result := { "type": "implementation_too_many_variables", "element": parent, "path": parent.path, - "description": "Too many variables - The existence of too many variables in a single IaC script may reveal that the script is being used for too many purposes." - }} + "description": "Too many variables - The existence of too many variables in a single IaC script may reveal that the script is being used for too many purposes.", + } } diff --git a/glitch/rego/queries/library/glitch_lib.rego b/glitch/rego/queries/library/glitch_lib.rego index 26876f95..35f7ba2b 100644 --- a/glitch/rego/queries/library/glitch_lib.rego +++ b/glitch/rego/queries/library/glitch_lib.rego @@ -1,116 +1,119 @@ package glitch_lib -_gather_parent_unit_blocks[ub] { - walk(input, [_, ub]) - ub.ir_type == "UnitBlock" -} +import rego.v1 +_gather_parent_unit_blocks contains ub if { + walk(input, [_, ub]) + ub.ir_type == "UnitBlock" +} -all_atomic_units(node) = units { - units = {n | - walk(node, [path, n]) - n.ir_type == "AtomicUnit" - } +all_atomic_units(node) := units if { + units = {n | + walk(node, [path, n]) + n.ir_type == "AtomicUnit" + } } -all_attributes(node) = attrs { - attrs = {n | - walk(node, [path, n]) - n.ir_type == "Attribute" - # We only want KeyValues inside it, not itself - n.value.ir_type != "BlockExpr" - } +all_attributes(node) := attrs if { + attrs = {n | + walk(node, [path, n]) + n.ir_type == "Attribute" + + # We only want KeyValues inside it, not itself + n.value.ir_type != "BlockExpr" + } } -all_variables(node) = vars { - vars = {n | - walk(node, [path, n]) - n.ir_type == "Variable" - # We only want KeyValues inside it, not itself - n.value.ir_type != "BlockExpr" - } +all_variables(node) := vars if { + vars = {n | + walk(node, [path, n]) + n.ir_type == "Variable" + + # We only want KeyValues inside it, not itself + n.value.ir_type != "BlockExpr" + } } # This allows us to stop at the first level of conditional statement -all_conditional_statements(node) = conditions { - conditions = {n | - walk(node, [path, n]) - n.ir_type == "ConditionalStatement" - } +all_conditional_statements(node) := conditions if { + conditions = {n | + walk(node, [path, n]) + n.ir_type == "ConditionalStatement" + } } -count_nodes_with_irtype(root, t) = n { - n = count({ - v | - [_, v] := walk(root) - v.ir_type == t - }) +count_nodes_with_irtype(root, t) := n if { + n = count({ + v | + [_, v] := walk(root) + v.ir_type == t + }) } -traverse(node, pattern) { - # Use walk to safely traverse all nodes - walk(node, [path, n]) - check_leaf(n, pattern) # Only check leaf nodes +traverse(node, pattern) if { + # Use walk to safely traverse all nodes + walk(node, [path, n]) + check_leaf(n, pattern) # Only check leaf nodes } -check_leaf(node, pattern) { - node.ir_type == "String" - check_string(node, pattern) -} else { - node.ir_type == "MethodCall" - check_string(node.receiver, pattern) -} else { +check_leaf(node, pattern) if { + node.ir_type == "String" + check_string(node, pattern) +} else if { + node.ir_type == "MethodCall" + check_string(node.receiver, pattern) +} else if { node.ir_type == "Boolean" - check_boolean(node, pattern) -} else { - node.ir_type == "VariableReference" - check_string(node, pattern) -} else { - node.ir_type == "FunctionCall" - check_function_call(node, pattern) + check_boolean(node, pattern) +} else if { + node.ir_type == "VariableReference" + check_string(node, pattern) +} else if { + node.ir_type == "FunctionCall" + check_function_call(node, pattern) } -check_string(node, pattern) { - # If it is a string, - is_string(pattern) - regex.match(pattern, node.value) -} else { +check_string(node, pattern) if { + # If it is a string, + is_string(pattern) + regex.match(pattern, node.value) +} else if { # If it is an array or set - not is_string(pattern) - contains(node.value, pattern[_]) + not is_string(pattern) + has_substring(node.value, pattern[_]) } -check_boolean(node, value) { +check_boolean(node, value) if { node.value == value } # We are simply checking the name of the function called -check_function_call(node, pattern) { - # If it is a string, - is_string(pattern) - regex.match(pattern, node.name) -} else { +check_function_call(node, pattern) if { + # If it is a string, + is_string(pattern) + regex.match(pattern, node.name) +} else if { # If it is an array or set - not is_string(pattern) - contains(node.name, pattern[_]) + not is_string(pattern) + has_substring(node.name, pattern[_]) } # Implemented as a substitute for VarChecker, checks if there is at least one VariableReference and passes if there isn't -traverse_var(node) { - not has_variable_reference(node) +traverse_var(node) if { + not has_variable_reference(node) } -has_variable_reference(node) { - walk(node, [_, n]) - n.ir_type == "VariableReference" +has_variable_reference(node) if { + walk(node, [_, n]) + n.ir_type == "VariableReference" } # Check if a string contains a substring -contains(str, substr) { - regex.match(sprintf("(?i).*%s.*", [substr]), str) +has_substring(str, substr) if { + regex.match(sprintf("(?i).*%s.*", [substr]), str) } -is_ir_type_in(value, allowed) { - t := allowed[_] - value.ir_type == t -} \ No newline at end of file +is_ir_type_in(value, allowed) if { + t := allowed[_] + value.ir_type == t +} diff --git a/glitch/rego/queries/security/sec_def_admin.rego b/glitch/rego/queries/security/sec_def_admin.rego index b0727c31..369cf90d 100644 --- a/glitch/rego/queries/security/sec_def_admin.rego +++ b/glitch/rego/queries/security/sec_def_admin.rego @@ -1,8 +1,10 @@ package glitch +import rego.v1 + import data.glitch_lib -check_def_admin_pair(name, value) { +check_def_admin_pair(name, value) if { # Check if the name of the node matches any of the roles or users defined in security combined := array.concat(data.security.roles, data.security.users) element := combined[_] @@ -12,51 +14,51 @@ check_def_admin_pair(name, value) { # Check if there is not a VariableReference object glitch_lib.traverse_var(value) - # Check if it is a admin user + # Check if it is a admin user glitch_lib.traverse(value, data.security.admin) } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] # We need to use walk since we can have Hashs inside one another glitch_lib.is_ir_type_in(node.value, ["String"]) check_def_admin_pair(node.name, node.value) matched_node := node - result := {{ + result := { "type": "sec_def_admin", "element": matched_node, "path": parent.path, - "description": "Admin by default - Developers should always try to give the least privileges possible. Admin privileges may indicate a security problem. (CWE-250)" - }} + "description": "Admin by default - Developers should always try to give the least privileges possible. Admin privileges may indicate a security problem. (CWE-250)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] # We need to use walk since we can have Hashs inside one another walk(node, [_, n]) - n.ir_type == "Hash" + n.ir_type == "Hash" current_pair := n.value[_] glitch_lib.is_ir_type_in(current_pair.value, ["String"]) check_def_admin_pair(current_pair.key.value, current_pair.value) matched_node := current_pair - result := {{ + result := { "type": "sec_def_admin", "element": matched_node, "path": parent.path, - "description": "Admin by default - Developers should always try to give the least privileges possible. Admin privileges may indicate a security problem. (CWE-250)" - }} + "description": "Admin by default - Developers should always try to give the least privileges possible. Admin privileges may indicate a security problem. (CWE-250)", + } } diff --git a/glitch/rego/queries/security/sec_empty_pass.rego b/glitch/rego/queries/security/sec_empty_pass.rego index eca1492d..3ee71ca6 100644 --- a/glitch/rego/queries/security/sec_empty_pass.rego +++ b/glitch/rego/queries/security/sec_empty_pass.rego @@ -1,17 +1,19 @@ package glitch +import rego.v1 + import data.glitch_lib -whitelist_contains(name) { - whitelist := array.concat(data.security.secrets_white_list, data.security.profile) - whitelist[_] == name +whitelist_contains(name) if { + whitelist := array.concat(data.security.secrets_white_list, data.security.profile) + whitelist[_] == name } -checking_value(value) { +checking_value(value) if { value.ir_type == "String" value.value == "" -} else { +} else if { value.ir_type == "Null" value.value == null @@ -20,71 +22,69 @@ checking_value(value) { possible_value := null_values[_] - glitch_lib.contains(value.code, possible_value) -} else { + glitch_lib.has_substring(value.code, possible_value) +} else if { value.ir_type == "Undef" value.value == null - null_values := data.security.null_values - - glitch_lib.contains(value.code, "undef") -} + glitch_lib.has_substring(value.code, "undef") +} -check_pair_empty_password(name, value) { +check_pair_empty_password(name, value) if { hardcoded := data.security.passwords item := hardcoded[_] hard_coded_pattern := sprintf("[_A-Za-z0-9$/\\.\\[\\]-]*%s\\b", [item]) - + regex.match(hard_coded_pattern, name) - not whitelist_contains(lower(name)) + not whitelist_contains(lower(name)) glitch_lib.traverse_var(value) checking_value(value) } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] walk(node, [_, n]) - n.value.ir_type != "Hash" + n.value.ir_type != "Hash" check_pair_empty_password(node.name, node.value) matched_node := node - - result := {{ + + result := { "type": "sec_empty_pass", "element": matched_node, "path": parent.path, - "description": "Empty password - An empty password is indicative of a weak password which may lead to a security breach. (CWE-258)" - }} + "description": "Empty password - An empty password is indicative of a weak password which may lead to a security breach. (CWE-258)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] walk(node, [_, n]) n.ir_type == "Hash" current_pair := n.value[_] check_pair_empty_password(current_pair.key.value, current_pair.value) matched_node := current_pair - - result := {{ + + result := { "type": "sec_empty_pass", "element": matched_node, "path": parent.path, - "description": "Empty password - An empty password is indicative of a weak password which may lead to a security breach. (CWE-258)" - }} + "description": "Empty password - An empty password is indicative of a weak password which may lead to a security breach. (CWE-258)", + } } diff --git a/glitch/rego/queries/security/sec_full_permission_filesystem.rego b/glitch/rego/queries/security/sec_full_permission_filesystem.rego index 6cd31b61..9f4ce924 100644 --- a/glitch/rego/queries/security/sec_full_permission_filesystem.rego +++ b/glitch/rego/queries/security/sec_full_permission_filesystem.rego @@ -1,35 +1,37 @@ package glitch +import rego.v1 + import data.glitch_lib mode_set := {"mode", "m"} -check_permission(value) { - value.ir_type == "String" - regex.match("(?:^0?777$)|(?:(?:^|(?:ugo)|o|a)\\+[rwx]{3})", value.value) -} else { - value.ir_type == "Integer" - value.value == 777 +check_permission(value) if { + value.ir_type == "String" + regex.match("(?:^0?777$)|(?:(?:^|(?:ugo)|o|a)\\+[rwx]{3})", value.value) +} else if { + value.ir_type == "Integer" + value.value == 777 } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - atomic_units := glitch_lib.all_atomic_units(parent) - node := atomic_units[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + atomic_units := glitch_lib.all_atomic_units(parent) + node := atomic_units[_] - node.type == data.security.file_commands[_] + node.type == data.security.file_commands[_] - attrs := glitch_lib.all_attributes(node) - attr := attrs[_] + attrs := glitch_lib.all_attributes(node) + attr := attrs[_] - attr.name == mode_set[_] - check_permission(attr.value) + attr.name == mode_set[_] + check_permission(attr.value) - result := {{ + result := { "type": "sec_full_permission_filesystem", "element": attr, "path": parent.path, - "description": "Full permission to the filesystem - Files should not have full permissions to every user. (CWE-732)" - }} + "description": "Full permission to the filesystem - Files should not have full permissions to every user. (CWE-732)", + } } diff --git a/glitch/rego/queries/security/sec_hard_pass.rego b/glitch/rego/queries/security/sec_hard_pass.rego index 07845955..40ebbfe4 100644 --- a/glitch/rego/queries/security/sec_hard_pass.rego +++ b/glitch/rego/queries/security/sec_hard_pass.rego @@ -1,67 +1,68 @@ package glitch +import rego.v1 + import data.glitch_lib -whitelist_contains(name) { - whitelist := array.concat(data.security.secrets_white_list, data.security.profile) - whitelist[_] == name +whitelist_contains(name) if { + whitelist := array.concat(data.security.secrets_white_list, data.security.profile) + whitelist[_] == name } -check_pair_hard_password(name, value) { +check_pair_hard_password(name, value) if { hardcoded := data.security.passwords item := hardcoded[_] hard_coded_pattern := sprintf("[_A-Za-z0-9$/\\.\\[\\]-]*%s\\b", [item]) - + regex.match(hard_coded_pattern, lower(name)) - not whitelist_contains(lower(name)) + not whitelist_contains(lower(name)) glitch_lib.traverse_var(value) value.value != "" -} else { +} else if { # Check for sensitive data with secret value assignments sensitive_item := data.security.sensitive_data[_] - glitch_lib.contains(lower(name), lower(sensitive_item)) - + glitch_lib.has_substring(lower(name), lower(sensitive_item)) secret_value := data.security.secret_value_assign[_] - glitch_lib.contains(lower(value.value), lower(secret_value)) + glitch_lib.has_substring(lower(value.value), lower(secret_value)) # Exclude password cases (those will be handled by sec_hard_pass) - glitch_lib.contains(lower(secret_value), "password") + glitch_lib.has_substring(lower(secret_value), "password") } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] # We need to use walk since we can have Hashs inside one another walk(node, [_, n]) glitch_lib.is_ir_type_in(node.value, ["String", "Array"]) check_pair_hard_password(node.name, node.value) matched_node := node - - result := {{ + + result := { "type": "sec_hard_pass", "element": matched_node, "path": parent.path, - "description": "Hard-coded password - Developers should not reveal sensitive information in the source code. (CWE-259)" - }} + "description": "Hard-coded password - Developers should not reveal sensitive information in the source code. (CWE-259)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] # We need to use walk since we can have Hashs inside one another walk(node, [_, n]) @@ -70,11 +71,11 @@ Glitch_Analysis[result] { glitch_lib.is_ir_type_in(current_pair.value, ["String", "Array"]) check_pair_hard_password(current_pair.key.value, current_pair.value) matched_node := current_pair - - result := {{ + + result := { "type": "sec_hard_pass", "element": matched_node, "path": parent.path, - "description": "Hard-coded password - Developers should not reveal sensitive information in the source code. (CWE-259)" - }} + "description": "Hard-coded password - Developers should not reveal sensitive information in the source code. (CWE-259)", + } } diff --git a/glitch/rego/queries/security/sec_hard_secr.rego b/glitch/rego/queries/security/sec_hard_secr.rego index 184e04c7..adf60072 100644 --- a/glitch/rego/queries/security/sec_hard_secr.rego +++ b/glitch/rego/queries/security/sec_hard_secr.rego @@ -1,112 +1,113 @@ package glitch +import rego.v1 + import data.glitch_lib -whitelist_contains(name) { - whitelist := array.concat(data.security.secrets_white_list, data.security.profile) - whitelist[_] == name +whitelist_contains(name) if { + whitelist := array.concat(data.security.secrets_white_list, data.security.profile) + whitelist[_] == name } # Flatten variable names by converting brackets and quotes to dots -flatten_name(name) = flat_name { - step1 := replace(name, "['", ".") - step2 := replace(step1, "']", "") - step3 := replace(step2, "[\"", ".") - step4 := replace(step3, "\"]", "") - step5 := replace(step4, "[", ".") - step6 := replace(step5, "]", "") - - # Clean up multiple dots - step7 := replace(step6, "..", ".") - flat_name := trim(step7, ".") +flatten_name(name) := flat_name if { + step1 := replace(name, "['", ".") + step2 := replace(step1, "']", "") + step3 := replace(step2, "[\"", ".") + step4 := replace(step3, "\"]", "") + step5 := replace(step4, "[", ".") + step6 := replace(step5, "]", "") + + # Clean up multiple dots + step7 := replace(step6, "..", ".") + flat_name := trim(step7, ".") } -check_pair_hard_secr(name, value) { +check_pair_hard_secr(name, value) if { hardcoded := data.security.secrets item := hardcoded[_] hard_coded_pattern := sprintf("[_A-Za-z0-9$/\\.\\[\\]-]*%s\\b", [item]) - + regex.match(hard_coded_pattern, lower(name)) - not whitelist_contains(lower(name)) + not whitelist_contains(lower(name)) glitch_lib.traverse_var(value) -} else { - item := lower(data.security.ssh_dirs[_]) +} else if { + item := lower(data.security.ssh_dirs[_]) - glitch_lib.contains(lower(name), item) + glitch_lib.has_substring(lower(name), item) - pattern := ".*\\/id_rsa.*" + pattern := ".*\\/id_rsa.*" - glitch_lib.traverse(value, pattern) -} else { + glitch_lib.traverse(value, pattern) +} else if { # Check for sensitive data with secret value assignments sensitive_item := data.security.sensitive_data[_] - glitch_lib.contains(lower(name), lower(sensitive_item)) + glitch_lib.has_substring(lower(name), lower(sensitive_item)) secret_value := data.security.secret_value_assign[_] - glitch_lib.contains(lower(value.value), lower(secret_value)) + glitch_lib.has_substring(lower(value.value), lower(secret_value)) # Exclude password cases (those will be handled by sec_hard_pass) - not glitch_lib.contains(lower(secret_value), "password") - -} else { - item := data.security.misc_secrets[_] - flat_name := flatten_name(name) - - pattern := sprintf( - "([_A-Za-z0-9$-]*[-_]%s([-_].*)?$)|(^%s([-_].*)?$)", - [item, item] - ) - - regex.match(pattern, flat_name) - value.value != "" - glitch_lib.traverse_var(value) + not glitch_lib.has_substring(lower(secret_value), "password") +} else if { + item := data.security.misc_secrets[_] + flat_name := flatten_name(name) + + pattern := sprintf( + "([_A-Za-z0-9$-]*[-_]%s([-_].*)?$)|(^%s([-_].*)?$)", + [item, item], + ) + + regex.match(pattern, flat_name) + value.value != "" + glitch_lib.traverse_var(value) } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] # We need to use walk since we can have Hashs inside one another walk(node, [_, n]) - glitch_lib.is_ir_type_in(node.value, ["String", "Array"]) + glitch_lib.is_ir_type_in(node.value, ["String", "Array"]) check_pair_hard_secr(node.name, node.value) matched_node := node - - result := {{ + + result := { "type": "sec_hard_secr", "element": matched_node, "path": parent.path, - "description": "Hard-coded secret - Developers should not reveal sensitive information in the source code. (CWE-798)" - }} + "description": "Hard-coded secret - Developers should not reveal sensitive information in the source code. (CWE-798)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] # We need to use walk since we can have Hashs inside one another walk(node, [_, n]) n.ir_type == "Hash" current_pair := n.value[_] - glitch_lib.is_ir_type_in(current_pair.value, ["String", "Array"]) + glitch_lib.is_ir_type_in(current_pair.value, ["String", "Array"]) check_pair_hard_secr(current_pair.key.value, current_pair.value) matched_node := current_pair - - result := {{ + + result := { "type": "sec_hard_secr", "element": matched_node, "path": parent.path, - "description": "Hard-coded secret - Developers should not reveal sensitive information in the source code. (CWE-798)" - }} -} \ No newline at end of file + "description": "Hard-coded secret - Developers should not reveal sensitive information in the source code. (CWE-798)", + } +} diff --git a/glitch/rego/queries/security/sec_hard_user.rego b/glitch/rego/queries/security/sec_hard_user.rego index 6a6eca5c..ecedc1f0 100644 --- a/glitch/rego/queries/security/sec_hard_user.rego +++ b/glitch/rego/queries/security/sec_hard_user.rego @@ -1,55 +1,56 @@ package glitch +import rego.v1 + import data.glitch_lib -whitelist_contains(name) { - whitelist := array.concat(data.security.secrets_white_list, data.security.profile) - whitelist[_] == name +whitelist_contains(name) if { + whitelist := array.concat(data.security.secrets_white_list, data.security.profile) + whitelist[_] == name } - -check_pair_hard_users(name, value) { +check_pair_hard_users(name, value) if { hardcoded := data.security.users item := hardcoded[_] hard_coded_pattern := sprintf("[_A-Za-z0-9$/\\.\\[\\]-]*%s\\b", [item]) - + regex.match(hard_coded_pattern, lower(name)) - not whitelist_contains(lower(name)) + not whitelist_contains(lower(name)) glitch_lib.traverse_var(value) } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] # We need to use walk since we can have Hashs inside one another walk(node, [_, n]) glitch_lib.is_ir_type_in(node.value, ["String", "Null", "Array"]) check_pair_hard_users(node.name, node.value) matched_node := node - - result := {{ + + result := { "type": "sec_hard_user", "element": matched_node, "path": parent.path, - "description": "Hard-coded user - Developers should not reveal sensitive information in the source code. (CWE-798)" - }} + "description": "Hard-coded user - Developers should not reveal sensitive information in the source code. (CWE-798)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] # We need to use walk since we can have Hashs inside one another walk(node, [_, n]) @@ -58,11 +59,11 @@ Glitch_Analysis[result] { glitch_lib.is_ir_type_in(current_pair.value, ["String", "Null", "Array"]) check_pair_hard_users(current_pair.key.value, current_pair.value) matched_node := current_pair - - result := {{ + + result := { "type": "sec_hard_user", "element": matched_node, "path": parent.path, - "description": "Hard-coded user - Developers should not reveal sensitive information in the source code. (CWE-798)" - }} -} \ No newline at end of file + "description": "Hard-coded user - Developers should not reveal sensitive information in the source code. (CWE-798)", + } +} diff --git a/glitch/rego/queries/security/sec_https.rego b/glitch/rego/queries/security/sec_https.rego index 1d8ec5c9..1a4ab79d 100644 --- a/glitch/rego/queries/security/sec_https.rego +++ b/glitch/rego/queries/security/sec_https.rego @@ -1,121 +1,122 @@ package glitch +import rego.v1 + import data.glitch_lib url_regex := "^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?(?:[a-zA-Z0-9]+([_\\-\\.][a-zA-Z0-9]+)*\\.[a-zA-Z]{2,}|(?:[0-9]{1,3}\\.){3}[0-9]{1,3})(:[0-9]{1,5})?(\\/.*)?$" -normalize_url(url) = out { - # Remove backslash line breaks - use replace instead of regex.replace - no_breaks := replace(url, "\\\n", "") - - # Remove internal quotes - no_quotes := replace(no_breaks, "'", "") - no_quotes2 := replace(no_quotes, "\"", "") - - # Collapse whitespace - out := replace(no_quotes2, " ", "") +normalize_url(url) := out if { + # Remove backslash line breaks - use replace instead of regex.replace + no_breaks := replace(url, "\\\n", "") + + # Remove internal quotes + no_quotes := replace(no_breaks, "'", "") + no_quotes2 := replace(no_quotes, "\"", "") + + # Collapse whitespace + out := replace(no_quotes2, " ", "") } + # In this case, I am assuming that link strings are either in a Sum node or String node -check_http_without_tls(node) { - node.ir_type == "Sum" - url := lower(normalize_url(node.code)) +check_http_without_tls(node) if { + node.ir_type == "Sum" + url := lower(normalize_url(node.code)) url_is_http(url) not url_is_whitelisted(url) -} else { - node.ir_type == "String" +} else if { + node.ir_type == "String" url_is_http(node.value) not url_is_whitelisted(node.value) } # Check if string contains template variables like {{ var }}, $var, ${var}, etc. -has_template_variable(url) { - regex.match(".*\\{\\{.*\\}\\}.*", url) # Matches {{ ... }} -} else { - regex.match(".*\\$\\{.*\\}.*", url) # Matches ${ ... } -} else { - regex.match(".*\\$[a-zA-Z_][a-zA-Z0-9_]*.*", url) # Matches $var +has_template_variable(url) if { + regex.match(".*\\{\\{.*\\}\\}.*", url) # Matches {{ ... }} +} else if { + regex.match(".*\\$\\{.*\\}.*", url) # Matches ${ ... } +} else if { + regex.match(".*\\$[a-zA-Z_][a-zA-Z0-9_]*.*", url) # Matches $var } -url_is_http(url) { +url_is_http(url) if { # If it has template variables, skip regex validation has_template_variable(url) url_has_http_or_www(lower(url)) - not glitch_lib.contains(url, "https://") -} else { + not glitch_lib.has_substring(url, "https://") +} else if { # If no template variables, use full regex validation regex.match(url_regex, lower(url)) url_has_http_or_www(lower(url)) - not glitch_lib.contains(url, "https://") + not glitch_lib.has_substring(url, "https://") } -url_has_http_or_www(url) { - glitch_lib.contains(url, "http://") -} else { - glitch_lib.contains(url, "www") +url_has_http_or_www(url) if { + glitch_lib.has_substring(url, "http://") +} else if { + glitch_lib.has_substring(url, "www") } -url_is_whitelisted(url) { +url_is_whitelisted(url) if { host := extract_hostname(url) host == data.security.url_http_white_list[_] } -extract_hostname(value) = output { - parts := split(value, "://") - rest := select_rest(parts, value) - host_port := split(rest, "/")[0] - output := split(host_port, ":")[0] -} else = "" { - true -} +extract_hostname(value) := output if { + parts := split(value, "://") + rest := select_rest(parts, value) + host_port := split(rest, "/")[0] + output := split(host_port, ":")[0] +} else := "" -select_rest(parts, original) = output { - count(parts) > 1 - output := parts[1] -} else = output { - output := trim_left(original, "/") +select_rest(parts, original) := output if { + count(parts) > 1 + output := parts[1] +} else := output if { + output := trim_left(original, "/") } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] - # We need to use walk since we can have Hashs inside one another + # We need to use walk since we can have Hashs inside one another walk(node, [_, n]) - n.value.ir_type != "Hash" - check_http_without_tls(n.value) - matched_node := n + n.value.ir_type != "Hash" + check_http_without_tls(n.value) + matched_node := n - result := {{ + result := { "type": "sec_https", "element": matched_node, "path": parent.path, - "description": "Use of HTTP without TLS - The developers should always favor the usage of HTTPS. (CWE-319)" - }} + "description": "Use of HTTP without TLS - The developers should always favor the usage of HTTPS. (CWE-319)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] - # We need to use walk since we can have Hashs inside one another + # We need to use walk since we can have Hashs inside one another walk(node, [_, n]) - n.ir_type == "Hash" - current_pair := n.value[_] - check_http_without_tls(current_pair.value) - matched_node := current_pair + n.ir_type == "Hash" + current_pair := n.value[_] + check_http_without_tls(current_pair.value) + matched_node := current_pair - result := {{ + result := { "type": "sec_https", "element": matched_node, "path": parent.path, - "description": "Use of HTTP without TLS - The developers should always favor the usage of HTTPS. (CWE-319)" - }} -} \ No newline at end of file + "description": "Use of HTTP without TLS - The developers should always favor the usage of HTTPS. (CWE-319)", + } +} diff --git a/glitch/rego/queries/security/sec_invalid_bind.rego b/glitch/rego/queries/security/sec_invalid_bind.rego index 8b75da78..40912f7b 100644 --- a/glitch/rego/queries/security/sec_invalid_bind.rego +++ b/glitch/rego/queries/security/sec_invalid_bind.rego @@ -1,64 +1,66 @@ package glitch +import rego.v1 + import data.glitch_lib ipv6_not_allowed_strings := {"*", "::"} -check_inv_bind(name, value) { +check_inv_bind(name, value) if { # Check the value of the attribute is 0.0.0.0 glitch_lib.traverse(value, "^(https?://)?0\\.0\\.0\\.0.*") -} else { - # Check if it is a ipv6 wildcard with the name of ip - name == "ip" +} else if { + # Check if it is a ipv6 wildcard with the name of ip + name == "ip" glitch_lib.traverse(value, ipv6_not_allowed_strings) -} else { - # Check if it is a ipv6 wildcard with the name in config - name == data.security.ip_binding_commands[_] +} else if { + # Check if it is a ipv6 wildcard with the name in config + name == data.security.ip_binding_commands[_] glitch_lib.traverse(value, ipv6_not_allowed_strings) -} else { - # Check if it is a boolean with the name in config - value.ir_type == "Boolean" - name == data.security.ip_binding_commands[_] - value.value == "true" +} else if { + # Check if it is a boolean with the name in config + value.ir_type == "Boolean" + name == data.security.ip_binding_commands[_] + value.value == "true" } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] - glitch_lib.is_ir_type_in(node.value, ["String"]) + glitch_lib.is_ir_type_in(node.value, ["String"]) check_inv_bind(node.name, node.value) matched_node := node - result := {{ + result := { "type": "sec_invalid_bind", "element": matched_node, "path": parent.path, - "description": "Invalid IP address binding - Binding to the address 0.0.0.0 allows connections from every possible network which might be a security issues. (CWE-284)" - }} + "description": "Invalid IP address binding - Binding to the address 0.0.0.0 allows connections from every possible network which might be a security issues. (CWE-284)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] - walk(node, [_, n]) - n.ir_type == "Hash" + walk(node, [_, n]) + n.ir_type == "Hash" current_pair := n.value[_] - glitch_lib.is_ir_type_in(current_pair.value, ["String"]) + glitch_lib.is_ir_type_in(current_pair.value, ["String"]) check_inv_bind(current_pair.key.value, current_pair.value) matched_node := current_pair - result := {{ + result := { "type": "sec_invalid_bind", "element": matched_node, "path": parent.path, - "description": "Invalid IP address binding - Binding to the address 0.0.0.0 allows connections from every possible network which might be a security issues. (CWE-284)" - }} -} \ No newline at end of file + "description": "Invalid IP address binding - Binding to the address 0.0.0.0 allows connections from every possible network which might be a security issues. (CWE-284)", + } +} diff --git a/glitch/rego/queries/security/sec_no_default_switch.rego b/glitch/rego/queries/security/sec_no_default_switch.rego index 84dcdf14..7179581e 100644 --- a/glitch/rego/queries/security/sec_no_default_switch.rego +++ b/glitch/rego/queries/security/sec_no_default_switch.rego @@ -1,41 +1,43 @@ package glitch +import rego.v1 + import data.glitch_lib -path_contains_statements(path) { - some i - path[i] == "statements" +path_contains_statements(path) if { + some i + path[i] == "statements" } -has_default_case(node) { - some path, value - walk(node, [path, value]) - count(path) > 0 - last := path[count(path) - 1] - last == "is_default" - value == true - not path_contains_statements(path) +has_default_case(node) if { + some path, value + walk(node, [path, value]) + count(path) > 0 + last := path[count(path) - 1] + last == "is_default" + value == true + not path_contains_statements(path) } -check_missing_default(node) { +check_missing_default(node) if { not has_default_case(node) -} +} -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - conditions := glitch_lib.all_conditional_statements(parent) - node := conditions[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + conditions := glitch_lib.all_conditional_statements(parent) + node := conditions[_] - node.is_top == true - node.type == "SWITCH" - - check_missing_default(node) + node.is_top == true + node.type == "SWITCH" - result := {{ + check_missing_default(node) + + result := { "type": "sec_no_default_switch", "element": node, "path": parent.path, - "description": "Missing default case statement - Not handling every possible input combination might allow an attacker to trigger an error for an unhandled value. (CWE-478)" - }} -} \ No newline at end of file + "description": "Missing default case statement - Not handling every possible input combination might allow an attacker to trigger an error for an unhandled value. (CWE-478)", + } +} diff --git a/glitch/rego/queries/security/sec_no_int_check.rego b/glitch/rego/queries/security/sec_no_int_check.rego index a52443d3..74b9c2e2 100644 --- a/glitch/rego/queries/security/sec_no_int_check.rego +++ b/glitch/rego/queries/security/sec_no_int_check.rego @@ -1,77 +1,80 @@ package glitch +import rego.v1 + import data.glitch_lib -attr_has_any_checksum(attr, checksum_values) { - check := checksum_values[_] - pattern := sprintf("(?i).*%s.*", [check]) - # We use traverse so it can find all strings and test them inside - glitch_lib.traverse(attr, pattern) +attr_has_any_checksum(attr, checksum_values) if { + check := checksum_values[_] + pattern := sprintf("(?i).*%s.*", [check]) + + # We use traverse so it can find all strings and test them inside + glitch_lib.traverse(attr, pattern) } -check_integrity_check_atomic_unit(node) { - attributes = glitch_lib.all_attributes(node) - - attr = attributes[_] - - download = data.security.download_extensions[_] - - # We use code, since it can be a SUM atribute - regex.match(sprintf("(http|https|www)[^ ,]*\\.%s", [download]), attr.code) - - checksum_values = data.security.checksum - - attributes_without_checksum := {attr | - attr := attributes[_] - not attr_has_any_checksum(attr, checksum_values) - } - - # Trigger integrity check only if ALL attributes lack a checksum keyword - count(attributes_without_checksum) == count(attributes) +check_integrity_check_atomic_unit(node) if { + attributes = glitch_lib.all_attributes(node) + + attr = attributes[_] + + download = data.security.download_extensions[_] + + # We use code, since it can be a SUM atribute + regex.match(sprintf("(http|https|www)[^ ,]*\\.%s", [download]), attr.code) + + checksum_values = data.security.checksum + + attributes_without_checksum := {attr | + attr := attributes[_] + not attr_has_any_checksum(attr, checksum_values) + } + + # Trigger integrity check only if ALL attributes lack a checksum keyword + count(attributes_without_checksum) == count(attributes) } -check_integrity_check_keyvalues(node) { - value = data.security.checksum[_] - glitch_lib.contains(node.name, value) - false_pattern = "^(?i)(no|false)$" - glitch_lib.traverse(node, false_pattern) -} else { - # This case is repeated since for puppet it becames a boolean and ansible a string - value = data.security.checksum[_] - glitch_lib.contains(node.name, value) - glitch_lib.traverse(node, false) +check_integrity_check_keyvalues(node) if { + value = data.security.checksum[_] + glitch_lib.has_substring(node.name, value) + false_pattern = "^(?i)(no|false)$" + glitch_lib.traverse(node, false_pattern) +} else if { + # This case is repeated since for puppet it becames a boolean and ansible a string + value = data.security.checksum[_] + glitch_lib.has_substring(node.name, value) + glitch_lib.traverse(node, false) } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - atomic_units := glitch_lib.all_atomic_units(parent) - node := atomic_units[_] - - check_integrity_check_atomic_unit(node) - - result := { - "type": "sec_no_int_check", - "element": node, - "path": parent.path, - "description": "No integrity check - The content of files downloaded from the internet should be checked. (CWE-353)" - } +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + atomic_units := glitch_lib.all_atomic_units(parent) + node := atomic_units[_] + + check_integrity_check_atomic_unit(node) + + result := { + "type": "sec_no_int_check", + "element": node, + "path": parent.path, + "description": "No integrity check - The content of files downloaded from the internet should be checked. (CWE-353)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] - - check_integrity_check_keyvalues(node) - - result := { - "type": "sec_no_int_check", - "element": node, - "path": parent.path, - "description": "No integrity check - The content of files downloaded from the internet should be checked. (CWE-353)" - } +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] + + check_integrity_check_keyvalues(node) + + result := { + "type": "sec_no_int_check", + "element": node, + "path": parent.path, + "description": "No integrity check - The content of files downloaded from the internet should be checked. (CWE-353)", + } } diff --git a/glitch/rego/queries/security/sec_obsolete_command.rego b/glitch/rego/queries/security/sec_obsolete_command.rego index 3b26a9cd..18eb4d14 100644 --- a/glitch/rego/queries/security/sec_obsolete_command.rego +++ b/glitch/rego/queries/security/sec_obsolete_command.rego @@ -1,40 +1,42 @@ package glitch +import rego.v1 + import data.glitch_lib -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - atomic_units := glitch_lib.all_atomic_units(parent) - node := atomic_units[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + atomic_units := glitch_lib.all_atomic_units(parent) + node := atomic_units[_] - node.type == data.security.obsolete_commands[_] + node.type == data.security.obsolete_commands[_] - result := { - "type": "sec_obsolete_command", - "element": node, - "path": parent.path, - "description": "Use of obsolete command or function - Avoid using obsolete or deprecated commands and functions. (CWE-477)" - } + result := { + "type": "sec_obsolete_command", + "element": node, + "path": parent.path, + "description": "Use of obsolete command or function - Avoid using obsolete or deprecated commands and functions. (CWE-477)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - atomic_units := glitch_lib.all_atomic_units(parent) - node := atomic_units[_] - - endswith(node.type, data.security.shell_resources[_]) - attrs := glitch_lib.all_attributes(node) - attr := attrs[_] - attr.value.ir_type == "String" - cmd := split(attr.value.value, " ")[0] - cmd == data.security.obsolete_commands[_] - - result := { - "type": "sec_obsolete_command", - "element": attr, - "path": parent.path, - "description": "Use of obsolete command or function - Avoid using obsolete or deprecated commands and functions. (CWE-477)" - } -} \ No newline at end of file +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + atomic_units := glitch_lib.all_atomic_units(parent) + node := atomic_units[_] + + endswith(node.type, data.security.shell_resources[_]) + attrs := glitch_lib.all_attributes(node) + attr := attrs[_] + attr.value.ir_type == "String" + cmd := split(attr.value.value, " ")[0] + cmd == data.security.obsolete_commands[_] + + result := { + "type": "sec_obsolete_command", + "element": attr, + "path": parent.path, + "description": "Use of obsolete command or function - Avoid using obsolete or deprecated commands and functions. (CWE-477)", + } +} diff --git a/glitch/rego/queries/security/sec_susp_comm.rego b/glitch/rego/queries/security/sec_susp_comm.rego index 85009b30..33b7e497 100644 --- a/glitch/rego/queries/security/sec_susp_comm.rego +++ b/glitch/rego/queries/security/sec_susp_comm.rego @@ -1,8 +1,10 @@ package glitch +import rego.v1 + import data.glitch_lib -check_susp_comment(comment) { +check_susp_comment(comment) if { lines := split(comment.content, "\n") line := lines[_] @@ -11,21 +13,21 @@ check_susp_comment(comment) { element := data.security.suspicious_words[_] pattern := sprintf(".*%s.*", [element]) - - regex.match(pattern, lower_line) -} -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] + regex.match(pattern, lower_line) +} + +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] parent.path != "" - comment := parent.comments[_] + comment := parent.comments[_] + + check_susp_comment(comment) - check_susp_comment(comment) - - result := {{ + result := { "type": "sec_susp_comm", "element": comment, "path": parent.path, - "description": "Suspicious comment - Comments with keywords such as TODO HACK or FIXME may reveal problems possibly exploitable. (CWE-546)" - }} + "description": "Suspicious comment - Comments with keywords such as TODO HACK or FIXME may reveal problems possibly exploitable. (CWE-546)", + } } diff --git a/glitch/rego/queries/security/sec_weak_crypt.rego b/glitch/rego/queries/security/sec_weak_crypt.rego index c5ba7a01..d6d64069 100644 --- a/glitch/rego/queries/security/sec_weak_crypt.rego +++ b/glitch/rego/queries/security/sec_weak_crypt.rego @@ -1,71 +1,73 @@ package glitch +import rego.v1 + import data.glitch_lib -name_in_whitelist(name) { - glitch_lib.contains(name, data.security.weak_crypt_whitelist[_]) +name_in_whitelist(name) if { + glitch_lib.has_substring(name, data.security.weak_crypt_whitelist[_]) } -check_weak_crypt(value, name) { - glitch_lib.traverse(value, data.security.weak_crypt) - not glitch_lib.traverse(value, data.security.weak_crypt_whitelist) - not name_in_whitelist(name) +check_weak_crypt(value, name) if { + glitch_lib.traverse(value, data.security.weak_crypt) + not glitch_lib.traverse(value, data.security.weak_crypt_whitelist) + not name_in_whitelist(name) } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] - node.value.ir_type != "Hash" - check_weak_crypt(node.value, node.name) - matched_node := node + node.value.ir_type != "Hash" + check_weak_crypt(node.value, node.name) + matched_node := node - result := { - "type": "sec_weak_crypt", - "element": matched_node, - "path": parent.path, - "description": "Weak Crypto Algorithm - Weak crypto algorithms should be avoided since they are susceptible to security issues. (CWE-326 | CWE-327)" - } + result := { + "type": "sec_weak_crypt", + "element": matched_node, + "path": parent.path, + "description": "Weak Crypto Algorithm - Weak crypto algorithms should be avoided since they are susceptible to security issues. (CWE-326 | CWE-327)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - attr := glitch_lib.all_attributes(parent) - variables := glitch_lib.all_variables(parent) - all_nodes := attr | variables - node := all_nodes[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + attr := glitch_lib.all_attributes(parent) + variables := glitch_lib.all_variables(parent) + all_nodes := attr | variables + node := all_nodes[_] - walk(node, [_, n]) - n.ir_type == "Hash" + walk(node, [_, n]) + n.ir_type == "Hash" current_pair := n.value[_] - check_weak_crypt(current_pair.value, current_pair.key.value) + check_weak_crypt(current_pair.value, current_pair.key.value) matched_node := current_pair - result := { - "type": "sec_weak_crypt", - "element": matched_node, - "path": parent.path, - "description": "Weak Crypto Algorithm - Weak crypto algorithms should be avoided since they are susceptible to security issues. (CWE-326 | CWE-327)" - } + result := { + "type": "sec_weak_crypt", + "element": matched_node, + "path": parent.path, + "description": "Weak Crypto Algorithm - Weak crypto algorithms should be avoided since they are susceptible to security issues. (CWE-326 | CWE-327)", + } } -Glitch_Analysis[result] { - parent := glitch_lib._gather_parent_unit_blocks[_] - parent.path != "" - atomic_units := glitch_lib.all_atomic_units(parent) - node := atomic_units[_] +Glitch_Analysis contains result if { + parent := glitch_lib._gather_parent_unit_blocks[_] + parent.path != "" + atomic_units := glitch_lib.all_atomic_units(parent) + node := atomic_units[_] - check_weak_crypt(node.type, node.name) + check_weak_crypt(node.type, node.name) - result := { - "type": "sec_weak_crypt", - "element": node, - "path": parent.path, - "description": "Weak Crypto Algorithm - Weak crypto algorithms should be avoided since they are susceptible to security issues. (CWE-326 | CWE-327)" - } + result := { + "type": "sec_weak_crypt", + "element": node, + "path": parent.path, + "description": "Weak Crypto Algorithm - Weak crypto algorithms should be avoided since they are susceptible to security issues. (CWE-326 | CWE-327)", + } } diff --git a/glitch/rego/rego_python/pyproject.toml b/glitch/rego/rego_python/pyproject.toml index 1356b5db..90839112 100644 --- a/glitch/rego/rego_python/pyproject.toml +++ b/glitch/rego/rego_python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "rego_python" -version = "0.2.0" +version = "0.3.0" description = "Python wrapper for Rego engine" authors = [ {name = "Daniel Carvalho", email = "daniel.m.carvalho2002@gmail.com"}, diff --git a/glitch/rego/rego_python/src/rego_python/go/regolib.go b/glitch/rego/rego_python/src/rego_python/go/regolib.go index 2a4254cd..c0c36aba 100644 --- a/glitch/rego/rego_python/src/rego_python/go/regolib.go +++ b/glitch/rego/rego_python/src/rego_python/go/regolib.go @@ -42,6 +42,7 @@ func RunRego(inputJSON *C.char, dataJSON *C.char, modulesJSON *C.char) *C.char { regoArgs = append(regoArgs, rego.Query("data.glitch.Glitch_Analysis")) regoArgs = append(regoArgs, rego.Input(inputVal)) regoArgs = append(regoArgs, rego.Store(store)) + regoArgs = append(regoArgs, rego.Strict(true)) for name, code := range moduleMap { regoArgs = append(regoArgs, rego.Module(name, code)) @@ -56,6 +57,10 @@ func RunRego(inputJSON *C.char, dataJSON *C.char, modulesJSON *C.char) *C.char { return C.CString(fmt.Sprintf(`{"error": "rego evaluation failed: %s"}`, err)) } + if len(results) == 0 { + return C.CString(`{"error":"query returned undefined"}`) + } + out, err := json.Marshal(results) if err != nil { return C.CString(fmt.Sprintf(`{"error": "output serialization failed: %s"}`, err))