Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(general): number of words larger/less than or equal operators #3827

Merged
merged 15 commits into from
Nov 14, 2022
Merged
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
10 changes: 9 additions & 1 deletion checkov/common/checks_infra/checks_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@
RangeIncludesAttributeSolver,
RangeNotIncludesAttributeSolver,
NumberOfWordsEqualsAttributeSolver,
NumberOfWordsNotEqualsAttributeSolver
NumberOfWordsNotEqualsAttributeSolver,
NumberOfWordsGreaterThanAttributeSolver,
NumberOfWordsGreaterThanOrEqualAttributeSolver,
NumberOfWordsLessThanAttributeSolver,
NumberOfWordsLessThanOrEqualAttributeSolver,
)
from checkov.common.checks_infra.solvers.connections_solvers.connection_one_exists_solver import \
ConnectionOneExistsSolver
Expand Down Expand Up @@ -105,6 +109,10 @@
"range_not_includes": RangeNotIncludesAttributeSolver,
"number_of_words_equals": NumberOfWordsEqualsAttributeSolver,
"number_of_words_not_equals": NumberOfWordsNotEqualsAttributeSolver,
"number_of_words_greater_than": NumberOfWordsGreaterThanAttributeSolver,
"number_of_words_greater_than_or_equal": NumberOfWordsGreaterThanOrEqualAttributeSolver,
"number_of_words_less_than_or_equal": NumberOfWordsLessThanOrEqualAttributeSolver,
"number_of_words_less_than": NumberOfWordsLessThanAttributeSolver,
}

operators_to_complex_solver_classes: dict[str, Type[BaseComplexSolver]] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@
from checkov.common.checks_infra.solvers.attribute_solvers.range_not_includes_attribute_solver import RangeNotIncludesAttributeSolver # noqa
from checkov.common.checks_infra.solvers.attribute_solvers.number_of_words_equals_attribute_solver import NumberOfWordsEqualsAttributeSolver # noqa
from checkov.common.checks_infra.solvers.attribute_solvers.number_of_words_not_equals_attribute_solver import NumberOfWordsNotEqualsAttributeSolver # noqa
from checkov.common.checks_infra.solvers.attribute_solvers.number_of_words_greater_than_attribute_solver import NumberOfWordsGreaterThanAttributeSolver # noqa
from checkov.common.checks_infra.solvers.attribute_solvers.number_of_words_greater_than_or_equal_attribute_solver import NumberOfWordsGreaterThanOrEqualAttributeSolver # noqa
from checkov.common.checks_infra.solvers.attribute_solvers.number_of_words_less_than_or_equal_attribute_solver import NumberOfWordsLessThanOrEqualAttributeSolver # noqa
from checkov.common.checks_infra.solvers.attribute_solvers.number_of_words_less_than_attribute_solver import NumberOfWordsLessThanAttributeSolver # noqa
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from __future__ import annotations

from typing import Any, TYPE_CHECKING

from checkov.common.graph.checks_infra.enums import Operators
from checkov.common.checks_infra.solvers.attribute_solvers.base_attribute_solver import BaseAttributeSolver
from checkov.common.util.type_forcers import force_int

if TYPE_CHECKING:
from typing_extensions import TypeGuard


class BaseNumberOfWordsAttributeSolver(BaseAttributeSolver):
kadoshms marked this conversation as resolved.
Show resolved Hide resolved
operator = Operators.NUMBER_OF_WORDS_GREATER_THAN # noqa: CCE003 # a static attribute

def _validate_vertex_value(self, attr: Any) -> TypeGuard[str]:
return isinstance(attr, str)

def _get_number_of_words(self, attr: str) -> int:
return len(attr.split())

def _numerize_value(self) -> int | None:
return force_int(self.value)

def _get_operation(self, vertex: dict[str, Any], attribute: str | None) -> bool:
attr = vertex.get(attribute) # type:ignore[arg-type] # due to attribute can be None
return self._validate_vertex_value(attr)
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
from typing import Optional, Any, Dict

from checkov.common.checks_infra.solvers.attribute_solvers.base_number_of_words_attribute_solver import \
BaseNumberOfWordsAttributeSolver
from checkov.common.graph.checks_infra.enums import Operators
from checkov.common.checks_infra.solvers.attribute_solvers.base_attribute_solver import BaseAttributeSolver
from checkov.common.util.type_forcers import force_int


