def test_add_lmco_phase(self): phase = lmco.PHASE_DELIVERY refs = KillChainPhasesReference() refs.append(phase) self.assertTrue(isinstance(refs[0], KillChainPhaseReference)) self.assertTrue(refs[0].phase_id, lmco.PHASE_DELIVERY.phase_id)
def test_add_lmco_phase(self): phase = lmco.PHASE_DELIVERY refs = KillChainPhasesReference() refs.append(phase) self.assertTrue(isinstance(refs[0], KillChainPhaseReference)) self.assertTrue(refs[0].phase_id, lmco.PHASE_DELIVERY.phase_id)
class TTP(stix.BaseCoreComponent): """Implementation of the STIX TTP. Args: id_ (optional): An identifier. If ``None``, a value will be generated via ``mixbox.idgen.create_id()``. If set, this will unset the ``idref`` property. idref (optional): An identifier reference. If set this will unset the ``id_`` property. timestamp (optional): A timestamp value. Can be an instance of ``datetime.datetime`` or ``str``. description: A description of the purpose or intent of this object. short_description: A short description of the intent or purpose of this object. title: The title of this object. """ _binding = ttp_binding _binding_class = _binding.TTPType _namespace = "http://stix.mitre.org/TTP-1" _version = "1.2" _ALL_VERSIONS = ("1.0", "1.0.1", "1.1", "1.1.1", "1.2") _ID_PREFIX = "ttp" behavior = fields.TypedField("Behavior", Behavior) related_ttps = fields.TypedField("Related_TTPs", RelatedTTPs) intended_effects = StatementField("Intended_Effect", Statement, vocab_type=vocabs.IntendedEffect, multiple=True) resources = fields.TypedField("Resources", Resource) victim_targeting = fields.TypedField("Victim_Targeting", VictimTargeting) exploit_targets = fields.TypedField("Exploit_Targets", ExploitTargets) related_packages = fields.TypedField("Related_Packages", RelatedPackageRefs) kill_chain_phases = fields.TypedField("Kill_Chain_Phases", KillChainPhasesReference) information_source = fields.TypedField("Information_Source", InformationSource) def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): super(TTP, self).__init__(id_=id_, idref=idref, timestamp=timestamp, title=title, description=description, short_description=short_description) self.related_packages = RelatedPackageRefs() self.exploit_targets = ExploitTargets() self.related_ttps = RelatedTTPs() self.kill_chain_phases = KillChainPhasesReference() def add_related_ttp(self, value): """Adds an Related TTP to the :attr:`related_ttps` list property of this :class:`TTP`. The `TTP` parameter must be an instance of :class:`.RelatedTTP` or :class:`TTP`. If the `TTP` parameter is ``None``, no item wil be added to the ``related_ttps`` list property. Calling this method is the same as calling ``append()`` on the ``related_ttps`` property. See Also: The :class:`RelatedTTPs` documentation. Note: If the `TTP` parameter is not an instance of :class:`.RelatedTTP` an attempt will be made to convert it to one. Args: TTP: An instance of :class:`TTP` or :class:`.RelatedTTP`. Raises: ValueError: If the `TTP` parameter cannot be converted into an instance of :class:`.RelatedTTP` """ self.related_ttps.append(value) def add_exploit_target(self, value): """Adds a :class:`.ExploitTarget` object to the :attr:`exploit_targets` collection. """ self.exploit_targets.append(value) def add_intended_effect(self, value): """Adds a :class:`.Statement` object to the :attr:`intended_effects` collection. If `value` is a string, an attempt will be made to convert it into an instance of :class:`.Statement`. """ self.intended_effects.append(value) def add_kill_chain_phase(self, value): """Adds a :class:`.KillChainPhaseReference` to the :attr:`kill_chain_phases` collection. Args: value: A :class:`.KillChainPhase`, :class:`.KillChainPhaseReference` or a ``str`` representing the phase_id of. Note that you if you are defining a custom Kill Chain, you need to add it to the STIX package separately. """ self.kill_chain_phases.append(value) def add_related_package(self, value): """Adds a :class:`.RelatedPackageRef` object to the :attr:`related_packages` collection. Args: value: A :class:`.RelatedPackageRef` or a :class:`.STIXPackage` object. """ self.related_packages.append(value)
class Indicator(stix.BaseCoreComponent): """Implementation of the STIX Indicator. Args: id_ (optional): An identifier. If ``None``, a value will be generated via ``mixbox.idgen.create_id()``. If set, this will unset the ``idref`` property. idref (optional): An identifier reference. If set this will unset the ``id_`` property. title (optional): A string title. timestamp (optional): A timestamp value. Can be an instance of ``datetime.datetime`` or ``str``. description (optional): A string description. short_description (optional): A string short description. """ _binding = indicator_binding _binding_class = indicator_binding.IndicatorType _namespace = 'http://stix.mitre.org/Indicator-2' _version = "2.2" _ALL_VERSIONS = ("2.0", "2.0.1", "2.1", "2.1.1", "2.2") _ALLOWED_COMPOSITION_OPERATORS = ('AND', 'OR') _ID_PREFIX = "indicator" _try_cast = False producer = fields.TypedField("Producer", InformationSource) observable = fields.TypedField("Observable", Observable) indicator_types = VocabField("Type", IndicatorType, multiple=True, key_name="indicator_types") confidence = fields.TypedField("Confidence", Confidence) indicated_ttps = fields.TypedField("Indicated_TTP", RelatedTTP, multiple=True, key_name="indicated_ttps") test_mechanisms = fields.TypedField("Test_Mechanisms", TestMechanisms) alternative_id = fields.TypedField("Alternative_ID", multiple=True) suggested_coas = fields.TypedField("Suggested_COAs", SuggestedCOAs) sightings = fields.TypedField("Sightings", Sightings) composite_indicator_expression = fields.TypedField( "Composite_Indicator_Expression", "stix.indicator.CompositeIndicatorExpression") kill_chain_phases = fields.TypedField("Kill_Chain_Phases", KillChainPhasesReference) valid_time_positions = fields.TypedField("Valid_Time_Position", ValidTime, multiple=True, key_name="valid_time_positions") related_indicators = fields.TypedField("Related_Indicators", RelatedIndicators) related_campaigns = fields.TypedField( "Related_Campaigns", type_="stix.indicator.RelatedCampaignRefs") likely_impact = fields.TypedField("Likely_Impact", Statement) negate = fields.TypedField("negate") related_packages = fields.TypedField("Related_Packages", RelatedPackageRefs) def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): super(Indicator, self).__init__(id_=id_, idref=idref, timestamp=timestamp, title=title, description=description, short_description=short_description) self.observable = None self.indicator_types = IndicatorTypes() self.test_mechanisms = TestMechanisms() self.alternative_id = None self.suggested_coas = SuggestedCOAs() self.sightings = Sightings() self.composite_indicator_expression = None self.kill_chain_phases = KillChainPhasesReference() self.related_indicators = RelatedIndicators() self.related_campaigns = RelatedCampaignRefs() self.observable_composition_operator = "OR" self.related_packages = RelatedPackageRefs() @property def observables(self): """A list of ``cybox.core.Observable`` instances. This can be set to a single object instance or a list of objects. Note: If only one Observable is set, this property will return a list with the ``observable`` property. If multiple ``cybox.core.Observable`` this property will return Observables under the ``cybox.core.ObservableComposition``. Access to the top level ``cybox.core.Observable`` is made via ``observable`` property. Default Value: Empty ``list``. Returns: A ``list`` of ``cybox.core.Observable`` instances. """ if not self.observable: return [] elif self.observable.observable_composition: return self.observable.observable_composition.observables return [] @observables.setter def observables(self, value): """ The method will automatically create a top ``cybox.core.Observable`` and append all ``cybox.core.Observable`` using ``observable_composition`` property when a ``list`` is given with length greater than 1. Note: The top level ``cybox.core.Observable`` will set the ``operator`` property for the ``cybox.core.ObservableComposition`` via the ``observable_composition_operator`` property. The value of ``operator`` can be changed via ``observable_composition_operator`` property. By default, the composition layer will be set to ``"OR"``. Args: value: A ``list`` of ``cybox.core.Observable`` instances or a single ``cybox.core.Observable`` instance. Raises: ValueError: If set to a value that cannot be converted to an instance of ``cybox.core.Observable``. """ if not value: return if isinstance(value, Observable): self.observable = value elif utils.is_sequence(value): if len(value) == 1: self.observable = value return observable_comp = ObservableComposition() observable_comp.operator = self.observable_composition_operator for element in value: observable_comp.add(element) self.observable = Observable() self.observable.observable_composition = observable_comp def set_observables(self, value): self.observables = value def add_observable(self, observable): """Adds an observable to the ``observable`` property of the :class:`Indicator`. If the `observable` parameter is ``None``, no item will be added to the ``observable`` property. Note: The STIX Language dictates that an :class:`Indicator` can have only one ``Observable`` under it. Because of this, when a user adds another ``Observable`` a new, empty ``Observable`` will be crated and append the existing and new ``observable`` using the ``ObservableComposition`` property. To access the top level ``Observable`` can be achieved by the ``observable`` property .By default, the ``operator`` of the composition layer will be set to ``"OR"``. The ``operator`` value can be changed via the ``observable_composition_operator`` property. Setting ``observable`` or ``observables`` with re-initialize the property and lose all ``Observable`` in the composition layer. Args: observable: An instance of ``cybox.core.Observable`` or an object type that can be converted into one. Raises: ValueError: If the `observable` param cannot be converted into an instance of ``cybox.core.Observable``. """ if not observable: return # Sets the first observable. elif not self.observable: self.observable = observable # When another is inserted. A "root" Observable is created and the # user's Observables are appended to the composition. elif not self.observable.observable_composition: observable_comp = ObservableComposition() observable_comp.operator = self.observable_composition_operator observable_comp.add(self.observable) observable_comp.add(observable) self.observable = Observable() self.observable.observable_composition = observable_comp # Keep appending to "root" Observable. else: self.observable.observable_composition.add(observable) def add_alternative_id(self, value): """Adds an alternative id to the ``alternative_id`` list property. Note: If ``None`` is passed in no value is added to the ``alternative_id`` list property. Args: value: An identifier value. """ if not value: return self.alternative_id.append(value) def add_valid_time_position(self, value): """Adds an valid time position to the ``valid_time_positions`` property list. If `value` is ``None``, no item is added to the ``value_time_positions`` list. Args: value: An instance of :class:`stix.indicator.valid_time.ValidTime`. Raises: ValueError: If the `value` argument is not an instance of :class:`stix.indicator.valid_time.ValidTime`. """ self.valid_time_positions.append(value) def add_indicator_type(self, value): """Adds a value to the ``indicator_types`` list property. The `value` parameter can be a ``str`` or an instance of :class:`stix.common.vocabs.VocabString`. Note: If the `value` parameter is a ``str`` instance, an attempt will be made to convert it into an instance of :class:`stix.common.vocabs.IndicatorType` Args: value: An instance of :class:`stix.common.vocabs.VocabString` or ``str``. Raises: ValueError: If the `value` param is a ``str`` instance that cannot be converted into an instance of :class:`stix.common.vocabs.IndicatorType`. """ self.indicator_types.append(value) def add_indicated_ttp(self, v): """Adds an Indicated TTP to the ``indicated_ttps`` list property of this :class:`Indicator`. The `v` parameter must be an instance of :class:`stix.common.related.RelatedTTP` or :class:`stix.ttp.TTP`. If the `v` parameter is ``None``, no item wil be added to the ``indicated_ttps`` list property. Note: If the `v` parameter is not an instance of :class:`stix.common.related.RelatedTTP` an attempt will be made to convert it to one. Args: v: An instance of :class:`stix.common.related.RelatedTTP` or :class:`stix.ttp.TTP`. Raises: ValueError: If the `v` parameter cannot be converted into an instance of :class:`stix.common.related.RelatedTTP` """ self.indicated_ttps.append(v) def add_test_mechanism(self, tm): """Adds an Test Mechanism to the ``test_mechanisms`` list property of this :class:`Indicator`. The `tm` parameter must be an instance of a :class:`stix.indicator.test_mechanism._BaseTestMechanism` implementation. If the `tm` parameter is ``None``, no item will be added to the ``test_mechanisms`` list property. See Also: Test Mechanism implementations are found under the :mod:`stix.extensions.test_mechanism` package. Args: tm: An instance of a :class:`stix.indicator.test_mechanism._BaseTestMechanism` implementation. Raises: ValueError: If the `tm` parameter is not an instance of :class:`stix.indicator.test_mechanism._BaseTestMechanism` """ self.test_mechanisms.append(tm) def add_related_indicator(self, indicator): """Adds an Related Indicator to the ``related_indicators`` list property of this :class:`Indicator`. The `indicator` parameter must be an instance of :class:`stix.common.related.RelatedIndicator` or :class:`Indicator`. If the `indicator` parameter is ``None``, no item wil be added to the ``related_indicators`` list property. Calling this method is the same as calling ``append()`` on the ``related_indicators`` proeprty. See Also: The :class:`RelatedIndicators` documentation. Note: If the `tm` parameter is not an instance of :class:`stix.common.related.RelatedIndicator` an attempt will be made to convert it to one. Args: indicator: An instance of :class:`Indicator` or :class:`stix.common.related.RelatedIndicator`. Raises: ValueError: If the `indicator` parameter cannot be converted into an instance of :class:`stix.common.related.RelatedIndicator` """ self.related_indicators.append(indicator) def add_related_campaign(self, value): """Adds a Related Campaign to this Indicator. The `value` parameter must be an instance of :class:`.RelatedCampaignRef` or :class:`.CampaignRef`. If the `value` parameter is ``None``, no item wil be added to the ``related_campaigns`` collection. Calling this method is the same as calling ``append()`` on the ``related_campaigns`` property. See Also: The :class:`.RelatedCampaignRef` documentation. Note: If the `value` parameter is not an instance of :class:`.RelatedCampaignRef` an attempt will be made to convert it to one. Args: value: An instance of :class:`.RelatedCampaignRef` or :class:`.Campaign`. Raises: ValueError: If the `value` parameter cannot be converted into an instance of :class:`.RelatedCampaignRef` """ self.related_campaigns.append(value) @property def observable_composition_operator(self): return self._observable_composition_operator @observable_composition_operator.setter def observable_composition_operator(self, value): if value in self._ALLOWED_COMPOSITION_OPERATORS: self._observable_composition_operator = value if self.observable and self.observable.observable_composition: self.observable.observable_composition.operator = value return error = "observable_composition_operator must one of {0}" error = error.format(self._ALLOWED_COMPOSITION_OPERATORS) raise ValueError(error) def add_kill_chain_phase(self, value): """Add a new Kill Chain Phase reference to this Indicator. Args: value: a :class:`stix.common.kill_chains.KillChainPhase` or a `str` representing the phase_id of. Note that you if you are defining a custom Kill Chain, you need to add it to the STIX package separately. """ self.kill_chain_phases.append(value) def add_related_package(self, value): self.related_packages.append(value) def set_producer_identity(self, identity): """Sets the name of the producer of this indicator. This is the same as calling ``indicator.producer.identity.name = identity``. If the ``producer`` property is ``None``, it will be initialized to an instance of :class:`stix.common.information_source.InformationSource`. If the ``identity`` property of the ``producer`` instance is ``None``, it will be initialized to an instance of :class:`stix.common.identity.Identity`. Note: if the `identity` parameter is not an instance :class:`stix.common.identity.Identity` an attempt will be made to convert it to one. Args: identity: An instance of ``str`` or ``stix.common.identity.Identity``. """ def unset_producer_identity(): try: self.producer.identity.name = None except AttributeError: pass if not identity: unset_producer_identity() return if not self.producer: self.producer = InformationSource() if isinstance(identity, Identity): self.producer.identity = identity return if not self.producer.identity: self.producer.identity = Identity() self.producer.identity.name = str(identity) def set_produced_time(self, produced_time): """Sets the ``produced_time`` property of the ``producer`` property instance fo `produced_time`. This is the same as calling ``indicator.producer.time.produced_time = produced_time``. The `produced_time` parameter must be an instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Note: If `produced_time` is a ``str`` or ``datetime.datetime`` instance an attempt will be made to convert it into an instance of ``cybox.common.DateTimeWithPrecision``. Args: produced_time: An instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. """ if not self.producer: self.producer = InformationSource() if not self.producer.time: self.producer.time = Time() self.producer.time.produced_time = produced_time def get_produced_time(self): """Gets the produced time for this :class:`Indicator`. This is the same as calling ``produced_time = indicator.producer.time.produced_time``. Returns: ``None`` or an instance of ``cybox.common.DateTimeWithPrecision``. """ try: return self.producer.time.produced_time except AttributeError: return None def set_received_time(self, received_time): """Sets the received time for this :class:`Indicator`. This is the same as calling ``indicator.producer.time.produced_time = produced_time``. The `received_time` parameter must be an instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Args: received_time: An instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Note: If `received_time` is a ``str`` or ``datetime.datetime`` instance an attempt will be made to convert it into an instance of ``cybox.common.DateTimeWithPrecision``. """ if not self.producer: self.producer = InformationSource() if not self.producer.time: self.producer.time = Time() self.producer.time.received_time = received_time def get_received_time(self): """Gets the received time for this :class:`Indicator`. This is the same as calling ``received_time = indicator.producer.time.received_time``. Returns: ``None`` or an instance of ``cybox.common.DateTimeWithPrecision``. """ try: return self.producer.time.received_time except AttributeError: return None def _merge_observables(self, observables): observable_composition = ObservableComposition() observable_composition.operator = self.observable_composition_operator for observable in observables: observable_composition.add(observable) root_observable = Observable() root_observable.observable_composition = observable_composition return root_observable def add_object(self, object_): """Adds a python-cybox Object instance to the ``observables`` list property. This is the same as calling ``indicator.add_observable(object_)``. Note: If the `object` param is not an instance of ``cybox.core.Object`` an attempt will be made to to convert it into one before wrapping it in an ``cybox.core.Observable`` layer. Args: object_: An instance of ``cybox.core.Object`` or an object that can be converted into an instance of ``cybox.core.Observable`` Raises: ValueError: if the `object_` param cannot be converted to an instance of ``cybox.core.Observable``. """ if not object_: return observable = Observable(object_) self.add_observable(observable) def _finalize_obj(self, entity_obj): """Omits the `negate` field if it is not equal to True. """ if self.negate: entity_obj.negate = True elif hasattr(entity_obj, 'negate'): entity_obj.negate = None def _finalize_dict(self, entity_dict): """Omits the `negate` field if it is not equal to True. """ if self.negate: entity_dict['negate'] = True elif 'negate' in entity_dict: del entity_dict['negate']
class Indicator(stix.BaseCoreComponent): """Implementation of the STIX Indicator. Args: id_ (optional): An identifier. If ``None``, a value will be generated via ``mixbox.idgen.create_id()``. If set, this will unset the ``idref`` property. idref (optional): An identifier reference. If set this will unset the ``id_`` property. title (optional): A string title. timestamp (optional): A timestamp value. Can be an instance of ``datetime.datetime`` or ``str``. description (optional): A string description. short_description (optional): A string short description. """ _binding = indicator_binding _binding_class = indicator_binding.IndicatorType _namespace = 'http://stix.mitre.org/Indicator-2' _version = "2.2" _ALL_VERSIONS = ("2.0", "2.0.1", "2.1", "2.1.1", "2.2") _ALLOWED_COMPOSITION_OPERATORS = ('AND', 'OR') _ID_PREFIX = "indicator" _try_cast = False producer = fields.TypedField("Producer", InformationSource) observable = fields.TypedField("Observable", Observable) indicator_types = VocabField("Type", IndicatorType, multiple=True, key_name="indicator_types") confidence = fields.TypedField("Confidence", Confidence) indicated_ttps = fields.TypedField("Indicated_TTP", RelatedTTP, multiple=True, key_name="indicated_ttps") test_mechanisms = fields.TypedField("Test_Mechanisms", TestMechanisms) alternative_id = fields.TypedField("Alternative_ID", multiple=True) suggested_coas = fields.TypedField("Suggested_COAs", SuggestedCOAs) sightings = fields.TypedField("Sightings", Sightings) composite_indicator_expression = fields.TypedField("Composite_Indicator_Expression", "stix.indicator.CompositeIndicatorExpression") kill_chain_phases = fields.TypedField("Kill_Chain_Phases", KillChainPhasesReference) valid_time_positions = fields.TypedField("Valid_Time_Position", ValidTime, multiple=True, key_name="valid_time_positions") related_indicators = fields.TypedField("Related_Indicators", RelatedIndicators) related_campaigns = fields.TypedField("Related_Campaigns", type_="stix.indicator.RelatedCampaignRefs") likely_impact = fields.TypedField("Likely_Impact", Statement) negate = fields.TypedField("negate") related_packages = fields.TypedField("Related_Packages", RelatedPackageRefs) def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): super(Indicator, self).__init__( id_=id_, idref=idref, timestamp=timestamp, title=title, description=description, short_description=short_description ) self.observable = None self.indicator_types = IndicatorTypes() self.test_mechanisms = TestMechanisms() self.alternative_id = None self.suggested_coas = SuggestedCOAs() self.sightings = Sightings() self.composite_indicator_expression = None self.kill_chain_phases = KillChainPhasesReference() self.related_indicators = RelatedIndicators() self.related_campaigns = RelatedCampaignRefs() self.observable_composition_operator = "OR" self.related_packages = RelatedPackageRefs() @property def observables(self): """A list of ``cybox.core.Observable`` instances. This can be set to a single object instance or a list of objects. Note: If only one Observable is set, this property will return a list with the ``observable`` property. If multiple ``cybox.core.Observable`` this property will return Observables under the ``cybox.core.ObservableComposition``. Access to the top level ``cybox.core.Observable`` is made via ``observable`` property. Default Value: Empty ``list``. Returns: A ``list`` of ``cybox.core.Observable`` instances. """ if not self.observable: return [] elif self.observable.observable_composition: return self.observable.observable_composition.observables # self.observable is defined and not a composition. return [self.observable] @observables.setter def observables(self, value): """ The method will automatically create a top ``cybox.core.Observable`` and append all ``cybox.core.Observable`` using ``observable_composition`` property when a ``list`` is given with length greater than 1. Note: The top level ``cybox.core.Observable`` will set the ``operator`` property for the ``cybox.core.ObservableComposition`` via the ``observable_composition_operator`` property. The value of ``operator`` can be changed via ``observable_composition_operator`` property. By default, the composition layer will be set to ``"OR"``. Args: value: A ``list`` of ``cybox.core.Observable`` instances or a single ``cybox.core.Observable`` instance. Raises: ValueError: If set to a value that cannot be converted to an instance of ``cybox.core.Observable``. """ if not value: return if isinstance(value, Observable): self.observable = value elif utils.is_sequence(value): if len(value) == 1: self.add_observable(value[0]) return observable_comp = ObservableComposition() observable_comp.operator = self.observable_composition_operator for element in value: observable_comp.add(element) self.observable = Observable() self.observable.observable_composition = observable_comp def set_observables(self, value): self.observables = value def add_observable(self, observable): """Adds an observable to the ``observable`` property of the :class:`Indicator`. If the `observable` parameter is ``None``, no item will be added to the ``observable`` property. Note: The STIX Language dictates that an :class:`Indicator` can have only one ``Observable`` under it. Because of this, when a user adds another ``Observable`` a new, empty ``Observable`` will be crated and append the existing and new ``observable`` using the ``ObservableComposition`` property. To access the top level ``Observable`` can be achieved by the ``observable`` property .By default, the ``operator`` of the composition layer will be set to ``"OR"``. The ``operator`` value can be changed via the ``observable_composition_operator`` property. Setting ``observable`` or ``observables`` with re-initialize the property and lose all ``Observable`` in the composition layer. Args: observable: An instance of ``cybox.core.Observable`` or an object type that can be converted into one. Raises: ValueError: If the `observable` param cannot be converted into an instance of ``cybox.core.Observable``. """ if not observable: return # Sets the first observable. elif not self.observable: self.observable = observable # When another is inserted. A "root" Observable is created and the # user's Observables are appended to the composition. elif not self.observable.observable_composition: observable_comp = ObservableComposition() observable_comp.operator = self.observable_composition_operator observable_comp.add(self.observable) observable_comp.add(observable) self.observable = Observable() self.observable.observable_composition = observable_comp # Keep appending to "root" Observable. else: self.observable.observable_composition.add(observable) def add_alternative_id(self, value): """Adds an alternative id to the ``alternative_id`` list property. Note: If ``None`` is passed in no value is added to the ``alternative_id`` list property. Args: value: An identifier value. """ if not value: return self.alternative_id.append(value) def add_valid_time_position(self, value): """Adds an valid time position to the ``valid_time_positions`` property list. If `value` is ``None``, no item is added to the ``value_time_positions`` list. Args: value: An instance of :class:`stix.indicator.valid_time.ValidTime`. Raises: ValueError: If the `value` argument is not an instance of :class:`stix.indicator.valid_time.ValidTime`. """ self.valid_time_positions.append(value) def add_indicator_type(self, value): """Adds a value to the ``indicator_types`` list property. The `value` parameter can be a ``str`` or an instance of :class:`stix.common.vocabs.VocabString`. Note: If the `value` parameter is a ``str`` instance, an attempt will be made to convert it into an instance of :class:`stix.common.vocabs.IndicatorType` Args: value: An instance of :class:`stix.common.vocabs.VocabString` or ``str``. Raises: ValueError: If the `value` param is a ``str`` instance that cannot be converted into an instance of :class:`stix.common.vocabs.IndicatorType`. """ self.indicator_types.append(value) def add_indicated_ttp(self, v): """Adds an Indicated TTP to the ``indicated_ttps`` list property of this :class:`Indicator`. The `v` parameter must be an instance of :class:`stix.common.related.RelatedTTP` or :class:`stix.ttp.TTP`. If the `v` parameter is ``None``, no item wil be added to the ``indicated_ttps`` list property. Note: If the `v` parameter is not an instance of :class:`stix.common.related.RelatedTTP` an attempt will be made to convert it to one. Args: v: An instance of :class:`stix.common.related.RelatedTTP` or :class:`stix.ttp.TTP`. Raises: ValueError: If the `v` parameter cannot be converted into an instance of :class:`stix.common.related.RelatedTTP` """ self.indicated_ttps.append(v) def add_test_mechanism(self, tm): """Adds an Test Mechanism to the ``test_mechanisms`` list property of this :class:`Indicator`. The `tm` parameter must be an instance of a :class:`stix.indicator.test_mechanism._BaseTestMechanism` implementation. If the `tm` parameter is ``None``, no item will be added to the ``test_mechanisms`` list property. See Also: Test Mechanism implementations are found under the :mod:`stix.extensions.test_mechanism` package. Args: tm: An instance of a :class:`stix.indicator.test_mechanism._BaseTestMechanism` implementation. Raises: ValueError: If the `tm` parameter is not an instance of :class:`stix.indicator.test_mechanism._BaseTestMechanism` """ self.test_mechanisms.append(tm) def add_related_indicator(self, indicator): """Adds an Related Indicator to the ``related_indicators`` list property of this :class:`Indicator`. The `indicator` parameter must be an instance of :class:`stix.common.related.RelatedIndicator` or :class:`Indicator`. If the `indicator` parameter is ``None``, no item wil be added to the ``related_indicators`` list property. Calling this method is the same as calling ``append()`` on the ``related_indicators`` proeprty. See Also: The :class:`RelatedIndicators` documentation. Note: If the `tm` parameter is not an instance of :class:`stix.common.related.RelatedIndicator` an attempt will be made to convert it to one. Args: indicator: An instance of :class:`Indicator` or :class:`stix.common.related.RelatedIndicator`. Raises: ValueError: If the `indicator` parameter cannot be converted into an instance of :class:`stix.common.related.RelatedIndicator` """ self.related_indicators.append(indicator) def add_related_campaign(self, value): """Adds a Related Campaign to this Indicator. The `value` parameter must be an instance of :class:`.RelatedCampaignRef` or :class:`.CampaignRef`. If the `value` parameter is ``None``, no item wil be added to the ``related_campaigns`` collection. Calling this method is the same as calling ``append()`` on the ``related_campaigns`` property. See Also: The :class:`.RelatedCampaignRef` documentation. Note: If the `value` parameter is not an instance of :class:`.RelatedCampaignRef` an attempt will be made to convert it to one. Args: value: An instance of :class:`.RelatedCampaignRef` or :class:`.Campaign`. Raises: ValueError: If the `value` parameter cannot be converted into an instance of :class:`.RelatedCampaignRef` """ self.related_campaigns.append(value) @property def observable_composition_operator(self): return self._observable_composition_operator @observable_composition_operator.setter def observable_composition_operator(self, value): if value in self._ALLOWED_COMPOSITION_OPERATORS: self._observable_composition_operator = value if self.observable and self.observable.observable_composition: self.observable.observable_composition.operator = value return error = "observable_composition_operator must one of {0}" error = error.format(self._ALLOWED_COMPOSITION_OPERATORS) raise ValueError(error) def add_kill_chain_phase(self, value): """Add a new Kill Chain Phase reference to this Indicator. Args: value: a :class:`stix.common.kill_chains.KillChainPhase` or a `str` representing the phase_id of. Note that you if you are defining a custom Kill Chain, you need to add it to the STIX package separately. """ self.kill_chain_phases.append(value) def add_related_package(self, value): self.related_packages.append(value) def set_producer_identity(self, identity): """Sets the name of the producer of this indicator. This is the same as calling ``indicator.producer.identity.name = identity``. If the ``producer`` property is ``None``, it will be initialized to an instance of :class:`stix.common.information_source.InformationSource`. If the ``identity`` property of the ``producer`` instance is ``None``, it will be initialized to an instance of :class:`stix.common.identity.Identity`. Note: if the `identity` parameter is not an instance :class:`stix.common.identity.Identity` an attempt will be made to convert it to one. Args: identity: An instance of ``str`` or ``stix.common.identity.Identity``. """ def unset_producer_identity(): try: self.producer.identity.name = None except AttributeError: pass if not identity: unset_producer_identity() return if not self.producer: self.producer = InformationSource() if isinstance(identity, Identity): self.producer.identity = identity return if not self.producer.identity: self.producer.identity = Identity() self.producer.identity.name = str(identity) def set_produced_time(self, produced_time): """Sets the ``produced_time`` property of the ``producer`` property instance fo `produced_time`. This is the same as calling ``indicator.producer.time.produced_time = produced_time``. The `produced_time` parameter must be an instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Note: If `produced_time` is a ``str`` or ``datetime.datetime`` instance an attempt will be made to convert it into an instance of ``cybox.common.DateTimeWithPrecision``. Args: produced_time: An instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. """ if not self.producer: self.producer = InformationSource() if not self.producer.time: self.producer.time = Time() self.producer.time.produced_time = produced_time def get_produced_time(self): """Gets the produced time for this :class:`Indicator`. This is the same as calling ``produced_time = indicator.producer.time.produced_time``. Returns: ``None`` or an instance of ``cybox.common.DateTimeWithPrecision``. """ try: return self.producer.time.produced_time except AttributeError: return None def set_received_time(self, received_time): """Sets the received time for this :class:`Indicator`. This is the same as calling ``indicator.producer.time.produced_time = produced_time``. The `received_time` parameter must be an instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Args: received_time: An instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Note: If `received_time` is a ``str`` or ``datetime.datetime`` instance an attempt will be made to convert it into an instance of ``cybox.common.DateTimeWithPrecision``. """ if not self.producer: self.producer = InformationSource() if not self.producer.time: self.producer.time = Time() self.producer.time.received_time = received_time def get_received_time(self): """Gets the received time for this :class:`Indicator`. This is the same as calling ``received_time = indicator.producer.time.received_time``. Returns: ``None`` or an instance of ``cybox.common.DateTimeWithPrecision``. """ try: return self.producer.time.received_time except AttributeError: return None def _merge_observables(self, observables): observable_composition = ObservableComposition() observable_composition.operator = self.observable_composition_operator for observable in observables: observable_composition.add(observable) root_observable = Observable() root_observable.observable_composition = observable_composition return root_observable def add_object(self, object_): """Adds a python-cybox Object instance to the ``observables`` list property. This is the same as calling ``indicator.add_observable(object_)``. Note: If the `object` param is not an instance of ``cybox.core.Object`` an attempt will be made to to convert it into one before wrapping it in an ``cybox.core.Observable`` layer. Args: object_: An instance of ``cybox.core.Object`` or an object that can be converted into an instance of ``cybox.core.Observable`` Raises: ValueError: if the `object_` param cannot be converted to an instance of ``cybox.core.Observable``. """ if not object_: return observable = Observable(object_) self.add_observable(observable) def _finalize_obj(self, entity_obj): """Omits the `negate` field if it is not equal to True. """ if self.negate: entity_obj.negate = True elif hasattr(entity_obj, 'negate'): entity_obj.negate = None def _finalize_dict(self, entity_dict): """Omits the `negate` field if it is not equal to True. """ if self.negate: entity_dict['negate'] = True elif 'negate' in entity_dict: del entity_dict['negate']
class Indicator(stix.BaseCoreComponent): """Implementation of the STIX Indicator. Args: id_ (optional): An identifier. If ``None``, a value will be generated via ``mixbox.idgen.create_id()``. If set, this will unset the ``idref`` property. idref (optional): An identifier reference. If set this will unset the ``id_`` property. title (optional): A string title. timestamp (optional): A timestamp value. Can be an instance of ``datetime.datetime`` or ``str``. description (optional): A string description. short_description (optional): A string short description. """ _binding = indicator_binding _binding_class = indicator_binding.IndicatorType _namespace = 'http://stix.mitre.org/Indicator-2' _version = "2.2" _ALL_VERSIONS = ("2.0", "2.0.1", "2.1", "2.1.1", "2.2") _ALLOWED_COMPOSITION_OPERATORS = ('AND', 'OR') _ID_PREFIX = "indicator" def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): super(Indicator, self).__init__(id_=id_, idref=idref, timestamp=timestamp, title=title, description=description, short_description=short_description) self.producer = None self.observables = None self.indicator_types = IndicatorTypes() self.confidence = None self.indicated_ttps = _IndicatedTTPs() self.test_mechanisms = TestMechanisms() self.alternative_id = None self.suggested_coas = SuggestedCOAs() self.sightings = Sightings() self.composite_indicator_expression = None self.kill_chain_phases = KillChainPhasesReference() self.valid_time_positions = _ValidTimePositions() self.related_indicators = None self.related_campaigns = RelatedCampaignRefs() self.observable_composition_operator = "OR" self.likely_impact = None self.negate = None self.related_packages = RelatedPackageRefs() @property def producer(self): """Contains information about the source of the :class:`Indicator`. Default Value: ``None`` Returns: An instance of :class:`stix.common.information_source.InformationSource` Raises: ValueError: If set to a value that is not ``None`` and not an instance of :class:`stix.common.information_source.InformationSource` """ return self._producer @producer.setter def producer(self, value): self._set_var(InformationSource, try_cast=False, producer=value) @property def observable(self): """A convenience property for accessing or setting the only ``cybox.core.Observable`` instance held by this Indicator. Default Value: Empty ``list``. Setting this property results in the ``observables`` property being reinitialized to an empty ``list`` and appending the input value, resulting in a ``list`` containing one value. Note: If the ``observables`` list contains more than one item, this property will only return the first item in the list. Returns: An instance of ``cybox.core.Observable``. Raises: ValueError: If set to a value that cannot be converted to an instance of ``cybox.core.Observable``. """ if self.observables: return self.observables[0] else: return None @observable.setter def observable(self, observable): self._observables = _Observables(observable) @property def observables(self): """A list of ``cybox.core.Observable`` instances. This can be set to a single object instance or a list of objects. Note: If the input value or values are not instance(s) of ``cybox.core.Observable``, an attempt will be made to convert the value to an instance of ``cybox.core.Observable``. Default Value: Empty ``list`` Returns: A ``list`` of ``cybox.core.Observable`` instances. Raises: ValueError: If set to a value that cannot be converted to an instance of ``cybox.core.Observable``. """ return self._observables @observables.setter def observables(self, value): self._observables = _Observables(value) def add_observable(self, observable): """Adds an observable to the ``observables`` list property of the :class:`Indicator`. If the `observable` parameter is ``None``, no item will be added to the ``observables`` list. Note: The STIX Language dictates that an :class:`Indicator` can have only one ``Observable`` under it. Because of this, the ``to_xml()`` method will convert the ``observables`` list into an ``cybox.core.ObservableComposition`` instance, in which each item in the ``observables`` list will be added to the composition. By default, the ``operator`` of the composition layer will be set to ``"OR"``. The ``operator`` value can be changed via the ``observable_composition_operator`` property. Args: observable: An instance of ``cybox.core.Observable`` or an object type that can be converted into one. Raises: ValueError: If the `observable` param cannot be converted into an instance of ``cybox.core.Observable``. """ self.observables.append(observable) @property def alternative_id(self): """An alternative identifi er for this :class:`Indicator` This property can be set to a single string identifier or a list of identifiers. If set to a single object, the object will be inserted into an empty list internally. Default Value: Empty ``list`` Returns: A list of alternative ids. """ return self._alternative_id @alternative_id.setter def alternative_id(self, value): self._alternative_id = [] if not value: return elif utils.is_sequence(value): self._alternative_id.extend(x for x in value if x) else: self._alternative_id.append(value) def add_alternative_id(self, value): """Adds an alternative id to the ``alternative_id`` list property. Note: If ``None`` is passed in no value is added to the ``alternative_id`` list property. Args: value: An identifier value. """ if not value: return self.alternative_id.append(value) @property def valid_time_positions(self): """A list of valid time positions for this :class:`Indicator`. This property can be set to a single instance or a list of :class:`stix.indicator.valid_time.ValidTime` instances. If set to a single instance, that object is converted into a list containing one item. Default Value: Empty ``list`` Returns: A list of :class:`stix.indicator.valid_time.ValidTime` instances. """ return self._valid_time_positions @valid_time_positions.setter def valid_time_positions(self, value): self._valid_time_positions = _ValidTimePositions(value) def add_valid_time_position(self, value): """Adds an valid time position to the ``valid_time_positions`` property list. If `value` is ``None``, no item is added to the ``value_time_positions`` list. Args: value: An instance of :class:`stix.indicator.valid_time.ValidTime`. Raises: ValueError: If the `value` argument is not an instance of :class:`stix.indicator.valid_time.ValidTime`. """ self.valid_time_positions.append(value) @property def indicator_types(self): """A list of indicator types for this :class:`Indicator`. This property can be set to lists or single instances of ``str`` or :class:`stix.common.vocabs.VocabString` or an instance of :class:`IndicatorTypes`. Note: If an instance of ``str`` is passed in (or a ``list`` containing ``str`` values) an attempt will be made to convert that string value to an instance of :class:`stix.common.vocabs.IndicatorType`. Default Value: An empty ``IndicatorTypes`` instance. See Also: Documentation for :class:`IndicatorTypes`. Returns: An instance of ``IndicatorTypes``. """ return self._indicator_types @indicator_types.setter def indicator_types(self, value): self._indicator_types = IndicatorTypes(value) def add_indicator_type(self, value): """Adds a value to the ``indicator_types`` list property. The `value` parameter can be a ``str`` or an instance of :class:`stix.common.vocabs.VocabString`. Note: If the `value` parameter is a ``str`` instance, an attempt will be made to convert it into an instance of :class:`stix.common.vocabs.IndicatorType` Args: value: An instance of :class:`stix.common.vocabs.VocabString` or ``str``. Raises: ValueError: If the `value` param is a ``str`` instance that cannot be converted into an instance of :class:`stix.common.vocabs.IndicatorType`. """ self.indicator_types.append(value) @property def confidence(self): """The confidence for this :class:`Indicator`. This property can be set to an instance of ``str``, :class:`stix.common.vocabs.VocabString`, or :class:`stix.common.confidence.Confidence`. Default Value: ``None`` Note: If set to an instance of ``str`` or :class:`stix.common.vocabs.VocabString`, that value will be wrapped in an instance of :class:`stix.common.confidence.Confidence`. Returns: An instance of of :class:`stix.common.confidence.Confidence`. Raises: ValueError: If set to a ``str`` value that cannot be converted into an instance of :class:`stix.common.confidence.Confidence`. """ return self._confidence @confidence.setter def confidence(self, value): self._set_var(Confidence, confidence=value) @property def indicated_ttps(self): return self._indicated_ttps @indicated_ttps.setter def indicated_ttps(self, value): self._indicated_ttps = _IndicatedTTPs(value) def add_indicated_ttp(self, v): """Adds an Indicated TTP to the ``indicated_ttps`` list property of this :class:`Indicator`. The `v` parameter must be an instance of :class:`stix.common.related.RelatedTTP` or :class:`stix.ttp.TTP`. If the `v` parameter is ``None``, no item wil be added to the ``indicated_ttps`` list property. Note: If the `v` parameter is not an instance of :class:`stix.common.related.RelatedTTP` an attempt will be made to convert it to one. Args: v: An instance of :class:`stix.common.related.RelatedTTP` or :class:`stix.ttp.TTP`. Raises: ValueError: If the `v` parameter cannot be converted into an instance of :class:`stix.common.related.RelatedTTP` """ self.indicated_ttps.append(v) @property def test_mechanisms(self): return self._test_mechanisms @test_mechanisms.setter def test_mechanisms(self, value): self._test_mechanisms = TestMechanisms(value) def add_test_mechanism(self, tm): """Adds an Test Mechanism to the ``test_mechanisms`` list property of this :class:`Indicator`. The `tm` parameter must be an instance of a :class:`stix.indicator.test_mechanism._BaseTestMechanism` implementation. If the `tm` parameter is ``None``, no item will be added to the ``test_mechanisms`` list property. See Also: Test Mechanism implementations are found under the :mod:`stix.extensions.test_mechanism` package. Args: tm: An instance of a :class:`stix.indicator.test_mechanism._BaseTestMechanism` implementation. Raises: ValueError: If the `tm` parameter is not an instance of :class:`stix.indicator.test_mechanism._BaseTestMechanism` """ self.test_mechanisms.append(tm) @property def related_indicators(self): return self._related_indicators @related_indicators.setter def related_indicators(self, value): if isinstance(value, RelatedIndicators): self._related_indicators = value else: self._related_indicators = RelatedIndicators(value) def add_related_indicator(self, indicator): """Adds an Related Indicator to the ``related_indicators`` list property of this :class:`Indicator`. The `indicator` parameter must be an instance of :class:`stix.common.related.RelatedIndicator` or :class:`Indicator`. If the `indicator` parameter is ``None``, no item wil be added to the ``related_indicators`` list property. Calling this method is the same as calling ``append()`` on the ``related_indicators`` proeprty. See Also: The :class:`RelatedIndicators` documentation. Note: If the `tm` parameter is not an instance of :class:`stix.common.related.RelatedIndicator` an attempt will be made to convert it to one. Args: indicator: An instance of :class:`Indicator` or :class:`stix.common.related.RelatedIndicator`. Raises: ValueError: If the `indicator` parameter cannot be converted into an instance of :class:`stix.common.related.RelatedIndicator` """ self.related_indicators.append(indicator) @property def related_campaigns(self): return self._related_campaigns @related_campaigns.setter def related_campaigns(self, value): if isinstance(value, RelatedCampaignRefs): self._related_campaigns = value else: self._related_campaigns = RelatedCampaignRefs(value) def add_related_campaign(self, value): """Adds a Related Campaign to this Indicator. The `value` parameter must be an instance of :class:`.RelatedCampaignRef` or :class:`.CampaignRef`. If the `value` parameter is ``None``, no item wil be added to the ``related_campaigns`` collection. Calling this method is the same as calling ``append()`` on the ``related_campaigns`` property. See Also: The :class:`.RelatedCampaignRef` documentation. Note: If the `value` parameter is not an instance of :class:`.RelatedCampaignRef` an attempt will be made to convert it to one. Args: value: An instance of :class:`.RelatedCampaignRef` or :class:`.Campaign`. Raises: ValueError: If the `value` parameter cannot be converted into an instance of :class:`.RelatedCampaignRef` """ self.related_campaigns.append(value) @property def observable_composition_operator(self): return self._observable_composition_operator @observable_composition_operator.setter def observable_composition_operator(self, value): if value in self._ALLOWED_COMPOSITION_OPERATORS: self._observable_composition_operator = value return error = "observable_composition_operator must one of {0}" error = error.format(self._ALLOWED_COMPOSITION_OPERATORS) raise ValueError(error) @property def likely_impact(self): return self._likely_impact @likely_impact.setter def likely_impact(self, value): self._set_var(Statement, likely_impact=value) @property def negate(self): return self._negate @negate.setter def negate(self, value): self._negate = utils.xml_bool(value) @property def kill_chain_phases(self): return self._kill_chain_phases @kill_chain_phases.setter def kill_chain_phases(self, value): self._kill_chain_phases = KillChainPhasesReference(value) def add_kill_chain_phase(self, value): """Add a new Kill Chain Phase reference to this Indicator. Args: value: a :class:`stix.common.kill_chains.KillChainPhase` or a `str` representing the phase_id of. Note that you if you are defining a custom Kill Chain, you need to add it to the STIX package separately. """ self.kill_chain_phases.append(value) @property def related_packages(self): return self._related_packages @related_packages.setter def related_packages(self, value): self._related_packages = RelatedPackageRefs(value) def add_related_package(self, value): self.related_packages.append(value) def set_producer_identity(self, identity): """Sets the name of the producer of this indicator. This is the same as calling ``indicator.producer.identity.name = identity``. If the ``producer`` property is ``None``, it will be initialized to an instance of :class:`stix.common.information_source.InformationSource`. If the ``identity`` property of the ``producer`` instance is ``None``, it will be initialized to an instance of :class:`stix.common.identity.Identity`. Note: if the `identity` parameter is not an instance :class:`stix.common.identity.Identity` an attempt will be made to convert it to one. Args: identity: An instance of ``str`` or ``stix.common.identity.Identity``. """ def unset_producer_identity(): try: self.producer.identity.name = None except AttributeError: pass if not identity: unset_producer_identity() return if not self.producer: self.producer = InformationSource() if isinstance(identity, Identity): self.producer.identity = identity return if not self.producer.identity: self.producer.identity = Identity() self.producer.identity.name = str(identity) def set_produced_time(self, produced_time): """Sets the ``produced_time`` property of the ``producer`` property instance fo `produced_time`. This is the same as calling ``indicator.producer.time.produced_time = produced_time``. The `produced_time` parameter must be an instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Note: If `produced_time` is a ``str`` or ``datetime.datetime`` instance an attempt will be made to convert it into an instance of ``cybox.common.DateTimeWithPrecision``. Args: produced_time: An instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. """ if not self.producer: self.producer = InformationSource() if not self.producer.time: self.producer.time = Time() self.producer.time.produced_time = produced_time def get_produced_time(self): """Gets the produced time for this :class:`Indicator`. This is the same as calling ``produced_time = indicator.producer.time.produced_time``. Returns: ``None`` or an instance of ``cybox.common.DateTimeWithPrecision``. """ try: return self.producer.time.produced_time except AttributeError: return None def set_received_time(self, received_time): """Sets the received time for this :class:`Indicator`. This is the same as calling ``indicator.producer.time.produced_time = produced_time``. The `received_time` parameter must be an instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Args: received_time: An instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Note: If `received_time` is a ``str`` or ``datetime.datetime`` instance an attempt will be made to convert it into an instance of ``cybox.common.DateTimeWithPrecision``. """ if not self.producer: self.producer = InformationSource() if not self.producer.time: self.producer.time = Time() self.producer.time.received_time = received_time def get_received_time(self): """Gets the received time for this :class:`Indicator`. This is the same as calling ``received_time = indicator.producer.time.received_time``. Returns: ``None`` or an instance of ``cybox.common.DateTimeWithPrecision``. """ try: return self.producer.time.received_time except AttributeError: return None def _merge_observables(self, observables): observable_composition = ObservableComposition() observable_composition.operator = self.observable_composition_operator for observable in observables: observable_composition.add(observable) root_observable = Observable() root_observable.observable_composition = observable_composition return root_observable def add_object(self, object_): """Adds a python-cybox Object instance to the ``observables`` list property. This is the same as calling ``indicator.add_observable(object_)``. Note: If the `object` param is not an instance of ``cybox.core.Object`` an attempt will be made to to convert it into one before wrapping it in an ``cybox.core.Observable`` layer. Args: object_: An instance of ``cybox.core.Object`` or an object that can be converted into an instance of ``cybox.core.Observable`` Raises: ValueError: if the `object_` param cannot be converted to an instance of ``cybox.core.Observable``. """ if not object_: return observable = Observable(object_) self.add_observable(observable) def to_obj(self, return_obj=None, ns_info=None): if not return_obj: return_obj = self._binding_class() super(Indicator, self).to_obj(return_obj=return_obj, ns_info=ns_info) return_obj.negate = True if self.negate else None if self.confidence: return_obj.Confidence = self.confidence.to_obj(ns_info=ns_info) if self.indicator_types: return_obj.Type = self.indicator_types.to_obj(ns_info=ns_info) if self.indicated_ttps: return_obj.Indicated_TTP = self.indicated_ttps.to_obj( ns_info=ns_info) if self.producer: return_obj.Producer = self.producer.to_obj(ns_info=ns_info) if self.test_mechanisms: return_obj.Test_Mechanisms = self.test_mechanisms.to_obj( ns_info=ns_info) if self.likely_impact: return_obj.Likely_Impact = self.likely_impact.to_obj( ns_info=ns_info) if self.alternative_id: return_obj.Alternative_ID = self.alternative_id if self.valid_time_positions: return_obj.Valid_Time_Position = self.valid_time_positions.to_obj( ns_info=ns_info) if self.suggested_coas: return_obj.Suggested_COAs = self.suggested_coas.to_obj( ns_info=ns_info) if self.sightings: return_obj.Sightings = self.sightings.to_obj(ns_info=ns_info) if self.composite_indicator_expression: return_obj.Composite_Indicator_Expression = self.composite_indicator_expression.to_obj( ns_info=ns_info) if self.kill_chain_phases: return_obj.Kill_Chain_Phases = self.kill_chain_phases.to_obj( ns_info=ns_info) if self.related_indicators: return_obj.Related_Indicators = self.related_indicators.to_obj( ns_info=ns_info) if self.related_campaigns: return_obj.Related_Campaigns = self.related_campaigns.to_obj( ns_info=ns_info) if self.related_packages: return_obj.Related_Packages = self.related_packages.to_obj( ns_info=ns_info) if self.observables: if len(self.observables) > 1: root_observable = self._merge_observables(self.observables) else: root_observable = self.observables[0] return_obj.Observable = root_observable.to_obj(ns_info=ns_info) return return_obj @classmethod def from_obj(cls, obj, return_obj=None): if not obj: return None if not return_obj: return_obj = cls() super(Indicator, cls).from_obj(obj, return_obj=return_obj) if isinstance(obj, cls._binding_class): return_obj.negate = obj.negate return_obj.producer = InformationSource.from_obj(obj.Producer) return_obj.confidence = Confidence.from_obj(obj.Confidence) return_obj.sightings = Sightings.from_obj(obj.Sightings) return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_obj( obj.Composite_Indicator_Expression) return_obj.kill_chain_phases = KillChainPhasesReference.from_obj( obj.Kill_Chain_Phases) return_obj.related_indicators = RelatedIndicators.from_obj( obj.Related_Indicators) return_obj.likely_impact = Statement.from_obj(obj.Likely_Impact) return_obj.indicator_types = IndicatorTypes.from_obj(obj.Type) return_obj.test_mechanisms = TestMechanisms.from_obj( obj.Test_Mechanisms) return_obj.suggested_coas = SuggestedCOAs.from_obj( obj.Suggested_COAs) return_obj.alternative_id = obj.Alternative_ID return_obj.indicated_ttps = _IndicatedTTPs.from_obj( obj.Indicated_TTP) return_obj.valid_time_positions = _ValidTimePositions.from_obj( obj.Valid_Time_Position) return_obj.observable = Observable.from_obj(obj.Observable) return_obj.related_campaigns = RelatedCampaignRefs.from_obj( obj.Related_Campaigns) return_obj.related_packages = RelatedPackageRefs.from_obj( obj.Related_Packages) return return_obj def to_dict(self): keys = ('observables', 'observable_composition_operator', 'negate') d = utils.to_dict(self, skip=keys) if self.negate: d['negate'] = True if self.observables: if len(self.observables) == 1: d['observable'] = self.observables[0].to_dict() else: composite_observable = self._merge_observables( self.observables) d['observable'] = composite_observable.to_dict() return d @classmethod def from_dict(cls, dict_repr, return_obj=None): if not dict_repr: return None if not return_obj: return_obj = cls() super(Indicator, cls).from_dict(dict_repr, return_obj=return_obj) get = dict_repr.get return_obj.negate = get('negate') return_obj.alternative_id = get('alternative_id') return_obj.indicated_ttps = _IndicatedTTPs.from_dict( get('indicated_ttps')) return_obj.test_mechanisms = TestMechanisms.from_list( get('test_mechanisms')) return_obj.suggested_coas = SuggestedCOAs.from_dict( get('suggested_coas')) return_obj.sightings = Sightings.from_dict(get('sightings')) return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_dict( get('composite_indicator_expression')) return_obj.kill_chain_phases = KillChainPhasesReference.from_dict( get('kill_chain_phases')) return_obj.related_indicators = RelatedIndicators.from_dict( get('related_indicators')) return_obj.likely_impact = Statement.from_dict(get('likely_impact')) return_obj.indicator_types = IndicatorTypes.from_list( get('indicator_types')) return_obj.confidence = Confidence.from_dict(get('confidence')) return_obj.valid_time_positions = _ValidTimePositions.from_dict( get('valid_time_positions')) return_obj.observable = Observable.from_dict(get('observable')) return_obj.producer = InformationSource.from_dict(get('producer')) return_obj.related_campaigns = RelatedCampaignRefs.from_dict( get('related_campaigns')) return_obj.related_packages = RelatedPackageRefs.from_dict( get('related_packages')) return return_obj
ttp.add_intended_effect(IntendedEffect('Account Takeover')) # TTP - Attack Pattern attack_pattern = AttackPattern() attack_pattern.capec_id = 'CAPEC-98' attack_pattern.description = 'Phishing' attack_pattern.short_description = 'Phishing' ttp.behavior = Behavior() ttp.behavior.add_attack_pattern(attack_pattern) # TTP - Kill Chain Phase phase = KillChainPhase( name='Infect Machine', phase_id='example:TTP-7a0fb8e4-a778-4c79-9c7e-8747675da5f1') kc_phases = KillChainPhasesReference() kc_phases.append(KillChainPhaseReference(name=phase.name)) ttp.kill_chain_phases = kc_phases # TTP - Resource (Tool, Infrastructure, Personas) resource = Resource() tool = ToolInformation(title='malware.exe') tool.type_ = AttackerToolType('Malware') tool.description = 'Tool Description' tool.short_description = 'Tool Short Description' infrastructure = Infrastructure(title='Leveraged Domains') infrastructure.types = AttackerInfrastructureType('Domain Registration') infrastructure.description = 'Infrastructure Description' infrastructure.short_description = 'Infrastructure Short Description' domain = DomainName() domain.value = 'totally-not-malware.biz'
class Indicator(stix.BaseCoreComponent): """Implementation of the STIX Indicator. Args: id_ (optional): An identifier. If ``None``, a value will be generated via ``mixbox.idgen.create_id()``. If set, this will unset the ``idref`` property. idref (optional): An identifier reference. If set this will unset the ``id_`` property. title (optional): A string title. timestamp (optional): A timestamp value. Can be an instance of ``datetime.datetime`` or ``str``. description (optional): A string description. short_description (optional): A string short description. """ _binding = indicator_binding _binding_class = indicator_binding.IndicatorType _namespace = 'http://stix.mitre.org/Indicator-2' _version = "2.2" _ALL_VERSIONS = ("2.0", "2.0.1", "2.1", "2.1.1", "2.2") _ALLOWED_COMPOSITION_OPERATORS = ('AND', 'OR') _ID_PREFIX = "indicator" def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): super(Indicator, self).__init__( id_=id_, idref=idref, timestamp=timestamp, title=title, description=description, short_description=short_description ) self.producer = None self.observables = None self.indicator_types = IndicatorTypes() self.confidence = None self.indicated_ttps = _IndicatedTTPs() self.test_mechanisms = TestMechanisms() self.alternative_id = None self.suggested_coas = SuggestedCOAs() self.sightings = Sightings() self.composite_indicator_expression = None self.kill_chain_phases = KillChainPhasesReference() self.valid_time_positions = _ValidTimePositions() self.related_indicators = None self.related_campaigns = RelatedCampaignRefs() self.observable_composition_operator = "OR" self.likely_impact = None self.negate = None self.related_packages = RelatedPackageRefs() @property def producer(self): """Contains information about the source of the :class:`Indicator`. Default Value: ``None`` Returns: An instance of :class:`stix.common.information_source.InformationSource` Raises: ValueError: If set to a value that is not ``None`` and not an instance of :class:`stix.common.information_source.InformationSource` """ return self._producer @producer.setter def producer(self, value): self._set_var(InformationSource, try_cast=False, producer=value) @property def observable(self): """A convenience property for accessing or setting the only ``cybox.core.Observable`` instance held by this Indicator. Default Value: Empty ``list``. Setting this property results in the ``observables`` property being reinitialized to an empty ``list`` and appending the input value, resulting in a ``list`` containing one value. Note: If the ``observables`` list contains more than one item, this property will only return the first item in the list. Returns: An instance of ``cybox.core.Observable``. Raises: ValueError: If set to a value that cannot be converted to an instance of ``cybox.core.Observable``. """ if self.observables: return self.observables[0] else: return None @observable.setter def observable(self, observable): self._observables = _Observables(observable) @property def observables(self): """A list of ``cybox.core.Observable`` instances. This can be set to a single object instance or a list of objects. Note: If the input value or values are not instance(s) of ``cybox.core.Observable``, an attempt will be made to convert the value to an instance of ``cybox.core.Observable``. Default Value: Empty ``list`` Returns: A ``list`` of ``cybox.core.Observable`` instances. Raises: ValueError: If set to a value that cannot be converted to an instance of ``cybox.core.Observable``. """ return self._observables @observables.setter def observables(self, value): self._observables = _Observables(value) def add_observable(self, observable): """Adds an observable to the ``observables`` list property of the :class:`Indicator`. If the `observable` parameter is ``None``, no item will be added to the ``observables`` list. Note: The STIX Language dictates that an :class:`Indicator` can have only one ``Observable`` under it. Because of this, the ``to_xml()`` method will convert the ``observables`` list into an ``cybox.core.ObservableComposition`` instance, in which each item in the ``observables`` list will be added to the composition. By default, the ``operator`` of the composition layer will be set to ``"OR"``. The ``operator`` value can be changed via the ``observable_composition_operator`` property. Args: observable: An instance of ``cybox.core.Observable`` or an object type that can be converted into one. Raises: ValueError: If the `observable` param cannot be converted into an instance of ``cybox.core.Observable``. """ self.observables.append(observable) @property def alternative_id(self): """An alternative identifi er for this :class:`Indicator` This property can be set to a single string identifier or a list of identifiers. If set to a single object, the object will be inserted into an empty list internally. Default Value: Empty ``list`` Returns: A list of alternative ids. """ return self._alternative_id @alternative_id.setter def alternative_id(self, value): self._alternative_id = [] if not value: return elif utils.is_sequence(value): self._alternative_id.extend(x for x in value if x) else: self._alternative_id.append(value) def add_alternative_id(self, value): """Adds an alternative id to the ``alternative_id`` list property. Note: If ``None`` is passed in no value is added to the ``alternative_id`` list property. Args: value: An identifier value. """ if not value: return self.alternative_id.append(value) @property def valid_time_positions(self): """A list of valid time positions for this :class:`Indicator`. This property can be set to a single instance or a list of :class:`stix.indicator.valid_time.ValidTime` instances. If set to a single instance, that object is converted into a list containing one item. Default Value: Empty ``list`` Returns: A list of :class:`stix.indicator.valid_time.ValidTime` instances. """ return self._valid_time_positions @valid_time_positions.setter def valid_time_positions(self, value): self._valid_time_positions = _ValidTimePositions(value) def add_valid_time_position(self, value): """Adds an valid time position to the ``valid_time_positions`` property list. If `value` is ``None``, no item is added to the ``value_time_positions`` list. Args: value: An instance of :class:`stix.indicator.valid_time.ValidTime`. Raises: ValueError: If the `value` argument is not an instance of :class:`stix.indicator.valid_time.ValidTime`. """ self.valid_time_positions.append(value) @property def indicator_types(self): """A list of indicator types for this :class:`Indicator`. This property can be set to lists or single instances of ``str`` or :class:`stix.common.vocabs.VocabString` or an instance of :class:`IndicatorTypes`. Note: If an instance of ``str`` is passed in (or a ``list`` containing ``str`` values) an attempt will be made to convert that string value to an instance of :class:`stix.common.vocabs.IndicatorType`. Default Value: An empty ``IndicatorTypes`` instance. See Also: Documentation for :class:`IndicatorTypes`. Returns: An instance of ``IndicatorTypes``. """ return self._indicator_types @indicator_types.setter def indicator_types(self, value): self._indicator_types = IndicatorTypes(value) def add_indicator_type(self, value): """Adds a value to the ``indicator_types`` list property. The `value` parameter can be a ``str`` or an instance of :class:`stix.common.vocabs.VocabString`. Note: If the `value` parameter is a ``str`` instance, an attempt will be made to convert it into an instance of :class:`stix.common.vocabs.IndicatorType` Args: value: An instance of :class:`stix.common.vocabs.VocabString` or ``str``. Raises: ValueError: If the `value` param is a ``str`` instance that cannot be converted into an instance of :class:`stix.common.vocabs.IndicatorType`. """ self.indicator_types.append(value) @property def confidence(self): """The confidence for this :class:`Indicator`. This property can be set to an instance of ``str``, :class:`stix.common.vocabs.VocabString`, or :class:`stix.common.confidence.Confidence`. Default Value: ``None`` Note: If set to an instance of ``str`` or :class:`stix.common.vocabs.VocabString`, that value will be wrapped in an instance of :class:`stix.common.confidence.Confidence`. Returns: An instance of of :class:`stix.common.confidence.Confidence`. Raises: ValueError: If set to a ``str`` value that cannot be converted into an instance of :class:`stix.common.confidence.Confidence`. """ return self._confidence @confidence.setter def confidence(self, value): self._set_var(Confidence, confidence=value) @property def indicated_ttps(self): return self._indicated_ttps @indicated_ttps.setter def indicated_ttps(self, value): self._indicated_ttps = _IndicatedTTPs(value) def add_indicated_ttp(self, v): """Adds an Indicated TTP to the ``indicated_ttps`` list property of this :class:`Indicator`. The `v` parameter must be an instance of :class:`stix.common.related.RelatedTTP` or :class:`stix.ttp.TTP`. If the `v` parameter is ``None``, no item wil be added to the ``indicated_ttps`` list property. Note: If the `v` parameter is not an instance of :class:`stix.common.related.RelatedTTP` an attempt will be made to convert it to one. Args: v: An instance of :class:`stix.common.related.RelatedTTP` or :class:`stix.ttp.TTP`. Raises: ValueError: If the `v` parameter cannot be converted into an instance of :class:`stix.common.related.RelatedTTP` """ self.indicated_ttps.append(v) @property def test_mechanisms(self): return self._test_mechanisms @test_mechanisms.setter def test_mechanisms(self, value): self._test_mechanisms = TestMechanisms(value) def add_test_mechanism(self, tm): """Adds an Test Mechanism to the ``test_mechanisms`` list property of this :class:`Indicator`. The `tm` parameter must be an instance of a :class:`stix.indicator.test_mechanism._BaseTestMechanism` implementation. If the `tm` parameter is ``None``, no item will be added to the ``test_mechanisms`` list property. See Also: Test Mechanism implementations are found under the :mod:`stix.extensions.test_mechanism` package. Args: tm: An instance of a :class:`stix.indicator.test_mechanism._BaseTestMechanism` implementation. Raises: ValueError: If the `tm` parameter is not an instance of :class:`stix.indicator.test_mechanism._BaseTestMechanism` """ self.test_mechanisms.append(tm) @property def related_indicators(self): return self._related_indicators @related_indicators.setter def related_indicators(self, value): if isinstance(value, RelatedIndicators): self._related_indicators = value else: self._related_indicators = RelatedIndicators(value) def add_related_indicator(self, indicator): """Adds an Related Indicator to the ``related_indicators`` list property of this :class:`Indicator`. The `indicator` parameter must be an instance of :class:`stix.common.related.RelatedIndicator` or :class:`Indicator`. If the `indicator` parameter is ``None``, no item wil be added to the ``related_indicators`` list property. Calling this method is the same as calling ``append()`` on the ``related_indicators`` proeprty. See Also: The :class:`RelatedIndicators` documentation. Note: If the `tm` parameter is not an instance of :class:`stix.common.related.RelatedIndicator` an attempt will be made to convert it to one. Args: indicator: An instance of :class:`Indicator` or :class:`stix.common.related.RelatedIndicator`. Raises: ValueError: If the `indicator` parameter cannot be converted into an instance of :class:`stix.common.related.RelatedIndicator` """ self.related_indicators.append(indicator) @property def related_campaigns(self): return self._related_campaigns @related_campaigns.setter def related_campaigns(self, value): if isinstance(value, RelatedCampaignRefs): self._related_campaigns = value else: self._related_campaigns = RelatedCampaignRefs(value) def add_related_campaign(self, value): """Adds a Related Campaign to this Indicator. The `value` parameter must be an instance of :class:`.RelatedCampaignRef` or :class:`.CampaignRef`. If the `value` parameter is ``None``, no item wil be added to the ``related_campaigns`` collection. Calling this method is the same as calling ``append()`` on the ``related_campaigns`` property. See Also: The :class:`.RelatedCampaignRef` documentation. Note: If the `value` parameter is not an instance of :class:`.RelatedCampaignRef` an attempt will be made to convert it to one. Args: value: An instance of :class:`.RelatedCampaignRef` or :class:`.Campaign`. Raises: ValueError: If the `value` parameter cannot be converted into an instance of :class:`.RelatedCampaignRef` """ self.related_campaigns.append(value) @property def observable_composition_operator(self): return self._observable_composition_operator @observable_composition_operator.setter def observable_composition_operator(self, value): if value in self._ALLOWED_COMPOSITION_OPERATORS: self._observable_composition_operator = value return error = "observable_composition_operator must one of {0}" error = error.format(self._ALLOWED_COMPOSITION_OPERATORS) raise ValueError(error) @property def likely_impact(self): return self._likely_impact @likely_impact.setter def likely_impact(self, value): self._set_var(Statement, likely_impact=value) @property def negate(self): return self._negate @negate.setter def negate(self, value): self._negate = utils.xml_bool(value) @property def kill_chain_phases(self): return self._kill_chain_phases @kill_chain_phases.setter def kill_chain_phases(self, value): self._kill_chain_phases = KillChainPhasesReference(value) def add_kill_chain_phase(self, value): """Add a new Kill Chain Phase reference to this Indicator. Args: value: a :class:`stix.common.kill_chains.KillChainPhase` or a `str` representing the phase_id of. Note that you if you are defining a custom Kill Chain, you need to add it to the STIX package separately. """ self.kill_chain_phases.append(value) @property def related_packages(self): return self._related_packages @related_packages.setter def related_packages(self, value): self._related_packages = RelatedPackageRefs(value) def add_related_package(self, value): self.related_packages.append(value) def set_producer_identity(self, identity): """Sets the name of the producer of this indicator. This is the same as calling ``indicator.producer.identity.name = identity``. If the ``producer`` property is ``None``, it will be initialized to an instance of :class:`stix.common.information_source.InformationSource`. If the ``identity`` property of the ``producer`` instance is ``None``, it will be initialized to an instance of :class:`stix.common.identity.Identity`. Note: if the `identity` parameter is not an instance :class:`stix.common.identity.Identity` an attempt will be made to convert it to one. Args: identity: An instance of ``str`` or ``stix.common.identity.Identity``. """ def unset_producer_identity(): try: self.producer.identity.name = None except AttributeError: pass if not identity: unset_producer_identity() return if not self.producer: self.producer = InformationSource() if isinstance(identity, Identity): self.producer.identity = identity return if not self.producer.identity: self.producer.identity = Identity() self.producer.identity.name = str(identity) def set_produced_time(self, produced_time): """Sets the ``produced_time`` property of the ``producer`` property instance fo `produced_time`. This is the same as calling ``indicator.producer.time.produced_time = produced_time``. The `produced_time` parameter must be an instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Note: If `produced_time` is a ``str`` or ``datetime.datetime`` instance an attempt will be made to convert it into an instance of ``cybox.common.DateTimeWithPrecision``. Args: produced_time: An instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. """ if not self.producer: self.producer = InformationSource() if not self.producer.time: self.producer.time = Time() self.producer.time.produced_time = produced_time def get_produced_time(self): """Gets the produced time for this :class:`Indicator`. This is the same as calling ``produced_time = indicator.producer.time.produced_time``. Returns: ``None`` or an instance of ``cybox.common.DateTimeWithPrecision``. """ try: return self.producer.time.produced_time except AttributeError: return None def set_received_time(self, received_time): """Sets the received time for this :class:`Indicator`. This is the same as calling ``indicator.producer.time.produced_time = produced_time``. The `received_time` parameter must be an instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Args: received_time: An instance of ``str``, ``datetime.datetime``, or ``cybox.common.DateTimeWithPrecision``. Note: If `received_time` is a ``str`` or ``datetime.datetime`` instance an attempt will be made to convert it into an instance of ``cybox.common.DateTimeWithPrecision``. """ if not self.producer: self.producer = InformationSource() if not self.producer.time: self.producer.time = Time() self.producer.time.received_time = received_time def get_received_time(self): """Gets the received time for this :class:`Indicator`. This is the same as calling ``received_time = indicator.producer.time.received_time``. Returns: ``None`` or an instance of ``cybox.common.DateTimeWithPrecision``. """ try: return self.producer.time.received_time except AttributeError: return None def _merge_observables(self, observables): observable_composition = ObservableComposition() observable_composition.operator = self.observable_composition_operator for observable in observables: observable_composition.add(observable) root_observable = Observable() root_observable.observable_composition = observable_composition return root_observable def add_object(self, object_): """Adds a python-cybox Object instance to the ``observables`` list property. This is the same as calling ``indicator.add_observable(object_)``. Note: If the `object` param is not an instance of ``cybox.core.Object`` an attempt will be made to to convert it into one before wrapping it in an ``cybox.core.Observable`` layer. Args: object_: An instance of ``cybox.core.Object`` or an object that can be converted into an instance of ``cybox.core.Observable`` Raises: ValueError: if the `object_` param cannot be converted to an instance of ``cybox.core.Observable``. """ if not object_: return observable = Observable(object_) self.add_observable(observable) def to_obj(self, return_obj=None, ns_info=None): if not return_obj: return_obj = self._binding_class() super(Indicator, self).to_obj(return_obj=return_obj, ns_info=ns_info) return_obj.negate = True if self.negate else None if self.confidence: return_obj.Confidence = self.confidence.to_obj(ns_info=ns_info) if self.indicator_types: return_obj.Type = self.indicator_types.to_obj(ns_info=ns_info) if self.indicated_ttps: return_obj.Indicated_TTP = self.indicated_ttps.to_obj(ns_info=ns_info) if self.producer: return_obj.Producer = self.producer.to_obj(ns_info=ns_info) if self.test_mechanisms: return_obj.Test_Mechanisms = self.test_mechanisms.to_obj(ns_info=ns_info) if self.likely_impact: return_obj.Likely_Impact = self.likely_impact.to_obj(ns_info=ns_info) if self.alternative_id: return_obj.Alternative_ID = self.alternative_id if self.valid_time_positions: return_obj.Valid_Time_Position = self.valid_time_positions.to_obj(ns_info=ns_info) if self.suggested_coas: return_obj.Suggested_COAs = self.suggested_coas.to_obj(ns_info=ns_info) if self.sightings: return_obj.Sightings = self.sightings.to_obj(ns_info=ns_info) if self.composite_indicator_expression: return_obj.Composite_Indicator_Expression = self.composite_indicator_expression.to_obj(ns_info=ns_info) if self.kill_chain_phases: return_obj.Kill_Chain_Phases = self.kill_chain_phases.to_obj(ns_info=ns_info) if self.related_indicators: return_obj.Related_Indicators = self.related_indicators.to_obj(ns_info=ns_info) if self.related_campaigns: return_obj.Related_Campaigns = self.related_campaigns.to_obj(ns_info=ns_info) if self.related_packages: return_obj.Related_Packages = self.related_packages.to_obj(ns_info=ns_info) if self.observables: if len(self.observables) > 1: root_observable = self._merge_observables(self.observables) else: root_observable = self.observables[0] return_obj.Observable = root_observable.to_obj(ns_info=ns_info) return return_obj @classmethod def from_obj(cls, obj, return_obj=None): if not obj: return None if not return_obj: return_obj = cls() super(Indicator, cls).from_obj(obj, return_obj=return_obj) if isinstance(obj, cls._binding_class): return_obj.negate = obj.negate return_obj.producer = InformationSource.from_obj(obj.Producer) return_obj.confidence = Confidence.from_obj(obj.Confidence) return_obj.sightings = Sightings.from_obj(obj.Sightings) return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_obj(obj.Composite_Indicator_Expression) return_obj.kill_chain_phases = KillChainPhasesReference.from_obj(obj.Kill_Chain_Phases) return_obj.related_indicators = RelatedIndicators.from_obj(obj.Related_Indicators) return_obj.likely_impact = Statement.from_obj(obj.Likely_Impact) return_obj.indicator_types = IndicatorTypes.from_obj(obj.Type) return_obj.test_mechanisms = TestMechanisms.from_obj(obj.Test_Mechanisms) return_obj.suggested_coas = SuggestedCOAs.from_obj(obj.Suggested_COAs) return_obj.alternative_id = obj.Alternative_ID return_obj.indicated_ttps = _IndicatedTTPs.from_obj(obj.Indicated_TTP) return_obj.valid_time_positions = _ValidTimePositions.from_obj(obj.Valid_Time_Position) return_obj.observable = Observable.from_obj(obj.Observable) return_obj.related_campaigns = RelatedCampaignRefs.from_obj(obj.Related_Campaigns) return_obj.related_packages = RelatedPackageRefs.from_obj(obj.Related_Packages) return return_obj def to_dict(self): keys = ('observables', 'observable_composition_operator', 'negate') d = utils.to_dict(self, skip=keys) if self.negate: d['negate'] = True if self.observables: if len(self.observables) == 1: d['observable'] = self.observables[0].to_dict() else: composite_observable = self._merge_observables(self.observables) d['observable'] = composite_observable.to_dict() return d @classmethod def from_dict(cls, dict_repr, return_obj=None): if not dict_repr: return None if not return_obj: return_obj = cls() super(Indicator, cls).from_dict(dict_repr, return_obj=return_obj) get = dict_repr.get return_obj.negate = get('negate') return_obj.alternative_id = get('alternative_id') return_obj.indicated_ttps = _IndicatedTTPs.from_dict(get('indicated_ttps')) return_obj.test_mechanisms = TestMechanisms.from_list(get('test_mechanisms')) return_obj.suggested_coas = SuggestedCOAs.from_dict(get('suggested_coas')) return_obj.sightings = Sightings.from_dict(get('sightings')) return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_dict(get('composite_indicator_expression')) return_obj.kill_chain_phases = KillChainPhasesReference.from_dict(get('kill_chain_phases')) return_obj.related_indicators = RelatedIndicators.from_dict(get('related_indicators')) return_obj.likely_impact = Statement.from_dict(get('likely_impact')) return_obj.indicator_types = IndicatorTypes.from_list(get('indicator_types')) return_obj.confidence = Confidence.from_dict(get('confidence')) return_obj.valid_time_positions = _ValidTimePositions.from_dict(get('valid_time_positions')) return_obj.observable = Observable.from_dict(get('observable')) return_obj.producer = InformationSource.from_dict(get('producer')) return_obj.related_campaigns = RelatedCampaignRefs.from_dict(get('related_campaigns')) return_obj.related_packages = RelatedPackageRefs.from_dict(get('related_packages')) return return_obj