import pytest import stix2 from .constants import CAMPAIGN_MORE_KWARGS def test_making_new_version(): campaign_v1 = stix2.Campaign(**CAMPAIGN_MORE_KWARGS) campaign_v2 = campaign_v1.new_version(name="fred") assert campaign_v1.id == campaign_v2.id assert campaign_v1.created_by_ref == campaign_v2.created_by_ref assert campaign_v1.created == campaign_v2.created assert campaign_v1.name != campaign_v2.name assert campaign_v2.name == "fred" assert campaign_v1.description == campaign_v2.description assert campaign_v1.modified < campaign_v2.modified def test_making_new_version_with_unset(): campaign_v1 = stix2.Campaign(**CAMPAIGN_MORE_KWARGS) campaign_v2 = campaign_v1.new_version(description=None) assert campaign_v1.id == campaign_v2.id assert campaign_v1.created_by_ref == campaign_v2.created_by_ref assert campaign_v1.created == campaign_v2.created assert campaign_v1.name == campaign_v2.name with pytest.raises(AttributeError): assert campaign_v2.description assert campaign_v1.modified < campaign_v2.modified def test_making_new_version_with_embedded_object(): campaign_v1 = stix2.Campaign( external_references=[{ "source_name": "capec", "external_id": "CAPEC-163" }], **CAMPAIGN_MORE_KWARGS ) campaign_v2 = campaign_v1.new_version(external_references=[{ "source_name": "capec", "external_id": "CAPEC-164" }]) assert campaign_v1.id == campaign_v2.id assert campaign_v1.created_by_ref == campaign_v2.created_by_ref assert campaign_v1.created == campaign_v2.created assert campaign_v1.name == campaign_v2.name assert campaign_v1.description == campaign_v2.description assert campaign_v1.modified < campaign_v2.modified assert campaign_v1.external_references[0].external_id != campaign_v2.external_references[0].external_id def test_revoke(): campaign_v1 = stix2.Campaign(**CAMPAIGN_MORE_KWARGS) campaign_v2 = campaign_v1.revoke() assert campaign_v1.id == campaign_v2.id assert campaign_v1.created_by_ref == campaign_v2.created_by_ref assert campaign_v1.created == campaign_v2.created assert campaign_v1.name == campaign_v2.name assert campaign_v1.description == campaign_v2.description assert campaign_v1.modified < campaign_v2.modified assert campaign_v2.revoked def test_versioning_error_invalid_property(): campaign_v1 = stix2.Campaign(**CAMPAIGN_MORE_KWARGS) with pytest.raises(stix2.exceptions.UnmodifiablePropertyError) as excinfo: campaign_v1.new_version(type="threat-actor") assert str(excinfo.value) == "These properties cannot be changed when making a new version: type." def test_versioning_error_bad_modified_value(): campaign_v1 = stix2.Campaign(**CAMPAIGN_MORE_KWARGS) with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo: campaign_v1.new_version(modified="2015-04-06T20:03:00.000Z") assert excinfo.value.cls == stix2.Campaign assert excinfo.value.prop_name == "modified" assert excinfo.value.reason == "The new modified datetime cannot be before than or equal to the current modified datetime." \ "It cannot be equal, as according to STIX 2 specification, objects that are different " \ "but have the same id and modified timestamp do not have defined consumer behavior." msg = "Invalid value for {0} '{1}': {2}" msg = msg.format(stix2.Campaign.__name__, "modified", "The new modified datetime cannot be before than or equal to the current modified datetime." "It cannot be equal, as according to STIX 2 specification, objects that are different " "but have the same id and modified timestamp do not have defined consumer behavior.") assert str(excinfo.value) == msg def test_versioning_error_usetting_required_property(): campaign_v1 = stix2.Campaign(**CAMPAIGN_MORE_KWARGS) with pytest.raises(stix2.exceptions.MissingPropertiesError) as excinfo: campaign_v1.new_version(name=None) assert excinfo.value.cls == stix2.Campaign assert excinfo.value.properties == ["name"] msg = "No values for required properties for {0}: ({1})." msg = msg.format(stix2.Campaign.__name__, "name") assert str(excinfo.value) == msg def test_versioning_error_new_version_of_revoked(): campaign_v1 = stix2.Campaign(**CAMPAIGN_MORE_KWARGS) campaign_v2 = campaign_v1.revoke() with pytest.raises(stix2.exceptions.RevokeError) as excinfo: campaign_v2.new_version(name="barney") assert str(excinfo.value) == "Cannot create a new version of a revoked object." assert excinfo.value.called_by == "new_version" assert str(excinfo.value) == "Cannot create a new version of a revoked object." def test_versioning_error_revoke_of_revoked(): campaign_v1 = stix2.Campaign(**CAMPAIGN_MORE_KWARGS) campaign_v2 = campaign_v1.revoke() with pytest.raises(stix2.exceptions.RevokeError) as excinfo: campaign_v2.revoke() assert str(excinfo.value) == "Cannot revoke an already revoked object." assert excinfo.value.called_by == "revoke" assert str(excinfo.value) == "Cannot revoke an already revoked object." def test_making_new_version_dict(): campaign_v1 = CAMPAIGN_MORE_KWARGS campaign_v2 = stix2.utils.new_version(CAMPAIGN_MORE_KWARGS, name="fred") assert campaign_v1['id'] == campaign_v2['id'] assert campaign_v1['created_by_ref'] == campaign_v2['created_by_ref'] assert campaign_v1['created'] == campaign_v2['created'] assert campaign_v1['name'] != campaign_v2['name'] assert campaign_v2['name'] == "fred" assert campaign_v1['description'] == campaign_v2['description'] assert stix2.utils.parse_into_datetime(campaign_v1['modified'], precision='millisecond') < campaign_v2['modified'] def test_versioning_error_dict_bad_modified_value(): with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo: stix2.utils.new_version(CAMPAIGN_MORE_KWARGS, modified="2015-04-06T20:03:00.000Z") assert excinfo.value.cls == dict assert excinfo.value.prop_name == "modified" assert excinfo.value.reason == "The new modified datetime cannot be before than or equal to the current modified datetime." \ "It cannot be equal, as according to STIX 2 specification, objects that are different " \ "but have the same id and modified timestamp do not have defined consumer behavior." def test_versioning_error_dict_no_modified_value(): campaign_v1 = { 'type': 'campaign', 'id': "campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f", 'created': "2016-04-06T20:03:00.000Z", 'name': "Green Group Attacks Against Finance", } campaign_v2 = stix2.utils.new_version(campaign_v1, modified="2017-04-06T20:03:00.000Z") assert str(campaign_v2['modified']) == "2017-04-06T20:03:00.000Z" def test_making_new_version_invalid_cls(): campaign_v1 = "This is a campaign." with pytest.raises(ValueError) as excinfo: stix2.utils.new_version(campaign_v1, name="fred") assert 'cannot create new version of object of this type' in str(excinfo.value) def test_revoke_dict(): campaign_v1 = CAMPAIGN_MORE_KWARGS campaign_v2 = stix2.utils.revoke(campaign_v1) assert campaign_v1['id'] == campaign_v2['id'] assert campaign_v1['created_by_ref'] == campaign_v2['created_by_ref'] assert campaign_v1['created'] == campaign_v2['created'] assert campaign_v1['name'] == campaign_v2['name'] assert campaign_v1['description'] == campaign_v2['description'] assert stix2.utils.parse_into_datetime(campaign_v1['modified'], precision='millisecond') < campaign_v2['modified'] assert campaign_v2['revoked'] def test_versioning_error_revoke_of_revoked_dict(): campaign_v1 = CAMPAIGN_MORE_KWARGS campaign_v2 = stix2.utils.revoke(campaign_v1) with pytest.raises(stix2.exceptions.RevokeError) as excinfo: stix2.utils.revoke(campaign_v2) assert excinfo.value.called_by == "revoke" def test_revoke_invalid_cls(): campaign_v1 = "This is a campaign." with pytest.raises(ValueError) as excinfo: stix2.utils.revoke(campaign_v1) assert 'cannot revoke object of this type' in str(excinfo.value) def test_remove_custom_stix_property(): mal = stix2.Malware(name="ColePowers", labels=["rootkit"], x_custom="armada", allow_custom=True) mal_nc = stix2.utils.remove_custom_stix(mal) assert "x_custom" not in mal_nc assert stix2.utils.parse_into_datetime(mal["modified"], precision="millisecond") < stix2.utils.parse_into_datetime(mal_nc["modified"], precision="millisecond") def test_remove_custom_stix_object(): @stix2.CustomObject("x-animal", [ ("species", stix2.properties.StringProperty(required=True)), ("animal_class", stix2.properties.StringProperty()), ]) class Animal(object): def __init__(self, animal_class=None, **kwargs): if animal_class and animal_class not in ["mammal", "bird"]: raise ValueError("Not a recognized class of animal") animal = Animal(species="lion", animal_class="mammal") nc = stix2.utils.remove_custom_stix(animal) assert nc is None