Skip to content

Commit

Permalink
Merge pull request #51 from chisholm/halt-generator-fix
Browse files Browse the repository at this point in the history
Fix reference graph generator's "halt" generator to inherit customizations
  • Loading branch information
rpiazza authored Dec 8, 2023
2 parents 36ebc73 + 4af46db commit b64aaba
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 3 deletions.
26 changes: 24 additions & 2 deletions stix2generator/generation/object_generator.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import copy
import itertools
import logging
import math
Expand Down Expand Up @@ -106,8 +107,8 @@ def __init__(
:param spec_registry: A name->specification mapping used to look
up references inside of specifications
:param semantic_providers: A list of semantic providers (e.g.
instances of subclasses of SemanticsProvider)
:param semantic_providers: An iterable of semantic providers (e.g.
instances of subclasses of SemanticsProvider), or None
:param config: A Config instance giving user settings regarding
generation. If None, defaults will be used.
"""
Expand Down Expand Up @@ -142,6 +143,27 @@ def spec_names(self):
"""
return self.__specs.keys()

def derive_generator(self, new_config):
"""
Create an object generator using this generator's registry and
semantics providers, but with a given config.
:param new_config: An instance of Config giving desired configuration
settings for the new object generator
:return: A new object generator
"""

our_providers = copy.deepcopy(set(self.__semantics.values()))
our_specs = copy.deepcopy(self.__specs)

new_generator = ObjectGenerator(
spec_registry=our_specs,
semantic_providers=our_providers,
config=new_config
)

return new_generator

def generate(
self, spec_name, expected_type=None, spec_name_stack=None,
value_constraint=None
Expand Down
2 changes: 1 addition & 1 deletion stix2generator/generation/reference_graph_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ def __init__(
.Config(
**halt_generator_config_dict
)
self.__halt_generator = stix2generator.create_object_generator(
self.__halt_generator = object_generator.derive_generator(
halt_generator_config
)

Expand Down
87 changes: 87 additions & 0 deletions stix2generator/test/test_reference_graph_generator.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import copy

import pytest
import stix2
import stix2.base
import stix2.utils

import stix2generator
import stix2generator.generation.object_generator
import stix2generator.generation.reference_graph_generator
import stix2generator.generation.semantics
import stix2generator.test.utils

_TLP_MARKING_DEFINITION_IDS = {
Expand Down Expand Up @@ -959,3 +961,88 @@ def test_not_parsing(num_trials):

else:
assert isinstance(obj, dict)


def test_ref_gen_with_custom():
"""
Set up a reference graph generator with an object generator with stuff
you would not get by default via
stix2generator.create_object_generator(...). Reference graph generator
creates a derivative "halt" generator, and we need to ensure that the
latter inherits the same config as the original object generator, including
registry and semantics providers.
"""
class TestSemantics(stix2generator.generation.semantics.SemanticsProvider):
def get_semantics(self):
return ["testxyz"]

def testxyz(self, spec, generator, constraint):
return "test"

custom_registry = {
# A self-referential type, to cause simple reference chains
"test-ref-type": {
"type": "object",
"optional": ["obj_ref"],
"properties": {
"id": {
"type": "string",
"semantics": "stix-id",
"stix-type": "test-ref-type"
},
"type": "test-ref-type",
"test_prop": {
"type": "string",
"semantics": "testxyz"
},
"obj_ref": {
"type": "string",
"semantics": "stix-id",
"stix-type": "test-ref-type"
}
}
}
}

@stix2.CustomObject("test-ref-type", [])
class TestRefType:
# This class won't be used since we'll turn the parse setting off; but
# we need a registration so test-ref-type is seen as a generatable
# type. So we can leave it empty.
pass

semantics_providers = [
TestSemantics(),
stix2generator.generation.semantics.STIXSemantics()
]

obj_gen_config = stix2generator.generation.object_generator.Config(
optional_property_probability=1,
minimize_ref_properties=False
)

obj_gen = stix2generator.generation.object_generator.ObjectGenerator(
custom_registry, semantics_providers, obj_gen_config
)

ref_gen_config = stix2generator.generation.reference_graph_generator \
.Config(
graph_type="TREE",
parse=False
)

ref_gen = stix2generator.generation.reference_graph_generator \
.ReferenceGraphGenerator(
obj_gen, ref_gen_config
)

_, graph = ref_gen.generate()

for obj in graph.values():
# Ensure our semantics provider was invoked properly for all objects
assert obj["test_prop"] == "test"

# Ensure no dangling references
obj_ref = obj.get("obj_ref")
if obj_ref is not None:
assert obj_ref in graph

0 comments on commit b64aaba

Please sign in to comment.