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)
Example #2
0
    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()
Example #3
0
    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.handling = 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()
    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)
Example #5
0
 def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None):
     self.id_ = id_ or stix.utils.create_id("indicator")
     self.idref = idref
     self.version = self._version
     self.producer = None
     self.observables = None
     self.title = title
     self.description = description
     self.short_description = short_description
     self.indicator_types = None
     self.confidence = None
     self.indicated_ttps = None
     self.test_mechanisms = None
     self.alternative_id = None
     self.suggested_coas = SuggestedCOAs()
     self.sightings = Sightings()
     self.composite_indicator_expression = None
     self.handling = None
     self.kill_chain_phases = KillChainPhasesReference()
     self.valid_time_positions = None
     self.related_indicators = None
     self.observable_composition_operator = "AND"
     self.likely_impact = None
 
     if timestamp:
         self.timestamp = timestamp
     else:
         self.timestamp = datetime.now(tzutc()) if not idref else None
Example #6
0
 def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None):
     self.id_ = id_ or stix.utils.create_id("indicator")
     self.idref = idref
     self.version = self._version
     self.producer = None
     self.observables = None
     self.title = title
     self.description = description
     self.short_description = short_description
     self.indicator_types = None
     self.confidence = None
     self.indicated_ttps = None
     self.test_mechanisms = None
     self.alternative_id = None
     self.suggested_coas = SuggestedCOAs()
     self.sightings = Sightings()
     self.composite_indicator_expression = None
     self.handling = None
     self.kill_chain_phases = KillChainPhasesReference()
     self.valid_time_positions = None
     self.related_indicators = None
     self.observable_composition_operator = "AND"
 
     if timestamp:
         self.timestamp = timestamp
     else:
         self.timestamp = datetime.now(tzutc()) if not idref else None
Example #7
0
    def generate_indicators(self, count):
        '''Generate a list of STIX Indicators'''
        indicators = []
        for i in range(0, count):
            indicator = Indicator(title='Multiple indicator types')
            indicator.set_producer_identity(Identity(name='Secret Source'))
            indicator.set_produced_time(datetime.today())
            indicator.add_indicator_type(choice(['Malware Artifacts', 'C2', 'Exfiltration']))
            indicator.add_short_description('Short description...')
            indicator.add_description('Long description...')
            indicator.confidence = Confidence(choice(['High', 'Medium', 'Low', 'None', 'Unknown']))
            kill_chain_phase = choice(LMCO_KILL_CHAIN_PHASES)
            indicator.kill_chain_phases = KillChainPhasesReference(
                [KillChainPhaseReference(name=kill_chain_phase.name)])
            ips = self.gen_ips(randint(0, 5))
            for ip in ips:
                indicator.add_observable(ip)

            # user_agents = self.gen_user_agents(randint(0, 5))
            # for ua in user_agents:
            #     indicator.add_observable(ua)

            # fqnds = self.gen_fqdns(randint(0, 5))
            # for f in fqnds:
            #     indicator.add_observable(f)

            # urls = self.gen_urls(randint(0, 5))
            # for u in urls:
            #     indicator.add_observable(u)

            indicators.append(indicator)

        return indicators
Example #8
0
    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
Example #9
0
    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
Example #10
0
    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()
Example #11
0
def add_kill_chain_phase_reference_data_from_frame(api_object, frame):
    indicator = api_object.obj
    indicator_builder_data = frame[0].f_locals['indicator_data']
    kill_chain_phase_id = indicator_builder_data.get('kill_chain_phase')
    if kill_chain_phase_id:
        kill_chain_phase = KillChainPhaseReference(
            kill_chain_id=KILL_CHAIN_ID, phase_id=kill_chain_phase_id)
        indicator._object.kill_chain_phases = KillChainPhasesReference(
            [kill_chain_phase])
    return api_object
Example #12
0
    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()
Example #13
0
    def from_dict(cls, dict_repr, return_obj=None):
        if not dict_repr:
            return None
        if not return_obj:
            return_obj = cls()

        return_obj.id_       = dict_repr.get('id')
        return_obj.idref     = dict_repr.get('idref')
        return_obj.timestamp = dict_repr.get('timestamp')
        return_obj.title     = dict_repr.get('title')
        return_obj.version   = dict_repr.get('version', cls._version)
        observable_dict      = dict_repr.get('observable')
        producer_dict        = dict_repr.get('producer')
        description_dict     = dict_repr.get('description')
        indicator_type_list  = dict_repr.get('indicator_types', [])
        confidence_dict      = dict_repr.get('confidence')
        alternative_id_dict  = dict_repr.get('alternative_id')
        valid_time_position_dict  = dict_repr.get('valid_time_positions')

        return_obj.short_description = StructuredText.from_dict(dict_repr.get('short_description'))
        return_obj.indicated_ttps = [RelatedTTP.from_dict(x) for x in dict_repr.get('indicated_ttps', [])]
        return_obj.test_mechanisms = [_BaseTestMechanism.from_dict(x) for x in dict_repr.get('test_mechanisms', [])]
        return_obj.suggested_coas = SuggestedCOAs.from_dict(dict_repr.get('suggested_coas'))
        return_obj.sightings = Sightings.from_dict(dict_repr.get('sightings'))
        return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_dict(dict_repr.get('composite_indicator_expression'))
        return_obj.handling = Marking.from_dict(dict_repr.get('handling'))
        return_obj.kill_chain_phases = KillChainPhasesReference.from_dict(dict_repr.get('kill_chain_phases'))
        return_obj.related_indicators = RelatedIndicators.from_dict(dict_repr.get('related_indicators'))
        return_obj.likely_impact = Statement.from_dict(dict_repr.get('likely_impact'))
        
        if observable_dict:
            return_obj.add_observable(Observable.from_dict(observable_dict))
        if producer_dict:
            return_obj.producer = InformationSource.from_dict(producer_dict)
        if description_dict:
            return_obj.description = StructuredText.from_dict(description_dict)
        for indicator_type_dict in indicator_type_list:
                return_obj.add_indicator_type(VocabString.from_dict(indicator_type_dict))
        if confidence_dict:
            return_obj.confidence = Confidence.from_dict(confidence_dict)
        if alternative_id_dict:
            return_obj.alternative_id = alternative_id_dict
        if valid_time_position_dict:
            for valid_time_position_type_dict in valid_time_position_dict:
                return_obj.add_valid_time_position(ValidTime.from_dict(valid_time_position_type_dict))
        
        return return_obj
