Add a new_version() restriction preventing SCO ID contributing
properties from being changed, if a UUIDv5 is in use. Changing one of those properties would imply an ID change, which is not allowed across versions. Also: - add a trailing comma - change unchangable_properties to a set instead of a list, in case there are dupe props between STIX_UNMOD_PROPERTIES and sco_locked_propspull/1/head
parent
eaa7f17ee4
commit
9c5f950d5b
|
@ -335,10 +335,10 @@ def test_version_sco_with_custom():
|
||||||
)
|
)
|
||||||
|
|
||||||
new_file_sco_obj = stix2.versioning.new_version(
|
new_file_sco_obj = stix2.versioning.new_version(
|
||||||
file_sco_obj, name="newname.txt",
|
file_sco_obj, size=1234,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert new_file_sco_obj.name == "newname.txt"
|
assert new_file_sco_obj.size == 1234
|
||||||
|
|
||||||
revoked_obj = stix2.versioning.revoke(new_file_sco_obj)
|
revoked_obj = stix2.versioning.revoke(new_file_sco_obj)
|
||||||
assert revoked_obj.revoked
|
assert revoked_obj.revoked
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import copy
|
import copy
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
import itertools
|
||||||
|
import uuid
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from six.moves.collections_abc import Mapping
|
from six.moves.collections_abc import Mapping
|
||||||
|
|
||||||
import stix2.base
|
import stix2.base
|
||||||
from stix2.utils import get_timestamp, parse_into_datetime
|
from stix2.utils import get_timestamp, parse_into_datetime
|
||||||
import stix2.v20.base
|
import stix2.v20
|
||||||
|
|
||||||
from .exceptions import (
|
from .exceptions import (
|
||||||
InvalidValueError, RevokeError, UnmodifiablePropertyError,
|
InvalidValueError, RevokeError, UnmodifiablePropertyError,
|
||||||
|
@ -78,7 +80,7 @@ def _is_versionable(data):
|
||||||
# work for dicts.
|
# work for dicts.
|
||||||
is_21 = False
|
is_21 = False
|
||||||
if isinstance(data, stix2.base._STIXBase) and \
|
if isinstance(data, stix2.base._STIXBase) and \
|
||||||
not isinstance(data, stix2.v20.base._STIXBase20):
|
not isinstance(data, stix2.v20._STIXBase20):
|
||||||
# (is_21 means 2.1 or later; try not to be 2.1-specific)
|
# (is_21 means 2.1 or later; try not to be 2.1-specific)
|
||||||
is_21 = True
|
is_21 = True
|
||||||
elif isinstance(data, dict):
|
elif isinstance(data, dict):
|
||||||
|
@ -117,7 +119,7 @@ def _is_versionable(data):
|
||||||
# but do check SCOs
|
# but do check SCOs
|
||||||
cls = class_maps["observables"][obj_type]
|
cls = class_maps["observables"][obj_type]
|
||||||
is_versionable = _VERSIONING_PROPERTIES.issubset(
|
is_versionable = _VERSIONING_PROPERTIES.issubset(
|
||||||
cls._properties
|
cls._properties,
|
||||||
)
|
)
|
||||||
|
|
||||||
return is_versionable, is_21
|
return is_versionable, is_21
|
||||||
|
@ -147,7 +149,6 @@ def new_version(data, allow_custom=None, **kwargs):
|
||||||
"Try a dictionary or instance of an SDO or SRO class.",
|
"Try a dictionary or instance of an SDO or SRO class.",
|
||||||
)
|
)
|
||||||
|
|
||||||
unchangable_properties = []
|
|
||||||
if data.get('revoked'):
|
if data.get('revoked'):
|
||||||
raise RevokeError("new_version")
|
raise RevokeError("new_version")
|
||||||
try:
|
try:
|
||||||
|
@ -157,9 +158,20 @@ def new_version(data, allow_custom=None, **kwargs):
|
||||||
properties_to_change = kwargs.keys()
|
properties_to_change = kwargs.keys()
|
||||||
|
|
||||||
# Make sure certain properties aren't trying to change
|
# Make sure certain properties aren't trying to change
|
||||||
for prop in STIX_UNMOD_PROPERTIES:
|
# ID contributing properties of 2.1+ SCOs may also not change if a UUIDv5
|
||||||
|
# is in use (depending on whether they were used to create it... but they
|
||||||
|
# probably were). That would imply an ID change, which is not allowed
|
||||||
|
# across versions.
|
||||||
|
sco_locked_props = []
|
||||||
|
if is_21 and isinstance(data, stix2.base._Observable):
|
||||||
|
uuid_ = uuid.UUID(data["id"][-36:])
|
||||||
|
if uuid_.variant == uuid.RFC_4122 and uuid_.version == 5:
|
||||||
|
sco_locked_props = data._id_contributing_properties
|
||||||
|
|
||||||
|
unchangable_properties = set()
|
||||||
|
for prop in itertools.chain(STIX_UNMOD_PROPERTIES, sco_locked_props):
|
||||||
if prop in properties_to_change:
|
if prop in properties_to_change:
|
||||||
unchangable_properties.append(prop)
|
unchangable_properties.add(prop)
|
||||||
if unchangable_properties:
|
if unchangable_properties:
|
||||||
raise UnmodifiablePropertyError(unchangable_properties)
|
raise UnmodifiablePropertyError(unchangable_properties)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue