Improve is_sdo() et al utility functions with respect to

dict/mapping values: do a simple verification of the value's
STIX version, not just its type.  Added a lot more unit tests to
test behavior on dicts.  To make the implementation work, I had
to move the detect_spec_version() function out of the parsing
module and into utils.  So that required small changes at all
its previous call sites.
pull/1/head
Michael Chisholm 2021-01-19 19:57:05 -05:00
parent f8c86f7352
commit fe2330af07
7 changed files with 417 additions and 121 deletions

View File

@ -4,7 +4,7 @@ import copy
from . import registry from . import registry
from .exceptions import ParseError from .exceptions import ParseError
from .utils import _get_dict from .utils import _get_dict, detect_spec_version
def parse(data, allow_custom=False, version=None): def parse(data, allow_custom=False, version=None):
@ -42,46 +42,6 @@ def parse(data, allow_custom=False, version=None):
return obj return obj
def _detect_spec_version(stix_dict):
"""
Given a dict representing a STIX object, try to detect what spec version
it is likely to comply with.
:param stix_dict: A dict with some STIX content. Must at least have a
"type" property.
:return: A STIX version in "X.Y" format
"""
obj_type = stix_dict["type"]
if 'spec_version' in stix_dict:
# For STIX 2.0, applies to bundles only.
# For STIX 2.1+, applies to SCOs, SDOs, SROs, and markings only.
v = stix_dict['spec_version']
elif "id" not in stix_dict:
# Only 2.0 SCOs don't have ID properties
v = "2.0"
elif obj_type == 'bundle':
# Bundle without a spec_version property: must be 2.1. But to
# future-proof, use max version over all contained SCOs, with 2.1
# minimum.
v = max(
"2.1",
max(
_detect_spec_version(obj) for obj in stix_dict["objects"]
),
)
elif obj_type in registry.STIX2_OBJ_MAPS["2.1"]["observables"]:
# Non-bundle object with an ID and without spec_version. Could be a
# 2.1 SCO or 2.0 SDO/SRO/marking. Check for 2.1 SCO...
v = "2.1"
else:
# Not a 2.1 SCO; must be a 2.0 object.
v = "2.0"
return v
def dict_to_stix2(stix_dict, allow_custom=False, version=None): def dict_to_stix2(stix_dict, allow_custom=False, version=None):
"""convert dictionary to full python-stix2 object """convert dictionary to full python-stix2 object
@ -115,7 +75,7 @@ def dict_to_stix2(stix_dict, allow_custom=False, version=None):
raise ParseError("Can't parse object with no 'type' property: %s" % str(stix_dict)) raise ParseError("Can't parse object with no 'type' property: %s" % str(stix_dict))
if not version: if not version:
version = _detect_spec_version(stix_dict) version = detect_spec_version(stix_dict)
OBJ_MAP = dict( OBJ_MAP = dict(
registry.STIX2_OBJ_MAPS[version]['objects'], registry.STIX2_OBJ_MAPS[version]['objects'],
@ -165,7 +125,7 @@ def parse_observable(data, _valid_refs=None, allow_custom=False, version=None):
obj['_valid_refs'] = _valid_refs or [] obj['_valid_refs'] = _valid_refs or []
if not version: if not version:
version = _detect_spec_version(obj) version = detect_spec_version(obj)
try: try:
OBJ_MAP_OBSERVABLE = registry.STIX2_OBJ_MAPS[version]['observables'] OBJ_MAP_OBSERVABLE = registry.STIX2_OBJ_MAPS[version]['observables']

View File

@ -2,7 +2,7 @@ from __future__ import unicode_literals
import pytest import pytest
from stix2.parsing import _detect_spec_version from stix2.utils import detect_spec_version
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -207,6 +207,6 @@ from stix2.parsing import _detect_spec_version
], ],
) )
def test_spec_version_detect(obj_dict, expected_ver): def test_spec_version_detect(obj_dict, expected_ver):
detected_ver = _detect_spec_version(obj_dict) detected_ver = detect_spec_version(obj_dict)
assert detected_ver == expected_ver assert detected_ver == expected_ver

