Skip to content

Commit 19cc785

Browse files
authored
Merge pull request #2570 from bridgecrewio/new_yaml_operator_jsonpath_exists
new operator jsonpath exists
2 parents 35a3d17 + c4115e1 commit 19cc785

File tree

15 files changed

+213
-22
lines changed

15 files changed

+213
-22
lines changed

checkov/common/checks_infra/checks_parser.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
LessThanAttributeSolver,
2828
LessThanOrEqualAttributeSolver,
2929
JsonpathEqualsAttributeSolver,
30+
JsonpathExistsAttributeSolver
3031
)
3132
from checkov.common.checks_infra.solvers.attribute_solvers.not_subset_attribute_solver import NotSubsetAttributeSolver
3233
from checkov.common.checks_infra.solvers.attribute_solvers.subset_attribute_solver import SubsetAttributeSolver
@@ -57,7 +58,8 @@
5758
"less_than_or_equal": LessThanOrEqualAttributeSolver,
5859
"subset": SubsetAttributeSolver,
5960
"not_subset": NotSubsetAttributeSolver,
60-
"jsonpath_equals": JsonpathEqualsAttributeSolver
61+
"jsonpath_equals": JsonpathEqualsAttributeSolver,
62+
"jsonpath_exists": JsonpathExistsAttributeSolver
6163
}
6264

6365
operators_to_complex_solver_classes = {

checkov/common/checks_infra/solvers/attribute_solvers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@
1717
from checkov.common.checks_infra.solvers.attribute_solvers.less_than_attribute_solver import LessThanAttributeSolver
1818
from checkov.common.checks_infra.solvers.attribute_solvers.less_than_or_equal_attribute_solver import LessThanOrEqualAttributeSolver
1919
from checkov.common.checks_infra.solvers.attribute_solvers.jsonpath_equals_attribute_solver import JsonpathEqualsAttributeSolver
20+
from checkov.common.checks_infra.solvers.attribute_solvers.jsonpath_exists_attribute_solver import JsonpathExistsAttributeSolver
21+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from typing import List, Optional, Any, Dict
2+
3+
from checkov.common.checks_infra.solvers.attribute_solvers.jsonpath_equals_attribute_solver import JsonpathEqualsAttributeSolver
4+
from checkov.common.graph.checks_infra.enums import Operators
5+
6+
7+
class JsonpathExistsAttributeSolver(JsonpathEqualsAttributeSolver):
8+
operator = Operators.JSONPATH_EXISTS
9+
10+
def __init__(self, resource_types: List[str], attribute: Optional[str], value: Any) -> None:
11+
super().__init__(resource_types=resource_types,
12+
attribute=attribute, value=value)
13+
14+
def _get_operation(self, vertex: Dict[str, Any], attribute: Optional[str]) -> bool:
15+
return vertex.get(attribute) is not None
16+

checkov/common/graph/checks_infra/enums.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,4 @@ class Operators:
4545
AND = 'and'
4646
OR = 'or'
4747
JSONPATH_EQUALS = 'jsonpath_equals'
48+
JSONPATH_EXISTS = 'jsonpath_exists'

docs/3.Custom Policies/YAML Custom Policies.md

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -90,33 +90,22 @@ definition:
9090
| Not Ends With | `not_ending_with` |
9191
| Greater Than | `greater_than` |
9292
| Greater Than Or Equal | `greater_than_or_equal` |
93-
| Less Than | `less_than` |
94-
| Less Than Or Equal | `less_than_or_equal` |
95-
| Subset | `subset` |
96-
| Not Subset | `not_subset` |
97-
98-
### Attribute Condition: Keys and Values
99-
100-
| Key | Type | Value(s) |
101-
| --- | --- | --- |
102-
| `cond_type` | string | Must be `attribute` |
103-
| `resource_type` | collection of strings | Use either `all` or `[resource types from list]` |
104-
| `attribute` | string | Attribute of defined resource types. For example, `automated_snapshot_retention_period` |
105-
| `operator` | string | - `equals`, `not_equals`, `regex_match`, `not_regex_match`, `exists`, `not exists`, `any`, `contains`, `not_contains`, `within`, `starting_with`, `not_starting_with`, `ending_with`, `not_ending_with`, `greater_than`, `greater_than_or_equal`, `less_than`, `less_than_or_equal`, `subset`, `not_subset` |
106-
| `value` (not relevant for operator: `exists`/`not_exists`) | string | User input. |
10793
| Less Than | `less_than` |
10894
| Less Than Or Equal | `less_than_or_equal` |
95+
| Subset | `subset` |
96+
| Not Subset | `not_subset` |
10997
| Json Path Equals | `jsonpath_equals` |
98+
| Json Path Exists | `jsonpath_exists` |
11099

111100
### Attribute Condition: Keys and Values
112101

113-
| Key | Type | Value(s) |
114-
| --- | --- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
115-
| `cond_type` | string | Must be `attribute` |
116-
| `resource_type` | collection of strings | Use either `all` or `[resource types from list]` |
117-
| `attribute` | string | Attribute of defined resource types. For example, `automated_snapshot_retention_period` |
118-
| `operator` | string | - `equals`, `not_equals`, `regex_match`, `not_regex_match`, `exists`, `not exists`, `any`, `contains`, `not_contains`, `within`, `starting_with`, `not_starting_with`, `ending_with`, `not_ending_with`, `greater_than`, `greater_than_or_equal`, `less_than`, `less_than_or_equal`, `jsonpath_equals` |
119-
| `value` (not relevant for operator: `exists`/`not_exists`) | string | User input. |
102+
| Key | Type | Value(s) |
103+
| --- | --- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
104+
| `cond_type` | string | Must be `attribute` |
105+
| `resource_type` | collection of strings | Use either `all` or `[resource types from list]` |
106+
| `attribute` | string | Attribute of defined resource types. For example, `automated_snapshot_retention_period` |
107+
| `operator` | string | - `equals`, `not_equals`, `regex_match`, `not_regex_match`, `exists`, `not exists`, `any`, `contains`, `not_contains`, `within`, `starting_with`, `not_starting_with`, `ending_with`, `not_ending_with`, `greater_than`, `greater_than_or_equal`, `less_than`, `less_than_or_equal`, `jsonpath_equals`, `jsonpath_exists` |
108+
| `value` (not relevant for operator: `exists`/`not_exists`) | string | User input. |
120109

121110

122111
## Connection State Block
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
metadata:
2+
id: "AzureSecureRule"
3+
scope:
4+
provider: "AZURE"
5+
definition:
6+
cond_type: "attribute"
7+
resource_types:
8+
- "azurerm_network_security_group"
9+
attribute: "security_rule[?(@.name == 'rule_we_care_about')].source_address_prefixes[*]"
10+
operator: "jsonpath_equals"
11+
value: "allowed_ip"

tests/terraform/graph/checks_infra/attribute_solvers/jsonpath_equals_solver/test_solver.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,12 @@ def test_jsonpath_equals_solver_wildcard(self):
2727
expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}
2828

