Source code for stix2generator.test.test_reference_graph_generator

import copy
import pytest
import stix2.base
import stix2generator
import stix2generator.test.utils
import stix2generator.utils
import stix2generator.generation.object_generator
import stix2generator.generation.reference_graph_generator


_TLP_MARKING_DEFINITION_IDS = {
    "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9",
    "marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da",
    "marking-definition--f88d31f6-486f-44da-b317-01333bde0b82",
    "marking-definition--5e57c739-391a-4eb3-b6be-7d15ca92d5ed",
}


[docs]@pytest.mark.parametrize( "seed_type", [ "identity", stix2generator.utils.STIXTypeClass.SDO, stix2generator.utils.STIXTypeClass.SCO, stix2generator.utils.STIXTypeClass.SRO ] ) def test_seeds(num_trials, seed_type): obj_gen_config = stix2generator.generation.object_generator.Config( minimize_ref_properties=False ) obj_gen = stix2generator.create_object_generator(obj_gen_config) ref_graph_gen = stix2generator.generation.reference_graph_generator \ .ReferenceGraphGenerator(obj_gen, stix_version="2.1") for _ in range(num_trials): _, graph = ref_graph_gen.generate(seed_type) # Ensure the graph has at least one object of type seed_type. assert any( stix2generator.utils.is_stix_type( obj, seed_type, stix_version="2.1" ) for obj in graph.values() )
[docs]def test_bad_seed(): obj_gen_config = stix2generator.generation.object_generator.Config( minimize_ref_properties=False ) obj_gen = stix2generator.create_object_generator(obj_gen_config) ref_graph_gen = stix2generator.generation.reference_graph_generator \ .ReferenceGraphGenerator(obj_gen, stix_version="2.1") with pytest.raises( stix2generator.exceptions.GeneratableSTIXTypeNotFoundError ): ref_graph_gen.generate("foo")
def _reverse_constraint(constraint): """ Creates a "reversed" constraint: a constraint where the object types and property names have been reversed. :param constraint: A inverse property constraint object :return: The reversed constraint object """ reversed_ = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint( constraint.object_type2, constraint.prop_name2, constraint.object_type1, constraint.prop_name1 ) return reversed_
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type2", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type2", "prop2": "3"}), ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type2", "prop2": ["99", "1"]}), ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type2", "prop2": ["99", "3"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type2", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type2", "prop2": "3"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type2", "prop2": ["99", "1"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type2", "prop2": ["99", "3"]}), ] ) def test_inverse_property_constraint_applicable_diff_types(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type2", "prop2") assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert reversed_constraint.is_applicable(src_obj, "prop1", dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2", "prop2": "3"}, {"id": "2", "type": "type1", "prop1": "3", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": "2", "prop2": "2"}, {"id": "2", "type": "type1", "prop1": "3", "prop2": ["99", "1"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"], "prop2": "3"}, {"id": "2", "type": "type1", "prop1": "1", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type1", "prop1": "99", "prop2": ["99", "3"]}), ] ) def test_inverse_property_constraint_applicable_same_types(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type1", "prop2") assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert reversed_constraint.is_applicable(src_obj, "prop1", dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type1", "prop1": "1"}), ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type1", "prop1": "3"}) ] ) def test_inverse_property_constraint_applicable_same_types_props(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type1", "prop1") assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert reversed_constraint.is_applicable(src_obj, "prop1", dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, ref_prop, dest_obj", [ # wrong references ({"id": "1", "type": "type1", "prop1": "3"}, "prop1", {"id": "2", "type": "type2", "prop2": "4"}), ({"id": "1", "type": "type1", "prop1": ["3", "99"]}, "prop1", {"id": "2", "type": "type2", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": "3"}, "prop1", {"id": "2", "type": "type2", "prop2": ["4", "1"]}), # wrong properties ({"id": "1", "type": "type1", "prop1": "2"}, "prop4", {"id": "2", "type": "type2", "prop4": "1"}), ({"id": "1", "type": "type1", "prop3": ["2", "99"]}, "prop2", {"id": "2", "type": "type2", "prop2": "1"}), ({"id": "1", "type": "type1", "prop3": "2"}, "prop1", {"id": "2", "type": "type2", "prop4": ["99", "1"]}), # wrong types ({"id": "1", "type": "type3", "prop1": "2"}, "prop1", {"id": "2", "type": "type2", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, "prop1", {"id": "2", "type": "type4", "prop2": "1"}), ({"id": "1", "type": "type3", "prop1": "2"}, "prop1", {"id": "2", "type": "type4", "prop2": ["99", "1"]}), ] ) def test_inverse_property_constraint_not_applicable_diff_types(src_obj, ref_prop, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type2", "prop2") assert not inv_prop_constraint.is_applicable(src_obj, ref_prop, dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert not reversed_constraint.is_applicable(src_obj, ref_prop, dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, ref_prop, dest_obj", [ # wrong references ({"id": "1", "type": "type1", "prop1": "3", "prop2": "2"}, "prop1", {"id": "2", "type": "type1", "prop1": "1", "prop2": "4"}), ({"id": "1", "type": "type1", "prop1": ["3", "99"], "prop2": "3"}, "prop1", {"id": "2", "type": "type1", "prop1": "1", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": "3", "prop2": "1"}, "prop1", {"id": "2", "type": "type1", "prop1": "2", "prop2": ["4", "1"]}), # wrong properties ({"id": "1", "type": "type1", "prop1": "2", "prop2": "3"}, "prop1", {"id": "2", "type": "type1", "prop4": "1"}), ({"id": "1", "type": "type1", "prop3": ["2", "99"]}, "prop1", {"id": "2", "type": "type1", "prop2": "1"}), ({"id": "1", "type": "type1", "prop3": "2"}, "prop1", {"id": "2", "type": "type1", "prop4": ["99", "1"]}), ({"id": "1", "type": "type1", "prop1": "2", "prop2": "3"}, "prop3", {"id": "2", "type": "type1", "prop1": ["99", "1"], "prop2": "3"}), ] ) def test_inverse_property_constraint_not_applicable_same_types(src_obj, ref_prop, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type1", "prop2") assert not inv_prop_constraint.is_applicable(src_obj, ref_prop, dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert not reversed_constraint.is_applicable(src_obj, ref_prop, dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, ref_prop, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "3"}, "prop1", {"id": "2", "type": "type1", "prop1": "1"}), ({"id": "1", "type": "type1", "prop1": "3"}, "prop1", {"id": "2", "type": "type1", "prop1": "3"}), ({"id": "1", "type": "type1", "prop1": "2"}, "prop2", {"id": "2", "type": "type1", "prop1": "1"}), ({"id": "1", "type": "type2", "prop1": "2"}, "prop1", {"id": "2", "type": "type1", "prop1": "1"}), ({"id": "1", "type": "type1", "prop2": "2"}, "prop1", {"id": "2", "type": "type1", "prop1": "1"}), ] ) def test_inverse_property_constraint_not_applicable_same_types_props(src_obj, ref_prop, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type1", "prop1") assert not inv_prop_constraint.is_applicable(src_obj, ref_prop, dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert not reversed_constraint.is_applicable(src_obj, ref_prop, dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type2", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type2", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type2", "prop2": ["99", "1"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type2", "prop2": ["99", "1"]}), ] ) def test_inverse_property_constraint_holds_diff_types(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type2", "prop2") # Ensure I didn't mess up the test case... # .holds() assumes .is_applicable(). assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) assert inv_prop_constraint.holds(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert reversed_constraint.holds(src_obj, "prop1", dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2", "prop2": "2"}, {"id": "2", "type": "type1", "prop1": "4", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"], "prop2": "2"}, {"id": "2", "type": "type1", "prop1": "4", "prop2": "1"}), ({"id": "1", "type": "type1", "prop1": "2", "prop2": "99"}, {"id": "2", "type": "type1", "prop1": "2", "prop2": ["99", "1"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"], "prop2": "99"}, {"id": "2", "type": "type1", "prop1": "2", "prop2": ["99", "1"]}), ] ) def test_inverse_property_constraint_holds_same_types(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type1", "prop2") # Ensure I didn't mess up the test case... # .holds() assumes .is_applicable(). assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) assert inv_prop_constraint.holds(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert reversed_constraint.holds(src_obj, "prop1", dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type1", "prop1": "1"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type1", "prop1": "1"}), ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type1", "prop1": ["99", "1"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type1", "prop1": ["99", "1"]}), ] ) def test_inverse_property_constraint_holds_same_types_props(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type1", "prop1") # Ensure I didn't mess up the test case... # .holds() assumes .is_applicable(). assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) assert inv_prop_constraint.holds(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert reversed_constraint.holds(src_obj, "prop1", dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type2", "prop2": "4"}), ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type2", "prop2": ["2", "99"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type2", "prop2": "4"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type2", "prop2": ["99", "2"]}), ] ) def test_inverse_property_constraint_not_holds_diff_types(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type2", "prop2") # Ensure I didn't mess up the test case... # .holds() assumes .is_applicable(). assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) assert not inv_prop_constraint.holds(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert not reversed_constraint.holds(src_obj, "prop1", dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2", "prop2": "2"}, {"id": "2", "type": "type1", "prop1": "1", "prop2": "4"}), ({"id": "1", "type": "type1", "prop1": "2", "prop2": "2"}, {"id": "2", "type": "type1", "prop1": "1", "prop2": ["4", "99"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"], "prop2": "2"}, {"id": "2", "type": "type1", "prop1": "1", "prop2": "4"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"], "prop2": "2"}, {"id": "2", "type": "type1", "prop1": "1", "prop2": ["99", "4"]}), ] ) def test_inverse_property_constraint_not_holds_same_types(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type1", "prop2") # Ensure I didn't mess up the test case... # .holds() assumes .is_applicable(). assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) assert not inv_prop_constraint.holds(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert not reversed_constraint.holds(src_obj, "prop1", dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type1", "prop1": "2"}), ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type1", "prop1": "3"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type1", "prop1": "4"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type1", "prop1": ["2", "3"]}), ] ) def test_inverse_property_constraint_not_holds_same_types_props(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type1", "prop1") # Ensure I didn't mess up the test case... # .holds() assumes .is_applicable(). assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) assert not inv_prop_constraint.holds(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) assert not reversed_constraint.holds(src_obj, "prop1", dest_obj)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type2", "prop2": "4"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type2", "prop2": "4"}), ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type2", "prop2": ["4", "99"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type2", "prop2": ["4", "99"]}), ] ) def test_inverse_property_constraint_enforce_diff_types(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type2", "prop2") # Copies for reverse test to work on src_copy = copy.deepcopy(src_obj) dest_copy = copy.deepcopy(dest_obj) assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) inv_prop_constraint.enforce(src_obj, "prop1", dest_obj) assert inv_prop_constraint.holds(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) reversed_constraint.enforce(src_copy, "prop1", dest_copy) assert reversed_constraint.holds(src_copy, "prop1", dest_copy)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2", "prop2": "2"}, {"id": "2", "type": "type1", "prop1": "2", "prop2": "4"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"], "prop2": "2"}, {"id": "2", "type": "type1", "prop1": "3", "prop2": "4"}), ({"id": "1", "type": "type1", "prop1": "2", "prop2": "3"}, {"id": "2", "type": "type1", "prop1": "1", "prop2": ["4", "99"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"], "prop2": "3"}, {"id": "2", "type": "type1", "prop1": "1", "prop2": ["4", "99"]}), ] ) def test_inverse_property_constraint_enforce_same_types(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type1", "prop2") # Copies for reverse test to work on src_copy = copy.deepcopy(src_obj) dest_copy = copy.deepcopy(dest_obj) assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) inv_prop_constraint.enforce(src_obj, "prop1", dest_obj) assert inv_prop_constraint.holds(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) reversed_constraint.enforce(src_copy, "prop1", dest_copy) assert reversed_constraint.holds(src_copy, "prop1", dest_copy)
[docs]@pytest.mark.parametrize( "src_obj, dest_obj", [ ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type1", "prop1": "3"}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type1", "prop1": "3"}), ({"id": "1", "type": "type1", "prop1": "2"}, {"id": "2", "type": "type1", "prop1": ["4", "99"]}), ({"id": "1", "type": "type1", "prop1": ["2", "99"]}, {"id": "2", "type": "type1", "prop1": ["4", "99"]}), ] ) def test_inverse_property_constraint_enforce_same_types_props(src_obj, dest_obj): inv_prop_constraint = stix2generator.generation.reference_graph_generator\ .InversePropertyConstraint("type1", "prop1", "type1", "prop1") # Copies for reverse test to work on src_copy = copy.deepcopy(src_obj) dest_copy = copy.deepcopy(dest_obj) assert inv_prop_constraint.is_applicable(src_obj, "prop1", dest_obj) inv_prop_constraint.enforce(src_obj, "prop1", dest_obj) assert inv_prop_constraint.holds(src_obj, "prop1", dest_obj) reversed_constraint = _reverse_constraint(inv_prop_constraint) reversed_constraint.enforce(src_copy, "prop1", dest_copy) assert reversed_constraint.holds(src_copy, "prop1", dest_copy)
[docs]@pytest.mark.parametrize( "graph, src_id, dest_id", [ ([{"id": "1", "type": "foo", "prop1_ref": "2"}, {"id": "2", "type": "bar"}], "1", "2"), ([{"id": "1", "type": "foo", "prop1_ref": "2"}, {"id": "2", "type": "bar", "prop2_ref": "1"}], "1", "2"), ([{"id": "1", "type": "foo", "prop1_ref": "2"}, {"id": "2", "type": "bar", "prop2_ref": "1"}], "2", "1"), ([{"id": "1", "type": "foo", "prop1_refs": ["2", "3"]}, {"id": "2", "type": "bar"}], "1", "2"), ([{"id": "1", "type": "foo", "prop1_ref": "1"}], "1", "1"), ] ) def test_is_reachable(graph, src_id, dest_id): # easier to give a list and derive the mapping by_id = { obj["id"]: obj for obj in graph } assert stix2generator.generation.reference_graph_generator._is_reachable( src_id, dest_id, by_id )
[docs]@pytest.mark.parametrize( "graph, src_id, dest_id", [ ([{"id": "1", "type": "foo", "prop1_ref": "2"}, {"id": "2", "type": "bar"}], "2", "1"), ([{"id": "1", "type": "foo", "prop1_ref": "2"}, {"id": "2", "type": "bar"}, {"id": "3", "type": "baz", "prop3_ref": "2"}], "1", "3"), ([{"id": "1", "type": "foo", "prop1_ref": "2"}, {"id": "2", "type": "bar", "prop2_ref": "3"}, {"id": "3", "type": "baz"}], "3", "1") ] ) def test_is_not_reachable(graph, src_id, dest_id): # easier to give a list and derive the mapping by_id = { obj["id"]: obj for obj in graph } assert not stix2generator.generation.reference_graph_generator._is_reachable( src_id, dest_id, by_id )
[docs]def test_find_property_constraints_process(): src_obj = { "id": "1", "type": "process", "child_refs": ["2"] } dest_obj = { "id": "2", "type": "process", "parent_ref": "1" } constraints = stix2generator.generation.reference_graph_generator\ ._find_property_constraints(src_obj, "child_refs", dest_obj) constraints = list(constraints) # collect them all into a list. assert len(constraints) == 1 assert constraints[0].object_type1 == "process" \ and constraints[0].object_type2 == "process" \ and "child_refs" in ( constraints[0].prop_name1, constraints[0].prop_name2 )
[docs]def test_find_property_constraints_directory(): src_obj = { "id": "1", "type": "directory", "contains_refs": ["2", "3"] } dest_obj = { "id": "2", "type": "file", "parent_directory_ref": "1" } constraints = stix2generator.generation.reference_graph_generator\ ._find_property_constraints(src_obj, "contains_refs", dest_obj) constraints = list(constraints) # collect them all into a list. assert len(constraints) == 1 constraint = constraints[0] # the constraint could be defined either way, so lets allow either assert ( constraint.object_type1 == "directory" and constraint.prop_name1 == "contains_refs" and constraint.object_type2 == "file" and constraint.prop_name2 == "parent_directory_ref" ) or ( constraint.object_type1 == "file" and constraint.prop_name1 == "parent_directory_ref" and constraint.object_type2 == "directory" and constraint.prop_name2 == "contains_refs" )
[docs]def test_find_property_constraints_missing_inverse(): src_obj = { "id": "1", "type": "directory", "contains_refs": ["2", "3"] } dest_obj = { "id": "2", "type": "file" # inverse property is missing, so no constraint is applicable } constraints = stix2generator.generation.reference_graph_generator\ ._find_property_constraints(src_obj, "contains_refs", dest_obj) constraints = list(constraints) # collect them all into a list. assert not constraints
[docs]@pytest.mark.parametrize( "src_obj_type, ref_prop, dest_obj_type", [ # just pick some types/props we know imply constraints ("process", "child_refs", "process"), ("process", "parent_ref", "process"), ("network-traffic", "encapsulates_refs", "network-traffic"), ("directory", "contains_refs", "file") ] ) def test_would_be_constrained(src_obj_type, ref_prop, dest_obj_type): assert stix2generator.generation.reference_graph_generator\ ._would_be_constrained(src_obj_type, ref_prop, dest_obj_type)
[docs]@pytest.mark.parametrize( "src_obj_type, ref_prop, dest_obj_type", [ ("file", "content_ref", "artifact"), ("network-traffic", "object_marking_refs", "marking-definition"), ("process", "image_ref", "file"), ] ) def test_would_not_be_constrained(src_obj_type, ref_prop, dest_obj_type): assert not stix2generator.generation.reference_graph_generator\ ._would_be_constrained(src_obj_type, ref_prop, dest_obj_type)
[docs]@pytest.mark.parametrize( "src_obj, ref_prop, dest_obj", [ ( { "id": "1", "type": "file", "parent_directory_ref": "2" }, "parent_directory_ref", { "id": "2", "type": "directory", "contains_refs": "4" } ), ( { "id": "1", "type": "process", "parent_ref": "2" }, "parent_ref", { "id": "2", "type": "process", "child_refs": ["4", "99"] } ) ] ) def test_delete_inverse_properties(src_obj, ref_prop, dest_obj): # check at least one constraint applies first... assert any( constraint.is_applicable(src_obj, ref_prop, dest_obj) for constraint in stix2generator.generation.reference_graph_generator._INVERSE_PROPERTIES ) stix2generator.generation.reference_graph_generator\ ._delete_inverse_properties( src_obj, ref_prop, dest_obj ) # now, none should apply. assert all( not constraint.is_applicable(src_obj, ref_prop, dest_obj) for constraint in stix2generator.generation.reference_graph_generator._INVERSE_PROPERTIES )
def _has_cycle(graph): # This is kinda silly, doing repeated reachability tests (a single DFS # through the graph would be more efficient), but it's simple to write and # should suffice for a unit test right? for id_, obj in graph.items(): for _, ref_id in stix2generator.utils.find_references(obj): if stix2generator.generation.reference_graph_generator\ ._is_reachable( ref_id, id_, graph ): result = True break else: continue break else: result = False return result def _has_reuse(graph): # Maps an object ID to another ID which refers to it (via some ref # property). referrers = {} for id_, obj in graph.items(): for _, ref_id in stix2generator.utils.find_references(obj): if ref_id in _TLP_MARKING_DEFINITION_IDS: # We *must* reuse these because they have fixed IDs, so ignore # them. continue other_referrer_id = referrers.get(ref_id) if other_referrer_id is None: referrers[ref_id] = id_ else: result = True break else: continue break else: result = False return result
[docs]def test_graph_tree(num_trials): obj_gen_config = stix2generator.generation.object_generator.Config( minimize_ref_properties=False ) obj_gen = stix2generator.create_object_generator(obj_gen_config) graph_gen_config = stix2generator.generation.reference_graph_generator\ .Config( graph_type="tree", inverse_property_constraints="delete" ) ref_graph_gen = stix2generator.generation.reference_graph_generator\ .ReferenceGraphGenerator(obj_gen, graph_gen_config) for _ in range(num_trials): _, graph = ref_graph_gen.generate() assert not _has_cycle(graph) assert not _has_reuse(graph) assert not stix2generator.test.utils.has_dangling_references(graph) assert stix2generator.test.utils.is_connected(graph)
[docs]def test_graph_dag(num_trials): obj_gen_config = stix2generator.generation.object_generator.Config( minimize_ref_properties=False ) obj_gen = stix2generator.create_object_generator(obj_gen_config) graph_gen_config = stix2generator.generation.reference_graph_generator\ .Config( graph_type="dag", inverse_property_constraints="delete" ) ref_graph_gen = stix2generator.generation.reference_graph_generator\ .ReferenceGraphGenerator(obj_gen, graph_gen_config) for _ in range(num_trials): _, graph = ref_graph_gen.generate() assert not _has_cycle(graph) assert not stix2generator.test.utils.has_dangling_references(graph) assert stix2generator.test.utils.is_connected(graph)
[docs]def test_graph_random(num_trials): obj_gen_config = stix2generator.generation.object_generator.Config( minimize_ref_properties=False ) obj_gen = stix2generator.create_object_generator(obj_gen_config) graph_gen_config = stix2generator.generation.reference_graph_generator\ .Config( graph_type="random", inverse_property_constraints="delete" ) ref_graph_gen = stix2generator.generation.reference_graph_generator\ .ReferenceGraphGenerator(obj_gen, graph_gen_config) for _ in range(num_trials): _, graph = ref_graph_gen.generate() assert not stix2generator.test.utils.has_dangling_references(graph) assert stix2generator.test.utils.is_connected(graph)
def _objects_of_type(graph, type_): for obj in graph.values(): if obj["type"] == type_: yield obj def _object_pairs_of_types(graph, type1, type2): for obj1 in _objects_of_type(graph, type1): for obj2 in _objects_of_type(graph, type2): if obj1 is not obj2: yield obj1, obj2 def _constraints_enforced(graph): """ Check if all applicable constraints have been enforced in the given graph. :return: True if all applicable constraints are enforced; False if not """ for constraint in stix2generator.generation.reference_graph_generator\ ._INVERSE_PROPERTIES: for obj1, obj2 in _object_pairs_of_types( graph, constraint.object_type1, constraint.object_type2 ): if ( constraint.is_applicable(obj1, constraint.prop_name1, obj2) and not constraint.holds(obj1, constraint.prop_name1, obj2) ) or ( constraint.is_applicable(obj1, constraint.prop_name2, obj2) and not constraint.holds(obj1, constraint.prop_name2, obj2) ): result = False break else: continue break else: result = True return result def _constraints_applicable(graph): """ Check whether there are any applicable constraints on objects in the given graph. :param graph: The graph, as map from ID to object :return: True if any constraint applies to any objects; False if not """ for constraint in stix2generator.generation.reference_graph_generator\ ._INVERSE_PROPERTIES: for obj1, obj2 in _object_pairs_of_types( graph, constraint.object_type1, constraint.object_type2 ): if constraint.is_applicable( obj1, constraint.prop_name1, obj2 ) or constraint.is_applicable( obj1, constraint.prop_name2, obj2 ): result = True break else: continue break else: result = False return result
[docs]def test_graph_enforce_inverse_properties(num_trials): obj_gen_config = stix2generator.generation.object_generator.Config( minimize_ref_properties=False ) obj_gen = stix2generator.create_object_generator(obj_gen_config) graph_gen_config = stix2generator.generation.reference_graph_generator\ .Config( inverse_property_constraints="enforce" ) ref_graph_gen = stix2generator.generation.reference_graph_generator\ .ReferenceGraphGenerator(obj_gen, graph_gen_config) for _ in range(num_trials): # I feel like I should hard-code a STIX object type I know to contain # lots of reference properties and have applicable constraints, to have # a high likelihood that reference properties will be generated, the # graph will grow, and there will be some applicable constraints to # test. _, graph = ref_graph_gen.generate("network-traffic") assert _constraints_enforced(graph)
[docs]def test_graph_delete_inverse_properties(num_trials): obj_gen_config = stix2generator.generation.object_generator.Config( minimize_ref_properties=False ) obj_gen = stix2generator.create_object_generator(obj_gen_config) graph_gen_config = stix2generator.generation.reference_graph_generator\ .Config( inverse_property_constraints="delete" ) ref_graph_gen = stix2generator.generation.reference_graph_generator\ .ReferenceGraphGenerator(obj_gen, graph_gen_config) for _ in range(num_trials): # I feel like I should hard-code a STIX object type I know to contain # lots of reference properties and have applicable constraints, to have # a high likelihood that reference properties will be generated, the # graph will grow, and there will be some applicable constraints to # test. _, graph = ref_graph_gen.generate("network-traffic") assert not _constraints_applicable(graph)
# Nothing to test for inverse_property_constraints=ignore. In that case, # anything goes.
[docs]def test_preexisting_objects(): obj_gen_config = stix2generator.generation.object_generator.Config( minimize_ref_properties=False ) obj_gen = stix2generator.create_object_generator(obj_gen_config) ref_graph_gen = stix2generator.generation.reference_graph_generator \ .ReferenceGraphGenerator(obj_gen, stix_version="2.1") _, graph1 = ref_graph_gen.generate() _, graph2 = ref_graph_gen.generate(preexisting_objects=graph1) # just ensure graph2 absorbed graph1. Anything else we can test? assert all(id_ in graph2 for id_ in graph1) # ensure all objects got parsed ok assert all( isinstance(obj, stix2.base._STIXBase) for obj in graph2.values() )
[docs]def test_stix2_parsing(): obj_gen_config = stix2generator.generation.object_generator.Config( minimize_ref_properties=False ) obj_gen = stix2generator.create_object_generator(obj_gen_config) ref_graph_gen = stix2generator.generation.reference_graph_generator \ .ReferenceGraphGenerator(obj_gen, stix_version="2.1") graph1 = { "identity--74fa9f1b-897e-40dc-8f1c-d2f531c956bb": { "id": "identity--74fa9f1b-897e-40dc-8f1c-d2f531c956bb", "type": "identity", "spec_version": "2.1" # Omit the required "name" property. # Should be ok since the property is not used by any generators, # and we don't expect this dict to be parsed and produce any # validation errors. } } _, graph2 = ref_graph_gen.generate(preexisting_objects=graph1) # ensure graph2 absorbed graph1 assert all( id_ in graph2 for id_ in graph1 ) # ensure our preexisting identity is still a dict assert isinstance( graph2["identity--74fa9f1b-897e-40dc-8f1c-d2f531c956bb"], dict )