1"""STIX 2.1 Common Data Types and Properties.""" 2 3from collections import OrderedDict 4 5from ..custom import _custom_marking_builder 6from ..exceptions import InvalidValueError 7from ..markings import _MarkingsMixin 8from ..markings.utils import check_tlp_marking 9from ..properties import ( 10 BooleanProperty, DictionaryProperty, HashesProperty, IDProperty, 11 IntegerProperty, ListProperty, Property, ReferenceProperty, 12 SelectorProperty, StringProperty, TimestampProperty, TypeProperty, 13) 14from ..utils import NOW, _get_dict 15from .base import _STIXBase21 16 17 18class ExternalReference(_STIXBase21): 19 """For more detailed information on this object's properties, see 20 `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_bajcvqteiard>`__. 21 """ 22 23 _properties = OrderedDict([ 24 ('source_name', StringProperty(required=True)), 25 ('description', StringProperty()), 26 ('url', StringProperty()), 27 ('hashes', HashesProperty(spec_version='2.1')), 28 ('external_id', StringProperty()), 29 ]) 30 31 # This is hash-algorithm-ov 32 _LEGAL_HASHES = { 33 "MD5", "SHA-1", "SHA-256", "SHA-512", "SHA3-256", "SHA3-512", "SSDEEP", 34 "TLSH", 35 } 36 37 def _check_object_constraints(self): 38 super(ExternalReference, self)._check_object_constraints() 39 self._check_at_least_one_property(['description', 'external_id', 'url']) 40 41 if "hashes" in self: 42 if any( 43 hash_ not in self._LEGAL_HASHES 44 for hash_ in self["hashes"] 45 ): 46 raise InvalidValueError( 47 ExternalReference, "hashes", 48 "Hash algorithm names must be members of hash-algorithm-ov", 49 ) 50 51 52class KillChainPhase(_STIXBase21): 53 """For more detailed information on this object's properties, see 54 `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_i4tjv75ce50h>`__. 55 """ 56 57 _properties = OrderedDict([ 58 ('kill_chain_name', StringProperty(required=True)), 59 ('phase_name', StringProperty(required=True)), 60 ]) 61 62 63class GranularMarking(_STIXBase21): 64 """For more detailed information on this object's properties, see 65 `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_robezi5egfdr>`__. 66 """ 67 68 _properties = OrderedDict([ 69 ('lang', StringProperty()), 70 ('marking_ref', ReferenceProperty(valid_types='marking-definition', spec_version='2.1')), 71 ('selectors', ListProperty(SelectorProperty, required=True)), 72 ]) 73 74 def _check_object_constraints(self): 75 super(GranularMarking, self)._check_object_constraints() 76 self._check_at_least_one_property(['lang', 'marking_ref']) 77 78 79class LanguageContent(_STIXBase21): 80 """For more detailed information on this object's properties, see 81 `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_nfwr8z9ax2bi>`__. 82 """ 83 84 _type = 'language-content' 85 _properties = OrderedDict([ 86 ('type', TypeProperty(_type, spec_version='2.1')), 87 ('spec_version', StringProperty(fixed='2.1')), 88 ('id', IDProperty(_type, spec_version='2.1')), 89 ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), 90 ('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), 91 ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), 92 ('object_ref', ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1', required=True)), 93 # TODO: 'object_modified' it MUST be an exact match for the modified time of the STIX Object (SRO or SDO) being referenced. 94 ('object_modified', TimestampProperty(precision='millisecond')), 95 # TODO: 'contents' https://docs.google.com/document/d/1ShNq4c3e1CkfANmD9O--mdZ5H0O_GLnjN28a_yrEaco/edit#heading=h.cfz5hcantmvx 96 ('contents', DictionaryProperty(spec_version='2.1', required=True)), 97 ('revoked', BooleanProperty(default=lambda: False)), 98 ('labels', ListProperty(StringProperty)), 99 ('confidence', IntegerProperty()), 100 ('external_references', ListProperty(ExternalReference)), 101 ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), 102 ('granular_markings', ListProperty(GranularMarking)), 103 ]) 104 105 106class TLPMarking(_STIXBase21): 107 """For more detailed information on this object's properties, see 108 `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_yd3ar14ekwrs>`__. 109 """ 110 111 _type = 'tlp' 112 _properties = OrderedDict([ 113 ('tlp', StringProperty(required=True)), 114 ]) 115 116 117class StatementMarking(_STIXBase21): 118 """For more detailed information on this object's properties, see 119 `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_3ru8r05saera>`__. 120 """ 121 122 _type = 'statement' 123 _properties = OrderedDict([ 124 ('statement', StringProperty(required=True)), 125 ]) 126 127 def __init__(self, statement=None, **kwargs): 128 # Allow statement as positional args. 129 if statement and not kwargs.get('statement'): 130 kwargs['statement'] = statement 131 132 super(StatementMarking, self).__init__(**kwargs) 133 134 135class MarkingProperty(Property): 136 """Represent the marking objects in the ``definition`` property of 137 marking-definition objects. 138 """ 139 140 def clean(self, value): 141 if type(value) in OBJ_MAP_MARKING.values(): 142 return value 143 else: 144 raise ValueError("must be a Statement, TLP Marking or a registered marking.") 145 146 147class MarkingDefinition(_STIXBase21, _MarkingsMixin): 148 """For more detailed information on this object's properties, see 149 `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_hr5vgqxjk7ns>`__. 150 """ 151 152 _type = 'marking-definition' 153 _properties = OrderedDict([ 154 ('type', TypeProperty(_type, spec_version='2.1')), 155 ('spec_version', StringProperty(fixed='2.1')), 156 ('id', IDProperty(_type)), 157 ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), 158 ('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), 159 ('definition_type', StringProperty(required=True)), 160 ('name', StringProperty()), 161 ('definition', MarkingProperty(required=True)), 162 ('external_references', ListProperty(ExternalReference)), 163 ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), 164 ('granular_markings', ListProperty(GranularMarking)), 165 ]) 166 167 def __init__(self, **kwargs): 168 if set(('definition_type', 'definition')).issubset(kwargs.keys()): 169 # Create correct marking type object 170 try: 171 marking_type = OBJ_MAP_MARKING[kwargs['definition_type']] 172 except KeyError: 173 raise ValueError("definition_type must be a valid marking type") 174 175 if not isinstance(kwargs['definition'], marking_type): 176 defn = _get_dict(kwargs['definition']) 177 kwargs['definition'] = marking_type(**defn) 178 179 super(MarkingDefinition, self).__init__(**kwargs) 180 181 def _check_object_constraints(self): 182 super(MarkingDefinition, self)._check_object_constraints() 183 check_tlp_marking(self, '2.1') 184 185 def serialize(self, pretty=False, include_optional_defaults=False, **kwargs): 186 check_tlp_marking(self, '2.1') 187 return super(MarkingDefinition, self).serialize(pretty, include_optional_defaults, **kwargs) 188 189 190OBJ_MAP_MARKING = { 191 'tlp': TLPMarking, 192 'statement': StatementMarking, 193} 194 195 196def CustomMarking(type='x-custom-marking', properties=None): 197 """Custom STIX Marking decorator. 198 199 Example: 200 >>> from stix2.v21 import CustomMarking 201 >>> from stix2.properties import IntegerProperty, StringProperty 202 >>> @CustomMarking('x-custom-marking', [ 203 ... ('property1', StringProperty(required=True)), 204 ... ('property2', IntegerProperty()), 205 ... ]) 206 ... class MyNewMarkingObjectType(): 207 ... pass 208 209 """ 210 def wrapper(cls): 211 return _custom_marking_builder(cls, type, properties, '2.1', _STIXBase21) 212 return wrapper 213 214 215# TODO: don't allow the creation of any other TLPMarkings than the ones below 216 217TLP_WHITE = MarkingDefinition( 218 id='marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9', 219 created='2017-01-20T00:00:00.000Z', 220 definition_type='tlp', 221 name='TLP:WHITE', 222 definition=TLPMarking(tlp='white'), 223) 224 225TLP_GREEN = MarkingDefinition( 226 id='marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da', 227 created='2017-01-20T00:00:00.000Z', 228 definition_type='tlp', 229 name='TLP:GREEN', 230 definition=TLPMarking(tlp='green'), 231) 232 233TLP_AMBER = MarkingDefinition( 234 id='marking-definition--f88d31f6-486f-44da-b317-01333bde0b82', 235 created='2017-01-20T00:00:00.000Z', 236 definition_type='tlp', 237 name='TLP:AMBER', 238 definition=TLPMarking(tlp='amber'), 239) 240 241TLP_RED = MarkingDefinition( 242 id='marking-definition--5e57c739-391a-4eb3-b6be-7d15ca92d5ed', 243 created='2017-01-20T00:00:00.000Z', 244 definition_type='tlp', 245 name='TLP:RED', 246 definition=TLPMarking(tlp='red'), 247) 248