class NumberOfWordsEqualsAttributeSolver(BaseAttributeSolver):
class NumberOfWordsEqualsAttributeSolver(BaseNumberOfWordsAttributeSolver):
operator = Operators.NUMBER_OF_WORDS_EQUALS # noqa: CCE003 # a static attribute

def _get_operation(self, vertex: Dict[str, Any], attribute: Optional[str]) -> bool:
vertex_attr = vertex.get(attribute) # type:ignore[arg-type] # due to attribute can be None
attr = vertex.get(attribute) # type:ignore[arg-type] # due to attribute can be None

if not isinstance(vertex_attr, str):
if not self._validate_vertex_value(attr):
return False
words = vertex_attr.split()
value_numeric = force_int(self.value)

return len(words) == value_numeric
num_of_words = self._get_number_of_words(attr)
value_numeric = self._numerize_value()

return num_of_words == value_numeric
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Optional, Any, Dict

from checkov.common.checks_infra.solvers.attribute_solvers.base_number_of_words_attribute_solver import \
BaseNumberOfWordsAttributeSolver
from checkov.common.graph.checks_infra.enums import Operators


class NumberOfWordsGreaterThanAttributeSolver(BaseNumberOfWordsAttributeSolver):
operator = Operators.NUMBER_OF_WORDS_GREATER_THAN # noqa: CCE003 # a static attribute

def _get_operation(self, vertex: Dict[str, Any], attribute: Optional[str]) -> bool:
attr = vertex.get(attribute) # type:ignore[arg-type] # due to attribute can be None

if not self._validate_vertex_value(attr):
return False

num_of_words = self._get_number_of_words(attr)
value_numeric = self._numerize_value()

if value_numeric is None:
return False

return num_of_words > value_numeric
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Optional, Any, Dict

from checkov.common.checks_infra.solvers.attribute_solvers.base_number_of_words_attribute_solver import \
BaseNumberOfWordsAttributeSolver
from checkov.common.graph.checks_infra.enums import Operators


class NumberOfWordsGreaterThanOrEqualAttributeSolver(BaseNumberOfWordsAttributeSolver):
operator = Operators.NUMBER_OF_WORDS_GREATER_THAN # noqa: CCE003 # a static attribute
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's the wrong operator 🤔


def _get_operation(self, vertex: Dict[str, Any], attribute: Optional[str]) -> bool:
attr = vertex.get(attribute) # type:ignore[arg-type] # due to attribute can be None

if not self._validate_vertex_value(attr):
return False

num_of_words = self._get_number_of_words(attr)
value_numeric = self._numerize_value()

if value_numeric is None:
return False

return num_of_words >= value_numeric
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Optional, Any, Dict

from checkov.common.checks_infra.solvers.attribute_solvers.base_number_of_words_attribute_solver import \
BaseNumberOfWordsAttributeSolver
from checkov.common.graph.checks_infra.enums import Operators


class NumberOfWordsLessThanAttributeSolver(BaseNumberOfWordsAttributeSolver):
operator = Operators.NUMBER_OF_WORDS_LESS_THAN # noqa: CCE003 # a static attribute

def _get_operation(self, vertex: Dict[str, Any], attribute: Optional[str]) -> bool:
attr = vertex.get(attribute) # type:ignore[arg-type] # due to attribute can be None

if not self._validate_vertex_value(attr):
return False

num_of_words = self._get_number_of_words(attr)
value_numeric = self._numerize_value()

if value_numeric is None:
return False

return num_of_words < value_numeric
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Optional, Any, Dict

from checkov.common.checks_infra.solvers.attribute_solvers.base_number_of_words_attribute_solver import \
BaseNumberOfWordsAttributeSolver
from checkov.common.graph.checks_infra.enums import Operators


class NumberOfWordsLessThanOrEqualAttributeSolver(BaseNumberOfWordsAttributeSolver):
operator = Operators.NUMBER_OF_WORDS_GREATER_THAN # noqa: CCE003 # a static attribute
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also here


def _get_operation(self, vertex: Dict[str, Any], attribute: Optional[str]) -> bool:
attr = vertex.get(attribute) # type:ignore[arg-type] # due to attribute can be None

if not self._validate_vertex_value(attr):
return False

num_of_words = self._get_number_of_words(attr)
value_numeric = self._numerize_value()

if value_numeric is None:
return False