Example #14
0
    def from_obj(cls, obj, return_obj=None):        
        if not obj:
            return None
        if not return_obj:
            return_obj = cls()

        return_obj.id_              = obj.get_id()
        return_obj.idref            = obj.get_idref()
        return_obj.timestamp        = obj.get_timestamp()
        
        if isinstance(obj, cls._binding_class):
            return_obj.title            = obj.get_Title()
            return_obj.description      = StructuredText.from_obj(obj.get_Description())
            return_obj.short_description = StructuredText.from_obj(obj.get_Short_Description())
            return_obj.producer         = InformationSource.from_obj(obj.get_Producer())
            return_obj.confidence       = Confidence.from_obj(obj.get_Confidence())
            return_obj.sightings        = Sightings.from_obj(obj.get_Sightings())
            return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_obj(obj.get_Composite_Indicator_Expression())
            return_obj.handling = Marking.from_obj(obj.get_Handling())
            return_obj.kill_chain_phases = KillChainPhasesReference.from_obj(obj.get_Kill_Chain_Phases())
            return_obj.related_indicators = RelatedIndicators.from_obj(obj.get_Related_Indicators())
            return_obj.likely_impact = Statement.from_obj(obj.get_Likely_Impact())
            
            if obj.get_version():
                return_obj.version = obj.get_version()
            if obj.get_Type():
                for indicator_type in obj.get_Type():
                    return_obj.add_indicator_type(VocabString.from_obj(indicator_type)) 
            if obj.get_Observable():
                observable_obj = obj.get_Observable()
                observable = Observable.from_obj(observable_obj)
                return_obj.observables.append(observable)
            if obj.get_Indicated_TTP():
                return_obj.indicated_ttps = [RelatedTTP.from_obj(x) for x in obj.get_Indicated_TTP()]
            if obj.get_Test_Mechanisms():
                return_obj.test_mechanisms = [_BaseTestMechanism.from_obj(x) for x in obj.get_Test_Mechanisms().get_Test_Mechanism()]
            if obj.get_Suggested_COAs():
                return_obj.suggested_coas = SuggestedCOAs.from_obj(obj.get_Suggested_COAs())
            if obj.get_Alternative_ID():
                return_obj.alternative_id = obj.get_Alternative_ID()
            if obj.get_Valid_Time_Position():
                return_obj.valid_time_positions = [ValidTime.from_obj(x) for x in obj.get_Valid_Time_Position()]    
            
        return return_obj
Example #15
0
    def from_obj(cls, obj, return_obj=None):
        if not obj:
            return None

        if not return_obj:
            return_obj = cls()

        super(TTP, cls).from_obj(obj, return_obj=return_obj)

        if isinstance(obj, cls._binding_class):
            return_obj.behavior = Behavior.from_obj(obj.Behavior)
            return_obj.related_ttps = RelatedTTPs.from_obj(obj.Related_TTPs)
            return_obj.exploit_targets = ExploitTargets.from_obj(obj.Exploit_Targets)
            return_obj.resources = Resource.from_obj(obj.Resources)
            return_obj.victim_targeting = VictimTargeting.from_obj(obj.Victim_Targeting)
            return_obj.intended_effects = _IntendedEffects.from_obj(obj.Intended_Effect)
            return_obj.kill_chain_phases = KillChainPhasesReference.from_obj(obj.Kill_Chain_Phases)
            return_obj.related_packages = RelatedPackageRefs.from_obj(obj.Related_Packages)

        return return_obj
Example #16
0
    def from_dict(cls, dict_repr, return_obj=None):
        if not dict_repr:
            return None

        if not return_obj:
            return_obj = cls()

        super(TTP, cls).from_dict(dict_repr, return_obj=return_obj)

        get = dict_repr.get
        return_obj.behavior = Behavior.from_dict(get('behavior'))
        return_obj.related_ttps = RelatedTTPs.from_dict(get('related_ttps'))
        return_obj.exploit_targets = ExploitTargets.from_dict(get('exploit_targets'))
        return_obj.intended_effects = _IntendedEffects.from_dict(get('intended_effects'))
        return_obj.resources = Resource.from_dict(get('resources'))
        return_obj.victim_targeting = VictimTargeting.from_dict(get('victim_targeting'))
        return_obj.related_packages = RelatedPackageRefs.from_dict(get('related_packages'))
        return_obj.kill_chain_phases = KillChainPhasesReference.from_dict(get('kill_chain_phases'))

        return return_obj
Example #17
0
    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.handling = Marking.from_dict(get('handling'))
        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
Example #18
0
    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.handling = Marking.from_obj(obj.Handling)
            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 main():
    stix_pkg = STIXPackage()

    # create LM-style kill chain
    # REF: http://stix.mitre.org/language/version{{site.current_version}}/stix_v{{site.current_version}}_lmco_killchain.xml

    recon = KillChainPhase(phase_id="stix:TTP-af1016d6-a744-4ed7-ac91-00fe2272185a", name="Reconnaissance", ordinality="1")
    weapon = KillChainPhase(phase_id="stix:TTP-445b4827-3cca-42bd-8421-f2e947133c16", name="Weaponization", ordinality="2")
    deliver = KillChainPhase(phase_id="stix:TTP-79a0e041-9d5f-49bb-ada4-8322622b162d", name="Delivery", ordinality="3")
    exploit = KillChainPhase(phase_id="stix:TTP-f706e4e7-53d8-44ef-967f-81535c9db7d0", name="Exploitation", ordinality="4")
    install = KillChainPhase(phase_id="stix:TTP-e1e4e3f7-be3b-4b39-b80a-a593cfd99a4f", name="Installation", ordinality="5")
    control = KillChainPhase(phase_id="stix:TTP-d6dc32b9-2538-4951-8733-3cb9ef1daae2", name="Command and Control", ordinality="6")
    action = KillChainPhase(phase_id="stix:TTP-786ca8f9-2d9a-4213-b38e-399af4a2e5d6", name="Actions on Objectives", ordinality="7")

    lmchain = KillChain(id_="stix:TTP-af3e707f-2fb9-49e5-8c37-14026ca0a5ff", name="LM Cyber Kill Chain")
    lmchain.definer = "LMCO"

    lmchain.kill_chain_phases = [recon, weapon, deliver, exploit, install, control, action]
    stix_pkg.ttps.kill_chains.append(lmchain)

    infect = KillChainPhase(name="Infect Machine")
    exfil = KillChainPhase(name="Exfiltrate Data")

    mychain = KillChain(name="Organization-specific Kill Chain")
    mychain.definer = "Myself"

    mychain.kill_chain_phases = [infect, exfil]
    stix_pkg.ttps.add_ttp(TTP())
    stix_pkg.ttps.kill_chains.append(mychain)

    indicator = Indicator()
    indicator.kill_chain_phases = KillChainPhasesReference([
        KillChainPhaseReference(phase_id=exfil.phase_id, kill_chain_id=mychain.id_),
        KillChainPhaseReference(phase_id=action.phase_id, kill_chain_id=lmchain.id_)
    ])
    stix_pkg.add_indicator(indicator)

    print(stix_pkg.to_xml(encoding=None))
