Skip to content

Commit

Permalink
Merge pull request #11427 from vojtapolasek/validate_levels_in_controls
Browse files Browse the repository at this point in the history
Validate levels in controls
  • Loading branch information
Mab879 authored Jan 10, 2024
2 parents 24b9960 + 07687e7 commit 599c8e1
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 2 deletions.
2 changes: 1 addition & 1 deletion controls/cis_alinux2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ controls:
- id: 4.1.11
title: Ensure unsuccessful unauthorized file access attempts are collected (Scored)
levels:
- l2_server
- l2
status: automated
rules:
- audit_rules_unsuccessful_file_modification_creat
Expand Down
20 changes: 19 additions & 1 deletion ssg/controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,22 @@ def check_all_rules_exist(self, existing_rules):
self.id, c.id, ", ".join(nonexisting_rules))
raise ValueError(msg)

def check_levels_validity(self):
"""
This function goes through all controls in the policy and checks if all
levels defined for individual controls are valid for the policy.
If the policy has no levels defined, then all controls should have the
"default" level defined (this is defined implicitly).
"""
for c in self.controls:
expected_levels = [lvl.id for lvl in self.levels]
for lvl in c.levels:
if lvl not in expected_levels:
msg = ("Invalid level {0} used in control {1} "
"defined at {2}. Allowed levels are: {3}".format(
lvl, c.id, self.filepath, expected_levels))
raise ValueError(msg)

def remove_selections_not_known(self, known_rules):
for c in self.controls:
selections = set(c.selected).intersection(known_rules)
Expand Down Expand Up @@ -313,7 +329,8 @@ def load(self):
self.title = ssg.utils.required_key(yaml_contents, "title")
self.source = yaml_contents.get("source", "")

level_list = yaml_contents.get("levels", [])
default_level_dict = {"id": "default"}
level_list = yaml_contents.get("levels", [default_level_dict])
for lv in level_list:
level = Level.from_level_dict(lv)
self.levels.append(level)
Expand All @@ -324,6 +341,7 @@ def load(self):
else:
controls_tree = ssg.utils.required_key(yaml_contents, "controls")
self.save_controls_tree(controls_tree)
self.check_levels_validity()

def get_control(self, control_id):
try:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
policy: ABCD Benchmark for securing Linux systems
title: ABCD Benchmark for securing Linux systems
id: abcd
version: 1.2.3
source: https://www.abcd.com/linux.pdf
controls:
- id: R1
title: User session timeout
levels:
- l1
description: >-
Remote user sessions must be closed after a certain
period of inactivity.
automated: yes
rules:
- sshd_set_idle_timeout
- accounts_tmout
- var_accounts_tmout=10_min
- configure_crypto_policy
notes: >-
Certain period of inactivity is vague.
- id: R2
title: Minimization of configuration
description: >-
The features configured at the level of launched services
should be limited to the strict minimum.
rationale: >-
Minimization of configuration helps to reduce attack surface.
automated: no
notes: >-
This is individual depending on the system workload
therefore needs to be audited manually.
related_rules:
- systemd_target_multi_user
28 changes: 28 additions & 0 deletions tests/unit/ssg-module/data/policy_with_invalid_level.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
policy: ABCD Benchmark for securing Linux systems with levels
title: ABCD Benchmark for securing Linux systems with levels
id: abcd-levels
version: 1.2.3
source: https://www.abcd.com/linux.pdf
levels:
- id: low
- id: medium
inherits_from:
- low
- id: high
inherits_from:
- medium

controls:
- id: S1
title: User session timeout

- id: S2
levels:
- invalid
rules:
- var_password_pam_minlen=1
- var_some_variable=1

- id: S3
levels:
- medium
11 changes: 11 additions & 0 deletions tests/unit/ssg-module/test_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,3 +521,14 @@ def test_control_with_bad_level():
except ValueError as e:
assert type(e) is ValueError
assert control_obj is None


def test_validating_policy_levels(env_yaml):
c1_path = os.path.join(data_dir, "policy_with_different_than_implicit_default_level.yml")
c2_path = os.path.join(data_dir, "policy_with_invalid_level.yml")
for policy_path in [c1_path, c2_path]:
policy = ssg.controls.Policy(policy_path, env_yaml)
with pytest.raises(ValueError):
policy.load()


0 comments on commit 599c8e1

Please sign in to comment.