return num_of_words <= value_numeric
4 changes: 4 additions & 0 deletions checkov/common/graph/checks_infra/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ class Operators:
RANGE_NOT_INCLUDES = 'range_not_includes'
NUMBER_OF_WORDS_EQUALS = 'number_of_words_equals'
NUMBER_OF_WORDS_NOT_EQUALS = 'number_of_words_not_equals'
NUMBER_OF_WORDS_GREATER_THAN = 'number_of_words_greater_than'
NUMBER_OF_WORDS_GREATER_THAN_OR_EQUAL = 'number_of_words_greater_than_or_equal'
NUMBER_OF_WORDS_LESS_THAN = 'number_of_words_less_than'
NUMBER_OF_WORDS_LESS_THAN_OR_EQUAL = 'number_of_words_less_than_or_equal'
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
metadata:
id: "NumberOfWordsGreaterThanOrEqual"
definition:
cond_type: "attribute"
resource_types:
- aws_security_group
attribute: ingress.description
operator: number_of_words_greater_than_or_equal
value: 6
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os

from tests.terraform.graph.checks_infra.test_base import TestBaseSolver

TEST_DIRNAME = os.path.dirname(os.path.realpath(__file__))


class TestNumberOfWordsGreaterThanOrEqual(TestBaseSolver):
def setUp(self):
self.checks_dir = TEST_DIRNAME
super(TestNumberOfWordsGreaterThanOrEqual, self).setUp()

def test_number_of_words_greater_than_or_equal(self):
# this is just a basic check to make sure the operator works
# we'll check all the other combinations more directly (because coming up with all the policy combos is painful)
root_folder = '../../../resources/number_of_words'
check_id = "NumberOfWordsGreaterThanOrEqual"
should_pass = ['aws_security_group.sg1']
should_fail = ['aws_security_group.sg2']
expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}

self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
metadata:
id: "NumberOfWordsGreaterThan"
definition:
cond_type: "attribute"
resource_types:
- aws_security_group
attribute: ingress.description
operator: number_of_words_greater_than
value: 4
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os

from tests.terraform.graph.checks_infra.test_base import TestBaseSolver

TEST_DIRNAME = os.path.dirname(os.path.realpath(__file__))


class TestNumberOfWordsGreaterThan(TestBaseSolver):
def setUp(self):
self.checks_dir = TEST_DIRNAME
super(TestNumberOfWordsGreaterThan, self).setUp()

def test_number_of_words_greater_than(self):
# this is just a basic check to make sure the operator works
# we'll check all the other combinations more directly (because coming up with all the policy combos is painful)
root_folder = '../../../resources/number_of_words'
check_id = "NumberOfWordsGreaterThan"
should_pass = ['aws_security_group.sg1']
should_fail = ['aws_security_group.sg2']
expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}

self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
metadata:
id: "NumberOfWordsLessThanOrEqual"
definition:
cond_type: "attribute"
resource_types:
- aws_security_group
attribute: ingress.description
operator: number_of_words_less_than_or_equal
value: 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os

from tests.terraform.graph.checks_infra.test_base import TestBaseSolver

TEST_DIRNAME = os.path.dirname(os.path.realpath(__file__))


class TestNumberOfWordsLessThanOrEqual(TestBaseSolver):
def setUp(self):
self.checks_dir = TEST_DIRNAME
super(TestNumberOfWordsLessThanOrEqual, self).setUp()

def test_number_of_words_less_than_or_equal(self):
# this is just a basic check to make sure the operator works
# we'll check all the other combinations more directly (because coming up with all the policy combos is painful)
root_folder = '../../../resources/number_of_words'
check_id = "NumberOfWordsLessThanOrEqual"
should_pass = ['aws_security_group.sg2']
should_fail = ['aws_security_group.sg1']
expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}

self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
metadata:
id: "NumberOfWordsLessThan"
definition:
cond_type: "attribute"
resource_types:
- aws_security_group
attribute: ingress.description
operator: number_of_words_less_than
value: 4
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os

from tests.terraform.graph.checks_infra.test_base import TestBaseSolver

TEST_DIRNAME = os.path.dirname(os.path.realpath(__file__))


class TestNumberOfWordsLessThan(TestBaseSolver):
def setUp(self):
self.checks_dir = TEST_DIRNAME
super(TestNumberOfWordsLessThan, self).setUp()

def test_number_of_words_less_than(self):
# this is just a basic check to make sure the operator works
# we'll check all the other combinations more directly (because coming up with all the policy combos is painful)
root_folder = '../../../resources/number_of_words'
check_id = "NumberOfWordsLessThan"
should_pass = ['aws_security_group.sg2']
should_fail = ['aws_security_group.sg1']
expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}

self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)