Merge branch 'master' of github.com:oasis-open/cti-python-stix2 into main

pull/1/head
chrisr3d 2020-06-30 11:28:21 +02:00
commit fc270711a8
5 changed files with 128 additions and 50 deletions

View File

@ -3,8 +3,6 @@ language: python
cache: pip
dist: xenial
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"
- "3.7"
@ -13,9 +11,9 @@ install:
- pip install -U pip setuptools
- pip install tox-travis
- pip install codecov
- if [[ $TRAVIS_PYTHON_VERSION != 3.4 ]]; then pip install pre-commit; fi
- pip install pre-commit
script:
- tox
- if [[ $TRAVIS_PYTHON_VERSION != 3.4 ]]; then pre-commit run --all-files; fi
- pre-commit run --all-files
after_success:
- codecov

View File

@ -39,10 +39,7 @@ setup(
'Intended Audience :: Developers',
'Topic :: Security',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
@ -51,7 +48,6 @@ setup(
keywords='stix stix2 json cti cyber threat intelligence',
packages=find_packages(exclude=['*.test', '*.test.*']),
install_requires=[
'enum34 ; python_version<"3.4"',
'pytz',
'requests',
'simplejson',

View File

@ -198,15 +198,29 @@ class ListProperty(Property):
def __init__(self, contained, **kwargs):
"""
``contained`` should be a function which returns an object from the value.
``contained`` should be a Property class or instance, or a _STIXBase
subclass.
"""
if inspect.isclass(contained) and issubclass(contained, Property):
# If it's a class and not an instance, instantiate it so that
# clean() can be called on it, and ListProperty.clean() will
# use __call__ when it appends the item.
self.contained = contained()
else:
self.contained = None
if inspect.isclass(contained):
# Property classes are instantiated; _STIXBase subclasses are left
# as-is.
if issubclass(contained, Property):
self.contained = contained()
elif issubclass(contained, _STIXBase):
self.contained = contained
elif isinstance(contained, Property):
self.contained = contained
if not self.contained:
raise TypeError(
"Invalid list element type: {}".format(
str(contained),
),
)
super(ListProperty, self).__init__(**kwargs)
def clean(self, value):
@ -218,40 +232,28 @@ class ListProperty(Property):
if isinstance(value, (_STIXBase, string_types)):
value = [value]
result = []
for item in value:
try:
valid = self.contained.clean(item)
except ValueError:
raise
except AttributeError:
# type of list has no clean() function (eg. built in Python types)
# TODO Should we raise an error here?
valid = item
if isinstance(self.contained, Property):
result = [
self.contained.clean(item)
for item in value
]
if type(self.contained) is EmbeddedObjectProperty:
obj_type = self.contained.type
elif type(self.contained).__name__ == "STIXObjectProperty":
# ^ this way of checking doesn't require a circular import
# valid is already an instance of a python-stix2 class; no need
# to turn it into a dictionary and then pass it to the class
# constructor again
result.append(valid)
continue
elif type(self.contained) is DictionaryProperty:
obj_type = dict
else:
obj_type = self.contained
else: # self.contained must be a _STIXBase subclass
result = []
for item in value:
if isinstance(item, self.contained):
valid = item
elif isinstance(item, Mapping):
# attempt a mapping-like usage...
valid = self.contained(**item)
if isinstance(valid, Mapping):
try:
valid._allow_custom
except AttributeError:
result.append(obj_type(**valid))
else:
result.append(obj_type(allow_custom=True, **valid))
else:
result.append(obj_type(valid))
raise ValueError("Can't create a {} out of {}".format(
self.contained._type, str(item),
))
result.append(valid)
# STIX spec forbids empty lists
if len(result) < 1:

View File

@ -3,8 +3,10 @@ import uuid
import pytest
import stix2
import stix2.base
from stix2.exceptions import (
AtLeastOnePropertyError, CustomContentError, DictionaryKeyError,
ExtraPropertiesError,
)
from stix2.properties import (
BinaryProperty, BooleanProperty, DictionaryProperty,
@ -66,7 +68,7 @@ def test_fixed_property():
assert p.clean(p.default())
def test_list_property():
def test_list_property_property_type():
p = ListProperty(StringProperty)
assert p.clean(['abc', 'xyz'])
@ -74,6 +76,88 @@ def test_list_property():
p.clean([])
def test_list_property_property_type_custom():
class TestObj(stix2.base._STIXBase):
_type = "test"
_properties = {
"foo": StringProperty(),
}
p = ListProperty(EmbeddedObjectProperty(type=TestObj))
objs_custom = [
TestObj(foo="abc", bar=123, allow_custom=True),
TestObj(foo="xyz"),
]
assert p.clean(objs_custom)
dicts_custom = [
{"foo": "abc", "bar": 123},
{"foo": "xyz"},
]
# no opportunity to set allow_custom=True when using dicts
with pytest.raises(ExtraPropertiesError):
p.clean(dicts_custom)
def test_list_property_object_type():
class TestObj(stix2.base._STIXBase):
_type = "test"
_properties = {
"foo": StringProperty(),
}
p = ListProperty(TestObj)
objs = [TestObj(foo="abc"), TestObj(foo="xyz")]
assert p.clean(objs)
dicts = [{"foo": "abc"}, {"foo": "xyz"}]
assert p.clean(dicts)
def test_list_property_object_type_custom():
class TestObj(stix2.base._STIXBase):
_type = "test"
_properties = {
"foo": StringProperty(),
}
p = ListProperty(TestObj)
objs_custom = [
TestObj(foo="abc", bar=123, allow_custom=True),
TestObj(foo="xyz"),
]
assert p.clean(objs_custom)
dicts_custom = [
{"foo": "abc", "bar": 123},
{"foo": "xyz"},
]
# no opportunity to set allow_custom=True when using dicts
with pytest.raises(ExtraPropertiesError):
p.clean(dicts_custom)
def test_list_property_bad_element_type():
with pytest.raises(TypeError):
ListProperty(1)
def test_list_property_bad_value_type():
class TestObj(stix2.base._STIXBase):
_type = "test"
_properties = {
"foo": StringProperty(),
}
list_prop = ListProperty(TestObj)
with pytest.raises(ValueError):
list_prop.clean([1])
def test_string_property():
prop = StringProperty()

View File

@ -1,5 +1,5 @@
[tox]
envlist = py27,py34,py35,py36,py37,py38,style,isort-check,packaging
envlist = py35,py36,py37,py38,style,isort-check,packaging
[testenv]
deps =
@ -42,8 +42,6 @@ commands =
[travis]
python =
2.7: py27, style
3.4: py34
3.5: py35
3.6: py36
3.7: py37