Example #20
0
def process_kill_chain_phases(phases, obj1x):
    for phase in phases:
        if phase["kill_chain_name"] in _KILL_CHAINS:
            kill_chain_phases = _KILL_CHAINS[
                phase["kill_chain_name"]]["phases"]
            if not phase["phase_name"] in kill_chain_phases:
                kill_chain_phases.update({
                    phase["phase_name"]:
                    KillChainPhase(phase_id=create_id1x("TTP"),
                                   name=phase["phase_name"],
                                   ordinality=None)
                })
                _KILL_CHAINS[phase["kill_chain_name"]][
                    "kill_chain"].add_kill_chain_phase(
                        kill_chain_phases[phase["phase_name"]])
            kcp = kill_chain_phases[phase["phase_name"]]
            if not obj1x.kill_chain_phases:
                obj1x.kill_chain_phases = KillChainPhasesReference()
        else:
            kc = KillChain(id_=create_id1x("TTP"),
                           name=phase["kill_chain_name"])
            _KILL_CHAINS[phase["kill_chain_name"]] = {"kill_chain": kc}
            kcp = KillChainPhase(name=phase["phase_name"],
                                 phase_id=create_id1x("TTP"))
            kc.add_kill_chain_phase(kcp)
            _KILL_CHAINS[phase["kill_chain_name"]]["phases"] = {
                phase["phase_name"]: kcp
            }
        obj1x.add_kill_chain_phase(
            KillChainPhaseReference(
                phase_id=kcp.phase_id,
                name=kcp.name,
                ordinality=None,
                kill_chain_id=_KILL_CHAINS[
                    phase["kill_chain_name"]]["kill_chain"].id_,
                kill_chain_name=_KILL_CHAINS[
                    phase["kill_chain_name"]]["kill_chain"].name))
Example #21
0
 def kill_chain_phases(self, value):
     self._kill_chain_phases = KillChainPhasesReference(value)
Example #22
0
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)
Example #23
0
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
Example #24
0
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']
Example #25
0
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()
Example #26
0
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']
 def test_add_no_id_phase(self):
     refs = KillChainPhasesReference()
     phase = KillChainPhase()
     self.assertRaises(ValueError, refs.append, phase)