2929
self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
30+
31+
def test_jsonpath_equals_azure_rule(self):
32+
root_folder = '../../../resources/azure_secure_rule'
33+
check_id = "AzureSecureRule"
34+
should_pass = ['azurerm_network_security_group.sg_fail']
35+
should_fail = ['azurerm_network_security_group.sg_fail2']
36+
expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}
37+
38+
self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
metadata:
2+
id: "AzureSecureRule"
3+
scope:
4+
provider: "AZURE"
5+
definition:
6+
cond_type: "attribute"
7+
resource_types:
8+
- "azurerm_resource_group"
9+
- "azurerm_network_security_group"
10+
attribute: "security_rule[?(@.name == 'rule_we_do not_care_about')].source_address_prefixes"
11+
operator: "jsonpath_exists"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
metadata:
2+
id: "CkSshPortOpenForAll"
3+
scope:
4+
provider: "AWS"
5+
definition:
6+
and:
7+
- cond_type: "attribute"
8+
resource_types:
9+
- "aws_security_group"
10+
attribute: "ingress[?(@.to_port == 22 & @.from_port == 22)].cidr_blocks[*]"
11+
operator: "jsonpath_exists"
12+
- cond_type: "attribute"
13+
resource_types:
14+
- "aws_security_group"
15+
attribute: "ingress[?(@.to_port == 443 & @.from_port == 443)].cidr_blocks[?(@ == '8.0.4.19/92')]"
16+
operator: "jsonpath_exists"
17+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
metadata:
2+
id: "PublicDBSG"
3+
scope:
4+
provider: "AWS"
5+
definition:
6+
cond_type: "attribute"
7+
resource_types:
8+
- "aws_db_security_group"
9+
- "aws_security_group"
10+
attribute: "ingress.cidr_blocks"
11+
operator: "jsonpath_exists"
12+

