-
Notifications
You must be signed in to change notification settings - Fork 709
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
Prepare OVAL object model for integration #11206
Merged
jan-cerny
merged 12 commits into
ComplianceAsCode:master
from
Honny1:integration-of-OVAL-Object-mode
Oct 26, 2023
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
bfa253f
Add a method to the OVAL Definition that checks if it is applicable t…
Honny1 564d1a8
Add a method to the OVAL definition that checks if the Affected in Me…
Honny1 3253f05
Add methods for obtaining references
Honny1 2f4c86f
Update info about generator in genereted OVAL document
Honny1 61d7cea
Add logging and fix imports
Honny1 ed09f13
Update tests
Honny1 31fde9b
Create class for stroing references of OVAL definitions
Honny1 1714dfb
Create OVAL container class
Honny1 3f3d7d4
Create OVAL shorthand class
Honny1 3f14a04
Inherit features from OVAL container to OVAL document
Honny1 9d48820
Fix missing tag of affected element child
Honny1 5eb2824
Add Unit tests
Honny1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
import logging | ||
|
||
from .general import OVALBaseObject | ||
from .oval_definition_references import OVALDefinitionReference | ||
from .oval_entities import ( | ||
load_definition, | ||
load_object, | ||
load_state, | ||
load_test, | ||
load_variable, | ||
) | ||
|
||
|
||
class ExceptionDuplicateOVALEntity(Exception): | ||
pass | ||
|
||
|
||
def _is_external_variable(component): | ||
return "external_variable" in component.tag | ||
|
||
|
||
def _handle_existing_id(component, component_dict): | ||
# ID is identical, but OVAL entities are semantically different => | ||
# report and error and exit with failure | ||
# Fixes: https://github.com/ComplianceAsCode/content/issues/1275 | ||
if ( | ||
component != component_dict[component.id_] | ||
and not _is_external_variable(component) | ||
and not _is_external_variable(component_dict[component.id_]) | ||
): | ||
# This is an error scenario - since by skipping second | ||
# implementation and using the first one for both references, | ||
# we might evaluate wrong requirement for the second entity | ||
# => report an error and exit with failure in that case | ||
# See | ||
# https://github.com/ComplianceAsCode/content/issues/1275 | ||
# for a reproducer and what could happen in this case | ||
raise ExceptionDuplicateOVALEntity( | ||
( | ||
"ERROR: it's not possible to use the same ID: {} for two semantically" | ||
" different OVAL entities:\nFirst entity:\n{}\nSecond entity:\n{}\n" | ||
"Use different ID for the second entity!!!\n" | ||
).format( | ||
component.id_, | ||
str(component), | ||
str(component_dict[component.id_]), | ||
) | ||
) | ||
elif not _is_external_variable(component): | ||
# If OVAL entity is identical, but not external_variable, the | ||
# implementation should be rewritten each entity to be present | ||
# just once | ||
logging.info( | ||
( | ||
"OVAL ID {} is used multiple times and should represent " | ||
"the same elements.\nRewrite the OVAL checks. Place the identical IDs" | ||
" into their own definition and extend this definition by it.\n" | ||
).format(component.id_) | ||
) | ||
|
||
|
||
def add_oval_component(component, component_dict): | ||
if component.id_ not in component_dict: | ||
component_dict[component.id_] = component | ||
else: | ||
_handle_existing_id(component, component_dict) | ||
|
||
|
||
def _copy_component(destination, source_of_components): | ||
for component in source_of_components.values(): | ||
add_oval_component(component, destination) | ||
|
||
|
||
def _remove_keys_from_dict(to_remove, dict_): | ||
for k in to_remove: | ||
del dict_[k] | ||
|
||
|
||
def _keep_keys_in_dict(dict_, to_keep): | ||
to_remove = [key for key in dict_ if key not in to_keep] | ||
_remove_keys_from_dict(to_remove, dict_) | ||
|
||
|
||
def _save_referenced_vars(ref, entity): | ||
ref.save_variables(entity.get_variable_references()) | ||
|
||
|
||
def _save_definitions_references(ref, definition): | ||
if definition.criteria: | ||
ref.save_tests(definition.criteria.get_test_references()) | ||
ref.save_definitions(definition.criteria.get_extend_definition_references()) | ||
|
||
|
||
def _save_test_references(ref, test): | ||
ref.save_object(test.object_ref) | ||
ref.save_states(test.state_refs) | ||
|
||
|
||
def _save_object_references(ref, object_): | ||
_save_referenced_vars(ref, object_) | ||
ref.save_states(object_.get_state_references()) | ||
ref.save_objects(object_.get_object_references()) | ||
|
||
|
||
def _save_variable_references(ref, variable): | ||
_save_referenced_vars(ref, variable) | ||
ref.save_objects(variable.get_object_references()) | ||
|
||
|
||
class OVALContainer(OVALBaseObject): | ||
def __init__(self): | ||
self.definitions = {} | ||
self.tests = {} | ||
self.objects = {} | ||
self.states = {} | ||
self.variables = {} | ||
|
||
def _call_function_for_every_component(self, _function, object_): | ||
_function(self.definitions, object_.definitions) | ||
_function(self.tests, object_.tests) | ||
_function(self.objects, object_.objects) | ||
_function(self.states, object_.states) | ||
_function(self.variables, object_.variables) | ||
|
||
def load_definition(self, oval_definition_xml_el): | ||
definition = load_definition(oval_definition_xml_el) | ||
add_oval_component(definition, self.definitions) | ||
|
||
def load_test(self, oval_test_xml_el): | ||
test = load_test(oval_test_xml_el) | ||
add_oval_component(test, self.tests) | ||
|
||
def load_object(self, oval_object_xml_el): | ||
object_ = load_object(oval_object_xml_el) | ||
add_oval_component(object_, self.objects) | ||
|
||
def load_state(self, oval_state_xml_element): | ||
state = load_state(oval_state_xml_element) | ||
add_oval_component(state, self.states) | ||
|
||
def load_variable(self, oval_variable_xml_element): | ||
variable = load_variable(oval_variable_xml_element) | ||
add_oval_component(variable, self.variables) | ||
|
||
def add_content_of_container(self, container): | ||
self._call_function_for_every_component(_copy_component, container) | ||
|
||
@staticmethod | ||
def _skip_if_is_none(value, component_id): | ||
raise NotImplementedError() | ||
|
||
def _process_component(self, ref, type_, function_save_refs): | ||
MAP_COMPONENT_DICT = { | ||
"definitions": self.definitions, | ||
"tests": self.tests, | ||
"objects": self.objects, | ||
"states": self.states, | ||
"variables": self.variables, | ||
} | ||
source = MAP_COMPONENT_DICT.get(type_) | ||
to_process, id_getter = ref.get_to_process_dict_and_id_getter(type_) | ||
while to_process: | ||
id_ = id_getter() | ||
entity = source.get(id_) | ||
if self._skip_if_is_none(entity, id_): | ||
continue | ||
function_save_refs(ref, entity) | ||
|
||
def _process_definition_references(self, ref): | ||
self._process_component( | ||
ref, | ||
"definitions", | ||
_save_definitions_references, | ||
) | ||
|
||
def _process_test_references(self, ref): | ||
self._process_component( | ||
ref, | ||
"tests", | ||
_save_test_references, | ||
) | ||
|
||
def _process_object_references(self, ref): | ||
self._process_component( | ||
ref, | ||
"objects", | ||
_save_object_references, | ||
) | ||
|
||
def _process_state_references(self, ref): | ||
self._process_component( | ||
ref, | ||
"states", | ||
_save_referenced_vars, | ||
) | ||
|
||
def _process_variable_references(self, ref): | ||
self._process_component( | ||
ref, | ||
"variables", | ||
_save_variable_references, | ||
) | ||
|
||
def _process_objects_states_variables_references(self, ref): | ||
while ( | ||
ref.to_process_objects or ref.to_process_states or ref.to_process_variables | ||
): | ||
self._process_object_references(ref) | ||
self._process_state_references(ref) | ||
self._process_variable_references(ref) | ||
|
||
def get_all_references_of_definition(self, definition_id): | ||
if definition_id not in self.definitions: | ||
raise ValueError( | ||
"ERROR: OVAL definition '{}' doesn't exist.".format(definition_id) | ||
) | ||
ref = OVALDefinitionReference(definition_id) | ||
self._process_definition_references(ref) | ||
self._process_test_references(ref) | ||
self._process_objects_states_variables_references(ref) | ||
return ref | ||
|
||
def keep_referenced_components(self, ref): | ||
self._call_function_for_every_component(_keep_keys_in_dict, ref) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function looks very similar to a part of
append
function inssg/build_ovals.py
. You should first extract the common code out to a function and then call that function both here and there to prevent unwanted code duplication and responsibility duplication.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the next PR, I plan to rework
ssg/build_ovals.py
andcombine_ovals.py
and remove some duplicated and unused code. So this duplication will be resolved.