diff --git a/.travis.yml b/.travis.yml index f55fcac7..e99e66dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,16 @@ +os: linux language: python +cache: pip +dist: xenial python: - "2.7" - "3.4" - "3.5" - "3.6" + - "3.7" + - "3.8" install: + - pip install -U pip setuptools - pip install tox-travis script: - tox diff --git a/CHANGES.txt b/CHANGES.txt index 4ff6a402..b22812d1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,27 @@ +Version 1.2.0.11 +2020-11-16 +- #367 Fix deprecation warning from collections module + +Version 1.2.0.10 +2020-05-01 +- #366 Check add_reference methods to prevent NoneType has no attribute 'append' +- Changes to STIXPackage to prevent the empty tag from appearing in serialization + +Version 1.2.0.9 +2020-04-16 +- #364 TTPs would fail to serialize XML Kill_Chains if no TTP was set +- Added Python 3.8 to test harness + +Version 1.2.0.8 +2020-03-09 +- #357 Add xnl:Type to the PersonName element (CIQ) +- Update the allowable values for PersonName and OrganisationName +- Update tests per recent CybOX release + +Version 1.2.0.7 +2019-09-06 +- Update package requirements + Version 1.2.0.6 2018-04-25 - #347 Property targeted_technical_details missing in VictimTargeting class @@ -36,6 +60,65 @@ Version 1.2.0.2 - #302 Correctly handle CIQ Identity objects. - Add additional fields to CIQ Identity object. +Version 1.1.1.19 +2020-11-16 +- #367 Fix deprecation warning from collections module + +Version 1.1.1.18 +2020-05-01 +- #366 Check add_reference methods to prevent NoneType has no attribute 'append' +- Changes to STIXPackage to prevent the empty tag from appearing in serialization + +Version 1.1.1.17 +2020-04-21 +- #365 AISMarkingStructure not serializing correctly. PY3 compatibility issue + +Version 1.1.1.16 +2020-04-16 +- #364 TTPs would fail to serialize XML Kill_Chains if no TTP was set +- Added Python 3.8 to test harness + +Version 1.1.1.15 +2020-03-09 +- #357 Add xnl:Type to the PersonName element (CIQ) +- Update the allowable values for PersonName and OrganisationName +- Update tests per recent CybOX release + +Version 1.1.1.14 +2019-11-26 +- #361 fix problem with lookup_extension for TestMechanism + +Version 1.1.1.13 +2019-09-06 +- Update package requirements + +Version 1.1.1.12 +2018-04-25 +- #347 Property targeted_technical_details missing in VictimTargeting class + +Version 1.1.1.11 +2018-03-21 +- Fix issue with PyPI. + +Version 1.1.1.10 +2018-03-06 +- #343 Create separate test environments for when maec is or is not installed +- #339 [Python3 not supported - v1.1/1.2] to_json vaguely broken on some files +- #338 VocabTypes are unhashable in 1.1.1.x on Python 3 +- #325 Bug in Indicator class setter for observables property +- Change stix.ToolInformation to use AttackerToolType vocab +- Removed Python 2.6 environment from CI tests + +Version 1.1.1.9 +2017-05-25 +- Support Python 3.6 +- #321 Fix CIQ extensions. + +Version 1.1.1.8 +2017-01-18 +- Update to support Python 3. +- Convert to use mixbox. + Version 1.1.1.7 2016-10-21 - Improved handling of Industry Sectors in CIQ Identity for AIS Markings diff --git a/README.rst b/README.rst index 6df07068..e934477c 100644 --- a/README.rst +++ b/README.rst @@ -1,11 +1,12 @@ python-stix =========== -A python library for parsing, manipulating, and generating STIX v1.2 content. +A python library for parsing, manipulating, and generating `Structured Threat Information eXpression (STIX™) `_ v1.2.0 content. :Source: https://github.com/STIXProject/python-stix -:Documentation: http://stix.readthedocs.org +:Documentation: https://stix.readthedocs.io/ :Information: https://stixproject.github.io/ +:Download: https://pypi.python.org/pypi/stix/ |travis_badge| |landscape_io_badge| |version_badge| @@ -24,7 +25,7 @@ Installation ------------ The python-stix library is hosted on `PyPI -`_ and the most recent stable version can be +`_ and the most recent stable version can be installed with `pip `_: :: @@ -60,13 +61,13 @@ Installation on Windows ~~~~~~~~~~~~~~~~~~~~~~~ Download the Lxml wheel for your version of Python from -http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml, then install it via "pip install +http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml, then install it via "pip install .whl". For example, to install it on 64-bit Windows running Python 2.7: :: - > pip install lxml-3.6.1-cp27-cp27m-win_amd64.whl - > pip install stix + $ pip install lxml-3.6.1-cp27-cp27m-win_amd64.whl + $ pip install stix Versioning ---------- diff --git a/docs/api/indicator/indicator.rst b/docs/api/indicator/indicator.rst index 0b299213..ccc78ff9 100644 --- a/docs/api/indicator/indicator.rst +++ b/docs/api/indicator/indicator.rst @@ -29,7 +29,7 @@ Classes add_short_description, add_test_mechanism, add_valid_time_position, alternative_id, confidence, description, descriptions, find, get_produced_time, get_received_time, handling, id_, idref, - indicated_ttps, indicator_types, information_source, kill_chain_phases, + indicated_ttps, indicator_types, kill_chain_phases, likely_impact, negate, observable, observable_composition_operator, observables, producer, related_campaigns, related_indicators, related_packages, set_produced_time, set_producer_identity, diff --git a/docs/index.rst b/docs/index.rst index 9d61b71d..a650a9fb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,17 +22,17 @@ version of STIX. ============ =================== STIX Version python-stix Version ============ =================== -1.2 1.2.0.6 (`PyPI`__) (`GitHub`__) -1.1.1 1.1.1.12 (`PyPI`__) (`GitHub`__) +1.2 1.2.0.11 (`PyPI`__) (`GitHub`__) +1.1.1 1.1.1.18 (`PyPI`__) (`GitHub`__) 1.1.0 1.1.0.6 (`PyPI`__) (`GitHub`__) 1.0.1 1.0.1.1 (`PyPI`__) (`GitHub`__) 1.0 1.0.0a7 (`PyPI`__) (`GitHub`__) ============ =================== -__ https://pypi.python.org/pypi/stix/1.2.0.6 -__ https://github.com/STIXProject/python-stix/tree/v1.2.0.6 -__ https://pypi.python.org/pypi/stix/1.1.1.12 -__ https://github.com/STIXProject/python-stix/tree/v1.1.1.12 +__ https://pypi.python.org/pypi/stix/1.2.0.11 +__ https://github.com/STIXProject/python-stix/tree/v1.2.0.11 +__ https://pypi.python.org/pypi/stix/1.1.1.18 +__ https://github.com/STIXProject/python-stix/tree/v1.1.1.18 __ https://pypi.python.org/pypi/stix/1.1.0.6 __ https://github.com/STIXProject/python-stix/tree/v1.1.0.6 __ https://pypi.python.org/pypi/stix/1.0.1.1 diff --git a/examples/vuln_affected_software.py b/examples/vuln_affected_software.py index 3f1d5532..620087c0 100644 --- a/examples/vuln_affected_software.py +++ b/examples/vuln_affected_software.py @@ -28,7 +28,7 @@ # Wrap the Product Object in an Observable instance observable = Observable(software) -# Attach the Product observable to the affected_sofware list of +# Attach the Product observable to the affected_software list of # RelatedObservable instances. This wraps our Observable in a # RelatedObservable layer. vuln = Vulnerability() diff --git a/setup.cfg b/setup.cfg index 2be68365..e383efad 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,17 @@ +[bumpversion] +current_version = 1.2.0.11 +parse = (?P\d+)\.(?P\d+)\.(?P\d+).(?P\d+) +serialize = {major}.{minor}.{patch}.{revision} +commit = True +tag = True + +[bumpversion:file:stix/version.py] + +[bumpversion:file:docs/index.rst] + +[metadata] +license_file = LICENSE.txt + [bdist_wheel] universal = True + diff --git a/setup.py b/setup.py index fb657a39..717d437d 100644 --- a/setup.py +++ b/setup.py @@ -3,6 +3,7 @@ # Copyright (c) 2017, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from io import open # Allow `encoding` kwarg on Python 2.7 from os.path import abspath, dirname, join @@ -11,6 +12,7 @@ BASE_DIR = dirname(abspath(__file__)) VERSION_FILE = join(BASE_DIR, 'stix', 'version.py') + def get_version(): with open(VERSION_FILE) as f: for line in f.readlines(): @@ -20,15 +22,17 @@ def get_version(): raise AttributeError("Package does not have a __version__") -with open('README.rst') as f: - readme = f.read() +def get_long_description(): + with open('README.rst', encoding='utf-8') as f: + return f.read() install_requires = [ - 'lxml>=2.3', - 'python-dateutil', + 'lxml>=2.2.3 ; python_version == "2.7" or python_version >= "3.5"', + 'lxml>=2.2.3,<4.4.0 ; python_version > "2.7" and python_version < "3.5"', + 'mixbox>=1.0.4', 'cybox>=2.1.0.13,<2.1.1.0', - 'mixbox>=1.0.2', + 'python-dateutil', ] @@ -38,10 +42,11 @@ def get_version(): author="STIX Project, MITRE Corporation", author_email="stix@mitre.org", description="An API for parsing and generating STIX content.", - long_description=readme, - url="http://stix.mitre.org", + long_description=get_long_description(), + url="https://stixproject.github.io/", packages=find_packages(), install_requires=install_requires, + license="BSD", classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', @@ -50,9 +55,15 @@ def get_version(): 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', - ] + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + ], + project_urls={ + 'Documentation': 'https://stix.readthedocs.io/', + 'Source Code': 'https://github.com/STIXProject/python-stix/', + 'Bug Tracker': 'https://github.com/STIXProject/python-stix/issues/', + }, ) diff --git a/stix/base.py b/stix/base.py index 14f50a6d..b5c417ee 100644 --- a/stix/base.py +++ b/stix/base.py @@ -8,6 +8,7 @@ from sys import version_info # mixbox +from mixbox import compat from mixbox import idgen from mixbox import entities from mixbox import fields @@ -311,7 +312,7 @@ def istypeof(cls, obj): return isinstance(obj, cls) -class TypedList(TypedCollection, collections.MutableSequence): +class TypedList(TypedCollection, compat.MutableSequence): def __init__(self, *args): TypedCollection.__init__(self, *args) diff --git a/stix/bindings/stix_common.py b/stix/bindings/stix_common.py index 64cfac7c..8ae31be4 100644 --- a/stix/bindings/stix_common.py +++ b/stix/bindings/stix_common.py @@ -3495,7 +3495,7 @@ class StructuredTextType(GeneratedsSuper): Note that if the markup tags used by this format would be interpreted as XML information (such as the bracket-based tags of HTML) the text area should be enclosed in a CDATA section to prevent the markup from - interferring with XMLvalidation of the CybOX document. If this + interfering with XMLvalidation of the CybOX document. If this attribute is absent, the implication is that no markup is being used.""" subclass = None superclass = None diff --git a/stix/common/information_source.py b/stix/common/information_source.py index f7042f27..ef718ba7 100644 --- a/stix/common/information_source.py +++ b/stix/common/information_source.py @@ -22,7 +22,7 @@ class InformationSource(stix.Entity): _binding_class = stix_common_binding.InformationSourceType _namespace = 'http://stix.mitre.org/common-1' - identity = fields.TypedField("Identity", type_=Identity, factory=IdentityFactory) + identity = fields.TypedField("Identity", Identity, factory=IdentityFactory) descriptions = fields.TypedField("Description", StructuredTextList) contributing_sources = fields.TypedField("Contributing_Sources", type_="stix.common.information_source.ContributingSources") time = fields.TypedField("Time", cybox.common.Time) @@ -39,15 +39,15 @@ def __init__(self, description=None, identity=None, time=None, tools=None, contr self.time = time self.tools = tools self.references = references - #self.roles = None def add_contributing_source(self, value): self.contributing_sources.append(value) - def add_reference(self, value): if not value: return + if self.references is None: + self.references = References() # TODO: Check if it's a valid URI? self.references.append(value) @@ -80,7 +80,6 @@ def add_description(self, description): """ self.descriptions.add(description) - def add_role(self, value): self.roles.append(value) @@ -95,4 +94,3 @@ class ContributingSources(stix.EntityList): @classmethod def _dict_as_list(cls): return False - \ No newline at end of file diff --git a/stix/common/profiles.py b/stix/common/profiles.py index a4499d01..1e523b6c 100644 --- a/stix/common/profiles.py +++ b/stix/common/profiles.py @@ -3,13 +3,14 @@ import collections +from mixbox import compat from mixbox import fields import stix from stix.bindings import stix_common as stix_common_binding -class Profiles(collections.MutableSequence, stix.Entity): +class Profiles(compat.MutableSequence, stix.Entity): _binding = stix_common_binding _binding_class = stix_common_binding.ProfilesType _namespace = 'http://stix.mitre.org/common-1' diff --git a/stix/common/references.py b/stix/common/references.py index 45bfbb27..f5c7dc1e 100644 --- a/stix/common/references.py +++ b/stix/common/references.py @@ -2,13 +2,14 @@ # See LICENSE.txt for complete terms. import collections +from mixbox import compat from mixbox import fields import stix from stix.bindings import stix_common as stix_common_binding -class References(collections.MutableSequence, stix.Entity): +class References(compat.MutableSequence, stix.Entity): _binding = stix_common_binding _binding_class = stix_common_binding.ReferencesType _namespace = 'http://stix.mitre.org/common-1' diff --git a/stix/common/structured_text.py b/stix/common/structured_text.py index b90c0396..9dc73b47 100644 --- a/stix/common/structured_text.py +++ b/stix/common/structured_text.py @@ -10,7 +10,13 @@ import stix import stix.utils as utils import stix.bindings.stix_common as stix_common_binding -from mixbox.vendor.six import text_type +from mixbox.vendor.six import PY2, PY3, text_type + + +if PY2: + from collections import Sequence +elif PY3: + from collections.abc import Sequence #: Default ordinality value for StructuredText. @@ -58,7 +64,7 @@ def to_dict(self): """Converts this object into a dictionary representation. Note: - If no properies or attributes are set other than ``value``, + If no properties or attributes are set other than ``value``, this will return a string. """ @@ -89,7 +95,7 @@ def _unset_default(text): """Unsets the ordinality of the StructuredText object `text` if the ordinality is equal to the DEFAULT_ORDINALITY. - The ordinaity will be returned to its original state after exiting the + The ordinality will be returned to its original state after exiting the context manager. """ @@ -105,7 +111,7 @@ def _unset_default(text): text.ordinality = ordinality -class StructuredTextList(stix.TypedCollection, collections.Sequence): +class StructuredTextList(stix.TypedCollection, Sequence): """A sequence type used to store StructureText objects. Args: diff --git a/stix/core/stix_package.py b/stix/core/stix_package.py index 14e9fbb1..da2d5643 100644 --- a/stix/core/stix_package.py +++ b/stix/core/stix_package.py @@ -104,7 +104,7 @@ def __init__(self, id_=None, idref=None, timestamp=None, stix_header=None, self.indicators = indicators or Indicators() self.incidents = incidents or Incidents() self.threat_actors = threat_actors or ThreatActors() - self.ttps = ttps or TTPs() + self.ttps = ttps self.related_packages = related_packages self.reports = reports or Reports() self.timestamp = timestamp @@ -180,7 +180,7 @@ def add_ttp(self, ttp): """ if self.ttps is None: self.ttps = TTPs() - self.ttps.append(ttp) + self.ttps.ttp.append(ttp) def add_report(self, report): """Adds a :class:`.Report` object to the :attr:`reports` collection. diff --git a/stix/core/ttps.py b/stix/core/ttps.py index d5620509..9f1cb265 100644 --- a/stix/core/ttps.py +++ b/stix/core/ttps.py @@ -5,12 +5,10 @@ from functools import partial # mixbox -from mixbox import entities from mixbox import fields # stix import stix -from stix import utils from stix.ttp import TTP from stix.common.kill_chains import KillChains from stix.bindings import stix_core as core_binding @@ -19,24 +17,23 @@ from stix.utils.deprecated import IdrefDeprecatedList -class TTPs(stix.EntityList): +class TTPs(stix.Entity): _binding = core_binding _binding_class = _binding.TTPsType _namespace = 'http://stix.mitre.org/stix-1' - ttps = fields.TypedField( - name="TTP", - type_=TTP, - multiple=True, - key_name="ttps", - listfunc=partial(IdrefDeprecatedList, type=TTP) - ) - + ttp = fields.TypedField("TTP", TTP, multiple=True, key_name="ttps", listfunc=partial(IdrefDeprecatedList, type=TTP)) kill_chains = fields.TypedField("Kill_Chains", KillChains) def __init__(self, ttps=None): - super(TTPs, self).__init__(ttps) + super(TTPs, self).__init__() + self.ttp = ttps self.kill_chains = KillChains() def add_ttp(self, ttp): - self.append(ttp) + self.ttp.append(ttp) + + def add_kill_chain(self, kc): + if self.kill_chains is None: + self.kill_chains = KillChains() + self.kill_chains.kill_chain.append(kc) diff --git a/stix/exploit_target/vulnerability.py b/stix/exploit_target/vulnerability.py index 78e0974a..814c2f19 100644 --- a/stix/exploit_target/vulnerability.py +++ b/stix/exploit_target/vulnerability.py @@ -4,13 +4,10 @@ from mixbox import fields import stix -import stix.utils as utils import stix.bindings.exploit_target as exploit_target_binding -import stix.bindings.stix_common as stix_common_binding -from stix.common import DateTimeWithPrecision, StructuredTextList +from stix.common import DateTimeWithPrecision, References, StructuredTextList from stix.common.related import GenericRelationshipList, RelatedObservable -from mixbox import entities, fields -from stix.common import References + class Vulnerability(stix.Entity): """Implementation of STIX ``Vulnerability``. @@ -44,7 +41,6 @@ def __init__(self, title=None, description=None, short_description=None): self.title = title self.descriptions = StructuredTextList(description) self.short_descriptions = StructuredTextList(short_description) - self.references = [] @property def description(self): @@ -102,9 +98,11 @@ def add_short_description(self, description): def add_reference(self, reference): if not reference: return - + if self.references is None: + self.references = References() self.references.append(reference) + class CVSSVector(stix.Entity): """ Common Vulnerabilit Scoring System object, representing its component measures diff --git a/stix/extensions/identity/ciq_identity_3_0.py b/stix/extensions/identity/ciq_identity_3_0.py index a2d89f08..2010ac8f 100644 --- a/stix/extensions/identity/ciq_identity_3_0.py +++ b/stix/extensions/identity/ciq_identity_3_0.py @@ -1028,13 +1028,53 @@ class PersonName(stix.Entity): _namespace = XML_NS_XNL XML_TAG = "{%s}PersonName" % _namespace - def __init__(self, name_elements=None): + TYPE_ALIAS = 'Alias' + TYPE_LEGAL_NAME = 'LegalName' + TYPE_KNOWN_AS = 'KnownAs' + TYPE_MAIDEN_NAME = 'MaidenName' + TYPE_FORMER_NAME = 'FormerName' + TYPE_COMMON_USE = 'CommonUse' + TYPE_NAME_AT_BIRTH = 'NameAtBirth' + TYPE_PREFERRED_NAME = 'PreferredName' + TYPE_OFFICIAL_NAME = 'OfficialName' + TYPE_UNOFFICIAL_NAME = 'UnofficialName' + TYPE_NICK_NAME = 'NickName' + TYPE_PET_NAME = 'PetName' + + TYPES = ( + TYPE_ALIAS, + TYPE_LEGAL_NAME, + TYPE_KNOWN_AS, + TYPE_MAIDEN_NAME, + TYPE_FORMER_NAME, + TYPE_COMMON_USE, + TYPE_NAME_AT_BIRTH, + TYPE_PREFERRED_NAME, + TYPE_OFFICIAL_NAME, + TYPE_UNOFFICIAL_NAME, + TYPE_NICK_NAME, + TYPE_PET_NAME, + ) + + def __init__(self, name_elements=None, type_=None): self.name_elements = [] + self.type_ = type_ if name_elements: for name_element in name_elements: self.add_name_element(name_element) + @property + def type_(self): + return self._type + + @type_.setter + def type_(self, value): + if value and value not in self.TYPES: + raise ValueError('value must be one of %s: ' % (self.TYPES,)) + + self._type = value + def add_name_element(self, value): if isinstance(value, string_types): self.name_elements.append(PersonNameElement(value=value)) @@ -1050,6 +1090,9 @@ def to_obj(self, return_obj=None, ns_info=None): root_tag = PersonName.XML_TAG return_obj = et.Element(root_tag) + if self.type_: + return_obj.attrib['{%s}Type' % XML_NS_XNL] = self.type_ + for name_element in self.name_elements: return_obj.append(name_element.to_obj(ns_info=ns_info)) @@ -1063,6 +1106,8 @@ def from_obj(cls, obj, return_obj=None): if not return_obj: return_obj = cls() + return_obj.type_ = obj.attrib.get('{%s}Type' % XML_NS_XNL) + name_elements = obj.findall(PersonNameElement.XML_TAG) if name_elements: for name_element_obj in name_elements: @@ -1074,6 +1119,9 @@ def from_obj(cls, obj, return_obj=None): def to_dict(self): d = {} + if self.type_: + d['type'] = self.type_ + if self.name_elements: for ne in self.name_elements: d.setdefault('name_elements', []).append(ne.to_dict()) @@ -1088,6 +1136,8 @@ def from_dict(cls, dict_repr, return_obj=None): if not return_obj: return_obj = cls() + return_obj.type_ = dict_repr.get('type') + ne_dicts = dict_repr.get('name_elements', []) for ne_dict in ne_dicts: @@ -1100,11 +1150,40 @@ class OrganisationName(stix.Entity): _namespace = XML_NS_XNL XML_TAG = "{%s}OrganisationName" % _namespace + TYPE_LEGAL_NAME = 'LegalName' + TYPE_FORMER_NAME = 'FormerName' + TYPE_COMMON_USE = 'CommonUse' + TYPE_PUBLISHING_NAME = 'PublishingName' + TYPE_OFFICIAL_NAME = 'OfficialName' + TYPE_UNOFFICIAL_NAME = 'UnofficialName' + TYPE_UNDEFINED = 'Undefined' + + TYPES = ( + TYPE_LEGAL_NAME, + TYPE_FORMER_NAME, + TYPE_COMMON_USE, + TYPE_PUBLISHING_NAME, + TYPE_OFFICIAL_NAME, + TYPE_UNOFFICIAL_NAME, + TYPE_UNDEFINED, + ) + def __init__(self, name_elements=None, subdivision_names=None, type_=None): self.type_ = type_ self.name_elements = name_elements self.subdivision_names = subdivision_names + @property + def type_(self): + return self._type + + @type_.setter + def type_(self, value): + if value and value not in self.TYPES: + raise ValueError('value must be one of %s: ' % (self.TYPES,)) + + self._type = value + @property def name_elements(self): return self._name_elements @@ -1235,9 +1314,6 @@ def value(self): @value.setter def value(self, value): - # if not value: - # raise ValueError('value cannot be None') - self._value = value @classmethod @@ -1270,8 +1346,8 @@ class PersonNameElement(_BaseNameElement): _namespace = XML_NS_XNL XML_TAG = "{%s}NameElement" % _namespace - TYPE_TITLE = 'Title' TYPE_PRECEDING_TITLE = 'PrecedingTitle' + TYPE_TITLE = 'Title' TYPE_FIRST_NAME = 'FirstName' TYPE_MIDDLE_NAME = 'MiddleName' TYPE_LAST_NAME = 'LastName' diff --git a/stix/indicator/indicator.py b/stix/indicator/indicator.py index 5842209f..fc941045 100644 --- a/stix/indicator/indicator.py +++ b/stix/indicator/indicator.py @@ -470,7 +470,7 @@ def add_related_indicator(self, indicator): ``related_indicators`` list property. Calling this method is the same as calling ``append()`` on the - ``related_indicators`` proeprty. + ``related_indicators`` property. See Also: The :class:`RelatedIndicators` documentation. diff --git a/stix/test/coa_test.py b/stix/test/coa_test.py index 9a3c0e17..05d2b905 100644 --- a/stix/test/coa_test.py +++ b/stix/test/coa_test.py @@ -51,9 +51,9 @@ class COATests(EntityTestCase, unittest.TestCase): }, 'objective': ObjectiveTests._full_dict, 'parameter_observables': { - 'major_version': 2, - 'minor_version': 1, - 'update_version': 0, + 'cybox_major_version': '2', + 'cybox_minor_version': '1', + 'cybox_update_version': '0', 'observables': [ { 'idref': "example:Observable-1" diff --git a/stix/test/core/stix_package_test.py b/stix/test/core/stix_package_test.py index 47e31db4..0ce35e13 100644 --- a/stix/test/core/stix_package_test.py +++ b/stix/test/core/stix_package_test.py @@ -101,9 +101,9 @@ class STIXPackageTests(EntityTestCase, unittest.TestCase): 'incidents': IncidentsTests._full_dict, 'indicators': IndicatorsTests._full_dict, 'observables': { - 'major_version': 2, - 'minor_version': 1, - 'update_version': 0, + 'cybox_major_version': '2', + 'cybox_minor_version': '1', + 'cybox_update_version': '0', 'observables': [ { 'idref': "example:Observable-1" diff --git a/stix/test/extensions/identity/ciq_identity_3_0_test.py b/stix/test/extensions/identity/ciq_identity_3_0_test.py index 20d7d101..542371e9 100644 --- a/stix/test/extensions/identity/ciq_identity_3_0_test.py +++ b/stix/test/extensions/identity/ciq_identity_3_0_test.py @@ -40,8 +40,16 @@ class CIQIdentity3_0InstanceTests(EntityTestCase, unittest.TestCase): ], 'person_names': [ { + 'type': 'LegalName', 'name_elements': [ - {'value': 'John Smith'} + { + 'element_type': 'FirstName', + 'value': 'John', + }, + { + 'element_type': 'LastName', + 'value': 'Smith', + } ] }, { diff --git a/stix/test/extensions/malware/maec_4_1_malware_test.py b/stix/test/extensions/malware/maec_4_1_malware_test.py index 1829246c..30257c3c 100644 --- a/stix/test/extensions/malware/maec_4_1_malware_test.py +++ b/stix/test/extensions/malware/maec_4_1_malware_test.py @@ -134,14 +134,14 @@ def test_parse_malware(self): """Test parsing a normal MalwareInstance from XML """ stix_pkg = STIXPackage.from_xml(self.XML) - mw = stix_pkg.ttps[0].behavior.malware_instances[0].to_dict() + mw = stix_pkg.ttps.ttp[0].behavior.malware_instances[0].to_dict() self.assertTrue('names' in mw) def test_parse_malware_maec(self): """Test parsing a MaecInstance from XML """ stix_pkg = STIXPackage.from_xml(self.XML_MAEC) - mw = stix_pkg.ttps[0].behavior.malware_instances[0].to_dict() + mw = stix_pkg.ttps.ttp[0].behavior.malware_instances[0].to_dict() self.assertTrue('names' in mw) diff --git a/stix/test/report_test.py b/stix/test/report_test.py index f99ecb7f..2ce43055 100644 --- a/stix/test/report_test.py +++ b/stix/test/report_test.py @@ -102,9 +102,9 @@ class ReportTests(EntityTestCase, unittest.TestCase): 'incidents': IncidentsTests._full_dict, 'indicators': IndicatorsTests._full_dict, 'observables': { - 'major_version': 2, - 'minor_version': 1, - 'update_version': 0, + 'cybox_major_version': '2', + 'cybox_minor_version': '1', + 'cybox_update_version': '0', 'observables': [ { 'idref': "example:Observable-1" diff --git a/stix/test/ttp_test.py b/stix/test/ttp_test.py index ef43689f..c7c4a339 100644 --- a/stix/test/ttp_test.py +++ b/stix/test/ttp_test.py @@ -52,9 +52,9 @@ class InfrastructureTests(EntityTestCase, unittest.TestCase): 'short_description': 'Short Description', 'types': ['foo', 'bar'], 'observable_characterization': { - 'major_version': 2, - 'minor_version': 1, - 'update_version': 0, + 'cybox_major_version': '2', + 'cybox_minor_version': '1', + 'cybox_update_version': '0', 'observables': [ { 'idref': "example:Observable-1" @@ -188,9 +188,9 @@ class VictimTargetingTests(EntityTestCase, unittest.TestCase): } ], 'targeted_technical_details': { - 'major_version': 2, - 'minor_version': 1, - 'update_version': 0, + 'cybox_major_version': '2', + 'cybox_minor_version': '1', + 'cybox_update_version': '0', 'observables': [ { 'idref': "example:Observable-2" diff --git a/stix/version.py b/stix/version.py index e146c343..a2a3a195 100644 --- a/stix/version.py +++ b/stix/version.py @@ -1,4 +1,4 @@ # Copyright (c) 2017, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -__version__ = "1.2.0.6" +__version__ = "1.2.0.11" diff --git a/tox.ini b/tox.ini index 8067c9d6..275e8bdf 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,11 @@ [tox] -envlist = py27, py34, py35, py36, lxml23, no-maec +envlist = py27, py34, py35, py36, py37, py38, lxml23, docs, no-maec, packaging [testenv] commands = nosetests stix # NOTE: python-stix does not have any doctests # sphinx-build -b doctest docs docs/_build/doctest - sphinx-build -b html docs docs/_build/html deps = -rrequirements.txt @@ -30,9 +29,21 @@ deps = nose==1.3.7 tox==2.7.0 +[testenv:docs] +commands = + sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html + +[testenv:packaging] +deps = + readme_renderer +commands = + python setup.py check -r -s + [travis] python = - 2.7: py27, docs, lxml23, no-maec + 2.7: py27, docs, lxml23, no-maec, packaging 3.4: py34, no-maec 3.5: py35, no-maec - 3.6: py36, no-maec + 3.6: py36, no-maec, packaging + 3.7: py37, no-maec + 3.8: py38, no-maec