tests/terraform/graph/checks_infra/attribute_solvers/jsonpath_exists_solver/__init__.py

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
resource "xyz" "pass" {
2+
arr {
3+
name = "a"
4+
value = "a"
5+
}
6+
arr {
7+
name = "b"
8+
value = "x"
9+
}
10+
}
11+
12+
resource "xyz" "fail" {
13+
arr {
14+
name = "b"
15+
value = "x"
16+
}
17+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
metadata:
2+
id: "example"
3+
scope:
4+
provider: "AWS"
5+
definition:
6+
cond_type: "attribute"
7+
resource_types:
8+
- "xyz"
9+
attribute: "arr[?(@.name == 'a')]"
10+
operator: "jsonpath_exists"
11+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import os
2+
3+
from tests.terraform.graph.checks_infra.test_base import TestBaseSolver
4+
5+
TEST_DIRNAME = os.path.dirname(os.path.realpath(__file__))
6+
7+
8+
class TestJsonpathExistsSolver(TestBaseSolver):
9+
def setUp(self):
10+
self.checks_dir = TEST_DIRNAME
11+
super().setUp()
12+
13+
def test_jsonpath_exists_solver_simple(self):
14+
root_folder = '../../../resources/public_security_groups'
15+
check_id = "PublicDBSG"
16+
should_pass = ['aws_security_group.aws_security_group_private']
17+
should_fail = ['aws_db_security_group.aws_db_security_group_public']
18+
expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}
19+
20+
self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
21+
22+
def test_jsonpath_exists_solver_wildcard(self):
23+
root_folder = '../../../resources/security_group_multiple_rules3'
24+
check_id = "CkSshPortOpenForAll"
25+
should_pass = ['aws_security_group.sg1']
26+
should_fail = ['aws_security_group.sg2', 'aws_security_group.sg3']
27+
expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}
28+
29+
self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
30+
31+
def test_jsonpath_exists_azure_rule(self):
32+
root_folder = '../../../resources/azure_secure_rule'
33+
check_id = "AzureSecureRule"
34+
should_pass = ['azurerm_network_security_group.sg_fail']
35+
should_fail = ['azurerm_network_security_group.sg_fail2']
36+
expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}
37+
38+
self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
39+
40+
def test_jsonpath_exists_example(self):
41+
root_folder = './'
42+
check_id = "example"
43+
should_pass = ['xyz.pass']
44+
should_fail = ['xyz.fail']
45+
expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}
46+
47+
self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
resource "azurerm_resource_group" "example" {
2+
name = "example-resources"
3+
location = "West Europe"
4+
}
5+
6+
resource "azurerm_network_security_group" "example" {
7+
name = "acceptanceTestSecurityGroup1"
8+
location = azurerm_resource_group.example.location
9+
resource_group_name = azurerm_resource_group.example.name
10+
}
11+
12+
resource "azurerm_network_security_group" "sg_fail" {
13+
# this will fail DoNotUseInlineRule
14+
name = "sg-fail"
15+
location = azurerm_resource_group.example.location
16+
resource_group_name = azurerm_resource_group.example.name
17+
18+
security_rule = [
19+
{
20+
name = "rule_we_care_about"
21+
source_address_prefixes = ["allowed_ip"]
22+
},
23+
{
24+
name = "rule_we_do not_care_about"
25+
source_address_prefixes = ["some_other_ip"]
26+
}
27+
]
28+
}
29+
30+
resource "azurerm_network_security_group" "sg_fail2" {
31+
# this will fail DoNotUseInlineRule
32+
name = "sg-fail"
33+
location = "azurerm_resource_group.example.location"
34+
resource_group_name = "azurerm_resource_group.example.name"
35+
36+
security_rule = [
37+
{
38+
name = "rule_we_care_about"
39+
source_address_prefixes = ["disallowed_ip"]
40+
},
41+
{
42+
name = "rule_we_do not_care_about2"
43+
source_address_prefixes = ["allowed_ip"]
44+
}
45+
]
46+
}

0 commit comments

Comments
 (0)