View File

@ -30,11 +30,6 @@ def test_is_sdo(type_, stix_version):
id_ = type_ + "--a12fa04c-6586-4128-8d1a-cfe0d1c081f5" id_ = type_ + "--a12fa04c-6586-4128-8d1a-cfe0d1c081f5"
assert stix2.utils.is_sdo(id_, stix_version) assert stix2.utils.is_sdo(id_, stix_version)
d = {
"type": type_
}
assert stix2.utils.is_sdo(d, stix_version)
assert stix2.utils.is_stix_type( assert stix2.utils.is_stix_type(
type_, stix_version, stix2.utils.STIXTypeClass.SDO type_, stix_version, stix2.utils.STIXTypeClass.SDO
) )
@ -97,11 +92,6 @@ def test_is_sco(type_, stix_version):
id_ = type_ + "--a12fa04c-6586-4128-8d1a-cfe0d1c081f5" id_ = type_ + "--a12fa04c-6586-4128-8d1a-cfe0d1c081f5"
assert stix2.utils.is_sco(id_, stix_version) assert stix2.utils.is_sco(id_, stix_version)
d = {
"type": type_
}
assert stix2.utils.is_sco(d, stix_version)
assert stix2.utils.is_stix_type( assert stix2.utils.is_stix_type(
type_, stix_version, stix2.utils.STIXTypeClass.SCO type_, stix_version, stix2.utils.STIXTypeClass.SCO
) )
@ -147,11 +137,6 @@ def test_is_sro(type_, stix_version):
id_ = type_ + "--a12fa04c-6586-4128-8d1a-cfe0d1c081f5" id_ = type_ + "--a12fa04c-6586-4128-8d1a-cfe0d1c081f5"
assert stix2.utils.is_sro(id_, stix_version) assert stix2.utils.is_sro(id_, stix_version)
d = {
"type": type_
}
assert stix2.utils.is_sro(d, stix_version)
assert stix2.utils.is_stix_type( assert stix2.utils.is_stix_type(
type_, stix_version, stix2.utils.STIXTypeClass.SRO type_, stix_version, stix2.utils.STIXTypeClass.SRO
) )
@ -191,11 +176,6 @@ def test_is_marking(stix_version):
id_ = "marking-definition--a12fa04c-6586-4128-8d1a-cfe0d1c081f5" id_ = "marking-definition--a12fa04c-6586-4128-8d1a-cfe0d1c081f5"
assert stix2.utils.is_marking(id_, stix_version) assert stix2.utils.is_marking(id_, stix_version)
d = {
"type": "marking-definition"
}
assert stix2.utils.is_marking(d, stix_version)
assert stix2.utils.is_stix_type( assert stix2.utils.is_stix_type(
"marking-definition", stix_version, "marking-definition" "marking-definition", stix_version, "marking-definition"
) )
@ -244,11 +224,6 @@ def test_is_object(type_, stix_version):
id_ = type_ + "--a12fa04c-6586-4128-8d1a-cfe0d1c081f5" id_ = type_ + "--a12fa04c-6586-4128-8d1a-cfe0d1c081f5"
assert stix2.utils.is_object(id_, stix_version) assert stix2.utils.is_object(id_, stix_version)
d = {
"type": type_
}
assert stix2.utils.is_object(d, stix_version)
@pytest.mark.parametrize("stix_version", ["2.0", "2.1"]) @pytest.mark.parametrize("stix_version", ["2.0", "2.1"])
def test_is_not_object(stix_version): def test_is_not_object(stix_version):

View File