Example #28
0
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
Example #29
0
class Indicator(stix.Entity):
    _binding = indicator_binding
    _binding_class = indicator_binding.IndicatorType
    _namespace = 'http://stix.mitre.org/Indicator-2'
    _version = "2.1.1"

    def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None):
        self.id_ = id_ or stix.utils.create_id("indicator")
        self.idref = idref
        self.version = self._version
        self.producer = None
        self.observables = None
        self.title = title
        self.description = description
        self.short_description = short_description
        self.indicator_types = None
        self.confidence = None
        self.indicated_ttps = None
        self.test_mechanisms = None
        self.alternative_id = None
        self.suggested_coas = SuggestedCOAs()
        self.sightings = Sightings()
        self.composite_indicator_expression = None
        self.handling = None
        self.kill_chain_phases = KillChainPhasesReference()
        self.valid_time_positions = None
        self.related_indicators = None
        self.observable_composition_operator = "AND"
    
        if timestamp:
            self.timestamp = timestamp
        else:
            self.timestamp = datetime.now(tzutc()) if not idref else None
    
    @property
    def id_(self):
        return self._id
    
    @id_.setter
    def id_(self, value):
        if not value:
            self._id = None
        else:
            self._id = value
            self.idref = None
    
    @property
    def idref(self):
        return self._idref
    
    @idref.setter
    def idref(self, value):
        if not value:
            self._idref = None
        else:
            self._idref = value
            self.id_ = None # unset id_ if idref is present
    
    @property
    def timestamp(self):
        return self._timestamp

    @timestamp.setter
    def timestamp(self, value):
        self._timestamp = dates.parse_value(value)

    @property
    def description(self):
        return self._description

    @description.setter
    def description(self, value):
        if value:
            if isinstance(value, StructuredText):
                self._description = value
            else:
                self._description = StructuredText(value=value)
        else:
            self._description = None

    @property
    def short_description(self):
        return self._short_description

    @short_description.setter
    def short_description(self, value):
        if value:
            if isinstance(value, StructuredText):
                self._short_description = value
            else:
                self._short_description = StructuredText(value=value)
        else:
            self._short_description = None

    @property
    def producer(self):
        return self._producer

    @producer.setter
    def producer(self, value):
        if value and not isinstance(value, InformationSource):
            raise ValueError('value must be instance of InformationSource')

        self._producer = value

    @property
    def observable(self):
        if self.observables:
            return self.observables[0]
        else:
            return None
    
    @observable.setter
    def observable(self, observable):
        self._observables = []
        self.add_observable(observable)

    @property
    def observables(self):
        return self._observables

    @observables.setter
    def observables(self, valuelist):
        self._observables = [] # initialize the variable

        if valuelist:
            for value in valuelist:
                self.add_observable(value)
                
    @property
    def alternative_id(self):
        return self._alternative_id

    @alternative_id.setter
    def alternative_id(self, value):
        self._alternative_id = []
        if not value:
            return
        elif isinstance(value, list):
            for v in value:
                self.add_alternative_id(v)
        else:
            self.add_alternative_id(value)

    def add_alternative_id(self, value):
        if not value:
            return
        else:
            self.alternative_id.append(value)
                
    @property
    def valid_time_positions(self):
        return self._valid_time_positions

    @valid_time_positions.setter
    def valid_time_positions(self, value):
        self._valid_time_positions = []
        if not value:
            return
        elif isinstance(value, list):
            for v in value:
                self.add_valid_time_position(v)
        else:
            self.add_valid_time_position(value)

    def add_valid_time_position(self, value):
        if not value:
            return
        elif isinstance(value, ValidTime):
            self.valid_time_positions.append(value)
        else:
            raise ValueError("value must be instance of ValidTime")

    @property
    def indicator_types(self):
        return self._indicator_types

    @indicator_types.setter
    def indicator_types(self, value):
        self._indicator_types = []
        if not value:
            return
        elif isinstance(value, list):
            for v in value:
                self.add_indicator_type(v)
        else:
            self.add_indicator_type(value)

    def add_indicator_type(self, value):
        if not value:
            return
        elif isinstance(value, VocabString):
            self.indicator_types.append(value)
        else:
            tmp_indicator_type = IndicatorType(value=value)
            self.indicator_types.append(tmp_indicator_type)
    
    @property
    def confidence(self):
        return self._confidence
    
    @confidence.setter
    def confidence(self, value):
        if not value:
            self._confidence = None
        elif isinstance(value, Confidence):
            self._confidence = value
        else:
            self._confidence = Confidence(value=value)

    @property
    def indicated_ttps(self):
        return self._indicated_ttps
    
    @indicated_ttps.setter
    def indicated_ttps(self, value):
        self._indicated_ttps = []
        if not value:
            return
        elif isinstance(value, list):
            for v in value:
                self.add_indicated_ttp(v)
        else:
            self.add_indicated_ttp(value)
            
    def add_indicated_ttp(self, v):
        if not v:
            return
        elif isinstance(v, RelatedTTP):
            self.indicated_ttps.append(v)
        else:
            self.indicated_ttps.append(RelatedTTP(v))
        
    @property
    def test_mechanisms(self):
        return self._test_mechanisms
    
    @test_mechanisms.setter
    def test_mechanisms(self, value):
        self._test_mechanisms = []
        if not value:
            return
        elif isinstance(value, list):
            for v in value:
                self.add_test_mechanism(v)
        else:
            self.add_test_mechanism(value)
            
    def add_test_mechanism(self, tm):
        if not tm:
            return
        elif isinstance(tm, _BaseTestMechanism):
            self.test_mechanisms.append(tm)
        else:
            raise ValueError('Cannot add type %s to test_mechanisms list' % type(tm))

    @property
    def handling(self):
        return self._handling
    
    @handling.setter
    def handling(self, value):
        if not value:
            self._handling = None
        elif isinstance(value, Marking):
            self._handling = value
        else:
            raise ValueError('unable to set handling to type %s' % type(value))

    @property
    def related_indicators(self):
        return self._related_indicators

    @related_indicators.setter
    def related_indicators(self, value):
        self._related_indicators = RelatedIndicators()
        
        if not value:
            return
        elif isinstance(value, RelatedIndicators):
            self._related_indicators = value
        elif isinstance(value, list):
            for v in value:
                self.add_related_indicator(v)
        else:
            self.add_related_indicator(value)
    
    def add_related_indicator(self, indicator):
        if not indicator:
            return
        elif isinstance(indicator, RelatedIndicator):
            self.related_indicators.append(indicator)
        else:
            self.related_indicators.append(RelatedIndicator(indicator))

    @property
    def observable_composition_operator(self):
        return self._observable_composition_operator

    @observable_composition_operator.setter
    def observable_composition_operator(self, value):
        if value not in ("AND", "OR"):
            raise ValueError("observable_composition_operator must be 'AND' or 'OR'")
        
        self._observable_composition_operator = value

    def set_producer_identity(self, identity):
        '''
        Sets the name of the producer of this indicator.
        The identity param can be a string (name) or an Identity
        instance.
        '''
        if not self.producer:
            self.producer = InformationSource()

        if isinstance(identity, Identity):
            self.producer.identity = identity
        else:
            if not self.producer.identity:
                self.producer.identity = Identity()

            self.producer.identity.name = identity # assume it's a string

    def set_produced_time(self, produced_time):
        '''The produced date variable must be in ISO 8601 format'''
        if not self.producer.time:
            self.producer.time = Time()

        self.producer.time.produced_time = produced_time

    def get_produced_time(self):
        if self.producer and self.producer.time:
            return self.producer.time.produced_time
        else:
            return None    

    def set_received_time(self, received_time):
        '''Set the time when this indicator was received'''
        if not self.producer.time:
            self.producer.time = Time()

        self.producer.time.received_time = received_time

    def get_received_time(self):
        '''Return the time when this indicator was received'''
        if self.producer and self.producer.time:
            return self.producer.time.received_time
        else:
            return None

    def add_observable(self, observable):
        ''' Adds an observable to the Indicator. If the number of observables associated with this indicator 
            is greater than one, the indicator will nest all of its observables under a parent observable
            composition, with an logical operator of 'OR'. If this is not ideal, an separate indicator
            should be made for each observable'''

        if isinstance(observable, Observable):
            self.observables.append(observable)
        else:
            # try to cast it to an Observable type
            self.observables.append(Observable(observable))

    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_):
        ''' The object paramter is wrapped in an observable and attached to the indicator. The object must be a 
            cybox.core.DefinedObject instance'''

        observable = Observable(object_)
        self.add_observable(observable)

    def to_obj(self, return_obj=None):
        if not return_obj:
            return_obj = self._binding_class()

        return_obj.set_id(self.id_)
        return_obj.set_idref(self.idref)
        return_obj.set_timestamp(dates.serialize_value(self.timestamp))
        return_obj.set_Title(self.title)
        
        if self.version:
            return_obj.set_version(self._version)
        if self.description:
            return_obj.set_Description(self.description.to_obj())
        if self.short_description:
            return_obj.set_Short_Description(self.short_description.to_obj())
        if self.confidence:
            return_obj.set_Confidence(self.confidence.to_obj())
        if self.indicator_types:
            for indicator_type in self.indicator_types:
                tmp_indicator_type = indicator_type.to_obj()
                return_obj.add_Type(tmp_indicator_type)
        if self.indicated_ttps:
            return_obj.set_Indicated_TTP([x.to_obj() for x in self.indicated_ttps])
        if self.observables:
            if len(self.observables) > 1:
                root_observable = self._merge_observables(self.observables)
            else:
                root_observable = self.observables[0]
            return_obj.set_Observable(root_observable.to_obj())
        if self.producer:
            return_obj.set_Producer(self.producer.to_obj())
        if self.test_mechanisms:
            tms_obj = self._binding.TestMechanismsType()
            tms_obj.set_Test_Mechanism([x.to_obj() for x in self.test_mechanisms])
            return_obj.set_Test_Mechanisms(tms_obj)
        if self.alternative_id:
            return_obj.set_Alternative_ID(self.alternative_id)
        if self.valid_time_positions:
            return_obj.set_Valid_Time_Position([x.to_obj() for x in self.valid_time_positions])
        if self.suggested_coas:
            return_obj.set_Suggested_COAs(self.suggested_coas.to_obj())
        if self.sightings:
            return_obj.set_Sightings(self.sightings.to_obj())
        if self.composite_indicator_expression:
            return_obj.set_Composite_Indicator_Expression(self.composite_indicator_expression.to_obj())
        if self.handling:
            return_obj.set_Handling(self.handling.to_obj())
        if self.kill_chain_phases:
            return_obj.set_Kill_Chain_Phases(self.kill_chain_phases.to_obj())
        if self.related_indicators:
            return_obj.set_Related_Indicators(self.related_indicators.to_obj())

        return return_obj

    @classmethod
    def from_obj(cls, obj, return_obj=None):        
        if not obj:
            return None
        if not return_obj:
            return_obj = cls()

        return_obj.id_              = obj.get_id()
        return_obj.idref            = obj.get_idref()
        return_obj.timestamp        = obj.get_timestamp()
        
        if isinstance(obj, cls._binding_class):
            return_obj.title            = obj.get_Title()
            return_obj.description      = StructuredText.from_obj(obj.get_Description())
            return_obj.short_description = StructuredText.from_obj(obj.get_Short_Description())
            return_obj.producer         = InformationSource.from_obj(obj.get_Producer())
            return_obj.confidence       = Confidence.from_obj(obj.get_Confidence())
            return_obj.sightings        = Sightings.from_obj(obj.get_Sightings())
            return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_obj(obj.get_Composite_Indicator_Expression())
            return_obj.handling = Marking.from_obj(obj.get_Handling())
            return_obj.kill_chain_phases = KillChainPhasesReference.from_obj(obj.get_Kill_Chain_Phases())
            return_obj.related_indicators = RelatedIndicators.from_obj(obj.get_Related_Indicators())
            
            if obj.get_version():
                return_obj.version = obj.get_version()
            if obj.get_Type():
                for indicator_type in obj.get_Type():
                    return_obj.add_indicator_type(VocabString.from_obj(indicator_type)) 
            if obj.get_Observable():
                observable_obj = obj.get_Observable()
                observable = Observable.from_obj(observable_obj)
                return_obj.observables.append(observable)
            if obj.get_Indicated_TTP():
                return_obj.indicated_ttps = [RelatedTTP.from_obj(x) for x in obj.get_Indicated_TTP()]
            if obj.get_Test_Mechanisms():
                return_obj.test_mechanisms = [_BaseTestMechanism.from_obj(x) for x in obj.get_Test_Mechanisms().get_Test_Mechanism()]
            if obj.get_Suggested_COAs():
                return_obj.suggested_coas = SuggestedCOAs.from_obj(obj.get_Suggested_COAs())
            if obj.get_Alternative_ID():
                return_obj.alternative_id = obj.get_Alternative_ID()
            if obj.get_Valid_Time_Position():
                return_obj.valid_time_positions = [ValidTime.from_obj(x) for x in obj.get_Valid_Time_Position()]    
            
        return return_obj

    def to_dict(self):
        d = {}
        if self.id_:
            d['id'] = self.id_
        if self.idref:
            d['idref'] = self.idref
        if self.timestamp:
            d['timestamp'] = dates.serialize_value(self.timestamp)
        if self.version:
            d['version'] = self.version
        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()
        if self.producer:
            d['producer'] = self.producer.to_dict()
        if self.title:
            d['title'] = self.title
        if self.description:
            d['description'] = self.description.to_dict()
        if self.short_description:
            d['short_description'] = self.short_description.to_dict()
        if self.indicator_types:
            d['indicator_types'] = [x.to_dict() for x in self.indicator_types]
        if self.confidence:
            d['confidence'] = self.confidence.to_dict()
        if self.indicated_ttps:
            d['indicated_ttps'] = [x.to_dict() for x in self.indicated_ttps]
        if self.test_mechanisms:
            d['test_mechanisms'] = [x.to_dict() for x in self.test_mechanisms]
        if self.alternative_id:
            d['alternative_id'] = self.alternative_id
        if self.valid_time_positions:
            d['valid_time_positions'] = [x.to_dict() for x in self.valid_time_positions]
        if self.suggested_coas:
            d['suggested_coas'] = self.suggested_coas.to_dict()
        if self.sightings:
            d['sightings'] = self.sightings.to_dict()
        if self.composite_indicator_expression:
            d['composite_indicator_expression'] = self.composite_indicator_expression.to_dict()
        if self.handling:
            d['handling'] = self.handling.to_dict()
        if self.kill_chain_phases:
            d['kill_chain_phases'] = self.kill_chain_phases.to_dict()
        if self.related_indicators:
            d['related_indicators'] = self.related_indicators.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()

        return_obj.id_       = dict_repr.get('id')
        return_obj.idref     = dict_repr.get('idref')
        return_obj.timestamp = dict_repr.get('timestamp')
        return_obj.title     = dict_repr.get('title')
        return_obj.version   = dict_repr.get('version', cls._version)
        observable_dict      = dict_repr.get('observable')
        producer_dict        = dict_repr.get('producer')
        description_dict     = dict_repr.get('description')
        indicator_type_list  = dict_repr.get('indicator_types', [])
        confidence_dict      = dict_repr.get('confidence')
        alternative_id_dict  = dict_repr.get('alternative_id')
        valid_time_position_dict  = dict_repr.get('valid_time_positions')

        return_obj.short_description = StructuredText.from_dict(dict_repr.get('short_description'))
        return_obj.indicated_ttps = [RelatedTTP.from_dict(x) for x in dict_repr.get('indicated_ttps', [])]
        return_obj.test_mechanisms = [_BaseTestMechanism.from_dict(x) for x in dict_repr.get('test_mechanisms', [])]
        return_obj.suggested_coas = SuggestedCOAs.from_dict(dict_repr.get('suggested_coas'))
        return_obj.sightings = Sightings.from_dict(dict_repr.get('sightings'))
        return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_dict(dict_repr.get('composite_indicator_expression'))
        return_obj.handling = Marking.from_dict(dict_repr.get('handling'))
        return_obj.kill_chain_phases = KillChainPhasesReference.from_dict(dict_repr.get('kill_chain_phases'))
        return_obj.related_indicators = RelatedIndicators.from_dict(dict_repr.get('related_indicators'))
        
        if observable_dict:
            return_obj.add_observable(Observable.from_dict(observable_dict))
        if producer_dict:
            return_obj.producer = InformationSource.from_dict(producer_dict)
        if description_dict:
            return_obj.description = StructuredText.from_dict(description_dict)
        for indicator_type_dict in indicator_type_list:
                return_obj.add_indicator_type(VocabString.from_dict(indicator_type_dict))
        if confidence_dict:
            return_obj.confidence = Confidence.from_dict(confidence_dict)
        if alternative_id_dict:
            return_obj.alternative_id = alternative_id_dict
        if valid_time_position_dict:
            for valid_time_position_type_dict in valid_time_position_dict:
                return_obj.add_valid_time_position(ValidTime.from_dict(valid_time_position_type_dict))
        
        return return_obj
