"""STIX 2.1 Common Data Types and Properties.""" from collections import OrderedDict from ..base import _STIXBase from ..custom import _custom_marking_builder from ..exceptions import InvalidValueError from ..markings import _MarkingsMixin from ..markings.utils import check_tlp_marking from ..properties import ( BooleanProperty, DictionaryProperty, HashesProperty, IDProperty, IntegerProperty, ListProperty, Property, ReferenceProperty, SelectorProperty, StringProperty, TimestampProperty, TypeProperty, ) from ..utils import NOW, _get_dict class ExternalReference(_STIXBase): """For more detailed information on this object's properties, see `the STIX 2.1 specification `__. """ _properties = OrderedDict([ ('source_name', StringProperty(required=True)), ('description', StringProperty()), ('url', StringProperty()), ('hashes', HashesProperty(spec_version='2.1')), ('external_id', StringProperty()), ]) # This is hash-algorithm-ov _LEGAL_HASHES = { "MD5", "SHA-1", "SHA-256", "SHA-512", "SHA3-256", "SHA3-512", "SSDEEP", "TLSH", } def _check_object_constraints(self): super(ExternalReference, self)._check_object_constraints() self._check_at_least_one_property(['description', 'external_id', 'url']) if "hashes" in self: if any( hash_ not in self._LEGAL_HASHES for hash_ in self["hashes"] ): raise InvalidValueError( ExternalReference, "hashes", "Hash algorithm names must be members of hash-algorithm-ov", ) class KillChainPhase(_STIXBase): """For more detailed information on this object's properties, see `the STIX 2.1 specification `__. """ _properties = OrderedDict([ ('kill_chain_name', StringProperty(required=True)), ('phase_name', StringProperty(required=True)), ]) class GranularMarking(_STIXBase): """For more detailed information on this object's properties, see `the STIX 2.1 specification `__. """ _properties = OrderedDict([ ('lang', StringProperty()), ('marking_ref', ReferenceProperty(valid_types='marking-definition', spec_version='2.1')), ('selectors', ListProperty(SelectorProperty, required=True)), ]) def _check_object_constraints(self): super(GranularMarking, self)._check_object_constraints() self._check_at_least_one_property(['lang', 'marking_ref']) class LanguageContent(_STIXBase): """For more detailed information on this object's properties, see `the STIX 2.1 specification `__. """ _type = 'language-content' _properties = OrderedDict([ ('type', TypeProperty(_type)), ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('object_ref', ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1', required=True)), # TODO: 'object_modified' it MUST be an exact match for the modified time of the STIX Object (SRO or SDO) being referenced. ('object_modified', TimestampProperty(precision='millisecond')), # TODO: 'contents' https://docs.google.com/document/d/1ShNq4c3e1CkfANmD9O--mdZ5H0O_GLnjN28a_yrEaco/edit#heading=h.cfz5hcantmvx ('contents', DictionaryProperty(spec_version='2.1', required=True)), ('revoked', BooleanProperty()), ('labels', ListProperty(StringProperty)), ('confidence', IntegerProperty()), ('external_references', ListProperty(ExternalReference)), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ]) class TLPMarking(_STIXBase): """For more detailed information on this object's properties, see `the STIX 2.1 specification `__. """ _type = 'tlp' _properties = OrderedDict([ ('tlp', StringProperty(required=True)), ]) class StatementMarking(_STIXBase): """For more detailed information on this object's properties, see `the STIX 2.1 specification `__. """ _type = 'statement' _properties = OrderedDict([ ('statement', StringProperty(required=True)), ]) def __init__(self, statement=None, **kwargs): # Allow statement as positional args. if statement and not kwargs.get('statement'): kwargs['statement'] = statement super(StatementMarking, self).__init__(**kwargs) class MarkingProperty(Property): """Represent the marking objects in the ``definition`` property of marking-definition objects. """ def clean(self, value): if type(value) in OBJ_MAP_MARKING.values(): return value else: raise ValueError("must be a Statement, TLP Marking or a registered marking.") class MarkingDefinition(_STIXBase, _MarkingsMixin): """For more detailed information on this object's properties, see `the STIX 2.1 specification `__. """ _type = 'marking-definition' _properties = OrderedDict([ ('type', TypeProperty(_type)), ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type)), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('external_references', ListProperty(ExternalReference)), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('definition_type', StringProperty(required=True)), ('name', StringProperty()), ('definition', MarkingProperty(required=True)), ]) def __init__(self, **kwargs): if set(('definition_type', 'definition')).issubset(kwargs.keys()): # Create correct marking type object try: marking_type = OBJ_MAP_MARKING[kwargs['definition_type']] except KeyError: raise ValueError("definition_type must be a valid marking type") if not isinstance(kwargs['definition'], marking_type): defn = _get_dict(kwargs['definition']) kwargs['definition'] = marking_type(**defn) interoperability = kwargs.get('interoperability', False) self._properties['id'].interoperability = interoperability if kwargs.get('created_by_ref'): self._properties['created_by_ref'].interoperability = interoperability if kwargs.get('object_marking_refs'): self._properties['object_marking_refs'].contained.interoperability = interoperability super(MarkingDefinition, self).__init__(**kwargs) def _check_object_constraints(self): super(MarkingDefinition, self)._check_object_constraints() check_tlp_marking(self, '2.1') def serialize(self, pretty=False, include_optional_defaults=False, **kwargs): check_tlp_marking(self, '2.1') return super(MarkingDefinition, self).serialize(pretty, include_optional_defaults, **kwargs) OBJ_MAP_MARKING = { 'tlp': TLPMarking, 'statement': StatementMarking, } def CustomMarking(type='x-custom-marking', properties=None): """Custom STIX Marking decorator. Example: >>> from stix2.v21 import CustomMarking >>> from stix2.properties import IntegerProperty, StringProperty >>> @CustomMarking('x-custom-marking', [ ... ('property1', StringProperty(required=True)), ... ('property2', IntegerProperty()), ... ]) ... class MyNewMarkingObjectType(): ... pass """ def wrapper(cls): return _custom_marking_builder(cls, type, properties, '2.1') return wrapper # TODO: don't allow the creation of any other TLPMarkings than the ones below TLP_WHITE = MarkingDefinition( id='marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9', created='2017-01-20T00:00:00.000Z', definition_type='tlp', name='TLP:WHITE', definition=TLPMarking(tlp='white'), ) TLP_GREEN = MarkingDefinition( id='marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da', created='2017-01-20T00:00:00.000Z', definition_type='tlp', name='TLP:GREEN', definition=TLPMarking(tlp='green'), ) TLP_AMBER = MarkingDefinition( id='marking-definition--f88d31f6-486f-44da-b317-01333bde0b82', created='2017-01-20T00:00:00.000Z', definition_type='tlp', name='TLP:AMBER', definition=TLPMarking(tlp='amber'), ) TLP_RED = MarkingDefinition( id='marking-definition--5e57c739-391a-4eb3-b6be-7d15ca92d5ed', created='2017-01-20T00:00:00.000Z', definition_type='tlp', name='TLP:RED', definition=TLPMarking(tlp='red'), )