@ -237,3 +237,146 @@ def test_find_property_index(object, tuple_to_find, expected_index):
) )
def test_iterate_over_values(dict_value, tuple_to_find, expected_index): def test_iterate_over_values(dict_value, tuple_to_find, expected_index):
assert stix2.serialization._find_property_in_seq(dict_value.values(), *tuple_to_find) == expected_index assert stix2.serialization._find_property_in_seq(dict_value.values(), *tuple_to_find) == expected_index
@pytest.mark.parametrize(
"type_", [
"attack-pattern",
"campaign",
"course-of-action",
"identity",
"indicator",
"intrusion-set",
"malware",
"observed-data",
"report",
"threat-actor",
"tool",
"vulnerability"
]
)
def test_is_sdo_dict(type_):
d = {
"type": type_
}
assert stix2.utils.is_sdo(d, "2.0")
@pytest.mark.parametrize(
"dict_", [
{"type": "software", "spec_version": "2.1"},
{"type": "software"},
{"type": "identity", "spec_version": "2.1"},
{"type": "marking-definition", "spec_version": "2.1"},
{"type": "marking-definition"},
{"type": "bundle", "spec_version": "2.1"},
{"type": "bundle"},
{"type": "language-content", "spec_version": "2.1"},
{"type": "language-content"},
{"type": "relationship", "spec_version": "2.1"},
{"type": "relationship"},
{"type": "foo", "spec_version": "2.1"},
{"type": "foo"},
]
)
def test_is_not_sdo_dict(dict_):
assert not stix2.utils.is_sdo(dict_, "2.0")
def test_is_sco_dict():
d = {
"type": "file"
}
assert stix2.utils.is_sco(d, "2.0")
@pytest.mark.parametrize(
"dict_", [
{"type": "identity"},
{"type": "identity", "spec_version": "2.1"},
{"type": "software", "spec_version": "2.1"},
{"type": "marking-definition", "spec_version": "2.1"},
{"type": "marking-definition"},
{"type": "bundle", "spec_version": "2.1"},
{"type": "bundle"},
{"type": "language-content", "spec_version": "2.1"},
{"type": "language-content"},
{"type": "relationship", "spec_version": "2.1"},
{"type": "relationship"},
{"type": "foo", "spec_version": "2.1"},
{"type": "foo"},
]
)
def test_is_not_sco_dict(dict_):
assert not stix2.utils.is_sco(dict_, "2.0")
@pytest.mark.parametrize(
"dict_", [
{"type": "relationship"},
{"type": "sighting"},
]
)
def test_is_sro_dict(dict_):
assert stix2.utils.is_sro(dict_, "2.0")
@pytest.mark.parametrize(
"dict_", [
{"type": "identity", "spec_version": "2.1"},
{"type": "identity"},
{"type": "software", "spec_version": "2.1"},
{"type": "software"},
{"type": "marking-definition", "spec_version": "2.1"},
{"type": "marking-definition"},
{"type": "bundle", "spec_version": "2.1"},
{"type": "bundle"},
{"type": "language-content", "spec_version": "2.1"},
{"type": "language-content"},
{"type": "relationship", "spec_version": "2.1"},
{"type": "sighting", "spec_version": "2.1"},
{"type": "foo", "spec_version": "2.1"},
{"type": "foo"},
]
)
def test_is_not_sro_dict(dict_):
assert not stix2.utils.is_sro(dict_, "2.0")
@pytest.mark.parametrize(
"dict_", [
{"type": "identity"},
{"type": "software"},
{"type": "marking-definition"},
{
"type": "bundle",
"id": "bundle--8f431680-6278-4767-ba43-5edb682d7086",
"spec_version": "2.0",
"objects": [
{"type": "identity"},
{"type": "software"},
{"type": "marking-definition"},
]
},
]
)
def test_is_object_dict(dict_):
assert stix2.utils.is_object(dict_, "2.0")
@pytest.mark.parametrize(
"dict_", [
{"type": "identity", "spec_version": "2.1"},
{"type": "software", "spec_version": "2.1"},
{"type": "marking-definition", "spec_version": "2.1"},
{"type": "bundle", "spec_version": "2.1"},
{"type": "language-content", "spec_version": "2.1"},
{"type": "relationship", "spec_version": "2.1"},
{"type": "sighting", "spec_version": "2.1"},
{"type": "foo", "spec_version": "2.1"},
{"type": "foo"},
]
)
def test_is_not_object_dict(dict_):
assert not stix2.utils.is_object(dict_, "2.0")