Example #30
0
class Indicator(stix.Entity):
    _binding = indicator_binding
    _binding_class = indicator_binding.IndicatorType
    _namespace = 'http://stix.mitre.org/Indicator-2'
    _version = "2.1.1"

    def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None):
        self.id_ = id_ or stix.utils.create_id("indicator")
        self.idref = idref
        self.version = self._version
        self.producer = None
        self.observables = None
        self.title = title
        self.description = description
        self.short_description = short_description
        self.indicator_types = None
        self.confidence = None
        self.indicated_ttps = None
        self.test_mechanisms = None
        self.alternative_id = None
        self.suggested_coas = SuggestedCOAs()
        self.sightings = Sightings()
        self.composite_indicator_expression = None
        self.handling = None
        self.kill_chain_phases = KillChainPhasesReference()
        self.valid_time_positions = None
        self.related_indicators = None
        self.observable_composition_operator = "AND"
        self.likely_impact = None
    
        if timestamp:
            self.timestamp = timestamp
        else:
            self.timestamp = datetime.now(tzutc()) if not idref else None
    
    @property
    def id_(self):
        return self._id
    
    @id_.setter
    def id_(self, value):
        if not value:
            self._id = None
        else:
            self._id = value
            self.idref = None
    
    @property
    def idref(self):
        return self._idref
    
    @idref.setter
    def idref(self, value):
        if not value:
            self._idref = None
        else:
            self._idref = value
            self.id_ = None # unset id_ if idref is present
    
    @property
    def timestamp(self):
        return self._timestamp

    @timestamp.setter
    def timestamp(self, value):
        self._timestamp = dates.parse_value(value)

    @property
    def description(self):
        return self._description

    @description.setter
    def description(self, value):
        if value:
            if isinstance(value, StructuredText):
                self._description = value
            else:
                self._description = StructuredText(value=value)
        else:
            self._description = None

    @property
    def short_description(self):
        return self._short_description

    @short_description.setter
    def short_description(self, value):
        if value:
            if isinstance(value, StructuredText):
                self._short_description = value
            else:
                self._short_description = StructuredText(value=value)
        else:
            self._short_description = None

    @property
    def producer(self):
        return self._producer

    @producer.setter
    def producer(self, value):
        if value and not isinstance(value, InformationSource):
            raise ValueError('value must be instance of InformationSource')

        self._producer = value

    @property
    def observable(self):
        if self.observables:
            return self.observables[0]
        else:
            return None
    
    @observable.setter
    def observable(self, observable):
        self._observables = []
        self.add_observable(observable)

    @property
    def observables(self):
        return self._observables

    @observables.setter
    def observables(self, valuelist):
        self._observables = [] # initialize the variable

        if valuelist:
            for value in valuelist:
                self.add_observable(value)
                
    @property
    def alternative_id(self):
        return self._alternative_id

    @alternative_id.setter
    def alternative_id(self, value):
        self._alternative_id = []
        if not value:
            return
        elif isinstance(value, list):
            for v in value:
                self.add_alternative_id(v)
        else:
            self.add_alternative_id(value)

    def add_alternative_id(self, value):
        if not value:
            return
        else:
            self.alternative_id.append(value)
                
    @property
    def valid_time_positions(self):
        return self._valid_time_positions

    @valid_time_positions.setter
    def valid_time_positions(self, value):
        self._valid_time_positions = []
        if not value:
            return
        elif isinstance(value, list):
            for v in value:
                self.add_valid_time_position(v)
        else:
            self.add_valid_time_position(value)

    def add_valid_time_position(self, value):
        if not value:
            return
        elif isinstance(value, ValidTime):
            self.valid_time_positions.append(value)
        else:
            raise ValueError("value must be instance of ValidTime")

    @property
    def indicator_types(self):
        return self._indicator_types

    @indicator_types.setter
    def indicator_types(self, value):
        self._indicator_types = IndicatorTypes()
        if not value:
            return
        elif isinstance(value, list):
            for v in value:
                self.add_indicator_type(v)
        else:
            self.add_indicator_type(value)

    def add_indicator_type(self, value):
        if not value:
            return
        elif isinstance(value, VocabString):
            self.indicator_types.append(value)
        else:
            tmp_indicator_type = IndicatorType(value=value)
            self.indicator_types.append(tmp_indicator_type)
    
    @property
    def confidence(self):
        return self._confidence
    
    @confidence.setter
    def confidence(self, value):
        if not value:
            self._confidence = None
        elif isinstance(value, Confidence):
            self._confidence = value
        else:
            self._confidence = Confidence(value=value)

    @property
    def indicated_ttps(self):
        return self._indicated_ttps
    
    @indicated_ttps.setter
    def indicated_ttps(self, value):
        self._indicated_ttps = []
        if not value:
            return
        elif isinstance(value, list):
            for v in value:
                self.add_indicated_ttp(v)
        else:
            self.add_indicated_ttp(value)
            
    def add_indicated_ttp(self, v):
        if not v:
            return
        elif isinstance(v, RelatedTTP):
            self.indicated_ttps.append(v)
        else:
            self.indicated_ttps.append(RelatedTTP(v))
        
    @property
    def test_mechanisms(self):
        return self._test_mechanisms
    
    @test_mechanisms.setter
    def test_mechanisms(self, value):
        self._test_mechanisms = []
        if not value:
            return
        elif isinstance(value, list):
            for v in value:
                self.add_test_mechanism(v)
        else:
            self.add_test_mechanism(value)
            
    def add_test_mechanism(self, tm):
        if not tm:
            return
        elif isinstance(tm, _BaseTestMechanism):
            self.test_mechanisms.append(tm)
        else:
            raise ValueError('Cannot add type %s to test_mechanisms list' % type(tm))

    @property
    def handling(self):
        return self._handling
    
    @handling.setter
    def handling(self, value):
        if not value:
            self._handling = None
        elif isinstance(value, Marking):
            self._handling = value
        else:
            raise ValueError('unable to set handling to type %s' % type(value))

    @property
    def related_indicators(self):
        return self._related_indicators

    @related_indicators.setter
    def related_indicators(self, value):
        self._related_indicators = RelatedIndicators()
        
        if not value:
            return
        elif isinstance(value, RelatedIndicators):
            self._related_indicators = value
        elif isinstance(value, list):
            for v in value:
                self.add_related_indicator(v)
        else:
            self.add_related_indicator(value)
    
    def add_related_indicator(self, indicator):
        if not indicator:
            return
        elif isinstance(indicator, RelatedIndicator):
            self.related_indicators.append(indicator)
        else:
            self.related_indicators.append(RelatedIndicator(indicator))

    @property
    def observable_composition_operator(self):
        return self._observable_composition_operator

    @observable_composition_operator.setter
    def observable_composition_operator(self, value):
        if value not in ("AND", "OR"):
            raise ValueError("observable_composition_operator must be 'AND' or 'OR'")
        
        self._observable_composition_operator = value
    
    @property
    def likely_impact(self):
        return self._likely_impact
    
    @likely_impact.setter
    def likely_impact(self, value):
        if not value:
            self._likely_impact = None
        elif isinstance(value, Statement):
            self._likely_impact = value
        else:
            self._likely_impact = Statement(value=value)
    
    def set_producer_identity(self, identity):
        '''
        Sets the name of the producer of this indicator.
        The identity param can be a string (name) or an Identity
        instance.
        '''
        if not self.producer:
            self.producer = InformationSource()

        if isinstance(identity, Identity):
            self.producer.identity = identity
        else:
            if not self.producer.identity:
                self.producer.identity = Identity()

            self.producer.identity.name = identity # assume it's a string

    def set_produced_time(self, produced_time):
        '''The produced date variable must be in ISO 8601 format'''
        if not self.producer.time:
            self.producer.time = Time()

        self.producer.time.produced_time = produced_time

    def get_produced_time(self):
        if self.producer and self.producer.time:
            return self.producer.time.produced_time
        else:
            return None    

    def set_received_time(self, received_time):
        '''Set the time when this indicator was received'''
        if not self.producer.time:
            self.producer.time = Time()

        self.producer.time.received_time = received_time

    def get_received_time(self):
        '''Return the time when this indicator was received'''
        if self.producer and self.producer.time:
            return self.producer.time.received_time
        else:
            return None

    def add_observable(self, observable):
        ''' Adds an observable to the Indicator. If the number of observables associated with this indicator 
            is greater than one, the indicator will nest all of its observables under a parent observable
            composition, with an logical operator of 'OR'. If this is not ideal, an separate indicator
            should be made for each observable'''

        if isinstance(observable, Observable):
            self.observables.append(observable)
        else:
            # try to cast it to an Observable type
            self.observables.append(Observable(observable))

    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_):
        ''' The object paramter is wrapped in an observable and attached to the indicator. The object must be a 
            cybox.core.DefinedObject instance'''

        observable = Observable(object_)
        self.add_observable(observable)

    def to_obj(self, return_obj=None):
        if not return_obj:
            return_obj = self._binding_class()

        return_obj.set_id(self.id_)
        return_obj.set_idref(self.idref)
        return_obj.set_timestamp(dates.serialize_value(self.timestamp))
        return_obj.set_Title(self.title)
        
        if self.version:
            return_obj.set_version(self._version)
        if self.description:
            return_obj.set_Description(self.description.to_obj())
        if self.short_description:
            return_obj.set_Short_Description(self.short_description.to_obj())
        if self.confidence:
            return_obj.set_Confidence(self.confidence.to_obj())
        if self.indicator_types:
            for indicator_type in self.indicator_types:
                tmp_indicator_type = indicator_type.to_obj()
                return_obj.add_Type(tmp_indicator_type)
        if self.indicated_ttps:
            return_obj.set_Indicated_TTP([x.to_obj() for x in self.indicated_ttps])
        if self.observables:
            if len(self.observables) > 1:
                root_observable = self._merge_observables(self.observables)
            else:
                root_observable = self.observables[0]
            return_obj.set_Observable(root_observable.to_obj())
        if self.producer:
            return_obj.set_Producer(self.producer.to_obj())
        if self.test_mechanisms:
            tms_obj = self._binding.TestMechanismsType()
            tms_obj.set_Test_Mechanism([x.to_obj() for x in self.test_mechanisms])
            return_obj.set_Test_Mechanisms(tms_obj)
        if self.likely_impact:
            return_obj.set_Likely_Impact(self.likely_impact.to_obj())
        if self.alternative_id:
            return_obj.set_Alternative_ID(self.alternative_id)
        if self.valid_time_positions:
            return_obj.set_Valid_Time_Position([x.to_obj() for x in self.valid_time_positions])
        if self.suggested_coas:
            return_obj.set_Suggested_COAs(self.suggested_coas.to_obj())
        if self.sightings:
            return_obj.set_Sightings(self.sightings.to_obj())
        if self.composite_indicator_expression:
            return_obj.set_Composite_Indicator_Expression(self.composite_indicator_expression.to_obj())
        if self.handling:
            return_obj.set_Handling(self.handling.to_obj())
        if self.kill_chain_phases:
            return_obj.set_Kill_Chain_Phases(self.kill_chain_phases.to_obj())
        if self.related_indicators:
            return_obj.set_Related_Indicators(self.related_indicators.to_obj())

        return return_obj

    @classmethod
    def from_obj(cls, obj, return_obj=None):        
        if not obj:
            return None
        if not return_obj:
            return_obj = cls()

        return_obj.id_              = obj.get_id()
        return_obj.idref            = obj.get_idref()
        return_obj.timestamp        = obj.get_timestamp()
        
        if isinstance(obj, cls._binding_class):
            return_obj.title            = obj.get_Title()
            return_obj.description      = StructuredText.from_obj(obj.get_Description())
            return_obj.short_description = StructuredText.from_obj(obj.get_Short_Description())
            return_obj.producer         = InformationSource.from_obj(obj.get_Producer())
            return_obj.confidence       = Confidence.from_obj(obj.get_Confidence())
            return_obj.sightings        = Sightings.from_obj(obj.get_Sightings())
            return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_obj(obj.get_Composite_Indicator_Expression())
            return_obj.handling = Marking.from_obj(obj.get_Handling())
            return_obj.kill_chain_phases = KillChainPhasesReference.from_obj(obj.get_Kill_Chain_Phases())
            return_obj.related_indicators = RelatedIndicators.from_obj(obj.get_Related_Indicators())
            return_obj.likely_impact = Statement.from_obj(obj.get_Likely_Impact())
            
            if obj.get_version():
                return_obj.version = obj.get_version()
            if obj.get_Type():
                for indicator_type in obj.get_Type():
                    return_obj.add_indicator_type(VocabString.from_obj(indicator_type)) 
            if obj.get_Observable():
                observable_obj = obj.get_Observable()
                observable = Observable.from_obj(observable_obj)
                return_obj.observables.append(observable)
            if obj.get_Indicated_TTP():
                return_obj.indicated_ttps = [RelatedTTP.from_obj(x) for x in obj.get_Indicated_TTP()]
            if obj.get_Test_Mechanisms():
                return_obj.test_mechanisms = [_BaseTestMechanism.from_obj(x) for x in obj.get_Test_Mechanisms().get_Test_Mechanism()]
            if obj.get_Suggested_COAs():
                return_obj.suggested_coas = SuggestedCOAs.from_obj(obj.get_Suggested_COAs())
            if obj.get_Alternative_ID():
                return_obj.alternative_id = obj.get_Alternative_ID()
            if obj.get_Valid_Time_Position():
                return_obj.valid_time_positions = [ValidTime.from_obj(x) for x in obj.get_Valid_Time_Position()]    
            
        return return_obj

    def to_dict(self):
        d = {}
        if self.id_:
            d['id'] = self.id_
        if self.idref:
            d['idref'] = self.idref
        if self.timestamp:
            d['timestamp'] = dates.serialize_value(self.timestamp)
        if self.version:
            d['version'] = self.version
        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()
        if self.producer:
            d['producer'] = self.producer.to_dict()
        if self.title:
            d['title'] = self.title
        if self.description:
            d['description'] = self.description.to_dict()
        if self.short_description:
            d['short_description'] = self.short_description.to_dict()
        if self.indicator_types:
            d['indicator_types'] = [x.to_dict() for x in self.indicator_types]
        if self.confidence:
            d['confidence'] = self.confidence.to_dict()
        if self.indicated_ttps:
            d['indicated_ttps'] = [x.to_dict() for x in self.indicated_ttps]
        if self.test_mechanisms:
            d['test_mechanisms'] = [x.to_dict() for x in self.test_mechanisms]
        if self.likely_impact:
            d['likely_impact'] = self.likely_impact.to_dict()
        if self.alternative_id:
            d['alternative_id'] = self.alternative_id
        if self.valid_time_positions:
            d['valid_time_positions'] = [x.to_dict() for x in self.valid_time_positions]
        if self.suggested_coas:
            d['suggested_coas'] = self.suggested_coas.to_dict()
        if self.sightings:
            d['sightings'] = self.sightings.to_dict()
        if self.composite_indicator_expression:
            d['composite_indicator_expression'] = self.composite_indicator_expression.to_dict()
        if self.handling:
            d['handling'] = self.handling.to_dict()
        if self.kill_chain_phases:
            d['kill_chain_phases'] = self.kill_chain_phases.to_dict()
        if self.related_indicators:
            d['related_indicators'] = self.related_indicators.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()

        return_obj.id_       = dict_repr.get('id')
        return_obj.idref     = dict_repr.get('idref')
        return_obj.timestamp = dict_repr.get('timestamp')
        return_obj.title     = dict_repr.get('title')
        return_obj.version   = dict_repr.get('version', cls._version)
        observable_dict      = dict_repr.get('observable')
        producer_dict        = dict_repr.get('producer')
        description_dict     = dict_repr.get('description')
        indicator_type_list  = dict_repr.get('indicator_types', [])
        confidence_dict      = dict_repr.get('confidence')
        alternative_id_dict  = dict_repr.get('alternative_id')
        valid_time_position_dict  = dict_repr.get('valid_time_positions')

        return_obj.short_description = StructuredText.from_dict(dict_repr.get('short_description'))
        return_obj.indicated_ttps = [RelatedTTP.from_dict(x) for x in dict_repr.get('indicated_ttps', [])]
        return_obj.test_mechanisms = [_BaseTestMechanism.from_dict(x) for x in dict_repr.get('test_mechanisms', [])]
        return_obj.suggested_coas = SuggestedCOAs.from_dict(dict_repr.get('suggested_coas'))
        return_obj.sightings = Sightings.from_dict(dict_repr.get('sightings'))
        return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_dict(dict_repr.get('composite_indicator_expression'))
        return_obj.handling = Marking.from_dict(dict_repr.get('handling'))
        return_obj.kill_chain_phases = KillChainPhasesReference.from_dict(dict_repr.get('kill_chain_phases'))
        return_obj.related_indicators = RelatedIndicators.from_dict(dict_repr.get('related_indicators'))
        return_obj.likely_impact = Statement.from_dict(dict_repr.get('likely_impact'))
        
        if observable_dict:
            return_obj.add_observable(Observable.from_dict(observable_dict))
        if producer_dict:
            return_obj.producer = InformationSource.from_dict(producer_dict)
        if description_dict:
            return_obj.description = StructuredText.from_dict(description_dict)
        for indicator_type_dict in indicator_type_list:
                return_obj.add_indicator_type(VocabString.from_dict(indicator_type_dict))
        if confidence_dict:
            return_obj.confidence = Confidence.from_dict(confidence_dict)
        if alternative_id_dict:
            return_obj.alternative_id = alternative_id_dict
        if valid_time_position_dict:
            for valid_time_position_type_dict in valid_time_position_dict:
                return_obj.add_valid_time_position(ValidTime.from_dict(valid_time_position_type_dict))
        
        return return_obj