View File

@ -243,9 +243,22 @@ def test_iterate_over_values(dict_value, tuple_to_find, expected_index):
assert stix2.serialization._find_property_in_seq(dict_value.values(), *tuple_to_find) == expected_index assert stix2.serialization._find_property_in_seq(dict_value.values(), *tuple_to_find) == expected_index
# Only 2.1-specific types/behaviors tested here.
@pytest.mark.parametrize( @pytest.mark.parametrize(
"type_", [ "type_", [
"attack-pattern",
"campaign",
"course-of-action",
"identity",
"indicator",
"intrusion-set",
"malware",
"observed-data",
"report",
"threat-actor",
"tool",
"vulnerability",
# New in 2.1
"grouping", "grouping",
"infrastructure", "infrastructure",
"location", "location",
@ -254,27 +267,127 @@ def test_iterate_over_values(dict_value, tuple_to_find, expected_index):
"opinion" "opinion"
] ]
) )
def test_is_sdo(type_): def test_is_sdo_dict(type_):
assert stix2.utils.is_sdo(type_, "2.1")
id_ = type_ + "--a12fa04c-6586-4128-8d1a-cfe0d1c081f5"
assert stix2.utils.is_sdo(id_, "2.1")
d = { d = {
"type": type_ "type": type_,
"spec_version": "2.1"
} }
assert stix2.utils.is_sdo(d, "2.1") assert stix2.utils.is_sdo(d, "2.1")
assert stix2.utils.is_stix_type(
type_, "2.1", stix2.utils.STIXTypeClass.SDO @pytest.mark.parametrize(
) "dict_", [
{"type": "software", "spec_version": "2.1"},
{"type": "software"},
{"type": "identity"},
{"type": "marking-definition", "spec_version": "2.1"},
{"type": "marking-definition"},
{"type": "bundle", "spec_version": "2.1"},
{"type": "bundle"},
{"type": "language-content", "spec_version": "2.1"},
{"type": "language-content"},
{"type": "relationship", "spec_version": "2.1"},
{"type": "relationship"},
{"type": "foo", "spec_version": "2.1"},
]
)
def test_is_not_sdo_dict(dict_):
assert not stix2.utils.is_sdo(dict_, "2.1")
def test_type_checks_language_content(): def test_is_sco_dict():
assert stix2.utils.is_object("language-content", "2.1") d = {
assert not stix2.utils.is_sdo("language-content", "2.1") "type": "file",
assert not stix2.utils.is_sco("language-content", "2.1") "spec_version": "2.1"
assert not stix2.utils.is_sro("language-content", "2.1") }
assert stix2.utils.is_stix_type(
"language-content", "2.1", "language-content" assert stix2.utils.is_sco(d, "2.1")
)
@pytest.mark.parametrize(
"dict_", [
{"type": "identity"},
{"type": "identity", "spec_version": "2.1"},
{"type": "software"},
{"type": "marking-definition", "spec_version": "2.1"},
{"type": "marking-definition"},
{"type": "bundle", "spec_version": "2.1"},
{"type": "bundle"},
{"type": "language-content", "spec_version": "2.1"},
{"type": "language-content"},
{"type": "relationship", "spec_version": "2.1"},
{"type": "relationship"},
{"type": "foo", "spec_version": "2.1"},
]
)
def test_is_not_sco_dict(dict_):
assert not stix2.utils.is_sco(dict_, "2.1")
@pytest.mark.parametrize(
"dict_", [
{"type": "relationship", "spec_version": "2.1"},
{"type": "sighting", "spec_version": "2.1"},
]
)
def test_is_sro_dict(dict_):
assert stix2.utils.is_sro(dict_, "2.1")
@pytest.mark.parametrize(
"dict_", [
{"type": "identity", "spec_version": "2.1"},
{"type": "identity"},
{"type": "software", "spec_version": "2.1"},
{"type": "software"},
{"type": "marking-definition", "spec_version": "2.1"},
{"type": "marking-definition"},
{"type": "bundle", "spec_version": "2.1"},
{"type": "bundle"},
{"type": "language-content", "spec_version": "2.1"},
{"type": "language-content"},
{"type": "relationship"},
{"type": "sighting"},
{"type": "foo", "spec_version": "2.1"},
]
)
def test_is_not_sro_dict(dict_):
assert not stix2.utils.is_sro(dict_, "2.1")
@pytest.mark.parametrize(
"dict_", [
{"type": "identity", "spec_version": "2.1"},
{"type": "software", "spec_version": "2.1"},
{"type": "marking-definition", "spec_version": "2.1"},
{"type": "language-content", "spec_version": "2.1"},
{
"type": "bundle",
"id": "bundle--8f431680-6278-4767-ba43-5edb682d7086",
"objects": [
{"type": "identity", "spec_version": "2.1"},
{"type": "software", "spec_version": "2.1"},
{"type": "marking-definition", "spec_version": "2.1"},
{"type": "language-content", "spec_version": "2.1"},
]
},
]
)
def test_is_object_dict(dict_):
assert stix2.utils.is_object(dict_, "2.1")
@pytest.mark.parametrize(
"dict_", [
{"type": "identity"},
{"type": "software"},
{"type": "marking-definition"},
{"type": "bundle"},
{"type": "language-content"},
{"type": "relationship"},
{"type": "sighting"},
{"type": "foo"},
]
)
def test_is_not_object_dict(dict_):
assert not stix2.utils.is_object(dict_, "2.1")

View File

@ -1,5 +1,6 @@
"""Utility functions and classes for the STIX2 library.""" """Utility functions and classes for the STIX2 library."""
import collections.abc
import datetime as dt import datetime as dt
import enum import enum
import json import json
@ -318,6 +319,46 @@ def get_type_from_id(stix_id):
return stix_id.split('--', 1)[0] return stix_id.split('--', 1)[0]
def detect_spec_version(stix_dict):
"""
Given a dict representing a STIX object, try to detect what spec version
it is likely to comply with.
:param stix_dict: A dict with some STIX content. Must at least have a
"type" property.
:return: A STIX version in "X.Y" format
"""
obj_type = stix_dict["type"]
if 'spec_version' in stix_dict:
# For STIX 2.0, applies to bundles only.
# For STIX 2.1+, applies to SCOs, SDOs, SROs, and markings only.
v = stix_dict['spec_version']
elif "id" not in stix_dict:
# Only 2.0 SCOs don't have ID properties
v = "2.0"
elif obj_type == 'bundle':
# Bundle without a spec_version property: must be 2.1. But to
# future-proof, use max version over all contained SCOs, with 2.1
# minimum.
v = max(
"2.1",
max(
detect_spec_version(obj) for obj in stix_dict["objects"]
),
)
elif obj_type in mappings.STIX2_OBJ_MAPS["2.1"]["observables"]:
# Non-bundle object with an ID and without spec_version. Could be a
# 2.1 SCO or 2.0 SDO/SRO/marking. Check for 2.1 SCO...
v = "2.1"
else:
# Not a 2.1 SCO; must be a 2.0 object.
v = "2.0"
return v
def _stix_type_of(value): def _stix_type_of(value):
""" """
Get a STIX type from the given value: if a STIX ID is passed, the type Get a STIX type from the given value: if a STIX ID is passed, the type
@ -342,55 +383,93 @@ def _stix_type_of(value):
def is_sdo(value, stix_version=stix2.version.DEFAULT_VERSION): def is_sdo(value, stix_version=stix2.version.DEFAULT_VERSION):
""" """
Determine whether the given object, type, or ID is/is for an SDO. Determine whether the given object, type, or ID is/is for an SDO of the
given STIX version. If value is a type or ID, this just checks whether
the type was registered as an SDO in the given STIX version. If a mapping,
*simple* STIX version inference is additionally done on the value, and the
result is checked against stix_version. It does not attempt to fully
validate the value.
:param value: A mapping with a "type" property, or a STIX ID or type :param value: A mapping with a "type" property, or a STIX ID or type
as a string as a string
:param stix_version: A STIX version as a string :param stix_version: A STIX version as a string
:return: True if the type of the given value is an SDO type; False :return: True if the type of the given value is an SDO type of the given
if not version; False if not
""" """
cls_maps = mappings.STIX2_OBJ_MAPS[stix_version]
type_ = _stix_type_of(value) result = True
result = type_ in cls_maps["objects"] and type_ not in { if isinstance(value, collections.abc.Mapping):
"relationship", "sighting", "marking-definition", "bundle", value_stix_version = detect_spec_version(value)
"language-content" if value_stix_version != stix_version:
} result = False
if result:
cls_maps = mappings.STIX2_OBJ_MAPS[stix_version]
type_ = _stix_type_of(value)
result = type_ in cls_maps["objects"] and type_ not in {
"relationship", "sighting", "marking-definition", "bundle",
"language-content"
}
return result return result
def is_sco(value, stix_version=stix2.version.DEFAULT_VERSION): def is_sco(value, stix_version=stix2.version.DEFAULT_VERSION):
""" """
Determine whether the given object, type, or ID is/is for an SCO. Determine whether the given object, type, or ID is/is for an SCO of the
given STIX version. If value is a type or ID, this just checks whether
the type was registered as an SCO in the given STIX version. If a mapping,
*simple* STIX version inference is additionally done on the value, and the
result is checked against stix_version. It does not attempt to fully
validate the value.
:param value: A mapping with a "type" property, or a STIX ID or type :param value: A mapping with a "type" property, or a STIX ID or type
as a string as a string
:param stix_version: A STIX version as a string :param stix_version: A STIX version as a string
:return: True if the type of the given value is an SCO type; False :return: True if the type of the given value is an SCO type of the given
if not version; False if not
""" """
cls_maps = mappings.STIX2_OBJ_MAPS[stix_version]
type_ = _stix_type_of(value) result = True
result = type_ in cls_maps["observables"] if isinstance(value, collections.abc.Mapping):
value_stix_version = detect_spec_version(value)
if value_stix_version != stix_version:
result = False
if result:
cls_maps = mappings.STIX2_OBJ_MAPS[stix_version]
type_ = _stix_type_of(value)
result = type_ in cls_maps["observables"]
return result return result
def is_sro(value, stix_version=stix2.version.DEFAULT_VERSION): def is_sro(value, stix_version=stix2.version.DEFAULT_VERSION):
""" """
Determine whether the given object, type, or ID is/is for an SRO. Determine whether the given object, type, or ID is/is for an SRO of the
given STIX version. If value is a type or ID, this just checks whether
the type was registered as an SRO in the given STIX version. If a mapping,
*simple* STIX version inference is additionally done on the value, and the
result is checked against stix_version. It does not attempt to fully
validate the value.
:param value: A mapping with a "type" property, or a STIX ID or type :param value: A mapping with a "type" property, or a STIX ID or type
as a string as a string
:param stix_version: A STIX version as a string :param stix_version: A STIX version as a string
:return: True if the type of the given value is an SRO type; False :return: True if the type of the given value is an SRO type of the given
if not version; False if not
""" """
# No STIX version dependence here yet... result = True
type_ = _stix_type_of(value) if isinstance(value, collections.abc.Mapping):
result = type_ in ("sighting", "relationship") value_stix_version = detect_spec_version(value)
if value_stix_version != stix_version:
result = False
if result:
# No need to check registration in this case
type_ = _stix_type_of(value)
result = type_ in ("sighting", "relationship")
return result return result
@ -398,7 +477,11 @@ def is_sro(value, stix_version=stix2.version.DEFAULT_VERSION):
def is_object(value, stix_version=stix2.version.DEFAULT_VERSION): def is_object(value, stix_version=stix2.version.DEFAULT_VERSION):
""" """
Determine whether an object, type, or ID is/is for any STIX object. This Determine whether an object, type, or ID is/is for any STIX object. This
includes all SDOs, SCOs, meta-objects, and bundle. includes all SDOs, SCOs, meta-objects, and bundle. If value is a type or
ID, this just checks whether the type was registered in the given STIX
version. If a mapping, *simple* STIX version inference is additionally
done on the value, and the result is checked against stix_version. It does
not attempt to fully validate the value.
:param value: A mapping with a "type" property, or a STIX ID or type :param value: A mapping with a "type" property, or a STIX ID or type
as a string as a string
@ -406,24 +489,46 @@ def is_object(value, stix_version=stix2.version.DEFAULT_VERSION):
:return: True if the type of the given value is a valid STIX type with :return: True if the type of the given value is a valid STIX type with
respect to the given STIX version; False if not respect to the given STIX version; False if not
""" """
cls_maps = mappings.STIX2_OBJ_MAPS[stix_version]
type_ = _stix_type_of(value) result = True
result = type_ in cls_maps["observables"] or type_ in cls_maps["objects"] if isinstance(value, collections.abc.Mapping):
value_stix_version = detect_spec_version(value)
if value_stix_version != stix_version:
result = False
if result:
cls_maps = mappings.STIX2_OBJ_MAPS[stix_version]
type_ = _stix_type_of(value)
result = type_ in cls_maps["observables"] \
or type_ in cls_maps["objects"]
return result return result
def is_marking(value, stix_version=stix2.version.DEFAULT_VERSION): def is_marking(value, stix_version=stix2.version.DEFAULT_VERSION):
"""Determines whether the given value is/is for a marking definition. """
Determine whether the given object, type, or ID is/is for an marking
definition of the given STIX version. If value is a type or ID, this just
checks whether the type was registered as an SDO in the given STIX version.
If a mapping, *simple* STIX version inference is additionally done on the
value, and the result is checked against stix_version. It does not attempt
to fully validate the value.
:param value: A STIX object, object ID, or type as a string. :param value: A STIX object, object ID, or type as a string.
:param stix_version: A STIX version as a string :param stix_version: A STIX version as a string
:return: True if the value is/is for a marking definition, False otherwise. :return: True if the value is/is for a marking definition, False otherwise.
""" """
# No STIX version dependence here yet... result = True
type_ = _stix_type_of(value) if isinstance(value, collections.abc.Mapping):
result = type_ == "marking-definition" value_stix_version = detect_spec_version(value)
if value_stix_version != stix_version:
result = False
if result:
# No need to check registration in this case
type_ = _stix_type_of(value)
result = type_ == "marking-definition"
return result return result
@ -464,7 +569,7 @@ def is_stix_type(value, stix_version=stix2.version.DEFAULT_VERSION, *types):
# Assume a string STIX type is given instead of a class enum, # Assume a string STIX type is given instead of a class enum,
# and just check for exact match. # and just check for exact match.
obj_type = _stix_type_of(value) obj_type = _stix_type_of(value)
result = is_object(obj_type, stix_version) and obj_type == type_ result = obj_type == type_ and is_object(value, stix_version)
if result: if result:
break break

View File

@ -10,7 +10,7 @@ from six.moves.collections_abc import Mapping
import stix2.base import stix2.base
import stix2.registry import stix2.registry
from stix2.utils import get_timestamp, parse_into_datetime from stix2.utils import get_timestamp, parse_into_datetime, detect_spec_version
import stix2.v20 import stix2.v20
from .exceptions import ( from .exceptions import (
@ -87,7 +87,7 @@ def _is_versionable(data):
# (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):
stix_version = stix2.parsing._detect_spec_version(data) stix_version = detect_spec_version(data)
is_21 = stix_version != "2.0" is_21 = stix_version != "2.0"
# Then, determine versionability. # Then, determine versionability.