diff --git a/.gitignore b/.gitignore index 267b66db..fda5964e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ # pycharm .idea/ .idea +.pytest_cache/ # python setuptools build/ diff --git a/.travis.yml b/.travis.yml index 3a162b37..ab509e99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,19 @@ +os: linux language: python -env: - - TOXENV=py26 - - TOXENV=py27 - - TOXENV=lxml23 - +cache: pip +dist: xenial +python: + - "2.7" + - "3.4" + - "3.5" + - "3.6" + - "3.7" + - "3.8" install: - - pip install tox - + - pip install -U pip setuptools + - pip install tox-travis script: - tox - -branches: - only: - - master - notifications: email: - stix-commits-list@lists.mitre.org diff --git a/CHANGES.txt b/CHANGES.txt index 1f6d6478..cf2d2bc9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,80 @@ +Version 1.1.1.19 +2020-11-16 +- #327 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 + +Version 1.1.1.6 +2016-10-14 +- Add support for AIS Markings in STIX 1.1.1 +- Support additional fields in CIQ Identity + +Version 1.1.1.5 +2015-04-29 +- #200 Refactored ns_dict behavior in to_xml() to allow custom namespace mappings +- #254 Added information_source to MarkingSpecification class. +- #188 Added nationalities to CIQ Identity extension. +- #246 Added coa_requested to Incident class. +- #248 Set include_schemalocs to False by default in to_xml() +- #249 Removed "Work In Progress" label from RTD documentation. + Version 1.1.1.4 2015-03-19 - #44 Added StructuredCOA support. This completes the COA structure. @@ -91,7 +168,7 @@ Version 1.1.0.4 2014-02-31 - Added COAs, Exploit Targets, Campaigns, and Related Packages to STIXPackage class - Updated behavior of id, idref, and timestamp on core STIX constructs to align with best practices -- Added MAEC malware extension +- Added MAEC malware extension - Improved Identity extension mechanism - Added Sightings to Indicator - Updates to OpenIOC test mechanism extension @@ -101,7 +178,7 @@ Version 1.1.0.4 - Added Handling support to Indicator - Added several fields to COA and Exploit Target - Bug fixes - + Version 1.1.0.3 2014-02-24 - Added TTP structure (stix.ttp.TTP) @@ -131,7 +208,7 @@ Version 1.1.0.1 - Fixed several CIQ Identity extension bugs - Improved namespace parser performance - Updated Incident test -- Added initial code for better support of non-standard id namespaces in input documents +- Added initial code for better support of non-standard id namespaces in input documents Version 1.1.0.0 2014-02-20 @@ -191,7 +268,7 @@ Version 1.0.0a5 2013-06-27 - Improved README documentation - Fixed interface issues between python-stix and python-cybox classes -- Added support for additional namespaces to be added to STIXPackage.to_xml() +- Added support for additional namespaces to be added to STIXPackage.to_xml() - Fixed installation issues with pip and setup.py Version 1.0.0a4 @@ -215,4 +292,4 @@ Version 1.0.0a2 Version 1.0.0a1 2013-04-22 - First (alpha) release on PyPI -- Compatible with STIX 1.0 Draft 2 \ No newline at end of file +- Compatible with STIX 1.0 Draft 2 diff --git a/README.rst b/README.rst index 97329b69..08211d6f 100644 --- a/README.rst +++ b/README.rst @@ -1,40 +1,44 @@ python-stix =========== -A python library for parsing, manipulating, and generating STIX v1.1.1 content. +A python library for parsing, manipulating, and generating `Structured Threat Information eXpression (STIX™) `_ v1.1.1 content. :Source: https://github.com/STIXProject/python-stix -:Documentation: http://stix.readthedocs.org -:Information: http://stix.mitre.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| |downloads badge| +|travis_badge| |landscape_io_badge| |version_badge| -.. |travis badge| image:: https://api.travis-ci.org/STIXProject/python-stix.png?branch=master +.. |travis_badge| image:: https://api.travis-ci.org/STIXProject/python-stix.svg?branch=master :target: https://travis-ci.org/STIXProject/python-stix :alt: Build Status -.. |landscape.io badge| image:: https://landscape.io/github/STIXProject/python-stix/master/landscape.png +.. |landscape_io_badge| image:: https://landscape.io/github/STIXProject/python-stix/master/landscape.svg :target: https://landscape.io/github/STIXProject/python-stix/master :alt: Code Health -.. |version badge| image:: https://pypip.in/v/stix/badge.png - :target: https://pypi.python.org/pypi/stix/ -.. |downloads badge| image:: https://pypip.in/d/stix/badge.png +.. |version_badge| image:: https://img.shields.io/pypi/v/stix.svg?maxAge=3600 :target: https://pypi.python.org/pypi/stix/ + :alt: Version Installation ------------ -The python-stix library can be installed via the distutils setup.py script -included at the root directory: +The python-stix library is hosted on `PyPI +`_ and the most recent stable version can be +installed with `pip `_: - $ python setup.py install - -The python-stix library is also hosted on `PyPI -`_ and can be installed with `pip -`_: +:: $ pip install stix +The python-stix library can also be installed via the distutils setup.py script +included at the root directory: + +:: + + $ python setup.py install + Dependencies ------------ @@ -53,6 +57,18 @@ Installation on Ubuntu 14.04 (and older) $ sudo apt-get install python-dev python-pip libxml2-dev libxslt-dev zlib1g-dev $ sudo pip install stix +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 +.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 + Versioning ---------- @@ -67,36 +83,36 @@ Layout The python-stix package layout is as follows: -* ``stix/`` : root level package +* ``stix/`` : root level package. -* ``examples/`` : example python scripts that leverage the python-stix library +* ``examples/`` : example python scripts that leverage the python-stix library. * ``stix/utils/`` : utility classes and modules used internally by the python-stix - library + library. * ``stix/bindings/`` : generateDS generated xml-to-python bindings (leveraged for - parsing and output of STIX XML content) - -* ``stix/campaign/`` : APIs for STIX Campaign constructs + parsing and output of STIX XML content). + +* ``stix/campaign/`` : APIs for STIX Campaign constructs. -* ``stix/coa/`` : APIs for STIX Course Of Action constructs +* ``stix/coa/`` : APIs for STIX Course Of Action constructs. -* ``stix/core/`` : APIs for core STIX constructs (e.g., STIX Header, STIX Package) +* ``stix/core/`` : APIs for core STIX constructs (e.g., STIX Header, STIX Package). * ``stix/common/`` : APIs for common STIX constructs (e.g., Structured Text, - Information Source) + Information Source). -* ``stix/exploit_target/`` : APIs for STIX Exploit Target constructs +* ``stix/exploit_target/`` : APIs for STIX Exploit Target constructs. -* ``stix/incident/`` : APIs for common Incident constructs +* ``stix/incident/`` : APIs for common Incident constructs. -* ``stix/indicator/`` : APIs for STIX Indicator constructs +* ``stix/indicator/`` : APIs for STIX Indicator constructs. -* ``stix/extensions/`` : APIs for STIX extensions (e.g., CIQ Identity) +* ``stix/extensions/`` : APIs for STIX extensions (e.g., CIQ Identity). -* ``stix/threat_actor/`` : APIs for STIX Threat Actor constructs +* ``stix/threat_actor/`` : APIs for STIX Threat Actor constructs. -* ``stix/ttp/`` : APIs for STIX TTP constructs +* ``stix/ttp/`` : APIs for STIX TTP constructs. Please refer to examples for concrete examples of how to interact with the -python-stix library +python-stix library. diff --git a/docs/api/common/identity.rst b/docs/api/common/identity.rst index b1dcb283..d00db56f 100644 --- a/docs/api/common/identity.rst +++ b/docs/api/common/identity.rst @@ -13,13 +13,8 @@ Classes .. autoclass:: RelatedIdentities :show-inheritance: :members: - + Functions --------- .. autofunction:: add_extension - -Constants ---------- - -.. autodata:: _EXTENSION_MAP diff --git a/docs/api/common/vocabs.rst b/docs/api/common/vocabs.rst index 3dce840b..6af0120f 100644 --- a/docs/api/common/vocabs.rst +++ b/docs/api/common/vocabs.rst @@ -138,8 +138,4 @@ Functions --------- .. autofunction:: add_vocab - -Constants ---------- - -.. autodata:: _VOCAB_MAP +.. autofunction:: register_vocab diff --git a/docs/api/data_marking.rst b/docs/api/data_marking.rst index 37f96699..a4612d17 100644 --- a/docs/api/data_marking.rst +++ b/docs/api/data_marking.rst @@ -22,8 +22,3 @@ Functions --------- .. autofunction:: add_extension - -Constants ---------- - -.. autodata:: _EXTENSION_MAP diff --git a/docs/api/extensions/marking/ais.rst b/docs/api/extensions/marking/ais.rst new file mode 100644 index 00000000..6ebceee6 --- /dev/null +++ b/docs/api/extensions/marking/ais.rst @@ -0,0 +1,297 @@ +:mod:`stix.extensions.marking.ais` Module +==================================================== + +.. automodule:: stix.extensions.marking.ais + +Classes +------- + +.. autoclass:: AISMarkingStructure + :show-inheritance: + :members: + +Functions +--------- + +.. autofunction:: add_ais_marking + + +Examples +-------- + +Applying AIS Markings +--------------------- + +The STIX specification allows data markings to be applied to any combination of +attributes and elements that can be described by XPath. That being said, the +Automated Indicator Sharing (AIS) capability requires those markings controlled +structure to select all nodes and attributes ``//node() | //@*``. All required +fields to create a valid AIS Markings are provided through the ``add_ais_marking`` +function. + +.. code-block:: python + + # python-stix imports + import stix + from stix.core import STIXPackage + from stix.extensions.marking.ais import (add_ais_marking, + COMMUNICATIONS_SECTOR, + INFORMATION_TECHNOLOGY_SECTOR) + from stix.indicator import Indicator + + # Create new STIX Package + stix_package = STIXPackage() + + # Create new Indicator + indicator = Indicator(title='My Indicator Example', + description='Example using AIS') + + # Add indicator to our STIX Package + stix_package.add_indicator(indicator) + + # Create AIS Marking with CIQ Identity and attach it to STIX Header. + add_ais_marking(stix_package, False, 'EVERYONE', 'GREEN', + country_name_code='US', + country_name_code_type='ISO 3166-1 alpha-2', + admin_area_name_code='US-VA', + admin_area_name_code_type='ISO 3166-2', + organisation_name='Example Corporation', + industry_type=[INFORMATION_TECHNOLOGY_SECTOR, COMMUNICATIONS_SECTOR] + ) + + # Print the XML. + print stix_package.to_xml() + + # Print the JSON. + print stix_package.to_json() + +This corresponds to the XML result: + +.. code-block:: xml + + + + + + //node() | //@* + + + + + + + + + \ + + + Example Corporation + + + + + + + + + + + + + + + + + + + + + + My Indicator Example + Example using AIS + + + + +The following corresponds to the JSON result: + +.. code-block:: json + + { + "stix_header": { + "handling": [ + { + "controlled_structure": "//node() | //@*", + "information_source": { + "identity": { + "xsi:type": "stix-ciqidentity:CIQIdentity3.0InstanceType", + "specification": { + "organisation_info": { + "industry_type": "Information Technology Sector|Communications Sector" + }, + "party_name": { + "organisation_names": [ + { + "name_elements": [ + { + "value": "Example Corporation" + } + ] + } + ] + }, + "addresses": [ + { + "country": { + "name_elements": [ + { + "name_code_type": "ISO 3166-1 alpha-2", + "name_code": "US" + } + ] + }, + "administrative_area": { + "name_elements": [ + { + "name_code_type": "ISO 3166-2", + "name_code": "US-VA" + } + ] + } + } + ] + } + } + }, + "marking_structures": [ + { + "xsi:type": "AIS:AISMarkingStructure", + "not_proprietary": { + "tlp_marking": { + "color": "GREEN" + }, + "ais_consent": { + "consent": "EVERYONE" + }, + "cisa_proprietary": "false" + } + } + ] + } + ] + }, + "version": "1.1.1", + "indicators": [ + { + "description": "Example using AIS", + "title": "My Indicator Example", + "timestamp": "2017-10-02T14:26:57.510000+00:00", + "id": "example:indicator-81466b8d-4efb-460f-ba13-b072420b9540" + } + ], + "id": "example:Package-a8c8135d-18d8-4384-903f-71285a02346e" + } + +Parsing AIS Markings +-------------------- + +Using the same example used for Applying AIS Markings. This would be how a +consumer of AIS would parse the data. + +.. code-block:: python + + # python-stix imports + import stix + from stix.core import STIXPackage + import stix.extensions.marking.ais # Register the AIS markings + + # Parse STIX Package + stix_package = STIXPackage.from_xml("stix_input.xml") + # stix_package = STIXPackage.from_json("stix_input.json") + + # Print all indicators + for indicator in stix_package.indicators: + print(indicator) + + # Extract markings from STIX Header + markings = stix_package.stix_header.handling + + # Print all markings contained in the STIX Header + for marking in markings: + print(marking) + print(marking.marking_structures) + print("----------MARKING CONTENT----------") + ais_struct = marking.marking_structures[0] + print("OBJ: %s" % ais_struct) + print("NotProprietary OBJ: %s" % ais_struct.not_proprietary) + print("CISA_Proprietary: %s" % ais_struct.not_proprietary.cisa_proprietary) + print("Consent: %s" % ais_struct.not_proprietary.ais_consent.consent) + print("TLP color: %s" % ais_struct.not_proprietary.tlp_marking.color) + + print("----------INFORMATION SOURCE----------") + identity = marking.information_source.identity.specification + print("OBJ: %s" % identity) + print("Organization Name: %s" % identity.party_name.organisation_names[0].name_elements[0].value) + print("Country: %s" % identity.addresses[0].country.name_elements[0].name_code) + print("Country code type: %s" % identity.addresses[0].country.name_elements[0].name_code_type) + print("Administrative area: %s" % identity.addresses[0].administrative_area.name_elements[0].name_code) + print("Administrative area code type: %s" % identity.addresses[0].administrative_area.name_elements[0].name_code_type) + print("Industry Type: %s" % identity.organisation_info.industry_type) + + + >>> + >>> + >>> [, ...] + >>> ----------MARKING CONTENT---------- + >>> OBJ: + >>> NotProprietary OBJ: + >>> CISA_Proprietary: False + >>> Consent: EVERYONE + >>> TLP color: GREEN + >>> ----------INFORMATION SOURCE---------- + >>> OBJ: + >>> Organization Name: Example Corporation + >>> Country: US + >>> Country code type: ISO 3166-1 alpha-2 + >>> Administrative area: US-VA + >>> Administrative area code type: ISO 3166-2 + >>> Industry Type: Information Technology Sector|Communications Sector + +Constants +--------- + +The following constants can be used for the ``industry_type`` keyword argument to +``add_ais_marking``: + +.. autodata:: CHEMICAL_SECTOR +.. autodata:: COMMERCIAL_FACILITIES_SECTOR +.. autodata:: COMMUNICATIONS_SECTOR +.. autodata:: CRITICAL_MANUFACTURING_SECTOR +.. autodata:: DAMS_SECTOR +.. autodata:: DEFENSE_INDUSTRIAL_BASE_SECTOR +.. autodata:: EMERGENCY_SERVICES_SECTOR +.. autodata:: ENERGY_SECTOR +.. autodata:: FINANCIAL_SERVICES_SECTOR +.. autodata:: FOOD_AND_AGRICULTURE_SECTOR +.. autodata:: GOVERNMENT_FACILITIES_SECTOR +.. autodata:: HEALTH_CARE_AND_PUBLIC_HEALTH_SECTOR +.. autodata:: INFORMATION_TECHNOLOGY_SECTOR +.. autodata:: NUCLEAR_REACTORS_MATERIALS_AND_WASTE_SECTOR +.. autodata:: OTHER +.. autodata:: TRANSPORTATION_SYSTEMS_SECTOR +.. autodata:: WATER_AND_WASTEWATER_SYSTEMS_SECTOR diff --git a/docs/api/index.rst b/docs/api/index.rst index 71e5c6ab..7537d708 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -16,6 +16,7 @@ Modules located in the base `stix`_ package .. toctree:: :titlesonly: + stix base data_marking diff --git a/docs/api/indicator/test_mechanism.rst b/docs/api/indicator/test_mechanism.rst index 9267a96d..239dd25f 100644 --- a/docs/api/indicator/test_mechanism.rst +++ b/docs/api/indicator/test_mechanism.rst @@ -14,8 +14,3 @@ Functions --------- .. autofunction:: add_extension - -Constants ---------- - -.. autodata:: _EXTENSION_MAP diff --git a/docs/api/stix.rst b/docs/api/stix.rst new file mode 100644 index 00000000..8249b60b --- /dev/null +++ b/docs/api/stix.rst @@ -0,0 +1,12 @@ +:mod:`stix` Module +================================== + +.. module:: stix + +Classes +------- + +.. autofunction:: supported_stix_version +.. autofunction:: register_extension +.. autofunction:: lookup_extension +.. autofunction:: add_extension diff --git a/docs/api/ttp/malware_instance.rst b/docs/api/ttp/malware_instance.rst index 19786fe2..669e2d80 100644 --- a/docs/api/ttp/malware_instance.rst +++ b/docs/api/ttp/malware_instance.rst @@ -14,8 +14,3 @@ Functions --------- .. autofunction:: add_extension - -Constants ---------- - -.. autodata:: _EXTENSION_MAP diff --git a/docs/api/utils/nsparser.rst b/docs/api/utils/nsparser.rst index a39c808f..00180ed2 100644 --- a/docs/api/utils/nsparser.rst +++ b/docs/api/utils/nsparser.rst @@ -1,24 +1,6 @@ :mod:`stix.utils.nsparser` Module ================================== -.. module:: stix.utils.nsparser - -Classes -------- - -.. autoclass:: NamespaceParser - :show-inheritance: +.. automodule:: stix.utils.nsparser :members: - -Constants ---------- - -.. autodata:: XML_NAMESPACES - -.. autodata:: STIX_NS_TO_SCHEMALOCATION - -.. autodata:: EXT_NS_TO_SCHEMALOCATION - -.. autodata:: DEFAULT_STIX_NS_TO_PREFIX - -.. autodata:: DEFAULT_EXT_TO_PREFIX + :undoc-members: diff --git a/docs/conf.py b/docs/conf.py index 3ece72c7..c4c7dc0b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ 'sphinx.ext.ifconfig', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', - 'sphinxcontrib.napoleon', + 'sphinx.ext.napoleon', ] intersphinx_mapping = { diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 833cff93..0449fddb 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -10,7 +10,7 @@ This page gives an introduction to **python-stix** and how to use it. Prerequisites ------------- -The python-stix library provides an API for creating or processing STIX content. As such, it is a developer tool that can be leveraged by those who know Python 2.6/2.7 and are familiar with object-oriented programming practices, Python package layouts, and are comfortable with the installation of Python libraries. To contribute code to the python-stix repository, users must be familiar with `git`_ and `GitHub pull request`_ methodologies. Understanding XML, XML Schema, and the STIX language is also incredibly helpful when using python-stix in an application. +The python-stix library provides an API for creating or processing STIX content. As such, it is a developer tool that can be leveraged by those who know Python 2.7/3.3+ and are familiar with object-oriented programming practices, Python package layouts, and are comfortable with the installation of Python libraries. To contribute code to the python-stix repository, users must be familiar with `git`_ and `GitHub pull request`_ methodologies. Understanding XML, XML Schema, and the STIX language is also incredibly helpful when using python-stix in an application. .. _git: http://git-scm.com/documentation .. _GitHub pull request: https://help.github.com/articles/using-pull-requests diff --git a/docs/index.rst b/docs/index.rst index ee6a4de1..7a7e5974 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,9 +5,9 @@ The **python-stix** library provides an API for developing and consuming *Struct .. note:: - These docs provide standard reference for this Python library. For documentation on *idiomatic* usage and *common patterns*, as well as various STIX-related information and utilities, please visit the `STIXProject at GitHub`_. - - .. _STIXProject at GitHub: http://stixproject.github.io/ + These docs provide standard reference for this Python library. For documentation on *idiomatic* usage and *common patterns*, as well as various STIX-related information and utilities, please visit the `STIXProject at GitHub`_. + + .. _STIXProject at GitHub: http://stixproject.github.io/ .. _STIX website: http://stix.mitre.org @@ -22,14 +22,14 @@ version of STIX. ============ =================== STIX Version python-stix Version ============ =================== -1.1.1 1.1.1.5 (`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`__) +1.1.1 1.1.1.19 (`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.1.1.5 -__ https://github.com/STIXProject/python-stix/tree/v1.1.1.5 +__ https://pypi.python.org/pypi/stix/1.1.1.19 +__ https://github.com/STIXProject/python-stix/tree/v1.1.1.19 __ 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 @@ -67,6 +67,18 @@ API Reference api/index api/coverage +FAQ +=== + +- My RAM consumption rises when processing a large amount of files. + This problem is caused by a python-cybox_ caching mechanism that is enabled + by default. To prevent this issue from happening use the + ``cybox.utils.caches.cache_clear()`` method in your code/script to release + the cached resources as appropriate. Refer to the ``cybox`` documentation + for more details. + +.. _python-cybox: http://cybox.readthedocs.io/ + Contributing ============ If a bug is found, a feature is missing, or something just isn't behaving the way you'd expect it to, please submit an issue to our `tracker`_. If you'd like to contribute code to our repository, you can do so by issuing a `pull request`_ and we will work with you to try and integrate that code into our repository. @@ -80,4 +92,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/requirements.txt b/requirements.txt index 54d46380..14df8061 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,7 @@ --e .[docs,test] +maec>=4.1.0.13,<4.1.1.0 # For tests that include MAEC +nose==1.3.7 +sphinx==1.3.6 +sphinx_rtd_theme==0.2.4 +tox==2.7.0 + +-e . diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..1920de95 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,17 @@ +[bumpversion] +current_version = 1.1.1.19 +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 6ad803b2..1a599e72 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,9 @@ #!/usr/bin/env python -# Copyright (c) 2015 - The MITRE Corporation -# For license information, see the LICENSE.txt file +# Copyright (c) 2016, 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,29 +22,17 @@ def get_version(): raise AttributeError("Package does not have a __version__") -with open('README.rst') as f: - readme = f.read() - -extras_require = { - 'docs': [ - 'Sphinx==1.2.1', - # TODO: remove when updating to Sphinx 1.3, since napoleon will be - # included as sphinx.ext.napoleon - 'sphinxcontrib-napoleon==0.2.4', - 'sphinx_rtd_theme==0.1.7', - ], - 'test': [ - 'nose==1.3.0', - 'tox==1.6.1', - 'maec>=4.1.0.12,<4.1.1.0' - ], -} +def get_long_description(): + with open('README.rst', encoding='utf-8') as f: + return f.read() install_requires = [ - 'lxml>=2.3', + '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', 'python-dateutil', - 'cybox>=2.1.0.11,<2.1.1.0' ] @@ -52,16 +42,28 @@ 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, - extras_require=extras_require, + license="BSD", classifiers=[ - "Programming Language :: Python", - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", - "Operating System :: OS Independent", - ] + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + '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', + '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/__init__.py b/stix/__init__.py index 95df5e34..2f289ab3 100644 --- a/stix/__init__.py +++ b/stix/__init__.py @@ -1,12 +1,138 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. # Make sure base gets imported before common. -from .base import Entity, EntityList, TypedList, BaseCoreComponent # noqa -from . import common # noqa +from .base import (Entity, EntityList, TypedCollection, TypedList, # noqa + BaseCoreComponent) + +from mixbox.vendor.six import string_types, iteritems + +#: Mapping of xsi:types to implementation/extension classes +_EXTENSION_MAP = {} + + +def _lookup_unprefixed(typename): + """Attempts to resolve a class for the input XML type `typename`. + + Args: + typename: The name of an STIX XML type (e.g., TLPMarkingStructureType) + without a namespace prefix. + + Returns: + A stix.Entity implementation class for the `typename`. + + Raises: + ValueError: If no class has been registered for the input `typename`. + + """ + for xsi_type, klass in iteritems(_EXTENSION_MAP): + if typename in xsi_type: + return klass + + error = "Unregistered extension type: %s" % typename + raise ValueError(error) + + +def _lookup_extension(xsi_type): + """Returns a Python class for the `xsi_type` value. + + Args: + xsi_type: An xsi:type value string. + + Returns: + A stix.Entity implementation class for the `xsi_type`. + + Raises: + ValueError: If no class has been registered for the `xsi_type`. + + """ + import stix.extensions.malware.maec_4_1_malware + + if xsi_type in _EXTENSION_MAP: + return _EXTENSION_MAP[xsi_type] + + raise ValueError("Unregistered xsi:type %s" % xsi_type) + + +def lookup_extension(typeinfo, default=None): + """Returns a stix.Entity class for that has been registered for the + `typeinfo` value. + + Note: + This is for internal use only. + + Args: + typeinfo: An object or string containing type information. This can be + either an xsi:type attribute value or a stix.bindings object. + default: Return class if typeinfo is None or contains no xml type + information. + Returns: + A stix.Entity implementation class for the `xsi_type`. + + Raises: + ValueError: If no class has been registered for the `xsi_type`. + + """ + if typeinfo is None and default: + return default + + # If the `typeinfo` was a string, consider it a full xsi:type value. + if isinstance(typeinfo, string_types): + return _lookup_extension(typeinfo) + + # Most extension bindings include this attribute. + if not hasattr(typeinfo, 'xml_type'): + if default: + return default + + error = "Input %s is missing xml_type attribute. Cannot lookup class." + raise ValueError(error % type(typeinfo)) + + # Extension binding classes usually (always?) have an `xmlns_prefix` + # class attribute. + if hasattr(typeinfo, 'xmlns_prefix'): + xsi_type = "%s:%s" % (typeinfo.xmlns_prefix, typeinfo.xml_type) + return _lookup_extension(xsi_type) + + # no xmlns_prefix found, try to resolve the class by just the `xml_type` + return _lookup_unprefixed(typeinfo.xml_type) + + +def add_extension(cls): + """Registers a stix.Entity class as an implementation of an xml type. + + Classes must have an ``_XSI_TYPE`` class attributes to be registered. The + value of this attribute must be a valid xsi:type. + + Note: + This was designed for internal use. + + """ + _EXTENSION_MAP[cls._XSI_TYPE] = cls # noqa + + +def register_extension(cls): + """Class decorator for registering a stix.Entity class as an implementation + of an xml type. + + Classes must have an ``_XSI_TYPE`` class attributes to be registered. + + Note: + This was designed for internal use. + + """ + add_extension(cls) + return cls + +from . import common # noqa from .version import __version__ # noqa + def supported_stix_version(): - return '.'.join(__version__.split('.')[:3]) + """Returns a tuple of STIX version strings that this version of python-stix + supports (i.e., can parse). + + """ + return ("1.0", "1.0.1", "1.1", "1.1.1") diff --git a/stix/base.py b/stix/base.py index f01dd60a..b232cd3c 100644 --- a/stix/base.py +++ b/stix/base.py @@ -1,30 +1,32 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -# builtin -import json +# stdlib import collections import itertools -import StringIO + +# mixbox +from mixbox import compat +from mixbox import idgen +from mixbox import entities +from mixbox import fields +from mixbox import binding_utils +from mixbox import namespaces +from mixbox.vendor.six import StringIO, iteritems, itervalues, text_type, binary_type # internal -from . import bindings, utils +from . import utils def _override(*args, **kwargs): raise NotImplementedError() -class Entity(object): +class Entity(entities.Entity): """Base class for all classes in the STIX API.""" _namespace = None _XSI_TYPE = None - def _collect_ns_info(self, ns_info=None): - if not ns_info: - return - ns_info.collect(self) - def _set_var(self, klass, try_cast=True, arg=None, **kwargs): """Sets an instance property value. @@ -47,7 +49,7 @@ def _set_var(self, klass, try_cast=True, arg=None, **kwargs): and the field value is the value. """ - name, item = kwargs.iteritems().next() + name, item = next(iteritems(kwargs)) attr = utils.private_name(name) # 'title' => '_title' if item is None: @@ -82,36 +84,21 @@ def _set_vocab(self, klass=None, **kwargs): from stix.common import VocabString klass = klass or VocabString - item = kwargs.itervalues().next() + item = next(itervalues(kwargs)) if isinstance(item, VocabString): self._set_var(VocabString, **kwargs) else: self._set_var(klass, **kwargs) - def to_obj(self, return_obj=None, ns_info=None): - """Converts an `Entity` into a binding object. - - Note: - This needs to be overridden by derived classes. - - """ - self._collect_ns_info(ns_info) - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - """Create an object from a binding object""" - raise NotImplementedError() - def to_xml(self, include_namespaces=True, include_schemalocs=False, ns_dict=None, schemaloc_dict=None, pretty=True, auto_namespace=True, encoding='utf-8'): """Serializes a :class:`Entity` instance to an XML string. The default character encoding is ``utf-8`` and can be set via the - `encoding` parameter. If `encoding` is ``None``, a unicode string - is returned. + `encoding` parameter. If `encoding` is ``None``, a string (unicode in + Python 2, str in Python 3) is returned. Args: auto_namespace: Automatically discover and export XML namespaces @@ -132,7 +119,8 @@ def to_xml(self, include_namespaces=True, include_schemalocs=False, only be included if `auto_namespace` is ``False``. pretty: Pretty-print the XML. encoding: The output character encoding. Default is ``utf-8``. If - `encoding` is set to ``None``, a unicode string is returned. + `encoding` is set to ``None``, a string (unicode in Python 2, + str in Python 3) is returned. Returns: An XML string for this @@ -140,15 +128,7 @@ def to_xml(self, include_namespaces=True, include_schemalocs=False, """ - from .utils import nsparser - parser = nsparser.NamespaceParser() - - if auto_namespace: - ns_info = nsparser.NamespaceInfo() - else: - ns_info = None - - obj = self.to_obj(ns_info=ns_info) + from mixbox.entities import NamespaceCollector if (not auto_namespace) and (not ns_dict): raise Exception( @@ -156,34 +136,33 @@ def to_xml(self, include_namespaces=True, include_schemalocs=False, "or missing." ) + ns_info = NamespaceCollector() + + obj = self.to_obj(ns_info=ns_info if auto_namespace else None) + + ns_info.finalize(ns_dict=ns_dict, schemaloc_dict=schemaloc_dict) + if auto_namespace: - ns_info.finalize(ns_dict=ns_dict, schemaloc_dict=schemaloc_dict) obj_ns_dict = ns_info.binding_namespaces else: - ns_info = nsparser.NamespaceInfo() - ns_info.finalized_namespaces = ns_dict or {} - ns_info.finalized_schemalocs = schemaloc_dict or {} obj_ns_dict = dict( itertools.chain( - ns_dict.iteritems(), - nsparser.DEFAULT_STIX_NAMESPACES.iteritems() + iteritems(ns_info.binding_namespaces), + iteritems(namespaces.get_full_ns_map()) ) ) namespace_def = "" if include_namespaces: - xmlns = parser.get_xmlns_str(ns_info.finalized_namespaces) - namespace_def += ("\n\t" + xmlns) - - if include_schemalocs and include_namespaces: - schemaloc = parser.get_schemaloc_str(ns_info.finalized_schemalocs) - namespace_def += ("\n\t" + schemaloc) - - if not pretty: - namespace_def = namespace_def.replace('\n\t', ' ') - - with bindings.save_encoding(encoding): - sio = StringIO.StringIO() + delim = "\n\t" if pretty else " " + xmlns = ns_info.get_xmlns_string(delim) + namespace_def += (delim + xmlns) + if include_schemalocs: + schemaloc = ns_info.get_schema_location_string(delim) + namespace_def += (delim + schemaloc) + + with binding_utils.save_encoding(encoding): + sio = StringIO() obj.export( sio.write, # output buffer 0, # output level @@ -193,62 +172,13 @@ def to_xml(self, include_namespaces=True, include_schemalocs=False, ) # Ensure that the StringIO buffer is unicode - s = unicode(sio.getvalue()) + s = text_type(sio.getvalue()) if encoding: return s.encode(encoding) return s - def to_json(self): - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_doc): - """Parses the JSON document `json_doc` and returns a STIX - :class:`Entity` implementation instance. - - Arguments: - json_doc: Input JSON representation of a STIX entity. This can be - a readable object or a JSON string. - - Returns: - An implementation of :class:`.Entity` (e.g., - :class:`.STIXPackage`). - - """ - try: - d = json.load(json_doc) - except AttributeError: # catch the read() error - d = json.loads(json_doc) - - return cls.from_dict(d) - - def to_dict(self): - """Converts a STIX :class:`Entity` implementation into a Python - dictionary. This may be overridden by derived classes. - - """ - return utils.to_dict(self) - - @classmethod - def from_dict(cls, d, return_obj=None): - """Convert from dict representation to object representation. - This should be overriden by a subclass - - """ - raise NotImplementedError() - - @classmethod - def object_from_dict(cls, entity_dict): - """Convert from dict representation to object representation.""" - return cls.from_dict(entity_dict).to_obj() - - @classmethod - def dict_from_object(cls, entity_obj): - """Convert from object representation to dict representation.""" - return cls.from_obj(entity_obj).to_dict() - def walk(self): return utils.walk.iterwalk(self) @@ -265,54 +195,40 @@ def find(self, id_): return entity -class EntityList(collections.MutableSequence, Entity): - _binding_class = _override - _binding_var = None - _contained_type = _override - _inner_name = None - _dict_as_list = False +class EntityList(entities.EntityList, Entity): + def to_xml(self, *args, **kwargs): + return Entity.to_xml(self, *args, **kwargs) - def __init__(self, *args): - super(EntityList, self).__init__() - self._inner = [] - if not any(args): - return +class TypedCollection(object): + """Abstract base class for non-STIX collections of entities. - for arg in args: - if utils.is_sequence(arg): - self.extend(arg) - else: - self.append(arg) + See also: + TypedList - def __nonzero__(self): - return bool(self._inner) + """ + _contained_type = _override - def __getitem__(self, key): - return self._inner.__getitem__(key) + def __init__(self, *args): + self._inner = [] + self._initialize_inner(*args) - def __setitem__(self, key, value): - if not self._is_valid(value): - value = self._fix_value(value) - self._inner.__setitem__(key, value) + def _initialize_inner(self, *args): + """Must be overridden by subclass. - def __delitem__(self, key): - self._inner.__delitem__(key) + """ + raise NotImplementedError() def __len__(self): return len(self._inner) - def insert(self, idx, value): - if not value: - return - if not self._is_valid(value): - value = self._fix_value(value) - self._inner.insert(idx, value) + def __nonzero__(self): + return bool(self._inner) def _is_valid(self, value): """Check if this is a valid object to add to the list.""" - # Subclasses can override this function, but if it becomes common, it's - # probably better to use self._contained_type.istypeof(value) + # Subclasses can override this function, but if it becomes common, + # it's probably better to use self._contained_type.istypeof(value) return isinstance(value, self._contained_type) def _fix_value(self, value): @@ -322,7 +238,7 @@ def _fix_value(self, value): """ try: new_value = self._contained_type(value) - except: + except Exception: error = "Can't put '{0}' ({1}) into a {2}. Expected a {3} object." error = error.format( value, # Input value @@ -334,104 +250,71 @@ def _fix_value(self, value): return new_value - # The next four functions can be overridden, but otherwise define the - # default behavior for EntityList subclasses which define the following - # class-level members: - # - _binding_class - # - _binding_var - # - _contained_type - # - _inner_name - - def to_obj(self, return_obj=None, ns_info=None): - super(EntityList, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - objlist = [x.to_obj(ns_info=ns_info) for x in self] - setattr(return_obj, self._binding_var, objlist) - - return return_obj + def to_obj(self, ns_info=None): + return [x.to_obj(ns_info=ns_info) for x in self] def to_list(self): return [h.to_dict() for h in self] - def to_dict(self): - if self._dict_as_list: - return self.to_list() - - d = utils.to_dict(self, skip=('inner',)) - - if self._inner: - d[self._inner_name] = [h.to_dict() for h in self] - - return d - @classmethod - def from_obj(cls, obj, return_obj=None, contained_type=None, - binding_var=None): - if not obj: + def from_obj(cls, obj_list, contained_type=None): + if obj_list is None: return None - if return_obj is None: - return_obj = cls() if not contained_type: contained_type = cls._contained_type - if not binding_var: - binding_var = cls._binding_var - for item in getattr(obj, binding_var): - return_obj.append(contained_type.from_obj(item)) + if not utils.is_sequence(obj_list): + obj_list = [obj_list] - return return_obj + items = (contained_type.from_obj(x) for x in obj_list) + return cls(items) @classmethod - def from_list(cls, list_repr, return_obj=None, contained_type=None): + def from_list(cls, list_repr, contained_type=None): - if not utils.is_sequence(list_repr): - return None + if not list_repr: + return cls() + + if isinstance(list_repr, dict) or not utils.is_sequence(list_repr): + list_repr = [list_repr] - if return_obj is None: - return_obj = cls() if not contained_type: contained_type = cls._contained_type - return_obj.extend(contained_type.from_dict(x) for x in list_repr) - return return_obj - - @classmethod - def from_dict(cls, dict_repr, return_obj=None, contained_type=None, - inner_name=None): - - if cls._dict_as_list: - return cls.from_list( - dict_repr, - return_obj=return_obj, - contained_type=contained_type, - ) + items = (contained_type.from_dict(x) for x in list_repr) + return cls(items) - if not isinstance(dict_repr, dict): - return None + from_dict = from_list + to_dict = to_list - if return_obj is None: - return_obj = cls() - if not contained_type: - contained_type = cls._contained_type - if not inner_name: - inner_name = cls._inner_name + @classmethod + def object_from_dict(cls, entity_dict): + """Convert from dict representation to object representation.""" + return cls.from_dict(entity_dict).to_obj() - for item in dict_repr.get(inner_name, []): - return_obj.append(contained_type.from_dict(item)) + @classmethod + def dict_from_object(cls, entity_obj): + """Convert from object representation to dict representation.""" + return cls.from_obj(entity_obj).to_dict() - return return_obj + @classmethod + def istypeof(cls, obj): + """Check if `cls` is the type of `obj` + In the normal case, as implemented here, a simple isinstance check is + used. However, there are more complex checks possible. For instance, + EmailAddress.istypeof(obj) checks if obj is an Address object with + a category of Address.CAT_EMAIL + """ + return isinstance(obj, cls) -class TypedList(collections.MutableSequence): - _contained_type = _override +class TypedList(TypedCollection, compat.MutableSequence): def __init__(self, *args): - self._inner = [] + TypedCollection.__init__(self, *args) + def _initialize_inner(self, *args): # Check if it was initialized with args=None if not any(args): return @@ -442,9 +325,6 @@ def __init__(self, *args): else: self.append(arg) - def __nonzero__(self): - return bool(self._inner) - def __getitem__(self, key): return self._inner.__getitem__(key) @@ -466,358 +346,36 @@ def insert(self, idx, value): value = self._fix_value(value) self._inner.insert(idx, value) - def _is_valid(self, value): - """Check if this is a valid object to add to the list.""" - # Subclasses can override this function, but if it becomes common, - # it's probably better to use self._contained_type.istypeof(value) - return isinstance(value, self._contained_type) - def _fix_value(self, value): - """Attempt to coerce value into the correct type. - - Subclasses can override this function. - """ - try: - new_value = self._contained_type(value) - except Exception: - error = "Can't put '{0}' ({1}) into a {2}. Expected a {3} object." - error = error.format( - value, # Input value - type(value), # Type of input value - type(self), # Type of collection - self._contained_type # Expected type of input value - ) - raise ValueError(error) - - return new_value - - def to_obj(self, ns_info=None): - return [x.to_obj(ns_info=ns_info) for x in self] - - def to_list(self): - return [h.to_dict() for h in self] - - to_dict = to_list - - @classmethod - def from_obj(cls, obj_list, contained_type=None): - if not obj_list: - return None - - return_obj = cls() - - if not contained_type: - contained_type = cls._contained_type - - if not utils.is_sequence(obj_list): - obj_list = [obj_list] - - return_obj.extend(contained_type.from_obj(x) for x in obj_list) - return return_obj - - @classmethod - def from_list(cls, list_repr, contained_type=None): - - if not utils.is_sequence(list_repr): - return None - - return_obj = cls() - - if not contained_type: - contained_type = cls._contained_type - - return_obj.extend(contained_type.from_dict(x) for x in list_repr) - return return_obj - - from_dict = from_list - - @classmethod - def object_from_dict(cls, entity_dict): - """Convert from dict representation to object representation.""" - return cls.from_dict(entity_dict).to_obj() - - @classmethod - def dict_from_object(cls, entity_obj): - """Convert from object representation to dict representation.""" - return cls.from_obj(entity_obj).to_dict() +def _validate_version(instance, value): + if value: + utils.check_version(instance._ALL_VERSIONS, value) class BaseCoreComponent(Entity): _ALL_VERSIONS = () _ID_PREFIX = None + title = fields.TypedField("Title") + id_ = fields.IdField("id") + idref = fields.IdrefField("idref") + description = fields.TypedField("Description", type_="stix.common.StructuredText") + short_description = fields.TypedField("Short_Description", type_="stix.common.StructuredText") + version = fields.TypedField("version", preset_hook=_validate_version) + timestamp = fields.DateTimeField("timestamp") + handling = fields.TypedField("Handling", type_="stix.data_marking.Marking") + def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): + super(BaseCoreComponent, self).__init__() - self.id_ = id_ or utils.create_id(self._ID_PREFIX) + self.id_ = id_ or idgen.create_id(self._ID_PREFIX) self.idref = idref self.title = title self.description = description self.short_description = short_description - self.version = None - self.information_source = None if timestamp: self.timestamp = timestamp else: self.timestamp = utils.dates.now() if not idref else None - - @property - def id_(self): - """The ``id_`` property serves as an identifier. This is - automatically set during ``__init__()``. - - Default Value: ``None`` - - Note: - Both the ``id_`` and ``idref`` properties cannot be set at the - same time. **Setting one will unset the other!** - - Returns: - A string id. - - """ - return self._id - - @id_.setter - def id_(self, value): - if not value: - self._id = None - else: - self._id = value - self.idref = None - - @property - def idref(self): - """The ``idref`` property must be set to the ``id_`` value of another - object instance of the same type. An idref does not need to resolve to - a local object instance. - - Default Value: ``None``. - - Note: - Both the ``id_`` and ``idref`` properties cannot be set at the - same time. **Setting one will unset the other!** - - Returns: - The value of the ``idref`` property - - """ - return self._idref - - @idref.setter - def idref(self, value): - if not value: - self._idref = None - else: - self._idref = value - self.id_ = None # unset id_ if idref is present - - @property - def version(self): - """The schematic version of this component. This property - will always return ``None`` unless it is set to a value different than - ``self.__class__._version``. - - Note: - This property refers to the version of the schema component - type and should not be used for the purpose of content versioning. - - Default Value: ``None`` - - Returns: - The value of the ``version`` property if set to a value different - than ``self.__class__._version`` - - """ - return self._version - - @version.setter - def version(self, value): - if not value: - self._version = None - else: - utils.check_version(self._ALL_VERSIONS, value) - self._version = value - - @property - def timestamp(self): - """The timestam property declares the time of creation and is - automatically set in ``__init__()``. - - This property can accept ``datetime.datetime`` or ``str`` values. - If an ``str`` value is supplied, a best-effort attempt is made to - parse it into an instance of ``datetime.datetime``. - - Default Value: A ``datetime.dateime`` instance with a value of the - date/time when ``__init__()`` was called. - - Note: - If an ``idref`` is set during ``__init__()``, the value of - ``timestamp`` will not automatically generated and instead default - to the ``timestamp`` parameter, which has a default value of - ``None``. - - Returns: - An instance of ``datetime.datetime``. - - """ - return self._timestamp - - @timestamp.setter - def timestamp(self, value): - self._timestamp = utils.dates.parse_value(value) - - @property - def title(self): - return self._title - - @title.setter - def title(self, value): - self._title = value - - @property - def description(self): - """A description about the contents or purpose of this object. - - Default Value: ``None`` - - Note: - If set to a value that is not an instance of - :class:`.StructuredText`, an attempt to will be made to convert - the value into an instance of :class:`.StructuredText`. - - Returns: - An instance of - :class:`.StructuredText` - - """ - return self._description - - @description.setter - def description(self, value): - from stix.common import StructuredText - self._set_var(StructuredText, description=value) - - @property - def short_description(self): - """A short description about the contents or purpose of this object. - - Default Value: ``None`` - - Note: - If set to a value that is not an instance of - :class:`.StructuredText`, an attempt to will be made to convert - the value into an instance of :class:`.StructuredText`. - - Returns: - An instance of - :class:`.StructuredText` - - """ - return self._short_description - - @short_description.setter - def short_description(self, value): - from stix.common import StructuredText - self._set_var(StructuredText, short_description=value) - - @property - def information_source(self): - """Contains information about the source of this object. - - Default Value: ``None`` - - Returns: - An instance of - :class:`.InformationSource` - - Raises: - ValueError: If set to a value that is not ``None`` and not an - instance of - :class:`.InformationSource` - - """ - return self._information_source - - @information_source.setter - def information_source(self, value): - from stix.common import InformationSource - self._set_var(InformationSource, try_cast=False, information_source=value) - - @classmethod - def from_obj(cls, obj, return_obj=None): - from stix.common import StructuredText, InformationSource - - if not return_obj: - raise ValueError("Must provide a return_obj argument") - - if not obj: - raise ValueError("Must provide an obj argument") - - return_obj.id_ = obj.id - return_obj.idref = obj.idref - return_obj.timestamp = obj.timestamp - - # These may not be found on the input obj if it isn't a full - # type definition (e.g., used as a reference) - return_obj.version = getattr(obj, 'version', None) - return_obj.title = getattr(obj, 'Title', None) - return_obj.description = \ - StructuredText.from_obj(getattr(obj, 'Description', None)) - return_obj.short_description = \ - StructuredText.from_obj(getattr(obj, 'Short_Description', None)) - return_obj.information_source = \ - InformationSource.from_obj(getattr(obj, 'Information_Source', None)) - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - raise ValueError("Must provide a return_obj argument") - - super(BaseCoreComponent, self).to_obj( - return_obj=return_obj, - ns_info=ns_info - ) - - return_obj.id = self.id_ - return_obj.idref = self.idref - return_obj.version = self.version - return_obj.Title = self.title - - if self.timestamp: - return_obj.timestamp = utils.dates.serialize_value(self.timestamp) - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.short_description: - return_obj.Short_Description = self.short_description.to_obj(ns_info=ns_info) - if self.information_source: - return_obj.Information_Source = self.information_source.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - from stix.common import StructuredText, InformationSource - - if not return_obj: - raise ValueError("Must provide a return_obj argument") - - get = d.get - return_obj.id_ = get('id') - return_obj.idref = get('idref') - return_obj.timestamp = get('timestamp') - return_obj.version = get('version') - return_obj.title = get('title') - return_obj.description = \ - StructuredText.from_dict(get('description')) - return_obj.short_description = \ - StructuredText.from_dict(get('short_description')) - return_obj.information_source = \ - InformationSource.from_dict(get('information_source')) - - return return_obj - - def to_dict(self): - return super(BaseCoreComponent, self).to_dict() diff --git a/stix/bindings/__init__.py b/stix/bindings/__init__.py index c88b876c..c042c61d 100644 --- a/stix/bindings/__init__.py +++ b/stix/bindings/__init__.py @@ -1,432 +1,109 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -import re -import base64 import collections -import contextlib -from xml.sax import saxutils -from datetime import datetime, tzinfo, timedelta from lxml import etree as etree_ -import cybox.bindings as cybox_bindings +import mixbox.xml -from stix import xmlconst -try: - import maec.bindings as maec_bindings - _MAEC_INSTALLED = True -except ImportError: - _MAEC_INSTALLED = False - - -CDATA_START = "" - -ExternalEncoding = 'utf-8' -Tag_pattern_ = re.compile(r'({.*})?(.*)') - -# These are only used internally -_tzoff_pattern = re.compile(r'(\+|-)((0\d|1[0-3]):[0-5]\d|14:00)$') -_Tag_strip_pattern_ = re.compile(r'\{.*\}') - - -@contextlib.contextmanager -def save_encoding(encoding='utf-8'): - global ExternalEncoding - - try: - # Save original binding encoding attribute value - orig_stix_encoding = ExternalEncoding - orig_cybox_encoding = cybox_bindings.ExternalEncoding - - # Set binding encoding attribute value to `encoding` - ExternalEncoding = encoding - cybox_bindings.ExternalEncoding = encoding - - # Set MAEC binding encoding attribute to `encoding` if python-maec - # is installed. - if _MAEC_INSTALLED: - orig_maec_encoding = maec_bindings.ExternalEncoding - maec_bindings.ExternalEncoding = encoding - - # Return to caller - yield - - finally: - # Reset the binding encoding attribute values to original values - ExternalEncoding = orig_stix_encoding - cybox_bindings.ExternalEncoding = orig_cybox_encoding - - if _MAEC_INSTALLED: - maec_bindings.ExternalEncoding = orig_maec_encoding - - -def parsexml_(*args, **kwargs): - from stix.utils.parser import get_xml_parser - - if 'parser' not in kwargs: - # Use the lxml ElementTree compatible parser so that, e.g., - # we ignore comments. - kwargs['parser'] = get_xml_parser() - return etree_.parse(*args, **kwargs) - - -class _FixedOffsetTZ(tzinfo): +TypeInfo = collections.namedtuple("TypeInfo", ('ns', 'typename')) - def __init__(self, offset, name): - self.__offset = timedelta(minutes=offset) - self.__name = name - def utcoffset(self, dt): - return self.__offset +def get_type_info(node): + """Returns a ``TypeInfo`` object for `node`. - def tzname(self, dt): - return self.__name + This is accomplished by parsing the ``xsi:type`` attribute found on + `node`. - def dst(self, dt): - return None + Args: + node: An lxml.etree element object. - __reduce__ = object.__reduce__ + Raises: + KeyError: If `node` does not have an ``xsi:type`` attribute. + """ + xsi_type = node.attrib[mixbox.xml.TAG_XSI_TYPE] + typeinfo = xsi_type.split(":") -class GeneratedsSuper(object): + if len(typeinfo) == 2: + prefix, typename = typeinfo + else: + typename = typeinfo + prefix = None - def gds_format_string(self, input_data, input_name=''): - return input_data + ns = node.nsmap[prefix] + return TypeInfo(ns=ns, typename=typename) - def gds_validate_string(self, input_data, node, input_name=''): - return input_data - def gds_format_base64(self, input_data, input_name=''): - return base64.b64encode(input_data) +#: A mapping of namespace/type information to binding classes. +_BINDING_EXTENSION_MAP = {} - def gds_validate_base64(self, input_data, node, input_name=''): - return input_data - def gds_format_integer(self, input_data, input_name=''): - return '%d' % input_data +def add_extension(cls): + """Adds the binding class `cls` to the ``_EXTENSION_MAP``. - def gds_validate_integer(self, input_data, node, input_name=''): - return input_data - - def gds_format_integer_list(self, input_data, input_name=''): - return '%s' % input_data - - def gds_validate_integer_list(self, input_data, node, input_name=''): - values = input_data.split() - for value in values: - try: - fvalue = float(value) - except (TypeError, ValueError), exp: - raise_parse_error(node, 'Requires sequence of integers') - return input_data - - def gds_format_float(self, input_data, input_name=''): - return '%f' % input_data - - def gds_validate_float(self, input_data, node, input_name=''): - return input_data - - def gds_format_float_list(self, input_data, input_name=''): - return '%s' % input_data - - def gds_validate_float_list(self, input_data, node, input_name=''): - values = input_data.split() - for value in values: - try: - fvalue = float(value) - except (TypeError, ValueError), exp: - raise_parse_error(node, 'Requires sequence of floats') - return input_data - - def gds_format_double(self, input_data, input_name=''): - return '%e' % input_data - - def gds_validate_double(self, input_data, node, input_name=''): - return input_data - - def gds_format_double_list(self, input_data, input_name=''): - return '%s' % input_data - - def gds_validate_double_list(self, input_data, node, input_name=''): - values = input_data.split() - for value in values: - try: - fvalue = float(value) - except (TypeError, ValueError), exp: - raise_parse_error(node, 'Requires sequence of doubles') - return input_data - - def gds_format_boolean(self, input_data, input_name=''): - return ('%s' % input_data).lower() - - def gds_validate_boolean(self, input_data, node, input_name=''): - return input_data - - def gds_format_boolean_list(self, input_data, input_name=''): - return '%s' % input_data - - def gds_validate_boolean_list(self, input_data, node, input_name=''): - values = input_data.split() - for value in values: - if value not in ('true', '1', 'false', '0'): - msg = ('Requires sequence of booleans ' - '("true", "1", "false", "0")') - raise_parse_error(node, msg) - return input_data - - def gds_validate_datetime(self, input_data, node, input_name=''): - return input_data - - def gds_format_datetime(self, input_data, input_name=''): - if isinstance(input_data, basestring): - return input_data - if input_data.microsecond == 0: - _svalue = input_data.strftime('%Y-%m-%dT%H:%M:%S') - else: - _svalue = input_data.strftime('%Y-%m-%dT%H:%M:%S.%f') - if input_data.tzinfo is not None: - tzoff = input_data.tzinfo.utcoffset(input_data) - if tzoff is not None: - total_seconds = tzoff.seconds + (86400 * tzoff.days) - if total_seconds == 0: - _svalue += 'Z' - else: - if total_seconds < 0: - _svalue += '-' - total_seconds *= -1 - else: - _svalue += '+' - hours = total_seconds // 3600 - minutes = (total_seconds - (hours * 3600)) // 60 - _svalue += '{0:02d}:{1:02d}'.format(hours, minutes) - return _svalue - - def gds_parse_datetime(self, input_data, node, input_name=''): - tz = None - if input_data[-1] == 'Z': - tz = _FixedOffsetTZ(0, 'GMT') - input_data = input_data[:-1] - else: - results = _tzoff_pattern.search(input_data) - if results is not None: - tzoff_parts = results.group(2).split(':') - tzoff = int(tzoff_parts[0]) * 60 + int(tzoff_parts[1]) - if results.group(1) == '-': - tzoff *= -1 - tz = _FixedOffsetTZ(tzoff, results.group(0)) - input_data = input_data[:-6] - if len(input_data.split('.')) > 1: - dt = datetime.strptime(input_data, '%Y-%m-%dT%H:%M:%S.%f') - else: - dt = datetime.strptime(input_data, '%Y-%m-%dT%H:%M:%S') - return dt.replace(tzinfo=tz) - - def gds_validate_date(self, input_data, node, input_name=''): - return input_data - - def gds_format_date(self, input_data, input_name=''): - if isinstance(input_data, basestring): - return input_data - _svalue = input_data.strftime('%Y-%m-%d') - if input_data.tzinfo is not None: - tzoff = input_data.tzinfo.utcoffset(input_data) - if tzoff is not None: - total_seconds = tzoff.seconds + (86400 * tzoff.days) - if total_seconds == 0: - _svalue += 'Z' - else: - if total_seconds < 0: - _svalue += '-' - total_seconds *= -1 - else: - _svalue += '+' - hours = total_seconds // 3600 - minutes = (total_seconds - (hours * 3600)) // 60 - _svalue += '{0:02d}:{1:02d}'.format(hours, minutes) - return _svalue - - def gds_parse_date(self, input_data, node, input_name=''): - tz = None - if input_data[-1] == 'Z': - tz = _FixedOffsetTZ(0, 'GMT') - input_data = input_data[:-1] - else: - results = _tzoff_pattern.search(input_data) - if results is not None: - tzoff_parts = results.group(2).split(':') - tzoff = int(tzoff_parts[0]) * 60 + int(tzoff_parts[1]) - if results.group(1) == '-': - tzoff *= -1 - tz = _FixedOffsetTZ(tzoff, results.group(0)) - input_data = input_data[:-6] - return datetime.strptime(input_data, '%Y-%m-%d').replace(tzinfo=tz) - - def gds_str_lower(self, instring): - return instring.lower() - - def get_path_(self, node): - path_list = [] - self.get_path_list_(node, path_list) - path_list.reverse() - path = '/'.join(path_list) - return path - - def get_path_list_(self, node, path_list): - if node is None: - return - tag = _Tag_strip_pattern_.sub('', node.tag) - if tag: - path_list.append(tag) - self.get_path_list_(node.getparent(), path_list) - - def get_class_obj_(self, node, default_class=None): - class_obj1 = default_class - if 'xsi' in node.nsmap: - classname = node.get('{%s}type' % node.nsmap['xsi']) - if classname is not None: - names = classname.split(':') - if len(names) == 2: - classname = names[1] - class_obj2 = globals().get(classname) - if class_obj2 is not None: - class_obj1 = class_obj2 - return class_obj1 - - def gds_build_any(self, node, type_name=None): - return None - - -def showIndent(lwrite, level, pretty_print=True): - if pretty_print: - lwrite(' ' * level) - - -def quote_xml(text): - if text is None: - return u'' - - # Convert `text` to unicode string. This is mainly a catch-all for non - # string/unicode types like bool and int. - try: - text = unicode(text) - except UnicodeDecodeError: - text = text.decode(ExternalEncoding) - - # If it's a CDATA block, return the text as is. - if text.startswith(CDATA_START): - return text - - # If it's not a CDATA block, escape the XML and return the character - # encoded string. - return saxutils.escape(text) - - -def quote_attrib(text): - if text is None: - return u'""' - - # Convert `text` to unicode string. This is mainly a catch-all for non - # string/unicode types like bool and int. - try: - text = unicode(text) - except UnicodeDecodeError: - text = text.decode(ExternalEncoding) - - # Return the escaped the value of text. - # Note: This wraps the escaped text in quotation marks. - return saxutils.quoteattr(text) - - -def quote_python(inStr): - s1 = inStr - if s1.find("'") == -1: - if s1.find('\n') == -1: - return "'%s'" % s1 - else: - return "'''%s'''" % s1 - else: - if s1.find('"') != -1: - s1 = s1.replace('"', '\\"') - if s1.find('\n') == -1: - return '"%s"' % s1 - else: - return '"""%s"""' % s1 + This enables the lookup and instantiation of classes during parse when + ``xsi:type`` attributes are encountered. + """ + typeinfo = TypeInfo(ns=cls.xmlns, typename=cls.xml_type) + _BINDING_EXTENSION_MAP[typeinfo] = cls -def get_all_text_(node): - if node.text is not None: - text = node.text - else: - text = '' - for child in node: - if child.tail is not None: - text += child.tail - return text +def register_extension(cls): + """Class decorator for registering a binding class as an implementation of + an xml type. -def find_attr_value_(attr_name, node): - attrs = node.attrib - attr_parts = attr_name.split(':') - value = None - if len(attr_parts) == 1: - value = attrs.get(attr_name) - elif len(attr_parts) == 2: - prefix, name = attr_parts - namespace = node.nsmap.get(prefix) - if namespace is not None: - value = attrs.get('{%s}%s' % (namespace, name, )) - return value + Classes must have ``xmlns`` and ``xml_type`` class attributes to be + registered. + """ + add_extension(cls) + return cls -class GDSParseError(Exception): - pass -def raise_parse_error(node, msg): - msg = '%s (element %s/line %d)' % (msg, node.tag, node.sourceline) - raise GDSParseError(msg) +def lookup_extension(typeinfo, default=None): + """Looks up the binding class for `typeinfo`, which is a namespace/typename + pairing. + Args: + typeinfo: An lxml Element node or a stix.bindings.TypeInfo namedtuple. + default: A binding class that will be returned if typeinfo is an + Element without an xsi:type attribute. -def _cast(typ, value): - if typ is None or value is None: - return value - return typ(value) + Returns: + A binding class that has been registered for the namespace and typename + found on `typeinfo`. + """ + if not isinstance(typeinfo, TypeInfo): + if has_xsi_type(typeinfo): + typeinfo = get_type_info(typeinfo) + elif default: + return default -TypeInfo = collections.namedtuple("TypeInfo", ('ns', 'typename')) + if typeinfo in _BINDING_EXTENSION_MAP: + return _BINDING_EXTENSION_MAP[typeinfo] + fmt = "No class implemented or registered for XML type '{%s}%s'" + error = fmt % (typeinfo.ns, typeinfo.typename) + raise NotImplementedError(error) -def get_type_info(node): - xsi_type = node.attrib[xmlconst.TAG_XSI_TYPE] - typeinfo = xsi_type.split(":") - if len(typeinfo) == 2: - prefix, typename = typeinfo - else: - typename = typeinfo - prefix = None +def has_xsi_type(node): + """Returns ``True`` if `node` does not have an xsi:type attribute. - ns = node.nsmap[prefix] - return TypeInfo(ns=ns, typename=typename) + """ + return mixbox.xml.TAG_XSI_TYPE in node.attrib __all__ = [ - '_cast', + 'TypeInfo', + 'add_extension', 'etree_', - 'ExternalEncoding', - 'find_attr_value_', - 'get_all_text_', 'get_type_info', - 'parsexml_', - 'quote_xml', - 'quote_attrib', - 'quote_python', - 'raise_parse_error', - 'showIndent', - 'Tag_pattern_', - 'GeneratedsSuper', - 'CDATA_START', - 'CDATA_END', - 'TypeInfo' + 'has_xsi_type', + 'lookup_extension', + 'register_extension', ] diff --git a/stix/bindings/campaign.py b/stix/bindings/campaign.py index bb16e77e..b3725838 100644 --- a/stix/bindings/campaign.py +++ b/stix/bindings/campaign.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.stix_common as stix_common_binding import stix.bindings.data_marking as data_marking_binding @@ -71,6 +74,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='NamesTy for Name_ in self.Name: Name_.export(lwrite, level, nsmap, namespace_, name_='Name', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -139,6 +143,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Associa for Associated_Campaign_ in self.Associated_Campaign: Associated_Campaign_.export(lwrite, level, nsmap, namespace_, name_='Associated_Campaign', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -208,6 +213,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Indicator_ in self.Related_Indicator: Related_Indicator_.export(lwrite, level, nsmap, namespace_, name_='Related_Indicator', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -277,6 +283,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Incident_ in self.Related_Incident: Related_Incident_.export(lwrite, level, nsmap, namespace_, name_='Related_Incident', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -346,6 +353,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_TTP_ in self.Related_TTP: Related_TTP_.export(lwrite, level, nsmap, namespace_, name_='Related_TTP', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -417,6 +425,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Attribu for Attributed_Threat_Actor_ in self.Attributed_Threat_Actor: Attributed_Threat_Actor_.export(lwrite, level, nsmap, namespace_, name_='Attributed_Threat_Actor', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -432,17 +441,22 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): super(AttributionType, self).buildChildren(child_, node, nodeName_, True) # end class AttributionType + +@register_extension class CampaignType(stix_common_binding.CampaignBaseType): """The CampaignType characterizes a single cyber threat Campaign.Specifies the relevant STIX-Campaign schema version for this content.""" subclass = None superclass = stix_common_binding.CampaignBaseType + + xmlns = "http://stix.mitre.org/Campaign-1" + xmlns_prefix = "campaign" + xml_type = "CampaignType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, timestamp=None, version=None, Title=None, Description=None, Short_Description=None, Names=None, Intended_Effect=None, Status=None, Related_TTPs=None, Related_Incidents=None, Related_Indicators=None, Attribution=None, Associated_Campaigns=None, Confidence=None, Activity=None, Information_Source=None, Handling=None, Related_Packages=None): super(CampaignType, self).__init__(idref=idref, id=id, timestamp=timestamp) - self.xmlns = "http://stix.mitre.org/Campaign-1" - self.xmlns_prefix = "campaign" - self.xml_type = "CampaignType" self.version = _cast(None, version) self.Title = Title self.Description = Description @@ -607,6 +621,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Campaig if self.Related_Packages is not None: self.Related_Packages.export(lwrite, level, nsmap, namespace_, name_='Related_Packages', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -693,7 +708,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -739,7 +754,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/course_of_action.py b/stix/bindings/course_of_action.py index cfc6770c..e1b3392f 100644 --- a/stix/bindings/course_of_action.py +++ b/stix/bindings/course_of_action.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,8 +9,11 @@ # import sys -from stix.bindings import * + import cybox.bindings.cybox_core as cybox_core_binding +from mixbox.binding_utils import * + +from stix.bindings import lookup_extension, register_extension import stix.bindings.stix_common as stix_common_binding import stix.bindings.data_marking as data_marking_binding @@ -43,9 +46,6 @@ class StructuredCOAType(GeneratedsSuper): subclass = None superclass = None def __init__(self, idref=None, id=None): - self.xmlns = "http://stix.mitre.org/CourseOfAction-1" - self.xmlns_prefix = "coa" - self.xml_type = "CourseOfActionType" self.idref = _cast(None, idref) self.id = _cast(None, id) pass @@ -99,6 +99,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='coa:', def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='StructuredCOAType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -176,6 +177,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Objecti if self.Applicability_Confidence is not None: self.Applicability_Confidence.export(lwrite, level, nsmap, namespace_, name_='Applicability_Confidence', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -198,6 +200,8 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): self.set_Applicability_Confidence(obj_) # end class ObjectiveType + +@register_extension class CourseOfActionType(stix_common_binding.CourseOfActionBaseType): """The CourseOfActionType characterizes a Course of Action to be taken in regards to one of more cyber threats. NOTE: This construct is @@ -206,11 +210,15 @@ class CourseOfActionType(stix_common_binding.CourseOfActionBaseType): schema version for this content.""" subclass = None superclass = stix_common_binding.CourseOfActionBaseType + + xmlns = "http://stix.mitre.org/CourseOfAction-1" + xmlns_prefix = "coa" + xml_type = "CourseOfActionType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, timestamp=None, version=None, Title=None, Stage=None, Type=None, Description=None, Short_Description=None, Objective=None, Parameter_Observables=None, Structured_COA=None, Impact=None, Cost=None, Efficacy=None, Information_Source=None, Handling=None, Related_COAs=None, Related_Packages=None): super(CourseOfActionType, self).__init__(idref=idref, id=id, timestamp=timestamp) - self.xmlns = "http://stix.mitre.org/CourseOfAction-1" - self.xmlns_prefix = "coa" - self.xml_type = "CourseOfActionType" + self.version = _cast(None, version) self.Title = Title self.Stage = Stage @@ -354,6 +362,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='CourseO if self.Related_Packages is not None: self.Related_Packages.export(lwrite, level, nsmap, namespace_, name_='Related_Packages', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -395,24 +404,8 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): obj_.build(child_) self.set_Parameter_Observables(obj_) elif nodeName_ == 'Structured_COA': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "GenericStructuredCOAType": - from .extensions.structured_coa import generic - obj_ = generic.GenericStructuredCOAType.factory() - else: - raise NotImplementedError('No implementation class for Structured_COA: ' + type_name_) - else: - raise NotImplementedError('Structured_COA type not declared: missing xsi_type attribute') - + from .extensions.structured_coa import generic + obj_ = lookup_extension(child_).factory() obj_.build(child_) self.set_Structured_COA(obj_) elif nodeName_ == 'Impact': @@ -501,6 +494,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_COA_ in self.Related_COA: Related_COA_.export(lwrite, level, nsmap, namespace_, name_='Related_COA', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -523,7 +517,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -569,7 +563,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/data_marking.py b/stix/bindings/data_marking.py index fda03e89..acdbd848 100644 --- a/stix/bindings/data_marking.py +++ b/stix/bindings/data_marking.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -7,12 +7,12 @@ # # Generated Thu Apr 11 15:06:22 2013 by generateDS.py version 2.9a. # -# stdlib + import sys -# internal -from stix.bindings import * -from stix import xmlconst +from mixbox.binding_utils import * + +from stix.bindings import lookup_extension from . import stix_common as stix_common_binding XML_NS = "http://data-marking.mitre.org/Marking-1" @@ -75,6 +75,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Marking for Marking_ in self.Marking: Marking_.export(lwrite, level, nsmap, namespace_, name_='Marking', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -197,6 +198,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='marking def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='MarkingStructureType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -308,6 +310,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Marking if self.Information_Source is not None: self.Information_Source.export(lwrite, level, nsmap, namespace_, name_='Information_Source', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -339,20 +342,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): # Look for xsi:type. If not there, build an instance of # MarkingStructureType - if xmlconst.TAG_XSI_TYPE not in child_.attrib: - ref = MarkingStructureType.factory() - ref.build(child_) - self.Marking_Structure.append(ref) - return - - # Extract the xsi:type associated type namespace and type name - typeinfo = get_type_info(child_) - - if typeinfo not in _EXTENSION_MAP: - raise NotImplementedError('Marking structure type not implemented ' + typeinfo.typename) - - klass = _EXTENSION_MAP[typeinfo] - obj_ = klass.factory() + obj_ = lookup_extension(child_, MarkingStructureType).factory() obj_.build(child_) self.Marking_Structure.append(obj_) @@ -363,13 +353,6 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): # end class MarkingSpecificationType -_EXTENSION_MAP = {} - -def add_extension(klass): - typeinfo = TypeInfo(ns=klass.xmlns, typename=klass.xml_type) - _EXTENSION_MAP[typeinfo] = klass - - GDSClassesMapping = {} USAGE_TEXT = """ @@ -377,7 +360,7 @@ def add_extension(klass): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -423,7 +406,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/exploit_target.py b/stix/bindings/exploit_target.py index 146d49dc..e5f1b108 100644 --- a/stix/bindings/exploit_target.py +++ b/stix/bindings/exploit_target.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import lookup_extension, register_extension import stix.bindings.stix_common as stix_common_binding import stix.bindings.data_marking as data_marking_binding @@ -157,6 +160,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Vulnera if self.References is not None: self.References.export(lwrite, level, nsmap, namespace_, name_='References', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -202,7 +206,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): sval_ = child_.text try: ival_ = int(sval_) - except (TypeError, ValueError), exp: + except (TypeError, ValueError) as exp: raise_parse_error(child_, 'requires integer: %s' % exp) if ival_ <= 0: raise_parse_error(child_, 'requires positiveInteger') @@ -292,6 +296,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Configu showIndent(lwrite, level, pretty_print) lwrite('<%s:CCE_ID>%s%s' % (nsmap[namespace_], quote_xml(self.CCE_ID), nsmap[namespace_], eol_)) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -368,6 +373,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Affecte for Affected_Software_ in self.Affected_Software: Affected_Software_.export(lwrite, level, nsmap, namespace_, name_='Affected_Software', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -437,6 +443,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Exploit_Target_ in self.Related_Exploit_Target: Related_Exploit_Target_.export(lwrite, level, nsmap, namespace_, name_='Related_Exploit_Target', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -506,6 +513,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Weaknes showIndent(lwrite, level, pretty_print) lwrite('<%s:CWE_ID>%s%s' % (nsmap[namespace_], quote_xml(self.CWE_ID), nsmap[namespace_], eol_)) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -618,6 +626,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='CVSSVec if self.Environmental_Vector is not None: lwrite('<%s:Environmental_Vector>%s%s' % (nsmap[namespace_], quote_xml(self.Environmental_Vector), nsmap[namespace_], eol_)) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -710,6 +719,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Potenti for Potential_COA_ in self.Potential_COA: Potential_COA_.export(lwrite, level, nsmap, namespace_, name_='Potential_COA', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -725,16 +735,22 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): super(PotentialCOAsType, self).buildChildren(child_, node, nodeName_, True) # end class PotentialCOAsType + +@register_extension class ExploitTargetType(stix_common_binding.ExploitTargetBaseType): """Specifies the relevant STIX-ExploitTarget schema version for this content.""" subclass = None superclass = stix_common_binding.ExploitTargetBaseType + + xmlns = "http://stix.mitre.org/ExploitTarget-1" + xmlns_prefix = "et" + xml_type = "ExploitTargetType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, timestamp=None, version=None, Title=None, Description=None, Short_Description=None, Vulnerability=None, Weakness=None, Configuration=None, Potential_COAs=None, Information_Source=None, Handling=None, Related_Exploit_Targets=None, Related_Packages=None): super(ExploitTargetType, self).__init__(timestamp=timestamp, idref=idref, id=id) - self.xmlns = "http://stix.mitre.org/ExploitTarget-1" - self.xmlns_prefix = "et" - self.xml_type = "ExploitTargetType" + self.version = _cast(None, version) self.Title = Title self.Description = Description @@ -869,6 +885,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Exploit if self.Related_Packages is not None: self.Related_Packages.export(lwrite, level, nsmap, namespace_, name_='Related_Packages', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -951,7 +968,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -997,7 +1014,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/address/ciq_address_3_0.py b/stix/bindings/extensions/address/ciq_address_3_0.py index 6738143a..50938212 100644 --- a/stix/bindings/extensions/address/ciq_address_3_0.py +++ b/stix/bindings/extensions/address/ciq_address_3_0.py @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.stix_common as stix_common_binding XML_NS = "http://stix.mitre.org/extensions/Address#CIQAddress3.0-1" @@ -18,6 +21,7 @@ # Data representation classes. # +@register_extension class CIQAddress3_0InstanceType(stix_common_binding.AddressAbstractType): """The CIQAddress3.0InstanceType provides an extension to the stix_common_binding.AddressAbstractType which imports and leverages version 3.0 of @@ -25,12 +29,15 @@ class CIQAddress3_0InstanceType(stix_common_binding.AddressAbstractType): Addresses.""" subclass = None superclass = stix_common_binding.AddressAbstractType + + xmlns = XML_NS + xmlns_prefix = "stix-ciqaddress" + xml_type = "CIQAddress3.0InstanceType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, Location=None): super(CIQAddress3_0InstanceType, self).__init__() self.Location = Location - self.xmlns = XML_NS - self.xmlns_prefix = "ciqAddress" - self.xml_type = "CIQAddress3.0InstanceType" def factory(*args_, **kwargs_): if CIQAddress3_0InstanceType.subclass: return CIQAddress3_0InstanceType.subclass(*args_, **kwargs_) @@ -82,9 +89,10 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='CIQAddr eol_ = '' if self.Location is not None: showIndent(lwrite, level, pretty_print) - lwrite(etree_.tostring(self.Location, pretty_print=pretty_print)) + lwrite(etree_.tostring(self.Location, pretty_print=pretty_print).decode()) #self.Location.export(lwrite, level, nsmap, namespace_, name_='Location', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -105,7 +113,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -151,7 +159,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/attack_pattern/capec_2_7.py b/stix/bindings/extensions/attack_pattern/capec_2_7.py index e7cfbce5..2f8d92fd 100644 --- a/stix/bindings/extensions/attack_pattern/capec_2_7.py +++ b/stix/bindings/extensions/attack_pattern/capec_2_7.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.ttp as ttp_binding XML_NS = "http://stix.mitre.org/extensions/AP#CAPEC2.7-1" @@ -18,18 +21,22 @@ # Data representation classes. # +@register_extension class CAPEC2_7InstanceType(ttp_binding.AttackPatternType): """The CAPECInstanceType provides an extension to the APStructureAbstractType which imports and leverages the CAPEC schema for structured characterization of Attack Patterns.""" subclass = None superclass = ttp_binding.AttackPatternType + + xmlns = XML_NS + xmlns_prefix = "capecInstance" + xml_type = "CAPEC2.7InstanceType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, capec_id=None, Description=None, CAPEC=None): super(CAPEC2_7InstanceType, self).__init__(capec_id=capec_id, Description=Description) self.CAPEC = CAPEC - self.xmlns = XML_NS - self.xmlns_prefix = "capecInstance" - self.xml_type = "CAPEC2.7InstanceType" def factory(*args_, **kwargs_): if CAPEC2_7InstanceType.subclass: @@ -81,9 +88,10 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='CAPEC2. eol_ = '' if self.CAPEC is not None: showIndent(lwrite, level, pretty_print) - lwrite(etree_.tostring(self.CAPEC, pretty_print=pretty_print)) + lwrite(etree_.tostring(self.CAPEC, pretty_print=pretty_print).decode()) #self.CAPEC.export(lwrite, level, nsmap, namespace_, name_='CAPEC', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -104,7 +112,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -150,7 +158,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/identity/ciq_identity_3_0.py b/stix/bindings/extensions/identity/ciq_identity_3_0.py index ad8fd953..04795013 100644 --- a/stix/bindings/extensions/identity/ciq_identity_3_0.py +++ b/stix/bindings/extensions/identity/ciq_identity_3_0.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.stix_common as stix_common_binding XML_NS = "http://stix.mitre.org/extensions/Identity#CIQIdentity3.0-1" @@ -18,6 +21,8 @@ # Data representation classes. # + +@register_extension class CIQIdentity3_0InstanceType(stix_common_binding.IdentityType): """The CIQIdentity3.0InstanceType provides an extension to the IdentityStructureAbstractType which imports and leverages @@ -25,12 +30,14 @@ class CIQIdentity3_0InstanceType(stix_common_binding.IdentityType): characterization of Identities.""" subclass = None superclass = stix_common_binding.IdentityType + + xmlns = XML_NS + xmlns_prefix = "stix-ciqidentity" + xml_type = "CIQIdentity3.0InstanceType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, Name=None, Related_Identities=None, Specification=None, Role=None): super(CIQIdentity3_0InstanceType, self).__init__(idref=idref, id=id, Name=Name, Related_Identities=Related_Identities) - self.xmlns = XML_NS - self.xmlns_prefix = "stix-ciqidentity" - self.xml_type = "CIQIdentity3.0InstanceType" - self.xsi_type = None self.Specification = Specification if Role is None: self.Role = [] @@ -93,12 +100,13 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='CIQIden eol_ = '' if self.Specification is not None: showIndent(lwrite, level, pretty_print) - lwrite(etree_.tostring(self.Specification, pretty_print=pretty_print)) + lwrite(etree_.tostring(self.Specification, pretty_print=pretty_print).decode()) #self.Specification.export(lwrite, level, nsmap, namespace_, name_='Specification', pretty_print=pretty_print) for Role_ in self.Role: showIndent(lwrite, level, pretty_print) lwrite('<%s:Role>%s%s' % (nsmap[namespace_], quote_xml(Role_), nsmap[namespace_], eol_)) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -123,7 +131,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -169,7 +177,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/malware/maec_4_1.py b/stix/bindings/extensions/malware/maec_4_1.py index 5294cdc4..04787efd 100644 --- a/stix/bindings/extensions/malware/maec_4_1.py +++ b/stix/bindings/extensions/malware/maec_4_1.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,13 +9,12 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.ttp as ttp_binding -try: - from maec.bindings.maec_package import PackageType - maec_installed = True -except ImportError: - maec_installed = False + XML_NS = "http://stix.mitre.org/extensions/Malware#MAEC4.1-1" @@ -23,17 +22,22 @@ # Data representation classes. # + +@register_extension class MAEC4_1InstanceType(ttp_binding.MalwareInstanceType): """The MAEC4.1InstanceType provides an extension to ttp_binding.MalwareInstanceType - which imports and leverages the MAEC 4.0.1 schema for structured + which imports and leverages the MAEC 4.1 schema for structured characterization of Malware.""" subclass = None superclass = ttp_binding.MalwareInstanceType + + xmlns = XML_NS + xmlns_prefix = "stix-maec" + xml_type = "MAEC4.1InstanceType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, Type=None, Name=None, Description=None, MAEC=None): super(MAEC4_1InstanceType, self).__init__(Type=Type, Name=Name, Description=Description) - self.xmlns = XML_NS - self.xmlns_prefix = "stix-maec" - self.xml_type = "MAEC4.1InstanceType" self.MAEC = MAEC def factory(*args_, **kwargs_): if MAEC4_1InstanceType.subclass: @@ -80,12 +84,9 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='MAEC4.1 else: eol_ = '' if self.MAEC is not None: - if maec_installed and isinstance(self.MAEC, PackageType): - self.MAEC.export(lwrite, level, namespace_='stix-maec:', name_='MAEC', pretty_print=pretty_print) - else: - showIndent(lwrite, level, pretty_print) - lwrite(etree_.tostring(self.MAEC, pretty_print=pretty_print)) + self.MAEC.export(lwrite, level, namespace_='stix-maec:', name_='MAEC', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -95,12 +96,11 @@ def buildAttributes(self, node, attrs, already_processed): super(MAEC4_1InstanceType, self).buildAttributes(node, attrs, already_processed) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'MAEC': - if maec_installed: - obj_ = PackageType.factory() - obj_.build(child_) - self.set_MAEC(obj_) - else: - self.set_MAEC(child_) + # Fails hard if maec is not installed in your environment. + from maec.bindings.maec_package import PackageType + obj_ = PackageType.factory() + obj_.build(child_) + self.set_MAEC(obj_) super(MAEC4_1InstanceType, self).buildChildren(child_, node, nodeName_, True) # end class MAEC4_1InstanceType @@ -111,7 +111,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -157,7 +157,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) @@ -186,4 +186,4 @@ def main(): __all__ = [ "MAEC4_1InstanceType" - ] \ No newline at end of file + ] diff --git a/stix/bindings/extensions/marking/ais.py b/stix/bindings/extensions/marking/ais.py new file mode 100644 index 00000000..52a79f3f --- /dev/null +++ b/stix/bindings/extensions/marking/ais.py @@ -0,0 +1,573 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# See LICENSE.txt for complete terms. + +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys + +from mixbox.binding_utils import * +import stix.bindings.data_marking as data_marking_binding +from stix.bindings import register_extension + +XML_NS = "http://www.us-cert.gov/STIXMarkingStructure#AISConsentMarking-2" + + +# +# Data representation classes. +# + +@register_extension +class AISMarkingStructure(data_marking_binding.MarkingStructureType): + """ + The AISMarkingStructure is an implementation of the data marking schema + that allows determining consent to share information source attribution. + """ + subclass = None + superclass = data_marking_binding.MarkingStructureType + + xmlns = XML_NS + xmlns_prefix = "AIS" + xml_type = "AISMarkingStructure" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + + def __init__(self, idref=None, marking_model_ref=None, marking_model_name=None, id=None, Is_Proprietary=None, Not_Proprietary=None): + super(AISMarkingStructure, self).__init__(idref=idref, marking_model_ref=marking_model_ref, marking_model_name=marking_model_name, id=id) + self.Is_Proprietary = Is_Proprietary + self.Not_Proprietary = Not_Proprietary + + def factory(*args_, **kwargs_): + if AISMarkingStructure.subclass: + return AISMarkingStructure.subclass(*args_, **kwargs_) + else: + return AISMarkingStructure(*args_, **kwargs_) + factory = staticmethod(factory) + + def get_Is_Proprietary(self): + return self.Is_Proprietary + + def set_Is_Proprietary(self, Is_Proprietary): + self.Is_Proprietary = Is_Proprietary + + def get_Not_Proprietary(self): + return self.Not_Proprietary + + def set_Not_Proprietary(self, Not_Proprietary): + self.Not_Proprietary = Not_Proprietary + + def hasContent_(self): + if ( + self.Is_Proprietary is not None or + self.Not_Proprietary is not None or + super(AISMarkingStructure, self).hasContent_() + ): + return True + else: + return False + + def export(self, lwrite, level, nsmap, namespace_=XML_NS, name_='AISMarkingStructure', namespacedef_='', pretty_print=True): + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + showIndent(lwrite, level, pretty_print) + lwrite('<%s:%s%s' % (nsmap[namespace_], name_, namespacedef_ and ' ' + namespacedef_ or '', )) + already_processed = set() + self.exportAttributes(lwrite, level, already_processed, namespace_, name_='AISMarkingStructure') + if self.hasContent_(): + lwrite('>%s' % (eol_, )) + self.exportChildren(lwrite, level + 1, nsmap, XML_NS, name_, pretty_print=pretty_print) + showIndent(lwrite, level, pretty_print) + lwrite('%s' % (nsmap[namespace_], name_, eol_)) + else: + lwrite('/>%s' % (eol_, )) + + def exportAttributes(self, lwrite, level, already_processed, namespace_=XML_NS, name_='AISMarkingStructure'): + super(AISMarkingStructure, self).exportAttributes(lwrite, level, already_processed, namespace_, name_='AISMarkingStructure') + if 'xsi:type' not in already_processed: + already_processed.add('xsi:type') + xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) + lwrite(xsi_type) + def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='AISMarkingStructure', fromsubclass_=False, pretty_print=True): + super(AISMarkingStructure, self).exportChildren(lwrite, level, nsmap, namespace_, name_, True, pretty_print=pretty_print) + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + if self.Is_Proprietary is not None: + self.Is_Proprietary.export(lwrite, level, nsmap, namespace_, name_='Is_Proprietary', pretty_print=pretty_print) + if self.Not_Proprietary is not None: + self.Not_Proprietary.export(lwrite, level, nsmap, namespace_, name_='Not_Proprietary', pretty_print=pretty_print) + + def build(self, node): + self.__sourcenode__ = node + already_processed = set() + self.buildAttributes(node, node.attrib, already_processed) + for child in node: + nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] + self.buildChildren(child, node, nodeName_) + + def buildAttributes(self, node, attrs, already_processed): + super(AISMarkingStructure, self).buildAttributes(node, attrs, already_processed) + + def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): + if nodeName_ == 'Is_Proprietary': + obj_ = IsProprietary.factory() + obj_.build(child_) + self.set_Is_Proprietary(obj_) + if nodeName_ == 'Not_Proprietary': + obj_ = NotProprietary.factory() + obj_.build(child_) + self.set_Not_Proprietary(obj_) + super(AISMarkingStructure, self).buildChildren(child_, node, nodeName_, True) +# end class AISMarkingStructure + + +class IsProprietary(GeneratedsSuper): + subclass = None + superclass = None + + def __init__(self, CISA_Proprietary=None, AISConsent=None, TLPMarking=None): + self.CISA_Proprietary = _cast(bool, CISA_Proprietary) + self.AISConsent = AISConsent + self.TLPMarking = TLPMarking + + def factory(*args_, **kwargs_): + if IsProprietary.subclass: + return IsProprietary.subclass(*args_, **kwargs_) + else: + return IsProprietary(*args_, **kwargs_) + factory = staticmethod(factory) + + def get_AISConsent(self): + return self.AISConsent + + def set_AISConsent(self, AISConsent): + self.AISConsent = AISConsent + + def get_TLPMarking(self): + return self.TLPMarking + + def set_TLPMarking(self, TLPMarking): + self.TLPMarking = TLPMarking + + def get_CISA_Proprietary(self): + return self.CISA_Proprietary + + def set_CISA_Proprietary(self, CISA_Proprietary): + self.CISA_Proprietary = CISA_Proprietary + + def hasContent_(self): + if ( + self.AISConsent is not None or + self.TLPMarking is not None + ): + return True + else: + return False + + def export(self, lwrite, level, nsmap, namespace_=XML_NS, name_='IsProprietary', namespacedef_='', pretty_print=True): + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + showIndent(lwrite, level, pretty_print) + lwrite('<%s:%s%s' % (nsmap[namespace_], name_, namespacedef_ and ' ' + namespacedef_ or '', )) + already_processed = set() + self.exportAttributes(lwrite, level, already_processed, namespace_, name_='IsProprietary') + if self.hasContent_(): + lwrite('>%s' % (eol_, )) + self.exportChildren(lwrite, level + 1, nsmap, XML_NS, name_, pretty_print=pretty_print) + showIndent(lwrite, level, pretty_print) + lwrite('%s' % (nsmap[namespace_], name_, eol_)) + else: + lwrite('/>%s' % (eol_, )) + + def exportAttributes(self, lwrite, level, already_processed, namespace_=XML_NS, name_='IsProprietary'): + if self.CISA_Proprietary is not None and 'CISA_Proprietary' not in already_processed: + already_processed.add('CISA_Proprietary') + lwrite(' CISA_Proprietary="%s"' % self.gds_format_boolean(self.CISA_Proprietary, input_name='CISA_Proprietary')) + + def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='IsProprietary', fromsubclass_=False, pretty_print=True): + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + if self.AISConsent is not None: + self.AISConsent.export(lwrite, level, nsmap, namespace_, name_='AISConsent', pretty_print=pretty_print) + if self.TLPMarking is not None: + self.TLPMarking.export(lwrite, level, nsmap, namespace_, name_='TLPMarking', pretty_print=pretty_print) + + def build(self, node): + self.__sourcenode__ = node + already_processed = set() + self.buildAttributes(node, node.attrib, already_processed) + for child in node: + nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] + self.buildChildren(child, node, nodeName_) + + def buildAttributes(self, node, attrs, already_processed): + value = find_attr_value_('CISA_Proprietary', node) + if value is not None and 'CISA_Proprietary' not in already_processed: + already_processed.add('CISA_Proprietary') + if value in ('true', '1'): + self.CISA_Proprietary = True + elif value in ('false', '0'): + self.CISA_Proprietary = False + else: + raise_parse_error(node, 'Bad boolean attribute') + + def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): + if nodeName_ == 'AISConsent': + obj_ = AISConsentType.factory() + obj_.build(child_) + self.set_AISConsent(obj_) + elif nodeName_ == 'TLPMarking': + obj_ = TLPMarkingType.factory() + obj_.build(child_) + self.set_TLPMarking(obj_) +# end class IsProprietary + + +class NotProprietary(GeneratedsSuper): + subclass = None + superclass = None + + def __init__(self, CISA_Proprietary=None, AISConsent=None, TLPMarking=None): + self.CISA_Proprietary = _cast(bool, CISA_Proprietary) + self.AISConsent = AISConsent + self.TLPMarking = TLPMarking + + def factory(*args_, **kwargs_): + if NotProprietary.subclass: + return NotProprietary.subclass(*args_, **kwargs_) + else: + return NotProprietary(*args_, **kwargs_) + factory = staticmethod(factory) + + def get_AISConsent(self): + return self.AISConsent + + def set_AISConsent(self, AISConsent): + self.AISConsent = AISConsent + + def get_TLPMarking(self): + return self.TLPMarking + + def set_TLPMarking(self, TLPMarking): + self.TLPMarking = TLPMarking + + def get_CISA_Proprietary(self): + return self.CISA_Proprietary + + def set_CISA_Proprietary(self, CISA_Proprietary): + self.CISA_Proprietary = CISA_Proprietary + + def hasContent_(self): + if ( + self.AISConsent is not None or + self.TLPMarking is not None + ): + return True + else: + return False + + def export(self, lwrite, level, nsmap, namespace_=XML_NS, name_='NotProprietary', namespacedef_='', pretty_print=True): + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + showIndent(lwrite, level, pretty_print) + lwrite('<%s:%s%s' % (nsmap[namespace_], name_, namespacedef_ and ' ' + namespacedef_ or '', )) + already_processed = set() + self.exportAttributes(lwrite, level, already_processed, namespace_, name_='NotProprietary') + if self.hasContent_(): + lwrite('>%s' % (eol_, )) + self.exportChildren(lwrite, level + 1, nsmap, XML_NS, name_, pretty_print=pretty_print) + showIndent(lwrite, level, pretty_print) + lwrite('%s' % (nsmap[namespace_], name_, eol_)) + else: + lwrite('/>%s' % (eol_, )) + + def exportAttributes(self, lwrite, level, already_processed, namespace_=XML_NS, name_='NotProprietary'): + if self.CISA_Proprietary is not None and 'CISA_Proprietary' not in already_processed: + already_processed.add('CISA_Proprietary') + lwrite(' CISA_Proprietary="%s"' % self.gds_format_boolean(self.CISA_Proprietary, input_name='CISA_Proprietary')) + + def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='NotProprietary', fromsubclass_=False, pretty_print=True): + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + if self.AISConsent is not None: + self.AISConsent.export(lwrite, level, nsmap, namespace_, name_='AISConsent', pretty_print=pretty_print) + if self.TLPMarking is not None: + self.TLPMarking.export(lwrite, level, nsmap, namespace_, name_='TLPMarking', pretty_print=pretty_print) + + def build(self, node): + self.__sourcenode__ = node + already_processed = set() + self.buildAttributes(node, node.attrib, already_processed) + for child in node: + nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] + self.buildChildren(child, node, nodeName_) + + def buildAttributes(self, node, attrs, already_processed): + value = find_attr_value_('CISA_Proprietary', node) + if value is not None and 'CISA_Proprietary' not in already_processed: + already_processed.add('CISA_Proprietary') + if value in ('true', '1'): + self.CISA_Proprietary = True + elif value in ('false', '0'): + self.CISA_Proprietary = False + else: + raise_parse_error(node, 'Bad boolean attribute') + + def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): + if nodeName_ == 'AISConsent': + obj_ = AISConsentType.factory() + obj_.build(child_) + self.set_AISConsent(obj_) + elif nodeName_ == 'TLPMarking': + obj_ = TLPMarkingType.factory() + obj_.build(child_) + self.set_TLPMarking(obj_) +# end class NotProprietary + + +class AISConsentType(GeneratedsSuper): + subclass = None + superclass = None + + def __init__(self, consent=None): + self.consent = _cast(None, consent) + pass + + def factory(*args_, **kwargs_): + if AISConsentType.subclass: + return AISConsentType.subclass(*args_, **kwargs_) + else: + return AISConsentType(*args_, **kwargs_) + factory = staticmethod(factory) + + def get_consent(self): + return self.consent + + def set_consent(self, consent): + self.consent = consent + + def hasContent_(self): + if ( + + ): + return True + else: + return False + + def export(self, lwrite, level, nsmap, namespace_=XML_NS, name_='AISConsentType', namespacedef_='', pretty_print=True): + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + showIndent(lwrite, level, pretty_print) + lwrite('<%s:%s%s' % (nsmap[namespace_], name_, namespacedef_ and ' ' + namespacedef_ or '', )) + already_processed = set() + self.exportAttributes(lwrite, level, already_processed, namespace_, name_='AISConsentType') + if self.hasContent_(): + lwrite('>%s' % (eol_, )) + self.exportChildren(lwrite, level + 1, nsmap, XML_NS, name_, pretty_print=pretty_print) + lwrite('%s' % (nsmap[namespace_], name_, eol_)) + else: + lwrite('/>%s' % (eol_, )) + + def exportAttributes(self, lwrite, level, already_processed, namespace_=XML_NS, name_='AISConsentType'): + if self.consent is not None and 'consent' not in already_processed: + already_processed.add('consent') + lwrite(' consent=%s' % (quote_attrib(self.consent), )) + + def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='AISConsentType', fromsubclass_=False, pretty_print=True): + pass + + def build(self, node): + self.__sourcenode__ = node + already_processed = set() + self.buildAttributes(node, node.attrib, already_processed) + for child in node: + nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] + self.buildChildren(child, node, nodeName_) + + def buildAttributes(self, node, attrs, already_processed): + value = find_attr_value_('consent', node) + if value is not None and 'consent' not in already_processed: + already_processed.add('consent') + self.consent = value + + def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): + pass +# end class AISConsentType + + +class TLPMarkingType(GeneratedsSuper): + subclass = None + superclass = None + + def __init__(self, color=None): + self.color = _cast(None, color) + pass + + def factory(*args_, **kwargs_): + if TLPMarkingType.subclass: + return TLPMarkingType.subclass(*args_, **kwargs_) + else: + return TLPMarkingType(*args_, **kwargs_) + factory = staticmethod(factory) + + def get_color(self): + return self.color + + def set_color(self, color): + self.color = color + + def hasContent_(self): + if ( + + ): + return True + else: + return False + + def export(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TLPMarkingType', namespacedef_='', pretty_print=True): + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + showIndent(lwrite, level, pretty_print) + lwrite('<%s:%s%s' % (nsmap[namespace_], name_, namespacedef_ and ' ' + namespacedef_ or '', )) + already_processed = set() + self.exportAttributes(lwrite, level, already_processed, namespace_, name_='TLPMarkingType') + if self.hasContent_(): + lwrite('>%s' % (eol_, )) + self.exportChildren(lwrite, level + 1, nsmap, XML_NS, name_, pretty_print=pretty_print) + lwrite('%s' % (nsmap[namespace_], name_, eol_)) + else: + lwrite('/>%s' % (eol_, )) + + def exportAttributes(self, lwrite, level, already_processed, namespace_=XML_NS, name_='TLPMarkingType'): + if self.color is not None and 'color' not in already_processed: + already_processed.add('color') + lwrite(' color=%s' % (quote_attrib(self.color), )) + + def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TLPMarkingType', fromsubclass_=False, pretty_print=True): + pass + + def build(self, node): + self.__sourcenode__ = node + already_processed = set() + self.buildAttributes(node, node.attrib, already_processed) + for child in node: + nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] + self.buildChildren(child, node, nodeName_) + + def buildAttributes(self, node, attrs, already_processed): + value = find_attr_value_('color', node) + if value is not None and 'color' not in already_processed: + already_processed.add('color') + self.color = value + + def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): + pass +# end class TLPMarkingType + +GDSClassesMapping = {} + +USAGE_TEXT = """ +Usage: python .py [ -s ] +""" + + +def usage(): + print(USAGE_TEXT) + sys.exit(1) + + +def get_root_tag(node): + tag = Tag_pattern_.match(node.tag).groups()[-1] + rootClass = GDSClassesMapping.get(tag) + if rootClass is None: + rootClass = globals().get(tag) + return tag, rootClass + + +def parse(inFileName): + doc = parsexml_(inFileName) + rootNode = doc.getroot() + rootTag, rootClass = get_root_tag(rootNode) + if rootClass is None: + rootTag = 'AISMarkingStructure' + rootClass = AISMarkingStructure + rootObj = rootClass.factory() + rootObj.build(rootNode) + # Enable Python to collect the space used by the DOM. + doc = None + # sys.stdout.write('\n') + # rootObj.export(sys.stdout, 0, name_=rootTag, + # namespacedef_='', + # pretty_print=True) + return rootObj + + +def parseEtree(inFileName): + doc = parsexml_(inFileName) + rootNode = doc.getroot() + rootTag, rootClass = get_root_tag(rootNode) + if rootClass is None: + rootTag = 'AISMarkingStructure' + rootClass = AISMarkingStructure + rootObj = rootClass.factory() + rootObj.build(rootNode) + # Enable Python to collect the space used by the DOM. + doc = None + rootElement = rootObj.to_etree(None, name_=rootTag) + content = etree_.tostring(rootElement, pretty_print=True, + xml_declaration=True, encoding="utf-8") + sys.stdout.write(content) + sys.stdout.write('\n') + return rootObj, rootElement + + +def parseString(inString): + from mixbox.vendor.six import StringIO + doc = parsexml_(StringIO(inString)) + rootNode = doc.getroot() + rootTag, rootClass = get_root_tag(rootNode) + if rootClass is None: + rootTag = 'AISMarkingStructure' + rootClass = AISMarkingStructure + rootObj = rootClass.factory() + rootObj.build(rootNode) + # Enable Python to collect the space used by the DOM. + doc = None + # sys.stdout.write('\n') + # rootObj.export(sys.stdout, 0, name_="AISHandling", + # namespacedef_='') + return rootObj + + +def main(): + args = sys.argv[1:] + if len(args) == 1: + parse(args[0]) + else: + usage() + +if __name__ == '__main__': + #import pdb; pdb.set_trace() + main() + +__all__ = [ + "NotProprietary", + "IsProprietary" + "AISConsentType", + "AISMarkingStructure" + "TLPMarkingType" + ] diff --git a/stix/bindings/extensions/marking/simple_marking.py b/stix/bindings/extensions/marking/simple_marking.py index 54f43f17..61671ac5 100644 --- a/stix/bindings/extensions/marking/simple_marking.py +++ b/stix/bindings/extensions/marking/simple_marking.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.data_marking as data_marking_binding XML_NS = "http://data-marking.mitre.org/extensions/MarkingStructure#Simple-1" @@ -18,6 +21,8 @@ # Data representation classes. # + +@register_extension class SimpleMarkingStructureType(data_marking_binding.MarkingStructureType): """The SimpleMarkingStructureType is a basic implementation of the data marking schema that allows for a string statement to be @@ -28,6 +33,7 @@ class SimpleMarkingStructureType(data_marking_binding.MarkingStructureType): xmlns = XML_NS xmlns_prefix = "simpleMarking" xml_type = "SimpleMarkingStructureType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) subclass = None superclass = data_marking_binding.MarkingStructureType @@ -86,6 +92,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='SimpleM showIndent(lwrite, level, pretty_print) lwrite('<%s:Statement>%s%s' % (nsmap[namespace_], quote_xml(self.Statement), nsmap[namespace_], eol_)) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -101,7 +108,6 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): super(SimpleMarkingStructureType, self).buildChildren(child_, node, nodeName_, True) # end class SimpleMarkingStructureType -data_marking_binding.add_extension(SimpleMarkingStructureType) GDSClassesMapping = {} @@ -110,7 +116,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -156,7 +162,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/marking/terms_of_use_marking.py b/stix/bindings/extensions/marking/terms_of_use_marking.py index 46c8d5e6..f3152e78 100644 --- a/stix/bindings/extensions/marking/terms_of_use_marking.py +++ b/stix/bindings/extensions/marking/terms_of_use_marking.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.data_marking as data_marking_binding XML_NS = "http://data-marking.mitre.org/extensions/MarkingStructure#Terms_Of_Use-1" @@ -18,6 +21,7 @@ # Data representation classes. # +@register_extension class TermsOfUseMarkingStructureType(data_marking_binding.MarkingStructureType): """The TermsOfUseMarkingStructureType is a basic implementation of the data marking schema that allows for a string statement @@ -36,6 +40,7 @@ class TermsOfUseMarkingStructureType(data_marking_binding.MarkingStructureType): xmlns = XML_NS xmlns_prefix = "TOUMarking" xml_type = "TermsOfUseMarkingStructureType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) def __init__(self, idref=None, marking_model_ref=None, marking_model_name=None, id=None, Terms_Of_Use=None): super(TermsOfUseMarkingStructureType, self).__init__(idref=idref, marking_model_ref=marking_model_ref, marking_model_name=marking_model_name, id=id) @@ -89,6 +94,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TermsOf showIndent(lwrite, level, pretty_print) lwrite('<%s:Terms_Of_Use>%s%s' % (nsmap[namespace_], quote_xml(self.Terms_Of_Use), nsmap[namespace_], eol_)) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -104,8 +110,6 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): super(TermsOfUseMarkingStructureType, self).buildChildren(child_, node, nodeName_, True) # end class TermsOfUseMarkingStructureType -data_marking_binding.add_extension(TermsOfUseMarkingStructureType) - GDSClassesMapping = {} USAGE_TEXT = """ @@ -113,7 +117,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -159,7 +163,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/marking/tlp.py b/stix/bindings/extensions/marking/tlp.py index bf71fbba..83a68e1d 100644 --- a/stix/bindings/extensions/marking/tlp.py +++ b/stix/bindings/extensions/marking/tlp.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.data_marking as data_marking_binding XML_NS = "http://data-marking.mitre.org/extensions/MarkingStructure#TLP-1" @@ -18,6 +21,7 @@ # Data representation classes. # +@register_extension class TLPMarkingStructureType(data_marking_binding.MarkingStructureType): """The TLPMarkingStructureType is an implementation of the data marking schema that allows for a TLP Designation to be attached to an @@ -29,6 +33,7 @@ class TLPMarkingStructureType(data_marking_binding.MarkingStructureType): xmlns = XML_NS xmlns_prefix = "tlpMarking" xml_type = "TLPMarkingStructureType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) subclass = None superclass = data_marking_binding.MarkingStructureType @@ -83,6 +88,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TLPMark super(TLPMarkingStructureType, self).exportChildren(lwrite, level, nsmap, namespace_, name_, True, pretty_print=pretty_print) pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -99,7 +105,6 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): pass # end class TLPMarkingStructureType -data_marking_binding.add_extension(TLPMarkingStructureType) GDSClassesMapping = {} @@ -108,7 +113,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -154,7 +159,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/structured_coa/generic.py b/stix/bindings/extensions/structured_coa/generic.py index a7cd6b87..73991c69 100644 --- a/stix/bindings/extensions/structured_coa/generic.py +++ b/stix/bindings/extensions/structured_coa/generic.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension from stix.bindings.course_of_action import StructuredCOAType import stix.bindings.stix_common as stix_common_binding @@ -19,6 +22,8 @@ # Data representation classes. # + +@register_extension class GenericStructuredCOAType(StructuredCOAType): """The GenericStructuredCOAType specifies an instantial extension from the abstract course_of_action_binding.StructuredCOAType intended to support the generic @@ -26,11 +31,14 @@ class GenericStructuredCOAType(StructuredCOAType): location of the Generic Structured COA.""" subclass = None superclass = StructuredCOAType + + xmlns = XML_NS + xmlns_prefix = "genericStructuredCOA" + xml_type = "GenericStructuredCOAType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, reference_location=None, Description=None, Type=None, Specification=None): super(GenericStructuredCOAType, self).__init__(idref=idref, id=id) - self.xmlns = XML_NS - self.xmlns_prefix = "genericStructuredCOA" - self.xml_type = "GenericStructuredCOAType" self.reference_location = _cast(None, reference_location) self.Description = Description self.Type = Type @@ -101,6 +109,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Generic if self.Specification is not None: self.Specification.export(lwrite, level, nsmap, namespace_, name_='Specification', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -135,7 +144,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -164,7 +173,7 @@ def parse(inFileName): def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/test_mechanism/generic.py b/stix/bindings/extensions/test_mechanism/generic.py index 8afd5efd..5a40d9a9 100644 --- a/stix/bindings/extensions/test_mechanism/generic.py +++ b/stix/bindings/extensions/test_mechanism/generic.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.indicator as indicator_binding import stix.bindings.stix_common as stix_common_binding @@ -19,6 +22,7 @@ # Data representation classes. # +@register_extension class GenericTestMechanismType(indicator_binding.TestMechanismType): """The GenericTestMechanismType specifies an instantial extension from the abstract indicator_binding.TestMechanismType intended to support the generic @@ -26,11 +30,15 @@ class GenericTestMechanismType(indicator_binding.TestMechanismType): URL for the location of the Generic Test Mechanism.""" subclass = None superclass = indicator_binding.TestMechanismType + + xmlns = XML_NS + xmlns_prefix = "genericTM" + xml_type = "GenericTestMechanismType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + + def __init__(self, idref=None, id=None, Efficacy=None, Producer=None, reference_location=None, Description=None, Type=None, Specification=None): super(GenericTestMechanismType, self).__init__(idref=idref, id=id, Efficacy=Efficacy, Producer=Producer) - self.xmlns = XML_NS - self.xmlns_prefix = "genericTM" - self.xml_type = "GenericTestMechanismType" self.reference_location = _cast(None, reference_location) self.Description = Description self.Type = Type @@ -81,10 +89,10 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='generic # already_processed.add('xmlns') # xmlns = " xmlns:%s='%s'" % (self.xmlns_prefix, self.xmlns) # lwrite(xmlns) - # if 'xsi:type' not in already_processed: - # already_processed.add('xsi:type') - # xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) - # lwrite(xsi_type) + if 'xsi:type' not in already_processed: + already_processed.add('xsi:type') + xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) + lwrite(xsi_type) if self.reference_location is not None and 'reference_location' not in already_processed: already_processed.add('reference_location') lwrite(' reference_location=%s' % (quote_attrib(self.reference_location), )) @@ -101,6 +109,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Generic if self.Specification is not None: self.Specification.export(lwrite, level, nsmap, namespace_, name_='Specification', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -135,7 +144,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -181,7 +190,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/test_mechanism/open_ioc_2010.py b/stix/bindings/extensions/test_mechanism/open_ioc_2010.py index 42339cb6..88dec64d 100644 --- a/stix/bindings/extensions/test_mechanism/open_ioc_2010.py +++ b/stix/bindings/extensions/test_mechanism/open_ioc_2010.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.indicator as indicator_binding XML_NS = "http://stix.mitre.org/extensions/TestMechanism#OpenIOC2010-1" @@ -18,6 +21,7 @@ # Data representation classes. # +@register_extension class OpenIOC2010TestMechanismType(indicator_binding.TestMechanismType): """The OpenIOC2010TestMechanismType provides an extension to the indicator_binding.TestMechanismType which imports and leverages the 2010 Open IOC @@ -25,11 +29,14 @@ class OpenIOC2010TestMechanismType(indicator_binding.TestMechanismType): mechanism.""" subclass = None superclass = indicator_binding.TestMechanismType + + xmlns = XML_NS + xmlns_prefix = "stix-openioc" + xml_type = "OpenIOC2010TestMechanismType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, Efficacy=None, Producer=None, ioc=None): super(OpenIOC2010TestMechanismType, self).__init__(idref=idref, id=id, Efficacy=Efficacy, Producer=Producer) - self.xmlns = XML_NS - self.xmlns_prefix = "stix-openioc" - self.xml_type = "OpenIOC2010TestMechanismType" self.ioc = ioc def factory(*args_, **kwargs_): if OpenIOC2010TestMechanismType.subclass: @@ -69,10 +76,10 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='', name # already_processed.add('xmlns') # xmlns = " xmlns:%s='%s'" % (self.xmlns_prefix, self.xmlns) # lwrite(xmlns) - # if 'xsi:type' not in already_processed: - # already_processed.add('xsi:type') - # xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) - # lwrite(xsi_type) + if 'xsi:type' not in already_processed: + already_processed.add('xsi:type') + xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) + lwrite(xsi_type) def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='OpenIOC2010TestMechanismType', fromsubclass_=False, pretty_print=True): super(OpenIOC2010TestMechanismType, self).exportChildren(lwrite, level, nsmap, indicator_binding.XML_NS, name_, True, pretty_print=pretty_print) if pretty_print: @@ -81,9 +88,10 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='OpenIOC eol_ = '' if self.ioc is not None: showIndent(lwrite, level, pretty_print) - lwrite(etree_.tostring(self.ioc, pretty_print=pretty_print)) + lwrite(etree_.tostring(self.ioc, pretty_print=pretty_print).decode()) #self.ioc.export(lwrite, level, nsmap, namespace_, name_='ioc', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -104,7 +112,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -150,7 +158,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/test_mechanism/oval_5_10.py b/stix/bindings/extensions/test_mechanism/oval_5_10.py index 6e5aba15..2ce06695 100644 --- a/stix/bindings/extensions/test_mechanism/oval_5_10.py +++ b/stix/bindings/extensions/test_mechanism/oval_5_10.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.indicator as indicator_binding XML_NS = "http://stix.mitre.org/extensions/TestMechanism#OVAL5.10-1" @@ -18,17 +21,22 @@ # Data representation classes. # + +@register_extension class OVAL5_10TestMechanismType(indicator_binding.TestMechanismType): """The OVALTestMechanismType provides an extension to the indicator_binding.TestMechanismType which imports and leverages the OVAL schema in order to include OVAL Definitions as the test mechanism.""" subclass = None superclass = indicator_binding.TestMechanismType + + xmlns = XML_NS + xmlns_prefix = "ovalTM" + xml_type = "OVAL5.10TestMechanismType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, Efficacy=None, Producer=None, oval_definitions=None, oval_variables=None): super(OVAL5_10TestMechanismType, self).__init__(idref=idref, id=id, Efficacy=Efficacy, Producer=Producer) - self.xmlns = XML_NS - self.xmlns_prefix = "ovalTM" - self.xml_type = "OVAL5.10TestMechanismType" self.oval_definitions = oval_definitions self.oval_variables = oval_variables def factory(*args_, **kwargs_): @@ -72,10 +80,10 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='', name # already_processed.add('xmlns') # xmlns = " xmlns:%s='%s'" % (self.xmlns_prefix, self.xmlns) # lwrite(xmlns) - # if 'xsi:type' not in already_processed: - # already_processed.add('xsi:type') - # xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) - # lwrite(xsi_type) + if 'xsi:type' not in already_processed: + already_processed.add('xsi:type') + xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) + lwrite(xsi_type) def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='OVAL5.10TestMechanismType', fromsubclass_=False, pretty_print=True): super(OVAL5_10TestMechanismType, self).exportChildren(lwrite, level, nsmap, indicator_binding.XML_NS, name_, True, pretty_print=pretty_print) if pretty_print: @@ -84,13 +92,14 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='OVAL5.1 eol_ = '' if self.oval_definitions is not None: showIndent(lwrite, level, pretty_print) - lwrite(etree_.tostring(self.oval_definitions, pretty_print=pretty_print)) + lwrite(etree_.tostring(self.oval_definitions, pretty_print=pretty_print).decode()) #self.oval_definitions.export(lwrite, level, nsmap, namespace_, name_='oval_definitions', pretty_print=pretty_print) if self.oval_variables is not None: showIndent(lwrite, level, pretty_print) - lwrite(etree_.tostring(self.oval_variables, pretty_print=pretty_print)) + lwrite(etree_.tostring(self.oval_variables, pretty_print=pretty_print).decode()) #self.oval_variables.export(lwrite, level, nsmap, namespace_, name_='oval_variables', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -113,7 +122,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -159,7 +168,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/test_mechanism/snort.py b/stix/bindings/extensions/test_mechanism/snort.py index 84a384a0..27f72d5a 100644 --- a/stix/bindings/extensions/test_mechanism/snort.py +++ b/stix/bindings/extensions/test_mechanism/snort.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.indicator as indicator_binding import stix.bindings.stix_common as stix_common_binding @@ -19,17 +22,21 @@ # Data representation classes. # +@register_extension class SnortTestMechanismType(indicator_binding.TestMechanismType): """The SnortTestMechanismType specifies an instantial extension from the abstract TestMechanismType intended to support the inclusion of a Snort rule as a test mechanism content.""" subclass = None superclass = indicator_binding.TestMechanismType + + xmlns = XML_NS + xmlns_prefix = "snortTM" + xml_type = "SnortTestMechanismType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, Efficacy=None, Producer=None, Product_Name=None, Version=None, Rule=None, Event_Filter=None, Rate_Filter=None, Event_Suppression=None): super(SnortTestMechanismType, self).__init__(idref=idref, id=id, Efficacy=Efficacy, Producer=Producer) - self.xmlns = XML_NS - self.xmlns_prefix = "snortTM" - self.xml_type = "SnortTestMechanismType" self.Product_Name = Product_Name self.Version = Version if Rule is None: @@ -109,10 +116,10 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='snortTM # already_processed.add('xmlns') # xmlns = " xmlns:%s='%s'" % (self.xmlns_prefix, self.xmlns) # lwrite(xmlns) - # if 'xsi:type' not in already_processed: - # already_processed.add('xsi:type') - # xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) - # lwrite(xsi_type) + if 'xsi:type' not in already_processed: + already_processed.add('xsi:type') + xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) + lwrite(xsi_type) def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='SnortTestMechanismType', fromsubclass_=False, pretty_print=True): super(SnortTestMechanismType, self).exportChildren(lwrite, level, nsmap, indicator_binding.XML_NS, name_, True, pretty_print=pretty_print) if pretty_print: @@ -134,6 +141,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='SnortTe for Event_Suppression_ in self.Event_Suppression: Event_Suppression_.export(lwrite, level, nsmap, namespace_, name_='Event_Suppression', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -179,7 +187,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -225,7 +233,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/test_mechanism/yara.py b/stix/bindings/extensions/test_mechanism/yara.py index 3bacf92d..2e132b3b 100644 --- a/stix/bindings/extensions/test_mechanism/yara.py +++ b/stix/bindings/extensions/test_mechanism/yara.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.indicator as indicator_binding import stix.bindings.stix_common as stix_common_binding @@ -19,17 +22,21 @@ # Data representation classes. # +@register_extension class YaraTestMechanismType(indicator_binding.TestMechanismType): """The YaraTestMechanismType specifies an instantial extension from the abstract indicator_binding.TestMechanismType intended to support the inclusion of a YARA rule as a test mechanism content.""" subclass = None superclass = indicator_binding.TestMechanismType + + xmlns = XML_NS + xmlns_prefix = "yaraTM" + xml_type = "YaraTestMechanismType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, Efficacy=None, Producer=None, Version=None, Rule=None): super(YaraTestMechanismType, self).__init__(idref=idref, id=id, Efficacy=Efficacy, Producer=Producer) - self.xmlns = XML_NS - self.xmlns_prefix = "yaraTM" - self.xml_type = "YaraTestMechanismType" self.Version = Version self.Rule = Rule def factory(*args_, **kwargs_): @@ -73,10 +80,10 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='yaraTM: # already_processed.add('xmlns') # xmlns = " xmlns:%s='%s'" % (self.xmlns_prefix, self.xmlns) # lwrite(xmlns) - # if 'xsi:type' not in already_processed: - # already_processed.add('xsi:type') - # xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) - # lwrite(xsi_type) + if 'xsi:type' not in already_processed: + already_processed.add('xsi:type') + xsi_type = " xsi:type='%s:%s'" % (self.xmlns_prefix, self.xml_type) + lwrite(xsi_type) def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='YaraTestMechanismType', fromsubclass_=False, pretty_print=True): super(YaraTestMechanismType, self).exportChildren(lwrite, level, nsmap, indicator_binding.XML_NS, name_, True, pretty_print=pretty_print) if pretty_print: @@ -89,6 +96,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='YaraTes if self.Rule is not None: self.Rule.export(lwrite, level, nsmap, namespace_, name_='Rule', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -115,7 +123,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -161,7 +169,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/extensions/vulnerability/cvrf_1_1.py b/stix/bindings/extensions/vulnerability/cvrf_1_1.py index c7eed68e..7423a00a 100644 --- a/stix/bindings/extensions/vulnerability/cvrf_1_1.py +++ b/stix/bindings/extensions/vulnerability/cvrf_1_1.py @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import register_extension import stix.bindings.exploit_target as exploit_target_binding XML_NS = "http://stix.mitre.org/extensions/Vulnerability#CVRF-1" @@ -18,6 +21,7 @@ # Data representation classes. # +@register_extension class CVRF1_1InstanceType(exploit_target_binding.VulnerabilityType): """The CVRF1.1InstanceType provides an extension to the exploit_target_binding.VulnerabilityType which imports and leverages the CVRF schema @@ -26,11 +30,14 @@ class CVRF1_1InstanceType(exploit_target_binding.VulnerabilityType): do not have a CVE or OSVDB ID.""" subclass = None superclass = exploit_target_binding.VulnerabilityType + + xmlns = XML_NS + xmlns_prefix = "cvrfVuln" + xml_type = "CVRF1.1InstanceType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, Description=None, CVE_ID=None, OSVDB_ID=None, CVSS_Score=None, cvrfdoc=None): super(CVRF1_1InstanceType, self).__init__(Description=Description, CVE_ID=CVE_ID, OSVDB_ID=OSVDB_ID, CVSS_Score=CVSS_Score) - self.xmlns = XML_NS - self.xmlns_prefix = "cvrfVuln" - self.xml_type = "CVRF1.1InstanceType" self.cvrfdoc = cvrfdoc def factory(*args_, **kwargs_): if CVRF1_1InstanceType.subclass: @@ -82,9 +89,10 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='CVRF1.1 eol_ = '' if self.cvrfdoc is not None: showIndent(lwrite, level, pretty_print) - lwrite(etree_.tostring(self.cvrfdoc, pretty_print=pretty_print)) + lwrite(etree_.tostring(self.cvrfdoc, pretty_print=pretty_print).decode()) #self.cvrfdoc.export(lwrite, level, nsmap, namespace_, name_='cvrfdoc', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -105,7 +113,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -151,7 +159,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/incident.py b/stix/bindings/incident.py index 156067ff..b3c4052e 100644 --- a/stix/bindings/incident.py +++ b/stix/bindings/incident.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -8,9 +8,12 @@ # Generated Thu Apr 11 15:06:24 2013 by generateDS.py version 2.9a. # import sys -from stix.bindings import * + import cybox.bindings.cybox_core as cybox_core_binding import cybox.bindings.cybox_common as cybox_common_binding +from mixbox.binding_utils import * + +from stix.bindings import lookup_extension, register_extension import stix.bindings.stix_common as stix_common_binding import stix.bindings.data_marking as data_marking_binding @@ -90,6 +93,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Propert if self.Non_Public_Data_Compromised is not None: self.Non_Public_Data_Compromised.export(lwrite, level, nsmap, namespace_, name_='Non_Public_Data_Compromised', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -214,6 +218,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Affecte if self.Structured_Description is not None: self.Structured_Description.export(lwrite, level, "%s:" % (nsmap[namespace_]), name_='Structured_Description', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -247,24 +252,8 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): obj_.build(child_) self.set_Location_Class(obj_) elif nodeName_ == 'Location': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "CIQAddress3.0InstanceType": - import stix.bindings.extensions.address.ciq_address_3_0 as ciq_address_binding - obj_ = ciq_address_binding.CIQAddress3_0InstanceType.factory() - else: - raise NotImplementedError('No implementation class found for: ' + type_name_) - else: - raise NotImplementedError('Class not implemented for element') - + from .extensions.address import ciq_address_3_0 + obj_ = lookup_extension(child_).factory() obj_.build(child_) self.set_Location(obj_) elif nodeName_ == 'Nature_Of_Security_Effect': @@ -355,6 +344,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ImpactA if self.External_Impact_Assessment_Model is not None: self.External_Impact_Assessment_Model.export(lwrite, level, nsmap, namespace_, name_='External_Impact_Assessment_Model', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -457,6 +447,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='inciden def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ExternalImpactAssessmentModelType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -540,6 +531,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='COATake if self.Course_Of_Action is not None: self.Course_Of_Action.export(lwrite, level, nsmap, namespace_, name_='Course_Of_Action', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -560,24 +552,8 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): obj_.build(child_) self.set_Contributors(obj_) elif nodeName_ == 'Course_Of_Action': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "CourseOfActionType": - import stix.bindings.course_of_action as coa_binding - obj_ = coa_binding.CourseOfActionType.factory() - else: - raise NotImplementedError('Class not implemented for element type: ' + type_name_) - else: - obj_ = stix_common_binding.CourseOfActionBaseType.factory() # not abstract - + from . import course_of_action + obj_ = lookup_extension(child_, stix_common_binding.CourseOfActionBaseType).factory() obj_.build(child_) self.set_Course_Of_Action(obj_) # end class COATakenType @@ -651,6 +627,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='inciden def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='JournalEntryType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) self.valueOf_ = get_all_text_(node) @@ -663,7 +640,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('time') try: self.time = self.gds_parse_datetime(value, node, 'time') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (time): %s' % exp) value = find_attr_value_('time_precision', node) if value is not None and 'time_precision' not in already_processed: @@ -726,6 +703,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='inciden def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='COARequestedType', fromsubclass_=False, pretty_print=True): super(COARequestedType, self).exportChildren(lwrite, level, nsmap, namespace_, name_, True, pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -793,6 +771,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Contrib for Contributor_ in self.Contributor: Contributor_.export(lwrite, level, "%s:" % (nsmap[namespace_]), name_='Contributor', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -859,6 +838,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='COATime if self.End is not None: self.End.export(lwrite, level, nsmap, namespace_, name_='End', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -928,6 +908,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='inciden def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='LossEstimationType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -998,6 +979,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TotalLo if self.Actual_Total_Loss_Estimation is not None: self.Actual_Total_Loss_Estimation.export(lwrite, level, nsmap, namespace_, name_='Actual_Total_Loss_Estimation', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1080,6 +1062,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Indirec if self.Legal_And_Regulatory_Costs is not None: self.Legal_And_Regulatory_Costs.export(lwrite, level, nsmap, namespace_, name_='Legal_And_Regulatory_Costs', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1164,6 +1147,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='DirectI if self.Response_And_Recovery_Costs is not None: self.Response_And_Recovery_Costs.export(lwrite, level, nsmap, namespace_, name_='Response_And_Recovery_Costs', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1237,6 +1221,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='NatureO for Property_Affected_ in self.Property_Affected: Property_Affected_.export(lwrite, level, nsmap, namespace_, name_='Property_Affected', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1303,6 +1288,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='History if self.Journal_Entry is not None: self.Journal_Entry.export(lwrite, level, nsmap, namespace_, name_='Journal_Entry', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1372,6 +1358,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='History for History_Item_ in self.History_Item: History_Item_.export(lwrite, level, nsmap, namespace_, name_='History_Item', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1437,6 +1424,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Affecte for Affected_Asset_ in self.Affected_Asset: Affected_Asset_.export(lwrite, level, nsmap, namespace_, name_='Affected_Asset', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1545,6 +1533,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TimeTyp if self.Incident_Closed is not None: self.Incident_Closed.export(lwrite, level, nsmap, namespace_, name_='Incident_Closed', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1644,6 +1633,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Categor for Category_ in self.Category: Category_.export(lwrite, level, nsmap, namespace_, name_='Category', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1711,6 +1701,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Effects for Effect_ in self.Effect: Effect_.export(lwrite, level, nsmap, namespace_, name_='Effect', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1781,6 +1772,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Attribu for Threat_Actor_ in self.Threat_Actor: Threat_Actor_.export(lwrite, level, nsmap, namespace_, name_='Threat_Actor', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1850,6 +1842,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Indicator_ in self.Related_Indicator: Related_Indicator_.export(lwrite, level, nsmap, namespace_, name_='Related_Indicator', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1919,6 +1912,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Observable_ in self.Related_Observable: Related_Observable_.export(lwrite, level, nsmap, namespace_, name_='Related_Observable', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1988,6 +1982,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Leverag for Leveraged_TTP_ in self.Leveraged_TTP: Leveraged_TTP_.export(lwrite, level, nsmap, namespace_, name_='Leveraged_TTP', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2057,6 +2052,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Incident_ in self.Related_Incident: Related_Incident_.export(lwrite, level, nsmap, namespace_, name_='Related_Incident', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2133,6 +2129,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='AssetTy super(AssetTypeType, self).exportChildren(lwrite, level, nsmap, namespace_, name_, True, pretty_print=pretty_print) pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) self.valueOf_ = get_all_text_(node) @@ -2149,6 +2146,8 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): pass # end class AssetTypeType + +@register_extension class IncidentType(stix_common_binding.IncidentBaseType): """The IncidentType characterizes a single cyber threat Incident.Specifies the relevant STIX-Incident schema version for @@ -2156,11 +2155,14 @@ class IncidentType(stix_common_binding.IncidentBaseType): Incident specification.""" subclass = None superclass = stix_common_binding.IncidentBaseType + + xmlns = "http://stix.mitre.org/Incident-1" + xmlns_prefix = "incident" + xml_type = "IncidentType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, timestamp=None, URL=None, version=None, Title=None, External_ID=None, Time=None, Description=None, Short_Description=None, Categories=None, Reporter=None, Responder=None, Coordinator=None, Victim=None, Affected_Assets=None, Impact_Assessment=None, Status=None, Related_Indicators=None, Related_Observables=None, Leveraged_TTPs=None, Attributed_Threat_Actors=None, Intended_Effect=None, Security_Compromise=None, Discovery_Method=None, Related_Incidents=None, COA_Requested=None, COA_Taken=None, Confidence=None, Contact=None, History=None, Information_Source=None, Handling=None, Related_Packages=None): super(IncidentType, self).__init__(timestamp=timestamp, idref=idref, id=id) - self.xmlns = "http://stix.mitre.org/Incident-1" - self.xmlns_prefix = "incident" - self.xml_type = "IncidentType" self.URL = _cast(None, URL) self.version = _cast(None, version) self.Title = Title @@ -2439,6 +2441,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Inciden if self.Related_Packages is not None: self.Related_Packages.export(lwrite, level, nsmap, namespace_, name_='Related_Packages', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2492,21 +2495,8 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): obj_.build(child_) self.Coordinator.append(obj_) elif nodeName_ == 'Victim': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "CIQIdentity3.0InstanceType": - import stix.bindings.extensions.identity.ciq_identity_3_0 as ciq_identity_binding - obj_ = ciq_identity_binding.CIQIdentity3_0InstanceType.factory() - else: - obj_ = stix_common_binding.IdentityType.factory() # IdentityType is not abstract + import stix.bindings.extensions.identity.ciq_identity_3_0 as ciq_identity_binding + obj_ = lookup_extension(child_, stix_common_binding.IdentityType).factory() obj_.build(child_) self.Victim.append(obj_) elif nodeName_ == 'Affected_Assets': @@ -2638,6 +2628,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='NonPubl super(NonPublicDataCompromisedType, self).exportChildren(lwrite, level, nsmap, namespace_, name_, True, pretty_print=pretty_print) pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2706,6 +2697,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='inciden def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ExternalIDType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) self.valueOf_ = get_all_text_(node) @@ -2729,7 +2721,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -2775,8 +2767,8 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO - doc = parsexml_(StringIO(inString)) + from mixbox.vendor.six import BytesIO + doc = parsexml_(BytesIO(inString.encode('utf-8'))) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) if rootClass is None: diff --git a/stix/bindings/indicator.py b/stix/bindings/indicator.py index 4bbedf65..e8b5a8af 100644 --- a/stix/bindings/indicator.py +++ b/stix/bindings/indicator.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,8 +9,11 @@ # import sys -from stix.bindings import * + import cybox.bindings.cybox_core as cybox_core_binding +from mixbox.binding_utils import * + +from stix.bindings import lookup_extension, register_extension import stix.bindings.stix_common as stix_common_binding import stix.bindings.data_marking as data_marking_binding @@ -74,6 +77,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ValidTi if self.End_Time is not None: self.End_Time.export(lwrite, level, nsmap, namespace_, name_='End_Time', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -152,6 +156,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Composi for Indicator_ in self.Indicator: Indicator_.export(lwrite, level, nsmap, namespace_, name_='Indicator', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -220,12 +225,12 @@ class TestMechanismType(GeneratedsSuper): reference to the ID of a Test Mechanism specified elsewhere.""" subclass = None superclass = None - def __init__(self, idref=None, id=None, Efficacy=None, Producer=None, xsi_type=None): + def __init__(self, idref=None, id=None, Efficacy=None, Producer=None): self.idref = _cast(None, idref) self.id = _cast(None, id) self.Efficacy = Efficacy self.Producer = Producer - self.xsi_type = xsi_type + # self.xsi_type = xsi_type def factory(*args_, **kwargs_): if TestMechanismType.subclass: return TestMechanismType.subclass(*args_, **kwargs_) @@ -273,9 +278,9 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='indicat if self.id is not None and 'id' not in already_processed: already_processed.add('id') lwrite(' id=%s' % (quote_attrib(self.id), )) - if self.xsi_type is not None and 'xsi:type' not in already_processed: - already_processed.add('xsi:type') - lwrite(' xsi:type="%s"' % self.xsi_type) + # if self.xsi_type is not None and 'xsi:type' not in already_processed: + # already_processed.add('xsi:type') + # lwrite(' xsi:type="%s"' % self.xsi_type) def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TestMechanismType', fromsubclass_=False, pretty_print=True): if pretty_print: @@ -287,6 +292,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TestMec if self.Producer is not None: self.Producer.export(lwrite, level, nsmap, namespace_, name_='Producer', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -375,6 +381,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Sightin for Sighting_ in self.Sighting: Sighting_.export(lwrite, level, nsmap, namespace_, name_='Sighting', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -386,7 +393,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('sightings_count') try: self.sightings_count = int(value) - except ValueError, exp: + except ValueError as exp: raise_parse_error(node, 'Bad integer attribute: %s' % exp) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Sighting': @@ -486,6 +493,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Sightin if self.Related_Observables is not None: self.Related_Observables.export(lwrite, level, nsmap, namespace_, name_='Related_Observables', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -497,7 +505,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) value = find_attr_value_('timestamp_precision', node) if value is not None and 'timestamp_precision' not in already_processed: @@ -582,6 +590,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Observable_ in self.Related_Observable: Related_Observable_.export(lwrite, level, nsmap, namespace_, name_='Related_Observable', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -648,6 +657,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TestMec for Test_Mechanism_ in self.get_Test_Mechanism(): Test_Mechanism_.export(lwrite, level, nsmap, namespace_, name_='Test_Mechanism', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -657,37 +667,8 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Test_Mechanism': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "OVAL5.10TestMechanismType": - import stix.bindings.extensions.test_mechanism.oval_5_10 as oval_5_10_tm_binding - obj_ = oval_5_10_tm_binding.OVAL5_10TestMechanismType.factory() - elif type_name_ == "YaraTestMechanismType": - import stix.bindings.extensions.test_mechanism.yara as yara_tm_binding - obj_ = yara_tm_binding.YaraTestMechanismType.factory() - elif type_name_ == "SnortTestMechanismType": - import stix.bindings.extensions.test_mechanism.snort as snort_tm_binding - obj_ = snort_tm_binding.SnortTestMechanismType.factory() - elif type_name_ == "OpenIOC2010TestMechanismType": - import stix.bindings.extensions.test_mechanism.open_ioc_2010 as openioc_tm_binding - obj_ = openioc_tm_binding.OpenIOC2010TestMechanismType.factory() - elif type_name_ == "GenericTestMechanismType": - import stix.bindings.extensions.test_mechanism.generic as generic_tm_binding - obj_ = generic_tm_binding.GenericTestMechanismType.factory() - else: - raise NotImplementedError('Class not implemented for element: ' + type_name_) - else: - raise NotImplementedError( - 'Class not implemented for element: no xsi:type attribute found') - + from .extensions.test_mechanism import (generic, oval_5_10, open_ioc_2010, snort, yara) + obj_ = lookup_extension(child_).factory() obj_.build(child_) self.Test_Mechanism.append(obj_) # end class TestMechanismsType @@ -746,6 +727,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Suggest for Suggested_COA_ in self.Suggested_COA: Suggested_COA_.export(lwrite, level, nsmap, namespace_, name_='Suggested_COA', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -815,6 +797,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Indicator_ in self.Related_Indicator: Related_Indicator_.export(lwrite, level, nsmap, namespace_, name_='Related_Indicator', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -830,6 +813,8 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): super(RelatedIndicatorsType, self).buildChildren(child_, node, nodeName_, True) # end class RelatedIndicatorsType + +@register_extension class IndicatorType(stix_common_binding.IndicatorBaseType): """The IndicatorType characterizes a cyber threat indicator made up of a pattern identifying certain observable conditions as well as @@ -842,11 +827,14 @@ class IndicatorType(stix_common_binding.IndicatorBaseType): specifies the absence of the pattern.""" subclass = None superclass = stix_common_binding.IndicatorBaseType + + xmlns = XML_NS + xmlns_prefix = "indicator" + xml_type = "IndicatorType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, timestamp=None, negate=False, version=None, Title=None, Type=None, Alternative_ID=None, Description=None, Short_Description=None, Valid_Time_Position=None, Observable=None, Composite_Indicator_Expression=None, Indicated_TTP=None, Kill_Chain_Phases=None, Test_Mechanisms=None, Likely_Impact=None, Suggested_COAs=None, Handling=None, Confidence=None, Sightings=None, Related_Indicators=None, Related_Campaigns=None, Related_Packages=None, Producer=None): super(IndicatorType, self).__init__(idref=idref, id=id, timestamp=timestamp) - self.xmlns = "http://stix.mitre.org/Indicator-2" - self.xmlns_prefix = "indicator" - self.xml_type = "IndicatorType" self.negate = _cast(bool, negate) self.version = _cast(None, version) @@ -1048,6 +1036,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Indicat if self.Producer is not None: self.Producer.export(lwrite, level, nsmap, namespace_, name_='Producer', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1205,6 +1194,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Campaign_ in self.Related_Campaign: Related_Campaign_.export(lwrite, level, nsmap, namespace_, name_='Related_Campaign', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1228,7 +1218,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -1274,7 +1264,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/stix_common.py b/stix/bindings/stix_common.py index b0c813ec..64805856 100644 --- a/stix/bindings/stix_common.py +++ b/stix/bindings/stix_common.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,10 +9,12 @@ # import sys -from stix.bindings import * +from mixbox.binding_utils import * import cybox.bindings.cybox_common as cybox_common_binding import cybox.bindings.cybox_core as cybox_core_binding +from stix.bindings import get_type_info, lookup_extension + XML_NS = "http://stix.mitre.org/common-1" # @@ -87,6 +89,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Generic if self.Relationship is not None: self.Relationship.export(lwrite, level, nsmap, namespace_, name_='Relationship', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -170,6 +173,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='DateTimeWithPrecisionType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) self.valueOf_ = get_all_text_(node) @@ -238,6 +242,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Profile showIndent(lwrite, level, pretty_print) lwrite('<%s:Profile>%s%s' % (nsmap[namespace_],quote_xml(Profile_), nsmap[namespace_], eol_)) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -313,6 +318,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='RelatedPackageRefType', fromsubclass_=False, pretty_print=True): super(RelatedPackageRefType, self).exportChildren(lwrite, level, nsmap, namespace_, name_, True, pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -328,7 +334,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) super(RelatedPackageRefType, self).buildAttributes(node, attrs, already_processed) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): @@ -390,6 +396,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Package_Reference_ in self.Package_Reference: Package_Reference_.export(lwrite, level, nsmap, namespace_, name_='Package_Reference', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -473,6 +480,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ToolInf if self.Short_Description is not None: self.Short_Description.export(lwrite, level, nsmap, namespace_, name_='Short_Description', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -584,6 +592,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Informa if self.References is not None: self.References.export(lwrite, level, nsmap, namespace_, name_='References', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -597,22 +606,12 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): obj_.build(child_) self.set_Description(obj_) elif nodeName_ == 'Identity': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - if type_name_ == "CIQIdentity3.0InstanceType": - import stix.bindings.extensions.identity.ciq_identity_3_0 as ciq_identity_binding - obj_ = ciq_identity_binding.CIQIdentity3_0InstanceType.factory() - else: - obj_ = IdentityType.factory() # IdentityType is not abstract + from stix.bindings.extensions.identity import ciq_identity_3_0 + # Look for xsi:type. If not there, build an instance of + # IdentityType + obj_ = lookup_extension(child_, IdentityType).factory() obj_.build(child_) self.set_Identity(obj_) elif nodeName_ == 'Role': @@ -714,6 +713,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Confide if self.Confidence_Assertion_Chain is not None: self.Confidence_Assertion_Chain.export(lwrite, level, nsmap, namespace_, name_='Confidence_Assertion_Chain', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -725,7 +725,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) value = find_attr_value_('timestamp_precision', node) @@ -803,6 +803,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Activit if self.Description is not None: self.Description.export(lwrite, level, nsmap, namespace_, name_='Description', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -872,6 +873,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='KillCha for Kill_Chain_ in self.Kill_Chain: Kill_Chain_.export(lwrite, level, nsmap, namespace_, name_='Kill_Chain', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -973,6 +975,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='KillCha for Kill_Chain_Phase_ in self.Kill_Chain_Phase: Kill_Chain_Phase_.export(lwrite, level, nsmap, namespace_, name_='Kill_Chain_Phase', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1073,6 +1076,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='KillChainPhaseType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1084,7 +1088,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('ordinality') try: self.ordinality = int(value) - except ValueError, exp: + except ValueError as exp: raise_parse_error(node, 'Bad integer attribute: %s' % exp) value = find_attr_value_('name', node) if value is not None and 'name' not in already_processed: @@ -1153,6 +1157,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='KillCha for Kill_Chain_Phase_ in self.Kill_Chain_Phase: Kill_Chain_Phase_.export(lwrite, level, nsmap, namespace_, name_='Kill_Chain_Phase', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1221,6 +1226,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='KillCha super(KillChainPhaseReferenceType, self).exportChildren(lwrite, level, nsmap, namespace_, name_, True, pretty_print=pretty_print) pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1261,7 +1267,6 @@ def __init__(self, idref=None, id=None, Name=None, Related_Identities=None): self.id = _cast(None, id) self.Name = Name self.Related_Identities = Related_Identities - self.xsi_type = None def factory(*args_, **kwargs_): if IdentityType.subclass: return IdentityType.subclass(*args_, **kwargs_) @@ -1318,6 +1323,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Identit if self.Related_Identities is not None: self.Related_Identities.export(lwrite, level, nsmap, namespace_, name_='Related_Identities', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1397,6 +1403,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='GenericRelationshipListType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1462,6 +1469,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related if self.Campaign is not None: self.Campaign.export(lwrite, level, nsmap, namespace_, name_='Campaign', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1626,6 +1634,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related if self.Exploit_Target is not None: self.Exploit_Target.export(lwrite, level, nsmap, namespace_, name_='Exploit_Target', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1708,6 +1717,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related if self.Incident is not None: self.Incident.export(lwrite, level, nsmap, namespace_, name_='Incident', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1790,6 +1800,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related if self.Indicator is not None: self.Indicator.export(lwrite, level, nsmap, namespace_, name_='Indicator', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1872,6 +1883,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related if self.Observable is not None: self.Observable.export(lwrite, level, "%s:" % (nsmap[namespace_]), name_='Observable', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1937,6 +1949,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related if self.Threat_Actor is not None: self.Threat_Actor.export(lwrite, level, nsmap, namespace_, name_='Threat_Actor', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2019,6 +2032,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related if self.TTP is not None: self.TTP.export(lwrite, level, nsmap, namespace_, name_='TTP', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2101,6 +2115,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related if self.Identity is not None: self.Identity.export(lwrite, level, nsmap, namespace_, name_='Identity', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2198,6 +2213,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='IndicatorBaseType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2217,7 +2233,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): pass @@ -2299,6 +2315,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='IncidentBaseType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2318,7 +2335,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): pass @@ -2400,6 +2417,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TTPBaseType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2419,7 +2437,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): pass @@ -2502,6 +2520,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ExploitTargetBaseType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2521,7 +2540,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): pass @@ -2603,6 +2622,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='CourseOfActionBaseType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2622,7 +2642,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): pass @@ -2679,6 +2699,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related if self.Campaign is not None: self.Campaign.export(lwrite, level, nsmap, namespace_, name_='Campaign', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2759,6 +2780,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Campaig if self.Names is not None: self.Names.export(lwrite, level, nsmap, namespace_, name_='Names', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2774,7 +2796,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Names': @@ -2834,6 +2856,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='NamesTy for Name_ in self.Name: Name_.export(lwrite, level, nsmap, namespace_, name_='Name', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2924,6 +2947,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='CampaignBaseType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -2943,7 +2967,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): pass @@ -3026,6 +3050,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ThreatActorBaseType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -3045,7 +3070,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): pass @@ -3103,6 +3128,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Exploit for Exploit_Target_ in self.Exploit_Target: Exploit_Target_.export(lwrite, level, nsmap, namespace_, name_='Exploit_Target', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -3183,6 +3209,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='AddressAbstractType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -3245,6 +3272,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Contrib for Source_ in self.Source: Source_.export(lwrite, level, nsmap, namespace_, name_='Source', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -3311,6 +3339,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Referen showIndent(lwrite, level, pretty_print) lwrite('<%s:Reference>%s%s' % (nsmap[namespace_], quote_xml(Reference_), nsmap[namespace_], eol_)) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -3376,6 +3405,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Identity_ in self.Related_Identity: Related_Identity_.export(lwrite, level, nsmap, namespace_, name_='Related_Identity', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -3441,6 +3471,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Confide for Confidence_Assertion_ in self.Confidence_Assertion: Confidence_Assertion_.export(lwrite, level, nsmap, namespace_, name_='Confidence_Assertion', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -3551,7 +3582,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) value = find_attr_value_('timestamp_precision', node) if value is not None and 'timestamp_precision' not in already_processed: @@ -3632,6 +3663,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='StructuredTextType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) self.valueOf_ = get_all_text_(node) @@ -3706,6 +3738,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='EncodedCDATAType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) self.valueOf_ = get_all_text_(node) @@ -3789,6 +3822,7 @@ def exportAttributes(self, lwrite, level, already_processed, namespace_='stixCom def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ControlledVocabularyStringType', fromsubclass_=False, pretty_print=True): pass def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) self.valueOf_ = get_all_text_(node) @@ -3819,7 +3853,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -3865,7 +3899,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/stix_core.py b/stix/bindings/stix_core.py index 9ee8927b..a65eaa81 100644 --- a/stix/bindings/stix_core.py +++ b/stix/bindings/stix_core.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,10 +9,13 @@ # import sys -from stix.bindings import * + +import cybox.bindings.cybox_core as cybox_core_binding +from mixbox.binding_utils import * + +from stix.bindings import lookup_extension import stix.bindings.stix_common as stix_common_binding import stix.bindings.data_marking as data_marking_binding -import cybox.bindings.cybox_core as cybox_core_binding XML_NS = "http://stix.mitre.org/stix-1" @@ -126,10 +129,6 @@ def exportAttributes(self, lwrite, level, nsmap, already_processed, namespace_=X if self.timestamp is not None and 'timestamp' not in already_processed: already_processed.add('timestamp') lwrite(' timestamp="%s"' % self.gds_format_datetime(self.timestamp, input_name='timestamp')) - - #for ns, prefix in nsmap.iteritems(): - # lwrite(' xmlns:%s="%s"' % (prefix, ns)) - def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='STIXType', fromsubclass_=False, pretty_print=True): if pretty_print: eol_ = '\n' @@ -155,8 +154,8 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='STIXTyp self.Threat_Actors.export(lwrite, level, nsmap, namespace_, name_='Threat_Actors', pretty_print=pretty_print) if self.Related_Packages is not None: self.Related_Packages.export(lwrite, level, nsmap, namespace_, name_='Related_Packages', pretty_print=pretty_print) - def build(self, node): + self.__sourcenode__ = node already_processed = set() self.nsmap = node.nsmap self.buildAttributes(node, node.attrib, already_processed) @@ -181,7 +180,7 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('timestamp') try: self.timestamp = self.gds_parse_datetime(value, node, 'timestamp') - except ValueError, exp: + except ValueError as exp: raise ValueError('Bad date-time attribute (timestamp): %s' % exp) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'STIX_Header': @@ -281,6 +280,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_Package_ in self.Related_Package: Related_Package_.export(lwrite, level, nsmap, namespace_, name_='Related_Package', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -346,6 +346,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related if self.Package is not None: self.Package.export(lwrite, level, nsmap, namespace_, name_='Package', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -452,6 +453,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='STIXHea if self.Information_Source is not None: self.Information_Source.export(lwrite, level, nsmap, namespace_, name_='Information_Source', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -541,6 +543,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Indicat for Indicator_ in self.Indicator: Indicator_.export(lwrite, level, nsmap, namespace_, name_='Indicator', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -550,26 +553,11 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Indicator': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "IndicatorType": - import stix.bindings.indicator as indicator_binding - obj_ = indicator_binding.IndicatorType.factory() - else: - raise NotImplementedError('Class not implemented for element type: ' + type_name_) - else: - obj_ = stix_common_binding.IndicatorBaseType.factory() # not abstract - + from . import indicator + obj_ = lookup_extension(child_, stix_common_binding.IndicatorBaseType).factory() obj_.build(child_) self.Indicator.append(obj_) + # end class IndicatorsType class TTPsType(GeneratedsSuper): @@ -629,6 +617,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TTPsTyp if self.Kill_Chains is not None: self.Kill_Chains.export(lwrite, level, nsmap, namespace_, name_='Kill_Chains', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -638,26 +627,11 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'TTP': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "TTPType": - import stix.bindings.ttp as ttp_binding - obj_ = ttp_binding.TTPType.factory() - else: - raise NotImplementedError('Class not implemented for element type: ' + type_name_) - else: - obj_ = stix_common_binding.TTPBaseType.factory() # not abstract - + from . import ttp + obj_ = lookup_extension(child_, stix_common_binding.TTPBaseType).factory() obj_.build(child_) self.TTP.append(obj_) + elif nodeName_ == 'Kill_Chains': obj_ = stix_common_binding.KillChainsType.factory() obj_.build(child_) @@ -715,6 +689,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Inciden for Incident_ in self.Incident: Incident_.export(lwrite, level, nsmap, namespace_, name_='Incident', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -724,26 +699,11 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Incident': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "IncidentType": - import stix.bindings.incident as incident_binding - obj_ = incident_binding.IncidentType.factory() - else: - raise NotImplementedError('Class not implemented for element type: ' + type_name_) - else: - obj_ = stix_common_binding.IncidentBaseType.factory() # not abstract - + from . import incident + obj_ = lookup_extension(child_, stix_common_binding.IncidentBaseType).factory() obj_.build(child_) self.Incident.append(obj_) + # end class IncidentsType class CoursesOfActionType(GeneratedsSuper): @@ -797,6 +757,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Courses for Course_Of_Action_ in self.Course_Of_Action: Course_Of_Action_.export(lwrite, level, nsmap, namespace_, name_='Course_Of_Action', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -806,24 +767,8 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Course_Of_Action': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "CourseOfActionType": - import stix.bindings.course_of_action as coa_binding - obj_ = coa_binding.CourseOfActionType.factory() - else: - raise NotImplementedError('Class not implemented for element type: ' + type_name_) - else: - obj_ = stix_common_binding.CourseOfActionBaseType.factory() # not abstract - + from . import course_of_action + obj_ = lookup_extension(child_, stix_common_binding.CourseOfActionBaseType).factory() obj_.build(child_) self.Course_Of_Action.append(obj_) # end class CoursesOfActionType @@ -879,6 +824,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Campaig for Campaign_ in self.Campaign: Campaign_.export(lwrite, level, nsmap, namespace_, name_='Campaign', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -888,24 +834,8 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Campaign': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "CampaignType": - import stix.bindings.campaign as campaign_binding - obj_ = campaign_binding.CampaignType.factory() - else: - raise NotImplementedError('Class not implemented for element type: ' + type_name_) - else: - obj_ = stix_common_binding.CampaignBaseType.factory() # not abstract - + from . import campaign + obj_ = lookup_extension(child_, stix_common_binding.CampaignBaseType).factory() obj_.build(child_) self.Campaign.append(obj_) # end class CampaignsType @@ -961,6 +891,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ThreatA for Threat_Actor_ in self.Threat_Actor: Threat_Actor_.export(lwrite, level, nsmap, namespace_, name_='Threat_Actor', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -970,24 +901,8 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Threat_Actor': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "ThreatActorType": - import stix.bindings.threat_actor as ta_binding - obj_ = ta_binding.ThreatActorType.factory() - else: - raise NotImplementedError('Class not implemented for element type: ' + type_name_) - else: - obj_ = stix_common_binding.ThreatActorBaseType.factory() # not abstract - + from . import threat_actor + obj_ = lookup_extension(child_, stix_common_binding.ThreatActorBaseType).factory() obj_.build(child_) self.Threat_Actor.append(obj_) # end class ThreatActorsType @@ -1000,7 +915,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -1050,7 +965,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/threat_actor.py b/stix/bindings/threat_actor.py index 9e84d689..ef4972c5 100644 --- a/stix/bindings/threat_actor.py +++ b/stix/bindings/threat_actor.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. #!/usr/bin/env python @@ -9,7 +9,10 @@ # import sys -from stix.bindings import * + +from mixbox.binding_utils import * + +from stix.bindings import lookup_extension, register_extension import stix.bindings.stix_common as stix_common_binding import stix.bindings.data_marking as data_marking_binding @@ -74,6 +77,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Observe for Observed_TTP_ in self.Observed_TTP: Observed_TTP_.export(lwrite, level, nsmap, namespace_, name_='Observed_TTP', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -143,6 +147,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Associa for Associated_Campaign_ in self.Associated_Campaign: Associated_Campaign_.export(lwrite, level, nsmap, namespace_, name_='Associated_Campaign', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -212,6 +217,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Associa for Associated_Actor_ in self.Associated_Actor: Associated_Actor_.export(lwrite, level, nsmap, namespace_, name_='Associated_Actor', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -227,16 +233,20 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): super(AssociatedActorsType, self).buildChildren(child_, node, nodeName_, True) # end class AssociatedActorsType +@register_extension class ThreatActorType(stix_common_binding.ThreatActorBaseType): """Specifies the relevant STIX-ThreatActor schema version for this content.""" subclass = None superclass = stix_common_binding.ThreatActorBaseType + + xmlns = "http://stix.mitre.org/ThreatActor-1" + xmlns_prefix = "ta" + xml_type = "ThreatActorType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, timestamp=None, version=None, Title=None, Description=None, Short_Description=None, Identity=None, Type=None, Motivation=None, Sophistication=None, Intended_Effect=None, Planning_And_Operational_Support=None, Observed_TTPs=None, Associated_Campaigns=None, Associated_Actors=None, Handling=None, Confidence=None, Information_Source=None, Related_Packages=None): super(ThreatActorType, self).__init__(idref=idref, id=id, timestamp=timestamp) - self.xmlns = "http://stix.mitre.org/ThreatActor-1" - self.xmlns_prefix = "ta" - self.xml_type = "ThreatActorType" self.version = _cast(None, version) self.Title = Title self.Description = Description @@ -411,6 +421,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ThreatA if self.Related_Packages is not None: self.Related_Packages.export(lwrite, level, nsmap, namespace_, name_='Related_Packages', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -512,7 +523,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -558,7 +569,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/bindings/ttp.py b/stix/bindings/ttp.py index 8ba5882a..637835ab 100644 --- a/stix/bindings/ttp.py +++ b/stix/bindings/ttp.py @@ -1,15 +1,19 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# See LICENSE.txt for complete terms. + #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. -# See LICENSE.txt for complete terms. # # Generated Thu Apr 11 15:06:30 2013 by generateDS.py version 2.9a. # import sys -from stix.bindings import * + import cybox.bindings.cybox_core as cybox_core_binding +from mixbox.binding_utils import * + +from stix.bindings import lookup_extension, register_extension import stix.bindings.stix_common as stix_common_binding import stix.bindings.data_marking as data_marking_binding @@ -112,6 +116,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='AttackP if self.Short_Description is not None: self.Short_Description.export(lwrite, level, nsmap, namespace_, name_='Short_Description', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -252,6 +257,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Malware if self.Short_Description is not None: self.Short_Description.export(lwrite, level, nsmap, namespace_, name_='Short_Description', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -368,6 +374,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Exploit if self.Short_Description is not None: self.Short_Description.export(lwrite, level, nsmap, namespace_, name_='Short_Description', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -488,6 +495,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Infrast if self.Observable_Characterization is not None: self.Observable_Characterization.export(lwrite, level, "%s:" % (nsmap[namespace_]), name_='Observable_Characterization', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -577,6 +585,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='ToolsTy for Tool_ in self.Tool: Tool_.export(lwrite, level, nsmap, namespace_, name_='Tool', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -642,6 +651,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Exploit for Exploit_ in self.Exploit: Exploit_.export(lwrite, level, nsmap, namespace_, name_='Exploit', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -707,6 +717,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Malware for Malware_Instance_ in self.Malware_Instance: Malware_Instance_.export(lwrite, level, nsmap, namespace_, name_='Malware_Instance', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -716,22 +727,8 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Malware_Instance': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "MAEC4.1InstanceType": - from .extensions.malware import maec_4_1 - obj_ = maec_4_1.MAEC4_1InstanceType.factory() - else: - obj_ = MalwareInstanceType.factory() # MalwareInstanceType is not abstract - + from .extensions.malware import maec_4_1 + obj_ = lookup_extension(child_, MalwareInstanceType).factory() obj_.build(child_) self.Malware_Instance.append(obj_) # end class MalwareType @@ -787,6 +784,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='AttackP for Attack_Pattern_ in self.Attack_Pattern: Attack_Pattern_.export(lwrite, level, nsmap, namespace_, name_='Attack_Pattern', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -796,24 +794,8 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Attack_Pattern': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "CAPEC2.7InstanceType": - from .extensions.attack_pattern import capec_2_7 - obj_ = capec_2_7.CAPEC2_7InstanceType.factory() - else: - raise NotImplementedError('No implementation for type: ' + type_name_) - else: - obj_ = AttackPatternType.factory() # AttackPattern is not abstract - + from .extensions.attack_pattern import capec_2_7 + obj_ = lookup_extension(child_, AttackPatternType).factory() obj_.build(child_) self.Attack_Pattern.append(obj_) # end class AttackPatternsType @@ -876,6 +858,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Resourc if self.Personas is not None: self.Personas.export(lwrite, level, nsmap, namespace_, name_='Personas', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -949,6 +932,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Persona for Persona_ in self.Persona: Persona_.export(lwrite, level, nsmap, namespace_, name_='Persona', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -958,22 +942,8 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Persona': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "CIQIdentity3.0InstanceType": - from .extensions.identity import ciq_identity_3_0 - obj_ = ciq_identity_3_0.CIQIdentity3_0InstanceType.factory() - else: - obj_ = stix_common_binding.IdentityType.factory() # IdentityType is not abstract - + from .extensions.identity import ciq_identity_3_0 + obj_ = lookup_extension(child_, stix_common_binding.IdentityType).factory() obj_.build(child_) self.Persona.append(obj_) # end class PersonasType @@ -1036,6 +1006,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Behavio if self.Exploits is not None: self.Exploits.export(lwrite, level, nsmap, namespace_, name_='Exploits', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1133,6 +1104,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='VictimT self.Targeted_Technical_Details.export(lwrite, level, "%s:" % (nsmap[namespace_]), name_='Targeted_Technical_Details', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1142,22 +1114,8 @@ def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): if nodeName_ == 'Identity': - type_name_ = child_.attrib.get('{http://www.w3.org/2001/XMLSchema-instance}type') - if type_name_ is None: - type_name_ = child_.attrib.get('type') - if type_name_ is not None: - type_names_ = type_name_.split(':') - if len(type_names_) == 1: - type_name_ = type_names_[0] - else: - type_name_ = type_names_[1] - - if type_name_ == "CIQIdentity3.0InstanceType": - from .extensions.identity import ciq_identity_3_0 - obj_ = ciq_identity_3_0.CIQIdentity3_0InstanceType.factory() - else: - obj_ = stix_common_binding.IdentityType.factory() # IdentityType is not abstract - + from .extensions.identity import ciq_identity_3_0 + obj_ = lookup_extension(child_, stix_common_binding.IdentityType).factory() obj_.build(child_) self.set_Identity(obj_) elif nodeName_ == 'Targeted_Systems': @@ -1228,6 +1186,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Related for Related_TTP_ in self.Related_TTP: Related_TTP_.export(lwrite, level, nsmap, namespace_, name_='Related_TTP', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1243,18 +1202,22 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): super(RelatedTTPsType, self).buildChildren(child_, node, nodeName_, True) # end class RelatedTTPsType + +@register_extension class TTPType(stix_common_binding.TTPBaseType): """TTPType characterizes an individual adversary TTP.Specifies the relevant STIX-TTP schema version for this content.""" subclass = None superclass = stix_common_binding.TTPBaseType + + xmlns = "http://stix.mitre.org/TTP-1" + xmlns_prefix = "ttp" + xml_type = "TTPType" + xsi_type = "%s:%s" % (xmlns_prefix, xml_type) + def __init__(self, idref=None, id=None, timestamp=None, version=None, Title=None, Description=None, Short_Description=None, Intended_Effect=None, Behavior=None, Resources=None, Victim_Targeting=None, Exploit_Targets=None, Related_TTPs=None, Kill_Chain_Phases=None, Information_Source=None, Kill_Chains=None, Handling=None, Related_Packages=None): super(TTPType, self).__init__(idref=idref, id=id, timestamp=timestamp) - self.xmlns = "http://stix.mitre.org/TTP-1" - self.xmlns_prefix = "ttp" - self.xml_type = "TTPType" self.version = _cast(None, version) - self.Title = Title self.Description = Description self.Short_Description = Short_Description @@ -1396,6 +1359,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='TTPType if self.Related_Packages is not None: self.Related_Packages.export(lwrite, level, nsmap, namespace_, name_='Related_Packages', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1522,6 +1486,7 @@ def exportChildren(self, lwrite, level, nsmap, namespace_=XML_NS, name_='Exploit for Exploit_Target_ in self.Exploit_Target: Exploit_Target_.export(lwrite, level, nsmap, namespace_, name_='Exploit_Target', pretty_print=pretty_print) def build(self, node): + self.__sourcenode__ = node already_processed = set() self.buildAttributes(node, node.attrib, already_processed) for child in node: @@ -1545,7 +1510,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): """ def usage(): - print USAGE_TEXT + print(USAGE_TEXT) sys.exit(1) def get_root_tag(node): @@ -1591,7 +1556,7 @@ def parseEtree(inFileName): return rootObj, rootElement def parseString(inString): - from StringIO import StringIO + from mixbox.vendor.six import StringIO doc = parsexml_(StringIO(inString)) rootNode = doc.getroot() rootTag, rootClass = get_root_tag(rootNode) diff --git a/stix/campaign/__init__.py b/stix/campaign/__init__.py index 5de638fb..46703142 100644 --- a/stix/campaign/__init__.py +++ b/stix/campaign/__init__.py @@ -1,72 +1,90 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix -from stix.common import Activity, Confidence, Statement, VocabString -from stix.common.related import ( - GenericRelationshipList, RelatedCampaign, RelatedIncident, RelatedIndicator, - RelatedPackageRefs, RelatedThreatActor, RelatedTTP -) -from stix.common import vocabs -from stix.data_marking import Marking import stix.bindings.campaign as campaign_binding +from stix.common import Activity, Confidence, Statement +from stix.common import vocabs +from stix.common.information_source import InformationSource +from stix.common.related import ( + GenericRelationshipList, RelatedCampaign, RelatedIncident, + RelatedIndicator, RelatedPackageRefs, RelatedThreatActor, + RelatedTTP) +from stix.common.statement import StatementField +from stix.common.vocabs import VocabField, CampaignStatus class AssociatedCampaigns(GenericRelationshipList): _namespace = "http://stix.mitre.org/Campaign-1" _binding = campaign_binding _binding_class = campaign_binding.AssociatedCampaignsType - _binding_var = "Associated_Campaign" - _contained_type = RelatedCampaign - _inner_name = "campaigns" + + campaign = fields.TypedField("Associated_Campaign", RelatedCampaign, multiple=True, key_name="campaigns") class Attribution(GenericRelationshipList): _namespace = "http://stix.mitre.org/Campaign-1" _binding = campaign_binding _binding_class = campaign_binding.AttributionType - _binding_var = "Attributed_Threat_Actor" - _contained_type = RelatedThreatActor - _inner_name = "threat_actors" + + threat_actor = fields.TypedField("Attributed_Threat_Actor", RelatedThreatActor, multiple=True, key_name="threat_actors") class RelatedIncidents(GenericRelationshipList): _namespace = "http://stix.mitre.org/Campaign-1" _binding = campaign_binding _binding_class = campaign_binding.RelatedIncidentsType - _binding_var = "Related_Incident" - _contained_type = RelatedIncident - _inner_name = "incidents" + + incident = fields.TypedField("Related_Incident", RelatedIncident, multiple=True, key_name="incidents") class RelatedIndicators(GenericRelationshipList): _namespace = "http://stix.mitre.org/Campaign-1" _binding = campaign_binding _binding_class = campaign_binding.RelatedIndicatorsType - _binding_var = "Related_Indicator" - _contained_type = RelatedIndicator - _inner_name = "indicators" + + indicator = fields.TypedField(name="Related_Indicator", type_=RelatedIndicator, multiple=True, key_name="indicators") class RelatedTTPs(GenericRelationshipList): _namespace = "http://stix.mitre.org/Campaign-1" _binding = campaign_binding _binding_class = campaign_binding.RelatedTTPsType - _binding_var = "Related_TTP" - _contained_type = RelatedTTP - _inner_name = "ttps" + + ttp = fields.TypedField("Related_TTP", RelatedTTP, multiple=True, key_name="ttps") class Names(stix.EntityList): _namespace = "http://stix.mitre.org/Campaign-1" _binding = campaign_binding _binding_class = campaign_binding.NamesType - _binding_var = "Name" - _contained_type = VocabString - _inner_name = "names" + + name = VocabField("Name", multiple=True, key_name="names") + + @classmethod + def _dict_as_list(cls): + return False class Campaign(stix.BaseCoreComponent): + """Implementation of the STIX Campaign. + + Args: + id_ (optional): An identifier. If ``None``, a value will be generated + via ``mixbox.idgen.create_id()``. If set, this will unset the + ``idref`` property. + idref (optional): An identifier reference. If set this will unset the + ``id_`` property. + timestamp (optional): A timestamp value. Can be an instance of + ``datetime.datetime`` or ``str``. + description: A description of the purpose or intent of this object. + short_description: A short description of the intent + or purpose of this object. + title: The title of this object. + + """ _binding = campaign_binding _binding_class = _binding.CampaignType _namespace = "http://stix.mitre.org/Campaign-1" @@ -74,6 +92,19 @@ class Campaign(stix.BaseCoreComponent): _ALL_VERSIONS = ("1.0", "1.0.1", "1.1", "1.1.1") _ID_PREFIX = 'campaign' + activity = fields.TypedField("Activity", Activity, multiple=True) + associated_campaigns = fields.TypedField("Associated_Campaigns", AssociatedCampaigns) + attribution = fields.TypedField("Attribution", Attribution, multiple=True) + confidence = fields.TypedField("Confidence", Confidence) + status = VocabField("Status", CampaignStatus) + intended_effects = StatementField("Intended_Effect", Statement, vocab_type=vocabs.IntendedEffect, multiple=True, key_name="intended_effects") + names = fields.TypedField("Names", Names) + related_incidents = fields.TypedField("Related_Incidents", RelatedIncidents) + related_indicators = fields.TypedField("Related_Indicators", RelatedIndicators) + related_packages = fields.TypedField("Related_Packages", RelatedPackageRefs) + related_ttps = fields.TypedField("Related_TTPs", RelatedTTPs) + information_source = fields.TypedField("Information_Source", InformationSource) + def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): @@ -86,170 +117,17 @@ def __init__(self, id_=None, idref=None, timestamp=None, title=None, short_description=short_description ) - self.names = None - self.intended_effects = _IntendedEffects() - self.status = None self.related_ttps = RelatedTTPs() self.related_incidents = RelatedIncidents() self.related_indicators = RelatedIndicators() - self.attribution = _AttributionList() - self.associated_campaigns = AssociatedCampaigns() - self.confidence = None - self.activity = _Activities() - self.handling = None self.related_packages = RelatedPackageRefs() - @property - def intended_effects(self): - return self._intended_effects - - @intended_effects.setter - def intended_effects(self, value): - self._intended_effects = _IntendedEffects(value) - def add_intended_effect(self, value): self.intended_effects.append(value) - @property - def activity(self): - return self._activity - - @activity.setter - def activity(self, value): - self._activity = _Activities(value) - def add_activity(self, value): - self.activity.append(value) - - @property - def status(self): - return self._status - - @status.setter - def status(self, value): - self._set_vocab(vocabs.CampaignStatus, status=value) - - @property - def attribution(self): - return self._attribution - - @attribution.setter - def attribution(self, value): - self._attribution = _AttributionList(value) - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(Campaign, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if self.names: - return_obj.Names = self.names.to_obj(ns_info=ns_info) - if self.intended_effects: - return_obj.Intended_Effect = self.intended_effects.to_obj(ns_info=ns_info) - if self.status: - return_obj.Status = self.status.to_obj(ns_info=ns_info) - if self.related_ttps: - return_obj.Related_TTPs = self.related_ttps.to_obj(ns_info=ns_info) - if self.related_incidents: - return_obj.Related_Incidents = self.related_incidents.to_obj(ns_info=ns_info) - if self.related_indicators: - return_obj.Related_Indicators = self.related_indicators.to_obj(ns_info=ns_info) - if self.attribution: - return_obj.Attribution = self.attribution.to_obj(ns_info=ns_info) - if self.associated_campaigns: - return_obj.Associated_Campaigns = self.associated_campaigns.to_obj(ns_info=ns_info) - if self.confidence: - return_obj.Confidence = self.confidence.to_obj(ns_info=ns_info) - if self.activity: - return_obj.Activity = self.activity.to_obj(ns_info=ns_info) - if self.handling: - return_obj.Handling = self.handling.to_obj(ns_info=ns_info) - if self.related_packages: - return_obj.Related_Packages = self.related_packages.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - super(Campaign, cls).from_obj(obj, return_obj=return_obj) - - if isinstance(obj, cls._binding_class): - return_obj.names = Names.from_obj(obj.Names) - return_obj.intended_effects = \ - _IntendedEffects.from_obj(obj.Intended_Effect) - return_obj.status = VocabString.from_obj(obj.Status) - return_obj.related_ttps = RelatedTTPs.from_obj(obj.Related_TTPs) - return_obj.related_incidents = \ - RelatedIncidents.from_obj(obj.Related_Incidents) - return_obj.related_indicators = \ - RelatedIndicators.from_obj(obj.Related_Indicators) - return_obj.attribution = _AttributionList.from_obj(obj.Attribution) - return_obj.associated_campaigns = \ - AssociatedCampaigns.from_obj(obj.Associated_Campaigns) - return_obj.confidence = Confidence.from_obj(obj.Confidence) - return_obj.activity = _Activities.from_obj(obj.Activity) - return_obj.handling = Marking.from_obj(obj.Handling) - return_obj.related_packages = \ - RelatedPackageRefs.from_obj(obj.Related_Packages) - - return return_obj - - def to_dict(self): - return super(Campaign, self).to_dict() + """Adds an :class:`.Activity` object to the :attr:`activity` + collection. - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - super(Campaign, cls).from_dict(dict_repr, return_obj=return_obj) - - get = dict_repr.get # PEP 8 line lengths - return_obj.names = Names.from_dict(get('names')) - return_obj.intended_effects = \ - _IntendedEffects.from_dict(get('intended_effects')) - return_obj.status = VocabString.from_dict(get('status')) - return_obj.related_ttps = \ - RelatedTTPs.from_dict(get('related_ttps')) - return_obj.related_incidents = \ - RelatedIncidents.from_dict(get('related_incidents')) - return_obj.related_indicators = \ - RelatedIndicators.from_dict(get('related_indicators')) - return_obj.attribution = _AttributionList.from_list(get('attribution')) - return_obj.associated_campaigns = \ - AssociatedCampaigns.from_dict(get('associated_campaigns')) - return_obj.confidence = \ - Confidence.from_dict(get('confidence')) - return_obj.activity = _Activities.from_dict(get('activity')) - return_obj.handling = Marking.from_dict(get('handling')) - return_obj.related_packages = \ - RelatedPackageRefs.from_dict(get('related_packages')) - - return return_obj - - -# Not Actual STIX Types! -class _AttributionList(stix.TypedList): - _contained_type = Attribution - - -class _Activities(stix.TypedList): - _contained_type = Activity - - -class _IntendedEffects(stix.TypedList): - _contained_type = Statement - - def _fix_value(self, value): - intended_effect = vocabs.IntendedEffect(value) - return Statement(value=intended_effect) + """ + self.activity.append(value) diff --git a/stix/coa/__init__.py b/stix/coa/__init__.py index de79b406..6805c973 100644 --- a/stix/coa/__init__.py +++ b/stix/coa/__init__.py @@ -1,34 +1,58 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. # external +from mixbox import fields from cybox.core import Observables # internal import stix -from stix.data_marking import Marking -from stix.common import vocabs, related, VocabString, Statement +from stix.common import vocabs +from stix.common.related import GenericRelationshipList, RelatedPackageRefs, RelatedCOA +from stix.common.vocabs import VocabField +from stix.common.statement import Statement +from stix.common.information_source import InformationSource import stix.bindings.course_of_action as coa_binding # relative from .objective import Objective -from .structured_coa import _BaseStructuredCOA +from .structured_coa import StructuredCOAFactory, _BaseStructuredCOA # Redefines Stage = vocabs.COAStage COAType = vocabs.CourseOfActionType -class RelatedCOAs(related.GenericRelationshipList): +class RelatedCOAs(GenericRelationshipList): _namespace = "http://stix.mitre.org/CourseOfAction-1" _binding = coa_binding _binding_class = coa_binding.RelatedCOAsType - _binding_var = "Related_COA" - _contained_type = related.RelatedCOA - _inner_name = "coas" + + related_coa = fields.TypedField( + name="Related_COA", + type_=RelatedCOA, + multiple=True, + key_name="coas" + ) class CourseOfAction(stix.BaseCoreComponent): + """Implementation of the STIX Course of Action. + + Args: + id_ (optional): An identifier. If ``None``, a value will be generated + via ``mixbox.idgen.create_id()``. If set, this will unset the + ``idref`` property. + idref (optional): An identifier reference. If set this will unset the + ``id_`` property. + timestamp (optional): A timestamp value. Can be an instance of + ``datetime.datetime`` or ``str``. + description: A description of the purpose or intent of this object. + short_description: A short description of the intent + or purpose of this object. + title: The title of this object. + + """ _binding = coa_binding _binding_class = coa_binding.CourseOfActionType _namespace = "http://stix.mitre.org/CourseOfAction-1" @@ -36,6 +60,18 @@ class CourseOfAction(stix.BaseCoreComponent): _ALL_VERSIONS = ("1.0", "1.0.1", "1.1", "1.1.1") _ID_PREFIX = 'coa' + stage = VocabField("Stage", Stage) + type_ = VocabField("Type", COAType) + objective = fields.TypedField("Objective", Objective) + parameter_observables = fields.TypedField("Parameter_Observables", Observables) + structured_coa = fields.TypedField("Structured_COA", type_=_BaseStructuredCOA, factory=StructuredCOAFactory) + impact = fields.TypedField("Impact", Statement) + cost = fields.TypedField("Cost", Statement) + efficacy = fields.TypedField("Efficacy", Statement) + related_coas = fields.TypedField("Related_COAs", RelatedCOAs) + related_packages = fields.TypedField("Related_Packages", RelatedPackageRefs) + information_source = fields.TypedField("Information_Source", InformationSource) + def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): @@ -48,173 +84,8 @@ def __init__(self, id_=None, idref=None, timestamp=None, title=None, short_description=short_description ) - self.stage = None - self.type_ = None - self.objective = None - self.parameter_observables = None - self.structured_coa = None - self.impact = None - self.cost = None - self.efficacy = None - self.handling = None self.related_coas = RelatedCOAs() - self.related_packages = related.RelatedPackageRefs() - - @property - def stage(self): - return self._stage - - @stage.setter - def stage(self, value): - self._set_vocab(Stage, stage=value) - - @property - def type_(self): - return self._type - - @type_.setter - def type_(self, value): - self._set_vocab(COAType, type=value) - - @property - def objective(self): - return self._objective - - @objective.setter - def objective(self, value): - self._set_var(Objective, try_cast=False, objective=value) - - @property - def impact(self): - return self._impact - - @impact.setter - def impact(self, value): - self._set_var(Statement, impact=value) - - @property - def cost(self): - return self._cost - - @cost.setter - def cost(self, value): - self._set_var(Statement, cost=value) - - @property - def efficacy(self): - return self._efficacy - - @efficacy.setter - def efficacy(self, value): - self._set_var(Statement, efficacy=value) - - @property - def handling(self): - return self._handling - - @handling.setter - def handling(self, value): - self._set_var(Marking, try_cast=False, handling=value) - - @property - def structured_coa(self): - return self._structured_coa - - @structured_coa.setter - def structured_coa(self, value): - self._set_var(_BaseStructuredCOA, try_cast=False, structured_coa=value) - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(CourseOfAction, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if self.stage: - return_obj.Stage = self.stage.to_obj(ns_info=ns_info) - if self.type_: - return_obj.Type = self.type_.to_obj(ns_info=ns_info) - if self.objective: - return_obj.Objective = self.objective.to_obj(ns_info=ns_info) - if self.parameter_observables: - return_obj.Parameter_Observables = self.parameter_observables.to_obj(ns_info=ns_info) - if self.impact: - return_obj.Impact = self.impact.to_obj(ns_info=ns_info) - if self.cost: - return_obj.Cost = self.cost.to_obj(ns_info=ns_info) - if self.efficacy: - return_obj.Efficacy = self.efficacy.to_obj(ns_info=ns_info) - if self.handling: - return_obj.Handling = self.handling.to_obj(ns_info=ns_info) - if self.related_coas: - return_obj.Related_COAs = self.related_coas.to_obj(ns_info=ns_info) - if self.related_packages: - return_obj.Related_Packages = self.related_packages.to_obj(ns_info=ns_info) - if self.structured_coa: - return_obj.Structured_COA = self.structured_coa.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - super(CourseOfAction, cls).from_obj(obj, return_obj=return_obj) - - if isinstance(obj, cls._binding_class): # CourseOfActionType properties - return_obj.title = obj.Title - return_obj.stage = VocabString.from_obj(obj.Stage) - return_obj.type_ = VocabString.from_obj(obj.Type) - return_obj.objective = Objective.from_obj(obj.Objective) - return_obj.parameter_observables = \ - Observables.from_obj(obj.Parameter_Observables) - return_obj.impact = Statement.from_obj(obj.Impact) - return_obj.cost = Statement.from_obj(obj.Cost) - return_obj.efficacy = Statement.from_obj(obj.Efficacy) - return_obj.handling = Marking.from_obj(obj.Handling) - return_obj.related_coas = \ - RelatedCOAs.from_obj(obj.Related_COAs) - return_obj.related_packages = \ - related.RelatedPackageRefs.from_obj(obj.Related_Packages) - return_obj.structured_coa = \ - _BaseStructuredCOA.from_obj(obj.Structured_COA) - - return return_obj - - def to_dict(self): - return super(CourseOfAction, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - super(CourseOfAction, cls).from_dict(dict_repr, return_obj=return_obj) - - get = dict_repr.get - return_obj.stage = VocabString.from_dict(get('stage')) - return_obj.type_ = VocabString.from_dict(get('type')) - return_obj.objective = Objective.from_dict(get('objective')) - return_obj.parameter_observables = \ - Observables.from_dict(get('parameter_observables')) - return_obj.impact = Statement.from_dict(get('impact')) - return_obj.cost = Statement.from_dict(get('cost')) - return_obj.efficacy = Statement.from_dict(get('efficacy')) - return_obj.handling = Marking.from_dict(get('handling')) - return_obj.related_coas = \ - RelatedCOAs.from_dict(get('related_coas')) - return_obj.related_packages = \ - related.RelatedPackageRefs.from_dict(get('related_packages')) - return_obj.structured_coa = \ - _BaseStructuredCOA.from_dict(get('structured_coa')) - - return return_obj + self.related_packages = RelatedPackageRefs() # alias for CourseOfAction COA = CourseOfAction diff --git a/stix/coa/objective.py b/stix/coa/objective.py index 1dc9d405..31266ce6 100644 --- a/stix/coa/objective.py +++ b/stix/coa/objective.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix from stix.common import StructuredText, Confidence import stix.bindings.course_of_action as coa_binding @@ -10,76 +12,13 @@ class Objective(stix.Entity): _binding = coa_binding _binding_class = coa_binding.ObjectiveType _namespace = "http://stix.mitre.org/CourseOfAction-1" - - def __init__(self, description=None, short_description=None): - self.description = description - self.short_description = short_description - self.applicability_confidence = None - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - @property - def short_description(self): - return self._short_description - - @short_description.setter - def short_description(self, value): - self._set_var(StructuredText, short_description=value) - - @property - def applicability_confidence(self): - return self._applicability_confidence - - @applicability_confidence.setter - def applicability_confidence(self, value): - self._set_var(Confidence, try_cast=False, applicability_confidence=value) - def to_obj(self, return_obj=None, ns_info=None): - super(Objective, self).to_obj(return_obj=return_obj, ns_info=ns_info) + applicability_confidence = fields.TypedField("Applicability_Confidence", Confidence) + description = fields.TypedField("Description", StructuredText) + short_description = fields.TypedField("Short_Description", StructuredText) - if not return_obj: - return_obj = self._binding_class() - - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.short_description: - return_obj.Short_Description = self.short_description.to_obj(ns_info=ns_info) - if self.applicability_confidence: - return_obj.Applicability_Confidence = self.applicability_confidence.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.short_description = StructuredText.from_obj(obj.Short_Description) - return_obj.applicability_confidence = Confidence.from_obj(obj.Applicability_Confidence) - return return_obj - - def to_dict(self): - return super(Objective, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() + def __init__(self, description=None, short_description=None): + super(Objective, self).__init__() - get = dict_repr.get - return_obj.description = StructuredText.from_dict(get('description')) - return_obj.short_description = StructuredText.from_dict(get('short_description')) - return_obj.applicability_confidence = Confidence.from_dict(get('applicability_confidence')) - - return return_obj + self.description = description + self.short_description = short_description diff --git a/stix/coa/structured_coa.py b/stix/coa/structured_coa.py index 1b62138d..fdc82d86 100644 --- a/stix/coa/structured_coa.py +++ b/stix/coa/structured_coa.py @@ -1,87 +1,44 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# external +from mixbox import entities +from mixbox import fields + +# internal import stix from stix.bindings import course_of_action as coa_binding +class StructuredCOAFactory(entities.EntityFactory): + @classmethod + def entity_class(cls, key): + import stix.extensions.structured_coa.generic_structured_coa # noqa + return stix.lookup_extension(key) + + class _BaseStructuredCOA(stix.Entity): _namespace = "http://stix.mitre.org/CourseOfAction-1" _binding = coa_binding _binding_class = coa_binding.StructuredCOAType + id_ = fields.IdField("id") + idref = fields.IdrefField("idref") + def __init__(self, id_=None, idref=None): + super(_BaseStructuredCOA, self).__init__() self.id_ = id_ self.idref = idref - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - from stix.extensions.structured_coa.generic_structured_coa import GenericStructuredCOA # noqa - - if not return_obj: - klass = _BaseStructuredCOA.lookup_class(obj.xml_type) - return_obj = klass.from_obj(obj) - else: - return_obj.id_ = obj.id - return_obj.idref = obj.idref - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - super(_BaseStructuredCOA, self).to_obj( - return_obj=return_obj, - ns_info=ns_info - ) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.id = self.id_ - return_obj.idref = self.idref - return_obj.xsi_type = self._XSI_TYPE - - return return_obj - - @staticmethod - def lookup_class(xsi_type): - if not xsi_type: - raise ValueError("xsi:type is required") - for (k, v) in _EXTENSION_MAP.iteritems(): - # TODO: for now we ignore the prefix and just check for - # a partial match - if xsi_type in k: - return v - - raise ValueError("Unregistered xsi:type %s" % xsi_type) - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - - from stix.extensions.structured_coa.generic_structured_coa import GenericStructuredCOA # noqa - - if not return_obj: - klass = _BaseStructuredCOA.lookup_class(d.get('xsi:type')) - return_obj = klass.from_dict(d) - else: - return_obj.id_ = d.get('id') - return_obj.idref = d.get('idref') - - return return_obj - def to_dict(self): d = super(_BaseStructuredCOA, self).to_dict() - d['xsi:type'] = self._XSI_TYPE # added by subclass + d['xsi:type'] = self._XSI_TYPE return d + def to_obj(self, ns_info=None): + obj = super(_BaseStructuredCOA, self).to_obj(ns_info=ns_info) + obj.xsi_type = self._XSI_TYPE + return obj -#: Mapping of structured coa extension types to classes -_EXTENSION_MAP = {} - - -def add_extension(cls): - _EXTENSION_MAP[cls._XSI_TYPE] = cls # noqa +# Backwards compatibility +add_extension = stix.add_extension diff --git a/stix/common/__init__.py b/stix/common/__init__.py index 32a55393..06a03ca7 100644 --- a/stix/common/__init__.py +++ b/stix/common/__init__.py @@ -1,7 +1,11 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. from __future__ import absolute_import +from sys import version_info + +from mixbox.fields import TypedField, CDATAField +from mixbox.vendor.six import text_type from .structured_text import StructuredText # noqa from .vocabs import VocabString # noqa @@ -14,6 +18,8 @@ from .tools import ToolInformation # noqa from .names import Names # noqa from .campaign_reference import CampaignRef # noqa +from .references import References # noqa +from .profiles import Profiles # noqa from .related import ( # noqa GenericRelationshipList, RelatedCampaign, RelatedCOA, RelatedExploitTarget, RelatedIdentity, RelatedIncident, @@ -21,34 +27,6 @@ RelatedPackage, RelatedPackages, RelatedCampaignRef ) -# Patch in base types of Related* types -from stix.core import STIXPackage -from cybox.core import Observable -from stix.campaign import Campaign -from stix.coa import CourseOfAction -from stix.exploit_target import ExploitTarget -from stix.incident import Incident -from stix.indicator import Indicator -from stix.threat_actor import ThreatActor -from stix.ttp import TTP - - -RelatedCampaign._base_type = Campaign # noqa -RelatedCOA._base_type = CourseOfAction # noqa -RelatedExploitTarget._base_type = ExploitTarget # noqa -RelatedIdentity._base_type = Identity # noqa -RelatedIncident._base_type = Incident # noqa -RelatedIndicator._base_type = Indicator # noqa -RelatedThreatActor._base_type = ThreatActor # noqa -RelatedTTP._base_type = TTP # noqa -RelatedObservable._base_type = Observable # noqa -RelatedPackage._base_type = STIXPackage # noqa -RelatedCampaignRef._base_type = CampaignRef # noqa - -# Path the RelatedPackages _contained_type -RelatedPackages._contained_type = RelatedPackage # noqa - - import stix import stix.utils as utils import stix.bindings.stix_common as common_binding @@ -58,73 +36,24 @@ class EncodedCDATA(stix.Entity): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = _binding.EncodedCDATAType - + + value = CDATAField("valueOf_", key_name="value") + encoded = TypedField("encoded") + def __init__(self, value=None, encoded=None): + super(EncodedCDATA, self).__init__() self.value = value self.encoded = encoded - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._value = utils.strip_cdata(value) - @property def cdata(self): return utils.cdata(self.value) - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.value = obj.valueOf_ - return_obj.encoded = obj.encoded - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - super(EncodedCDATA, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.encoded = self.encoded - return_obj.valueOf_ = utils.cdata(self.value) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - - if not return_obj: - return_obj = cls() - - return_obj.value = utils.strip_cdata(d.get('value')) - return_obj.encoded = bool(d.get('encoded')) - - return return_obj - - def to_dict(self): - d = {} - - if not self.value: - return d - - d['value'] = utils.strip_cdata(self.value) - d['encoded'] = bool(self.encoded) - - return d - def __str__(self): - return self.__unicode__().encode("utf-8") + if version_info < (3,): + return self.__unicode__().encode("utf-8") + else: + return self.__unicode__() def __unicode__(self): - return unicode(self.value) + return text_type(self.value) diff --git a/stix/common/activity.py b/stix/common/activity.py index 76f1d175..87d99bfc 100644 --- a/stix/common/activity.py +++ b/stix/common/activity.py @@ -1,8 +1,11 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix import stix.bindings.stix_common as common_binding +import stix.utils from .datetimewithprecision import DateTimeWithPrecision from .structured_text import StructuredText @@ -13,65 +16,8 @@ class Activity(stix.Entity): _binding_class = common_binding.ActivityType _namespace = 'http://stix.mitre.org/common-1' - def __init__(self): - self.date_time = None - self.description = None - - @property - def date_time(self): - return self._date_time - - @date_time.setter - def date_time(self, value): - self._set_var(DateTimeWithPrecision, date_time=value) - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(Activity, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.date_time: - return_obj.Date_Time = self.date_time.to_obj(ns_info=ns_info) - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - - return return_obj + date_time = fields.TypedField("Date_Time", DateTimeWithPrecision) + description = fields.TypedField("Description", StructuredText) - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.date_time = DateTimeWithPrecision.from_obj(obj.Date_Time) - return_obj.description = StructuredText.from_obj(obj.Description) - - return return_obj - - def to_dict(self): - return super(Activity, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - get = dict_repr.get - return_obj.date_time = DateTimeWithPrecision.from_dict(get('date_time')) - return_obj.description = StructuredText.from_dict(get('description')) - - return return_obj + def __init__(self): + super(Activity, self).__init__() diff --git a/stix/common/campaign_reference.py b/stix/common/campaign_reference.py index e8ff1ebe..3eee73b8 100644 --- a/stix/common/campaign_reference.py +++ b/stix/common/campaign_reference.py @@ -1,9 +1,11 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# external +from mixbox import fields + # internal import stix -import stix.utils as utils import stix.bindings.stix_common as common_binding # relative @@ -15,77 +17,15 @@ class CampaignRef(stix.Entity): _binding = common_binding _binding_class = common_binding.CampaignReferenceType - def __init__(self, idref=None, timestamp=None, **kwargs): - super(CampaignRef, self).__init__(**kwargs) - self.idref = idref - self.timestamp = timestamp - self.names = None - - @property - def timestamp(self): - return self._timestamp - - @timestamp.setter - def timestamp(self, value): - self._timestamp = utils.dates.parse_value(value) + idref = fields.TypedField("idref") + timestamp = fields.DateTimeField("timestamp") + names = fields.TypedField("Names", Names) - @property - def names(self): - return self._names + def __init__(self, idref=None, timestamp=None): + super(CampaignRef, self).__init__() - @names.setter - def names(self, value): - self._names = Names(value) + self.idref = idref + self.timestamp = timestamp def add_name(self, name): self.names.append(name) - - def to_obj(self, return_obj=None, ns_info=None): - - if not return_obj: - return_obj = self._binding_class() - - return_obj = super(CampaignRef, self).to_obj( - return_obj=return_obj, - ns_info=ns_info - ) - - return_obj.idref = self.idref - return_obj.timestamp = utils.serialize_value(self.timestamp) - - if self.names: - return_obj.Names = self.names.to_obj() - - return return_obj - - def to_dict(self): - return super(CampaignRef, self).to_dict() - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.idref = obj.idref - return_obj.timestamp = obj.timestamp - return_obj.names = Names.from_obj(obj.Names) - - return return_obj - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - get = dict_repr.get - return_obj.idref = get("idref") - return_obj.timestamp = get("timestamp") - return_obj.names = Names.from_dict(get("names")) - - return return_obj diff --git a/stix/common/confidence.py b/stix/common/confidence.py index 4751c166..26b0796d 100644 --- a/stix/common/confidence.py +++ b/stix/common/confidence.py @@ -1,14 +1,17 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. from __future__ import absolute_import +from mixbox import fields + import stix import stix.utils as utils import stix.bindings.stix_common as common_binding -from . import vocabs, VocabString +from .vocabs import VocabField from .structured_text import StructuredText +from .datetimewithprecision import validate_precision class Confidence(stix.Entity): @@ -16,117 +19,25 @@ class Confidence(stix.Entity): _binding = common_binding _binding_class = common_binding.ConfidenceType - def __init__(self, value=None, timestamp=None, description=None, source=None): + value = VocabField("Value") + description = fields.TypedField("Description", StructuredText) + timestamp = fields.DateTimeField("timestamp") + timestamp_precision = fields.TypedField("timestamp_precision", preset_hook=validate_precision) + source = fields.TypedField("Source", type_="stix.common.InformationSource") + + def __init__(self, value=None, timestamp=None, description=None, + source=None): + super(Confidence, self).__init__() + self.timestamp = timestamp or utils.dates.now() self.timestamp_precision = "second" self.value = value self.description = description self.source = source + # TODO: support confidence_assertion_chain # self.confidence_assertion_chain = None - @property - def timestamp(self): - return self._timestamp - - @timestamp.setter - def timestamp(self, value): - self._timestamp = utils.dates.parse_value(value) - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._set_vocab(vocabs.HighMediumLow, value=value) - - @property - def source(self): - return self._source - - @source.setter - def source(self, value): - from .information_source import InformationSource - self._set_var(InformationSource, try_cast=False, source=value) - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - # @property - # def confidence_assertion_chain(self): - # return self._confidence_assertion_chain - - # @confidence_assertion_chain.setter - # def confidence_assertion_chain(self, value): - # if value: - # raise NotImplementedError() - - def to_obj(self, return_obj=None, ns_info=None): - super(Confidence, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - obj = self._binding_class() - obj.timestamp = utils.dates.serialize_value(self.timestamp) - obj.timestamp_precision = self.timestamp_precision - - if self.value: - obj.Value = self.value.to_obj(ns_info=ns_info) - if self.description: - obj.Description = self.description.to_obj(ns_info=ns_info) - if self.source: - obj.Source = self.source.to_obj(ns_info=ns_info) - - return obj - - def to_dict(self): - d = utils.to_dict(self, skip=('timestamp_precision',)) - - if self.timestamp_precision != 'second': - d['timestamp_precision'] = self.timestamp_precision - - return d - - @classmethod - def from_obj(cls, obj, return_obj=None): - from .information_source import InformationSource - - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.timestamp = obj.timestamp - return_obj.timestamp_precision = obj.timestamp_precision - return_obj.value = VocabString.from_obj(obj.Value) - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.source = InformationSource.from_obj(obj.Source) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - from .information_source import InformationSource - - if not d: - return None - - if not return_obj: - return_obj = cls() - - return_obj.timestamp = d.get('timestamp') - return_obj.timestamp_precision = d.get('timestamp_precision', 'second') - return_obj.value = VocabString.from_dict(d.get('value')) - return_obj.description = StructuredText.from_dict(d.get('description')) - return_obj.source = InformationSource.from_dict(d.get('source')) - - return return_obj - # class ConfidenceAssertionChain(stix.Entity): # _namespace = 'http://stix.mitre.org/common-2' diff --git a/stix/common/datetimewithprecision.py b/stix/common/datetimewithprecision.py index 248da5f2..28b58342 100644 --- a/stix/common/datetimewithprecision.py +++ b/stix/common/datetimewithprecision.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix import stix.utils as utils import stix.bindings.stix_common as common_binding @@ -11,78 +13,43 @@ DATETIME_PRECISION_VALUES = DATE_PRECISION_VALUES + TIME_PRECISION_VALUES +def validate_precision(instance, value): + if value is None: + return + elif value in DATETIME_PRECISION_VALUES: + return + else: + error = "The precision must be one of {0}. Received '{1}'" + error = error.format(DATETIME_PRECISION_VALUES, value) + raise ValueError(error) + + class DateTimeWithPrecision(stix.Entity): _binding = common_binding _binding_class = _binding.DateTimeWithPrecisionType _namespace = 'http://stix.mitre.org/common-1' + value = fields.DateTimeField("valueOf_", key_name="value") + precision = fields.TypedField("precision", preset_hook=validate_precision) + def __init__(self, value=None, precision='second'): super(DateTimeWithPrecision, self).__init__() self.value = value self.precision = precision - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._value = utils.dates.parse_value(value) - - @property - def precision(self): - return self._precision - - @precision.setter - def precision(self, value): - if value not in DATETIME_PRECISION_VALUES: - error = "The precision must be one of {0}. Received '{1}'" - error = error.format(DATE_PRECISION_VALUES, value) - raise ValueError(error) - - self._precision = value - - def to_obj(self, return_obj=None, ns_info=None): - super(DateTimeWithPrecision, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - obj = self._binding_class() - obj.valueOf_ = utils.dates.serialize_value(self.value) - obj.precision = self.precision - return obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.value = obj.valueOf_ - return_obj.precision = obj.precision - return return_obj - def to_dict(self): if self.precision == 'second': return utils.dates.serialize_value(self.value) - - d = {} - d['value'] = utils.dates.serialize_value(self.value) - d['precision'] = self.precision - return d + return super(DateTimeWithPrecision, self).to_dict() @classmethod - def from_dict(cls, dict_, return_obj=None): - if not dict_: + def from_dict(cls, cls_dict): + if not cls_dict: return None - - if not return_obj: - return_obj = cls() - - if not isinstance(dict_, dict): - return_obj.value = dict_ + elif not isinstance(cls_dict, dict): + obj = cls() + obj.value = cls_dict else: - return_obj.precision = dict_.get('precision') - return_obj.value = dict_.get('value') + obj = super(DateTimeWithPrecision, cls).from_dict(cls_dict) - return return_obj + return obj diff --git a/stix/common/identity.py b/stix/common/identity.py index dad8227b..a51b1725 100644 --- a/stix/common/identity.py +++ b/stix/common/identity.py @@ -1,142 +1,52 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -from __future__ import absolute_import +# external +from mixbox import fields +from mixbox import entities +# internal import stix import stix.bindings.stix_common as common_binding +from stix.bindings.stix_common import IdentityType + + +class IdentityFactory(entities.EntityFactory): + @classmethod + def entity_class(cls, key): + import stix.extensions.identity.ciq_identity_3_0 # noqa + return stix.lookup_extension(key, default=Identity) class Identity(stix.Entity): _binding = common_binding + _binding_class = IdentityType _namespace = 'http://stix.mitre.org/common-1' + id_ = fields.IdField("id") + idref = fields.IdrefField("idref") + name = fields.TypedField("Name") + related_identities = fields.TypedField("Related_Identities", type_="stix.common.identity.RelatedIdentities") + def __init__(self, id_=None, idref=None, name=None, related_identities=None): + super(Identity, self).__init__() + self.id_ = id_ self.idref = idref self.name = name self.related_identities = related_identities - @property - def id_(self): - return self._id - - @id_.setter - def id_(self, value): - if not value: - self._id = None - else: - self._id = value - self.idref = None - - @property - def idref(self): - return self._idref - - @idref.setter - def idref(self, value): - if not value: - self._idref = None - else: - self._idref = value - self.id_ = None # unset id_ if idref is present - - @property - def name(self): - return self._name - - @name.setter - def name(self, value): - self._name = value if value else None - - @property - def related_identities(self): - return self._related_identities - - @related_identities.setter - def related_identities(self, value): - self._related_identities = RelatedIdentities(value) - - def to_obj(self, return_obj=None, ns_info=None): - super(Identity, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding.IdentityType() - - return_obj.id = self.id_ - return_obj.idref = self.idref - - if self.name: - return_obj.Name = self.name - if self.related_identities: - return_obj.Related_Identities = \ - self.related_identities.to_obj(ns_info=ns_info) - - return return_obj - - @staticmethod - def lookup_class(xsi_type): - if not xsi_type: - raise ValueError("xsi:type is required") - - for (k, v) in _EXTENSION_MAP.iteritems(): - # TODO: for now we ignore the prefix and just check for - # a partial match - if xsi_type in k: - return v - - raise ValueError("Unregistered xsi:type %s" % xsi_type) - - @classmethod - def from_obj(cls, obj, return_obj=None): - import stix.extensions.identity.ciq_identity_3_0 # noqa - - if not obj: - return None - - if not return_obj: - if hasattr(obj, 'xml_type'): - klass = Identity.lookup_class(obj.xml_type) - return_obj = klass.from_obj(obj) - else: - return_obj = Identity.from_obj(obj, cls()) - else: - return_obj.id_ = obj.id - return_obj.idref = obj.idref - return_obj.name = obj.Name - return_obj.related_identities = \ - RelatedIdentities.from_obj(obj.Related_Identities) - - return return_obj - def to_dict(self): - return super(Identity, self).to_dict() + d = super(Identity, self).to_dict() - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - import stix.extensions.identity.ciq_identity_3_0 # noqa - - if not dict_repr: - return None + if self._XSI_TYPE: + d['xsi:type'] = self._XSI_TYPE - get = dict_repr.get - - if not return_obj: - xsi_type = get('xsi:type') - if xsi_type: - klass = Identity.lookup_class(get('xsi:type')) - return_obj = klass.from_dict(dict_repr) - else: - return_obj = Identity.from_dict(dict_repr, cls()) - else: - return_obj.name = get('name') - return_obj.id_ = get('id') - return_obj.idref = get('idref') - return_obj.related_identities = \ - RelatedIdentities.from_dict(get('related_identities')) - - return return_obj + return d + @staticmethod + def lookup_class(xsi_type): + return stix.lookup_extension(xsi_type, default=Identity) # We can't import RelatedIdentity until we have defined the Identity class. from stix.common.related import RelatedIdentity @@ -146,14 +56,13 @@ class RelatedIdentities(stix.EntityList): _namespace = 'http://stix.mitre.org/common-1' _binding = common_binding _binding_class = common_binding.RelatedIdentitiesType - _binding_var = "Related_Identity" - _contained_type = RelatedIdentity - _inner_name = "identities" + related_identity = fields.TypedField("Related_Identity", RelatedIdentity, multiple=True, key_name="identities") -#: Mapping of identity extension types to classes -_EXTENSION_MAP = {} + @classmethod + def _dict_as_list(cls): + return False -def add_extension(cls): - _EXTENSION_MAP[cls._XSI_TYPE] = cls # noqa +# Backwards compatibility +add_extension = stix.add_extension diff --git a/stix/common/information_source.py b/stix/common/information_source.py index d4fac2f0..10e6941e 100644 --- a/stix/common/information_source.py +++ b/stix/common/information_source.py @@ -1,19 +1,19 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -from __future__ import absolute_import - # external +from mixbox import fields import cybox.common +from cybox.common.tools import ToolInformationList # internal import stix -import stix.utils as utils import stix.bindings.stix_common as stix_common_binding # relative -from . import vocabs, VocabString -from .identity import Identity +from .vocabs import VocabField +from .references import References +from .identity import Identity, IdentityFactory from .structured_text import StructuredText @@ -22,182 +22,47 @@ class InformationSource(stix.Entity): _binding_class = stix_common_binding.InformationSourceType _namespace = 'http://stix.mitre.org/common-1' - def __init__(self, description=None, identity=None, time=None, tools=None, contributing_sources=None, references=None): - self.description = description + identity = fields.TypedField("Identity", type_=Identity, factory=IdentityFactory) + description = fields.TypedField("Description", StructuredText) + contributing_sources = fields.TypedField("Contributing_Sources", type_="stix.common.information_source.ContributingSources") + time = fields.TypedField("Time", cybox.common.Time) + roles = VocabField("Role", multiple=True, key_name="roles") + tools = fields.TypedField("Tools", ToolInformationList) + references = fields.TypedField("References", References) + + def __init__(self, description=None, identity=None, time=None, tools=None, + contributing_sources=None, references=None): + super(InformationSource, self).__init__() + self.identity = identity + self.description = description self.contributing_sources = contributing_sources self.time = time self.tools = tools self.references = references - self.roles = None - - @property - def contributing_sources(self): - return self._contributing_sources - - @contributing_sources.setter - def contributing_sources(self, value): - self._contributing_sources = ContributingSources(value) def add_contributing_source(self, value): self.contributing_sources.append(value) - - @property - def references(self): - return self._references - - @references.setter - def references(self, value): - self._references = [] - if not value: - return - elif utils.is_sequence(value): - for v in value: - self.add_reference(v) - else: - self.add_reference(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) - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - """Sets the value of the description property. - - If the value is an instance of basestring, it will be coerced into an - instance of StructuredText, with its 'text' property set to the input - value. - - """ - self._set_var(StructuredText, description=value) - - @property - def identity(self): - return self._identity - - @identity.setter - def identity(self, value): - self._set_var(Identity, try_cast=False, identity=value) - - @property - def time(self): - return self._time - - @time.setter - def time(self, value): - self._set_var(cybox.common.Time, try_cast=False, time=value) - - @property - def tools(self): - return self._tools - - @tools.setter - def tools(self, value): - self._set_var(cybox.common.ToolInformationList, try_cast=False, tools=value) - - @property - def roles(self): - return self._roles - - @roles.setter - def roles(self, value): - self._roles = _Roles(value) - def add_role(self, value): self.roles.append(value) - def to_obj(self, return_obj=None, ns_info=None): - super(InformationSource, self).to_obj( - return_obj=return_obj, - ns_info=ns_info - ) - - if not return_obj: - return_obj = self._binding_class() - - if self.description is not None: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.references: - return_obj.References = stix_common_binding.ReferencesType(Reference=self.references) - if self.contributing_sources: - return_obj.Contributing_Sources = self.contributing_sources.to_obj(ns_info=ns_info) - if self.identity: - return_obj.Identity = self.identity.to_obj(ns_info=ns_info) - if self.time: - return_obj.Time = self.time.to_obj(ns_info=ns_info) - if self.tools: - return_obj.Tools = self.tools.to_obj(ns_info=ns_info) - if self.roles: - return_obj.Role = self.roles.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.identity = Identity.from_obj(obj.Identity) - return_obj.contributing_sources = ContributingSources.from_obj(obj.Contributing_Sources) - return_obj.roles = _Roles.from_obj(obj.Role) - - if obj.References: - return_obj.references = obj.References.Reference - if obj.Time: - return_obj.time = cybox.common.Time.from_obj(obj.Time) - if obj.Tools: - return_obj.tools = cybox.common.ToolInformationList.from_obj(obj.Tools) - - return return_obj - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - # To resolve circular dependency - # TODO: Improve how this extension is handled. - - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - get = dict_repr.get - return_obj.description = StructuredText.from_dict(get('description')) - return_obj.references = get('references') - return_obj.contributing_sources = ContributingSources.from_dict(get('contributing_sources')) - return_obj.identity = Identity.from_dict(get('identity')) - return_obj.time = cybox.common.Time.from_dict(get('time')) - return_obj.tools = cybox.common.ToolInformationList.from_list(get('tools')) - return_obj.roles = _Roles.from_dict(get('roles')) - - return return_obj - - def to_dict(self): - return super(InformationSource, self).to_dict() - class ContributingSources(stix.EntityList): _namespace = "http://stix.mitre.org/common-1" _binding = stix_common_binding _binding_class = stix_common_binding.ContributingSourcesType - _binding_var = "Source" - _contained_type = InformationSource - _inner_name = "sources" + source = fields.TypedField("Source", InformationSource, multiple=True, key_name="sources") -# NOT AN ACTUAL STIX TYPE! -class _Roles(stix.TypedList): - _contained_type = VocabString - - def _fix_value(self, value): - return vocabs.InformationSourceRole(value) + @classmethod + def _dict_as_list(cls): + return False diff --git a/stix/common/kill_chains/__init__.py b/stix/common/kill_chains/__init__.py index 02189dda..0214c4fd 100644 --- a/stix/common/kill_chains/__init__.py +++ b/stix/common/kill_chains/__init__.py @@ -1,50 +1,40 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields +from mixbox import typedlist + # internal import stix import stix.bindings.stix_common as common_binding +from mixbox.vendor.six import string_types + class KillChain(stix.Entity): _binding = common_binding _namespace = 'http://stix.mitre.org/common-1' _binding_class = _binding.KillChainType + id_ = fields.TypedField("id") + name = fields.TypedField("name") + definer = fields.TypedField("definer") + reference = fields.TypedField("reference") + number_of_phases = fields.TypedField("number_of_phases") + kill_chain_phases = fields.TypedField("Kill_Chain_Phase", type_="stix.common.kill_chains.KillChainPhase", multiple=True, key_name="kill_chain_phases") + def __init__(self, id_=None, name=None, definer=None, reference=None): + super(KillChain, self).__init__() + self.id_ = id_ self.name = name self.definer = definer self.reference = reference self.number_of_phases = None # can we just do len(self.kill_chain_phases)? - self.kill_chain_phases = None - - @property - def kill_chain_phases(self): - return self._kill_chain_phases - - @kill_chain_phases.setter - def kill_chain_phases(self, value): - self._kill_chain_phases = _KillChainPhases(value) def add_kill_chain_phase(self, value): self.kill_chain_phases.append(value) - def to_obj(self, return_obj=None, ns_info=None): - super(KillChain, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.id = self.id_ - return_obj.name = self.name - return_obj.definer = self.definer - return_obj.reference = self.reference - return_obj.number_of_phases = self.number_of_phases - return_obj.Kill_Chain_Phase = self.kill_chain_phases.to_obj(ns_info=ns_info) - - return return_obj - def __eq__(self, other): if self is other: return True @@ -57,48 +47,17 @@ def __eq__(self, other): def __ne__(self, other): return not self.__eq__(other) - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.id_ = obj.id - return_obj.name = obj.name - return_obj.definer = obj.definer - return_obj.reference = obj.reference - return_obj.number_of_phases = obj.number_of_phases - return_obj.kill_chain_phases = _KillChainPhases.from_obj(obj.Kill_Chain_Phase) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - get = d.get - return_obj.id_ = get('id') - return_obj.name = get('name') - return_obj.definer = get('definer') - return_obj.reference = get('reference') - return_obj.number_of_phases = get('number_of_phases') - return_obj.kill_chain_phases = \ - _KillChainPhases.from_dict(get('kill_chain_phases')) - - return return_obj - class KillChains(stix.EntityList): _binding = common_binding _namespace = 'http://stix.mitre.org/common-1' _binding_class = _binding.KillChainsType - _contained_type = KillChain - _binding_var = "Kill_Chain" - _inner_name = "kill_chains" + + kill_chain = fields.TypedField("Kill_Chain", KillChain, multiple=True, key_name="kill_chains") + + @classmethod + def _dict_as_list(cls): + return False class KillChainPhase(stix.Entity): @@ -106,42 +65,17 @@ class KillChainPhase(stix.Entity): _namespace = 'http://stix.mitre.org/common-1' _binding_class = _binding.KillChainPhaseType + phase_id = fields.TypedField("phase_id") + name = fields.TypedField("name") + ordinality = fields.IntegerField("ordinality") + def __init__(self, phase_id=None, name=None, ordinality=None): + super(KillChainPhase, self).__init__() + self.phase_id = phase_id self.name = name self.ordinality = ordinality - @property - def phase_id(self): - return self._phase_id - - @phase_id.setter - def phase_id(self, value): - self._set_var(basestring, try_cast=False, phase_id=value) - - @property - def ordinality(self): - return self._ordinality - - @ordinality.setter - def ordinality(self, value): - if value is not None: - self._ordinality = int(value) - else: - self._ordinality = None - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(KillChainPhase, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - return_obj.phase_id = self.phase_id - return_obj.name = self.name - return_obj.ordinality = self.ordinality - - return return_obj - def __eq__(self, other): if other is self: return True @@ -155,103 +89,54 @@ def __ne__(self, other): return not self.__eq__(other) def __hash__(self): + # TODO (bworrell): Is all the tuple(sorted(...))) needed? return hash(tuple(sorted(self.to_dict().items()))) - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.phase_id = obj.phase_id - return_obj.name = obj.name - return_obj.ordinality = obj.ordinality - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - return_obj.phase_id = d.get('phase_id') - return_obj.name = d.get('name') - return_obj.ordinality = d.get('ordinality') - - return return_obj - - def to_dict(self): - return super(KillChainPhase, self).to_dict() - class KillChainPhaseReference(KillChainPhase): _binding = common_binding _namespace = 'http://stix.mitre.org/common-1' _binding_class = _binding.KillChainPhaseReferenceType + kill_chain_id = fields.TypedField("kill_chain_id") + kill_chain_name = fields.TypedField("kill_chain_name") + def __init__(self, phase_id=None, name=None, ordinality=None, kill_chain_id=None, kill_chain_name=None): super(KillChainPhaseReference, self).__init__(phase_id, name, ordinality) self.kill_chain_id = kill_chain_id self.kill_chain_name = kill_chain_name - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(KillChainPhaseReference, self).to_obj(return_obj=return_obj, ns_info=ns_info) - return_obj.kill_chain_id = self.kill_chain_id - return_obj.kill_chain_name = self.kill_chain_name - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - super(KillChainPhaseReference, cls).from_obj(obj, return_obj=return_obj) +class _KillChainPhaseReferenceList(typedlist.TypedList): + def __init__(self, *args): + super(_KillChainPhaseReferenceList, self).__init__(type=KillChainPhaseReference, *args) - return_obj.kill_chain_id = obj.kill_chain_id - return_obj.kill_chain_name = obj.kill_chain_name - return return_obj - - def to_dict(self): - return super(KillChainPhaseReference, self).to_dict() + def _fix_value(self, value): + if not isinstance(value, KillChainPhase): + return super(_KillChainPhaseReferenceList, self)._fix_value(value) - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() + if value.phase_id: + return KillChainPhaseReference(phase_id=value.phase_id) - super(KillChainPhaseReference, cls).from_dict(d, return_obj=return_obj) - return_obj.kill_chain_id = d.get('kill_chain_id') - return_obj.kill_chain_name = d.get('kill_chain_name') - return return_obj + raise ValueError("KillChainPhase must have a phase_id.") class KillChainPhasesReference(stix.EntityList): _binding = common_binding _namespace = 'http://stix.mitre.org/common-1' _binding_class = _binding.KillChainPhasesReferenceType - _contained_type = KillChainPhaseReference - _binding_var = "Kill_Chain_Phase" - _inner_name = "kill_chain_phases" - def _fix_value(self, value): - if not isinstance(value, KillChainPhase): - return super(KillChainPhasesReference, self)._fix_value(value) + kill_chain_phase = fields.TypedField( + name="Kill_Chain_Phase", + type_=KillChainPhaseReference, + multiple=True, + listfunc=_KillChainPhaseReferenceList, + key_name="kill_chain_phases" + ) - if value.phase_id: - return KillChainPhaseReference(phase_id=value.phase_id) - - raise ValueError("KillChainPhase must have a phase_id.") + @classmethod + def _dict_as_list(cls): + return False # NOT AN ACTUAL STIX TYPE! diff --git a/stix/common/kill_chains/lmco.py b/stix/common/kill_chains/lmco.py index 76430d4f..ad087e43 100644 --- a/stix/common/kill_chains/lmco.py +++ b/stix/common/kill_chains/lmco.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. from . import KillChain, KillChainPhase diff --git a/stix/common/names.py b/stix/common/names.py index 31a95943..a0f59320 100644 --- a/stix/common/names.py +++ b/stix/common/names.py @@ -1,19 +1,20 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# external +from mixbox import fields + # internal import stix import stix.bindings.stix_common as common_binding # relative -from .vocabs import VocabString +from .vocabs import VocabField class Names(stix.EntityList): - _namespace = "http://stix.mitre.org/common-1" + _namespace = 'http://stix.mitre.org/common-1' _binding = common_binding _binding_class = _binding.NamesType - _contained_type = VocabString - _binding_var = 'Name' - _inner_name = 'names' - _dict_as_list = True + + name = VocabField("Name", multiple=True) diff --git a/stix/common/profiles.py b/stix/common/profiles.py new file mode 100644 index 00000000..3e2f2d8d --- /dev/null +++ b/stix/common/profiles.py @@ -0,0 +1,50 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# 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 Profiles(compat.MutableSequence, stix.Entity): + _binding = stix_common_binding + _binding_class = stix_common_binding.ProfilesType + _namespace = 'http://stix.mitre.org/common-1' + + # Fields + profile = fields.TypedField("Profile", multiple=True) + + def __init__(self, profiles=None): + super(Profiles, self).__init__() + self.profile = profiles + + def __len__(self): + return self.profile.__len__() + + def __getitem__(self, item): + return self.profile.__getitem__(item) + + def __setitem__(self, key, value): + self.profile.__setitem__(key, value) + + def __delitem__(self, key): + self.profile.__delitem__(key) + + def insert(self, index, value): + self.profile.insert(index, value) + + def to_dict(self): + return [x for x in self] + + @classmethod + def from_dict(cls, cls_dict=None): + if not cls_dict: + return None + + obj = cls() + obj.profile = [x for x in cls_dict] + return obj diff --git a/stix/common/references.py b/stix/common/references.py new file mode 100644 index 00000000..9bedda4d --- /dev/null +++ b/stix/common/references.py @@ -0,0 +1,50 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# 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(compat.MutableSequence, stix.Entity): + _binding = stix_common_binding + _binding_class = stix_common_binding.ReferencesType + _namespace = 'http://stix.mitre.org/common-1' + + # Fields + reference = fields.TypedField("Reference", multiple=True) + + def __init__(self, references=None): + super(References, self).__init__() + self.reference = references + + def __len__(self): + return self.reference.__len__() + + def __getitem__(self, item): + return self.reference.__getitem__(item) + + def __setitem__(self, key, value): + self.reference.__setitem__(key, value) + + def __delitem__(self, key): + self.reference.__delitem__(key) + + def insert(self, index, value): + self.reference.insert(index, value) + + def to_dict(self): + return [x for x in self] + + @classmethod + def from_dict(cls, cls_dict=None): + if not cls_dict: + return None + + obj = cls() + obj.reference = [x for x in cls_dict] + return obj diff --git a/stix/common/related.py b/stix/common/related.py index f3718dfb..26736950 100644 --- a/stix/common/related.py +++ b/stix/common/related.py @@ -1,190 +1,91 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -from __future__ import absolute_import +# stdlib +import functools + +# mixbox +from mixbox import fields +from mixbox import typedlist + +# cybox +from cybox.core import Observable # internal import stix -import stix.utils as utils import stix.bindings.stix_common as common_binding import stix.bindings.stix_core as core_binding # relative -from .vocabs import VocabString +from .vocabs import VocabField from .information_source import InformationSource from .confidence import Confidence +ALLOWED_SCOPE = ('inclusive', 'exclusive') + + +def validate_scope(instance, value): + if not value: + return + elif value in ALLOWED_SCOPE: + return + else: + msg = "Scope must be one of {0}. Received '{1}'" + msg = msg.format(ALLOWED_SCOPE, value) + raise ValueError(msg) + + class GenericRelationship(stix.Entity): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = common_binding.GenericRelationshipType + confidence = fields.TypedField("Confidence", Confidence) + information_source = fields.TypedField("Information_Source", InformationSource) + relationship = VocabField("Relationship") + def __init__(self, confidence=None, information_source=None, relationship=None): + super(GenericRelationship, self).__init__() + self.confidence = confidence self.information_source = information_source self.relationship = relationship - @property - def confidence(self): - return self._confidence - - @confidence.setter - def confidence(self, value): - self._set_var(Confidence, confidence=value) - - @property - def information_source(self): - return self._information_source - - @information_source.setter - def information_source(self, value): - self._set_var(InformationSource, try_cast=False, information_source=value) - - @property - def relationship(self): - return self._relationship - - @relationship.setter - def relationship(self, value): - self._set_vocab(relationship=value) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.confidence = Confidence.from_obj(obj.Confidence) - return_obj.information_source = InformationSource.from_obj(obj.Information_Source) - return_obj.relationship = VocabString.from_obj(obj.Relationship) - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - super(GenericRelationship, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.confidence: - return_obj.Confidence = self.confidence.to_obj(ns_info=ns_info) - if self.information_source: - return_obj.Information_Source = self.information_source.to_obj(ns_info=ns_info) - if self.relationship: - return_obj.Relationship = self.relationship.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - return_obj.confidence = Confidence.from_dict(dict_repr.get('confidence')) - return_obj.information_source = InformationSource.from_dict(dict_repr.get('information_source')) - return_obj.relationship = VocabString.from_dict(dict_repr.get('relationship')) - - return return_obj - - def to_dict(self,): - d = {} - if self.confidence: - d['confidence'] = self.confidence.to_dict() - if self.information_source: - d['information_source'] = self.information_source.to_dict() - if self.relationship: - d['relationship'] = self.relationship.to_dict() - - return d - class RelatedPackageRef(GenericRelationship): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = common_binding.RelatedPackageRefType - def __init__(self, **kwargs): - super(RelatedPackageRef, self).__init__(**kwargs) - self.idref = None - self.timestamp = None - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - return_obj = super(RelatedPackageRef, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if self.idref: - return_obj.idref = self.idref - if self.timestamp: - return_obj.timestamp = self.timestamp - - return return_obj - - @property - def timestamp(self): - return self._timestamp - - @timestamp.setter - def timestamp(self, value): - self._timestamp = utils.dates.parse_value(value) - - def to_dict(self): - d = super(RelatedPackageRef, self).to_dict() - - if self.idref: - d['idref'] = self.idref - if self.timestamp: - d['timestamp'] = utils.dates.serialize_value(self.timestamp) - - return d - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - super(RelatedPackageRef, cls).from_obj(obj, return_obj) - - return_obj.idref = obj.idref - return_obj.timestamp = obj.timestamp - - return return_obj + idref = fields.IdrefField("idref") + timestamp = fields.DateTimeField("timestamp") - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - super(RelatedPackageRef, cls).from_dict(dict_repr, return_obj) + def __init__(self, idref=None, timestamp=None, confidence=None, + information_source=None, relationship=None): - return_obj.idref = dict_repr.get("idref") - return_obj.timestamp = dict_repr.get("timestamp") + super(RelatedPackageRef, self).__init__( + confidence=confidence, + information_source=information_source, + relationship=relationship + ) - return return_obj + self.idref = idref + self.timestamp = timestamp -class GenericRelationshipList(stix.EntityList): +class GenericRelationshipEntity(stix.Entity): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = _binding.GenericRelationshipListType _ALLOWED_SCOPE = ('inclusive', 'exclusive') + scope = fields.TypedField("scope") + def __init__(self, scope=None, *args): - super(GenericRelationshipList, self).__init__(*args) + + super(GenericRelationshipEntity, self).__init__(*args) self.scope = scope def __nonzero__(self): @@ -193,260 +94,195 @@ def __nonzero__(self): bool(self.scope) ) - @property - def scope(self): - return self._scope - @scope.setter - def scope(self, value): - if value is None or value in self._ALLOWED_SCOPE: - self._scope = value - return +class GenericRelationshipList(stix.EntityList): + """Base class for concrete GenericRelationshipList types. - msg = "Scope must be one of {0}. Received '{1}'" - msg = msg.format(self._ALLOWED_SCOPE, value) - raise ValueError(msg) + Note: + Subclasses must supply exactly one multiple TypedField. + """ - def to_obj(self, return_obj=None, ns_info=None): - list_obj = super(GenericRelationshipList, self).to_obj( - return_obj=return_obj, - ns_info=ns_info - ) + _namespace = "http://stix.mitre.org/common-1" + _binding = common_binding + _binding_class = _binding.GenericRelationshipListType - list_obj.scope = self.scope - return list_obj + scope = fields.TypedField("scope", preset_hook=validate_scope) - def to_dict(self): - return super(GenericRelationshipList, self).to_dict() + def __init__(self, scope=None, *args): + super(GenericRelationshipList, self).__init__(*args) + self.scope = scope - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if return_obj is None: - return_obj = cls() - - super(GenericRelationshipList, cls).from_obj( - obj, - return_obj=return_obj, - contained_type=cls._contained_type, - binding_var=cls._binding_var - ) + def __nonzero__(self): + return (super(GenericRelationshipList, self).__nonzero__() or + bool(self.scope)) - return_obj.scope = obj.scope + @classmethod + def _dict_as_list(cls): + return False - return return_obj - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if return_obj is None: - return_obj = cls() - - super(GenericRelationshipList, cls).from_dict( - dict_repr, - return_obj=return_obj, - contained_type=cls._contained_type, - inner_name=cls._inner_name - ) +class _RelatedPackageList(typedlist.TypedList): + def __init__(self, *args): + super(_RelatedPackageList, self).__init__(type=RelatedPackageRef, *args) - return_obj.scope = dict_repr.get('scope') + def _fix_value(self, value): + from stix.core import STIXPackage - return return_obj + if isinstance(value, STIXPackage) and value.id_: + return RelatedPackageRef(idref=value.id_, timestamp=value.timestamp) + fmt = ("Cannot add type '{0}' to RelatedPackageRefs collection. " + "Expected RelatedPackageRef or STIXPackage") + error = fmt.format(type(value)) + raise TypeError(error) -class RelatedPackages(GenericRelationshipList): - _namespace = 'http://stix.mitre.org/stix-1' - _binding = core_binding - _binding_class = core_binding.RelatedPackagesType - _binding_var = "Related_Package" - # _contained_type is patched in common/__init__.py - _inner_name = "related_packages" + def _is_valid(self, value): + return super(_RelatedPackageList, self)._is_valid(value) class RelatedPackageRefs(stix.EntityList): _namespace = 'http://stix.mitre.org/common-1' _binding = common_binding _binding_class = common_binding.RelatedPackageRefsType - _binding_var = "Package_Reference" - _contained_type = RelatedPackageRef - _inner_name = "packages" + + package = fields.TypedField( + name="Package_Reference", + type_=RelatedPackageRef, + multiple=True, + key_name="packages", + listfunc=_RelatedPackageList + ) + + @classmethod + def _dict_as_list(cls): + return False class _BaseRelated(GenericRelationship): """A base class for related types. This class is not a real STIX type and should not be directly instantiated. + + Note: + Subclasses must supply a TypedField named `item`! """ - # Subclasses should define - # - _base_type - # - _inner_var (This is the name of the contained XML element, and the - # lowercase version is used for the key name in the - # dictionary representation). - _base_type = None - _inner_var = None + item = None # override in subclass. def __init__(self, item=None, confidence=None, information_source=None, relationship=None): + super(_BaseRelated, self).__init__( confidence, information_source, relationship ) - self.item = item - - @property - def item(self): - return self._item - - @item.setter - def item(self, value): - self._set_item(value) - - def _set_item(self, value): - if value and not isinstance(value, self._base_type): - error = "Value must be instance of %s" % self._base_type.__name__ - raise ValueError(error) - - self._item = value - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(_BaseRelated, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if self.item: - setattr(return_obj, self._inner_var, self.item.to_obj(ns_info=ns_info)) - - return return_obj - - def to_dict(self): - d = utils.to_dict(self, skip=('item',)) - - if self.item: - d[self._inner_var.lower()] = self.item.to_dict() - return d - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - super(_BaseRelated, cls).from_obj(obj, return_obj) - - contained_item = getattr(obj, cls._inner_var) - return_obj.item = cls._base_type.from_obj(contained_item) - - return return_obj - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - super(_BaseRelated, cls).from_dict(dict_repr, return_obj) - - contained_item = dict_repr.get(cls._inner_var.lower()) - return_obj.item = cls._base_type.from_dict(contained_item) - - return return_obj + self.item = item class RelatedCampaign(_BaseRelated): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = common_binding.RelatedCampaignType - # _base_type is set in common/__init__.py - _inner_var = "Campaign" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("Campaign", type_="stix.campaign.Campaign") class RelatedCOA(_BaseRelated): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = common_binding.RelatedCourseOfActionType - # _base_type is set in common/__init__.py - _inner_var = "Course_Of_Action" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("Course_Of_Action", type_="stix.coa.CourseOfAction") class RelatedExploitTarget(_BaseRelated): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = common_binding.RelatedExploitTargetType - # _base_type is set in common/__init__.py - _inner_var = "Exploit_Target" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("Exploit_Target", type_="stix.exploit_target.ExploitTarget") class RelatedIdentity(_BaseRelated): _namespace = 'http://stix.mitre.org/common-1' _binding = common_binding _binding_class = common_binding.RelatedIdentityType - # _base_type is set in common/__init__.py - _inner_var = "Identity" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("Identity", type_="stix.common.identity.Identity", factory="stix.common.identity.IdentityFactory") class RelatedIncident(_BaseRelated): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = common_binding.RelatedIncidentType - # _base_type is set in common/__init__.py - _inner_var = "Incident" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("Incident", type_="stix.incident.Incident") class RelatedIndicator(_BaseRelated): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = common_binding.RelatedIndicatorType - # _base_type is set in common/__init__.py - _inner_var = "Indicator" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("Indicator", type_="stix.indicator.Indicator") class RelatedObservable(_BaseRelated): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = common_binding.RelatedObservableType - # _base_type is set in common/__init__.py - _inner_var = "Observable" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("Observable", type_=Observable) class RelatedThreatActor(_BaseRelated): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = common_binding.RelatedThreatActorType - # _base_type is set in common/__init__.py - _inner_var = "Threat_Actor" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("Threat_Actor", type_="stix.threat_actor.ThreatActor") class RelatedTTP(_BaseRelated): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = common_binding.RelatedTTPType - # _base_type is set in common/__init__.py - _inner_var = "TTP" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("TTP", type_="stix.ttp.TTP") class RelatedPackage(_BaseRelated): _namespace = "http://stix.mitre.org/stix-1" _binding = core_binding _binding_class = core_binding.RelatedPackageType - # _base_type is set in common/__init__.py - _inner_var = "Package" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("Package", type_="stix.core.STIXPackage") class RelatedCampaignRef(_BaseRelated): _namespace = "http://stix.mitre.org/common-1" _binding = common_binding _binding_class = _binding.RelatedCampaignReferenceType - # _base_type is set in common/__init__.py - _inner_var = "Campaign" + + # _BaseRelated requires an "item" field. + item = fields.TypedField("Campaign", type_="stix.common.CampaignRef") + + +class RelatedPackages(GenericRelationshipList): + _namespace = 'http://stix.mitre.org/stix-1' + _binding = core_binding + _binding_class = core_binding.RelatedPackagesType + + related_package = fields.TypedField("Related_Package", RelatedPackage, multiple=True, key_name="related_packages") diff --git a/stix/common/statement.py b/stix/common/statement.py index 69294d18..b1950266 100644 --- a/stix/common/statement.py +++ b/stix/common/statement.py @@ -1,15 +1,20 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. from __future__ import absolute_import +from mixbox import fields + import stix import stix.utils as utils import stix.bindings.stix_common as common_binding +from .datetimewithprecision import validate_precision from .confidence import Confidence from .structured_text import StructuredText -from .vocabs import VocabString, HighMediumLow +from .vocabs import VocabField, HighMediumLow +import mixbox +from stix.common.vocabs import VocabString class Statement(stix.Entity): @@ -17,8 +22,18 @@ class Statement(stix.Entity): _binding = common_binding _binding_class = common_binding.StatementType + # Fields + timestamp = fields.DateTimeField("timestamp") + timestamp_precision = fields.TypedField("timestamp_precision", preset_hook=validate_precision) + value = VocabField("Value", VocabString) + description = fields.TypedField("Description", StructuredText) + confidence = fields.TypedField("Confidence", Confidence) + source = fields.TypedField("Source", type_="stix.common.InformationSource") + def __init__(self, value=None, timestamp=None, description=None, source=None): + super(Statement, self).__init__() + self.timestamp = timestamp or utils.dates.now() self.timestamp_precision = "second" self.value = value @@ -26,100 +41,20 @@ def __init__(self, value=None, timestamp=None, description=None, self.source = source self.confidence = None - @property - def timestamp(self): - return self._timestamp - - @timestamp.setter - def timestamp(self, value): - self._timestamp = utils.dates.parse_value(value) - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._set_vocab(HighMediumLow, value=value) - - @property - def source(self): - return self._source - - @source.setter - def source(self, value): - from .information_source import InformationSource - self._set_var(InformationSource, try_cast=False, source=value) - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(Statement, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - obj = self._binding_class() - - if self.timestamp: - obj.timestamp = self.timestamp.isoformat() - obj.timestamp_precision = self.timestamp_precision - if self.value: - obj.Value = self.value.to_obj(ns_info=ns_info) - if self.description: - obj.Description = self.description.to_obj(ns_info=ns_info) - if self.source: - obj.Source = self.source.to_obj(ns_info=ns_info) - if self.confidence: - obj.Confidence = self.confidence.to_obj(ns_info=ns_info) - return obj +class StatementField(mixbox.fields.TypedField): + def __init__(self, *args, **kwargs): + self._vocab_type = kwargs.pop("vocab_type") + super(StatementField, self).__init__(*args, **kwargs) + self.type_ = Statement - def to_dict(self): - d = utils.to_dict(self, skip=('timestamp_precision',)) - - if self.timestamp_precision != 'second': - d['timestamp_precision'] = self.timestamp_precision - - return d - - @classmethod - def from_obj(cls, obj, return_obj=None): - from .information_source import InformationSource - - if not obj: + def _clean(self, value): + if value is None: return None - - if not return_obj: - return_obj = cls() - - return_obj.timestamp = obj.timestamp - return_obj.timestamp_precision = obj.timestamp_precision - return_obj.value = VocabString.from_obj(obj.Value) - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.source = InformationSource.from_obj(obj.Source) - return_obj.confidence = Confidence.from_obj(obj.Confidence) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - from .information_source import InformationSource - - if not d: - return None - - if not return_obj: - return_obj = cls() - - return_obj.timestamp = d.get('timestamp') - return_obj.timestamp_precision = d.get('timestamp_precision', 'second') - return_obj.value = VocabString.from_dict(d.get('value')) - return_obj.description = StructuredText.from_dict(d.get('description')) - return_obj.source = InformationSource.from_dict(d.get('source')) - return_obj.confidence = Confidence.from_dict(d.get('confidence')) - - return return_obj + elif isinstance(value, Statement): + return value + elif isinstance(value, stix.common.VocabString): + return Statement(value) + else: + vocabklass = self._vocab_type + return Statement(vocabklass(value)) diff --git a/stix/common/structured_text.py b/stix/common/structured_text.py index f9b07d89..66f6c187 100644 --- a/stix/common/structured_text.py +++ b/stix/common/structured_text.py @@ -1,69 +1,77 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +import itertools +import contextlib +import collections +from sys import version_info + +from mixbox import fields + import stix +import stix.utils as utils import stix.bindings.stix_common as stix_common_binding +from mixbox.vendor.six import text_type class StructuredText(stix.Entity): + """Used for storing descriptive text elements. + + Attributes: + id_: An id for the text element, typically used for controlled + structure xpath selectors. + value: The text value of this object. + structuring_format: The format of the text. For example, ``html5``. + """ + _binding = stix_common_binding + _binding_class = _binding.StructuredTextType _namespace = 'http://stix.mitre.org/common-1' + id_ = fields.IdField("id") + value = fields.TypedField("valueOf_", key_name="value") + structuring_format = fields.TypedField("structuring_format") + def __init__(self, value=None): + super(StructuredText, self).__init__() + + self.id_ = None self.value = value self.structuring_format = None - def to_obj(self, return_obj=None, ns_info=None): - super(StructuredText, self).to_obj( - return_obj=return_obj, - ns_info=ns_info + def is_plain(self): + plain = ( + (not self.id_) and + (not self.structuring_format) ) - text_obj = self._binding.StructuredTextType() - - text_obj.valueOf_ = self.value - if self.structuring_format is not None: - text_obj.structuring_format = self.structuring_format - return text_obj + return plain def to_dict(self): + """Converts this object into a dictionary representation. + + Note: + If no properies or attributes are set other than ``value``, + this will return a string. + + """ # Return a plain string if there is no format specified. - if self.structuring_format is None: + if self.is_plain(): return self.value else: return super(StructuredText, self).to_dict() - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.value = obj.valueOf_ - return_obj.structuring_format = obj.structuring_format - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - - if not return_obj: - return_obj = cls() + def __str__(self): + """Returns a UTF-8 encoded string representation of the ``value``. - if not isinstance(d, dict): - return_obj.value = d + """ + if version_info < (3,): + return self.__unicode__().encode("utf-8") else: - return_obj.value = d.get('value') - return_obj.structuring_format = d.get('structuring_format') - - return return_obj - - def __str__(self): - return self.__unicode__().encode("utf-8") + return self.__unicode__() def __unicode__(self): - return unicode(self.value) + """Returns a ``unicode`` string representation of the ``value``. + + """ + return text_type(self.value) diff --git a/stix/common/tools.py b/stix/common/tools.py index 046b4332..2bcca8f7 100644 --- a/stix/common/tools.py +++ b/stix/common/tools.py @@ -1,8 +1,11 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. # external +from mixbox import fields + import cybox.common +from cybox.common.vocabs import VocabField # internal import stix @@ -10,83 +13,25 @@ # relative from .structured_text import StructuredText +from .vocabs import AttackerToolType + class ToolInformation(stix.Entity, cybox.common.ToolInformation): _namespace = 'http://stix.mitre.org/common-1' _binding = common_binding _binding_class = common_binding.ToolInformationType - - def __init__(self, title=None, short_description=None, tool_name=None, tool_vendor=None): - super(ToolInformation, self).__init__(tool_name=tool_name, tool_vendor=tool_vendor) - self.title = title - self.short_description = short_description - - @property - def title(self): - return self._title - @title.setter - def title(self, value): - self._title = value + title = fields.TypedField("Title") + short_description = fields.TypedField("Short_Description", StructuredText) + type_ = VocabField("Type", AttackerToolType, multiple=True) - @property - def short_description(self): - return self._short_description + def __init__(self, title=None, short_description=None, tool_name=None, + tool_vendor=None): - @short_description.setter - def short_description(self, value): - self._set_var(StructuredText, short_description=value) - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - stix.Entity.to_obj(self, return_obj=return_obj, ns_info=ns_info) - cybox.common.ToolInformation.to_obj( - self, - return_obj=return_obj, - ns_info=ns_info + super(ToolInformation, self).__init__( + tool_name=tool_name, + tool_vendor=tool_vendor ) - return_obj.Title = self.title - if self.short_description: - return_obj.Short_Description = self.short_description.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - cybox.common.ToolInformation.from_obj(obj, return_obj) - - return_obj.title = obj.Title - return_obj.short_description = StructuredText.from_obj(obj.Short_Description) - - return return_obj - - def to_dict(self): - d = cybox.common.ToolInformation.to_dict(self) - - if self.title: - d['title'] = self.title - if self.short_description: - d['short_description'] = self.short_description.to_dict() - - return d - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - cybox.common.ToolInformation.from_dict(dict_repr, return_obj) - return_obj.title = dict_repr.get('title') - return_obj.short_description = StructuredText.from_dict(dict_repr.get('short_description')) - - return return_obj + self.title = title + self.short_description = short_description diff --git a/stix/common/vocabs.py b/stix/common/vocabs.py index db0ef3bd..105056da 100644 --- a/stix/common/vocabs.py +++ b/stix/common/vocabs.py @@ -1,15 +1,89 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# stdlib +from functools import partial + +# mixbox +from mixbox import entities, fields, typedlist + +# cybox +from cybox.common import vocabs + +# stix import stix import stix.bindings.stix_common as stix_common_binding -# TODO: handle normalization -# from cybox.utils import normalize_to_xml, denormalize_from_xml +def validate_value(instance, value): + allowed = instance._ALLOWED_VALUES + + if not value: + return + elif not allowed: + return + elif value in allowed: + return + else: + error = "Value for vocab {instance.__class__} must be one of {allowed}. Received '{value}'" + error = error.format(**locals()) + raise ValueError(error) + + +class VocabList(typedlist.TypedList): + """VocabString fields can be any type of VocabString, though there is often + a preferred/default VocabString type. + + The TypedList will attempt to make sure that every input item is an instance + of the default VocabString and throw an error if it isn't. This sublcass + overrides that behavior and allows any instance of VocabString to be + inserted. + """ + + def _is_valid(self, value): + return isinstance(value, VocabString) + + +class VocabField(fields.TypedField): + """TypedField subclass for VocabString fields.""" + + def __init__(self, *args, **kwargs): + """Intercepts the __init__() call to TypedField. + + Set the type that will be used in from_dict and from_obj calls to + :class:`VocabString`. + + Set the type that will be used in ``__set__`` for casting as the + original ``type_`` argument, or :class:`VocabString` if no `type_` + argument was provided. + + """ + super(VocabField, self).__init__(*args, **kwargs) + self.factory = VocabFactory # force this factory + + if self._unresolved_type is None: + self.type_ = VocabString + + self._listfunc = partial(VocabList, type=self._unresolved_type) + + def check_type(self, value): + return isinstance(value, VocabString) + + +class VocabFactory(entities.EntityFactory): + _convert_strings = True + + @classmethod + def entity_class(cls, key): + try: + return stix.lookup_extension(key, default=VocabString) + except ValueError: + return VocabString class VocabString(stix.Entity): + __hash__ = entities.Entity.__hash__ + _binding = stix_common_binding _binding_class = stix_common_binding.ControlledVocabularyStringType _namespace = 'http://stix.mitre.org/common-1' @@ -18,31 +92,16 @@ class VocabString(stix.Entity): _XSI_TYPE = None _ALLOWED_VALUES = None + value = fields.TypedField("valueOf_", key_name="value", preset_hook=validate_value) + vocab_name = fields.TypedField("vocab_name") + vocab_reference = fields.TypedField("vocab_reference") + xsi_type = fields.TypedField("xsi_type", key_name="xsi:type") + def __init__(self, value=None): super(VocabString, self).__init__() self.value = value self.xsi_type = self._XSI_TYPE - self.vocab_name = None - self.vocab_reference = None - - @property - def value(self): - return self._value - - @value.setter - def value(self, v): - allowed = self._ALLOWED_VALUES - - if not v: - self._value = None - elif allowed and (v not in allowed): - error = "Value must be one of {0}. Received '{1}'" - error = error.format(allowed, v) - raise ValueError(error) - else: - self._value = v - def __str__(self): return str(self.value) @@ -56,111 +115,32 @@ def __eq__(self, other): def is_plain(self): """Whether the VocabString can be represented as a single value.""" return ( - self._XSI_TYPE is None and + self.xsi_type is None and self.vocab_name is None and self.vocab_reference is None ) - @staticmethod - def lookup_class(xsi_type): - if not xsi_type: - return VocabString - - for (k, v) in _VOCAB_MAP.iteritems(): - # TODO: for now we ignore the prefix and just check for - # a partial match - if xsi_type in k: - return v - - return VocabString - - def to_obj(self, return_obj=None, ns_info=None): - super(VocabString, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - # TODO: handle normalization - # vocab_obj.valueOf_ = normalize_to_xml(self.value) - return_obj.valueOf_ = self.value - return_obj.xsi_type = self.xsi_type - - if self.vocab_name is not None: - return_obj.vocab_name = self.vocab_name - if self.vocab_reference is not None: - return_obj.vocab_reference = self.vocab_reference - - return return_obj - def to_dict(self): if self.is_plain(): return self.value - - d = {} - if self.value is not None: - d['value'] = self.value - if self.xsi_type is not None: - d['xsi:type'] = self.xsi_type - if self.vocab_name is not None: - d['vocab_name'] = self.vocab_name - if self.vocab_reference is not None: - d['vocab_reference'] = self.vocab_reference - - return d - - @classmethod - def from_obj(cls, vocab_obj, return_obj=None): - if not vocab_obj: - return None - - if not return_obj: - klass = VocabString.lookup_class(vocab_obj.xsi_type) - return klass.from_obj(vocab_obj, klass()) - - # xsi_type should be set automatically by the class's constructor. - - # TODO: handle denormalization - # vocab_str.value = denormalize_from_xml(vocab_obj.valueOf_) - return_obj.value = vocab_obj.valueOf_ - return_obj.vocab_name = vocab_obj.vocab_name - return_obj.vocab_reference = vocab_obj.vocab_reference - return_obj.xsi_type = vocab_obj.xsi_type - - return return_obj + return super(VocabString, self).to_dict() @classmethod - def from_dict(cls, vocab_dict, return_obj=None): - if not vocab_dict: - return None - - if not return_obj: - if isinstance(vocab_dict, dict): - klass = VocabString.lookup_class(vocab_dict.get('xsi:type')) - return klass.from_dict(vocab_dict, klass()) - else: - return_obj = cls() - - # xsi_type should be set automatically by the class's constructor. - - # In case this is a "plain" string, just set it. - if not isinstance(vocab_dict, dict): - return_obj.value = vocab_dict + def from_dict(cls, cls_dict): + if not cls_dict: + vocab = None + elif not isinstance(cls_dict, dict): + vocab = cls() + vocab.value = cls_dict else: - return_obj.value = vocab_dict.get('value') - return_obj.vocab_name = vocab_dict.get('vocab_name') - return_obj.vocab_reference = vocab_dict.get('vocab_reference') - return_obj.xsi_type = vocab_dict.get('xsi:type') - - return return_obj + vocab = super(VocabString, cls).from_dict(cls_dict) - -#: Mapping of Controlled Vocabulary xsi:type's to their class implementations. -_VOCAB_MAP = {} + return vocab def _get_terms(vocab_class): """Helper function used by register_vocab.""" - for k, v in vocab_class.__dict__.items(): + for k, v in vars(vocab_class).items(): if k.startswith("TERM_"): yield v @@ -172,11 +152,11 @@ def add_vocab(cls): The :meth:`register_vocab` class decorator has replaced this method. """ - _VOCAB_MAP[cls._XSI_TYPE] = cls + stix.add_extension(cls) def register_vocab(cls): - """Register a VocabString subclass. + """Class decorator that registers a VocabString subclass. Also, calculate all the permitted values for class being decorated by adding an ``_ALLOWED_VALUES`` tuple of all the values of class members @@ -190,31 +170,43 @@ def register_vocab(cls): @register_vocab -class AvailabilityLossType(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:AvailabilityLossTypeVocab-1.1.1' +class AvailabilityLossType_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:AvailabilityLossTypeVocab-1.0" + _VOCAB_VERSION = "1.0" + TERM_ACCELERATION = "Acceleration" + TERM_DEGREDATION = "Degredation" TERM_DESTRUCTION = "Destruction" - TERM_LOSS = "Loss" TERM_INTERRUPTION = "Interruption" - TERM_DEGRADATION = "Degradation" + TERM_LOSS = "Loss" + TERM_OBSCURATION = "Obscuration" + TERM_UNKNOWN = "Unknown" + + +@register_vocab +class AvailabilityLossType_1_1_1(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:AvailabilityLossTypeVocab-1.1.1" + _VOCAB_VERSION = "1.1.1" + TERM_ACCELERATION = "Acceleration" + TERM_DEGRADATION = "Degradation" + TERM_DESTRUCTION = "Destruction" + TERM_INTERRUPTION = "Interruption" + TERM_LOSS = "Loss" TERM_OBSCURATION = "Obscuration" TERM_UNKNOWN = "Unknown" @register_vocab -class ThreatActorType(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:ThreatActorTypeVocab-1.0' +class ThreatActorType_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:ThreatActorTypeVocab-1.0" + _VOCAB_VERSION = "1.0" TERM_CYBER_ESPIONAGE_OPERATIONS = "Cyber Espionage Operations" - TERM_HACKER = "Hacker" - TERM_HACKER_WHITE_HAT = "Hacker - White hat" - TERM_HACKER_GRAY_HAT = "Hacker - Gray hat" - TERM_HACKER_BLACK_HAT = "Hacker - Black hat" - TERM_HACKTIVIST = "Hacktivist" - TERM_STATE_ACTOR_OR_AGENCY = "State Actor / Agency" + TERM_DISGRUNTLED_CUSTOMER_OR_USER = "Disgruntled Customer / User" TERM_ECRIME_ACTOR_CREDENTIAL_THEFT_BOTNET_OPERATOR = "eCrime Actor - Credential Theft Botnet Operator" TERM_ECRIME_ACTOR_CREDENTIAL_THEFT_BOTNET_SERVICE = "eCrime Actor - Credential Theft Botnet Service" TERM_ECRIME_ACTOR_MALWARE_DEVELOPER = "eCrime Actor - Malware Developer" @@ -223,14 +215,20 @@ class ThreatActorType(VocabString): TERM_ECRIME_ACTOR_SPAM_SERVICE = "eCrime Actor - Spam Service" TERM_ECRIME_ACTOR_TRAFFIC_SERVICE = "eCrime Actor - Traffic Service" TERM_ECRIME_ACTOR_UNDERGROUND_CALL_SERVICE = "eCrime Actor - Underground Call Service" + TERM_HACKER = "Hacker" + TERM_HACKER_BLACK_HAT = "Hacker - Black hat" + TERM_HACKER_GRAY_HAT = "Hacker - Gray hat" + TERM_HACKER_WHITE_HAT = "Hacker - White hat" + TERM_HACKTIVIST = "Hacktivist" TERM_INSIDER_THREAT = "Insider Threat" - TERM_DISGRUNTLED_CUSTOMER_OR_USER = "Disgruntled Customer / User" + TERM_STATE_ACTOR_OR_AGENCY = "State Actor / Agency" @register_vocab -class AttackerInfrastructureType(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:AttackerInfrastructureTypeVocab-1.0' +class AttackerInfrastructureType_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:AttackerInfrastructureTypeVocab-1.0" + _VOCAB_VERSION = "1.0" TERM_ANONYMIZATION = "Anonymization" TERM_ANONYMIZATION_PROXY = "Anonymization - Proxy" @@ -249,96 +247,144 @@ class AttackerInfrastructureType(VocabString): TERM_DOMAIN_REGISTRATION_LEGITIMATE_DOMAIN_REGISTRATION_SERVICES = "Domain Registration - Legitimate Domain Registration Services" TERM_DOMAIN_REGISTRATION_MALICIOUS_DOMAIN_REGISTRARS = "Domain Registration - Malicious Domain Registrars" TERM_DOMAIN_REGISTRATION_TOPLEVEL_DOMAIN_REGISTRARS = "Domain Registration - Top-Level Domain Registrars" + TERM_ELECTRONIC_PAYMENT_METHODS = "Electronic Payment Methods" TERM_HOSTING = "Hosting" TERM_HOSTING_BULLETPROOF_OR_ROGUE_HOSTING = "Hosting - Bulletproof / Rogue Hosting" TERM_HOSTING_CLOUD_HOSTING = "Hosting - Cloud Hosting" TERM_HOSTING_COMPROMISED_SERVER = "Hosting - Compromised Server" TERM_HOSTING_FAST_FLUX_BOTNET_HOSTING = "Hosting - Fast Flux Botnet Hosting" TERM_HOSTING_LEGITIMATE_HOSTING = "Hosting - Legitimate Hosting" - TERM_ELECTRONIC_PAYMENT_METHODS = "Electronic Payment Methods" @register_vocab -class DiscoveryMethod(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:DiscoveryMethodVocab-1.0' +class DiscoveryMethod_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:DiscoveryMethodVocab-1.0" + _VOCAB_VERSION = "1.0" TERM_AGENT_DISCLOSURE = "Agent Disclosure" + TERM_ANTIVIRUS = "Antivirus" + TERM_AUDIT = "Audit" + TERM_CUSTOMER = "Customer" + TERM_FINANCIAL_AUDIT = "Financial Audit" TERM_FRAUD_DETECTION = "Fraud Detection" - TERM_MONITORING_SERVICE = "Monitoring Service" + TERM_HIPS = "HIPS" + TERM_INCIDENT_RESPONSE = "Incident Response" + TERM_IT_AUDIT = "IT Audit" TERM_LAW_ENFORCEMENT = "Law Enforcement" - TERM_CUSTOMER = "Customer" + TERM_LOG_REVIEW = "Log Review" + TERM_MONITORING_SERVICE = "Monitoring Service" + TERM_NIDS = "NIDS" + TERM_SECURITY_ALARM = "Security Alarm" + TERM_UNKNOWN = "Unknown" TERM_UNRELATED_PARTY = "Unrelated Party" - TERM_AUDIT = "Audit" + TERM_USER = "User" + +@register_vocab +class DiscoveryMethod_2_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:DiscoveryMethodVocab-2.0" + _VOCAB_VERSION = "2.0" + + TERM_AGENT_DISCLOSURE = "Agent Disclosure" TERM_ANTIVIRUS = "Antivirus" - TERM_INCIDENT_RESPONSE = "Incident Response" + TERM_AUDIT = "Audit" + TERM_CUSTOMER = "Customer" + TERM_EXTERNAL_FRAUD_DETECTION = "External - Fraud Detection" TERM_FINANCIAL_AUDIT = "Financial Audit" TERM_HIPS = "HIPS" + TERM_INCIDENT_RESPONSE = "Incident Response" + TERM_INTERNAL_FRAUD_DETECTION = "Internal - Fraud Detection" TERM_IT_AUDIT = "IT Audit" + TERM_LAW_ENFORCEMENT = "Law Enforcement" TERM_LOG_REVIEW = "Log Review" + TERM_MONITORING_SERVICE = "Monitoring Service" TERM_NIDS = "NIDS" TERM_SECURITY_ALARM = "Security Alarm" - TERM_USER = "User" TERM_UNKNOWN = "Unknown" + TERM_UNRELATED_PARTY = "Unrelated Party" + TERM_USER = "User" +@vocabs.register_vocab @register_vocab -class AttackerToolType(VocabString): +class AttackerToolType_1_0(vocabs.VocabString): _namespace = 'http://stix.mitre.org/default_vocabularies-1' _XSI_TYPE = 'stixVocabs:AttackerToolTypeVocab-1.0' + _VOCAB_VERSION = '1.0' + TERM_APPLICATION_SCANNER = "Application Scanner" TERM_MALWARE = "Malware" + TERM_PASSWORD_CRACKING = "Password Cracking" TERM_PENETRATION_TESTING = "Penetration Testing" TERM_PORT_SCANNER = "Port Scanner" TERM_TRAFFIC_SCANNER = "Traffic Scanner" TERM_VULNERABILITY_SCANNER = "Vulnerability Scanner" - TERM_APPLICATION_SCANNER = "Application Scanner" - TERM_PASSWORD_CRACKING = "Password Cracking" @register_vocab -class IndicatorType(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:IndicatorTypeVocab-1.1' +class IndicatorType_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:IndicatorTypeVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_MALICIOUS_EMAIL = "Malicious E-mail" - TERM_IP_WATCHLIST = "IP Watchlist" - TERM_FILE_HASH_WATCHLIST = "File Hash Watchlist" + TERM_ANONYMIZATION = "Anonymization" + TERM_C2 = "C2" TERM_DOMAIN_WATCHLIST = "Domain Watchlist" - TERM_URL_WATCHLIST = "URL Watchlist" + TERM_EXFILTRATION = "Exfiltration" + TERM_FILE_HASH_WATCHLIST = "File Hash Watchlist" + TERM_HOST_CHARACTERISTICS = "Host Characteristics" + TERM_IP_WATCHLIST = "IP Watchlist" + TERM_MALICIOUS_EMAIL = "Malicious E-mail" TERM_MALWARE_ARTIFACTS = "Malware Artifacts" - TERM_C2 = "C2" + TERM_URL_WATCHLIST = "URL Watchlist" + + +@register_vocab +class IndicatorType_1_1(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:IndicatorTypeVocab-1.1" + _VOCAB_VERSION = "1.1" + TERM_ANONYMIZATION = "Anonymization" + TERM_C2 = "C2" + TERM_COMPROMISED_PKI_CERTIFICATE = "Compromised PKI Certificate" + TERM_DOMAIN_WATCHLIST = "Domain Watchlist" TERM_EXFILTRATION = "Exfiltration" + TERM_FILE_HASH_WATCHLIST = "File Hash Watchlist" TERM_HOST_CHARACTERISTICS = "Host Characteristics" - TERM_COMPROMISED_PKI_CERTIFICATE = "Compromised PKI Certificate" - TERM_LOGIN_NAME = "Login Name" TERM_IMEI_WATCHLIST = "IMEI Watchlist" TERM_IMSI_WATCHLIST = "IMSI Watchlist" + TERM_IP_WATCHLIST = "IP Watchlist" + TERM_LOGIN_NAME = "Login Name" + TERM_MALICIOUS_EMAIL = "Malicious E-mail" + TERM_MALWARE_ARTIFACTS = "Malware Artifacts" + TERM_URL_WATCHLIST = "URL Watchlist" @register_vocab -class SystemType(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:SystemTypeVocab-1.0' +class SystemType_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:SystemTypeVocab-1.0" + _VOCAB_VERSION = "1.0" TERM_ENTERPRISE_SYSTEMS = "Enterprise Systems" TERM_ENTERPRISE_SYSTEMS_APPLICATION_LAYER = "Enterprise Systems - Application Layer" TERM_ENTERPRISE_SYSTEMS_DATABASE_LAYER = "Enterprise Systems - Database Layer" TERM_ENTERPRISE_SYSTEMS_ENTERPRISE_TECHNOLOGIES_AND_SUPPORT_INFRASTRUCTURE = "Enterprise Systems - Enterprise Technologies and Support Infrastructure" - TERM_ENTERPRISE_SYSTEMS_NETWORK_SYSTEMS = "Enterprise Systems - Network Systems" TERM_ENTERPRISE_SYSTEMS_NETWORKING_DEVICES = "Enterprise Systems - Networking Devices" - TERM_ENTERPRISE_SYSTEMS_WEB_LAYER = "Enterprise Systems - Web Layer" + TERM_ENTERPRISE_SYSTEMS_NETWORK_SYSTEMS = "Enterprise Systems - Network Systems" TERM_ENTERPRISE_SYSTEMS_VOIP = "Enterprise Systems - VoIP" + TERM_ENTERPRISE_SYSTEMS_WEB_LAYER = "Enterprise Systems - Web Layer" TERM_INDUSTRIAL_CONTROL_SYSTEMS = "Industrial Control Systems" TERM_INDUSTRIAL_CONTROL_SYSTEMS_EQUIPMENT_UNDER_CONTROL = "Industrial Control Systems - Equipment Under Control" TERM_INDUSTRIAL_CONTROL_SYSTEMS_OPERATIONS_MANAGEMENT = "Industrial Control Systems - Operations Management" TERM_INDUSTRIAL_CONTROL_SYSTEMS_SAFETY_PROTECTION_AND_LOCAL_CONTROL = "Industrial Control Systems - Safety, Protection and Local Control" TERM_INDUSTRIAL_CONTROL_SYSTEMS_SUPERVISORY_CONTROL = "Industrial Control Systems - Supervisory Control" TERM_MOBILE_SYSTEMS = "Mobile Systems" + TERM_MOBILE_SYSTEMS_MOBILE_DEVICES = "Mobile Systems - Mobile Devices" TERM_MOBILE_SYSTEMS_MOBILE_OPERATING_SYSTEMS = "Mobile Systems - Mobile Operating Systems" TERM_MOBILE_SYSTEMS_NEAR_FIELD_COMMUNICATIONS = "Mobile Systems - Near Field Communications" - TERM_MOBILE_SYSTEMS_MOBILE_DEVICES = "Mobile Systems - Mobile Devices" TERM_THIRDPARTY_SERVICES = "Third-Party Services" TERM_THIRDPARTY_SERVICES_APPLICATION_STORES = "Third-Party Services - Application Stores" TERM_THIRDPARTY_SERVICES_CLOUD_SERVICES = "Third-Party Services - Cloud Services" @@ -347,99 +393,180 @@ class SystemType(VocabString): TERM_THIRDPARTY_SERVICES_SOFTWARE_UPDATE = "Third-Party Services - Software Update" TERM_USERS = "Users" TERM_USERS_APPLICATION_AND_SOFTWARE = "Users - Application And Software" - TERM_USERS_WORKSTATION = "Users - Workstation" TERM_USERS_REMOVABLE_MEDIA = "Users - Removable Media" + TERM_USERS_WORKSTATION = "Users - Workstation" @register_vocab -class CampaignStatus(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:CampaignStatusVocab-1.0' +class CampaignStatus_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:CampaignStatusVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_ONGOING = "Ongoing" - TERM_HISTORIC = "Historic" TERM_FUTURE = "Future" + TERM_HISTORIC = "Historic" + TERM_ONGOING = "Ongoing" @register_vocab -class IncidentStatus(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:IncidentStatusVocab-1.0' +class IncidentStatus_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:IncidentStatusVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_NEW = "New" - TERM_OPEN = "Open" - TERM_STALLED = "Stalled" + TERM_CLOSED = "Closed" TERM_CONTAINMENT_ACHIEVED = "Containment Achieved" - TERM_RESTORATION_ACHIEVED = "Restoration Achieved" + TERM_DELETED = "Deleted" TERM_INCIDENT_REPORTED = "Incident Reported" - TERM_CLOSED = "Closed" + TERM_NEW = "New" + TERM_OPEN = "Open" TERM_REJECTED = "Rejected" - TERM_DELETED = "Deleted" + TERM_RESTORATION_ACHIEVED = "Restoration Achieved" + TERM_STALLED = "Stalled" @register_vocab -class ManagementClass(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:ManagementClassVocab-1.0' +class ManagementClass_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:ManagementClassVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_INTERNALLYMANAGED = "Internally-Managed" - TERM_EXTERNALLYMANAGEMENT = "Externally-Management" TERM_COMANAGEMENT = "Co-Management" + TERM_EXTERNALLYMANAGEMENT = "Externally-Management" + TERM_INTERNALLYMANAGED = "Internally-Managed" TERM_UNKNOWN = "Unknown" @register_vocab -class Motivation(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:MotivationVocab-1.1' +class Motivation_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:MotivationVocab-1.0" + _VOCAB_VERSION = "1.0" + TERM_EGO = "Ego" + TERM_FINANCIAL_OR_ECONOMIC = "Financial or Economic" TERM_IDEOLOGICAL = "Ideological" TERM_IDEOLOGICAL_ANTICORRUPTION = "Ideological - Anti-Corruption" - TERM_IDEOLOGICAL_ANTIESTABLISHMENT = "Ideological - Anti-Establishment" + TERM_IDEOLOGICAL_ANTIESTABLISMENT = "Ideological - Anti-Establisment" TERM_IDEOLOGICAL_ENVIRONMENTAL = "Ideological - Environmental" - TERM_IDEOLOGICAL_ETHNIC_OR_NATIONALIST = "Ideological - Ethnic / Nationalist" + TERM_IDEOLOGICAL_ETHNIC_NATIONALIST = "Ideological - Ethnic / Nationalist" + TERM_IDEOLOGICAL_HUMAN_RIGHTS = "Ideological - Human Rights" TERM_IDEOLOGICAL_INFORMATION_FREEDOM = "Ideological - Information Freedom" TERM_IDEOLOGICAL_RELIGIOUS = "Ideological - Religious" TERM_IDEOLOGICAL_SECURITY_AWARENESS = "Ideological - Security Awareness" + TERM_MILITARY = "Military" + TERM_OPPORTUNISTIC = "Opportunistic" + TERM_POLICITAL = "Policital" + + +@register_vocab +class Motivation_1_0_1(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:MotivationVocab-1.0.1" + _VOCAB_VERSION = "1.0.1" + + TERM_EGO = "Ego" + TERM_FINANCIAL_OR_ECONOMIC = "Financial or Economic" + TERM_IDEOLOGICAL = "Ideological" + TERM_IDEOLOGICAL_ANTI_CORRUPTION = "Ideological - Anti-Corruption" + TERM_IDEOLOGICAL_ANTI_ESTABLISHMENT = "Ideological - Anti-Establishment" + TERM_IDEOLOGICAL_ENVIRONMENTAL = "Ideological - Environmental" + TERM_IDEOLOGICAL_ETHNIC_NATIONALIST = "Ideological - Ethnic / Nationalist" TERM_IDEOLOGICAL_HUMAN_RIGHTS = "Ideological - Human Rights" + TERM_IDEOLOGICAL_INFORMATION_FREEDOM = "Ideological - Information Freedom" + TERM_IDEOLOGICAL_SECURITY_AWARENESS = "Ideological - Security Awareness" + TERM_IDEOLOGICAL__RELIGIOUS = "Ideological - Religious" + TERM_MILITARY = "Military" + TERM_OPPORTUNISTIC = "Opportunistic" + TERM_POLICITAL = "Policital" + + +@register_vocab +class Motivation_1_1(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:MotivationVocab-1.1" + _VOCAB_VERSION = "1.1" + TERM_EGO = "Ego" TERM_FINANCIAL_OR_ECONOMIC = "Financial or Economic" + TERM_IDEOLOGICAL = "Ideological" + TERM_IDEOLOGICAL_ANTICORRUPTION = "Ideological - Anti-Corruption" + TERM_IDEOLOGICAL_ANTIESTABLISHMENT = "Ideological - Anti-Establishment" + TERM_IDEOLOGICAL_ENVIRONMENTAL = "Ideological - Environmental" + TERM_IDEOLOGICAL_ETHNIC_NATIONALIST = "Ideological - Ethnic / Nationalist" + TERM_IDEOLOGICAL_HUMAN_RIGHTS = "Ideological - Human Rights" + TERM_IDEOLOGICAL_INFORMATION_FREEDOM = "Ideological - Information Freedom" + TERM_IDEOLOGICAL_RELIGIOUS = "Ideological - Religious" + TERM_IDEOLOGICAL_SECURITY_AWARENESS = "Ideological - Security Awareness" TERM_MILITARY = "Military" TERM_OPPORTUNISTIC = "Opportunistic" TERM_POLITICAL = "Political" @register_vocab -class IncidentCategory(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:IncidentCategoryVocab-1.0' +class IncidentCategory_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:IncidentCategoryVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_EXERCISEORNETWORK_DEFENSE_TESTING = "Exercise/Network Defense Testing" - TERM_UNAUTHORIZED_ACCESS = "Unauthorized Access" TERM_DENIAL_OF_SERVICE = "Denial of Service" - TERM_MALICIOUS_CODE = "Malicious Code" + TERM_EXERCISEORNETWORK_DEFENSE_TESTING = "Exercise/Network Defense Testing" TERM_IMPROPER_USAGE = "Improper Usage" - TERM_SCANSORPROBESORATTEMPTED_ACCESS = "Scans/Probes/Attempted Access" TERM_INVESTIGATION = "Investigation" + TERM_MALICIOUS_CODE = "Malicious Code" + TERM_SCANSORPROBESORATTEMPTED_ACCESS = "Scans/Probes/Attempted Access" + TERM_UNAUTHORIZED_ACCESS = "Unauthorized Access" @register_vocab -class ImpactQualification(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:ImpactQualificationVocab-1.0' +class ImpactQualification_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:ImpactQualificationVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_INSIGNIFICANT = "Insignificant" + TERM_CATASTROPHIC = "Catastrophic" + TERM_DAMAGING = "Damaging" TERM_DISTRACTING = "Distracting" + TERM_INSIGNIFICANT = "Insignificant" TERM_PAINFUL = "Painful" - TERM_DAMAGING = "Damaging" - TERM_CATASTROPHIC = "Catastrophic" TERM_UNKNOWN = "Unknown" @register_vocab -class PlanningAndOperationalSupport(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:PlanningAndOperationalSupportVocab-1.0.1' +class PlanningAndOperationalSupport_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:PlanningAndOperationalSupportVocab-1.0" + _VOCAB_VERSION = "1.0" + + TERM_DATA_EXPLOITATION = "Data Exploitation" + TERM_DATA_EXPLOITATION_ANALYTIC_SUPPORT = "Data Exploitation - Analytic Support" + TERM_DATA_EXPLOITATION_TRANSLATION_SUPPORT = "Data Exploitation - Translation Support" + TERM_FINANCIAL_RESOURCES = "Financial Resources" + TERM_FINANCIAL_RESOURCES_ACADEMIC = "Financial Resources - Academic" + TERM_FINANCIAL_RESOURCES_COMMERCIAL = "Financial Resources - Commercial" + TERM_FINANCIAL_RESOURCES_GOVERNMENT = "Financial Resources - Government" + TERM_FINANCIAL_RESOURCES_HACKTIVIST_OR_GRASSROOT = "Financial Resources - Hacktivist or Grassroot" + TERM_FINANCIAL_RESOURCES_NONATTRIBUTABLE_FINANCE = "Financial Resources - Non-Attributable Finance" + TERM_PLANNING = "Planning" + TERM_PLANNING_OPEN_SOURCE_INTELLIGENCE_OSINT_GETHERING = "Planning - Open-Source Intelligence (OSINT) Gethering" + TERM_PLANNING_OPERATIONAL_COVER_PLAN = "Planning - Operational Cover Plan" + TERM_PLANNING_PRE_OPERATIONAL_SURVEILLANCE_AND_RECONNAISSANCE = "Planning - Pre-Operational Surveillance and Reconnaissance" + TERM_PLANNING_TARGET_SELECTION = "Planning - Target Selection" + TERM_SKILL_DEVELOPMENT_RECRUITMENT = "Skill Development / Recruitment" + TERM_SKILL_DEVELOPMENT_RECRUITMENT_CONTRACTING_AND_HIRING = "Skill Development / Recruitment - Contracting and Hiring" + TERM_SKILL_DEVELOPMENT_RECRUITMENT_DOCUMENT_EXPLOITATION_DOCEX_TRAINING = "Skill Development / Recruitment - Document Exploitation (DOCEX) Training" + TERM_SKILL_DEVELOPMENT_RECRUITMENT_INTERNAL_TRAINING = "Skill Development / Recruitment - Internal Training" + TERM_SKILL_DEVELOPMENT_RECRUITMENT_MILITARY_PROGRAMS = "Skill Development / Recruitment - Military Programs" + TERM_SKILL_DEVELOPMENT_RECRUITMENT_SECURITY_HACKER_CONFERENCES = "Skill Development / Recruitment - Security / Hacker Conferences" + TERM_SKILL_DEVELOPMENT_RECRUITMENT_UNDERGROUND_FORUMS = "Skill Development / Recruitment - Underground Forums" + TERM_SKILL_DEVELOPMENT_RECRUITMENT_UNIVERSITY_PROGRAMS = "Skill Development / Recruitment - University Programs" + + +@register_vocab +class PlanningAndOperationalSupport_1_0_1(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:PlanningAndOperationalSupportVocab-1.0.1" + _VOCAB_VERSION = "1.0.1" TERM_DATA_EXPLOITATION = "Data Exploitation" TERM_DATA_EXPLOITATION_ANALYTIC_SUPPORT = "Data Exploitation - Analytic Support" @@ -450,6 +577,11 @@ class PlanningAndOperationalSupport(VocabString): TERM_FINANCIAL_RESOURCES_GOVERNMENT = "Financial Resources - Government" TERM_FINANCIAL_RESOURCES_HACKTIVIST_OR_GRASSROOT = "Financial Resources - Hacktivist or Grassroot" TERM_FINANCIAL_RESOURCES_NONATTRIBUTABLE_FINANCE = "Financial Resources - Non-Attributable Finance" + TERM_PLANNING = "Planning" + TERM_PLANNING_OPENSOURCE_INTELLIGENCE_OSINT_GATHERING = "Planning - Open-Source Intelligence (OSINT) Gathering" + TERM_PLANNING_OPERATIONAL_COVER_PLAN = "Planning - Operational Cover Plan" + TERM_PLANNING_PREOPERATIONAL_SURVEILLANCE_AND_RECONNAISSANCE = "Planning - Pre-Operational Surveillance and Reconnaissance" + TERM_PLANNING_TARGET_SELECTION = "Planning - Target Selection" TERM_SKILL_DEVELOPMENT_OR_RECRUITMENT = "Skill Development / Recruitment" TERM_SKILL_DEVELOPMENT_OR_RECRUITMENT_CONTRACTING_AND_HIRING = "Skill Development / Recruitment - Contracting and Hiring" TERM_SKILL_DEVELOPMENT_OR_RECRUITMENT_DOCUMENT_EXPLOITATION_DOCEX_TRAINING = "Skill Development / Recruitment - Document Exploitation (DOCEX) Training" @@ -458,167 +590,170 @@ class PlanningAndOperationalSupport(VocabString): TERM_SKILL_DEVELOPMENT_OR_RECRUITMENT_SECURITY_OR_HACKER_CONFERENCES = "Skill Development / Recruitment - Security / Hacker Conferences" TERM_SKILL_DEVELOPMENT_OR_RECRUITMENT_UNDERGROUND_FORUMS = "Skill Development / Recruitment - Underground Forums" TERM_SKILL_DEVELOPMENT_OR_RECRUITMENT_UNIVERSITY_PROGRAMS = "Skill Development / Recruitment - University Programs" - TERM_PLANNING = "Planning" - TERM_PLANNING_OPERATIONAL_COVER_PLAN = "Planning - Operational Cover Plan" - TERM_PLANNING_OPENSOURCE_INTELLIGENCE_OSINT_GATHERING = "Planning - Open-Source Intelligence (OSINT) Gathering" - TERM_PLANNING_PREOPERATIONAL_SURVEILLANCE_AND_RECONNAISSANCE = "Planning - Pre-Operational Surveillance and Reconnaissance" - TERM_PLANNING_TARGET_SELECTION = "Planning - Target Selection" @register_vocab -class CourseOfActionType(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:CourseOfActionTypeVocab-1.0' +class CourseOfActionType_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:CourseOfActionTypeVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_PERIMETER_BLOCKING = "Perimeter Blocking" - TERM_INTERNAL_BLOCKING = "Internal Blocking" - TERM_REDIRECTION = "Redirection" - TERM_REDIRECTION_HONEY_POT = "Redirection (Honey Pot)" - TERM_HARDENING = "Hardening" - TERM_PATCHING = "Patching" + TERM_DIPLOMATIC_ACTIONS = "Diplomatic Actions" TERM_ERADICATION = "Eradication" - TERM_REBUILDING = "Rebuilding" - TERM_TRAINING = "Training" + TERM_HARDENING = "Hardening" + TERM_INTERNAL_BLOCKING = "Internal Blocking" + TERM_LOGICAL_ACCESS_RESTRICTIONS = "Logical Access Restrictions" TERM_MONITORING = "Monitoring" + TERM_OTHER = "Other" + TERM_PATCHING = "Patching" + TERM_PERIMETER_BLOCKING = "Perimeter Blocking" TERM_PHYSICAL_ACCESS_RESTRICTIONS = "Physical Access Restrictions" - TERM_LOGICAL_ACCESS_RESTRICTIONS = "Logical Access Restrictions" - TERM_PUBLIC_DISCLOSURE = "Public Disclosure" - TERM_DIPLOMATIC_ACTIONS = "Diplomatic Actions" TERM_POLICY_ACTIONS = "Policy Actions" - TERM_OTHER = "Other" + TERM_PUBLIC_DISCLOSURE = "Public Disclosure" + TERM_REBUILDING = "Rebuilding" + TERM_REDIRECTION = "Redirection" + TERM_REDIRECTION_HONEY_POT = "Redirection (Honey Pot)" + TERM_TRAINING = "Training" @register_vocab -class SecurityCompromise(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:SecurityCompromiseVocab-1.0' +class SecurityCompromise_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:SecurityCompromiseVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_YES = "Yes" - TERM_SUSPECTED = "Suspected" TERM_NO = "No" + TERM_SUSPECTED = "Suspected" TERM_UNKNOWN = "Unknown" + TERM_YES = "Yes" @register_vocab -class ImpactRating(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:ImpactRatingVocab-1.0' +class ImpactRating_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:ImpactRatingVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_NONE = "None" + TERM_MAJOR = "Major" TERM_MINOR = "Minor" TERM_MODERATE = "Moderate" - TERM_MAJOR = "Major" + TERM_NONE = "None" TERM_UNKNOWN = "Unknown" @register_vocab -class AssetType(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:AssetTypeVocab-1.0' +class AssetType_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:AssetTypeVocab-1.0" + _VOCAB_VERSION = "1.0" + - TERM_BACKUP = "Backup" - TERM_DATABASE = "Database" - TERM_DHCP = "DHCP" - TERM_DIRECTORY = "Directory" - TERM_DCS = "DCS" - TERM_DNS = "DNS" - TERM_FILE = "File" - TERM_LOG = "Log" - TERM_MAIL = "Mail" - TERM_MAINFRAME = "Mainframe" - TERM_PAYMENT_SWITCH = "Payment switch" - TERM_POS_CONTROLLER = "POS controller" - TERM_PRINT = "Print" - TERM_PROXY = "Proxy" - TERM_REMOTE_ACCESS = "Remote access" - TERM_SCADA = "SCADA" - TERM_WEB_APPLICATION = "Web application" - TERM_SERVER = "Server" TERM_ACCESS_READER = "Access reader" - TERM_CAMERA = "Camera" - TERM_FIREWALL = "Firewall" - TERM_HSM = "HSM" - TERM_IDS = "IDS" - TERM_BROADBAND = "Broadband" - TERM_PBX = "PBX" - TERM_PRIVATE_WAN = "Private WAN" - TERM_PLC = "PLC" - TERM_PUBLIC_WAN = "Public WAN" - TERM_RTU = "RTU" - TERM_ROUTER_OR_SWITCH = "Router or switch" - TERM_SAN = "SAN" - TERM_TELEPHONE = "Telephone" - TERM_VOIP_ADAPTER = "VoIP adapter" - TERM_LAN = "LAN" - TERM_WLAN = "WLAN" - TERM_NETWORK = "Network" - TERM_AUTH_TOKEN = "Auth token" - TERM_ATM = "ATM" - TERM_DESKTOP = "Desktop" - TERM_PED_PAD = "PED pad" - TERM_GAS_TERMINAL = "Gas terminal" - TERM_LAPTOP = "Laptop" - TERM_MEDIA = "Media" - TERM_MOBILE_PHONE = "Mobile phone" - TERM_PERIPHERAL = "Peripheral" - TERM_POS_TERMINAL = "POS terminal" - TERM_KIOSK = "Kiosk" - TERM_TABLET = "Tablet" - TERM_VOIP_PHONE = "VoIP phone" - TERM_USER_DEVICE = "User Device" - TERM_TAPES = "Tapes" - TERM_DISK_MEDIA = "Disk media" - TERM_DOCUMENTS = "Documents" - TERM_FLASH_DRIVE = "Flash drive" - TERM_DISK_DRIVE = "Disk drive" - TERM_SMART_CARD = "Smart card" - TERM_PAYMENT_CARD = "Payment card" TERM_ADMINISTRATOR = "Administrator" + TERM_ATM = "ATM" TERM_AUDITOR = "Auditor" + TERM_AUTH_TOKEN = "Auth token" + TERM_BACKUP = "Backup" + TERM_BROADBAND = "Broadband" TERM_CALL_CENTER = "Call center" + TERM_CAMERA = "Camera" TERM_CASHIER = "Cashier" TERM_CUSTOMER = "Customer" + TERM_DATABASE = "Database" + TERM_DCS = "DCS" + TERM_DESKTOP = "Desktop" TERM_DEVELOPER = "Developer" + TERM_DHCP = "DHCP" + TERM_DIRECTORY = "Directory" + TERM_DISK_DRIVE = "Disk drive" + TERM_DISK_MEDIA = "Disk media" + TERM_DNS = "DNS" + TERM_DOCUMENTS = "Documents" TERM_ENDUSER = "End-user" TERM_EXECUTIVE = "Executive" + TERM_FILE = "File" TERM_FINANCE = "Finance" + TERM_FIREWALL = "Firewall" + TERM_FLASH_DRIVE = "Flash drive" TERM_FORMER_EMPLOYEE = "Former employee" + TERM_GAS_TERMINAL = "Gas terminal" TERM_GUARD = "Guard" TERM_HELPDESK = "Helpdesk" + TERM_HSM = "HSM" TERM_HUMAN_RESOURCES = "Human resources" + TERM_IDS = "IDS" + TERM_KIOSK = "Kiosk" + TERM_LAN = "LAN" + TERM_LAPTOP = "Laptop" + TERM_LOG = "Log" + TERM_MAIL = "Mail" + TERM_MAINFRAME = "Mainframe" TERM_MAINTENANCE = "Maintenance" TERM_MANAGER = "Manager" + TERM_MEDIA = "Media" + TERM_MOBILE_PHONE = "Mobile phone" + TERM_NETWORK = "Network" TERM_PARTNER = "Partner" + TERM_PAYMENT_CARD = "Payment card" + TERM_PAYMENT_SWITCH = "Payment switch" + TERM_PBX = "PBX" + TERM_PED_PAD = "PED pad" + TERM_PERIPHERAL = "Peripheral" TERM_PERSON = "Person" + TERM_PLC = "PLC" + TERM_POS_CONTROLLER = "POS controller" + TERM_POS_TERMINAL = "POS terminal" + TERM_PRINT = "Print" + TERM_PRIVATE_WAN = "Private WAN" + TERM_PROXY = "Proxy" + TERM_PUBLIC_WAN = "Public WAN" + TERM_REMOTE_ACCESS = "Remote access" + TERM_ROUTER_OR_SWITCH = "Router or switch" + TERM_RTU = "RTU" + TERM_SAN = "SAN" + TERM_SCADA = "SCADA" + TERM_SERVER = "Server" + TERM_SMART_CARD = "Smart card" + TERM_TABLET = "Tablet" + TERM_TAPES = "Tapes" + TERM_TELEPHONE = "Telephone" TERM_UNKNOWN = "Unknown" - + TERM_USER_DEVICE = "User Device" + TERM_VOIP_ADAPTER = "VoIP adapter" + TERM_VOIP_PHONE = "VoIP phone" + TERM_WEB_APPLICATION = "Web application" + TERM_WLAN = "WLAN" @register_vocab -class COAStage(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:COAStageVocab-1.0' +class COAStage_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:COAStageVocab-1.0" + _VOCAB_VERSION = "1.0" TERM_REMEDY = "Remedy" TERM_RESPONSE = "Response" @register_vocab -class LocationClass(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:LocationClassVocab-1.0' +class LocationClass_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:LocationClassVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_INTERNALLYLOCATED = "Internally-Located" - TERM_EXTERNALLYLOCATED = "Externally-Located" TERM_COLOCATED = "Co-Located" + TERM_EXTERNALLYLOCATED = "Externally-Located" + TERM_INTERNALLYLOCATED = "Internally-Located" TERM_MOBILE = "Mobile" TERM_UNKNOWN = "Unknown" @register_vocab -class InformationType(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:InformationTypeVocab-1.0' +class InformationType_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:InformationTypeVocab-1.0" + _VOCAB_VERSION = "1.0" + TERM_AUTHENTICATION_COOKIES = "Authentication Cookies" TERM_INFORMATION_ASSETS = "Information Assets" TERM_INFORMATION_ASSETS_CORPORATE_EMPLOYEE_INFORMATION = "Information Assets - Corporate Employee Information" TERM_INFORMATION_ASSETS_CUSTOMER_PII = "Information Assets - Customer PII" @@ -627,60 +762,58 @@ class InformationType(VocabString): TERM_INFORMATION_ASSETS_INTELLECTUAL_PROPERTY = "Information Assets - Intellectual Property" TERM_INFORMATION_ASSETS_MOBILE_PHONE_CONTACTS = "Information Assets - Mobile Phone Contacts" TERM_INFORMATION_ASSETS_USER_CREDENTIALS = "Information Assets - User Credentials" - TERM_AUTHENTICATION_COOKIES = "Authentication Cookies" @register_vocab -class ThreatActorSophistication(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:ThreatActorSophisticationVocab-1.0' +class ThreatActorSophistication_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:ThreatActorSophisticationVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_INNOVATOR = "Innovator" + TERM_ASPIRANT = "Aspirant" TERM_EXPERT = "Expert" - TERM_PRACTITIONER = "Practitioner" + TERM_INNOVATOR = "Innovator" TERM_NOVICE = "Novice" - TERM_ASPIRANT = "Aspirant" + TERM_PRACTITIONER = "Practitioner" @register_vocab -class HighMediumLow(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:HighMediumLowVocab-1.0' +class HighMediumLow_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:HighMediumLowVocab-1.0" + _VOCAB_VERSION = "1.0" TERM_HIGH = "High" - TERM_MEDIUM = "Medium" TERM_LOW = "Low" + TERM_MEDIUM = "Medium" TERM_NONE = "None" TERM_UNKNOWN = "Unknown" @register_vocab -class LossProperty(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:LossPropertyVocab-1.0' +class LossProperty_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:LossPropertyVocab-1.0" + _VOCAB_VERSION = "1.0" + TERM_ACCOUNTABILITY = "Accountability" + TERM_AVAILABILITY = "Availability" TERM_CONFIDENTIALITY = "Confidentiality" TERM_INTEGRITY = "Integrity" - TERM_AVAILABILITY = "Availability" - TERM_ACCOUNTABILITY = "Accountability" TERM_NONREPUDIATION = "Non-Repudiation" @register_vocab -class IntendedEffect(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:IntendedEffectVocab-1.0' +class IntendedEffect_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:IntendedEffectVocab-1.0" + _VOCAB_VERSION = "1.0" + TERM_ACCOUNT_TAKEOVER = "Account Takeover" TERM_ADVANTAGE = "Advantage" TERM_ADVANTAGE_ECONOMIC = "Advantage - Economic" TERM_ADVANTAGE_MILITARY = "Advantage - Military" TERM_ADVANTAGE_POLITICAL = "Advantage - Political" - TERM_THEFT = "Theft" - TERM_THEFT_INTELLECTUAL_PROPERTY = "Theft - Intellectual Property" - TERM_THEFT_CREDENTIAL_THEFT = "Theft - Credential Theft" - TERM_THEFT_IDENTITY_THEFT = "Theft - Identity Theft" - TERM_THEFT_THEFT_OF_PROPRIETARY_INFORMATION = "Theft - Theft of Proprietary Information" - TERM_ACCOUNT_TAKEOVER = "Account Takeover" TERM_BRAND_DAMAGE = "Brand Damage" TERM_COMPETITIVE_ADVANTAGE = "Competitive Advantage" TERM_DEGRADATION_OF_SERVICE = "Degradation of Service" @@ -693,76 +826,113 @@ class IntendedEffect(VocabString): TERM_FRAUD = "Fraud" TERM_HARASSMENT = "Harassment" TERM_ICS_CONTROL = "ICS Control" + TERM_THEFT = "Theft" + TERM_THEFT_CREDENTIAL_THEFT = "Theft - Credential Theft" + TERM_THEFT_IDENTITY_THEFT = "Theft - Identity Theft" + TERM_THEFT_INTELLECTUAL_PROPERTY = "Theft - Intellectual Property" + TERM_THEFT_THEFT_OF_PROPRIETARY_INFORMATION = "Theft - Theft of Proprietary Information" TERM_TRAFFIC_DIVERSION = "Traffic Diversion" TERM_UNAUTHORIZED_ACCESS = "Unauthorized Access" @register_vocab -class PackageIntent(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:PackageIntentVocab-1.0' +class PackageIntent_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:PackageIntentVocab-1.0" + _VOCAB_VERSION = "1.0" + TERM_ATTACK_PATTERN_CHARACTERIZATION = "Attack Pattern Characterization" + TERM_CAMPAIGN_CHARACTERIZATION = "Campaign Characterization" TERM_COLLECTIVE_THREAT_INTELLIGENCE = "Collective Threat Intelligence" - TERM_THREAT_REPORT = "Threat Report" + TERM_COURSES_OF_ACTION = "Courses of Action" + TERM_EXPLOIT_CHARACTERIZATION = "Exploit Characterization" + TERM_INCIDENT = "Incident" TERM_INDICATORS = "Indicators" - TERM_INDICATORS_PHISHING = "Indicators - Phishing" - TERM_INDICATORS_WATCHLIST = "Indicators - Watchlist" + TERM_INDICATORS_ENDPOINT_CHARACTERISTICS = "Indicators - Endpoint Characteristics" TERM_INDICATORS_MALWARE_ARTIFACTS = "Indicators - Malware Artifacts" TERM_INDICATORS_NETWORK_ACTIVITY = "Indicators - Network Activity" - TERM_INDICATORS_ENDPOINT_CHARACTERISTICS = "Indicators - Endpoint Characteristics" - TERM_CAMPAIGN_CHARACTERIZATION = "Campaign Characterization" - TERM_THREAT_ACTOR_CHARACTERIZATION = "Threat Actor Characterization" - TERM_EXPLOIT_CHARACTERIZATION = "Exploit Characterization" - TERM_ATTACK_PATTERN_CHARACTERIZATION = "Attack Pattern Characterization" + TERM_INDICATORS_PHISHING = "Indicators - Phishing" + TERM_INDICATORS_WATCHLIST = "Indicators - Watchlist" TERM_MALWARE_CHARACTERIZATION = "Malware Characterization" + TERM_MALWARE_SAMPLES = "Malware Samples" + TERM_OBSERVATIONS = "Observations" + TERM_OBSERVATIONS_EMAIL = "Observations - Email" + TERM_THREAT_ACTOR_CHARACTERIZATION = "Threat Actor Characterization" + TERM_THREAT_REPORT = "Threat Report" TERM_TTP_INFRASTRUCTURE = "TTP - Infrastructure" TERM_TTP_TOOLS = "TTP - Tools" + + +@register_vocab +class ReportIntent_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:ReportIntentVocab-1.0" + _VOCAB_VERSION = "1.0" + + TERM_ATTACK_PATTERN_CHARACTERIZATION = "Attack Pattern Characterization" + TERM_CAMPAIGN_CHARACTERIZATION = "Campaign Characterization" + TERM_COLLECTIVE_THREAT_INTELLIGENCE = "Collective Threat Intelligence" TERM_COURSES_OF_ACTION = "Courses of Action" + TERM_EXPLOIT_CHARACTERIZATION = "Exploit Characterization" TERM_INCIDENT = "Incident" + TERM_INDICATORS = "Indicators" + TERM_INDICATORS_ENDPOINT_CHARACTERISTICS = "Indicators - Endpoint Characteristics" + TERM_INDICATORS_MALWARE_ARTIFACTS = "Indicators - Malware Artifacts" + TERM_INDICATORS_NETWORK_ACTIVITY = "Indicators - Network Activity" + TERM_INDICATORS_PHISHING = "Indicators - Phishing" + TERM_INDICATORS_WATCHLIST = "Indicators - Watchlist" + TERM_MALWARE_CHARACTERIZATION = "Malware Characterization" + TERM_MALWARE_SAMPLES = "Malware Samples" TERM_OBSERVATIONS = "Observations" TERM_OBSERVATIONS_EMAIL = "Observations - Email" - TERM_MALWARE_SAMPLES = "Malware Samples" + TERM_THREAT_ACTOR_CHARACTERIZATION = "Threat Actor Characterization" + TERM_THREAT_REPORT = "Threat Report" + TERM_TTP_INFRASTRUCTURE = "TTP - Infrastructure" + TERM_TTP_TOOLS = "TTP - Tools" @register_vocab -class LossDuration(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:LossDurationVocab-1.0' +class LossDuration_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:LossDurationVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_PERMANENT = "Permanent" - TERM_WEEKS = "Weeks" TERM_DAYS = "Days" TERM_HOURS = "Hours" TERM_MINUTES = "Minutes" + TERM_PERMANENT = "Permanent" TERM_SECONDS = "Seconds" TERM_UNKNOWN = "Unknown" + TERM_WEEKS = "Weeks" @register_vocab -class OwnershipClass(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:OwnershipClassVocab-1.0' +class OwnershipClass_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:OwnershipClassVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_INTERNALLYOWNED = "Internally-Owned" + TERM_CUSTOMEROWNED = "Customer-Owned" TERM_EMPLOYEEOWNED = "Employee-Owned" + TERM_INTERNALLYOWNED = "Internally-Owned" TERM_PARTNEROWNED = "Partner-Owned" - TERM_CUSTOMEROWNED = "Customer-Owned" TERM_UNKNOWN = "Unknown" @register_vocab -class MalwareType(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:MalwareTypeVocab-1.0' +class MalwareType_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:MalwareTypeVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_AUTOMATED_TRANSFER_SCRIPTS = "Automated Transfer Scripts" TERM_ADWARE = "Adware" - TERM_DIALER = "Dialer" + TERM_AUTOMATED_TRANSFER_SCRIPTS = "Automated Transfer Scripts" TERM_BOT = "Bot" TERM_BOT_CREDENTIAL_THEFT = "Bot - Credential Theft" TERM_BOT_DDOS = "Bot - DDoS" TERM_BOT_LOADER = "Bot - Loader" TERM_BOT_SPAM = "Bot - Spam" + TERM_DIALER = "Dialer" TERM_DOS_OR_DDOS = "DoS / DDoS" TERM_DOS_OR_DDOS_PARTICIPATORY = "DoS / DDoS - Participatory" TERM_DOS_OR_DDOS_SCRIPT = "DoS / DDoS - Script" @@ -776,20 +946,21 @@ class MalwareType(VocabString): @register_vocab -class IncidentEffect(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:IncidentEffectVocab-1.0' +class IncidentEffect_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:IncidentEffectVocab-1.0" + _VOCAB_VERSION = "1.0" TERM_BRAND_OR_IMAGE_DEGRADATION = "Brand or Image Degradation" - TERM_LOSS_OF_COMPETITIVE_ADVANTAGE = "Loss of Competitive Advantage" - TERM_LOSS_OF_COMPETITIVE_ADVANTAGE_ECONOMIC = "Loss of Competitive Advantage - Economic" - TERM_LOSS_OF_COMPETITIVE_ADVANTAGE_MILITARY = "Loss of Competitive Advantage - Military" - TERM_LOSS_OF_COMPETITIVE_ADVANTAGE_POLITICAL = "Loss of Competitive Advantage - Political" TERM_DATA_BREACH_OR_COMPROMISE = "Data Breach or Compromise" TERM_DEGRADATION_OF_SERVICE = "Degradation of Service" TERM_DESTRUCTION = "Destruction" TERM_DISRUPTION_OF_SERVICE_OR_OPERATIONS = "Disruption of Service / Operations" TERM_FINANCIAL_LOSS = "Financial Loss" + TERM_LOSS_OF_COMPETITIVE_ADVANTAGE = "Loss of Competitive Advantage" + TERM_LOSS_OF_COMPETITIVE_ADVANTAGE_ECONOMIC = "Loss of Competitive Advantage - Economic" + TERM_LOSS_OF_COMPETITIVE_ADVANTAGE_MILITARY = "Loss of Competitive Advantage - Military" + TERM_LOSS_OF_COMPETITIVE_ADVANTAGE_POLITICAL = "Loss of Competitive Advantage - Political" TERM_LOSS_OF_CONFIDENTIAL_OR_PROPRIETARY_INFORMATION_OR_INTELLECTUAL_PROPERTY = "Loss of Confidential / Proprietary Information or Intellectual Property" TERM_REGULATORY_COMPLIANCE_OR_LEGAL_IMPACT = "Regulatory, Compliance or Legal Impact" TERM_UNINTENDED_ACCESS = "Unintended Access" @@ -797,12 +968,47 @@ class IncidentEffect(VocabString): @register_vocab -class InformationSourceRole(VocabString): - _namespace = 'http://stix.mitre.org/default_vocabularies-1' - _XSI_TYPE = 'stixVocabs:InformationSourceRoleVocab-1.0' +class InformationSourceRole_1_0(VocabString): + _namespace = "http://stix.mitre.org/default_vocabularies-1" + _XSI_TYPE = "stixVocabs:InformationSourceRoleVocab-1.0" + _VOCAB_VERSION = "1.0" - TERM_INITIAL_AUTHOR = "Initial Author" - TERM_CONTENT_ENHANCERORREFINER = "Content Enhancer/Refiner" TERM_AGGREGATOR = "Aggregator" + TERM_CONTENT_ENHANCERORREFINER = "Content Enhancer/Refiner" + TERM_INITIAL_AUTHOR = "Initial Author" TERM_TRANSFORMERORTRANSLATOR = "Transformer/Translator" +# Vocab aliases which resolve to the most recent version of the +# associated VocabString. +AssetType = AssetType_1_0 +AttackerInfrastructureType = AttackerInfrastructureType_1_0 +AttackerToolType = AttackerToolType_1_0 +AvailabilityLossType = AvailabilityLossType_1_1_1 +CampaignStatus = CampaignStatus_1_0 +COAStage = COAStage_1_0 +CourseOfActionType = CourseOfActionType_1_0 +DiscoveryMethod = DiscoveryMethod_2_0 +HighMediumLow = HighMediumLow_1_0 +ImpactQualification = ImpactQualification_1_0 +ImpactRating = ImpactRating_1_0 +IncidentCategory = IncidentCategory_1_0 +IncidentEffect = IncidentEffect_1_0 +IncidentStatus = IncidentStatus_1_0 +IndicatorType = IndicatorType_1_1 +InformationSourceRole = InformationSourceRole_1_0 +InformationType = InformationType_1_0 +IntendedEffect = IntendedEffect_1_0 +LocationClass = LocationClass_1_0 +LossDuration = LossDuration_1_0 +LossProperty = LossProperty_1_0 +MalwareType = MalwareType_1_0 +ManagementClass = ManagementClass_1_0 +Motivation = Motivation_1_1 +OwnershipClass = OwnershipClass_1_0 +PackageIntent = PackageIntent_1_0 +PlanningAndOperationalSupport = PlanningAndOperationalSupport_1_0_1 +ReportIntent = ReportIntent_1_0 +SecurityCompromise = SecurityCompromise_1_0 +SystemType = SystemType_1_0 +ThreatActorSophistication = ThreatActorSophistication_1_0 +ThreatActorType = ThreatActorType_1_0 diff --git a/stix/core/__init__.py b/stix/core/__init__.py index f744e7d1..a4817895 100644 --- a/stix/core/__init__.py +++ b/stix/core/__init__.py @@ -1,5 +1,100 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -from stix_package import STIXPackage # noqa -from stix_header import STIXHeader # noqa +# stdlib +from functools import partial + +# mixbox +from mixbox import fields + +# base import +import stix + +# component imports +from stix.campaign import Campaign +from stix.coa import CourseOfAction +from stix.exploit_target import ExploitTarget +from stix.indicator import Indicator +from stix.incident import Incident +from stix.threat_actor import ThreatActor + +# binding imports +from stix.bindings import stix_core as stix_core_binding +from stix.bindings import stix_common as stix_common_binding + + +class Campaigns(stix.EntityList): + _binding = stix_core_binding + _namespace = 'http://stix.mitre.org/stix-1' + _binding_class = _binding.CampaignsType + + campaign = fields.TypedField( + name="Campaign", + type_=Campaign, + multiple=True + ) + + +class CoursesOfAction(stix.EntityList): + _binding = stix_core_binding + _namespace = 'http://stix.mitre.org/stix-1' + _binding_class = _binding.CoursesOfActionType + + course_of_action = fields.TypedField( + name="Course_Of_Action", + type_=CourseOfAction, + multiple=True + ) + + +class ExploitTargets(stix.EntityList): + _binding = stix_common_binding + _namespace = 'http://stix.mitre.org/common-1' + _binding_class = _binding.ExploitTargetsType + + exploit_target = fields.TypedField( + name="Exploit_Target", + type_=ExploitTarget, + multiple=True + ) + + +class Incidents(stix.EntityList): + _binding = stix_core_binding + _namespace = 'http://stix.mitre.org/stix-1' + _binding_class = _binding.IncidentsType + + incident = fields.TypedField( + name="Incident", + type_=Incident, + multiple=True + ) + + +class Indicators(stix.EntityList): + _binding = stix_core_binding + _namespace = 'http://stix.mitre.org/stix-1' + _binding_class = _binding.IndicatorsType + + indicator = fields.TypedField( + name="Indicator", + type_=Indicator, + multiple=True + ) + + +class ThreatActors(stix.EntityList): + _binding = stix_core_binding + _namespace = 'http://stix.mitre.org/stix-1' + _binding_class = _binding.ThreatActorsType + + threat_actor = fields.TypedField( + name="Threat_Actor", + type_=ThreatActor, + multiple=True + ) + + +# Namespace flattening +from .stix_package import STIXPackage # noqa +from .stix_header import STIXHeader # noqa diff --git a/stix/core/stix_header.py b/stix/core/stix_header.py index 37afdd5e..90c58f9b 100644 --- a/stix/core/stix_header.py +++ b/stix/core/stix_header.py @@ -1,168 +1,67 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix -import stix.bindings.stix_common as stix_common_binding -import stix.bindings.stix_core as stix_core_binding -from stix.common import vocabs, InformationSource, StructuredText, VocabString +from stix.common import InformationSource, StructuredText, Profiles +from stix.common.vocabs import VocabField, PackageIntent from stix.data_marking import Marking +import stix.bindings.stix_core as stix_core_binding class STIXHeader(stix.Entity): + """The STIX Package Header. + + Args: + handling: The data marking section of the Header. + information_source: The :class:`.InformationSource` section of the + Header. + package_intents: A collection of :class:`.VocabString` + defining the intent of the parent :class:`.STIXPackage`. + description: A description of the intent or purpose + of the parent :class:`.STIXPackage`. + short_description: A short description of the intent + or purpose of the parent :class:`.STIXPackage`. + title: The title of the :class:`.STIXPackage`. + + Attributes: + profiles: A collection of STIX Profiles the parent + :class:`.STIXPackage` conforms to. + title: The title of the parent :class:`.STIXPackage`. + + """ _binding = stix_core_binding + _binding_class = _binding.STIXHeaderType _namespace = 'http://stix.mitre.org/stix-1' + title = fields.TypedField("Title") + package_intents = VocabField("Package_Intent", PackageIntent, multiple=True) + description = fields.TypedField("Description", StructuredText) + short_description = fields.TypedField("Short_Description", StructuredText) + handling = fields.TypedField("Handling", Marking) + information_source = fields.TypedField("Information_Source", InformationSource) + profiles = fields.TypedField("Profiles", Profiles) + def __init__(self, package_intents=None, description=None, handling=None, information_source=None, title=None, short_description=None): + super(STIXHeader, self).__init__() + self.package_intents = package_intents self.title = title self.description = description self.short_description = short_description self.handling = handling self.information_source = information_source - self.profiles = [] - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - """Sets the value of the description property. - - If the value is an instance of basestring, it will be coerced into an - instance of StructuredText, with its 'text' property set to the input - value. - - """ - self._set_var(StructuredText, description=value) - - @property - def short_description(self): - """The ``short_description`` property for this entity. - - Default Value: ``None`` - - Note: - If set to a value that is not an instance of - :class:`stix.common.structured_text.StructuredText`, an attempt to - will be made to convert the value into an instance of - :class:`stix.common.structured_text.StructuredText`. - - Returns: - An instance of - :class:`stix.common.structured_text.StructuredText` - - """ - return self._short_description - - @short_description.setter - def short_description(self, value): - self._set_var(StructuredText, short_description=value) - - @property - def handling(self): - return self._handling - - @handling.setter - def handling(self, value): - self._set_var(Marking, try_cast=False, handling=value) - - @property - def package_intents(self): - return self._package_intents - - @package_intents.setter - def package_intents(self, value): - self._package_intents = _PackageIntents(value) + self.profiles = None def add_package_intent(self, package_intent): self.package_intents.append(package_intent) - @property - def information_source(self): - return self._information_source - - @information_source.setter - def information_source(self, value): - self._set_var(InformationSource, try_cast=False, information_source=value) - def add_profile(self, profile): """Adds a profile to the STIX Header. A Profile is represented by a string URI. """ self.profiles.append(profile) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.title = obj.Title - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.short_description = StructuredText.from_obj(obj.Short_Description) - return_obj.handling = Marking.from_obj(obj.Handling) - return_obj.information_source = InformationSource.from_obj(obj.Information_Source) - return_obj.package_intents = _PackageIntents.from_obj(obj.Package_Intent) - return_obj.profiles = obj.Profiles.Profile if obj.Profiles else [] - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - super(STIXHeader, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding.STIXHeaderType() - - if self.title: - return_obj.Title = self.title - if self.package_intents: - return_obj.Package_Intent = self.package_intents.to_obj(ns_info=ns_info) - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.short_description: - return_obj.Short_Description = self.short_description.to_obj(ns_info=ns_info) - if self.handling: - return_obj.Handling = self.handling.to_obj(ns_info=ns_info) - if self.information_source: - return_obj.Information_Source = self.information_source.to_obj(ns_info=ns_info) - if self.profiles: - return_obj.Profiles = stix_common_binding.ProfilesType(Profile=self.profiles) - - return return_obj - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - get = dict_repr.get - - return_obj.title = get('title') - return_obj.package_intents = _PackageIntents.from_list(get('package_intents')) - return_obj.description = StructuredText.from_dict(get('description')) - return_obj.short_description = StructuredText.from_dict(get('short_description')) - return_obj.handling = Marking.from_dict(get('handling')) - return_obj.information_source = InformationSource.from_dict(get('information_source')) - return_obj.profiles = get('profiles') - - return return_obj - - def to_dict(self): - return super(STIXHeader, self).to_dict() - - -# NOT AN ACTUAL STIX TYPE! -class _PackageIntents(stix.TypedList): - _contained_type = VocabString - - def _fix_value(self, value): - return vocabs.PackageIntent(value) diff --git a/stix/core/stix_package.py b/stix/core/stix_package.py index 64c1842e..40d4b5ca 100644 --- a/stix/core/stix_package.py +++ b/stix/core/stix_package.py @@ -1,195 +1,189 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -# external +# mixbox +from mixbox import idgen +from mixbox import fields + +# cybox from cybox.core import Observable, Observables -# internal +# base import stix -import stix.utils as utils -import stix.utils.parser as parser - -from stix.campaign import Campaign -from stix.coa import CourseOfAction -from stix.exploit_target import ExploitTarget -from stix.indicator import Indicator -from stix.incident import Incident -from stix.threat_actor import ThreatActor -from stix.ttp import TTP -from stix.common.related import RelatedPackages + +# utility imports +from .. import utils +from ..utils import parser +# component imports +from ..campaign import Campaign +from ..coa import CourseOfAction +from ..exploit_target import ExploitTarget +from ..indicator import Indicator +from ..incident import Incident +from ..threat_actor import ThreatActor +from ..ttp import TTP + +# relationship imports +from ..common.related import RelatedPackages # relative imports from .stix_header import STIXHeader from .ttps import TTPs +from . import (Campaigns, CoursesOfAction, ExploitTargets, Incidents, + Indicators, ThreatActors) # binding imports -import stix.bindings.stix_common as stix_common_binding import stix.bindings.stix_core as stix_core_binding +import mixbox.entities class STIXPackage(stix.Entity): + """A STIX Package object. + + Args: + id_ (optional): An identifier. If ``None``, a value will be generated + via ``mixbox.idgen.create_id()``. If set, this will unset the + ``idref`` property. + idref: An identifier reference. If set this will unset + the ``id_`` property. + timestamp: A timestamp value. Can be an instance of + ``datetime.datetime`` or ``str``. + stix_header: A Report :class:`.Header` object. + campaigns: A collection of :class:`.Campaign` objects. + courses_of_action: A collection of :class:`.CourseOfAction` objects. + exploit_targets: A collection of :class:`.ExploitTarget` objects. + incidents: A collection of :class:`.Incident` objects. + indicators: A collection of :class:`.Indicator` objects. + threat_actors: A collection of :class:`.ThreatActor` objects. + ttps: A collection of :class:`.TTP` objects. + related_packages: A collection of + :class:`.RelatedPackage` objects. + + """ _binding = stix_core_binding _binding_class = _binding.STIXType _namespace = 'http://stix.mitre.org/stix-1' _version = "1.1.1" + _ALL_VERSIONS = ("1.0", "1.0.1", "1.1", "1.1.1") + + id_ = fields.IdField("id") + idref = fields.IdrefField("idref") + version = fields.TypedField("version") + timestamp = fields.DateTimeField("timestamp") + stix_header = fields.TypedField("STIX_Header", STIXHeader) + campaigns = fields.TypedField("Campaigns", Campaigns) + courses_of_action = fields.TypedField("Courses_Of_Action", CoursesOfAction) + exploit_targets = fields.TypedField("Exploit_Targets", ExploitTargets) + observables = fields.TypedField("Observables", Observables) + indicators = fields.TypedField("Indicators", Indicators) + incidents = fields.TypedField("Incidents", Incidents) + threat_actors = fields.TypedField("Threat_Actors", ThreatActors) + ttps = fields.TypedField("TTPs", TTPs) + related_packages = fields.TypedField("Related_Packages", RelatedPackages) def __init__(self, id_=None, idref=None, timestamp=None, stix_header=None, courses_of_action=None, exploit_targets=None, indicators=None, observables=None, incidents=None, threat_actors=None, - ttps=None, campaigns=None): + ttps=None, campaigns=None, related_packages=None): - self.id_ = id_ or stix.utils.create_id("Package") + super(STIXPackage, self).__init__() + + self.id_ = id_ or idgen.create_id("Package") self.idref = idref - self.version = self._version + self.version = STIXPackage._version self.stix_header = stix_header - self.campaigns = campaigns - self.courses_of_action = courses_of_action - self.exploit_targets = exploit_targets - self.observables = observables - self.indicators = indicators - self.incidents = incidents - self.threat_actors = threat_actors + self.campaigns = campaigns or Campaigns() + self.courses_of_action = courses_of_action or CoursesOfAction() + self.exploit_targets = exploit_targets or ExploitTargets() + self.observables = observables or Observables() + self.indicators = indicators or Indicators() + self.incidents = incidents or Incidents() + self.threat_actors = threat_actors or ThreatActors() self.ttps = ttps - self.related_packages = RelatedPackages() - - if timestamp: - self.timestamp = timestamp - else: - self.timestamp = utils.dates.now() if not idref else None - - @property - def id_(self): - return self._id - - @id_.setter - def id_(self, value): - if not value: - self._id = None - else: - self._id = value - self.idref = None - - @property - def idref(self): - return self._idref - - @idref.setter - def idref(self, value): - if not value: - self._idref = None - else: - self._idref = value - self.id_ = None # unset id_ if idref is present - - @property - def timestamp(self): - return self._timestamp - - @timestamp.setter - def timestamp(self, value): - self._timestamp = utils.dates.parse_value(value) - - @property - def stix_header(self): - return self._stix_header - - @stix_header.setter - def stix_header(self, value): - self._set_var(STIXHeader, try_cast=False, stix_header=value) - - @property - def indicators(self): - return self._indicators - - @indicators.setter - def indicators(self, value): - self._indicators = Indicators(value) + self.related_packages = related_packages + self.timestamp = timestamp def add_indicator(self, indicator): - self.indicators.append(indicator) - - @property - def campaigns(self): - return self._campaigns + """Adds an :class:`.Indicator` object to the :attr:`indicators` + collection. - @campaigns.setter - def campaigns(self, value): - self._campaigns = Campaigns(value) + """ + if self.indicators is None: + self.indicators = Indicators() + self.indicators.append(indicator) def add_campaign(self, campaign): + """Adds a :class:`Campaign` object to the :attr:`campaigns` collection. + + """ + if self.campaigns is None: + self.campaigns = Campaigns() self.campaigns.append(campaign) - @property - def observables(self): - return self._observables + def add_observable(self, observable): + """Adds an ``Observable`` object to the :attr:`observables` collection. - @observables.setter - def observables(self, value): - self._set_var(Observables, observables=value) + If `observable` is not an ``Observable`` instance, an effort will be + made to convert it to one. - def add_observable(self, observable): - if not self.observables: + """ + if self.observables is None: self.observables = Observables(observables=observable) else: self.observables.add(observable) - @property - def incidents(self): - return self._incidents - - @incidents.setter - def incidents(self, value): - self._incidents = Incidents(value) - def add_incident(self, incident): - self.incidents.append(incident) + """Adds an :class:`.Incident` object to the :attr:`incidents` + collection. - @property - def threat_actors(self): - return self._threat_actors - - @threat_actors.setter - def threat_actors(self, value): - self._threat_actors = ThreatActors(value) + """ + if self.incidents is None: + self.incidents = Incidents() + self.incidents.append(incident) def add_threat_actor(self, threat_actor): - self._threat_actors.append(threat_actor) + """Adds an :class:`.ThreatActor` object to the :attr:`threat_actors` + collection. - @property - def courses_of_action(self): - return self._courses_of_action - - @courses_of_action.setter - def courses_of_action(self, value): - self._courses_of_action = CoursesOfAction(value) + """ + if self.threat_actors is None: + self.threat_actors = ThreatActors() + self.threat_actors.append(threat_actor) def add_course_of_action(self, course_of_action): - self._courses_of_action.append(course_of_action) + """Adds an :class:`.CourseOfAction` object to the + :attr:`courses_of_action` collection. - @property - def exploit_targets(self): - return self._exploit_targets - - @exploit_targets.setter - def exploit_targets(self, value): - self._exploit_targets = ExploitTargets(value) + """ + if self.courses_of_action is None: + self.courses_of_action = CoursesOfAction() + self.courses_of_action.append(course_of_action) def add_exploit_target(self, exploit_target): - self._exploit_targets.append(exploit_target) - - @property - def ttps(self): - return self._ttps - - @ttps.setter - def ttps(self, value): - if isinstance(value, TTPs): - self._ttps = value - else: - self._ttps = TTPs(value) - + """Adds an :class:`.ExploitTarget` object to the + :attr:`exploit_targets` collection. + + """ + if self.exploit_targets is None: + self.exploit_targets = ExploitTargets() + self.exploit_targets.append(exploit_target) + def add_ttp(self, ttp): - self.ttps.append(ttp) + """Adds an :class:`.TTP` object to the :attr:`ttps` collection. + + """ + if self.ttps is None: + self.ttps = TTPs() + self.ttps.ttp.append(ttp) + + def add_related_package(self, related_package): + """Adds a :class:`.RelatedPackage` object to the + :attr:`related_packages` collection. + + """ + if self.related_packages is None: + self.related_packages = RelatedPackages() + self.related_packages.append(related_package) def add(self, entity): """Adds `entity` to a top-level collection. For example, if `entity` is @@ -208,7 +202,7 @@ def add(self, entity): Incident: self.add_incident, Indicator: self.add_indicator, ThreatActor: self.add_threat_actor, - TTP: self.add_threat_actor, + TTP: self.add_ttp, Observable: self.add_observable, } @@ -220,90 +214,6 @@ def add(self, entity): error = error.format(type(entity)) raise TypeError(error) - def to_obj(self, return_obj=None, ns_info=None): - super(STIXPackage, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.id = self.id_ - return_obj.idref = self.idref - return_obj.version = self.version - return_obj.timestamp = utils.dates.serialize_value(self.timestamp) - - if self.stix_header: - return_obj.STIX_Header = self.stix_header.to_obj(ns_info=ns_info) - if self.campaigns: - return_obj.Campaigns = self.campaigns.to_obj(ns_info=ns_info) - if self.courses_of_action: - return_obj.Courses_Of_Action = self.courses_of_action.to_obj(ns_info=ns_info) - if self.exploit_targets: - return_obj.Exploit_Targets = self.exploit_targets.to_obj(ns_info=ns_info) - if self.indicators: - return_obj.Indicators = self.indicators.to_obj(ns_info=ns_info) - if self.observables: - return_obj.Observables = self.observables.to_obj(ns_info=ns_info) - if self.incidents: - return_obj.Incidents = self.incidents.to_obj(ns_info=ns_info) - if self.threat_actors: - return_obj.Threat_Actors = self.threat_actors.to_obj(ns_info=ns_info) - if self.ttps: - return_obj.TTPs = self.ttps.to_obj(ns_info=ns_info) - if self.related_packages: - return_obj.Related_Packages = self.related_packages.to_obj(ns_info=ns_info) - - return return_obj - - def to_dict(self): - return super(STIXPackage, self).to_dict() - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not return_obj: - return_obj = cls() - - return_obj.id_ = obj.id - return_obj.idref = obj.idref - return_obj.timestamp = obj.timestamp - return_obj.stix_header = STIXHeader.from_obj(obj.STIX_Header) - return_obj.campaigns = Campaigns.from_obj(obj.Campaigns) - return_obj.courses_of_action = CoursesOfAction.from_obj(obj.Courses_Of_Action) - return_obj.exploit_targets = ExploitTargets.from_obj(obj.Exploit_Targets) - return_obj.indicators = Indicators.from_obj(obj.Indicators) - return_obj.observables = Observables.from_obj(obj.Observables) - return_obj.incidents = Incidents.from_obj(obj.Incidents) - return_obj.threat_actors = ThreatActors.from_obj(obj.Threat_Actors) - return_obj.ttps = TTPs.from_obj(obj.TTPs) - return_obj.related_packages = RelatedPackages.from_obj(obj.Related_Packages) - - # Don't overwrite unless a version is passed in - if obj.version: - return_obj.version = obj.version - - return return_obj - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not return_obj: - return_obj = cls() - - return_obj.id_ = dict_repr.get('id') - return_obj.idref = dict_repr.get('idref') - return_obj.timestamp = dict_repr.get('timestamp') - return_obj.version = dict_repr.get('version', cls._version) - return_obj.stix_header = STIXHeader.from_dict(dict_repr.get('stix_header')) - return_obj.campaigns = Campaigns.from_dict(dict_repr.get('campaigns')) - return_obj.courses_of_action = CoursesOfAction.from_dict(dict_repr.get('courses_of_action')) - return_obj.exploit_targets = ExploitTargets.from_dict(dict_repr.get('exploit_targets')) - return_obj.indicators = Indicators.from_dict(dict_repr.get('indicators')) - return_obj.observables = Observables.from_dict(dict_repr.get('observables')) - return_obj.incidents = Incidents.from_dict(dict_repr.get('incidents')) - return_obj.threat_actors = ThreatActors.from_dict(dict_repr.get('threat_actors')) - return_obj.ttps = TTPs.from_dict(dict_repr.get('ttps')) - return_obj.related_packages = RelatedPackages.from_dict(dict_repr.get('related_packages')) - - return return_obj - @classmethod def from_xml(cls, xml_file, encoding=None): """Parses the `xml_file` file-like object and returns a @@ -322,63 +232,3 @@ def from_xml(cls, xml_file, encoding=None): """ entity_parser = parser.EntityParser() return entity_parser.parse_xml(xml_file, encoding=encoding) - - -class Campaigns(stix.EntityList): - _binding = stix_core_binding - _namespace = 'http://stix.mitre.org/stix-1' - _binding_class = _binding.CampaignsType - _contained_type = Campaign - _binding_var = "Campaign" - _inner_name = "campaigns" - _dict_as_list = True - - -class CoursesOfAction(stix.EntityList): - _binding = stix_core_binding - _namespace = 'http://stix.mitre.org/stix-1' - _binding_class = _binding.CoursesOfActionType - _contained_type = CourseOfAction - _binding_var = "Course_Of_Action" - _inner_name = "courses_of_action" - _dict_as_list = True - - -class ExploitTargets(stix.EntityList): - _binding = stix_common_binding - _namespace = 'http://stix.mitre.org/common-1' - _binding_class = _binding.ExploitTargetsType - _contained_type = ExploitTarget - _binding_var = "Exploit_Target" - _inner_name = "exploit_targets" - _dict_as_list = True - - -class Incidents(stix.EntityList): - _binding = stix_core_binding - _namespace = 'http://stix.mitre.org/stix-1' - _binding_class = _binding.IncidentsType - _contained_type = Incident - _binding_var = "Incident" - _inner_name = "incidents" - _dict_as_list = True - - -class Indicators(stix.EntityList): - _binding = stix_core_binding - _namespace = 'http://stix.mitre.org/stix-1' - _binding_class = _binding.IndicatorsType - _contained_type = Indicator - _binding_var = "Indicator" - _inner_name = "indicators" - _dict_as_list = True - - -class ThreatActors(stix.EntityList): - _binding = stix_core_binding - _namespace = 'http://stix.mitre.org/stix-1' - _binding_class = _binding.ThreatActorsType - _contained_type = ThreatActor - _binding_var = "Threat_Actor" - _inner_name = "threat_actors" - _dict_as_list = True diff --git a/stix/core/ttps.py b/stix/core/ttps.py index 071280c1..29e50102 100644 --- a/stix/core/ttps.py +++ b/stix/core/ttps.py @@ -1,78 +1,33 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# mixbox +from mixbox import fields + +# stix import stix from stix.ttp import TTP -from stix.bindings import stix_core as core_binding from stix.common.kill_chains import KillChains +from stix.bindings import stix_core as core_binding -class TTPs(stix.EntityList): +class TTPs(stix.Entity): _binding = core_binding _binding_class = _binding.TTPsType _namespace = 'http://stix.mitre.org/stix-1' - _contained_type = TTP - _binding_var = "TTP" - _inner_name = "ttps" - - def __init__(self, ttps=None): - super(TTPs, self).__init__(ttps) - self.kill_chains = KillChains() - def __nonzero__(self): - return super(TTPs, self).__nonzero__() or bool(self.kill_chains) + ttp = fields.TypedField("TTP", TTP, multiple=True, key_name="ttps") + kill_chains = fields.TypedField("Kill_Chains", KillChains) - @property - def ttps(self): - return self._inner - - @ttps.setter - def ttps(self, value): - self._inner = [] - self.append(value) + def __init__(self, ttps=None): + super(TTPs, self).__init__() + self.ttp = ttps + self.kill_chains = KillChains() def add_ttp(self, ttp): - self.ttps.append(ttp) - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(TTPs, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if self.kill_chains: - return_obj.Kill_Chains = self.kill_chains.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): # noqa - if not obj: - return None - - if not return_obj: - return_obj = cls() - - super(TTPs, cls).from_obj(obj, return_obj=return_obj) - - return_obj.kill_chains = KillChains.from_obj(obj.Kill_Chains) - - return return_obj - - def to_dict(self): - return super(TTPs, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): # noqa - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - super(TTPs, cls).from_dict(dict_repr, return_obj) - - get = dict_repr.get - return_obj.kill_chains = KillChains.from_dict(get('kill_chains')) + self.ttp.append(ttp) - return return_obj + 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/data_marking.py b/stix/data_marking.py index 805f6ad5..8c5c939d 100644 --- a/stix/data_marking.py +++ b/stix/data_marking.py @@ -1,162 +1,25 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# external +from mixbox import fields +from mixbox import entities + +# internal import stix from stix.common import InformationSource -import stix.bindings.data_marking as stix_data_marking_binding - - -class Marking(stix.Entity): - _binding = stix_data_marking_binding - _binding_class = stix_data_marking_binding.MarkingType - _namespace = 'http://data-marking.mitre.org/Marking-1' - - def __init__(self, markings=None): - self.markings = _MarkingSpecifications(markings) - - @property - def markings(self): - return self._markings - - @markings.setter - def markings(self, value): - self._markings = _MarkingSpecifications(value) - - def add_marking(self, value): - self._markings.append(value) - - def to_obj(self, return_obj=None, ns_info=None): - super(Marking, self).to_obj( - return_obj=return_obj, - ns_info=ns_info - ) - - obj = self._binding_class() - - if self.markings: - obj.Marking = self.markings.to_obj(ns_info=ns_info) - - return obj - - def to_list(self): - return self.markings.to_list() if self.markings else [] - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.markings = _MarkingSpecifications.from_obj(obj.Marking) - - return return_obj - - @classmethod - def from_list(cls, markings_list, return_obj=None): - if not markings_list: - return None - - if not return_obj: - return_obj = cls() - - mlist = _MarkingSpecifications.from_list(markings_list) - return_obj.markings = mlist - - return return_obj - - to_dict = to_list - from_dict = from_list - - -class MarkingSpecification(stix.Entity): - _binding = stix_data_marking_binding - _binding_class = stix_data_marking_binding.MarkingSpecificationType - _namespace = 'http://data-marking.mitre.org/Marking-1' - - def __init__(self, controlled_structure=None, marking_structures=None): - self.id_ = None - self.idref = None - self.version = None - self.controlled_structure = controlled_structure - self.marking_structures = _MarkingStructures(marking_structures) - self.information_source = None - - @property - def information_source(self): - return self._information_source - - @information_source.setter - def information_source(self, value): - self._set_var(InformationSource, try_cast=False, information_source=value) - - @property - def marking_structures(self): - return self._marking_structures - - @marking_structures.setter - def marking_structures(self, value): - self._marking_structures = _MarkingStructures(value) - - def to_obj(self, return_obj=None, ns_info=None): - super(MarkingSpecification, self).to_obj( - return_obj=return_obj, - ns_info=ns_info - ) - - obj = self._binding_class() - - obj.id = self.id_ - obj.idref = self.idref - obj.version = self.version - obj.Controlled_Structure = self.controlled_structure - obj.Marking_Structure = self.marking_structures.to_obj(ns_info=ns_info) - if self.information_source: - obj.Information_Source = self.information_source.to_obj(ns_info=ns_info) - - return obj - - def to_dict(self): - return super(MarkingSpecification, self).to_dict() - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.id_ = obj.id - return_obj.idref = obj.idref - return_obj.version = obj.version - return_obj.controlled_structure = obj.Controlled_Structure - return_obj.marking_structures = _MarkingStructures.from_obj(obj.Marking_Structure) - return_obj.information_source = InformationSource.from_obj(obj.Information_Source) +# bindings +import stix.bindings.data_marking as stix_data_marking_binding - return return_obj +class MarkingStructureFactory(entities.EntityFactory): @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - get = d.get # PEP8 line length fix - return_obj.id_ = get('id') - return_obj.idref = get('idref') - return_obj.version = get('version') - return_obj.controlled_structure = get('controlled_structure') - return_obj.marking_structures = _MarkingStructures.from_dict( - get('marking_structures') - ) - return_obj.information_source = InformationSource.from_dict( - get('information_source') - ) - - return return_obj + def entity_class(cls, key): + import stix.extensions.marking.tlp # noqa + import stix.extensions.marking.simple_marking # noqa + import stix.extensions.marking.terms_of_use_marking # noqa + return stix.lookup_extension(key, default=MarkingStructure) class MarkingStructure(stix.Entity): @@ -165,120 +28,68 @@ class MarkingStructure(stix.Entity): _namespace = 'http://data-marking.mitre.org/Marking-1' _XSI_TYPE = None # overridden by subclasses + id_ = fields.IdField("id") + idref = fields.IdrefField("idref") + marking_model_name = fields.TypedField("marking_model_name") + marking_model_ref = fields.TypedField("marking_model_ref") + def __init__(self): + super(MarkingStructure, self).__init__() + self.id_ = None self.idref = None self.marking_model_name = None self.marking_model_ref = None - def to_obj(self, return_obj=None, ns_info=None): - super(MarkingStructure, self).to_obj( - return_obj=return_obj, - ns_info=ns_info - ) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.id = self.id_ - return_obj.idref = self.idref - return_obj.marking_model_name = self.marking_model_name - return_obj.marking_model_ref = self.marking_model_ref - - return return_obj - def to_dict(self): - d = {} + d = super(MarkingStructure, self).to_dict() if self._XSI_TYPE: d['xsi:type'] = self._XSI_TYPE - if self.id_: - d['id'] = self.id_ - if self.idref: - d['idref'] = self.idref - if self.marking_model_name: - d['marking_model_name'] = self.marking_model_name - if self.marking_model_ref: - d['marking_model_ref'] = self.marking_model_ref - return d @staticmethod def lookup_class(xsi_type): - if not xsi_type: - return MarkingStructure - - for (k, v) in _EXTENSION_MAP.iteritems(): - # TODO: for now we ignore the prefix and just check for - # a partial match - if xsi_type in k: - return v - - raise ValueError("Unregistered xsi:type %s" % xsi_type) - - @classmethod - def from_obj(cls, obj, return_obj=None): - import stix.extensions.marking.tlp # noqa - import stix.extensions.marking.simple_marking # noqa - import stix.extensions.marking.terms_of_use_marking # noqa - - if not obj: - return None + return stix.lookup_extension(xsi_type, default=MarkingStructure) - if return_obj: - m = return_obj - m.id_ = obj.id - m.idref = obj.idref - m.marking_model_name = obj.marking_model_name - m.marking_model_ref = obj.marking_model_ref - else: - if hasattr(obj, 'xml_type'): - klass = MarkingStructure.lookup_class(obj.xml_type) - m = klass.from_obj(obj) - else: - m = cls.from_obj(obj, cls()) - - return m - - @classmethod - def from_dict(cls, d, return_obj=None): - import stix.extensions.marking.tlp # noqa - import stix.extensions.marking.simple_marking # noqa - import stix.extensions.marking.terms_of_use_marking # noqa - - if not d: - return None +class MarkingSpecification(stix.Entity): + _binding = stix_data_marking_binding + _binding_class = stix_data_marking_binding.MarkingSpecificationType + _namespace = 'http://data-marking.mitre.org/Marking-1' - if return_obj is not None: - m = return_obj - m.id_ = d.get('id') - m.idref = d.get('idref') - m.marking_model_name = d.get('marking_model_name') - m.marking_model_ref = d.get('marking_model_ref') - else: - if 'xsi:type' in d: - cls = MarkingStructure.lookup_class(d.get('xsi:type')) - m = cls.from_dict(d) - else: - m = cls.from_dict(d, cls()) + id_ = fields.IdField("id") + idref = fields.IdrefField("idref") + version = fields.TypedField("version") + controlled_structure = fields.TypedField("Controlled_Structure") + marking_structures = fields.TypedField("Marking_Structure", MarkingStructure, factory=MarkingStructureFactory, multiple=True, key_name="marking_structures") + information_source = fields.TypedField("Information_Source", InformationSource) - return m + def __init__(self, controlled_structure=None, marking_structures=None): + super(MarkingSpecification, self).__init__() + self.id_ = None + self.idref = None + self.version = None + self.controlled_structure = controlled_structure + self.marking_structures = marking_structures + self.information_source = None -# Not Actual STIX Types! -class _MarkingSpecifications(stix.TypedList): - _contained_type = MarkingSpecification +class Marking(stix.EntityList): + _binding = stix_data_marking_binding + _binding_class = stix_data_marking_binding.MarkingType + _namespace = 'http://data-marking.mitre.org/Marking-1' -class _MarkingStructures(stix.TypedList): - _contained_type = MarkingStructure + marking = fields.TypedField("Marking", MarkingSpecification, multiple=True) + def __init__(self, markings=None): + super(Marking, self).__init__(markings) -#: Mapping of marking extension types to classes -_EXTENSION_MAP = {} + def add_marking(self, value): + self.marking.append(value) -def add_extension(cls): - _EXTENSION_MAP[cls._XSI_TYPE] = cls # noqa +# Backwards compatibility +add_extension = stix.add_extension diff --git a/stix/exploit_target/__init__.py b/stix/exploit_target/__init__.py index 98214d5b..dd9a41dd 100644 --- a/stix/exploit_target/__init__.py +++ b/stix/exploit_target/__init__.py @@ -1,6 +1,9 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# mixbox +from mixbox import fields + # internal import stix import stix.bindings.exploit_target as exploit_target_binding @@ -8,20 +11,20 @@ GenericRelationshipList, RelatedCOA, RelatedExploitTarget, RelatedPackageRefs ) -from stix.data_marking import Marking # relative -from .vulnerability import Vulnerability, _Vulnerabilities # noqa -from .weakness import Weakness, _Weaknesses # noqa -from .configuration import Configuration, _Configurations # noqa +from .vulnerability import Vulnerability # noqa +from .weakness import Weakness # noqa +from .configuration import Configuration # noqa +from stix.common import InformationSource class ExploitTarget(stix.BaseCoreComponent): - """Implementation of STIX ``ExploitTarget``. + """Implementation of STIX Exploit Target. Args: id_ (optional): An identifier. If ``None``, a value will be generated - via ``stix.utils.create_id()``. If set, this will unset the + via ``mixbox.idgen.create_id()``. If set, this will unset the ``idref`` property. idref (optional): An identifier reference. If set this will unset the ``id_`` property. @@ -39,6 +42,14 @@ class ExploitTarget(stix.BaseCoreComponent): _ALL_VERSIONS = ("1.0", "1.0.1", "1.1", "1.1.1") _ID_PREFIX = 'et' + potential_coas = fields.TypedField("Potential_COAs", type_="stix.exploit_target.PotentialCOAs") + related_exploit_targets = fields.TypedField("Related_Exploit_Targets", type_="stix.exploit_target.RelatedExploitTargets") + vulnerabilities = fields.TypedField("Vulnerability", Vulnerability, multiple=True, key_name="vulnerabilities") + weaknesses = fields.TypedField("Weakness", Weakness, multiple=True, key_name="weaknesses") + configuration = fields.TypedField("Configuration", Configuration, multiple=True) + related_packages = fields.TypedField("Related_Packages", RelatedPackageRefs) + information_source = fields.TypedField("Information_Source", InformationSource) + def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): @@ -51,204 +62,53 @@ def __init__(self, id_=None, idref=None, timestamp=None, title=None, short_description=short_description ) - self.handling = None self.potential_coas = PotentialCOAs() self.related_exploit_targets = RelatedExploitTargets() - self.vulnerabilities = None - self.weaknesses = None - self.configuration = None self.related_packages = RelatedPackageRefs() - @property - def handling(self): - - return self._handling - - @handling.setter - def handling(self, value): - self._set_var(Marking, try_cast=False, handling=value) - - @property - def vulnerabilities(self): - """A list of ``Vulnerability`` objects - - Default Value: ``None`` - - Returns: - A list of - :class:`stix.exploit_target.vulnerability` - - Raises: - ValueError: If set to a value that is not ``None`` and not an - instance of - :class:`stix.exploit_target.vulnerability` - - """ - - return self._vulnerabilities - - @vulnerabilities.setter - def vulnerabilities(self, value): - self._vulnerabilities = _Vulnerabilities(value) - - def add_vulnerability(self, v): - """Adds a vulnerability to the ``vulnerabilies`` list property. + def add_vulnerability(self, value): + """Adds a vulnerability to the :attr:`vulnerabilities` list property. Note: If ``None`` is passed in no value is added Args: - v: A Vulnerability value. + value: A :class:`.Vulnerability` object.. - Raises: ValueError if the ``v`` param is of type - :class:`stix.exploit_target.vulnerability` - - - """ - self.vulnerabilities.append(v) - - @property - def weaknesses(self): - """A list of ``Weakness`` objects - - Default Value: ``None`` - - Returns: - A list of - :class:`stix.exploit_target.weakness` - Raises: - ValueError: If set to a value that is not ``None`` and not an - instance of - :class:`stix.exploit_target.weakness` + ValueError: if the `value` param is of type :class:`.Vulnerability` """ - - return self._weaknesses - - @weaknesses.setter - def weaknesses(self, value): - self._weaknesses = _Weaknesses(value) - - def add_weakness(self, v): - """Adds a weakness to the ``weaknesses`` list property. + self.vulnerabilities.append(value) + + def add_weakness(self, value): + """Adds a weakness to the :attr:`weaknesses` list property. Note: If ``None`` is passed in no value is added Args: - v: A weakness value. + value: A :class:`.Weakness` object. - Raises: ValueError if the ``v`` param is of type :class:`stix.exploit_target.weakness` - - - """ - self.weaknesses.append(v) - - @property - def configuration(self): - """A list of ``Configuration`` objects - - Default Value: ``None`` - - Returns: - A list of - :class:`stix.exploit_target.configuration` - - Raises: - ValueError: If set to a value that is not ``None`` and not an - instance of - :class:`stix.exploit_target.configuration` + Raises: ValueError if the `value` param is of type :class:`.Weakness` """ - - return self._configuration - - @configuration.setter - def configuration(self, value): - self._configuration = _Configurations(value) + self.weaknesses.append(value) - def add_configuration(self, v): - """Adds a configuration to the ``configurations`` list property. + def add_configuration(self, value): + """Adds a configuration to the :attr:`configurations` list property. Note: If ``None`` is passed in no value is added Args: - v: A configuration value. + value: A configuration value. - Raises: ValueError if the ``v`` param is of type :class:`stix.exploit_target.configuration` + Raises: + ValueError: If the `value` param is of type :class:`.Configuration` """ - self.configuration.append(v) - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(ExploitTarget, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if self.handling: - return_obj.Handling = self.handling.to_obj(ns_info=ns_info) - if self.potential_coas: - return_obj.Potential_COAs = self.potential_coas.to_obj(ns_info=ns_info) - if self.related_exploit_targets: - return_obj.Related_Exploit_Targets = self.related_exploit_targets.to_obj(ns_info=ns_info) - if self.vulnerabilities: - return_obj.Vulnerability = self.vulnerabilities.to_obj(ns_info=ns_info) - if self.weaknesses: - return_obj.Weakness = self.weaknesses.to_obj(ns_info=ns_info) - if self.configuration: - return_obj.Configuration = self.configuration.to_obj(ns_info=ns_info) - if self.related_packages: - return_obj.Related_Packages = self.related_packages.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - super(ExploitTarget, cls).from_obj(obj, return_obj=return_obj) - - if isinstance(obj, cls._binding_class): - return_obj.handling = Marking.from_obj(obj.Handling) - return_obj.potential_coas = PotentialCOAs.from_obj(obj.Potential_COAs) - return_obj.related_exploit_targets = RelatedExploitTargets.from_obj(obj.Related_Exploit_Targets) - return_obj.vulnerabilities = _Vulnerabilities.from_obj(obj.Vulnerability) - return_obj.weaknesses = _Weaknesses.from_obj(obj.Weakness) - return_obj.configuration = _Configurations.from_obj(obj.Configuration) - return_obj.related_packages = RelatedPackageRefs.from_obj(obj.Related_Packages) - - return return_obj - - def to_dict(self): - return super(ExploitTarget, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - super(ExploitTarget, cls).from_dict(dict_repr, return_obj=return_obj) - - get = dict_repr.get - return_obj.handling = Marking.from_dict(get('handling')) - return_obj.potential_coas = PotentialCOAs.from_dict(get('potential_coas')) - return_obj.related_exploit_targets = RelatedExploitTargets.from_dict(get('related_exploit_targets')) - return_obj.vulnerabilities = _Vulnerabilities.from_dict(get('vulnerabilities')) - return_obj.weaknesses = _Weaknesses.from_dict(get('weaknesses')) - return_obj.configuration = _Configurations.from_dict(get('configuration')) - return_obj.related_packages = RelatedPackageRefs.from_dict(get('related_packages')) - - return return_obj + self.configuration.append(value) class PotentialCOAs(GenericRelationshipList): @@ -258,14 +118,11 @@ class PotentialCOAs(GenericRelationshipList): _namespace = "http://stix.mitre.org/ExploitTarget-1" _binding = exploit_target_binding _binding_class = exploit_target_binding.PotentialCOAsType - _binding_var = "Potential_COA" - _contained_type = RelatedCOA - _inner_name = "coas" + + potential_coa = fields.TypedField("Potential_COA", RelatedCOA, multiple=True, key_name="coas") def __init__(self, coas=None, scope=None): - if coas is None: - coas = [] - super(PotentialCOAs, self).__init__(scope, *coas) + super(PotentialCOAs, self).__init__(scope, coas) class RelatedExploitTargets(GenericRelationshipList): @@ -275,9 +132,8 @@ class RelatedExploitTargets(GenericRelationshipList): _namespace = "http://stix.mitre.org/ExploitTarget-1" _binding = exploit_target_binding _binding_class = exploit_target_binding.RelatedExploitTargetsType - _binding_var = "Related_Exploit_Target" - _contained_type = RelatedExploitTarget - _inner_name = "related_exploit_targets" + + related_exploit_target = fields.TypedField("Related_Exploit_Target", RelatedExploitTarget, multiple=True, key_name="related_exploit_targets") def __init__(self, related_exploit_targets=None, scope=None): super(RelatedExploitTargets, self).__init__(scope, related_exploit_targets) diff --git a/stix/exploit_target/configuration.py b/stix/exploit_target/configuration.py index 45a624ed..e5f1fd4a 100644 --- a/stix/exploit_target/configuration.py +++ b/stix/exploit_target/configuration.py @@ -1,9 +1,11 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix -import stix.bindings.exploit_target as exploit_target_binding from stix.common import StructuredText +import stix.bindings.exploit_target as exploit_target_binding class Configuration(stix.Entity): @@ -19,119 +21,13 @@ class Configuration(stix.Entity): _binding_class = _binding.ConfigurationType _namespace = "http://stix.mitre.org/ExploitTarget-1" + description = fields.TypedField("Description", type_="stix.common.StructuredText") + short_description = fields.TypedField("Short_Description", type_="stix.common.StructuredText") + cce_id = fields.TypedField("CCE_ID") + def __init__(self, description=None, short_description=None, cce_id=None): + super(Configuration, self).__init__() + self.description = description self.short_description = short_description self.cce_id = cce_id - - @property - def description(self): - """The ``description`` property for this :class:`Configuration`. - - Default Value: ``None`` - - Note: - If set to a value that is not an instance of - :class:`stix.common.structured_text.StructuredText`, an attempt to - will be made to convert the value into an instance of - :class:`stix.common.structured_text.StructuredText`. - - Returns: - An instance of - :class:`stix.common.structured_text.StructuredText` - - """ - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - @property - def short_description(self): - return self._short_description - - @short_description.setter - def short_description(self, value): - """The ``short_description`` property for this :class:`Configuration`. - - Default Value: ``None`` - - Note: - If set to a value that is not an instance of - :class:`stix.common.structured_text.StructuredText`, an attempt to - will be made to convert the value into an instance of - :class:`stix.common.structured_text.StructuredText`. - - Returns: - An instance of - :class:`stix.common.structured_text.StructuredText` - - """ - self._set_var(StructuredText, short_description=value) - - @property - def cce_id(self): - """Common Configuration Enumeration value for this :class:`Configuration`. - - Default Value: ``None`` - - Returns: - A string representing the CCE ID - """ - return self._cce_id - - @cce_id.setter - def cce_id(self, value): - self._cce_id = value - - def to_obj(self, return_obj=None, ns_info=None): - super(Configuration, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.short_description: - return_obj.Short_Description = self.short_description.to_obj(ns_info=ns_info) - return_obj.CCE_ID = self.cce_id - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.short_description = StructuredText.from_obj(obj.Short_Description) - return_obj.cce_id = obj.CCE_ID - - return return_obj - - def to_dict(self): - return super(Configuration, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - get = dict_repr.get - return_obj.description = StructuredText.from_dict(get('description')) - return_obj.short_description = StructuredText.from_dict(get('short_description')) - return_obj.cce_id = get('cce_id') - - return return_obj - - -# NOT AN ACTUAL STIX TYPE! -class _Configurations(stix.TypedList): - _contained_type = Configuration diff --git a/stix/exploit_target/vulnerability.py b/stix/exploit_target/vulnerability.py index 0d8702bf..87970241 100644 --- a/stix/exploit_target/vulnerability.py +++ b/stix/exploit_target/vulnerability.py @@ -1,15 +1,15 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +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, StructuredText from stix.common.related import GenericRelationshipList, RelatedObservable +from stix.common import References -# TODO document these class Vulnerability(stix.Entity): """Implementation of STIX ``Vulnerability``. @@ -19,210 +19,39 @@ class Vulnerability(stix.Entity): short_description (optional): A string short description. """ - _binding = exploit_target_binding _binding_class = _binding.VulnerabilityType _namespace = "http://stix.mitre.org/ExploitTarget-1" - def __init__(self, title=None, description=None, short_description=None): - self.is_known = None - self.is_publicly_acknowledged = None + is_known = fields.BooleanField("is_known") + is_publicly_acknowledged = fields.BooleanField("is_publicly_acknowledged") + title = fields.TypedField("Title") + description = fields.TypedField("Description", type_="stix.common.StructuredText") + short_description = fields.TypedField("Short_Description", type_="stix.common.StructuredText") + cve_id = fields.TypedField("CVE_ID") + osvdb_id = fields.TypedField("OSVDB_ID") + source = fields.TypedField("Source") + cvss_score = fields.TypedField("CVSS_Score", "stix.exploit_target.vulnerability.CVSSVector") + discovered_datetime = fields.TypedField("Discovered_DateTime", DateTimeWithPrecision) + published_datetime = fields.TypedField("Published_DateTime", DateTimeWithPrecision) + affected_software = fields.TypedField("Affected_Software", "stix.exploit_target.vulnerability.AffectedSoftware") + references = fields.TypedField("References", References) + + def __init__(self, title=None, description=None, short_description=None, references=None): + super(Vulnerability, self).__init__() + self.title = title self.description = description self.short_description = short_description - self.cve_id = None - self.osvdb_id = None - self.source = None - self.cvss_score = None - self.discovered_datetime = None - self.published_datetime = None - self.affected_software = AffectedSoftware() - self.references = [] - - @property - def title(self): - """ - String representing the Vulnerability Title - """ - return self._title - - @title.setter - def title(self, value): - self._title = value - - @property - def description(self): - """The ``description`` property for this :class:`Vulnerability`. - - Default Value: ``None`` - - Note: - If set to a value that is not an instance of - :class:`stix.common.structured_text.StructuredText`, an attempt to - will be made to convert the value into an instance of - :class:`stix.common.structured_text.StructuredText`. - - Returns: - An instance of - :class:`stix.common.structured_text.StructuredText` - - """ - - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - @property - def short_description(self): - """The ``short_description`` property for this :class:`Vulnerability`. - - Default Value: ``None`` - - Note: - If set to a value that is not an instance of - :class:`stix.common.structured_text.StructuredText`, an attempt to - will be made to convert the value into an instance of - :class:`stix.common.structured_text.StructuredText`. - - Returns: - An instance of - :class:`stix.common.structured_text.StructuredText` - - """ - return self._short_description - - @short_description.setter - def short_description(self, value): - self._set_var(StructuredText, short_description=value) - - @property - def discovered_datetime(self): - """ - Returns: - The time this vulnerability was discovered, represented as - class:`DateTimeWithPrecision` - """ - return self._discovered_datetime - - @discovered_datetime.setter - def discovered_datetime(self, value): - """ - Sets the time this vulnerability was discovered, represented as - class:`DateTimeWithPrecision` - - Default Value: ``None`` - - Returns: - None - - """ - self._set_var(DateTimeWithPrecision, discovered_datetime=value) - - @property - def references(self): - return self._references - - @references.setter - def references(self, value): - self._references = [] - - if not value: - return - elif utils.is_sequence(value): - self._references.extend(x for x in value if x) - else: - self._references.append(value) + self.references = references def add_reference(self, reference): if not reference: return - + if self.references is None: + self.references = References() self.references.append(reference) - def to_obj(self, return_obj=None, ns_info=None): - super(Vulnerability, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.Title = self.title - return_obj.CVE_ID = self.cve_id - return_obj.OSVDB_ID = self.osvdb_id - return_obj.Source = self.source - return_obj.is_known = utils.xml_bool(self.is_known) - return_obj.is_publicly_acknowledged = utils.xml_bool(self.is_publicly_acknowledged) - - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.short_description: - return_obj.Short_Description = self.short_description.to_obj(ns_info=ns_info) - if self.cvss_score: - return_obj.CVSS_Score = self.cvss_score.to_obj(ns_info=ns_info) - if self.discovered_datetime: - return_obj.Discovered_DateTime = self.discovered_datetime.to_obj(ns_info=ns_info) - if self.published_datetime: - return_obj.Published_DateTime = self.published_datetime.to_obj(ns_info=ns_info) - if self.affected_software: - return_obj.Affected_Software = self.affected_software.to_obj(ns_info=ns_info) - if self.references: - return_obj.References = stix_common_binding.ReferencesType(Reference=self.references) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.is_known = utils.xml_bool(obj.is_known) - return_obj.is_publicly_acknowledged = utils.xml_bool(obj.is_publicly_acknowledged) - return_obj.title = obj.Title - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.short_description = StructuredText.from_obj(obj.Short_Description) - return_obj.cve_id = obj.CVE_ID - return_obj.osvdb_id = obj.OSVDB_ID - return_obj.source = obj.Source - return_obj.cvss_score = CVSSVector.from_obj(obj.CVSS_Score) - return_obj.discovered_datetime = DateTimeWithPrecision.from_obj(obj.Discovered_DateTime) - return_obj.published_datetime = DateTimeWithPrecision.from_obj(obj.Published_DateTime) - return_obj.affected_software = AffectedSoftware.from_obj(obj.Affected_Software) - - if obj.References: - return_obj.references = obj.References.Reference - - return return_obj - - def to_dict(self): - return super(Vulnerability, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - get = dict_repr.get - return_obj.is_known = utils.xml_bool(get('is_known')) - return_obj.is_publicly_acknowledged = utils.xml_bool(get('is_publicly_acknowledged')) - return_obj.title = get('title') - return_obj.description = StructuredText.from_dict(get('description')) - return_obj.short_description = StructuredText.from_dict(get('short_description')) - return_obj.cve_id = get('cve_id') - return_obj.osvdb_id = get('osvdb_id') - return_obj.source = get('source') - return_obj.cvss_score = CVSSVector.from_dict(get('cvss_score')) - return_obj.discovered_datetime = DateTimeWithPrecision.from_dict(get('discovered_datetime')) - return_obj.published_datetime = DateTimeWithPrecision.from_dict(get('published_datetime')) - return_obj.affected_software = AffectedSoftware.from_dict(get('affected_software')) - return_obj.references = get('references') - - return return_obj - class CVSSVector(stix.Entity): """ @@ -233,93 +62,24 @@ class CVSSVector(stix.Entity): _binding_class = exploit_target_binding.CVSSVectorType _namespace = "http://stix.mitre.org/ExploitTarget-1" - def __init__(self): - self.overall_score = None - self.base_score = None - self.base_vector = None - self.temporal_score = None - self.temporal_vector = None - self.environmental_score = None - self.environmental_vector = None - - def to_obj(self, return_obj=None, ns_info=None): - super(CVSSVector, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.Overall_Score = self.overall_score - return_obj.Base_Score = self.base_score - return_obj.Base_Vector = self.base_vector - return_obj.Temporal_Score = self.temporal_score - return_obj.Temporal_Vector = self.temporal_vector - return_obj.Environmental_Score = self.environmental_score - return_obj.Environmental_Vector = self.environmental_vector - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.overall_score = obj.Overall_Score - return_obj.base_score = obj.Base_Score - return_obj.base_vector = obj.Base_Vector - return_obj.temporal_score = obj.Temporal_Score - return_obj.temporal_vector = obj.Temporal_Vector - return_obj.environmental_score = obj.Environmental_Score - return_obj.environmental_vector = obj.Environmental_Vector - - return return_obj + overall_score = fields.TypedField("Overall_Score") + base_score = fields.TypedField("Base_Score") + base_vector = fields.TypedField("Base_Vector") + temporal_score = fields.TypedField("Temporal_Score") + temporal_vector = fields.TypedField("Temporal_Vector") + environmental_score = fields.TypedField("Environmental_Score") + environmental_vector = fields.TypedField("Environmental_Vector") - def to_dict(self): - d = {} - - if self.overall_score: - d['overall_score'] = self.overall_score - if self.base_score: - d['base_score'] = self.base_score - if self.base_vector: - d['base_vector'] = self.base_vector - if self.temporal_score: - d['temporal_score'] = self.temporal_score - if self.temporal_vector: - d['temporal_vector'] = self.temporal_vector - if self.environmental_score: - d['environmental_score'] = self.environmental_score - if self.environmental_vector: - d['environmental_vector'] = self.environmental_vector - - return d - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - return_obj.overall_score = dict_repr.get('overall_score') - return_obj.base_score = dict_repr.get('base_score') - return_obj.base_vector = dict_repr.get('base_vector') - return_obj.temporal_score = dict_repr.get('temporal_score') - return_obj.temporal_vector = dict_repr.get('temporal_vector') - return_obj.environmental_score = dict_repr.get('environmental_score') - return_obj.environmental_vector = dict_repr.get('environmental_vector') - - return return_obj + def __init__(self): + super(CVSSVector, self).__init__() class AffectedSoftware(GenericRelationshipList): _binding = exploit_target_binding _binding_class = exploit_target_binding.AffectedSoftwareType _namespace = "http://stix.mitre.org/ExploitTarget-1" - _binding_var = "Affected_Software" - _contained_type = RelatedObservable - _inner_name = "affected_software" + + affected_software = fields.TypedField("Affected_Software", RelatedObservable, multiple=True, key_name="affected_software") # NOT AN ACTUAL STIX TYPE! diff --git a/stix/exploit_target/weakness.py b/stix/exploit_target/weakness.py index fce3607f..e9e7b4ef 100644 --- a/stix/exploit_target/weakness.py +++ b/stix/exploit_target/weakness.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix import stix.bindings.exploit_target as exploit_target_binding from stix.common import StructuredText @@ -18,83 +20,15 @@ class Weakness(stix.Entity): _binding_class = _binding.WeaknessType _namespace = "http://stix.mitre.org/ExploitTarget-1" + description = fields.TypedField("Description", type_="stix.common.StructuredText") + cwe_id = fields.TypedField("CWE_ID") + def __init__(self, description=None, cwe_id=None): + super(Weakness, self).__init__() + self.description = description self.cwe_id = cwe_id - @property - def cwe_id(self): - """ - Common Weakness Enumeration value as a string - """ - return self._cwe_id - - @cwe_id.setter - def cwe_id(self, value): - self._cwe_id = value - - @property - def description(self): - """The ``description`` property for this :class:`Weakness`. - - Default Value: ``None`` - - Note: - If set to a value that is not an instance of - :class:`stix.common.structured_text.StructuredText`, an attempt to - will be made to convert the value into an instance of - :class:`stix.common.structured_text.StructuredText`. - - Returns: - An instance of - :class:`stix.common.structured_text.StructuredText` - - """ - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(Weakness, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - return_obj.CWE_ID = self.cwe_id - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.cwe_id = obj.CWE_ID - - return return_obj - - def to_dict(self): - return super(Weakness, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - return_obj.description = StructuredText.from_dict(dict_repr.get('description')) - return_obj.cwe_id = dict_repr.get('cwe_id') - - return return_obj - class _Weaknesses(stix.TypedList): _contained_type = Weakness diff --git a/stix/extensions/__init__.py b/stix/extensions/__init__.py index 9a9569c5..2e7a26b0 100644 --- a/stix/extensions/__init__.py +++ b/stix/extensions/__init__.py @@ -1,2 +1,3 @@ # Copyright (c) 2015, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. + diff --git a/stix/extensions/identity/__init__.py b/stix/extensions/identity/__init__.py index 9a9569c5..2e7a26b0 100644 --- a/stix/extensions/identity/__init__.py +++ b/stix/extensions/identity/__init__.py @@ -1,2 +1,3 @@ # Copyright (c) 2015, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. + diff --git a/stix/extensions/identity/ciq_identity_3_0.py b/stix/extensions/identity/ciq_identity_3_0.py index 8e0b7cf6..2523841c 100644 --- a/stix/extensions/identity/ciq_identity_3_0.py +++ b/stix/extensions/identity/ciq_identity_3_0.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. import lxml.etree as et @@ -6,9 +6,10 @@ import stix import stix.utils as utils import stix.common as common -import stix.common.identity as identity import stix.bindings.extensions.identity.ciq_identity_3_0 as ciq_identity_binding +from mixbox.vendor.six import string_types + XML_NS_XPIL = "urn:oasis:names:tc:ciq:xpil:3" XML_NS_XNL = "urn:oasis:names:tc:ciq:xnl:3" @@ -18,16 +19,17 @@ et.register_namespace('xpil', XML_NS_XPIL) et.register_namespace('xnl', XML_NS_XNL) et.register_namespace('xal', XML_NS_XAL) -et.register_namespace('ExtSch', XML_NS_STIX_EXT) +et.register_namespace('stix-ciqidentity', XML_NS_STIX_EXT) +@stix.register_extension class CIQIdentity3_0Instance(common.Identity): - _binding = ciq_identity_binding - _binding_class = _binding.CIQIdentity3_0InstanceType - _namespace = "http://stix.mitre.org/extensions/Identity#CIQIdentity3.0-1" - _XML_NS_PREFIX = "ciqIdentity" - _XML_TYPE = "CIQIdentity3.0InstanceType" - _XSI_TYPE = "ciqIdentity:CIQIdentity3.0InstanceType" + _binding = ciq_identity_binding + _binding_class = _binding.CIQIdentity3_0InstanceType + _namespace = "http://stix.mitre.org/extensions/Identity#CIQIdentity3.0-1" + _XML_NS_PREFIX = "stix-ciqidentity" + _XML_TYPE = "CIQIdentity3.0InstanceType" + _XSI_TYPE = "stix-ciqidentity:CIQIdentity3.0InstanceType" def __init__(self, roles=None, specification=None): super(CIQIdentity3_0Instance, self).__init__() @@ -49,8 +51,8 @@ def roles(self, valuelist): self.add_role(role) def add_role(self, role): - if not isinstance(role, basestring): - raise ValueError('role is not instance of basestring') + if not isinstance(role, string_types): + raise ValueError('role is not instance of string_types') self.roles.append(role) @@ -65,50 +67,39 @@ def specification(self, value): self._specification = value - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(CIQIdentity3_0Instance, self).to_obj(return_obj) - - # return_obj.id = self.id_ - # return_obj.idref = self.idref_ - return_obj.xsi_type = self._XSI_TYPE + def to_obj(self, ns_info=None): + obj = super(CIQIdentity3_0Instance, self).to_obj(ns_info=ns_info) + obj.xsi_type = self._XSI_TYPE if self.roles: for role in self.roles: - return_obj.add_Role(role) + obj.add_Role(role) if self.specification: - return_obj.Specification = self.specification.to_obj(ns_info=ns_info) + obj.Specification = self.specification.to_obj(ns_info=ns_info) - return return_obj + return obj @classmethod - def from_obj(cls, obj, return_obj=None): - if obj is None: - return None - if not return_obj: - return_obj = cls() - - super(CIQIdentity3_0Instance, cls).from_obj(obj, return_obj) + def from_obj(cls, cls_obj): + obj = super(CIQIdentity3_0Instance, cls).from_obj(cls_obj) - roles = obj.Role - specification = obj.Specification + roles = cls_obj.Role + specification = cls_obj.Specification if roles: for role in roles: - return_obj.add_role(role) + obj.add_role(role) if specification is not None: - return_obj.specification = STIXCIQIdentity3_0.from_obj(specification) + obj.specification = STIXCIQIdentity3_0.from_obj(specification) - return return_obj + return obj def to_dict(self): d = super(CIQIdentity3_0Instance, self).to_dict() d['xsi:type'] = self._XSI_TYPE - + if self.roles: d['roles'] = [str(x) for x in self.roles] if self.specification: @@ -116,34 +107,32 @@ def to_dict(self): return d @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: + def from_dict(cls, cls_dict): + if not cls_dict: return None - if not return_obj: - return_obj = cls() + obj = super(CIQIdentity3_0Instance, cls).from_dict(cls_dict) - super(CIQIdentity3_0Instance, cls).from_dict(dict_repr, return_obj) - - roles = dict_repr.get('roles', []) - specification = dict_repr.get('specification') + roles = cls_dict.get('roles', []) + specification = cls_dict.get('specification') for role in roles: - return_obj.add_role(role) + obj.add_role(role) if specification: - return_obj.specification = STIXCIQIdentity3_0.from_dict(specification) + obj.specification = STIXCIQIdentity3_0.from_dict(specification) - return return_obj + return obj class STIXCIQIdentity3_0(stix.Entity): - _namespace = "http://stix.mitre.org/extensions/Identity#CIQIdentity3.0-1" + _namespace = "http://stix.mitre.org/extensions/Identity#CIQIdentity3.0-1" XML_TAG = "{%s}Specification" % _namespace - def __init__(self, party_name=None, languages=None, addresses=None, + def __init__(self, party_name=None, languages=None, addresses=None, organisation_info=None, electronic_address_identifiers=None, - free_text_lines=None, contact_numbers=None, nationalities=None): + free_text_lines=None, contact_numbers=None, + nationalities=None): self.party_name = party_name self.languages = languages self.addresses = addresses @@ -152,11 +141,11 @@ def __init__(self, party_name=None, languages=None, addresses=None, self.free_text_lines = free_text_lines self.contact_numbers = contact_numbers self.nationalities = nationalities - + @property def addresses(self): - return self._addresses - + return self._addresses + @addresses.setter def addresses(self, value): self._addresses = [] @@ -167,7 +156,7 @@ def addresses(self, value): self.add_address(v) else: self.add_address(value) - + def add_address(self, value): if not value: return @@ -175,11 +164,11 @@ def add_address(self, value): self.addresses.append(value) else: raise ValueError('value must be instance of Address') - + @property def languages(self): return self._languages - + @languages.setter def languages(self, value): self._languages = [] @@ -190,7 +179,7 @@ def languages(self, value): self.add_language(v) else: self.add_language(value) - + def add_language(self, value): if not value: return @@ -198,7 +187,7 @@ def add_language(self, value): self.languages.append(value) else: self.languages.append(Language(value)) - + @property def party_name(self): return self._party_name @@ -214,8 +203,8 @@ def party_name(self, value): @property def electronic_address_identifiers(self): - return self._electronic_address_identifiers - + return self._electronic_address_identifiers + @electronic_address_identifiers.setter def electronic_address_identifiers(self, value): self._electronic_address_identifiers = [] @@ -226,7 +215,7 @@ def electronic_address_identifiers(self, value): self.add_electronic_address_identifier(v) else: self.add_electronic_address_identifier(value) - + def add_electronic_address_identifier(self, value): if not value: return @@ -249,7 +238,7 @@ def free_text_lines(self, value): self.add_free_text_line(v) else: self.add_free_text_line(value) - + def add_free_text_line(self, value): if not value: return @@ -272,7 +261,7 @@ def contact_numbers(self, value): self.add_contact_number(v) else: self.add_contact_number(value) - + def add_contact_number(self, value): if not value: return @@ -302,7 +291,20 @@ def add_nationality(self, value): elif isinstance(value, Country): self.nationalities.append(value) else: - self.nationalities.append(Country(value)) + self.nationalities.append(Country(value)) + + @property + def organisation_info(self): + return self._organisation_info + + @organisation_info.setter + def organisation_info(self, value): + if not value: + self._organisation_info = None + elif isinstance(value, OrganisationInfo): + self._organisation_info = value + else: + raise ValueError('organisation_info must be instance of OrganisationInfo') @classmethod def from_obj(cls, obj, return_obj=None): @@ -318,7 +320,7 @@ def from_obj(cls, obj, return_obj=None): languages = obj.findall("{%s}Languages" % XML_NS_XPIL) if languages is not None and len(languages) > 0: return_obj.languages = [Language.from_obj(x) for x in languages[0]] - + addresses = obj.findall("{%s}Addresses" % XML_NS_XPIL) if addresses is not None and len(addresses) > 0: return_obj.addresses = [Address.from_obj(x) for x in addresses[0]] @@ -326,27 +328,33 @@ def from_obj(cls, obj, return_obj=None): nationalities = obj.findall("{%s}Nationalities" % XML_NS_XPIL) if nationalities is not None and len(nationalities) > 0: return_obj.nationalities = [Country.from_obj(x) for x in nationalities[0]] - + organisation_info = obj.findall(OrganisationInfo.XML_TAG) if organisation_info is not None and len(organisation_info) > 0: return_obj.organisation_info = OrganisationInfo.from_obj(organisation_info[0]) - + electronic_address_identifiers = obj.findall("{%s}ElectronicAddressIdentifiers" % XML_NS_XPIL) if electronic_address_identifiers is not None and len(electronic_address_identifiers) > 0: return_obj.electronic_address_identifiers = [ElectronicAddressIdentifier.from_obj(x) for x in electronic_address_identifiers[0]] - + free_text_lines = obj.findall("{%s}FreeTextLines" % XML_NS_XPIL) if free_text_lines is not None and len(free_text_lines) > 0: return_obj.free_text_lines = [FreeTextLine.from_obj(x) for x in free_text_lines[0]] - + contact_numbers = obj.findall("{%s}ContactNumbers" % XML_NS_XPIL) if contact_numbers is not None and len(contact_numbers) > 0: return_obj.contact_numbers = [ContactNumber.from_obj(x) for x in contact_numbers[0]] - + return return_obj def to_obj(self, return_obj=None, ns_info=None): - super(STIXCIQIdentity3_0, self).to_obj(return_obj=return_obj, ns_info=ns_info) + # Throw away return value; this class has no _binding_class, so + # it will return None anyway. This to_obj() is anomalous in that it + # returns an etree Element instead of a generateDS object. Bindings + # have all been hacked up to make this work. The super call does, + # however, do namespace collection (if ns_info is given), so it's + # still important. + super(STIXCIQIdentity3_0, self).to_obj(ns_info=ns_info) if not return_obj: root_tag = STIXCIQIdentity3_0.XML_TAG @@ -357,7 +365,7 @@ def to_obj(self, return_obj=None, ns_info=None): return_obj.append(ftl_root) for ftl in self.free_text_lines: ftl_root.append(ftl.to_obj(ns_info=ns_info)) - + if self.party_name: return_obj.append(self.party_name.to_obj(ns_info=ns_info)) @@ -366,22 +374,22 @@ def to_obj(self, return_obj=None, ns_info=None): return_obj.append(addresses_root) for address in self.addresses: addresses_root.append(address.to_obj(ns_info=ns_info)) - + if self.contact_numbers: contact_numbers_root = et.Element("{%s}ContactNumbers" % XML_NS_XPIL) return_obj.append(contact_numbers_root) for contact_number in self.contact_numbers: contact_numbers_root.append(contact_number.to_obj(ns_info=ns_info)) - + if self.electronic_address_identifiers: eai_root = et.Element("{%s}ElectronicAddressIdentifiers" % XML_NS_XPIL) return_obj.append(eai_root) for eai in self.electronic_address_identifiers: eai_root.append(eai.to_obj(ns_info=ns_info)) - + if self.organisation_info: return_obj.append(self.organisation_info.to_obj(ns_info=ns_info)) - + if self.languages: languages_root = et.Element("{%s}Languages" % XML_NS_XPIL) return_obj.append(languages_root) @@ -395,7 +403,7 @@ def to_obj(self, return_obj=None, ns_info=None): country_obj = country.to_obj(ns_info=ns_info) country_obj.tag = "{%s}Country" % XML_NS_XPIL nationalities_root.append(country_obj) - + return return_obj @classmethod @@ -412,6 +420,7 @@ def from_dict(cls, dict_repr, return_obj=None): return_obj.free_text_lines = [FreeTextLine.from_dict(x) for x in dict_repr.get('free_text_lines', [])] return_obj.contact_numbers = [ContactNumber.from_dict(x) for x in dict_repr.get('contact_numbers', [])] return_obj.nationalities = [Country.from_dict(x) for x in dict_repr.get('nationalities', [])] + return_obj.organisation_info = OrganisationInfo.from_dict(dict_repr.get('organisation_info')) return return_obj @@ -432,6 +441,8 @@ def to_dict(self): d['contact_numbers'] = [x.to_dict() for x in self.contact_numbers] if self.nationalities: d['nationalities'] = [x.to_dict() for x in self.nationalities] + if self.organisation_info: + d['organisation_info'] = self.organisation_info.to_dict() return d @@ -439,8 +450,9 @@ def to_dict(self): class Address(stix.Entity): _namespace = XML_NS_XPIL XML_TAG = "{%s}Address" % _namespace - - def __init__(self, free_text_address=None, country=None, administrative_area=None): + + def __init__(self, free_text_address=None, country=None, + administrative_area=None): self.free_text_address = free_text_address self.country = country self.administrative_area = administrative_area @@ -470,7 +482,7 @@ def free_text_address(self, value): self._set_var(FreeTextAddress, free_text_address=value) def to_obj(self, return_obj=None, ns_info=None): - super(Address, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(Address, self).to_obj(ns_info=ns_info) if not return_obj: return_obj = et.Element(self.XML_TAG) @@ -500,34 +512,34 @@ def from_obj(cls, obj, return_obj=None): return None if not return_obj: return_obj = cls() - + free_text_address = obj.findall("{%s}FreeTextAddress" % XML_NS_XAL) if len(free_text_address) > 0: return_obj.free_text_address = FreeTextAddress.from_obj(free_text_address[0]) - + country = obj.findall("{%s}Country" % XML_NS_XAL) if len(country) > 0: return_obj.country = Country.from_obj(country[0]) - + administrative_area = obj.findall("{%s}AdministrativeArea" % XML_NS_XAL) if len(administrative_area) > 0: return_obj.administrative_area = AdministrativeArea.from_obj(administrative_area[0]) - + return return_obj - + @classmethod def from_dict(cls, d, return_obj=None): if not d: return None if not return_obj: return_obj = cls() - + return_obj.free_text_address = FreeTextAddress.from_dict(d.get('free_text_address')) return_obj.country = Country.from_dict(d.get('country')) return_obj.administrative_area = AdministrativeArea.from_dict(d.get('administrative_area')) return return_obj - + class AdministrativeArea(stix.Entity): _namespace = XML_NS_XAL XML_TAG = "{%s}AdministrativeArea" % _namespace @@ -538,7 +550,7 @@ def __init__(self, name_elements=None): @property def name_elements(self): return self._name_elements - + @name_elements.setter def name_elements(self, value): self._name_elements = [] @@ -564,42 +576,42 @@ def from_obj(cls, obj, return_obj=None): return None if not return_obj: return_obj = cls() - + name_elements = obj.findall(NameElement.XML_TAG) if name_elements: for name_element in name_elements: return_obj.name_elements.append(NameElement.from_obj(name_element)) - + return return_obj - + def to_obj(self, return_obj=None, ns_info=None): - super(AdministrativeArea, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(AdministrativeArea, self).to_obj(ns_info=ns_info) if not return_obj: return_obj = et.Element(self.XML_TAG) - + for name_element in self.name_elements: return_obj.append(name_element.to_obj(ns_info=ns_info)) - + return return_obj - + def to_dict(self): d = {} if self.name_elements: d['name_elements'] = [x.to_dict() for x in self.name_elements] return d - + @classmethod def from_dict(cls, d, return_obj=None): if not d: return None if not return_obj: return_obj = cls() - + return_obj.name_elements = [NameElement.from_dict(x) for x in d.get('name_elements', [])] return return_obj - + class Country(stix.Entity): _namespace = XML_NS_XAL XML_TAG = "{%s}Country" % _namespace @@ -610,7 +622,7 @@ def __init__(self, name_elements=None): @property def name_elements(self): return self._name_elements - + @name_elements.setter def name_elements(self, value): self._name_elements = [] @@ -636,38 +648,38 @@ def from_obj(cls, obj, return_obj=None): return None if not return_obj: return_obj = cls() - + name_elements = obj.findall("{%s}NameElement" % XML_NS_XAL) if name_elements: for name_element in name_elements: return_obj.name_elements.append(NameElement.from_obj(name_element)) - + return return_obj - + def to_obj(self, return_obj=None, ns_info=None): - super(Country, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(Country, self).to_obj(ns_info=ns_info) if not return_obj: return_obj = et.Element(self.XML_TAG) - + for name_element in self.name_elements: return_obj.append(name_element.to_obj(ns_info=ns_info)) - + return return_obj - + def to_dict(self): d = {} if self.name_elements: d['name_elements'] = [x.to_dict() for x in self.name_elements] return d - + @classmethod def from_dict(cls, d, return_obj=None): if not d: return None if not return_obj: return_obj = cls() - + return_obj.name_elements = [NameElement.from_dict(x) for x in d.get('name_elements', [])] return return_obj @@ -675,17 +687,29 @@ def from_dict(cls, d, return_obj=None): class NameElement(stix.Entity): _namespace = XML_NS_XAL XML_TAG = "{%s}NameElement" % XML_NS_XAL - - def __init__(self, value=None): + + def __init__(self, value=None, name_type=None, name_code=None, + name_code_type=None): self.value = value - + self.name_type = name_type + self.name_code = name_code + self.name_code_type = name_code_type + def to_obj(self, return_obj=None, ns_info=None): - super(NameElement, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(NameElement, self).to_obj(ns_info=ns_info) return_obj = et.Element(self.XML_TAG) return_obj.text = self.value + + if self.name_type: + return_obj.set('{%s}NameType' % self._namespace, self.name_type) + if self.name_code: + return_obj.set('{%s}NameCode' % self._namespace, self.name_code) + if self.name_code_type: + return_obj.set('{%s}NameCodeType' % self._namespace, self.name_code_type) + return return_obj - + @classmethod def from_obj(cls, obj, return_obj=None): if obj is None: @@ -695,14 +719,24 @@ def from_obj(cls, obj, return_obj=None): return_obj = cls() return_obj.value = obj.text + return_obj.name_type = obj.get('{%s}NameType' % cls._namespace) + return_obj.name_code = obj.get('{%s}NameCode' % cls._namespace) + return_obj.name_code_type = obj.get('{%s}NameCodeType' % cls._namespace) + return return_obj - + def to_dict(self): d = {} if self.value: d['value'] = self.value + if self.name_type: + d['name_type'] = self.name_type + if self.name_code: + d['name_code'] = self.name_code + if self.name_code_type: + d['name_code_type'] = self.name_code_type return d - + @classmethod def from_dict(cls, d, return_obj=None): if not d: @@ -712,20 +746,23 @@ def from_dict(cls, d, return_obj=None): return_obj = cls() return_obj.value = d.get('value') + return_obj.name_type = d.get('name_type') + return_obj.name_code = d.get('name_code') + return_obj.name_code_type = d.get('name_code_type') return return_obj class FreeTextAddress(stix.Entity): _namespace = XML_NS_XAL XML_TAG = "{%s}FreeTextAddress" % XML_NS_XAL - + def __init__(self, address_lines=None): self.address_lines = address_lines @property def address_lines(self): return self._address_lines - + @address_lines.setter def address_lines(self, value): self._address_lines = [] @@ -743,17 +780,17 @@ def from_obj(cls, obj, return_obj=None): return None if not return_obj: return_obj = cls() - + address_line_tag = "{%s}AddressLine" % XML_NS_XAL address_lines = obj.findall(address_line_tag) if address_lines: for address_line in address_lines: return_obj.address_lines.append(address_line.text) - + return return_obj def to_obj(self, return_obj=None, ns_info=None): - super(FreeTextAddress, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(FreeTextAddress, self).to_obj(ns_info=ns_info) if not return_obj: return_obj = et.Element(self.XML_TAG) @@ -762,9 +799,9 @@ def to_obj(self, return_obj=None, ns_info=None): address_line = et.Element("{%s}AddressLine" % XML_NS_XAL) address_line.text = address return_obj.append(address_line) - + return return_obj - + @classmethod def from_dict(cls, d, return_obj=None): if not d: @@ -772,10 +809,10 @@ def from_dict(cls, d, return_obj=None): if not return_obj: return_obj = cls() - + return_obj.address_lines = d.get('address_lines', []) return return_obj - + def to_dict(self): d = {} if self.address_lines: @@ -805,31 +842,31 @@ def __init__(self, name_lines=None, person_names=None, organisation_names=None): self.add_organisation_name(value) def add_name_line(self, value): - if isinstance(value, basestring): + if isinstance(value, string_types): self.name_lines.append(NameLine(value)) elif isinstance(value, NameLine): self.name_lines.append(value) else: - raise ValueError('value must be a basestring or NameLine instance') + raise ValueError('value must be a string_types or NameLine instance') def add_person_name(self, value): - if isinstance(value, basestring): + if isinstance(value, string_types): self.person_names.append(PersonName(name_elements=[value])) elif isinstance(value, PersonName): - self.person_names.append(value) + self.person_names.append(value) else: - raise ValueError('value must be instance of PersonName or basestring') + raise ValueError('value must be instance of PersonName or string_types') def add_organisation_name(self, value): - if isinstance(value, basestring): - self.organisation_names.append(OrganisationName(name_elements=[value])) + if isinstance(value, string_types): + self.organisation_names.append(OrganisationName(name_elements=[value])) elif isinstance(value, OrganisationName): self.organisation_names.append(value) - else: + else: raise ValueError('value must be instance of OrganisationName') def to_obj(self, return_obj=None, ns_info=None): - super(PartyName, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(PartyName, self).to_obj(ns_info=ns_info) if not return_obj: root_tag = PartyName.XML_TAG @@ -913,7 +950,7 @@ def from_dict(cls, dict_repr, return_obj=None): for pn in pn_dicts: person_name = PersonName.from_dict(pn) - return_obj.add_person_name(person_name) + return_obj.add_person_name(person_name) return return_obj @@ -928,17 +965,17 @@ def __init__(self, value=None, type_=None): @property def value(self): - return self._value + return self._value @value.setter def value(self, value): - if value and not isinstance(value, basestring): - raise ValueError('value must be instance of basestring') + if value and not isinstance(value, string_types): + raise ValueError('value must be instance of string_types') self._value = value def to_obj(self, return_obj=None, ns_info=None): - super(NameLine, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(NameLine, self).to_obj(ns_info=ns_info) if not return_obj: root_tag = NameLine.XML_TAG @@ -947,7 +984,7 @@ def to_obj(self, return_obj=None, ns_info=None): if self.type: return_obj.attrib['Type'] = self.type - if self.value: + if self.value: return_obj.text = self.value return return_obj @@ -966,8 +1003,7 @@ def from_obj(cls, obj, return_obj=None): return return_obj def to_dict(self): - d = {} - d['value'] = self.value + d = {'value': self.value} if self.type: d['type'] = self.type @@ -992,15 +1028,55 @@ 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, basestring): + if isinstance(value, string_types): self.name_elements.append(PersonNameElement(value=value)) elif isinstance(value, PersonNameElement): self.name_elements.append(value) @@ -1008,12 +1084,15 @@ def add_name_element(self, value): raise ValueError('value must be instance of PersonNameElement') def to_obj(self, return_obj=None, ns_info=None): - super(PersonName, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(PersonName, self).to_obj(ns_info=ns_info) if not return_obj: 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)) @@ -1027,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: @@ -1038,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()) @@ -1052,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: @@ -1064,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 @@ -1085,7 +1200,7 @@ def name_elements(self, value): self.add_organisation_name_element(value) def add_organisation_name_element(self, value): - if isinstance(value, basestring): + if isinstance(value, string_types): self.name_elements.append(OrganisationNameElement(value=value)) elif isinstance(value, OrganisationNameElement): self.name_elements.append(value) @@ -1095,7 +1210,7 @@ def add_organisation_name_element(self, value): @property def subdivision_names(self): return self._subdivision_names - + @subdivision_names.setter def subdivision_names(self, value): self._subdivision_names = [] @@ -1114,7 +1229,7 @@ def add_subdivision_name(self, value): self.subdivision_names.append(value) def to_obj(self, return_obj=None, ns_info=None): - super(OrganisationName, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(OrganisationName, self).to_obj(ns_info=ns_info) if not return_obj: root_tag = OrganisationName.XML_TAG @@ -1137,7 +1252,7 @@ def from_obj(cls, obj, return_obj=None): return_obj = cls() return_obj.type_ = obj.attrib.get('{%s}Type' % XML_NS_XNL) - + name_elements = obj.findall(OrganisationNameElement.XML_TAG) if name_elements: for name_element_obj in name_elements: @@ -1154,7 +1269,7 @@ def from_obj(cls, obj, return_obj=None): def to_dict(self): d = {} - + if self.type_: d['type'] = self.type_ if self.name_elements: @@ -1183,13 +1298,14 @@ def from_dict(cls, dict_repr, return_obj=None): return_obj.add_subdivision_name(SubDivisionName.from_dict(sn_dict)) return return_obj - + class _BaseNameElement(stix.Entity): """Do not instantiate directly: use PersonNameElement or OrganisationNameElement """ + def __init__(self, value=None): self.value = value @@ -1199,9 +1315,6 @@ def value(self): @value.setter def value(self, value): - # if not value: - # raise ValueError('value cannot be None') - self._value = value @classmethod @@ -1213,7 +1326,7 @@ def from_obj(cls, obj, return_obj=None): return return_obj def to_obj(self, return_obj=None, ns_info=None): - super(_BaseNameElement, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(_BaseNameElement, self).to_obj(ns_info=ns_info) return_obj.text = self.value return return_obj @@ -1234,8 +1347,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' @@ -1263,7 +1376,7 @@ def element_type(self, value): if value and value not in self.TYPES: raise ValueError('value must be one of %s: ' % (self.TYPES,)) - self._element_type = value + self._element_type = value def to_obj(self, return_obj=None, ns_info=None): if not return_obj: @@ -1285,14 +1398,13 @@ def from_obj(cls, obj, return_obj=None): if not return_obj: return_obj = cls() - return_obj.element_type = obj.get('ElementType') + return_obj.element_type = obj.get('ElementType') return_obj.value = obj.text return return_obj def to_dict(self): - d = {} - d['value'] = self.value + d = {'value': self.value} if self.element_type: d['element_type'] = self.element_type @@ -1419,7 +1531,7 @@ def type(self, value): self._type = value def to_obj(self, return_obj=None, ns_info=None): - super(SubDivisionName, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(SubDivisionName, self).to_obj(ns_info=ns_info) if not return_obj: root_tag = SubDivisionName.XML_TAG @@ -1445,8 +1557,8 @@ def from_obj(cls, obj, return_obj=None): return return_obj def to_dict(self): - d = {} - d['value'] = self.value + d = {'value': self.value} + if self.type: d['type'] = self.type @@ -1468,61 +1580,61 @@ def from_dict(cls, dict_repr, return_obj=None): class Language(stix.Entity): _namespace = XML_NS_XPIL XML_TAG = "{%s}Language" % _namespace - + def __init__(self, value=None): self.value = value - + def to_obj(self, return_obj=None, ns_info=None): - super(Language, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(Language, self).to_obj(ns_info=ns_info) return_obj = et.Element(self.XML_TAG) return_obj.text = self.value return return_obj - + @classmethod def from_obj(cls, obj): if obj is None: return None - + return_obj = cls() return_obj.value = obj.text return return_obj - + def to_dict(self): d = {} if self.value: d['value'] = self.value return d - + @classmethod def from_dict(cls, d): if not d: return None - + return_obj = cls() return_obj.value = d.get('value') return return_obj - + class ElectronicAddressIdentifier(stix.Entity): _namespace = XML_NS_XPIL XML_TAG = "{%s}ElectronicAddressIdentifier" % _namespace - + def __init__(self, value=None, type_=None): self.type_ = type_ self.value = value - + def to_obj(self, return_obj=None, ns_info=None): - super(ElectronicAddressIdentifier, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(ElectronicAddressIdentifier, self).to_obj(ns_info=ns_info) return_obj = et.Element(self.XML_TAG) return_obj.text = self.value - + if self.type_: return_obj.attrib['{%s}Type' % XML_NS_XPIL] = self.type_ - + return return_obj - + @classmethod def from_obj(cls, obj, return_obj=None): if obj is None: @@ -1534,7 +1646,7 @@ def from_obj(cls, obj, return_obj=None): return_obj.type_ = obj.attrib.get('{%s}Type' % XML_NS_XPIL) return_obj.value = obj.text return return_obj - + def to_dict(self): d = {} if self.value: @@ -1542,7 +1654,7 @@ def to_dict(self): if self.type_: d['type'] = self.type_ return d - + @classmethod def from_dict(cls, d, return_obj=None): if not d: @@ -1562,16 +1674,16 @@ class OrganisationInfo(stix.Entity): def __init__(self, industry_type=None): self.industry_type = industry_type - + def to_obj(self, return_obj=None, ns_info=None): - super(OrganisationInfo, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(OrganisationInfo, self).to_obj(ns_info=ns_info) return_obj = et.Element(self.XML_TAG) if self.industry_type: return_obj.attrib['{%s}IndustryType' % self._namespace] = self.industry_type - + return return_obj - + @classmethod def from_obj(cls, obj, return_obj=None): if obj is None: @@ -1588,7 +1700,7 @@ def to_dict(self): if self.industry_type: d['industry_type'] = self.industry_type return d - + @classmethod def from_dict(cls, d, return_obj=None): if not d: @@ -1604,22 +1716,22 @@ def from_dict(cls, d, return_obj=None): class FreeTextLine(stix.Entity): _namespace = XML_NS_XPIL XML_TAG = "{%s}FreeTextLine" % _namespace - + def __init__(self, value=None, type_=None): self.value = value self.type_ = type_ - + def to_obj(self, return_obj=None, ns_info=None): - super(FreeTextLine, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(FreeTextLine, self).to_obj(ns_info=ns_info) return_obj = et.Element(self.XML_TAG) if self.type_: return_obj.attrib['{%s}Type' % self._namespace] = self.type_ if self.value: return_obj.text = self.value - + return return_obj - + @classmethod def from_obj(cls, obj, return_obj=None): if obj is None: @@ -1639,7 +1751,7 @@ def to_dict(self): if self.value: d['value'] = self.value return d - + @classmethod def from_dict(cls, d, return_obj=None): if not d: @@ -1656,26 +1768,26 @@ def from_dict(cls, d, return_obj=None): class ContactNumber(stix.Entity): _namespace = XML_NS_XPIL XML_TAG = "{%s}ContactNumber" % _namespace - + COM_MEDIA_TYPE_CELLPHONE = "Cellphone" COM_MEDIA_TYPE_FAX = "Fax" COM_MEDIA_TYPE_PAGER = "Pager" COM_MEDIA_TYPE_TELEPHONE = "Telephone" COM_MEDIA_TYPE_VOIP = "VOIP" - + ALLOWED_COM_MEDIA_TYPES = ( COM_MEDIA_TYPE_CELLPHONE, COM_MEDIA_TYPE_FAX, COM_MEDIA_TYPE_PAGER, COM_MEDIA_TYPE_TELEPHONE, COM_MEDIA_TYPE_VOIP ) - + def __init__(self, contact_number_elements=None, communication_media_type=None): self.communication_media_type = communication_media_type self.contact_number_elements = contact_number_elements - + @property def contact_number_elements(self): return self._contact_number_elements - + @contact_number_elements.setter def contact_number_elements(self, value): self._contact_number_elements = [] @@ -1686,7 +1798,7 @@ def contact_number_elements(self, value): self.add_contact_number_element(v) else: self.add_contact_number_element(value) - + def add_contact_number_element(self, value): if not value: return @@ -1694,11 +1806,11 @@ def add_contact_number_element(self, value): self.contact_number_elements.append(value) else: self.contact_number_elements.append(ContactNumberElement(value)) - + @property def communication_media_type(self): return self._communication_media_type - + @communication_media_type.setter def communication_media_type(self, value): if not value: @@ -1707,17 +1819,18 @@ def communication_media_type(self, value): raise ValueError('value must be one of %s' % (self.ALLOWED_COM_MEDIA_TYPES,)) else: self._communication_media_type = value - + def to_obj(self, return_obj=None, ns_info=None): + super(ContactNumber, self).to_obj(ns_info=ns_info) return_obj = et.Element(self.XML_TAG) if self.communication_media_type: return_obj.attrib['{%s}CommunicationMediaType' % self._namespace] = self.communication_media_type if self.contact_number_elements: for contact_number_element in self.contact_number_elements: return_obj.append(contact_number_element.to_obj(ns_info=ns_info)) - + return return_obj - + @classmethod def from_obj(cls, obj, return_obj=None): if obj is None: @@ -1727,11 +1840,11 @@ def from_obj(cls, obj, return_obj=None): return_obj = cls() return_obj.communication_media_type = obj.get('{%s}CommunicationMediaType' % cls._namespace) - + contact_number_elements = obj.findall("{%s}ContactNumberElement" % XML_NS_XPIL) if contact_number_elements is not None and len(contact_number_elements) > 0: return_obj.contact_number_elements = [ContactNumberElement.from_obj(x) for x in contact_number_elements] - + return return_obj def to_dict(self): @@ -1740,9 +1853,9 @@ def to_dict(self): d['communication_media_type'] = self.communication_media_type if self.contact_number_elements: d['contact_number_elements'] = [x.to_dict() for x in self.contact_number_elements] - + return d - + @classmethod def from_dict(cls, d, return_obj=None): if not d: @@ -1753,14 +1866,14 @@ def from_dict(cls, d, return_obj=None): return_obj.communication_media_type = d.get('communication_media_type') return_obj.contact_number_elements = [ContactNumberElement.from_dict(x) for x in d.get('contact_number_elements', [])] - + return return_obj class ContactNumberElement(stix.Entity): _namespace = XML_NS_XPIL XML_TAG = "{%s}ContactNumberElement" % _namespace - + TYPE_COUNTRY_CODE = "CountryCode" TYPE_AREA_CODE = "AreaCode" TYPE_LOCAL_NUMBER = "LocalNumber" @@ -1769,21 +1882,21 @@ class ContactNumberElement(stix.Entity): TYPE_SEPARATOR = "Separator" TYPE_NATIONAL_NUMBER = "NationalNumber" TYPE_INTERNATIONAL_NUMBER = "InternationalNumber" - + ALLOWED_TYPES = ( TYPE_AREA_CODE, TYPE_COUNTRY_CODE, TYPE_EXTENSION, TYPE_INTERNATIONAL_NUMBER, TYPE_LOCAL_NUMBER, TYPE_NATIONAL_NUMBER, TYPE_SEPARATOR, TYPE_PIN ) - + def __init__(self, value=None, type_=None): self.value = value self.type_ = type_ - + @property def type_(self): return self._type - + @type_.setter def type_(self, value): if not value: @@ -1792,18 +1905,18 @@ def type_(self, value): raise ValueError('value must be one of %s' % (self.ALLOWED_TYPES,)) else: self._type = value - + def to_obj(self, return_obj=None, ns_info=None): - super(ContactNumberElement, self).to_obj(return_obj=return_obj, ns_info=ns_info) + super(ContactNumberElement, self).to_obj(ns_info=ns_info) return_obj = et.Element(self.XML_TAG) if self.type_: return_obj.attrib['{%s}Type' % self._namespace] = self.type_ if self.value: return_obj.text = self.value - + return return_obj - + @classmethod def from_obj(cls, obj, return_obj=None): if obj is None: @@ -1823,7 +1936,7 @@ def to_dict(self): if self.value: d['value'] = self.value return d - + @classmethod def from_dict(cls, d, return_obj=None): if not d: @@ -1835,7 +1948,3 @@ def from_dict(cls, d, return_obj=None): return_obj.type_ = d.get('type') return_obj.value = d.get('value') return return_obj - - -# Register the extension -identity.add_extension(CIQIdentity3_0Instance) diff --git a/stix/extensions/malware/__init__.py b/stix/extensions/malware/__init__.py index 9a9569c5..2e7a26b0 100644 --- a/stix/extensions/malware/__init__.py +++ b/stix/extensions/malware/__init__.py @@ -1,2 +1,3 @@ # Copyright (c) 2015, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. + diff --git a/stix/extensions/malware/maec_4_1_malware.py b/stix/extensions/malware/maec_4_1_malware.py index 8c5e45be..0144ee7c 100644 --- a/stix/extensions/malware/maec_4_1_malware.py +++ b/stix/extensions/malware/maec_4_1_malware.py @@ -1,224 +1,37 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -# stdlib -from StringIO import StringIO -from distutils.version import LooseVersion - # external -from lxml import etree +from mixbox import fields + # internal import stix -import stix.utils as utils -import stix.utils.parser as parser -import stix.ttp.malware_instance +from stix.bindings.extensions.malware import maec_4_1 as maec_instance_binding from stix.ttp.malware_instance import MalwareInstance -import stix.bindings.extensions.malware.maec_4_1 as ext_binding - - -_MIN_PYTHON_MAEC_VERSION = '4.1.0.12' - - -class UnsupportedVersion(Exception): - def __init__(self, message, expected, found): - super(UnsupportedVersion, self).__init__(message) - self.expected = expected - self.found = found -def _check_maec_version(): - """Checks that the installed python-maec has a version greater than or - equal to the minimum supported version. - - Note: - We do this rather than having a python-maec dependency requirement - listed in setup.py because MAEC is used as an extension to STIX and - not a core component to STIX (like CybOX). - - Raises: - ImportError: If python-maec is not installed. - UnsupportedVersion: If the python-maec installation does not satisfy - the version requirements. - +@stix.register_extension +class MAECInstance(MalwareInstance): """ - import maec - - found = maec.__version__ - expected = _MIN_PYTHON_MAEC_VERSION - - if LooseVersion(found) >= LooseVersion(expected): - return + The MAECInstance object provides an extension to the MalwareInstanceType + which imports and leverages the MAEC 4.1 schema for structured + characterization of Malware. - fmt = ("Unsupported python-maec version installed: '%s'. Minimum version " - "is '%s'.") - error = fmt % (found, expected) - raise UnsupportedVersion(error, expected=expected, found=found) - - -try: - # Check that the correct version of python-maec is installed. - _check_maec_version() - - # Import maecPackage into global space - from maec.package.package import Package as maecPackage - - _MAEC_INSTALLED = True -except ImportError: - maecPackage, Package = None, None - _MAEC_INSTALLED = False - - -def is_maec(obj): - """Checks if the input object is python-maec object. - - Returns: - True if python-maec is ins + This class extension is automatically registered by the + MalwareInstanceFactory. + Warnings: + Interacting with the ``maec`` field will fail if the maec library is + not installed in your Python environment. """ - if not _MAEC_INSTALLED: - return False - - return isinstance(obj, maecPackage) - - -class MAECInstance(MalwareInstance): - _binding = ext_binding + _binding = maec_instance_binding _binding_class = _binding.MAEC4_1InstanceType - _namespace = 'http://stix.mitre.org/extensions/Malware#MAEC4.1-1' - _xml_ns_prefix = "stix-maec" + _namespace = "http://stix.mitre.org/extensions/Malware#MAEC4.1-1" _XSI_TYPE = "stix-maec:MAEC4.1InstanceType" - _TAG_MAEC = "{%s}MAEC" % _namespace + + maec = fields.TypedField("MAEC", type_="maec.package.package.Package") def __init__(self, maec=None): super(MAECInstance, self).__init__() - self.__input_namespaces__ = {} - self.__input_schemalocations__ = {} self.maec = maec - - @property - def maec(self): - return self._maec - - @maec.setter - def maec(self, value): - if value is None: - self._maec = None - elif _MAEC_INSTALLED and is_maec(value): - self._maec = value - elif utils.is_element(value) or utils.is_etree(value): - tree = parser.get_etree(value) - root = parser.get_etree_root(tree) - self._parse_etree(root) - self._maec = root - else: - error = ( - "Cannot set maec to '{0}'. Expected 'lxml.etree._Element' or " - "'maec.package.package.Package'." - ) - error = error.format(type(value)) - raise ValueError(error) - - def _parse_etree(self, root): - node_tag = root.tag - - if node_tag != self._TAG_MAEC: - self._cast_maec(root) - - self._collect_namespaces(root) - self._collect_schemalocs(root) - - def _cast_maec(self, node): - ns_maec = "http://maec.mitre.org/XMLSchema/maec-package-2" - node_ns = etree.QName(node).namespace - - if node_ns == ns_maec: - etree.register_namespace(self._xml_ns_prefix, self._namespace) - node.tag = self._TAG_MAEC - else: - error = "Cannot set maec. Expected tag '{0}' found '{1}'." - error = error.format(self._TAG_MAEC, node.tag) - raise ValueError(error) - - def _collect_schemalocs(self, node): - try: - schemaloc = parser.get_schemaloc_pairs(node) - self.__input_schemalocations__ = dict(schemaloc) - except KeyError: - self.__input_schemalocations__ = {} - - def _collect_namespaces(self, node): - self.__input_namespaces__ = dict(node.nsmap.iteritems()) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - super(MAECInstance, cls).from_obj(obj, return_obj) - - if _MAEC_INSTALLED: - return_obj.maec = maecPackage.from_obj(obj.MAEC) - else: - return_obj.maec = obj.MAEC - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(MAECInstance, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if _MAEC_INSTALLED and isinstance(self.maec, maecPackage): - return_obj.MAEC = self.maec.to_obj(ns_info=ns_info) - else: - return_obj.MAEC = self.maec - - return return_obj - - @classmethod - def _maec_from_dict(cls, d): - if _MAEC_INSTALLED: - return maecPackage.from_dict(d) - - raise ValueError( - "Unable to parse 'maec' value in dictionary. Please " - "install python-maec to parse dictionary value." - ) - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - - if not return_obj: - return_obj = cls() - - super(MAECInstance, cls).from_dict(d, return_obj) - - with utils.ignored(KeyError): - maec = d['maec'] - if isinstance(maec, dict): - return_obj.maec = cls._maec_from_dict(maec) - else: - parser = stix.utils.parser.get_xml_parser() - return_obj.maec = etree.parse(StringIO(maec), parser=parser) - - return return_obj - - def to_dict(self): - d = super(MAECInstance, self).to_dict() - - if self.maec is not None: - if _MAEC_INSTALLED and isinstance(self.maec, maecPackage): - d['maec'] = self.maec.to_dict() - else: - d['maec'] = etree.tostring(self.maec) - - return d - -# Register the extension -stix.ttp.malware_instance.add_extension(MAECInstance) diff --git a/stix/extensions/marking/__init__.py b/stix/extensions/marking/__init__.py index 9a9569c5..2e7a26b0 100644 --- a/stix/extensions/marking/__init__.py +++ b/stix/extensions/marking/__init__.py @@ -1,2 +1,3 @@ # Copyright (c) 2015, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. + diff --git a/stix/extensions/marking/ais.py b/stix/extensions/marking/ais.py new file mode 100644 index 00000000..5c6b36d2 --- /dev/null +++ b/stix/extensions/marking/ais.py @@ -0,0 +1,339 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# See LICENSE.txt for complete terms. + +""" +STIX Extension for AIS Data Markings + +Unlike the other marking extensions, the AIS marking extension is not loaded +automatically, since AIS markings are not a part of the published STIX 1.x +specifications. They are included in python-stix because they're common enough +that it is not worth creating a separate package. + +If you are writing code that needs to parse AIS markings, make sure that your +program imports this module before beginning to parse any STIX documents: + +.. code-block:: python + + import stix.extensions.marking.ais + +""" + +from mixbox import fields +from mixbox.namespaces import Namespace + +import stix.bindings.extensions.marking.ais as ais_binding +import stix.data_marking +from stix.data_marking import MarkingStructure + + +def validate_value(instance, value): + allowed = instance._ALLOWED_VALUES + if not value: + return + elif not allowed: + return + elif value in allowed: + return + else: + error = "Value must be one of {allowed}. Received '{value}'" + error = error.format(**locals()) + raise ValueError(error) + + +class AISConsentType(stix.Entity): + _binding = ais_binding + _binding_class = _binding.AISConsentType + _namespace = 'http://www.us-cert.gov/STIXMarkingStructure#AISConsentMarking-2' + _ALLOWED_VALUES = ('EVERYONE', 'USG', 'NONE') + + consent = fields.TypedField("consent", preset_hook=validate_value) + + def __init__(self, consent=None): + super(AISConsentType, self).__init__() + self.consent = consent + + +class TLPMarkingType(stix.Entity): + _binding = ais_binding + _binding_class = _binding.TLPMarkingType + _namespace = 'http://www.us-cert.gov/STIXMarkingStructure#AISConsentMarking-2' + _ALLOWED_VALUES = ('WHITE', 'GREEN', 'AMBER') + + color = fields.TypedField("color", preset_hook=validate_value) + + def __init__(self, color=None): + super(TLPMarkingType, self).__init__() + self.color = color + + +class NotProprietary(stix.Entity): + _binding = ais_binding + _binding_class = _binding.NotProprietary + _namespace = 'http://www.us-cert.gov/STIXMarkingStructure#AISConsentMarking-2' + + cisa_proprietary = fields.TypedField("CISA_Proprietary") + ais_consent = fields.TypedField("AISConsent", AISConsentType, key_name="ais_consent") + tlp_marking = fields.TypedField("TLPMarking", TLPMarkingType, key_name="tlp_marking") + + def __init__(self, cisa_proprietary='false', ais_consent=None, + tlp_marking=None): + super(NotProprietary, self).__init__() + + self.cisa_proprietary = cisa_proprietary + self.ais_consent = ais_consent + self.tlp_marking = tlp_marking + + +class IsProprietary(stix.Entity): + _binding = ais_binding + _binding_class = _binding.IsProprietary + _namespace = 'http://www.us-cert.gov/STIXMarkingStructure#AISConsentMarking-2' + + cisa_proprietary = fields.TypedField("CISA_Proprietary") + ais_consent = fields.TypedField("AISConsent", AISConsentType, key_name="ais_consent") + tlp_marking = fields.TypedField("TLPMarking", TLPMarkingType, key_name="tlp_marking") + + def __init__(self, cisa_proprietary='true', ais_consent=None, + tlp_marking=None): + super(IsProprietary, self).__init__() + + self.cisa_proprietary = cisa_proprietary + self.ais_consent = ais_consent + self.tlp_marking = tlp_marking + + +@stix.register_extension +class AISMarkingStructure(MarkingStructure): + _binding = ais_binding + _binding_class = _binding.AISMarkingStructure + _namespace = 'http://www.us-cert.gov/STIXMarkingStructure#AISConsentMarking-2' + _XSI_TYPE = "AIS:AISMarkingStructure" + + is_proprietary = fields.TypedField("Is_Proprietary", IsProprietary) + not_proprietary = fields.TypedField("Not_Proprietary", NotProprietary) + + def __init__(self, is_proprietary=None, not_proprietary=None): + super(AISMarkingStructure, self).__init__() + + self.is_proprietary = is_proprietary + self.not_proprietary = not_proprietary + + +NAMESPACES = [ + Namespace('http://www.us-cert.gov/STIXMarkingStructure#AISConsentMarking-2', 'AIS', 'http://www.us-cert.gov/sites/default/files/STIX_Namespace/AIS_Bundle_Marking_1.1.1_v1.0.xsd') +] + + +def _update_namespaces(): + # Update the python-stix namespace dictionary + from stix.utils import nsparser + import mixbox.namespaces + + nsparser.STIX_NAMESPACES.add_namespace(NAMESPACES[0]) + mixbox.namespaces.register_namespace(NAMESPACES[0]) + + +_update_namespaces() + + +# IndustryType allowed sectors +#: Chemical Sector +CHEMICAL_SECTOR = 'Chemical Sector' +#: Chemical Sector +COMMERCIAL_FACILITIES_SECTOR = 'Commercial Facilities Sector' +#: Commercial Facilities Sector +COMMUNICATIONS_SECTOR = 'Communications Sector' +#: Critical Manufacturing Sector +CRITICAL_MANUFACTURING_SECTOR = 'Critical Manufacturing Sector' +#: Dams Sector +DAMS_SECTOR = 'Dams Sector' +#: Defense Industrial Base Sector +DEFENSE_INDUSTRIAL_BASE_SECTOR = 'Defense Industrial Base Sector' +#: Emergency Services Sector +EMERGENCY_SERVICES_SECTOR = 'Emergency Services Sector' +#: Energy Sector +ENERGY_SECTOR = 'Energy Sector' +#: Financial Services Sector +FINANCIAL_SERVICES_SECTOR = 'Financial Services Sector' +#: Food and Agriculture Sector +FOOD_AND_AGRICULTURE_SECTOR = 'Food and Agriculture Sector' +#: Government Facilities Sector +GOVERNMENT_FACILITIES_SECTOR = 'Government Facilities Sector' +#: Healthcare and Public Health Sector +HEALTH_CARE_AND_PUBLIC_HEALTH_SECTOR = 'Healthcare and Public Health Sector' +#: Information Technology Sector +INFORMATION_TECHNOLOGY_SECTOR = 'Information Technology Sector' +#: Nuclear Reactors, Materials, and Waste Sector +NUCLEAR_REACTORS_MATERIALS_AND_WASTE_SECTOR = 'Nuclear Reactors, Materials, and Waste Sector' +#: Other +OTHER = 'Other' +#: Transportation Systems Sector +TRANSPORTATION_SYSTEMS_SECTOR = 'Transportation Systems Sector' +#: Water and Wastewater Systems Sector +WATER_AND_WASTEWATER_SYSTEMS_SECTOR = 'Water and Wastewater Systems Sector' + + +def _validate_and_create_industry_type(industry_type): + INDUSTRY_SECTORS = (CHEMICAL_SECTOR, COMMERCIAL_FACILITIES_SECTOR, + COMMUNICATIONS_SECTOR, CRITICAL_MANUFACTURING_SECTOR, + DAMS_SECTOR, DEFENSE_INDUSTRIAL_BASE_SECTOR, + EMERGENCY_SERVICES_SECTOR, ENERGY_SECTOR, + FINANCIAL_SERVICES_SECTOR, FOOD_AND_AGRICULTURE_SECTOR, + GOVERNMENT_FACILITIES_SECTOR, + HEALTH_CARE_AND_PUBLIC_HEALTH_SECTOR, + INFORMATION_TECHNOLOGY_SECTOR, + NUCLEAR_REACTORS_MATERIALS_AND_WASTE_SECTOR, + TRANSPORTATION_SYSTEMS_SECTOR, OTHER, + WATER_AND_WASTEWATER_SYSTEMS_SECTOR) + + lower_case_sectors = tuple(x.lower() for x in INDUSTRY_SECTORS) + result = "" + error = False + val = [] + + if isinstance(industry_type, str): + # Pipe-delimited or single string supplied. + val = [x.lower().strip() for x in industry_type.split("|")] + + elif isinstance(industry_type, (list, tuple)): + # Create pipe-delimited string when list of strings is provided. + val = [x.lower().strip() for x in industry_type] + + else: + error = True + + for item in val: + for idx, sector in enumerate(lower_case_sectors): + if item == sector: + if not result: + result = INDUSTRY_SECTORS[idx] + else: + result = "{0}|{1}".format(result, INDUSTRY_SECTORS[idx]) + break + else: + # The sectors collection was exhausted. No match found. + error = True + break + + if not error and val: + return result + + msg = 'IndustryType must be one of the following: {0}. Received \'{1}\'.' + raise ValueError(msg.format(INDUSTRY_SECTORS, industry_type)) + + +def add_ais_marking(stix_package, proprietary, consent, color, **kwargs): + """ + This utility functions aids in the creation of an AIS marking and appends + it to the provided STIX package. + + Args: + stix_package: A stix.core.STIXPackage object. + proprietary: True if marking uses IsProprietary, False for + NotProprietary. + consent: A string with one of the following values: "EVERYONE", "NONE" + or "USG". + color: A string that corresponds to TLP values: "WHITE", "GREEN" or + "AMBER". + **kwargs: Six required keyword arguments that are used to create a CIQ + identity object. These are: country_name_code, + country_name_code_type, admin_area_name_code, + admin_area_name_code_type, organisation_name, industry_type. + + Raises: + ValueError: When keyword arguments are missing. User did not supply + correct values for: proprietary, color and consent. + + Note: + The following line is required to register the AIS extension:: + + >>> import stix.extensions.marking.ais + + Any Markings under STIX Header will be removed. Please follow the + guidelines for `AIS`_. + + The industry_type keyword argument accepts: a list of string based on + defined sectors, a pipe-delimited string of sectors, or a single + sector. + + .. _AIS: + https://www.us-cert.gov/ais + + """ + from stix.common import InformationSource + from stix.extensions.identity.ciq_identity_3_0 import ( + CIQIdentity3_0Instance, STIXCIQIdentity3_0, PartyName, Address, + Country, NameElement, OrganisationInfo, AdministrativeArea) + from stix.core.stix_header import STIXHeader + from stix.data_marking import MarkingSpecification, Marking + + args = ('country_name_code', 'country_name_code_type', 'industry_type', + 'admin_area_name_code', 'admin_area_name_code_type', + 'organisation_name') + + diff = set(args) - set(kwargs.keys()) + + if diff: + msg = 'All keyword arguments must be provided. Missing: {0}' + raise ValueError(msg.format(tuple(diff))) + + party_name = PartyName() + party_name.add_organisation_name(kwargs['organisation_name']) + + country = Country() + country_name = NameElement() + country_name.name_code = kwargs['country_name_code'] + country_name.name_code_type = kwargs['country_name_code_type'] + country.add_name_element(country_name) + + admin_area = AdministrativeArea() + admin_area_name = NameElement() + admin_area_name.name_code = kwargs['admin_area_name_code'] + admin_area_name.name_code_type = kwargs['admin_area_name_code_type'] + admin_area.add_name_element(admin_area_name) + + address = Address() + address.country = country + address.administrative_area = admin_area + + org_info = OrganisationInfo() + org_info.industry_type = _validate_and_create_industry_type(kwargs['industry_type']) + + id_spec = STIXCIQIdentity3_0() + id_spec.party_name = party_name + id_spec.add_address(address) + id_spec.organisation_info = org_info + + identity = CIQIdentity3_0Instance() + identity.specification = id_spec + + if proprietary is True: + proprietary_obj = IsProprietary() + consent = 'EVERYONE' + elif proprietary is False: + proprietary_obj = NotProprietary() + else: + raise ValueError('proprietary expected True or False.') + + proprietary_obj.ais_consent = AISConsentType(consent=consent) + proprietary_obj.tlp_marking = TLPMarkingType(color=color) + + ais_marking = AISMarkingStructure() + + if isinstance(proprietary_obj, IsProprietary): + ais_marking.is_proprietary = proprietary_obj + else: + ais_marking.not_proprietary = proprietary_obj + + marking_spec = MarkingSpecification() + marking_spec.controlled_structure = '//node() | //@*' + marking_spec.marking_structures.append(ais_marking) + marking_spec.information_source = InformationSource() + marking_spec.information_source.identity = identity + + if not stix_package.stix_header: + stix_package.stix_header = STIXHeader() + + # Removes any other Markings if present. + stix_package.stix_header.handling = Marking() + stix_package.stix_header.handling.add_marking(marking_spec) diff --git a/stix/extensions/marking/simple_marking.py b/stix/extensions/marking/simple_marking.py index 9e072f02..3e0f5b53 100644 --- a/stix/extensions/marking/simple_marking.py +++ b/stix/extensions/marking/simple_marking.py @@ -1,65 +1,22 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -import stix.bindings.extensions.marking.simple_marking as simple_marking_binding -import stix.data_marking +from mixbox import fields + +import stix from stix.data_marking import MarkingStructure +import stix.bindings.extensions.marking.simple_marking as simple_marking_binding +@stix.register_extension class SimpleMarkingStructure(MarkingStructure): _binding = simple_marking_binding _binding_class = simple_marking_binding.SimpleMarkingStructureType _namespace = 'http://data-marking.mitre.org/extensions/MarkingStructure#Simple-1' _XSI_TYPE = "simpleMarking:SimpleMarkingStructureType" + statement = fields.TypedField("Statement") + def __init__(self, statement=None): super(SimpleMarkingStructure, self).__init__() self.statement = statement - - def to_obj(self, return_obj=None, ns_info=None): - super(SimpleMarkingStructure, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - MarkingStructure.to_obj(self, return_obj=return_obj, ns_info=ns_info) - return_obj.Statement = self.statement - - return return_obj - - def to_dict(self): - d = MarkingStructure.to_dict(self) - if self.statement: - d['statement'] = self.statement - - return d - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - MarkingStructure.from_obj(obj, return_obj) - return_obj.statement = obj.Statement - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - - if not return_obj: - return_obj = cls() - - MarkingStructure.from_dict(d, return_obj=return_obj) - return_obj.statement = d.get('statement') - - return return_obj - - -# Register extension -stix.data_marking.add_extension(SimpleMarkingStructure) diff --git a/stix/extensions/marking/terms_of_use_marking.py b/stix/extensions/marking/terms_of_use_marking.py index 72eadd17..da12f5cc 100644 --- a/stix/extensions/marking/terms_of_use_marking.py +++ b/stix/extensions/marking/terms_of_use_marking.py @@ -1,66 +1,22 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -import stix.bindings.extensions.marking.terms_of_use_marking as tou_marking_binding -import stix.data_marking +from mixbox import fields + +import stix from stix.data_marking import MarkingStructure +import stix.bindings.extensions.marking.terms_of_use_marking as tou_marking_binding +@stix.register_extension class TermsOfUseMarkingStructure(MarkingStructure): _binding = tou_marking_binding _binding_class = tou_marking_binding.TermsOfUseMarkingStructureType _namespace = 'http://data-marking.mitre.org/extensions/MarkingStructure#Terms_Of_Use-1' _XSI_TYPE = "TOUMarking:TermsOfUseMarkingStructureType" + terms_of_use = fields.TypedField("Terms_Of_Use") + def __init__(self, terms_of_use=None): super(TermsOfUseMarkingStructure, self).__init__() self.terms_of_use = terms_of_use - - def to_obj(self, return_obj=None, ns_info=None): - super(TermsOfUseMarkingStructure, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - MarkingStructure.to_obj(self, return_obj=return_obj, ns_info=ns_info) - return_obj.Terms_Of_Use = self.terms_of_use - - return return_obj - - def to_dict(self): - d = MarkingStructure.to_dict(self) - - if self.terms_of_use: - d['terms_of_use'] = self.terms_of_use - - return d - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - MarkingStructure.from_obj(obj, return_obj=return_obj) - return_obj.terms_of_use = obj.Terms_Of_Use - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - - if not return_obj: - return_obj = cls() - - MarkingStructure.from_dict(d, return_obj) - return_obj.terms_of_use = d.get('terms_of_use') - - return return_obj - - -# Register extension -stix.data_marking.add_extension(TermsOfUseMarkingStructure) diff --git a/stix/extensions/marking/tlp.py b/stix/extensions/marking/tlp.py index 9476d87a..6b95d7fd 100644 --- a/stix/extensions/marking/tlp.py +++ b/stix/extensions/marking/tlp.py @@ -1,65 +1,22 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -import stix.bindings.extensions.marking.tlp as tlp_binding -import stix.data_marking +from mixbox import fields + +import stix from stix.data_marking import MarkingStructure +import stix.bindings.extensions.marking.tlp as tlp_binding +@stix.register_extension class TLPMarkingStructure(MarkingStructure): _binding = tlp_binding _binding_class = tlp_binding.TLPMarkingStructureType _namespace = 'http://data-marking.mitre.org/extensions/MarkingStructure#TLP-1' _XSI_TYPE = "tlpMarking:TLPMarkingStructureType" + color = fields.TypedField("color") + def __init__(self, color=None): super(TLPMarkingStructure, self).__init__() self.color = color - - def to_obj(self, return_obj=None, ns_info=None): - super(TLPMarkingStructure, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - MarkingStructure.to_obj(self, return_obj=return_obj, ns_info=ns_info) - return_obj.color = self.color - - return return_obj - - def to_dict(self): - d = MarkingStructure.to_dict(self) - if self.color: - d['color'] = self.color - - return d - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - MarkingStructure.from_obj(obj, return_obj=return_obj) - return_obj.color = obj.color - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - - if not return_obj: - return_obj = cls() - - MarkingStructure.from_dict(d, return_obj) - return_obj.color = d.get('color') - - return return_obj - - -# Register extension -stix.data_marking.add_extension(TLPMarkingStructure) diff --git a/stix/extensions/structured_coa/__init__.py b/stix/extensions/structured_coa/__init__.py index 9a9569c5..2e7a26b0 100644 --- a/stix/extensions/structured_coa/__init__.py +++ b/stix/extensions/structured_coa/__init__.py @@ -1,2 +1,3 @@ # Copyright (c) 2015, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. + diff --git a/stix/extensions/structured_coa/generic_structured_coa.py b/stix/extensions/structured_coa/generic_structured_coa.py index c83ee5c2..549196d8 100644 --- a/stix/extensions/structured_coa/generic_structured_coa.py +++ b/stix/extensions/structured_coa/generic_structured_coa.py @@ -1,101 +1,30 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# external +from mixbox import fields + +# internal import stix -import stix.utils -import stix.coa.structured_coa -from stix.common import EncodedCDATA, StructuredText, VocabString +from stix.common import EncodedCDATA, StructuredText +from stix.common.vocabs import VocabField from stix.coa.structured_coa import _BaseStructuredCOA + +# bindings import stix.bindings.extensions.structured_coa.generic as generic_structured_coa_binding +@stix.register_extension class GenericStructuredCOA(_BaseStructuredCOA): _namespace = "http://stix.mitre.org/extensions/StructuredCOA#Generic-1" _binding = generic_structured_coa_binding _binding_class = _binding.GenericStructuredCOAType _XSI_TYPE = "genericStructuredCOA:GenericStructuredCOAType" + specification = fields.TypedField("Specification", EncodedCDATA) + description = fields.TypedField("Description", StructuredText) + reference_location = fields.TypedField("reference_location") + type_ = VocabField("Type") + def __init__(self, id_=None, idref=None): super(GenericStructuredCOA, self).__init__(id_=id_, idref=idref) - self.reference_location = None - self.description = None - self.type_ = None - self.specification = None - - @property - def specification(self): - return self._specification - - @specification.setter - def specification(self, value): - self._set_var(EncodedCDATA, specification=value) - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - @property - def type_(self): - return self._type - - @type_.setter - def type_(self, value): - self._set_vocab(type=value) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - super(GenericStructuredCOA, cls).from_obj(obj, return_obj) - return_obj.reference_location = obj.reference_location - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.type_ = VocabString.from_obj(obj.Type) - return_obj.specification = EncodedCDATA.from_obj(obj.Specification) - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(GenericStructuredCOA, self).to_obj(return_obj=return_obj, ns_info=ns_info) - if self.reference_location: - return_obj.reference_location = self.reference_location - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.type_: - return_obj.Type = self.type_.to_obj(ns_info=ns_info) - if self.specification: - return_obj.Specification = self.specification.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - super(GenericStructuredCOA, cls).from_dict(d, return_obj) - return_obj.reference_location = d.get('reference_location') - return_obj.description = StructuredText.from_dict(d.get('description')) - return_obj.type_ = VocabString.from_dict(d.get('type')) - return_obj.specification = EncodedCDATA.from_dict(d.get('specification')) - - return return_obj - - def to_dict(self): - return super(GenericStructuredCOA, self).to_dict() - - -# Register the extension -stix.coa.structured_coa.add_extension(GenericStructuredCOA) diff --git a/stix/extensions/test_mechanism/__init__.py b/stix/extensions/test_mechanism/__init__.py index 9a9569c5..2e7a26b0 100644 --- a/stix/extensions/test_mechanism/__init__.py +++ b/stix/extensions/test_mechanism/__init__.py @@ -1,2 +1,3 @@ # Copyright (c) 2015, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. + diff --git a/stix/extensions/test_mechanism/generic_test_mechanism.py b/stix/extensions/test_mechanism/generic_test_mechanism.py index f0edba67..dd20d433 100644 --- a/stix/extensions/test_mechanism/generic_test_mechanism.py +++ b/stix/extensions/test_mechanism/generic_test_mechanism.py @@ -1,125 +1,27 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix -import stix.utils import stix.indicator.test_mechanism +from stix.common.vocabs import VocabField from stix.common import EncodedCDATA, StructuredText, VocabString from stix.indicator.test_mechanism import _BaseTestMechanism import stix.bindings.extensions.test_mechanism.generic as generic_tm_binding +@stix.register_extension class GenericTestMechanism(_BaseTestMechanism): _namespace = "http://stix.mitre.org/extensions/TestMechanism#Generic-1" _binding = generic_tm_binding _binding_class = _binding.GenericTestMechanismType _XSI_TYPE = "genericTM:GenericTestMechanismType" + reference_location = fields.TypedField("reference_location") + description = fields.TypedField("Description", StructuredText) + specification = fields.TypedField("Specification", EncodedCDATA) + type_ = VocabField("Type") + def __init__(self, id_=None, idref=None): super(GenericTestMechanism, self).__init__(id_=id_, idref=idref) - self.reference_location = None - self.description = None - self.type_ = None - self.specification = None - - @property - def specification(self): - return self._specification - - @specification.setter - def specification(self, value): - if not value: - self._specification = None - if isinstance(value, EncodedCDATA): - self._specification = value - else: - self._specification = EncodedCDATA(value=value) - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - if not value: - self._description = None - elif isinstance(value, StructuredText): - self._description = value - else: - self._description = StructuredText(value) - - @property - def type_(self): - return self._type - - @type_.setter - def type_(self, value): - if not value: - self._type = None - elif isinstance(value, VocabString): - self._type = value - else: - self._type = VocabString(value) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - super(GenericTestMechanism, cls).from_obj(obj, return_obj) - return_obj.reference_location = obj.reference_location - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.type_ = VocabString.from_obj(obj.Type) - return_obj.specification = EncodedCDATA.from_obj(obj.Specification) - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(GenericTestMechanism, self).to_obj(return_obj=return_obj, ns_info=ns_info) - if self.reference_location: - return_obj.reference_location = self.reference_location - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.type_: - return_obj.Type = self.type_.to_obj(ns_info=ns_info) - if self.specification: - return_obj.Specification = self.specification.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - super(GenericTestMechanism, cls).from_dict(d, return_obj) - return_obj.reference_location = d.get('reference_location') - return_obj.description = StructuredText.from_dict(d.get('description')) - return_obj.type_ = VocabString.from_dict(d.get('type')) - return_obj.specification = EncodedCDATA.from_dict(d.get('specification')) - - return return_obj - - def to_dict(self): - d = super(GenericTestMechanism, self).to_dict() - - if self.reference_location: - d['reference_location'] = self.reference_location - if self.description: - d['description'] = self.description.to_dict() - if self.type_: - d['type'] = self.type_.to_dict() - if self.specification: - d['specification'] = self.specification.to_dict() - - return d - - -stix.indicator.test_mechanism.add_extension(GenericTestMechanism) diff --git a/stix/extensions/test_mechanism/open_ioc_2010_test_mechanism.py b/stix/extensions/test_mechanism/open_ioc_2010_test_mechanism.py index 44a7f37b..71ce210d 100644 --- a/stix/extensions/test_mechanism/open_ioc_2010_test_mechanism.py +++ b/stix/extensions/test_mechanism/open_ioc_2010_test_mechanism.py @@ -1,20 +1,19 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -# stdlib -from StringIO import StringIO - # external from lxml import etree +import mixbox.xml +from mixbox.fields import TypedField +from mixbox.vendor.six import BytesIO, iteritems # internal import stix -import stix.utils.parser as parser -import stix.indicator.test_mechanism from stix.indicator.test_mechanism import _BaseTestMechanism import stix.bindings.extensions.test_mechanism.open_ioc_2010 as open_ioc_tm_binding +@stix.register_extension class OpenIOCTestMechanism(_BaseTestMechanism): _namespace = "http://stix.mitre.org/extensions/TestMechanism#OpenIOC2010-1" _binding = open_ioc_tm_binding @@ -23,41 +22,23 @@ class OpenIOCTestMechanism(_BaseTestMechanism): _XSI_TYPE = "stix-openioc:OpenIOC2010TestMechanismType" _TAG_IOC = "{%s}ioc" % _namespace + ioc = TypedField("ioc") + def __init__(self, id_=None, idref=None): super(OpenIOCTestMechanism, self).__init__(id_=id_, idref=idref) self.ioc = None self.__input_namespaces__ = {} self.__input_schemalocations__ = {} - @property - def ioc(self): - return self._ioc - - @ioc.setter - def ioc(self, value): - if value is None: - self._ioc = None - return - - tree = parser.get_etree(value) - root = parser.get_etree_root(tree) - - if root.tag != self._TAG_IOC: - self._cast_ioc(root) - - self._collect_namespaces(root) - self._collect_schemalocs(root) - self._ioc = tree - def _collect_schemalocs(self, node): try: - schemaloc = parser.get_schemaloc_pairs(node) + schemaloc = mixbox.xml.get_schemaloc_pairs(node) self.__input_schemalocations__ = dict(schemaloc) except KeyError: self.__input_schemalocations__ = {} def _collect_namespaces(self, node): - self.__input_namespaces__ = dict(node.nsmap.iteritems()) + self.__input_namespaces__ = dict(iteritems(node.nsmap)) def _cast_ioc(self, node): ns_ioc = "http://schemas.mandiant.com/2010/ioc" @@ -71,14 +52,26 @@ def _cast_ioc(self, node): error = error.format(self._TAG_IOC, node.tag) raise ValueError(error) + def _processed_ioc(self): + if self.ioc is None: + return None + + tree = mixbox.xml.get_etree(self.ioc) + root = mixbox.xml.get_etree_root(tree) + + if root.tag != self._TAG_IOC: + self._cast_ioc(root) + + self._collect_namespaces(root) + self._collect_schemalocs(root) + return tree + @classmethod - def from_obj(cls, obj, return_obj=None): + def from_obj(cls, obj): if not obj: return None - if not return_obj: - return_obj = cls() - super(OpenIOCTestMechanism, cls).from_obj(obj, return_obj) + return_obj = super(OpenIOCTestMechanism, cls).from_obj(obj) return_obj.ioc = obj.ioc return return_obj @@ -86,21 +79,20 @@ def to_obj(self, return_obj=None, ns_info=None): if not return_obj: return_obj = self._binding_class() - super(OpenIOCTestMechanism, self).to_obj(return_obj=return_obj, ns_info=ns_info) - return_obj.ioc = self.ioc + super(OpenIOCTestMechanism, self).to_obj(ns_info=ns_info) + return_obj.ioc = self._processed_ioc() return return_obj @classmethod - def from_dict(cls, d, return_obj=None): + def from_dict(cls, d): if not d: return None - if not return_obj: - return_obj = cls() - - super(OpenIOCTestMechanism, cls).from_dict(d, return_obj) + + return_obj = super(OpenIOCTestMechanism, cls).from_dict(d) + if 'ioc' in d: - parser = stix.utils.parser.get_xml_parser() - return_obj.ioc = etree.parse(StringIO(d['ioc']), parser=parser) + parser = mixbox.xml.get_xml_parser() + return_obj.ioc = etree.parse(BytesIO(d['ioc']), parser=parser) return return_obj @@ -108,8 +100,6 @@ def to_dict(self): d = super(OpenIOCTestMechanism, self).to_dict() if self.ioc: - d['ioc'] = etree.tostring(self.ioc) + d['ioc'] = etree.tostring(self._processed_ioc()) return d - -stix.indicator.test_mechanism.add_extension(OpenIOCTestMechanism) diff --git a/stix/extensions/test_mechanism/snort_test_mechanism.py b/stix/extensions/test_mechanism/snort_test_mechanism.py index 944ce88e..7e3658a9 100644 --- a/stix/extensions/test_mechanism/snort_test_mechanism.py +++ b/stix/extensions/test_mechanism/snort_test_mechanism.py @@ -1,135 +1,27 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix from stix.common import EncodedCDATA from stix.indicator import test_mechanism import stix.bindings.extensions.test_mechanism.snort as snort_tm_binding +@stix.register_extension class SnortTestMechanism(test_mechanism._BaseTestMechanism): _namespace = "http://stix.mitre.org/extensions/TestMechanism#Snort-1" _binding = snort_tm_binding _binding_class = _binding.SnortTestMechanismType _XSI_TYPE = "snortTM:SnortTestMechanismType" - - def __init__(self, id_=None, idref=None): - super(SnortTestMechanism, self).__init__(id_=id_, idref=idref) - self.product_name = None - self.version = None - self.rules = None - self.event_filters = None - self.rate_filters = None - self.event_suppressions = None - - @property - def rules(self): - return self._rules - - @rules.setter - def rules(self, value): - self._rules = _EncodedCDATAs(value) - - def add_rule(self, rule): - self.rules.append(rule) - - @property - def event_filters(self): - return self._event_filters - - @event_filters.setter - def event_filters(self, value): - self._event_filters = _EncodedCDATAs(value) - - def add_event_filter(self, item): - self.event_filters.append(item) - - @property - def rate_filters(self): - return self._rate_filters - - @rate_filters.setter - def rate_filters(self, value): - self._rate_filters = _EncodedCDATAs(value) - - def add_rate_filter(self, item): - self.rate_filters.append(item) - - @property - def event_suppressions(self): - return self._event_suppressions - - @event_suppressions.setter - def event_suppressions(self, value): - self._event_suppressions = _EncodedCDATAs(value) - - def add_event_suppression(self, item): - self.event_suppressions.append(item) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - super(SnortTestMechanism, cls).from_obj(obj, return_obj) - return_obj.product_name = obj.Product_Name - return_obj.version = obj.Version - return_obj.rules = _EncodedCDATAs.from_obj(obj.Rule) - return_obj.event_filters = _EncodedCDATAs.from_obj(obj.Event_Filter) - return_obj.rate_filters = _EncodedCDATAs.from_obj(obj.Rate_Filter) - return_obj.event_suppressions = _EncodedCDATAs.from_obj(obj.Event_Suppression) - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(SnortTestMechanism, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - return_obj.Product_Name = self.product_name - return_obj.Version = self.version - - if self.rules: - return_obj.Rule = self.rules.to_obj(ns_info=ns_info) - if self.event_filters: - return_obj.Event_Filter = self.event_filters.to_obj(ns_info=ns_info) - if self.rate_filters: - return_obj.Rate_Filter = self.rate_filters.to_obj(ns_info=ns_info) - if self.event_suppressions: - return_obj.Event_Suppression = self.event_suppressions.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - super(SnortTestMechanism, cls).from_dict(d, return_obj) - - get = d.get - return_obj.product_name = get('product_name') - return_obj.version = get('version') - return_obj.rules = _EncodedCDATAs.from_dict(get('rules')) - return_obj.event_filters = _EncodedCDATAs.from_dict(get('event_filters')) - return_obj.rate_filters = _EncodedCDATAs.from_dict(get('rate_filters')) - return_obj.event_suppressions = _EncodedCDATAs.from_dict(get('event_suppressions')) - - return return_obj - def to_dict(self): - return super(SnortTestMechanism, self).to_dict() + product_name = fields.TypedField("Product_Name", EncodedCDATA) + version = fields.TypedField("Version", EncodedCDATA) + rules = fields.TypedField("Rule", EncodedCDATA, multiple=True, key_name="rules") + event_filters = fields.TypedField("Event_Filter", EncodedCDATA, multiple=True, key_name="event_filters") + rate_filters = fields.TypedField("Rate_Filter", EncodedCDATA, multiple=True, key_name="rate_filters") + event_suppressions = fields.TypedField("Event_Suppression", EncodedCDATA, multiple=True, key_name="event_suppressions") - -# Not an actual STIX data type! -class _EncodedCDATAs(stix.TypedList): - _contained_type = EncodedCDATA - - -# Register this extension -test_mechanism.add_extension(SnortTestMechanism) + def __init__(self, id_=None, idref=None): + super(SnortTestMechanism, self).__init__(id_=id_, idref=idref) diff --git a/stix/extensions/test_mechanism/yara_test_mechanism.py b/stix/extensions/test_mechanism/yara_test_mechanism.py index 79f5891e..35edcbcf 100644 --- a/stix/extensions/test_mechanism/yara_test_mechanism.py +++ b/stix/extensions/test_mechanism/yara_test_mechanism.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix import stix.utils import stix.indicator.test_mechanism @@ -9,77 +11,15 @@ import stix.bindings.extensions.test_mechanism.yara as yara_tm_binding +@stix.register_extension class YaraTestMechanism(_BaseTestMechanism): _namespace = "http://stix.mitre.org/extensions/TestMechanism#YARA-1" _binding = yara_tm_binding _binding_class = _binding.YaraTestMechanismType _XSI_TYPE = "yaraTM:YaraTestMechanismType" - + + version = fields.TypedField("Version") + rule = fields.TypedField("Rule", EncodedCDATA) + def __init__(self, id_=None, idref=None): super(YaraTestMechanism, self).__init__(id_=id_, idref=idref) - self.version = None - self.rule = None - - @property - def rule(self): - return self._rule - - @rule.setter - def rule(self, value): - if not value: - self._rule = None - if isinstance(value, EncodedCDATA): - self._rule = value - else: - self._rule = EncodedCDATA(value=value) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - super(YaraTestMechanism, cls).from_obj(obj, return_obj) - return_obj.version = obj.Version - return_obj.rule = EncodedCDATA.from_obj(obj.Rule) - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(YaraTestMechanism, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if self.version: - return_obj.Version = self.version - if self.rule: - return_obj.Rule = self.rule.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - super(YaraTestMechanism, cls).from_dict(d, return_obj) - return_obj.version = d.get('version') - return_obj.rule = EncodedCDATA.from_dict(d.get('rule')) - - return return_obj - - def to_dict(self): - d = super(YaraTestMechanism, self).to_dict() - - if self.version: - d['version'] = self.version - if self.rule: - d['rule'] = self.rule.to_dict() - - return d - -stix.indicator.test_mechanism.add_extension(YaraTestMechanism) diff --git a/stix/incident/__init__.py b/stix/incident/__init__.py index 26f96008..b2ee2d9f 100644 --- a/stix/incident/__init__.py +++ b/stix/incident/__init__.py @@ -1,18 +1,18 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields import stix import stix.bindings.incident as incident_binding -from stix.common import ( - vocabs, Identity, Statement, VocabString, - InformationSource, Confidence -) -from stix.common.related import ( - GenericRelationshipList, RelatedIndicator, RelatedThreatActor, RelatedTTP, - RelatedObservable, RelatedIncident -) -from stix.data_marking import Marking +from stix.common import vocabs +from stix.common import Statement, VocabString, InformationSource, Confidence +from stix.common.vocabs import VocabField +from stix.common.statement import StatementField +from stix.common.identity import Identity, IdentityFactory +from stix.common.related import (GenericRelationshipList, RelatedIndicator, + RelatedThreatActor, RelatedTTP, RelatedObservable, RelatedIncident, + RelatedPackageRefs) # relative from .affected_asset import AffectedAsset @@ -20,11 +20,27 @@ from .time import Time from .external_id import ExternalID from .impact_assessment import ImpactAssessment -from .coa import COATaken, COARequested, COATime # noqa +from .coa import COATaken, COARequested, COATime # noqa from .history import History class Incident(stix.BaseCoreComponent): + """Implementation of the STIX Incident. + + Args: + id_ (optional): An identifier. If ``None``, a value will be generated + via ``mixbox.idgen.create_id()``. If set, this will unset the + ``idref`` property. + idref (optional): An identifier reference. If set this will unset the + ``id_`` property. + timestamp (optional): A timestamp value. Can be an instance of + ``datetime.datetime`` or ``str``. + description: A description of the purpose or intent of this object. + short_description: A short description of the intent + or purpose of this object. + title: The title of this object. + + """ _binding = incident_binding _binding_class = _binding.IncidentType _namespace = "http://stix.mitre.org/Incident-1" @@ -32,7 +48,35 @@ class Incident(stix.BaseCoreComponent): _ALL_VERSIONS = ("1.0", "1.0.1", "1.1", "1.1.1") _ID_PREFIX = 'incident' - def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): + status = vocabs.VocabField("Status", vocabs.IncidentStatus) + time = fields.TypedField("Time", Time) + victims = fields.TypedField("Victim", Identity, factory=IdentityFactory, multiple=True, key_name="victims") + attributed_threat_actors = fields.TypedField("Attributed_Threat_Actors", type_="stix.incident.AttributedThreatActors") + related_indicators = fields.TypedField("Related_Indicators", type_="stix.incident.RelatedIndicators") + related_observables = fields.TypedField("Related_Observables", type_="stix.incident.RelatedObservables") + related_incidents = fields.TypedField("Related_Incidents", type_="stix.incident.RelatedIncidents") + related_packages = fields.TypedField("Related_Packages", RelatedPackageRefs) + affected_assets = fields.TypedField("Affected_Assets", type_="stix.incident.AffectedAssets") + categories = fields.TypedField("Categories", type_="stix.incident.IncidentCategories") + intended_effects = StatementField("Intended_Effect", Statement, vocab_type=vocabs.IntendedEffect, multiple=True, key_name="intended_effects") + leveraged_ttps = fields.TypedField("Leveraged_TTPs", type_="stix.incident.LeveragedTTPs") + discovery_methods = vocabs.VocabField("Discovery_Method", vocabs.DiscoveryMethod, multiple=True, key_name="discovery_methods") + reporter = fields.TypedField("Reporter", InformationSource) + responders = fields.TypedField("Responder", InformationSource, multiple=True, key_name="responders") + coordinators = fields.TypedField("Coordinator", InformationSource, multiple=True, key_name="coordinators") + external_ids = fields.TypedField("External_ID", ExternalID, multiple=True, key_name="external_ids") + impact_assessment = fields.TypedField("Impact_Assessment", ImpactAssessment) + security_compromise = vocabs.VocabField("Security_Compromise", vocabs.SecurityCompromise) + confidence = fields.TypedField("Confidence", Confidence) + coa_taken = fields.TypedField("COA_Taken", COATaken, multiple=True) + coa_requested = fields.TypedField("COA_Requested", COARequested, multiple=True) + history = fields.TypedField("History", History) + information_source = fields.TypedField("Information_Source", InformationSource) + url = fields.TypedField("URL") + contacts = fields.TypedField("Contact", InformationSource, multiple=True, key_name="contacts") + + def __init__(self, id_=None, idref=None, timestamp=None, title=None, + description=None, short_description=None): super(Incident, self).__init__( id_=id_, idref=idref, @@ -41,214 +85,108 @@ def __init__(self, id_=None, idref=None, timestamp=None, title=None, description description=description, short_description=short_description ) - - self.status = None - self.time = None - self.victims = None - self.attributed_threat_actors = AttributedThreatActors() self.related_indicators = RelatedIndicators() self.related_observables = RelatedObservables() self.related_incidents = RelatedIncidents() - self.affected_assets = None - self.categories = None - self.intended_effects = None + self.related_packages = RelatedPackageRefs() + self.categories = IncidentCategories() + self.affected_assets = AffectedAssets() self.leveraged_ttps = LeveragedTTPs() - self.discovery_methods = None - self.reporter = None - self.responders = None - self.coordinators = None - self.external_ids = None - self.impact_assessment = None - self.security_compromise = None - self.confidence = None - self.coa_taken = None - self.coa_requested = None - self.handling = None - self.history = History() - - - @property - def status(self): - return self._status - - @status.setter - def status(self, value): - self._set_vocab(vocabs.IncidentStatus, status=value) - - @property - def time(self): - return self._time - - @time.setter - def time(self, value): - self._set_var(Time, try_cast=False, time=value) - - @property - def handling(self): - return self._handling - - @handling.setter - def handling(self, value): - self._set_var(Marking, try_cast=False, handling=value) - @property - def intended_effects(self): - return self._intended_effects + def add_intended_effect(self, value): + """Adds a :class:`.Statement` object to the :attr:`intended_effects` + collection. - @intended_effects.setter - def intended_effects(self, value): - self._intended_effects = _IntendedEffects(value) + If `value` is a string, an attempt will be made to convert it into an + instance of :class:`.Statement`. - def add_intended_effect(self, value): + """ self.intended_effects.append(value) - @property - def victims(self): - return self._victims + def add_leveraged_ttps(self, ttp): + """Adds a :class:`.RelatedTTP` value to the :attr:`leveraged_ttps` + collection. - @victims.setter - def victims(self, value): - self._victims = _Victims(value) + """ + self.leveraged_ttps.append(ttp) def add_victim(self, victim): - self._victims.append(victim) + """Adds a :class:`.IdentityType` value to the :attr:`victims` + collection. - @property - def categories(self): - return self._categories - - @categories.setter - def categories(self, value): - self._categories = IncidentCategories(value) + """ + self.victims.append(victim) def add_category(self, category): + """Adds a :class:`.VocabString` object to the :attr:`categories` + collection. + + If `category` is a string, an attempt will be made to convert it into + an instance of :class:`.IncidentCategory`. + + """ self.categories.append(category) - @property - def affected_assets(self): - return self._affected_assets - - @affected_assets.setter - def affected_assets(self, value): - self._affected_assets = AffectedAssets(value) - def add_affected_asset(self, v): - self.affected_assets.append(v) + """Adds a :class:`.AffectedAsset` object to the :attr:`affected_assets` + collection. - @property - def discovery_methods(self): - return self._discovery_methods - - @discovery_methods.setter - def discovery_methods(self, value): - self._discovery_methods = DiscoveryMethods(value) + """ + self.affected_assets.append(v) def add_discovery_method(self, value): - self.discovery_methods.append(value) - - @property - def reporter(self): - return self._reporter + """Adds a :class:`.VocabString` object to the :attr:`discovery_methods` + collection. - @reporter.setter - def reporter(self, value): - self._set_var(InformationSource, try_cast=False, reporter=value) + If `value` is a string, an attempt will be made to convert it to an + instance of :class:`.DiscoveryMethod`. - @property - def responders(self): - return self._responders - - @responders.setter - def responders(self, value): - self._responders = _InformationSources(value) + """ + self.discovery_methods.append(value) def add_responder(self, value): - self.responders.append(value) - - @property - def coordinators(self): - return self._coordinators + """Adds a :class:`.InformationSource` object to the :attr:`responders` + collection. - @coordinators.setter - def coordinators(self, value): - self._coordinators = _InformationSources(value) + """ + self.responders.append(value) def add_coordinator(self, value): - self.coordinators.append(value) - - @property - def external_ids(self): - return self._external_ids + """Adds a :class:`.InformationSource` object to the :attr:`coordinators` + collection. - @external_ids.setter - def external_ids(self, value): - self._external_ids = _ExternalIDs(value) + """ + self.coordinators.append(value) def add_external_id(self, value): - self.external_ids.append(value) - - @property - def impact_assessment(self): - return self._impact_assessment - - @impact_assessment.setter - def impact_assessment(self, value): - self._set_var(ImpactAssessment, try_cast=False, impact_assessment=value) - - @property - def security_compromise(self): - return self._security_compromise - - @security_compromise.setter - def security_compromise(self, value): - self._set_vocab(vocabs.SecurityCompromise, security_compromise=value) + """Adds a :class:`.ExternalID` object to the :attr:`external_ids` + collection. - @property - def confidence(self): - return self._confidence - - @confidence.setter - def confidence(self, value): - self._set_var(Confidence, confidence=value) - - @property - def coa_taken(self): - return self._coa_taken - - @coa_taken.setter - def coa_taken(self, value): - self._coa_taken = _COAsTaken(value) + """ + self.external_ids.append(value) def add_coa_taken(self, value): - self.coa_taken.append(value) - - @property - def coa_requested(self): - return self._coa_requested + """Adds a :class:`.COATaken` object to the :attr:`coas_taken` + collection. - @coa_requested.setter - def coa_requested(self, value): - self._coa_requested = _COAsRequested(value) + """ + self.coa_taken.append(value) def add_coa_requested(self, value): - self.coa_requested.append(value) + """Adds a :class:`.COARequested` object to the :attr:`coas_requested` + collection. - @property - def related_indicators(self): - return self._related_indicators - - @related_indicators.setter - def related_indicators(self, value): - self._set_var(RelatedIndicators, related_indicators=value) + """ + self.coa_requested.append(value) def add_related_indicator(self, value): - """Adds an Related Indicator to the ``related_indicators`` list + """Adds an Related Indicator to the :attr:`related_indicators` list property of this :class:`Incident`. The `indicator` parameter must be an instance of :class:`.RelatedIndicator` or :class:`Indicator`. - If the `indicator` parameter is ``None``, no item wil be added to the + If the `indicator` parameter is ``None``, no item will be added to the ``related_indicators`` list property. Calling this method is the same as calling ``append()`` on the @@ -263,7 +201,7 @@ def add_related_indicator(self, value): made to convert it to one. Args: - indicator: An instance of :class:`Indicator` or + value: An instance of :class:`Indicator` or :class:`.RelatedIndicator`. Raises: @@ -273,14 +211,6 @@ def add_related_indicator(self, value): """ self.related_indicators.append(value) - @property - def related_observables(self): - return self._related_observables - - @related_observables.setter - def related_observables(self, value): - self._set_var(RelatedObservables, related_observables=value) - def add_related_observable(self, value): """Adds a Related Observable to the ``related_observables`` list property of this :class:`Incident`. @@ -313,241 +243,99 @@ def add_related_observable(self, value): """ self.related_observables.append(value) - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(Incident, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if self.time: - return_obj.Time = self.time.to_obj(ns_info=ns_info) - if self.victims: - return_obj.Victim = self.victims.to_obj(ns_info=ns_info) - if self.attributed_threat_actors: - return_obj.Attributed_Threat_Actors = self.attributed_threat_actors.to_obj(ns_info=ns_info) - if self.related_indicators: - return_obj.Related_Indicators = self.related_indicators.to_obj(ns_info=ns_info) - if self.related_observables: - return_obj.Related_Observables = self.related_observables.to_obj(ns_info=ns_info) - if self.related_incidents: - return_obj.Related_Incidents = self.related_incidents.to_obj(ns_info=ns_info) - if self.categories: - return_obj.Categories = self.categories.to_obj(ns_info=ns_info) - if self.intended_effects: - return_obj.Intended_Effect = self.intended_effects.to_obj(ns_info=ns_info) - if self.leveraged_ttps: - return_obj.Leveraged_TTPs = self.leveraged_ttps.to_obj(ns_info=ns_info) - if self.affected_assets: - return_obj.Affected_Assets = self.affected_assets.to_obj(ns_info=ns_info) - if self.discovery_methods: - return_obj.Discovery_Method = self.discovery_methods.to_obj(ns_info=ns_info) - if self.reporter: - return_obj.Reporter = self.reporter.to_obj(ns_info=ns_info) - if self.responders: - return_obj.Responder = self.responders.to_obj(ns_info=ns_info) - if self.coordinators: - return_obj.Coordinator = self.coordinators.to_obj(ns_info=ns_info) - if self.external_ids: - return_obj.External_ID = self.external_ids.to_obj(ns_info=ns_info) - if self.impact_assessment: - return_obj.Impact_Assessment = self.impact_assessment.to_obj(ns_info=ns_info) - if self.security_compromise: - return_obj.Security_Compromise = self.security_compromise.to_obj(ns_info=ns_info) - if self.confidence: - return_obj.Confidence = self.confidence.to_obj(ns_info=ns_info) - if self.coa_taken: - return_obj.COA_Taken = self.coa_taken.to_obj(ns_info=ns_info) - if self.coa_requested: - return_obj.COA_Requested = self.coa_requested.to_obj(ns_info=ns_info) - if self.status: - return_obj.Status = self.status.to_obj(ns_info=ns_info) - if self.handling: - return_obj.Handling = self.handling.to_obj(ns_info=ns_info) - if self.history: - return_obj.History = self.history.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - super(Incident, cls).from_obj(obj, return_obj=return_obj) - - if isinstance(obj, cls._binding_class): - return_obj.time = Time.from_obj(obj.Time) - return_obj.victims = _Victims.from_obj(obj.Victim) - return_obj.categories = IncidentCategories.from_obj(obj.Categories) - return_obj.intended_effects = _IntendedEffects.from_obj(obj.Intended_Effect) - return_obj.affected_assets = AffectedAssets.from_obj(obj.Affected_Assets) - return_obj.discovery_methods = DiscoveryMethods.from_obj(obj.Discovery_Method) - return_obj.coa_taken = _COAsTaken.from_obj(obj.COA_Taken) - return_obj.coa_requested = _COAsRequested.from_obj(obj.COA_Requested) - return_obj.confidence = Confidence.from_obj(obj.Confidence) - return_obj.attributed_threat_actors = AttributedThreatActors.from_obj(obj.Attributed_Threat_Actors) - return_obj.related_indicators = RelatedIndicators.from_obj(obj.Related_Indicators) - return_obj.related_observables = RelatedObservables.from_obj(obj.Related_Observables) - return_obj.leveraged_ttps = LeveragedTTPs.from_obj(obj.Leveraged_TTPs) - return_obj.related_incidents = RelatedIncidents.from_obj(obj.Related_Incidents) - return_obj.status = VocabString.from_obj(obj.Status) - return_obj.handling = Marking.from_obj(obj.Handling) - return_obj.history = History.from_obj(obj.History) - return_obj.responders = _InformationSources.from_obj(obj.Responder) - return_obj.coordinators = _InformationSources.from_obj(obj.Coordinator) - return_obj.external_ids = _ExternalIDs.from_obj(obj.External_ID) - return_obj.reporter = InformationSource.from_obj(obj.Reporter) - return_obj.impact_assessment = ImpactAssessment.from_obj(obj.Impact_Assessment) - return_obj.security_compromise = VocabString.from_obj(obj.Security_Compromise) - - return return_obj - - def to_dict(self): - return super(Incident, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - super(Incident, cls).from_dict(dict_repr, return_obj=return_obj) - - get = dict_repr.get - return_obj.time = Time.from_dict(get('time')) - return_obj.victims = _Victims.from_dict(get('victims')) - return_obj.categories = IncidentCategories.from_dict(get('categories')) - return_obj.attributed_threat_actors = AttributedThreatActors.from_dict(get('attributed_threat_actors')) - return_obj.related_indicators = RelatedIndicators.from_dict(get('related_indicators')) - return_obj.related_observables = RelatedObservables.from_dict(get('related_observables')) - return_obj.related_incidents = RelatedIncidents.from_dict(get('related_incidents')) - return_obj.intended_effects = _IntendedEffects.from_list(get('intended_effects')) - return_obj.leveraged_ttps = LeveragedTTPs.from_dict(get('leveraged_ttps')) - return_obj.affected_assets = AffectedAssets.from_dict(get('affected_assets')) - return_obj.discovery_methods = DiscoveryMethods.from_dict(get('discovery_methods')) - return_obj.reporter = InformationSource.from_dict(get('reporter')) - return_obj.responders = _InformationSources.from_dict(get('responders')) - return_obj.coordinators = _InformationSources.from_dict(get('coordinators')) - return_obj.external_ids = _ExternalIDs.from_dict(get('external_ids')) - return_obj.impact_assessment = ImpactAssessment.from_dict(get('impact_assessment')) - return_obj.security_compromise = VocabString.from_dict(get('security_compromise')) - return_obj.confidence = Confidence.from_dict(get('confidence')) - return_obj.coa_taken = _COAsTaken.from_dict(get('coa_taken')) - return_obj.coa_requested = _COAsRequested.from_dict(get('coa_requested')) - return_obj.status = VocabString.from_dict(get('status')) - return_obj.handling = Marking.from_dict(get('handling')) - return_obj.history = History.from_dict(get('history')) - - return return_obj + def add_related_package(self, value): + self.related_packages.append(value) + + def add_related_incidents(self, value): + self.related_incidents.append(value) class AttributedThreatActors(GenericRelationshipList): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = incident_binding.AttributedThreatActorsType - _binding_var = "Threat_Actor" - _contained_type = RelatedThreatActor - _inner_name = "threat_actors" + + threat_actor = fields.TypedField( + name="Threat_Actor", + type_=RelatedThreatActor, + multiple=True, + key_name="threat_actors" + ) class RelatedIndicators(GenericRelationshipList): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = incident_binding.RelatedIndicatorsType - _binding_var = "Related_Indicator" - _contained_type = RelatedIndicator - _inner_name = "indicators" + + indicator = fields.TypedField( + name="Related_Indicator", + type_=RelatedIndicator, + multiple=True, + key_name="indicators" + ) class RelatedObservables(GenericRelationshipList): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = incident_binding.RelatedObservablesType - _binding_var = "Related_Observable" - _contained_type = RelatedObservable - _inner_name = "observables" + + observable = fields.TypedField( + name="Related_Observable", + type_=RelatedObservable, + multiple=True, + key_name="observables" + ) class LeveragedTTPs(GenericRelationshipList): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = _binding.LeveragedTTPsType - _binding_var = "Leveraged_TTP" - _contained_type = RelatedTTP - _inner_name = "ttps" + + ttp = fields.TypedField( + name="Leveraged_TTP", + type_=RelatedTTP, + multiple=True, + key_name="ttps" + ) class RelatedIncidents(GenericRelationshipList): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = incident_binding.RelatedIncidentsType - _binding_var = "Related_Incident" - _contained_type = RelatedIncident - _inner_name = "incidents" + + incident = fields.TypedField( + name="Related_Incident", + type_=RelatedIncident, + multiple=True, + key_name="incidents" + ) class IncidentCategories(stix.EntityList): _namespace = "http://stix.mitre.org/Incident-1" - _contained_type = VocabString _binding = incident_binding _binding_class = _binding.CategoriesType - _binding_var = "Category" - _inner_name = "categories" - _dict_as_list = True - def _fix_value(self, value): - return vocabs.IncidentCategory(value) + category = VocabField( + name="Category", + type_=vocabs.IncidentCategory, + multiple=True, + key_name="categories" + ) class AffectedAssets(stix.EntityList): _namespace = "http://stix.mitre.org/Incident-1" - _contained_type = AffectedAsset _binding = incident_binding _binding_class = _binding.AffectedAssetsType - _binding_var = "Affected_Asset" - _inner_name = "affected_assets" - _dict_as_list = True - - -# NOT ACTUAL STIX TYPES! - -class DiscoveryMethods(stix.TypedList): - _contained_type = VocabString - - def _fix_value(self, value): - return vocabs.DiscoveryMethod(value) - - -class _COAsTaken(stix.TypedList): - _contained_type = COATaken - - -class _COAsRequested(stix.TypedList): - _contained_type = COARequested - - -class _ExternalIDs(stix.TypedList): - _contained_type = ExternalID - - -class _InformationSources(stix.TypedList): - _contained_type = InformationSource - - -class _Victims(stix.TypedList): - _contained_type = Identity - - def _fix_value(self, value): - return Identity(name=value) - - -class _IntendedEffects(stix.TypedList): - _contained_type = Statement - def _fix_value(self, value): - return Statement(value=vocabs.IntendedEffect(value)) + affected_asset = fields.TypedField( + name="Affected_Asset", + type_=AffectedAsset, + multiple=True, + key_name="affected_assets" + ) diff --git a/stix/incident/affected_asset.py b/stix/incident/affected_asset.py index 2a5dac02..571e96f9 100644 --- a/stix/incident/affected_asset.py +++ b/stix/incident/affected_asset.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + # external from cybox.core import Observables @@ -17,221 +19,47 @@ class AffectedAsset(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = incident_binding.AffectedAssetType - + + description = fields.TypedField("Description", StructuredText) + business_function_or_roles = fields.TypedField("Business_Function_Or_Role", StructuredText) + ownership_class = vocabs.VocabField("Ownership_Class", vocabs.OwnershipClass) + management_class = vocabs.VocabField("Management_Class", vocabs.ManagementClass) + location_class = vocabs.VocabField("Location_Class", vocabs.LocationClass) + # location = fields.TypedField("Location") + nature_of_security_effect = fields.TypedField("Nature_Of_Security_Effect", type_="stix.incident.affected_asset.NatureOfSecurityEffect") + structured_description = fields.TypedField("Structured_Description", Observables) + type_ = fields.TypedField("Type", type_="stix.incident.affected_asset.AssetType", key_name="type") + def __init__(self): - self.type_ = None - self.description = None - self.business_function_or_role = None - self.ownership_class = None - self.management_class = None - self.location_class = None - # self.location = None - self.nature_of_security_effect = None - self.structured_description = None - - @property - def type_(self): - return self._type - - @type_.setter - def type_(self, value): - self._set_var(AssetType, type=value) - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - @property - def business_function_or_role(self): - return self._business_function_or_role - - @business_function_or_role.setter - def business_function_or_role(self, value): - self._set_var(StructuredText, business_function_or_role=value) - - @property - def ownership_class(self): - return self._ownership_class - - @ownership_class.setter - def ownership_class(self, value): - self._set_vocab(vocabs.OwnershipClass, ownership_class=value) - - @property - def management_class(self): - return self._management_class - - @management_class.setter - def management_class(self, value): - self._set_vocab(vocabs.ManagementClass, management_class=value) - - @property - def location_class(self): - return self._location_class - - @location_class.setter - def location_class(self, value): - self._set_vocab(vocabs.LocationClass, location_class=value) - - @property - def nature_of_security_effect(self): - return self._nature_of_security_effect - - @nature_of_security_effect.setter - def nature_of_security_effect(self, value): - self._nature_of_security_effect = NatureOfSecurityEffect(value) + super(AffectedAsset, self).__init__() def add_property_affected(self, v): self.nature_of_security_effect.append(v) - - @property - def structured_description(self): - return self._structured_description - - @structured_description.setter - def structured_description(self, value): - self._set_var(Observables, structured_description=value) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.type_ = AssetType.from_obj(obj.Type) - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.business_function_or_role = StructuredText.from_obj(obj.Business_Function_Or_Role) - return_obj.ownership_class = VocabString.from_obj(obj.Ownership_Class) - return_obj.management_class = VocabString.from_obj(obj.Management_Class) - return_obj.location_class = VocabString.from_obj(obj.Location_Class) - # return_obj.location = None - - if obj.Nature_Of_Security_Effect: - n = obj.Nature_Of_Security_Effect - return_obj.nature_of_security_effect = [PropertyAffected.from_obj(x) for x in n.Property_Affected] - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - super(AffectedAsset, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.type_: - return_obj.Type = self.type_.to_obj(ns_info=ns_info) - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.business_function_or_role: - return_obj.Business_Function_Or_Role = self.business_function_or_role.to_obj(ns_info=ns_info) - if self.ownership_class: - return_obj.Ownership_Class = self.ownership_class.to_obj(ns_info=ns_info) - if self.management_class: - return_obj.Management_Class = self.management_class.to_obj(ns_info=ns_info) - if self.location_class: - return_obj.Location_Class = self.location_class.to_obj(ns_info=ns_info) - # if self.location: - # return_obj.Location = self.location.to_obj(ns_info=ns_info) - if self.nature_of_security_effect: - property_affected_list = [x.to_obj(ns_info=ns_info) for x in self.nature_of_security_effect] - n = self._binding.NatureOfSecurityEffectType(Property_Affected=property_affected_list) - return_obj.Nature_Of_Security_Effect = n - if self.structured_description: - return_obj.Structured_Description = self.structured_description.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - get = d.get - return_obj.type_ = AssetType.from_dict(get('type')) - return_obj.description = StructuredText.from_dict(get('description')) - return_obj.business_function_or_role = StructuredText.from_dict(get('business_function_or_role')) - return_obj.ownership_class = VocabString.from_dict(get('ownership_class')) - return_obj.management_class = VocabString.from_dict(get('management_class')) - return_obj.location_class = VocabString.from_dict(get('location_class')) - # return_obj.location = Location.from_dict(get('location')) - return_obj.nature_of_security_effect = NatureOfSecurityEffect.from_dict(get('nature_of_security_effect')) - return_obj.structured_description = Observables.from_dict(get('structured_description')) - return return_obj - - def to_dict(self): - return super(AffectedAsset, self).to_dict() class AssetType(VocabString): _namespace = "http://stix.mitre.org/Incident-1" - _binding = incident_binding + _binding = incident_binding _binding_class = incident_binding.AssetTypeType - + + count_affected = fields.IntegerField("count_affected") + def __init__(self, value=None, count_affected=None): - self.count_affected = count_affected super(AssetType, self).__init__(value) - + self.count_affected = count_affected + def is_plain(self): """Override VocabString.is_plain()""" return False - @property - def count_affected(self): - return self._count_affected - - @count_affected.setter - def count_affected(self, value): - self._set_var(int, count_affected=value) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - super(AssetType, cls).from_obj(obj, return_obj=return_obj) - return_obj.count_affected = obj.count_affected - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(AssetType, self).to_obj(return_obj=return_obj, ns_info=ns_info) - return_obj.count_affected = self.count_affected - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - super(AssetType, cls).from_dict(d, return_obj=return_obj) - return_obj.count_affected = d.get('count_affected') - return return_obj - - def to_dict(self): - d = super(AssetType, self).to_dict() - if self.count_affected: - d['count_affected'] = self.count_affected - return d - class NatureOfSecurityEffect(stix.EntityList): _namespace = "http://stix.mitre.org/Incident-1" - _contained_type = PropertyAffected _binding = incident_binding _binding_class = _binding.NatureOfSecurityEffectType - _binding_var = "Property_Affected" - _inner_name = "nature_of_security_effect" - _dict_as_list = True + + nature_of_security_effect = fields.TypedField("Property_Affected", PropertyAffected, multiple=True, key_name="nature_of_security_effect") + + @classmethod + def _dict_as_list(cls): + return True diff --git a/stix/incident/coa.py b/stix/incident/coa.py index 10828b6d..3384f838 100644 --- a/stix/incident/coa.py +++ b/stix/incident/coa.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + # internal import stix import stix.utils as utils @@ -16,208 +18,42 @@ class COATaken(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = _binding.COATakenType - + + course_of_action = fields.TypedField("Course_Of_Action", CourseOfAction) + contributors = fields.TypedField("Contributors", Contributors) + time = fields.TypedField("Time", type_="stix.incident.coa.COATime") + def __init__(self, course_of_action=None): + super(COATaken, self).__init__() + self.time = None self.course_of_action = course_of_action - self.contributors = Contributors() - - @property - def time(self): - return self._time - - @time.setter - def time(self, value): - self._set_var(COATime, try_cast=False, time=value) - - @property - def course_of_action(self): - return self._course_of_action - - @course_of_action.setter - def course_of_action(self, value): - self._set_var(CourseOfAction, try_cast=False, course_of_action=value) - - @property - def contributors(self): - return self._contributors - - @contributors.setter - def contributors(self, value): - self._contributors = Contributors(value) def add_contributor(self, value): self.contributors.append(value) - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.time = COATime.from_obj(obj.Time) - return_obj.contributors = Contributors.from_obj(obj.Contributors) - return_obj.course_of_action = CourseOfAction.from_obj(obj.Course_Of_Action) - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - super(COATaken, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.time: - return_obj.Time = self.time.to_obj(ns_info=ns_info) - if self.contributors: - return_obj.Contributors = self.contributors.to_obj(ns_info=ns_info) - if self.course_of_action: - return_obj.Course_Of_Action = self.course_of_action.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - return_obj.time = COATime.from_dict(d.get('time')) - return_obj.contributors = Contributors.from_dict(d.get('contributors')) - return_obj.course_of_action = CourseOfAction.from_dict(d.get('course_of_action')) - - return return_obj - - def to_dict(self): - return super(COATaken, self).to_dict() - class COARequested(COATaken): namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = _binding.COARequestedType + priority = fields.TypedField("priority") + def __init__(self, course_of_action=None): super(COARequested, self).__init__(course_of_action=course_of_action) - self.priority = None - - @property - def priority(self): - return self._priority - - @priority.setter - def priority(self, value): - if value is None: - self._priority = None - else: - self._priority = value - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - super(COARequested, cls).from_obj(obj, return_obj=return_obj) - return_obj.priority = obj.priority - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(COARequested, self).to_obj(return_obj=return_obj, ns_info=ns_info) - return_obj.priority = self.priority - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - super(COARequested, cls).from_dict(d, return_obj=return_obj) - return_obj.priority = d.get('priority') - - return return_obj - - def to_dict(self): - d = utils.to_dict(self) - return d class COATime(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = _binding.COATimeType - + + start = fields.TypedField("Start", DateTimeWithPrecision) + end = fields.TypedField("End", DateTimeWithPrecision) + def __init__(self, start=None, end=None): + super(COATime, self).__init__() + self.start = start self.end = end - - @property - def start(self): - return self._start - - @start.setter - def start(self, value): - self._set_var(DateTimeWithPrecision, start=value) - - @property - def end(self): - return self._end - - @end.setter - def end(self, value): - self._set_var(DateTimeWithPrecision, end=value) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.start = DateTimeWithPrecision.from_obj(obj.Start) - return_obj.end = DateTimeWithPrecision.from_obj(obj.End) - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(COATime, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if self.start: - return_obj.Start = self.start.to_obj(ns_info=ns_info) - if self.end: - return_obj.End = self.end.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - return_obj.start = DateTimeWithPrecision.from_dict(d.get('start')) - return_obj.end = DateTimeWithPrecision.from_dict(d.get('end')) - return return_obj - - def to_dict(self): - d = {} - - if self.start: - d['start'] = self.start.to_dict() - if self.end: - d['end'] = self.end.to_dict() - - return d diff --git a/stix/incident/contributors.py b/stix/incident/contributors.py index d00c33cc..919f8199 100644 --- a/stix/incident/contributors.py +++ b/stix/incident/contributors.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + # external from cybox.common import Contributor @@ -14,6 +16,5 @@ class Contributors(stix.EntityList): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = _binding.ContributorsType - _contained_type = Contributor - _binding_var = "Contributor" - _inner_name = "contributors" + + contributors = fields.TypedField("Contributor", Contributor, multiple=True, key_name="contributors") \ No newline at end of file diff --git a/stix/incident/direct_impact_summary.py b/stix/incident/direct_impact_summary.py index 1f942e56..952174d5 100644 --- a/stix/incident/direct_impact_summary.py +++ b/stix/incident/direct_impact_summary.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix from stix.common import VocabString from stix.common.vocabs import ImpactRating @@ -9,77 +11,12 @@ class DirectImpactSummary(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" - _binding = incident_binding + _binding = incident_binding _binding_class = incident_binding.DirectImpactSummaryType + asset_losses = fields.TypedField("Asset_Losses", ImpactRating) + business_mission_disruption = fields.TypedField("Business_Mission_Disruption", ImpactRating) + response_and_recovery_costs = fields.TypedField("Response_And_Recovery_Costs", ImpactRating) + def __init__(self): super(DirectImpactSummary, self).__init__() - self.asset_losses = None - self.business_mission_disruption = None - self.response_and_recovery_costs = None - - @property - def asset_losses(self): - return self._asset_losses - - @asset_losses.setter - def asset_losses(self, value): - self._set_vocab(ImpactRating, asset_losses=value) - - @property - def business_mission_disruption(self): - return self._business_mission_disruption - - @business_mission_disruption.setter - def business_mission_disruption(self, value): - self._set_vocab(ImpactRating, business_mission_disruption=value) - - @property - def response_and_recovery_costs(self): - return self._response_and_recovery_costs - - @response_and_recovery_costs.setter - def response_and_recovery_costs(self, value): - self._set_vocab(ImpactRating, response_and_recovery_costs=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(DirectImpactSummary, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - obj = self._binding_class() - if self.asset_losses: - obj.Asset_Losses = self.asset_losses.to_obj(ns_info=ns_info) - if self.business_mission_disruption: - obj.Business_Mission_Disruption = self.business_mission_disruption.to_obj(ns_info=ns_info) - if self.response_and_recovery_costs: - obj.Response_And_Recovery_Costs = self.response_and_recovery_costs.to_obj(ns_info=ns_info) - return obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.asset_losses = VocabString.from_obj(obj.Asset_Losses) - return_obj.business_mission_disruption = VocabString.from_obj(obj.Business_Mission_Disruption) - return_obj.response_and_recovery_costs = VocabString.from_obj(obj.Response_And_Recovery_Costs) - return return_obj - - def to_dict(self): - return super(DirectImpactSummary, self).to_dict() - - @classmethod - def from_dict(cls, dict_, return_obj=None): - if not dict_: - return None - - if not return_obj: - return_obj = cls() - - return_obj.asset_losses = VocabString.from_dict(dict_.get('asset_losses')) - return_obj.business_mission_disruption = VocabString.from_dict(dict_.get('business_mission_disruption')) - return_obj.response_and_recovery_costs = VocabString.from_dict(dict_.get('response_and_recovery_costs')) - - return return_obj diff --git a/stix/incident/external_id.py b/stix/incident/external_id.py index dcb86d5b..bb39ccc2 100644 --- a/stix/incident/external_id.py +++ b/stix/incident/external_id.py @@ -1,74 +1,21 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix import stix.bindings.incident as incident_binding class ExternalID(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" - _binding = incident_binding + _binding = incident_binding _binding_class = incident_binding.ExternalIDType + value = fields.TypedField("valueOf_", key_name="value") + source = fields.TypedField("source") + def __init__(self, value=None, source=None): super(ExternalID, self).__init__() self.value = value self.source = source - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._value = value - - @property - def source(self): - return self._source - - @source.setter - def source(self, value): - self._source = value - - def to_obj(self, return_obj=None, ns_info=None): - super(ExternalID, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - obj = self._binding_class() - obj.valueOf_ = self.value - obj.source = self.source - return obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.value = obj.valueOf_ - return_obj.source = obj.source - return return_obj - - def to_dict(self): - d = {} - d['value'] = self.value - d['source'] = self.source - return d - - @classmethod - def from_dict(cls, dict_, return_obj=None): - if not dict_: - return None - - if not return_obj: - return_obj = cls() - - if not isinstance(dict_, dict): - return_obj.value = dict_ - else: - return_obj.source = dict_.get('source') - return_obj.value = dict_.get('value') - - return return_obj diff --git a/stix/incident/history.py b/stix/incident/history.py index 9650a007..bffb4fb9 100644 --- a/stix/incident/history.py +++ b/stix/incident/history.py @@ -1,9 +1,10 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + # internal import stix -import stix.utils as utils import stix.bindings.incident as incident_binding from stix.common.datetimewithprecision import DATETIME_PRECISION_VALUES @@ -11,157 +12,48 @@ from .coa import COATaken +def validate_precision(instance, value): + if value and (value not in DATETIME_PRECISION_VALUES): + error = "time_precision must be one of {0}. Received '{1}'" + error = error.format(DATETIME_PRECISION_VALUES, value) + raise ValueError(error) + + class JournalEntry(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = incident_binding.JournalEntryType - + + value = fields.TypedField("valueOf_", key_name="value") + author = fields.TypedField("author") + time = fields.DateTimeField("time") + time_precision = fields.TypedField("time_precision", preset_hook=validate_precision) + def __init__(self, value=None): + super(JournalEntry, self).__init__() self.value = value - self.author = None - self.time = None self.time_precision = 'second' - - @property - def time(self): - return self._time - - @time.setter - def time(self, value): - self._time = utils.dates.parse_value(value) - - @property - def time_precision(self): - return self._time_precision - - @time_precision.setter - def time_precision(self, value): - if value and (value not in DATETIME_PRECISION_VALUES): - error = "time_precision must be one of {0}. Received '{1}'" - error = error.format(DATETIME_PRECISION_VALUES, value) - raise ValueError(error) - - self._time_precision = value - - def to_obj(self, return_obj=None, ns_info=None): - super(JournalEntry, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.valueOf_ = self.value - return_obj.author = self.author - return_obj.time = utils.dates.serialize_value(self.time) - return_obj.time_precision = self.time_precision - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.value = obj.valueOf_ - return_obj.author = obj.author - return_obj.time = obj.time - return_obj.time_precision = obj.time_precision - - return return_obj - - def to_dict(self): - return super(JournalEntry, self).to_dict() - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - - if not return_obj: - return_obj = cls() - - return_obj.value = d.get('value') - return_obj.author = d.get('author') - return_obj.time = d.get('time') - return_obj.time_precision = d.get('time_precision') - - return return_obj class HistoryItem(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = incident_binding.HistoryItemType - + + action_entry = fields.TypedField("Action_Entry", COATaken) + journal_entry = fields.TypedField("Journal_Entry", JournalEntry) + def __init__(self): - self.action_entry = None - self.journal_entry = None - - @property - def action_entry(self): - return self._action_entry - - @action_entry.setter - def action_entry(self, value): - self._set_var(COATaken, try_cast=False, action_entry=value) - - @property - def journal_entry(self): - return self._journal_entry - - @journal_entry.setter - def journal_entry(self, value): - self._set_var(JournalEntry, journal_entry=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(HistoryItem, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.action_entry: - return_obj.Action_Entry = self.action_entry.to_obj(ns_info=ns_info) - if self.journal_entry: - return_obj.Journal_Entry = self.journal_entry.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.action_entry = COATaken.from_obj(obj.Action_Entry) - return_obj.journal_entry = JournalEntry.from_obj(obj.Journal_Entry) - - return return_obj - - def to_dict(self): - return super(HistoryItem, self).to_dict() - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - - if not return_obj: - return_obj = cls() - - return_obj.action_entry = COATaken.from_dict(d.get('action_entry')) - return_obj.journal_entry = JournalEntry.from_dict(d.get('journal_entry')) - - return return_obj + super(HistoryItem, self).__init__() class History(stix.EntityList): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = incident_binding.HistoryType - _binding_var = "History_Item" - _contained_type = HistoryItem - _inner_name = "history_items" + + history_items = fields.TypedField("History_Item", HistoryItem, multiple=True, key_name="history_items") + + @classmethod + def _dict_as_list(cls): + return False diff --git a/stix/incident/impact_assessment.py b/stix/incident/impact_assessment.py index 2f106e30..52ed2b53 100644 --- a/stix/incident/impact_assessment.py +++ b/stix/incident/impact_assessment.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + # internal import stix import stix.bindings.incident as incident_binding @@ -11,12 +13,20 @@ from .indirect_impact_summary import IndirectImpactSummary from .total_loss_estimation import TotalLossEstimation +from stix.common.vocabs import VocabField + class ImpactAssessment(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" - _binding = incident_binding + _binding = incident_binding _binding_class = incident_binding.ImpactAssessmentType + direct_impact_summary = fields.TypedField("Direct_Impact_Summary", DirectImpactSummary) + indirect_impact_summary = fields.TypedField("Indirect_Impact_Summary", IndirectImpactSummary) + total_loss_estimation = fields.TypedField("Total_Loss_Estimation", TotalLossEstimation) + impact_qualification = vocabs.VocabField("Impact_Qualification", vocabs.ImpactQualification) + effects = fields.TypedField("Effects", type_="stix.incident.impact_assessment.Effects") + def __init__(self): super(ImpactAssessment, self).__init__() self.direct_impact_summary = None @@ -26,111 +36,17 @@ def __init__(self): self.effects = None # self.external_impact_assessment_model = None - @property - def effects(self): - return self._effects - - @effects.setter - def effects(self, value): - self._effects = Effects(value) - - def add_effect(self, value): - self.effects.append(value) - - @property - def direct_impact_summary(self): - return self._direct_impact_summary - - @direct_impact_summary.setter - def direct_impact_summary(self, value): - self._set_var(DirectImpactSummary, try_cast=False, direct_impact_summary=value) - - @property - def indirect_impact_summary(self): - return self._indirect_impact_summary - - @indirect_impact_summary.setter - def indirect_impact_summary(self, value): - self._set_var(IndirectImpactSummary, try_cast=False, indirect_impact_summary=value) - - @property - def total_loss_estimation(self): - return self._total_loss_estimation - - @total_loss_estimation.setter - def total_loss_estimation(self, value): - self._set_var(TotalLossEstimation, try_cast=False, total_loss_estimation=value) - - @property - def impact_qualification(self): - return self._impact_qualification - - @impact_qualification.setter - def impact_qualification(self, value): - self._set_vocab(vocabs.ImpactQualification, impact_qualification=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(ImpactAssessment, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - obj = self._binding_class() - - if self.direct_impact_summary: - obj.Direct_Impact_Summary = self.direct_impact_summary.to_obj(ns_info=ns_info) - if self.indirect_impact_summary: - obj.Indirect_Impact_Summary = self.indirect_impact_summary.to_obj(ns_info=ns_info) - if self.total_loss_estimation: - obj.Total_Loss_Estimation = self.total_loss_estimation.to_obj(ns_info=ns_info) - if self.impact_qualification: - obj.Impact_Qualification = self.impact_qualification.to_obj(ns_info=ns_info) - if self.effects: - obj.Effects = self.effects.to_obj(ns_info=ns_info) - - return obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.direct_impact_summary = DirectImpactSummary.from_obj(obj.Direct_Impact_Summary) - return_obj.indirect_impact_summary = IndirectImpactSummary.from_obj(obj.Indirect_Impact_Summary) - return_obj.total_loss_estimation = TotalLossEstimation.from_obj(obj.Total_Loss_Estimation) - return_obj.impact_qualification = VocabString.from_obj(obj.Impact_Qualification) - return_obj.effects = Effects.from_obj(obj.Effects) - - return return_obj - - def to_dict(self): - return super(ImpactAssessment, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - get = dict_repr.get - return_obj.direct_impact_summary = DirectImpactSummary.from_dict(get('direct_impact_summary')) - return_obj.indirect_impact_summary = IndirectImpactSummary.from_dict(get('indirect_impact_summary')) - return_obj.total_loss_estimation = TotalLossEstimation.from_dict(get('total_loss_estimation')) - return_obj.impact_qualification = VocabString.from_dict(get('impact_qualification')) - return_obj.effects = Effects.from_dict(get('effects')) - - return return_obj - class Effects(stix.EntityList): _namespace = "http://stix.mitre.org/Incident-1" - _contained_type = VocabString _binding = incident_binding _binding_class = _binding.EffectsType - _inner_name = "effects" - _binding_var = "Effect" - _dict_as_list = True + + effects = VocabField("Effect", VocabString, multiple=True, key_name="effects") + + @classmethod + def _dict_as_list(cls): + return True def _fix_value(self, value): return vocabs.IncidentEffect(value=value) diff --git a/stix/incident/indirect_impact_summary.py b/stix/incident/indirect_impact_summary.py index 9c89a945..93d339f5 100644 --- a/stix/incident/indirect_impact_summary.py +++ b/stix/incident/indirect_impact_summary.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix import stix.bindings.incident as incident_binding from stix.common import vocabs, VocabString @@ -8,90 +10,13 @@ class IndirectImpactSummary(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" - _binding = incident_binding + _binding = incident_binding _binding_class = incident_binding.IndirectImpactSummaryType + loss_of_competitive_advantage = vocabs.VocabField("Loss_Of_Competitive_Advantage", vocabs.SecurityCompromise) + brand_and_market_damage = vocabs.VocabField("Brand_And_Market_Damage", vocabs.SecurityCompromise) + increased_operating_costs = vocabs.VocabField("Increased_Operating_Costs", vocabs.SecurityCompromise) + legal_and_regulatory_costs = vocabs.VocabField("Legal_And_Regulatory_Costs", vocabs.SecurityCompromise) + def __init__(self): super(IndirectImpactSummary, self).__init__() - self.loss_of_competitive_advantage = None - self.brand_and_market_damage = None - self.increased_operating_costs = None - self.legal_and_regulatory_costs = None - - @property - def loss_of_competitive_advantage(self): - return self._loss_of_competitive_advantage - - @loss_of_competitive_advantage.setter - def loss_of_competitive_advantage(self, value): - self._set_vocab(vocabs.SecurityCompromise, loss_of_competitive_advantage=value) - - @property - def brand_and_market_damage(self): - return self._brand_and_market_damage - - @brand_and_market_damage.setter - def brand_and_market_damage(self, value): - self._set_vocab(vocabs.SecurityCompromise, brand_and_market_damage=value) - - @property - def increased_operating_costs(self): - return self._increased_operating_costs - - @increased_operating_costs.setter - def increased_operating_costs(self, value): - self._set_vocab(vocabs.SecurityCompromise, increased_operating_costs=value) - - @property - def legal_and_regulatory_costs(self): - return self._legal_and_regulatory_costs - - @legal_and_regulatory_costs.setter - def legal_and_regulatory_costs(self, value): - self._set_vocab(vocabs.SecurityCompromise, legal_and_regulatory_costs=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(IndirectImpactSummary, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - obj = self._binding_class() - if self.loss_of_competitive_advantage: - obj.Loss_Of_Competitive_Advantage = self.loss_of_competitive_advantage.to_obj(ns_info=ns_info) - if self.brand_and_market_damage: - obj.Brand_And_Market_Damage = self.brand_and_market_damage.to_obj(ns_info=ns_info) - if self.increased_operating_costs: - obj.Increased_Operating_Costs = self.increased_operating_costs.to_obj(ns_info=ns_info) - if self.legal_and_regulatory_costs: - obj.Legal_And_Regulatory_Costs = self.legal_and_regulatory_costs.to_obj(ns_info=ns_info) - return obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.loss_of_competitive_advantage = VocabString.from_obj(obj.Loss_Of_Competitive_Advantage) - return_obj.brand_and_market_damage = VocabString.from_obj(obj.Brand_And_Market_Damage) - return_obj.increased_operating_costs = VocabString.from_obj(obj.Increased_Operating_Costs) - return_obj.legal_and_regulatory_costs = VocabString.from_obj(obj.Legal_And_Regulatory_Costs) - return return_obj - - def to_dict(self): - return super(IndirectImpactSummary, self).to_dict() - - @classmethod - def from_dict(cls, dict_, return_obj=None): - if not dict_: - return None - - if not return_obj: - return_obj = cls() - - return_obj.loss_of_competitive_advantage = VocabString.from_dict(dict_.get('loss_of_competitive_advantage')) - return_obj.brand_and_market_damage = VocabString.from_dict(dict_.get('brand_and_market_damage')) - return_obj.increased_operating_costs = VocabString.from_dict(dict_.get('increased_operating_costs')) - return_obj.legal_and_regulatory_costs = VocabString.from_dict(dict_.get('legal_and_regulatory_costs')) - - return return_obj diff --git a/stix/incident/loss_estimation.py b/stix/incident/loss_estimation.py index dc9540d3..51a682d0 100644 --- a/stix/incident/loss_estimation.py +++ b/stix/incident/loss_estimation.py @@ -1,70 +1,19 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix import stix.bindings.incident as incident_binding class LossEstimation(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" - _binding = incident_binding + _binding = incident_binding _binding_class = incident_binding.LossEstimationType + iso_currency_code = fields.TypedField("iso_currency_code") + amount = fields.TypedField("amount") + def __init__(self): super(LossEstimation, self).__init__() - self.iso_currency_code = None - self.amount = None - - @property - def amount(self): - return self._amount - - @amount.setter - def amount(self, value): - self._amount = value - - @property - def iso_currency_code(self): - return self._iso_currency_code - - @iso_currency_code.setter - def iso_currency_code(self, value): - self._iso_currency_code = value - - def to_obj(self, return_obj=None, ns_info=None): - super(LossEstimation, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - obj = self._binding_class() - if self.amount: - obj.amount = self.amount - if self.iso_currency_code: - obj.iso_currency_code = self.iso_currency_code - return obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.amount = obj.amount - return_obj.iso_currency_code = obj.iso_currency_code - return return_obj - - def to_dict(self): - return super(LossEstimation, self).to_dict() - - @classmethod - def from_dict(cls, dict_, return_obj=None): - if not dict_: - return None - - if not return_obj: - return_obj = cls() - - return_obj.amount = dict_.get('amount') - return_obj.iso_currency_code = dict_.get('iso_currency_code') - - return return_obj diff --git a/stix/incident/property_affected.py b/stix/incident/property_affected.py index a699ef37..00e674d9 100644 --- a/stix/incident/property_affected.py +++ b/stix/incident/property_affected.py @@ -1,6 +1,7 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields import stix from stix.common import vocabs, VocabString, StructuredText @@ -9,156 +10,29 @@ class NonPublicDataCompromised(VocabString): _namespace = "http://stix.mitre.org/Incident-1" - _binding = incident_binding + _binding = incident_binding _binding_class = incident_binding.NonPublicDataCompromisedType - - def __init__(self, value=None, data_encrypted=None): - self.data_encrypted = data_encrypted - super(NonPublicDataCompromised, self).__init__(value) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - super(NonPublicDataCompromised, cls).from_obj(obj, return_obj=return_obj) - return_obj.data_encrypted = obj.data_encrypted - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(NonPublicDataCompromised, self).to_obj(return_obj=return_obj, ns_info=ns_info) - return_obj.data_encrypted = self.data_encrypted - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None + data_encrypted = fields.TypedField("data_encrypted") - if not return_obj: - return_obj = cls() - - super(NonPublicDataCompromised, cls).from_dict(d, return_obj=return_obj) - return_obj.data_encrypted = d.get('data_encrypted') - return return_obj + def __init__(self, value=None, data_encrypted=None): + super(NonPublicDataCompromised, self).__init__(value) + self.data_encrypted = data_encrypted def is_plain(self): return False - def to_dict(self): - d = super(NonPublicDataCompromised, self).to_dict() - - if self.data_encrypted: - d['data_encrypted'] = self.data_encrypted - - return d - class PropertyAffected(stix.Entity): _namespace = "http://stix.mitre.org/Incident-1" _binding = incident_binding _binding_class = incident_binding.PropertyAffectedType - - def __init__(self): - self.property_ = None - self.description_of_effect = None - self.type_of_availability_loss = None - self.duration_of_availability_loss = None - self.non_public_data_compromised = None - - @property - def property_(self): - return self._property - - @property_.setter - def property_(self, value): - self._set_vocab(vocabs.LossProperty, property=value) - - @property - def description_of_effect(self): - return self._description_of_effect - - @description_of_effect.setter - def description_of_effect(self, value): - self._set_var(StructuredText, description_of_effect=value) - @property - def type_of_availability_loss(self): - return self._type_of_availability_loss - - @type_of_availability_loss.setter - def type_of_availability_loss(self, value): - self._set_vocab(vocabs.AvailabilityLossType, type_of_availability_loss=value) - - @property - def duration_of_availability_loss(self): - return self._duration_of_availability_loss - - @duration_of_availability_loss.setter - def duration_of_availability_loss(self, value): - self._set_vocab(vocabs.LossDuration, duration_of_availability_loss=value) - - @property - def non_public_data_compromised(self): - return self._non_public_data_compromised - - @non_public_data_compromised.setter - def non_public_data_compromised(self, value): - self._set_var(NonPublicDataCompromised, non_public_data_compromised=value) - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.property_ = VocabString.from_obj(obj.Property) - return_obj.description_of_effect = StructuredText.from_obj(obj.Description_Of_Effect) - return_obj.type_of_availability_loss = VocabString.from_obj(obj.Type_Of_Availability_Loss) - return_obj.duration_of_availability_loss = VocabString.from_obj(obj.Duration_Of_Availability_Loss) - return_obj.non_public_data_compromised = NonPublicDataCompromised.from_obj(obj.Non_Public_Data_Compromised) - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - super(PropertyAffected, self).to_obj(return_obj=return_obj, ns_info=ns_info) + property_ = vocabs.VocabField("Property", vocabs.LossProperty, key_name="property") + descriptions_of_effect = fields.TypedField("Description_Of_Effect", StructuredText) + type_of_availability_loss = vocabs.VocabField("Type_Of_Availability_Loss", vocabs.AvailabilityLossType) + duration_of_availability_loss = vocabs.VocabField("Duration_Of_Availability_Loss", vocabs.LossDuration) + non_public_data_compromised = fields.TypedField("Non_Public_Data_Compromised", NonPublicDataCompromised) - if not return_obj: - return_obj = self._binding_class() - - if self.property_: - return_obj.Property = self.property_.to_obj(ns_info=ns_info) - if self.description_of_effect: - return_obj.Description_Of_Effect = self.description_of_effect.to_obj(ns_info=ns_info) - if self.type_of_availability_loss: - return_obj.Type_Of_Availability_Loss = self.type_of_availability_loss.to_obj(ns_info=ns_info) - if self.duration_of_availability_loss: - return_obj.Duration_Of_Availability_Loss = self.duration_of_availability_loss.to_obj(ns_info=ns_info) - if self.non_public_data_compromised: - return_obj.Non_Public_Data_Compromised = self.non_public_data_compromised.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if not return_obj: - return_obj = cls() - - return_obj.property_ = VocabString.from_dict(d.get('property')) - return_obj.description_of_effect = StructuredText.from_dict(d.get('description_of_effect')) - return_obj.type_of_availability_loss = VocabString.from_dict(d.get('type_of_availability_loss')) - return_obj.duration_of_availability_loss = VocabString.from_dict(d.get('duration_of_availability_loss')) - return_obj.non_public_data_compromised = NonPublicDataCompromised.from_dict(d.get('non_public_data_compromised')) - - return return_obj - - def to_dict(self): - return super(PropertyAffected, self).to_dict() + def __init__(self): + super(PropertyAffected, self).__init__() diff --git a/stix/incident/time.py b/stix/incident/time.py index d794b070..42cca222 100644 --- a/stix/incident/time.py +++ b/stix/incident/time.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix import stix.bindings.incident as incident_binding from stix.common import DateTimeWithPrecision @@ -11,11 +13,22 @@ class Time(stix.Entity): _binding_class = _binding.TimeType _namespace = "http://stix.mitre.org/Incident-1" + first_malicious_action = fields.TypedField("First_Malicious_Action", DateTimeWithPrecision) + initial_compromise = fields.TypedField("Initial_Compromise", DateTimeWithPrecision) + first_data_exfiltration = fields.TypedField("First_Data_Exfiltration", DateTimeWithPrecision) + incident_discovery = fields.TypedField("Incident_Discovery", DateTimeWithPrecision) + incident_opened = fields.TypedField("Incident_Opened", DateTimeWithPrecision) + containment_achieved = fields.TypedField("Containment_Achieved", DateTimeWithPrecision) + restoration_achieved = fields.TypedField("Restoration_Achieved", DateTimeWithPrecision) + incident_reported = fields.TypedField("Incident_Reported", DateTimeWithPrecision) + incident_closed = fields.TypedField("Incident_Closed", DateTimeWithPrecision) + def __init__(self, first_malicious_action=None, initial_compromise=None, first_data_exfiltration=None, incident_discovery=None, incident_opened=None, containment_achieved=None, restoration_achieved=None, incident_reported=None, incident_closed=None): + super(Time, self).__init__() self.first_malicious_action = first_malicious_action self.initial_compromise = initial_compromise @@ -26,145 +39,3 @@ def __init__(self, first_malicious_action=None, initial_compromise=None, self.restoration_achieved = restoration_achieved self.incident_reported = incident_reported self.incident_closed = incident_closed - - @property - def first_malicious_action(self): - return self._first_malicious_action - - @first_malicious_action.setter - def first_malicious_action(self, value): - self._set_var(DateTimeWithPrecision, first_malicious_action=value) - - @property - def initial_compromise(self): - return self._initial_compromise - - @initial_compromise.setter - def initial_compromise(self, value): - self._set_var(DateTimeWithPrecision, initial_compromise=value) - - @property - def first_data_exfiltration(self): - return self._first_data_exfiltration - - @first_data_exfiltration.setter - def first_data_exfiltration(self, value): - self._set_var(DateTimeWithPrecision, first_data_exfiltration=value) - - @property - def incident_discovery(self): - return self._incident_discovery - - @incident_discovery.setter - def incident_discovery(self, value): - self._set_var(DateTimeWithPrecision, incident_discovery=value) - - @property - def incident_opened(self): - return self._incident_opened - - @incident_opened.setter - def incident_opened(self, value): - self._set_var(DateTimeWithPrecision, incident_opened=value) - - @property - def containment_achieved(self): - return self._containment_achieved - - @containment_achieved.setter - def containment_achieved(self, value): - self._set_var(DateTimeWithPrecision, containment_achieved=value) - - @property - def restoration_achieved(self): - return self._restoration_achieved - - @restoration_achieved.setter - def restoration_achieved(self, value): - self._set_var(DateTimeWithPrecision, restoration_achieved=value) - - @property - def incident_reported(self): - return self._incident_reported - - @incident_reported.setter - def incident_reported(self, value): - self._set_var(DateTimeWithPrecision, incident_reported=value) - - @property - def incident_closed(self): - return self._incident_closed - - @incident_closed.setter - def incident_closed(self, value): - self._set_var(DateTimeWithPrecision, incident_closed=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(Time, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.first_malicious_action: - return_obj.First_Malicious_Action = self.first_malicious_action.to_obj(ns_info=ns_info) - if self.initial_compromise: - return_obj.Initial_Compromise = self.initial_compromise.to_obj(ns_info=ns_info) - if self.first_data_exfiltration: - return_obj.First_Data_Exfiltration = self.first_data_exfiltration.to_obj(ns_info=ns_info) - if self._incident_discovery: - return_obj.Incident_Discovery = self.incident_discovery.to_obj(ns_info=ns_info) - if self.incident_opened: - return_obj.Incident_Opened = self.incident_opened.to_obj(ns_info=ns_info) - if self.containment_achieved: - return_obj.Containment_Achieved = self.containment_achieved.to_obj(ns_info=ns_info) - if self.restoration_achieved: - return_obj.Restoration_Achieved = self.restoration_achieved.to_obj(ns_info=ns_info) - if self.incident_reported: - return_obj.Incident_Reported = self.incident_reported.to_obj(ns_info=ns_info) - if self.incident_closed: - return_obj.Incident_Closed = self.incident_closed.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.first_malicious_action = DateTimeWithPrecision.from_obj(obj.First_Malicious_Action) - return_obj.initial_compromise = DateTimeWithPrecision.from_obj(obj.Initial_Compromise) - return_obj.first_data_exfiltration = DateTimeWithPrecision.from_obj(obj.First_Data_Exfiltration) - return_obj.incident_discovery = DateTimeWithPrecision.from_obj(obj.Incident_Discovery) - return_obj.incident_opened = DateTimeWithPrecision.from_obj(obj.Incident_Opened) - return_obj.containment_achieved = DateTimeWithPrecision.from_obj(obj.Containment_Achieved) - return_obj.restoration_achieved = DateTimeWithPrecision.from_obj(obj.Restoration_Achieved) - return_obj.incident_reported = DateTimeWithPrecision.from_obj(obj.Incident_Reported) - return_obj.incident_closed = DateTimeWithPrecision.from_obj(obj.Incident_Closed) - - return return_obj - - def to_dict(self): - return super(Time, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - return_obj.first_malicious_action = DateTimeWithPrecision.from_dict(dict_repr.get('first_malicious_action')) - return_obj.initial_compromise = DateTimeWithPrecision.from_dict(dict_repr.get('initial_compromise')) - return_obj.first_data_exfiltration = DateTimeWithPrecision.from_dict(dict_repr.get('first_data_exfiltration')) - return_obj.incident_discovery = DateTimeWithPrecision.from_dict(dict_repr.get('incident_discovery')) - return_obj.incident_opened = DateTimeWithPrecision.from_dict(dict_repr.get('incident_opened')) - return_obj.containment_achieved = DateTimeWithPrecision.from_dict(dict_repr.get('containment_achieved')) - return_obj.restoration_achieved = DateTimeWithPrecision.from_dict(dict_repr.get('restoration_achieved')) - return_obj.incident_reported = DateTimeWithPrecision.from_dict(dict_repr.get('incident_reported')) - return_obj.incident_closed = DateTimeWithPrecision.from_dict(dict_repr.get('incident_closed')) - - return return_obj diff --git a/stix/incident/total_loss_estimation.py b/stix/incident/total_loss_estimation.py index 60a9b061..be70d8e7 100644 --- a/stix/incident/total_loss_estimation.py +++ b/stix/incident/total_loss_estimation.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + # internal import stix import stix.bindings.incident as incident_binding @@ -14,61 +16,8 @@ class TotalLossEstimation(stix.Entity): _binding = incident_binding _binding_class = incident_binding.TotalLossEstimationType + initial_reported_total_loss_estimation = fields.TypedField("Initial_Reported_Total_Loss_Estimation", LossEstimation) + actual_total_loss_estimation = fields.TypedField("Actual_Total_Loss_Estimation", LossEstimation) + def __init__(self): super(TotalLossEstimation, self).__init__() - self.initial_reported_total_loss_estimation = None - self.actual_total_loss_estimation = None - - @property - def initial_reported_total_loss_estimation(self): - return self._initial_reported_total_loss_estimation - - @initial_reported_total_loss_estimation.setter - def initial_reported_total_loss_estimation(self, value): - self._set_var(LossEstimation, initial_reported_total_loss_estimation=value) - - @property - def actual_total_loss_estimation(self): - return self._actual_total_loss_estimation - - @actual_total_loss_estimation.setter - def actual_total_loss_estimation(self, value): - self._set_var(LossEstimation, actual_total_loss_estimation=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(TotalLossEstimation, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - obj = self._binding_class() - if self.initial_reported_total_loss_estimation: - obj.Initial_Reported_Total_Loss_Estimation = self.initial_reported_total_loss_estimation.to_obj(ns_info=ns_info) - if self.actual_total_loss_estimation: - obj.Actual_Total_Loss_Estimation = self.actual_total_loss_estimation.to_obj(ns_info=ns_info) - return obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.initial_reported_total_loss_estimation = LossEstimation.from_obj(obj.Initial_Reported_Total_Loss_Estimation) - return_obj.actual_total_loss_estimation = LossEstimation.from_obj(obj.Actual_Total_Loss_Estimation) - return return_obj - - def to_dict(self): - return super(TotalLossEstimation, self).to_dict() - - @classmethod - def from_dict(cls, dict_, return_obj=None): - if not dict_: - return None - - if not return_obj: - return_obj = cls() - - return_obj.initial_reported_total_loss_estimation = LossEstimation.from_dict(dict_.get('initial_reported_total_loss_estimation')) - return_obj.actual_total_loss_estimation = LossEstimation.from_dict(dict_.get('actual_total_loss_estimation')) - - return return_obj diff --git a/stix/indicator/__init__.py b/stix/indicator/__init__.py index a3116185..65d34942 100644 --- a/stix/indicator/__init__.py +++ b/stix/indicator/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -from indicator import * # noqa +from .indicator import * # noqa diff --git a/stix/indicator/indicator.py b/stix/indicator/indicator.py index 3b202e47..06cb0933 100644 --- a/stix/indicator/indicator.py +++ b/stix/indicator/indicator.py @@ -1,30 +1,31 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -# external +# mixbox +from mixbox import fields +from mixbox import entities +from mixbox import typedlist + +# cybox from cybox.core import Observable, ObservableComposition from cybox.common import Time # internal import stix -import stix.utils as utils -from stix.common import ( - Identity, InformationSource, VocabString, Confidence, - RelatedTTP, Statement, CampaignRef -) -from stix.common.related import ( - GenericRelationshipList, RelatedCOA, RelatedIndicator, RelatedCampaignRef, - RelatedPackageRefs -) -from stix.data_marking import Marking -from stix.common.vocabs import IndicatorType +from stix.common.identity import Identity +from stix.common import (InformationSource, VocabString, Confidence, RelatedTTP, + Statement, CampaignRef) +from stix.common.related import (GenericRelationshipList, RelatedCOA, + RelatedIndicator, RelatedCampaignRef, RelatedPackageRefs) +from stix.common.vocabs import VocabField, IndicatorType from stix.common.kill_chains import KillChainPhasesReference +from stix import utils import stix.bindings.indicator as indicator_binding # relative from .test_mechanism import TestMechanisms from .sightings import Sightings -from .valid_time import _ValidTimePositions +from .valid_time import ValidTime class SuggestedCOAs(GenericRelationshipList): @@ -55,7 +56,7 @@ class SuggestedCOAs(GenericRelationshipList): >>> coa = CourseOfAction() >>> indicator = Indicator() >>> indicator.suggested_coas.append(coa) - >>> print type(indicator.suggested_coas[0]) + >>> print(type(indicator.suggested_coas[0])) Iterate over the ``suggested_coas`` property of an :class:`Indicator` @@ -63,7 +64,7 @@ class SuggestedCOAs(GenericRelationshipList): :class:`stix.coa.CourseOfAction` instance. >>> for related_coa in indicator.suggested_coas: - >>> print related_coa.item.id_ + >>> print(related_coa.item.id_) Args: suggested_coas(list): A list of :class:`stix.coa.CourseOfAction` @@ -78,14 +79,13 @@ class SuggestedCOAs(GenericRelationshipList): or ``"exclusive"``. See :class:`stix.common.related.GenericRelationshipList` documentation for more information. - """ + _namespace = "http://stix.mitre.org/Indicator-2" _binding = indicator_binding _binding_class = indicator_binding.SuggestedCOAsType - _binding_var = "Suggested_COA" - _contained_type = RelatedCOA - _inner_name = "suggested_coas" + + suggested_coa = fields.TypedField("Suggested_COA", RelatedCOA, multiple=True, key_name="suggested_coas") def __init__(self, suggested_coas=None, scope=None): super(SuggestedCOAs, self).__init__(scope, suggested_coas) @@ -119,7 +119,7 @@ class RelatedIndicators(GenericRelationshipList): >>> related = Indicator() >>> parent_indicator = Indicator() >>> parent_indicator.related_indicators.append(related) - >>> print type(indicator.related_indicators[0]) + >>> print(type(indicator.related_indicators[0])) Iterate over the ``related_indicators`` property of an @@ -127,7 +127,7 @@ class RelatedIndicators(GenericRelationshipList): :class:`Indicator`` instance: >>> for related in indicator.related_indicators: - >>> print related.item.id_ + >>> print(related.item.id_) Args: related_indicators (list, optional): A list of :class:`Indicator` or @@ -147,20 +147,19 @@ class RelatedIndicators(GenericRelationshipList): _namespace = "http://stix.mitre.org/Indicator-2" _binding = indicator_binding _binding_class = indicator_binding.RelatedIndicatorsType - _binding_var = "Related_Indicator" - _contained_type = RelatedIndicator - _inner_name = "related_indicators" + + related_indicator = fields.TypedField("Related_Indicator", RelatedIndicator, multiple=True, key_name="related_indicators") def __init__(self, related_indicators=None, scope=None): super(RelatedIndicators, self).__init__(scope, related_indicators) class Indicator(stix.BaseCoreComponent): - """Implementation of the STIX ``IndicatorType``. + """Implementation of the STIX Indicator. Args: id_ (optional): An identifier. If ``None``, a value will be generated - via ``stix.utils.create_id()``. If set, this will unset the + via ``mixbox.idgen.create_id()``. If set, this will unset the ``idref`` property. idref (optional): An identifier reference. If set this will unset the ``id_`` property. @@ -178,6 +177,25 @@ class Indicator(stix.BaseCoreComponent): _ALL_VERSIONS = ("2.0", "2.0.1", "2.1", "2.1.1") _ALLOWED_COMPOSITION_OPERATORS = ('AND', 'OR') _ID_PREFIX = "indicator" + _try_cast = False + + producer = fields.TypedField("Producer", InformationSource) + observable = fields.TypedField("Observable", Observable) + indicator_types = VocabField("Type", IndicatorType, multiple=True, key_name="indicator_types") + confidence = fields.TypedField("Confidence", Confidence) + indicated_ttps = fields.TypedField("Indicated_TTP", RelatedTTP, multiple=True, key_name="indicated_ttps") + test_mechanisms = fields.TypedField("Test_Mechanisms", TestMechanisms) + alternative_id = fields.TypedField("Alternative_ID", multiple=True) + suggested_coas = fields.TypedField("Suggested_COAs", SuggestedCOAs) + sightings = fields.TypedField("Sightings", Sightings) + composite_indicator_expression = fields.TypedField("Composite_Indicator_Expression", "stix.indicator.CompositeIndicatorExpression") + kill_chain_phases = fields.TypedField("Kill_Chain_Phases", KillChainPhasesReference) + valid_time_positions = fields.TypedField("Valid_Time_Position", ValidTime, multiple=True, key_name="valid_time_positions") + related_indicators = fields.TypedField("Related_Indicators", RelatedIndicators) + related_campaigns = fields.TypedField("Related_Campaigns", type_="stix.indicator.RelatedCampaignRefs") + likely_impact = fields.TypedField("Likely_Impact", Statement) + negate = fields.TypedField("negate") + related_packages = fields.TypedField("Related_Packages", RelatedPackageRefs) def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): @@ -191,124 +209,115 @@ def __init__(self, id_=None, idref=None, timestamp=None, title=None, short_description=short_description ) - self.producer = None - self.observables = None + self.observable = None self.indicator_types = IndicatorTypes() - self.confidence = None - self.indicated_ttps = _IndicatedTTPs() self.test_mechanisms = TestMechanisms() self.alternative_id = None self.suggested_coas = SuggestedCOAs() self.sightings = Sightings() self.composite_indicator_expression = None - self.handling = None self.kill_chain_phases = KillChainPhasesReference() - self.valid_time_positions = _ValidTimePositions() - self.related_indicators = None + self.related_indicators = RelatedIndicators() self.related_campaigns = RelatedCampaignRefs() self.observable_composition_operator = "OR" - self.likely_impact = None - self.negate = None self.related_packages = RelatedPackageRefs() @property - def producer(self): - """Contains information about the source of the :class:`Indicator`. + def observables(self): + """A list of ``cybox.core.Observable`` instances. This can be set to + a single object instance or a list of objects. - Default Value: ``None`` + Note: + If only one Observable is set, this property will return a list + with the ``observable`` property. - Returns: - An instance of - :class:`stix.common.information_source.InformationSource` + If multiple ``cybox.core.Observable`` this property will return + Observables under the ``cybox.core.ObservableComposition``. - Raises: - ValueError: If set to a value that is not ``None`` and not an - instance of - :class:`stix.common.information_source.InformationSource` + Access to the top level ``cybox.core.Observable`` is made via + ``observable`` property. - """ - return self._producer + Default Value: + Empty ``list``. - @producer.setter - def producer(self, value): - self._set_var(InformationSource, try_cast=False, producer=value) + Returns: + A ``list`` of ``cybox.core.Observable`` instances. - @property - def observable(self): - """A convenience property for accessing or setting the only - ``cybox.core.Observable`` instance held by this Indicator. + """ + if not self.observable: + return [] + elif self.observable.observable_composition: + return self.observable.observable_composition.observables - Default Value: Empty ``list``. + # self.observable is defined and not a composition. + return [self.observable] - Setting this property results in the ``observables`` property being - reinitialized to an empty ``list`` and appending the input value, - resulting in a ``list`` containing one value. + @observables.setter + def observables(self, value): + """ + The method will automatically create a top ``cybox.core.Observable`` and + append all ``cybox.core.Observable`` using ``observable_composition`` + property when a ``list`` is given with length greater than 1. Note: - If the ``observables`` list contains more than one item, this - property will only return the first item in the list. + The top level ``cybox.core.Observable`` will set the ``operator`` + property for the ``cybox.core.ObservableComposition`` via the + ``observable_composition_operator`` property. The value of + ``operator`` can be changed via ``observable_composition_operator`` + property. By default, the composition layer will be set to ``"OR"``. - Returns: - An instance of ``cybox.core.Observable``. + Args: + value: A ``list`` of ``cybox.core.Observable`` instances or a single + ``cybox.core.Observable`` instance. Raises: ValueError: If set to a value that cannot be converted to an instance of ``cybox.core.Observable``. - - """ - if self.observables: - return self.observables[0] - else: - return None - - @observable.setter - def observable(self, observable): - self._observables = _Observables(observable) + if not value: + return - @property - def observables(self): - """A list of ``cybox.core.Observable`` instances. This can be set to - a single object instance or a list of objects. + if isinstance(value, Observable): + self.observable = value - Note: - If the input value or values are not instance(s) of - ``cybox.core.Observable``, an attempt will be made to - convert the value to an instance of ``cybox.core.Observable``. + elif utils.is_sequence(value): + if len(value) == 1: + self.add_observable(value[0]) + return - Default Value: Empty ``list`` + observable_comp = ObservableComposition() + observable_comp.operator = self.observable_composition_operator - Returns: - A ``list`` of ``cybox.core.Observable`` instances. + for element in value: + observable_comp.add(element) - Raises: - ValueError: If set to a value that cannot be converted to an - instance of ``cybox.core.Observable``. + self.observable = Observable() + self.observable.observable_composition = observable_comp - """ - return self._observables - - @observables.setter - def observables(self, value): - self._observables = _Observables(value) + def set_observables(self, value): + self.observables = value def add_observable(self, observable): - """Adds an observable to the ``observables`` list property of the + """Adds an observable to the ``observable`` property of the :class:`Indicator`. If the `observable` parameter is ``None``, no item will be added - to the ``observables`` list. + to the ``observable`` property. Note: The STIX Language dictates that an :class:`Indicator` can have only - one ``Observable`` under it. Because of this, the ``to_xml()`` - method will convert the ``observables`` list into an - ``cybox.core.ObservableComposition`` instance, in which each item - in the ``observables`` list will be added to the composition. By + one ``Observable`` under it. Because of this, when a user adds + another ``Observable`` a new, empty ``Observable`` will be crated + and append the existing and new ``observable`` using the + ``ObservableComposition`` property. To access the top level + ``Observable`` can be achieved by the ``observable`` property .By default, the ``operator`` of the composition layer will be set to ``"OR"``. The ``operator`` value can be changed via the ``observable_composition_operator`` property. + Setting ``observable`` or ``observables`` with re-initialize the + property and lose all ``Observable`` in the composition layer. + Args: observable: An instance of ``cybox.core.Observable`` or an object type that can be converted into one. @@ -319,33 +328,28 @@ def add_observable(self, observable): instance of ``cybox.core.Observable``. """ - self.observables.append(observable) - - @property - def alternative_id(self): - """An alternative identifi er for this :class:`Indicator` + if not observable: + return - This property can be set to a single string identifier or a list of - identifiers. If set to a single object, the object will be inserted - into an empty list internally. + # Sets the first observable. + elif not self.observable: + self.observable = observable - Default Value: Empty ``list`` + # When another is inserted. A "root" Observable is created and the + # user's Observables are appended to the composition. + elif not self.observable.observable_composition: + observable_comp = ObservableComposition() + observable_comp.operator = self.observable_composition_operator - Returns: - A list of alternative ids. + observable_comp.add(self.observable) + observable_comp.add(observable) - """ - return self._alternative_id + self.observable = Observable() + self.observable.observable_composition = observable_comp - @alternative_id.setter - def alternative_id(self, value): - self._alternative_id = [] - if not value: - return - elif utils.is_sequence(value): - self._alternative_id.extend(x for x in value if x) + # Keep appending to "root" Observable. else: - self._alternative_id.append(value) + self.observable.observable_composition.add(observable) def add_alternative_id(self, value): """Adds an alternative id to the ``alternative_id`` list property. @@ -362,28 +366,6 @@ def add_alternative_id(self, value): return self.alternative_id.append(value) - - @property - def valid_time_positions(self): - """A list of valid time positions for this :class:`Indicator`. - - This property can be set to a single instance or a list of - :class:`stix.indicator.valid_time.ValidTime` instances. If set to a - single instance, that object is converted into a list containing - one item. - - Default Value: Empty ``list`` - - Returns: - A list of - :class:`stix.indicator.valid_time.ValidTime` instances. - - """ - return self._valid_time_positions - - @valid_time_positions.setter - def valid_time_positions(self, value): - self._valid_time_positions = _ValidTimePositions(value) def add_valid_time_position(self, value): """Adds an valid time position to the ``valid_time_positions`` property @@ -398,36 +380,9 @@ def add_valid_time_position(self, value): Raises: ValueError: If the `value` argument is not an instance of :class:`stix.indicator.valid_time.ValidTime`. - """ - self.valid_time_positions.append(value) - - @property - def indicator_types(self): - """A list of indicator types for this :class:`Indicator`. - - This property can be set to lists or single instances of ``str`` - or :class:`stix.common.vocabs.VocabString` or an instance - of :class:`IndicatorTypes`. - - Note: - If an instance of ``str`` is passed in (or a ``list`` containing - ``str`` values) an attempt will be made to convert that string - value to an instance of :class:`stix.common.vocabs.IndicatorType`. - - Default Value: An empty ``IndicatorTypes`` instance. - - See Also: - Documentation for :class:`IndicatorTypes`. - - Returns: - An instance of ``IndicatorTypes``. """ - return self._indicator_types - - @indicator_types.setter - def indicator_types(self, value): - self._indicator_types = IndicatorTypes(value) + self.valid_time_positions.append(value) def add_indicator_type(self, value): """Adds a value to the ``indicator_types`` list property. @@ -451,45 +406,6 @@ def add_indicator_type(self, value): """ self.indicator_types.append(value) - @property - def confidence(self): - """The confidence for this :class:`Indicator`. - - This property can be set to an instance of ``str``, - :class:`stix.common.vocabs.VocabString`, or - :class:`stix.common.confidence.Confidence`. - - Default Value: ``None`` - - Note: - If set to an instance of ``str`` or - :class:`stix.common.vocabs.VocabString`, that value will be wrapped - in an instance of - :class:`stix.common.confidence.Confidence`. - - Returns: - An instance of of - :class:`stix.common.confidence.Confidence`. - - Raises: - ValueError: If set to a ``str`` value that cannot be converted into - an instance of :class:`stix.common.confidence.Confidence`. - - """ - return self._confidence - - @confidence.setter - def confidence(self, value): - self._set_var(Confidence, confidence=value) - - @property - def indicated_ttps(self): - return self._indicated_ttps - - @indicated_ttps.setter - def indicated_ttps(self, value): - self._indicated_ttps = _IndicatedTTPs(value) - def add_indicated_ttp(self, v): """Adds an Indicated TTP to the ``indicated_ttps`` list property of this :class:`Indicator`. @@ -516,14 +432,6 @@ def add_indicated_ttp(self, v): """ self.indicated_ttps.append(v) - @property - def test_mechanisms(self): - return self._test_mechanisms - - @test_mechanisms.setter - def test_mechanisms(self, value): - self._test_mechanisms = TestMechanisms(value) - def add_test_mechanism(self, tm): """Adds an Test Mechanism to the ``test_mechanisms`` list property of this :class:`Indicator`. @@ -551,25 +459,6 @@ def add_test_mechanism(self, tm): """ self.test_mechanisms.append(tm) - @property - def handling(self): - return self._handling - - @handling.setter - def handling(self, value): - self._set_var(Marking, handling=value) - - @property - def related_indicators(self): - return self._related_indicators - - @related_indicators.setter - def related_indicators(self, value): - if isinstance(value, RelatedIndicators): - self._related_indicators = value - else: - self._related_indicators = RelatedIndicators(value) - def add_related_indicator(self, indicator): """Adds an Related Indicator to the ``related_indicators`` list property of this :class:`Indicator`. @@ -603,18 +492,35 @@ def add_related_indicator(self, indicator): """ self.related_indicators.append(indicator) - @property - def related_campaigns(self): - return self._related_campaigns + def add_related_campaign(self, value): + """Adds a Related Campaign to this Indicator. - @related_campaigns.setter - def related_campaigns(self, value): - if isinstance(value, RelatedCampaignRefs): - self._related_campaigns = value - else: - self._related_campaigns = RelatedCampaignRefs(value) + The `value` parameter must be an instance of :class:`.RelatedCampaignRef` + or :class:`.CampaignRef`. - def add_related_campaign(self, value): + If the `value` parameter is ``None``, no item wil be added to the + ``related_campaigns`` collection. + + Calling this method is the same as calling ``append()`` on the + ``related_campaigns`` property. + + See Also: + The :class:`.RelatedCampaignRef` documentation. + + Note: + If the `value` parameter is not an instance of + :class:`.RelatedCampaignRef` an attempt will be made to convert it + to one. + + Args: + value: An instance of :class:`.RelatedCampaignRef` or + :class:`.Campaign`. + + Raises: + ValueError: If the `value` parameter cannot be converted into + an instance of :class:`.RelatedCampaignRef` + + """ self.related_campaigns.append(value) @property @@ -625,36 +531,16 @@ def observable_composition_operator(self): def observable_composition_operator(self, value): if value in self._ALLOWED_COMPOSITION_OPERATORS: self._observable_composition_operator = value + + if self.observable and self.observable.observable_composition: + self.observable.observable_composition.operator = value + return error = "observable_composition_operator must one of {0}" error = error.format(self._ALLOWED_COMPOSITION_OPERATORS) raise ValueError(error) - @property - def likely_impact(self): - return self._likely_impact - - @likely_impact.setter - def likely_impact(self, value): - self._set_var(Statement, likely_impact=value) - - @property - def negate(self): - return self._negate - - @negate.setter - def negate(self, value): - self._negate = utils.xml_bool(value) - - @property - def kill_chain_phases(self): - return self._kill_chain_phases - - @kill_chain_phases.setter - def kill_chain_phases(self, value): - self._kill_chain_phases = KillChainPhasesReference(value) - def add_kill_chain_phase(self, value): """Add a new Kill Chain Phase reference to this Indicator. @@ -666,14 +552,6 @@ def add_kill_chain_phase(self, value): """ self.kill_chain_phases.append(value) - @property - def related_packages(self): - return self._related_packages - - @related_packages.setter - def related_packages(self, value): - self._related_packages = RelatedPackageRefs(value) - def add_related_package(self, value): self.related_packages.append(value) @@ -846,135 +724,35 @@ def add_object(self, object_): observable = Observable(object_) self.add_observable(observable) - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(Indicator, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - return_obj.negate = True if self.negate else None - - if self.confidence: - return_obj.Confidence = self.confidence.to_obj(ns_info=ns_info) - if self.indicator_types: - return_obj.Type = self.indicator_types.to_obj(ns_info=ns_info) - if self.indicated_ttps: - return_obj.Indicated_TTP = self.indicated_ttps.to_obj(ns_info=ns_info) - if self.producer: - return_obj.Producer = self.producer.to_obj(ns_info=ns_info) - if self.test_mechanisms: - return_obj.Test_Mechanisms = self.test_mechanisms.to_obj(ns_info=ns_info) - if self.likely_impact: - return_obj.Likely_Impact = self.likely_impact.to_obj(ns_info=ns_info) - if self.alternative_id: - return_obj.Alternative_ID = self.alternative_id - if self.valid_time_positions: - return_obj.Valid_Time_Position = self.valid_time_positions.to_obj(ns_info=ns_info) - if self.suggested_coas: - return_obj.Suggested_COAs = self.suggested_coas.to_obj(ns_info=ns_info) - if self.sightings: - return_obj.Sightings = self.sightings.to_obj(ns_info=ns_info) - if self.composite_indicator_expression: - return_obj.Composite_Indicator_Expression = self.composite_indicator_expression.to_obj(ns_info=ns_info) - if self.handling: - return_obj.Handling = self.handling.to_obj(ns_info=ns_info) - if self.kill_chain_phases: - return_obj.Kill_Chain_Phases = self.kill_chain_phases.to_obj(ns_info=ns_info) - if self.related_indicators: - return_obj.Related_Indicators = self.related_indicators.to_obj(ns_info=ns_info) - if self.related_campaigns: - return_obj.Related_Campaigns = self.related_campaigns.to_obj(ns_info=ns_info) - if self.related_packages: - return_obj.Related_Packages = self.related_packages.to_obj(ns_info=ns_info) - if self.observables: - if len(self.observables) > 1: - root_observable = self._merge_observables(self.observables) - else: - root_observable = self.observables[0] - return_obj.Observable = root_observable.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - super(Indicator, cls).from_obj(obj, return_obj=return_obj) - - if isinstance(obj, cls._binding_class): - return_obj.negate = obj.negate - return_obj.producer = InformationSource.from_obj(obj.Producer) - return_obj.confidence = Confidence.from_obj(obj.Confidence) - return_obj.sightings = Sightings.from_obj(obj.Sightings) - return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_obj(obj.Composite_Indicator_Expression) - return_obj.handling = Marking.from_obj(obj.Handling) - return_obj.kill_chain_phases = KillChainPhasesReference.from_obj(obj.Kill_Chain_Phases) - return_obj.related_indicators = RelatedIndicators.from_obj(obj.Related_Indicators) - return_obj.likely_impact = Statement.from_obj(obj.Likely_Impact) - return_obj.indicator_types = IndicatorTypes.from_obj(obj.Type) - return_obj.test_mechanisms = TestMechanisms.from_obj(obj.Test_Mechanisms) - return_obj.suggested_coas = SuggestedCOAs.from_obj(obj.Suggested_COAs) - return_obj.alternative_id = obj.Alternative_ID - return_obj.indicated_ttps = _IndicatedTTPs.from_obj(obj.Indicated_TTP) - return_obj.valid_time_positions = _ValidTimePositions.from_obj(obj.Valid_Time_Position) - return_obj.observable = Observable.from_obj(obj.Observable) - return_obj.related_campaigns = RelatedCampaignRefs.from_obj(obj.Related_Campaigns) - return_obj.related_packages = RelatedPackageRefs.from_obj(obj.Related_Packages) - - return return_obj - - def to_dict(self): - skip = ('observables', 'observable_composition_operator', 'negate') - d = utils.to_dict(self, skip=skip) + def _finalize_obj(self, entity_obj): + """Omits the `negate` field if it is not equal to True. + """ + if self.negate: + entity_obj.negate = True + elif hasattr(entity_obj, 'negate'): + entity_obj.negate = None + def _finalize_dict(self, entity_dict): + """Omits the `negate` field if it is not equal to True. + """ if self.negate: - d['negate'] = True + entity_dict['negate'] = True + elif 'negate' in entity_dict: + del entity_dict['negate'] - if self.observables: - if len(self.observables) == 1: - d['observable'] = self.observables[0].to_dict() - else: - composite_observable = self._merge_observables(self.observables) - d['observable'] = composite_observable.to_dict() - return d +def check_operator(composite_indicator_exp, value): + allowed = CompositeIndicatorExpression.OPERATORS - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - super(Indicator, cls).from_dict(dict_repr, return_obj=return_obj) - - get = dict_repr.get - return_obj.negate = get('negate') - return_obj.alternative_id = get('alternative_id') - return_obj.indicated_ttps = _IndicatedTTPs.from_dict(get('indicated_ttps')) - return_obj.test_mechanisms = TestMechanisms.from_list(get('test_mechanisms')) - return_obj.suggested_coas = SuggestedCOAs.from_dict(get('suggested_coas')) - return_obj.sightings = Sightings.from_dict(get('sightings')) - return_obj.composite_indicator_expression = CompositeIndicatorExpression.from_dict(get('composite_indicator_expression')) - return_obj.handling = Marking.from_dict(get('handling')) - return_obj.kill_chain_phases = KillChainPhasesReference.from_dict(get('kill_chain_phases')) - return_obj.related_indicators = RelatedIndicators.from_dict(get('related_indicators')) - return_obj.likely_impact = Statement.from_dict(get('likely_impact')) - return_obj.indicator_types = IndicatorTypes.from_list(get('indicator_types')) - return_obj.confidence = Confidence.from_dict(get('confidence')) - return_obj.valid_time_positions = _ValidTimePositions.from_dict(get('valid_time_positions')) - return_obj.observable = Observable.from_dict(get('observable')) - return_obj.producer = InformationSource.from_dict(get('producer')) - return_obj.related_campaigns = RelatedCampaignRefs.from_dict(get('related_campaigns')) - return_obj.related_packages = RelatedPackageRefs.from_dict(get('related_packages')) - - return return_obj - - -class CompositeIndicatorExpression(stix.EntityList): + if not value: + raise ValueError("operator must not be None or empty") + elif value not in allowed: + raise ValueError("operator must be one of: %s" % allowed) + else: + return + + +class CompositeIndicatorExpression(entities.EntityList): """Implementation of the STIX ``CompositeIndicatorExpressionType``. The ``CompositeIndicatorExpression`` class implements methods found on @@ -1016,87 +794,58 @@ class CompositeIndicatorExpression(stix.EntityList): _binding = indicator_binding _binding_class = indicator_binding.CompositeIndicatorExpressionType _namespace = 'http://stix.mitre.org/Indicator-2' - _contained_type = Indicator - _binding_var = "Indicator" - _inner_name = "indicators" - + OP_AND = "AND" OP_OR = "OR" OPERATORS = (OP_AND, OP_OR) - + operator = fields.TypedField("operator", preset_hook=check_operator) + indicator = fields.TypedField( + name="Indicator", + type_=Indicator, + multiple=True, + key_name="indicators" + ) + + # TODO (bworrell): Change this to *args, **kwargs to get around the weirdness + # that occurs when creating with kwarg and arglist. + # E.g, CompositeIndicatorExpression(operator="AND", arg1, arg2, arg3) + # will raise an error. def __init__(self, operator="OR", *args): super(CompositeIndicatorExpression, self).__init__(*args) self.operator = operator - @property - def operator(self): - return self._operator - - @operator.setter - def operator(self, value): - if not value: - raise ValueError("operator must not be None or empty") - elif value not in self.OPERATORS: - raise ValueError("operator must be one of: %s" % (self.OPERATORS,)) - else: - self._operator = value - - def __nonzero__(self): - return super(CompositeIndicatorExpression, self).__nonzero__() - - def to_obj(self, return_obj=None, ns_info=None): - list_obj = super(CompositeIndicatorExpression, self).to_obj(return_obj=return_obj, ns_info=ns_info) - list_obj.operator = self.operator - return list_obj - - def to_dict(self): - d = super(CompositeIndicatorExpression, self).to_dict() - if self.operator: - d['operator'] = self.operator - return d - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if return_obj is None: - return_obj = cls() - super(CompositeIndicatorExpression, cls).from_obj(obj, return_obj=return_obj) - return_obj.operator = obj.operator - return return_obj +class _RelatedCampaignRefList(typedlist.TypedList): + def __init__(self, *args): + super(_RelatedCampaignRefList, self).__init__(type=RelatedCampaignRef, *args) - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if return_obj is None: - return_obj = cls() + def _fix_value(self, value): + from stix.campaign import Campaign - super(CompositeIndicatorExpression, cls).from_dict(dict_repr, return_obj=return_obj) - return_obj.operator = dict_repr.get('operator') - return return_obj + if isinstance(value, Campaign) and value.id_: + return RelatedCampaignRef(CampaignRef(idref=value.id_)) + + msg = "Cannot insert object of type '%s' into '%s'" + msg = msg % (type(value), self.__class__.__name__) + raise TypeError(msg) class RelatedCampaignRefs(GenericRelationshipList): _namespace = "http://stix.mitre.org/Indicator-2" _binding = indicator_binding _binding_class = _binding.RelatedCampaignReferencesType - _binding_var = 'Related_Campaign' - _contained_type = RelatedCampaignRef - _inner_name = "related_campaigns" + + related_campaign = fields.TypedField( + name="Related_Campaign", + type_=RelatedCampaignRef, + multiple=True, + key_name="related_campaigns", + listfunc=_RelatedCampaignRefList + ) def __init__(self, related_campaign_refs=None, scope=None): super(RelatedCampaignRefs, self).__init__(scope, related_campaign_refs) - def _fix_value(self, value): - from stix.campaign import Campaign - - if isinstance(value, Campaign) and value.id_: - return RelatedCampaignRef(CampaignRef(idref=value.id_)) - else: - return super(RelatedCampaignRefs, self)._fix_value(value) - # NOT ACTUAL STIX TYPES! class IndicatorTypes(stix.TypedList): @@ -1118,7 +867,7 @@ class IndicatorTypes(stix.TypedList): >>> itypes = IndicatorTypes() >>> type_ = IndicatorType(IndicatorType.TERM_IP_WATCHLIST) >>> itypes.append(type_) - >>> print len(itypes) + >>> print(len(itypes)) 1 Add a string value: @@ -1128,7 +877,7 @@ class IndicatorTypes(stix.TypedList): >>> type(IndicatorType.TERM_IP_WATCHLIST) >>> itypes.append(IndicatorType.TERM_IP_WATCHLIST) - >>> print len(itypes) + >>> print(len(itypes)) 1 Args: @@ -1141,11 +890,3 @@ class IndicatorTypes(stix.TypedList): def _fix_value(self, value): return IndicatorType(value) - - -class _IndicatedTTPs(stix.TypedList): - _contained_type = RelatedTTP - - -class _Observables(stix.TypedList): - _contained_type = Observable diff --git a/stix/indicator/sightings.py b/stix/indicator/sightings.py index 7af12240..c110513c 100644 --- a/stix/indicator/sightings.py +++ b/stix/indicator/sightings.py @@ -1,12 +1,14 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix -import stix.utils as utils -from stix.common import ( - GenericRelationshipList, RelatedObservable, StructuredText, Confidence, - InformationSource -) +from stix import utils +from stix.common import (GenericRelationshipList, RelatedObservable, + StructuredText, Confidence, InformationSource) +from stix.common.datetimewithprecision import validate_precision + import stix.bindings.indicator as indicator_binding @@ -15,166 +17,49 @@ class Sighting(stix.Entity): _binding = indicator_binding _binding_class = _binding.SightingType + timestamp = fields.DateTimeField("timestamp") + timestamp_precision = fields.TypedField("timestamp_precision", preset_hook=validate_precision) + description = fields.TypedField("Description", StructuredText) + source = fields.TypedField("Source", InformationSource) + reference = fields.TypedField("Reference") + confidence = fields.TypedField("Confidence", Confidence) + related_observables = fields.TypedField("Related_Observables", type_="stix.indicator.sightings.RelatedObservables") + def __init__(self, timestamp=None, timestamp_precision=None, description=None): + super(Sighting, self).__init__() + self.timestamp = timestamp or utils.dates.now() self.timestamp_precision = timestamp_precision self.description = description self.source = None self.reference = None self.confidence = None - self.related_observables = RelatedObservables() - - @property - def timestamp(self): - return self._timestamp - - @timestamp.setter - def timestamp(self, value): - self._timestamp = utils.dates.parse_value(value) - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - @property - def source(self): - return self._source - - @source.setter - def source(self, value): - self._set_var(InformationSource, try_cast=False, source=value) - - @property - def confidence(self): - return self._confidence - - @confidence.setter - def confidence(self, value): - self._set_var(Confidence, confidence=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(Sighting, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.timestamp = utils.dates.serialize_value(self.timestamp) - return_obj.timestamp_precision = self.timestamp_precision - return_obj.Reference = self.reference - - if self.source: - return_obj.Source = self.source.to_obj(ns_info=ns_info) - if self.confidence: - return_obj.Confidence = self.confidence.to_obj(ns_info=ns_info) - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.related_observables: - return_obj.Related_Observables = self.related_observables.to_obj(ns_info=ns_info) - - return return_obj - - def to_dict(self): - return super(Sighting, self).to_dict() - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if return_obj is None: - return_obj = cls() - - return_obj.timestamp = obj.timestamp - return_obj.timestamp_precision = obj.timestamp_precision - return_obj.source = InformationSource.from_obj(obj.Source) - return_obj.reference = obj.Reference - return_obj.confidence = Confidence.from_obj(obj.Confidence) - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.related_observables = RelatedObservables.from_obj(obj.Related_Observables) - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if return_obj is None: - return_obj = cls() - - return_obj.timestamp = d.get('timestamp') - return_obj.timestamp_precision = d.get('timestamp_precision') - return_obj.source = InformationSource.from_dict(d.get('source')) - return_obj.reference = d.get('reference') - return_obj.confidence = Confidence.from_dict(d.get('confidence')) - return_obj.description = StructuredText.from_dict(d.get('description')) - return_obj.related_observables = RelatedObservables.from_dict(d.get('related_observables')) - return return_obj class Sightings(stix.EntityList): _namespace = "http://stix.mitre.org/Indicator-2" _binding = indicator_binding _binding_class = _binding.SightingsType - _contained_type = Sighting - _binding_var = "Sighting" - _inner_name = "sightings" - + + sightings_count = fields.TypedField("sightings_count") + sighting = fields.TypedField("Sighting", Sighting, multiple=True, key_name="sightings") + def __init__(self, sightings_count=None, *args): super(Sightings, self).__init__(*args) self.sightings_count = sightings_count def __nonzero__(self): - return super(Sightings, self).__nonzero__() or bool(self.sightings_count) - - @property - def sightings_count(self): - return self._sightings_count - - @sightings_count.setter - def sightings_count(self, value): - self._set_var(int, sightings_count=value) - - def to_obj(self, return_obj=None, ns_info=None): - list_obj = super(Sightings, self).to_obj(return_obj=return_obj, ns_info=ns_info) - list_obj.sightings_count = self.sightings_count - return list_obj - - def to_dict(self): - d = super(Sightings, self).to_dict() - if self.sightings_count: - d['sightings_count'] = self.sightings_count - return d - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if return_obj is None: - return_obj = cls() - - super(Sightings, cls).from_obj(obj, return_obj=return_obj) - return_obj.sightings_count = obj.sightings_count - return return_obj - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if return_obj is None: - return_obj = cls() - - super(Sightings, cls).from_dict(dict_repr, return_obj=return_obj) - return_obj.sightings_count = dict_repr.get('sightings_count') - return return_obj + return super(Sightings, self).__nonzero__() or (self.sightings_count is not None) class RelatedObservables(GenericRelationshipList): _namespace = "http://stix.mitre.org/Indicator-2" _binding = indicator_binding _binding_class = _binding.RelatedObservablesType - _binding_var = "Related_Observable" - _contained_type = RelatedObservable - _inner_name = "observables" + + observable = fields.TypedField( + name="Related_Observable", + type_=RelatedObservable, + multiple=True, + key_name="observables" + ) diff --git a/stix/indicator/test_mechanism.py b/stix/indicator/test_mechanism.py index f339b21c..1f09b6ea 100644 --- a/stix/indicator/test_mechanism.py +++ b/stix/indicator/test_mechanism.py @@ -1,128 +1,69 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# external +from mixbox import fields, entities + +# internal import stix from stix.common import InformationSource, Statement + +# bindings import stix.bindings.indicator as indicator_binding class _BaseTestMechanism(stix.Entity): _namespace = "http://stix.mitre.org/Indicator-2" _binding = indicator_binding - _binding_class = indicator_binding.TestMechanismType() - + _binding_class = indicator_binding.TestMechanismType + + id_ = fields.IdField("id") + idref = fields.IdField("idref") + efficacy = fields.TypedField("Efficacy", Statement) + producer = fields.TypedField("Producer", InformationSource) + def __init__(self, id_=None, idref=None): + super(_BaseTestMechanism, self).__init__() + self.id_ = id_ self.idref = idref self.efficacy = None self.producer = None - - @property - def efficacy(self): - return self._efficacy - - @efficacy.setter - def efficacy(self, value): - self._set_var(Statement, efficacy=value) - - @property - def producer(self): - return self._producer - - @producer.setter - def producer(self, value): - self._set_var(InformationSource, try_cast=False, producer=value) - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - from stix.extensions.test_mechanism.snort_test_mechanism import SnortTestMechanism # noqa - from stix.extensions.test_mechanism.open_ioc_2010_test_mechanism import OpenIOCTestMechanism # noqa - from stix.extensions.test_mechanism.yara_test_mechanism import YaraTestMechanism # noqa - from stix.extensions.test_mechanism.generic_test_mechanism import GenericTestMechanism # noqa - - if not return_obj: - klass = _BaseTestMechanism.lookup_class(obj.xml_type) - return_obj = klass.from_obj(obj) - else: - return_obj.id_ = obj.id - return_obj.idref = obj.idref - return_obj.efficacy = Statement.from_obj(obj.Efficacy) - return_obj.producer = InformationSource.from_obj(obj.Producer) - - return return_obj - - def to_obj(self, return_obj=None, ns_info=None): - super(_BaseTestMechanism, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.id = self.id_ - return_obj.idref = self.idref - return_obj.xsi_type = self._XSI_TYPE - - if self.efficacy: - return_obj.Efficacy = self.efficacy.to_obj(ns_info=ns_info) - if self.producer: - return_obj.Producer = self.producer.to_obj(ns_info=ns_info) - - return return_obj - - @staticmethod - def lookup_class(xsi_type): - if not xsi_type: - raise ValueError("xsi:type is required") - for (k, v) in _EXTENSION_MAP.iteritems(): - # TODO: for now we ignore the prefix and just check for - # a partial match - if xsi_type in k: - return v - - raise ValueError("Unregistered xsi:type %s" % xsi_type) - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - - from stix.extensions.test_mechanism.snort_test_mechanism import SnortTestMechanism # noqa - from stix.extensions.test_mechanism.open_ioc_2010_test_mechanism import OpenIOCTestMechanism # noqa - from stix.extensions.test_mechanism.yara_test_mechanism import YaraTestMechanism # noqa - from stix.extensions.test_mechanism.generic_test_mechanism import GenericTestMechanism # noqa - - if not return_obj: - klass = _BaseTestMechanism.lookup_class(d.get('xsi:type')) - return_obj = klass.from_dict(d) - else: - return_obj.id_ = d.get('id') - return_obj.idref = d.get('idref') - return_obj.efficacy = Statement.from_dict(d.get('efficacy')) - return_obj.producer = InformationSource.from_dict(d.get('producer')) - - return return_obj - + def to_obj(self, ns_info=None): + obj = super(_BaseTestMechanism, self).to_obj(ns_info=ns_info) + obj.xsi_type = self._XSI_TYPE + return obj + def to_dict(self): d = super(_BaseTestMechanism, self).to_dict() d['xsi:type'] = self._XSI_TYPE # added by subclass return d +class TestMechanismFactory(entities.EntityFactory): + @classmethod + def entity_class(self, key): + import stix.extensions.test_mechanism.snort_test_mechanism # noqa + import stix.extensions.test_mechanism.open_ioc_2010_test_mechanism # noqa + import stix.extensions.test_mechanism.yara_test_mechanism # noqa + import stix.extensions.test_mechanism.generic_test_mechanism # noqa + return stix.lookup_extension(key) + + class TestMechanisms(stix.EntityList): _binding = indicator_binding _namespace = 'http://stix.mitre.org/Indicator-2' _binding_class = _binding.TestMechanismsType - _contained_type = _BaseTestMechanism - _binding_var = "Test_Mechanism" - _inner_name = "test_mechanisms" - _dict_as_list = True + test_mechanism = fields.TypedField( + name="Test_Mechanism", + type_=_BaseTestMechanism, + factory=TestMechanismFactory, + multiple=True + ) -#: Mapping of test mechanism extension types to classes -_EXTENSION_MAP = {} -def add_extension(cls): - _EXTENSION_MAP[cls._XSI_TYPE] = cls # noqa +# Backwards compatibility +add_extension = stix.add_extension diff --git a/stix/indicator/valid_time.py b/stix/indicator/valid_time.py index c8348a5c..2514e4c9 100644 --- a/stix/indicator/valid_time.py +++ b/stix/indicator/valid_time.py @@ -1,76 +1,23 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix from stix.common import DateTimeWithPrecision import stix.bindings.indicator as indicator_binding +from mixbox.entities import Entity -class ValidTime(stix.Entity): +class ValidTime(Entity): _namespace = "http://stix.mitre.org/Indicator-2" _binding = indicator_binding _binding_class = _binding.ValidTimeType - + + start_time = fields.TypedField("Start_Time", DateTimeWithPrecision) + end_time = fields.TypedField("End_Time", DateTimeWithPrecision) + def __init__(self, start_time=None, end_time=None): + super(ValidTime, self).__init__() self.start_time = start_time self.end_time = end_time - - @property - def start_time(self): - return self._start_time - - @start_time.setter - def start_time(self, value): - self._set_var(DateTimeWithPrecision, start_time=value) - - @property - def end_time(self): - return self._end_time - - @end_time.setter - def end_time(self, value): - self._set_var(DateTimeWithPrecision, end_time=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(ValidTime, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.start_time: - return_obj.Start_Time = self.start_time.to_obj(ns_info=ns_info) - if self.end_time: - return_obj.End_Time = self.end_time.to_obj(ns_info=ns_info) - - return return_obj - - def to_dict(self): - return super(ValidTime, self).to_dict() - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if return_obj is None: - return_obj = cls() - - return_obj.start_time = DateTimeWithPrecision.from_obj(obj.Start_Time) - return_obj.end_time = DateTimeWithPrecision.from_obj(obj.End_Time) - return return_obj - - @classmethod - def from_dict(cls, d, return_obj=None): - if not d: - return None - if return_obj is None: - return_obj = cls() - - return_obj.start_time = DateTimeWithPrecision.from_dict(d.get('start_time')) - return_obj.end_time = DateTimeWithPrecision.from_dict(d.get('end_time')) - - return return_obj - - -# NOT AN ACTUAL STIX TYPE -class _ValidTimePositions(stix.TypedList): - _contained_type = ValidTime diff --git a/stix/test/__init__.py b/stix/test/__init__.py index b43ebb6d..94a03ffd 100644 --- a/stix/test/__init__.py +++ b/stix/test/__init__.py @@ -1,21 +1,60 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -import json +import contextlib +import functools import itertools +import json +import warnings import cybox.utils +from mixbox.binding_utils import ExternalEncoding +from mixbox.entities import NamespaceCollector +from mixbox.vendor.six import iteritems, text_type + +from stix.utils import silence_warnings + + +@contextlib.contextmanager +def ctx_assert_warnings(self): + """Context manager for verifying that a block of code has raised a + warning. -import stix.bindings as bindings -from stix.utils import NamespaceInfo + """ + with warnings.catch_warnings(record=True) as w: + # Raise all warnings + warnings.simplefilter('always') + + # Return to caller + yield + + # Assert that a warning was raised. + self.assertTrue(len(w) > 0) + + +def assert_warnings(func): + """Test function decorator which asserts that a warning has been raised + during the execution of the test. + + """ + @functools.wraps(func) + def inner(*args, **kwargs): + self = args[0] + with ctx_assert_warnings(self): + return func(*args, **kwargs) + + return inner def round_trip_dict(cls, dict_): obj = cls.object_from_dict(dict_) dict2 = cls.dict_from_object(obj) + api_obj = cls.from_dict(dict_) + dict2 = cls.to_dict(api_obj) return dict2 + def round_trip(o, output=False, list_=False): """ Performs all eight conversions to verify import/export functionality. @@ -34,8 +73,8 @@ def round_trip(o, output=False, list_=False): klass = o.__class__ if output: - print "Class: ", klass - print "-" * 40 + print("Class: ", klass) + print("-" * 40) # 1. cybox.Entity -> dict/list if list_: @@ -48,7 +87,7 @@ def round_trip(o, output=False, list_=False): if output: print(json_string) - print "-" * 40 + print("-" * 40) # Before parsing the JSON, make sure the cache is clear cybox.utils.cache_clear() @@ -63,30 +102,30 @@ def round_trip(o, output=False, list_=False): o2 = klass.from_dict(d2) # 5. Entity -> Bindings Object - ns_info = NamespaceInfo() + ns_info = NamespaceCollector() xobj = o2.to_obj(ns_info=ns_info) try: # 6. Bindings Object -> XML String - xml_string = o2.to_xml(encoding=bindings.ExternalEncoding) + xml_string = o2.to_xml(encoding=ExternalEncoding) - if not isinstance(xml_string, unicode): - xml_string = xml_string.decode(bindings.ExternalEncoding) + if not isinstance(xml_string, text_type): + xml_string = xml_string.decode(ExternalEncoding) except KeyError as ex: - print str(ex) + print(str(ex)) ns_info.finalize() - print ns_info.finalized_namespaces + print(ns_info.binding_namespaces) raise ex if output: print(xml_string) - print "-" * 40 + print("-" * 40) # Before parsing the XML, make sure the cache is clear cybox.utils.cache_clear() - #7. XML String -> Bindings Object + # 7. XML String -> Bindings Object xobj2 = klass._binding.parseString(xml_string) # 8. Bindings object -> cybox.Entity @@ -94,6 +133,7 @@ def round_trip(o, output=False, list_=False): return o3 + class EntityTestCase(object): """A base class for testing STIX Entities""" @@ -101,9 +141,10 @@ def setUp(self): self.assertNotEqual(self.klass, None) self.assertNotEqual(self._full_dict, None) + @silence_warnings def test_round_trip_full_dict(self): # Don't run this test on the base class - if type(self) == type(EntityTestCase): + if type(self) is EntityTestCase: return dict2 = round_trip_dict(self.klass, self._full_dict) @@ -112,39 +153,40 @@ def test_round_trip_full_dict(self): def _combine(self, d): items = itertools.chain( - self._full_dict.iteritems(), - d.iteritems() + iteritems(self._full_dict), + iteritems(d) ) return dict(items) + @silence_warnings def test_round_trip_full(self): # Don't run this test on the base class - if type(self) == type(EntityTestCase): + if type(self) is EntityTestCase: return ent = self.klass.from_dict(self._full_dict) ent2 = round_trip(ent, output=True) - #TODO: eventually we want to test the objects are the same, but for - # now, just make sure there aren't any errors. - + @silence_warnings def _test_round_trip_dict(self, input): dict2 = round_trip_dict(self.klass, input) - self.maxDiff = None self.assertEqual(input, dict2) + @silence_warnings def _test_partial_dict(self, partial): d = self._combine(partial) self._test_round_trip_dict(d) class TypedListTestCase(object): + + @silence_warnings def test_round_trip_rt(self): - if type(self) == type(TypedListTestCase): + if type(self) is TypedListTestCase: return obj = self.klass.from_dict(self._full_dict) dict2 = obj.to_dict() - self.assertEqual(self._full_dict, dict2) \ No newline at end of file + self.assertEqual(self._full_dict, dict2) diff --git a/stix/test/campaign_test.py b/stix/test/campaign_test.py index 19f8a019..b7d29c96 100644 --- a/stix/test/campaign_test.py +++ b/stix/test/campaign_test.py @@ -1,9 +1,10 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. import unittest -from stix.test import EntityTestCase, TypedListTestCase, data_marking_test +from stix.test import EntityTestCase, TypedListTestCase +import stix.test.data_marking_test as data_marking_test from stix.test.common import ( confidence_test, information_source_test, statement_test, related_test, activity_test @@ -21,18 +22,23 @@ class NamesTests(EntityTestCase, unittest.TestCase): "Crazy Squirrels", { 'value': "Medium", - 'xsi:type':'stixVocabs:HighMediumLowVocab-1.0' + 'xsi:type': 'stixVocabs:HighMediumLowVocab-1.0' } ] } + class IntendedEffectsTests(TypedListTestCase, unittest.TestCase): - klass = campaign._IntendedEffects + klass = campaign.Campaign - _full_dict = [ + _partial_dict = [ statement_test.StatementTests._full_dict ] + _full_dict = { + 'intended_effects': _partial_dict, + } + class RelatedTTPsTest(EntityTestCase, unittest.TestCase): klass = campaign.RelatedTTPs @@ -64,6 +70,7 @@ class RelatedIndicatorsTests(EntityTestCase, unittest.TestCase): ] } + class AttributionTests(EntityTestCase, unittest.TestCase): klass = campaign.Attribution @@ -76,11 +83,13 @@ class AttributionTests(EntityTestCase, unittest.TestCase): class AttributionListTests(TypedListTestCase, unittest.TestCase): - klass = campaign._AttributionList + klass = campaign.Campaign - _full_dict = [ - AttributionTests._full_dict - ] + _full_dict = { + 'attribution': [ + AttributionTests._full_dict + ] + } class AssociatedCampaignsTests(EntityTestCase, unittest.TestCase): @@ -94,12 +103,10 @@ class AssociatedCampaignsTests(EntityTestCase, unittest.TestCase): } -class ActivitiesTests(TypedListTestCase, unittest.TestCase): - klass = campaign._Activities +class ActivityTests(TypedListTestCase, unittest.TestCase): + klass = campaign.Activity - _full_dict = [ - activity_test.ActivityTests._full_dict - ] + _full_dict = activity_test.ActivityTests._full_dict class CampaignTest(EntityTestCase, unittest.TestCase): @@ -112,18 +119,18 @@ class CampaignTest(EntityTestCase, unittest.TestCase): 'description': 'A pretty novice set of actors.', 'short_description': 'novices', 'names': NamesTests._full_dict, - 'intended_effects': IntendedEffectsTests._full_dict, + 'intended_effects': IntendedEffectsTests._partial_dict, 'status': { 'value': "Ongoing", - 'xsi:type':'stixVocabs:CampaignStatusVocab-1.0' + 'xsi:type': 'stixVocabs:CampaignStatusVocab-1.0' }, 'related_ttps': RelatedTTPsTest._full_dict, 'related_incidents': RelatedIncidentsTests._full_dict, 'related_indicators': RelatedIndicatorsTests._full_dict, - 'attribution': AttributionListTests._full_dict, + 'attribution': [AttributionTests._full_dict], 'associated_campaigns': AssociatedCampaignsTests._full_dict, 'confidence': confidence_test.ConfidenceTests._full_dict, - 'activity': ActivitiesTests._full_dict, + 'activity': [ActivityTests._full_dict], 'information_source': information_source_test.InformationSourceTests._full_dict, 'handling': data_marking_test.MarkingTests._full_dict, 'related_packages': related_test.RelatedPackageRefsTests._full_dict diff --git a/stix/test/coa_test.py b/stix/test/coa_test.py index 7ade2353..98861537 100644 --- a/stix/test/coa_test.py +++ b/stix/test/coa_test.py @@ -3,10 +3,10 @@ import unittest -from stix.test import EntityTestCase, data_marking_test -from stix.test.common import ( - confidence_test, information_source_test, statement_test, related_test, -) +from stix.test import EntityTestCase +from stix.test import data_marking_test +from stix.test.common import (confidence_test, information_source_test, + statement_test, related_test) from stix.test.extensions.structured_coa import generic_test import stix.coa as coa import stix.coa.objective as objective @@ -21,6 +21,7 @@ class RelatedCOAsTests(EntityTestCase, unittest.TestCase): ] } + class ObjectiveTests(EntityTestCase, unittest.TestCase): klass = objective.Objective @@ -50,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" @@ -70,16 +71,12 @@ class COATests(EntityTestCase, unittest.TestCase): } - def test_structured_coa(self): coa_ = coa.CourseOfAction() - def should_fail(): - coa_.structured_coa = "ERROR" - self.assertRaises( TypeError, - should_fail + setattr(coa_, "structured_coa", "ERROR") ) from stix.extensions.structured_coa.generic_structured_coa import GenericStructuredCOA diff --git a/stix/test/common/tools_tests.py b/stix/test/common/tools_tests.py index 40fe4c9f..b9795de6 100644 --- a/stix/test/common/tools_tests.py +++ b/stix/test/common/tools_tests.py @@ -1,4 +1,5 @@ -__author__ = 'bworrell' +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# See LICENSE.txt for complete terms. import unittest from stix.test import EntityTestCase diff --git a/stix/test/core/stix_package_test.py b/stix/test/core/stix_package_test.py index f63edf21..6261cb9d 100644 --- a/stix/test/core/stix_package_test.py +++ b/stix/test/core/stix_package_test.py @@ -1,12 +1,13 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. import copy -import StringIO import unittest +from mixbox.vendor.six import BytesIO + from stix.test import EntityTestCase -from stix.test.common import kill_chains_test +from stix.test.common import kill_chains_test, related_test from . import stix_header_test @@ -29,6 +30,7 @@ class COAsTests(EntityTestCase, unittest.TestCase): {'idref': 'example:test-1'} ] + class ExploitTargetsTests(EntityTestCase, unittest.TestCase): klass = stix_package.ExploitTargets @@ -36,6 +38,7 @@ class ExploitTargetsTests(EntityTestCase, unittest.TestCase): {'idref': 'example:test-1'} ] + class IncidentsTests(EntityTestCase, unittest.TestCase): klass = stix_package.Incidents @@ -43,6 +46,7 @@ class IncidentsTests(EntityTestCase, unittest.TestCase): {'idref': 'example:test-1'} ] + class IndicatorsTests(EntityTestCase, unittest.TestCase): klass = stix_package.Indicators @@ -58,6 +62,7 @@ class ThreatActorsTests(EntityTestCase, unittest.TestCase): {'idref': 'example:test-1'} ] + class TTPsTests(EntityTestCase, unittest.TestCase): klass = stix_package.TTPs @@ -79,9 +84,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" @@ -90,10 +95,10 @@ class STIXPackageTests(EntityTestCase, unittest.TestCase): }, 'threat_actors': ThreatActorsTests._full_dict, 'ttps': TTPsTests._full_dict, + 'related_packages': related_test.RelatedPackagesTests._full_dict, 'version': "1.1.1" } - def test_deepcopy(self): """Test copy.deepcopy() against parsed document. @@ -106,7 +111,7 @@ def test_deepcopy(self): """ package = core.STIXPackage.from_xml( - StringIO.StringIO( + BytesIO( core.STIXPackage().to_xml() ) ) diff --git a/stix/test/data_marking_test.py b/stix/test/data_marking_test.py index 381a6e64..9833ad76 100644 --- a/stix/test/data_marking_test.py +++ b/stix/test/data_marking_test.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. import unittest @@ -12,7 +12,7 @@ class MarkingSpecificationTests(EntityTestCase, unittest.TestCase): klass = dm.MarkingSpecification _full_dict = { 'id': "foo", - 'idref': "foo_ref", + # 'idref': "foo_ref", # Cannot have both @id and @idref set at the same time. 'version': "1234", 'controlled_structure': "some xpath", 'marking_structures': [ @@ -26,21 +26,13 @@ class MarkingSpecificationTests(EntityTestCase, unittest.TestCase): class MarkingStructureTests(unittest.TestCase): - def test_xsi_type_required(self): - d = { - 'marking_model_name': 'TLP', - } - - # If there's not an xsi:type in the dict, this will raise an error. - self.assertRaises(ValueError, dm.MarkingStructure.from_dict, d) - - def test_xsi_type_required(self): + def test_bad_xsi_type(self): d = { 'marking_model_name': 'TLP', 'xsi:type': "UNKNOWN_XSI_TYPE", } - self.assertRaises(ValueError, dm.MarkingStructure.from_dict, d) + self.assertRaises(ValueError, dm.MarkingStructureFactory.from_dict, d) class MarkingTests(EntityTestCase, unittest.TestCase): diff --git a/stix/test/encoding_test.py b/stix/test/encoding_test.py index 9797c262..f8311534 100644 --- a/stix/test/encoding_test.py +++ b/stix/test/encoding_test.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. """Tests for various encoding issues throughout the library""" import unittest -from StringIO import StringIO +from mixbox.vendor.six import BytesIO, text_type +from mixbox import binding_utils -import stix.bindings as bindings from stix.core import STIXHeader, STIXPackage from stix.campaign import Campaign from stix.indicator import Indicator @@ -40,12 +40,12 @@ class EncodingTests(unittest.TestCase): @classmethod def setUpClass(cls): - cls.orig_encoding = bindings.ExternalEncoding - bindings.ExternalEncoding = 'utf-16' + cls.orig_encoding = binding_utils.ExternalEncoding + binding_utils.ExternalEncoding = 'utf-16' @classmethod def tearDownClass(cls): - bindings.ExternalEncoding = cls.orig_encoding + binding_utils.ExternalEncoding = cls.orig_encoding def _test_equal(self, obj1, obj2): self.assertEqual(obj1.title, obj2.title) @@ -119,90 +119,12 @@ def test_campaign(self): c2 = round_trip(c) self._test_equal(c, c2) - def test_quote_xml(self): - s = bindings.quote_xml(UNICODE_STR) - self.assertEqual(s, UNICODE_STR) - - def test_quote_attrib(self): - """Tests that the stix.bindings.quote_attrib method works properly - on unicode inputs. - - Note: - The quote_attrib method (more specifically, saxutils.quoteattr()) - adds quotation marks around the input data, so we need to strip - the leading and trailing chars to test effectively - """ - s = bindings.quote_attrib(UNICODE_STR) - s = s[1:-1] - self.assertEqual(s, UNICODE_STR) - - def test_quote_attrib_int(self): - i = 65536 - s = bindings.quote_attrib(i) - self.assertEqual(u'"65536"', s) - - def test_quote_attrib_bool(self): - b = True - s = bindings.quote_attrib(b) - self.assertEqual(u'"True"', s) - - def test_quote_xml_int(self): - i = 65536 - s = bindings.quote_xml(i) - self.assertEqual(unicode(i), s) - - def test_quote_xml_bool(self): - b = True - s = bindings.quote_xml(b) - self.assertEqual(unicode(b), s) - - def test_quote_xml_encoded(self): - encoding = bindings.ExternalEncoding - encoded = UNICODE_STR.encode(encoding) - quoted = bindings.quote_xml(encoded) - self.assertEqual(UNICODE_STR, quoted) - - def test_quote_attrib_encoded(self): - encoding = bindings.ExternalEncoding - encoded = UNICODE_STR.encode(encoding) - quoted = bindings.quote_attrib(encoded)[1:-1] - self.assertEqual(UNICODE_STR, quoted) - - def test_quote_xml_zero(self): - i = 0 - s = bindings.quote_xml(i) - self.assertEqual(unicode(i), s) - - def test_quote_attrib_zero(self): - i = 0 - s = bindings.quote_attrib(i) - self.assertEqual(u'"0"', s) - - def test_quote_xml_none(self): - i = None - s = bindings.quote_xml(i) - self.assertEqual(u'', s) - - def test_quote_attrib_none(self): - i = None - s = bindings.quote_attrib(i) - self.assertEqual(u'""', s) - - def test_quote_attrib_empty(self): - i = '' - s = bindings.quote_attrib(i) - self.assertEqual(u'""', s) - - def test_quote_xml_empty(self): - i = '' - s = bindings.quote_xml(i) - self.assertEqual(u'', s) - def test_to_xml_utf16_encoded(self): encoding = 'utf-16' s = STIXHeader() s.title = UNICODE_STR xml = s.to_xml(encoding=encoding) + print(xml) self.assertTrue(UNICODE_STR in xml.decode(encoding)) def test_to_xml_default_encoded(self): @@ -215,24 +137,23 @@ def test_to_xml_no_encoding(self): s = STIXHeader() s.title = UNICODE_STR xml = s.to_xml(encoding=None) - self.assertTrue(isinstance(xml, unicode)) + self.assertTrue(isinstance(xml, text_type)) self.assertTrue(UNICODE_STR in xml) def test_from_xml_utf16_encoded(self): utf16_xml = XML.encode('utf-16') - sio = StringIO(utf16_xml) + sio = BytesIO(utf16_xml) sp = STIXPackage.from_xml(sio, encoding='utf-16') header = sp.stix_header self.assertEqual(header.title, UNICODE_STR) def test_from_xml_default_encoded(self): utf8_xml = XML.encode('utf-8') - sio = StringIO(utf8_xml) + sio = BytesIO(utf8_xml) sp = STIXPackage.from_xml(sio) header = sp.stix_header self.assertEqual(header.title, UNICODE_STR) - def test_utf16_roundtrip(self): sh = STIXHeader() sh.title = UNICODE_STR @@ -243,7 +164,7 @@ def test_utf16_roundtrip(self): xml16 = sp.to_xml(encoding='utf-16') # deserialize as utf-16 - sp2 = STIXPackage.from_xml(StringIO(xml16), encoding='utf-16') + sp2 = STIXPackage.from_xml(BytesIO(xml16), encoding='utf-16') sh2 = sp2.stix_header # check that the titles align diff --git a/stix/test/exploit_target_test.py b/stix/test/exploit_target_test.py index 2965fd17..4ebc4e22 100644 --- a/stix/test/exploit_target_test.py +++ b/stix/test/exploit_target_test.py @@ -9,6 +9,7 @@ import stix.exploit_target as et from stix.exploit_target import weakness, vulnerability, configuration + class CVSSVectorTests(EntityTestCase, unittest.TestCase): klass = vulnerability.CVSSVector @@ -86,6 +87,7 @@ class RelatedExploitTargetsTests(EntityTestCase, unittest.TestCase): ] } + class WeaknessTests(EntityTestCase, unittest.TestCase): klass = weakness.Weakness @@ -103,7 +105,6 @@ class WeaknessesTests(TypedListTestCase, unittest.TestCase): ] - class ConfigurationTests(EntityTestCase, unittest.TestCase): klass = configuration.Configuration @@ -115,14 +116,6 @@ class ConfigurationTests(EntityTestCase, unittest.TestCase): } -class ConfigurationsTests(TypedListTestCase, unittest.TestCase): - klass = configuration._Configurations - - _full_dict = [ - ConfigurationTests._full_dict - ] - - class ExploitTargetTests(EntityTestCase, unittest.TestCase): klass = et.ExploitTarget _full_dict = { @@ -135,7 +128,7 @@ class ExploitTargetTests(EntityTestCase, unittest.TestCase): 'short_description': "an ExploitTarget", 'vulnerabilities': VulnerabilitiesTests._full_dict, 'weaknesses': WeaknessesTests._full_dict, - 'configuration': ConfigurationsTests._full_dict, + 'configuration': [ConfigurationTests._full_dict], 'potential_coas': PotentialCOAsTests._full_dict, 'information_source': information_source_test.InformationSourceTests._full_dict, 'handling': data_marking_test.MarkingTests._full_dict, 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 f56bb6f2..507fc3bf 100644 --- a/stix/test/extensions/identity/ciq_identity_3_0_test.py +++ b/stix/test/extensions/identity/ciq_identity_3_0_test.py @@ -36,8 +36,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', + } ] }, { @@ -57,8 +65,17 @@ class CIQIdentity3_0InstanceTests(EntityTestCase, unittest.TestCase): }, 'country': { 'name_elements': [ - {'value': 'name 1'}, - {'value': 'name 2'} + { + 'value': 'name 1', + 'name_code': 'US', + 'name_code_type': 'ISO 3166-1 alpha-2' + }, + { + 'value': 'name 2', + 'name_code': 'BZ', + 'name_code_type': 'ISO 3166-1 alpha-2', + 'name_type': 'ISO' + } ] }, 'administrative_area': { @@ -99,9 +116,12 @@ class CIQIdentity3_0InstanceTests(EntityTestCase, unittest.TestCase): {'value': 'name 2'} ] } - ] + ], + 'organisation_info': { + 'industry_type': 'SECTOR 1 | SECTOR 2', + } }, - 'xsi:type': 'ciqIdentity:CIQIdentity3.0InstanceType' + 'xsi:type': 'stix-ciqidentity:CIQIdentity3.0InstanceType' } diff --git a/stix/test/extensions/malware/__init__.py b/stix/test/extensions/malware/__init__.py index e69de29b..9b80d3dd 100644 --- a/stix/test/extensions/malware/__init__.py +++ b/stix/test/extensions/malware/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# See LICENSE.txt for complete terms. 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 66f19d63..b79a3ab8 100644 --- a/stix/test/extensions/malware/maec_4_1_malware_test.py +++ b/stix/test/extensions/malware/maec_4_1_malware_test.py @@ -1,11 +1,21 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# See LICENSE.txt for complete terms. + import unittest -from StringIO import StringIO -from lxml import etree +from stix.core import STIXPackage +from mixbox.vendor.six import StringIO, text_type -import stix.utils.parser from stix.test import EntityTestCase from stix.extensions.malware.maec_4_1_malware import MAECInstance +try: + import maec + maec_present = True +except ImportError: + maec_present = False + + +@unittest.skipIf(condition=maec_present is False, reason="These tests require the 'maec' library.") class PythonMAECTests(EntityTestCase, unittest.TestCase): klass = MAECInstance @@ -14,104 +24,152 @@ class PythonMAECTests(EntityTestCase, unittest.TestCase): 'maec': { 'malware_subjects': [ - {'malware_instance_object_attributes': - {'id': 'maec-tst-obj-1', - 'properties': { - 'hashes': - [ - { - 'simple_hash_value': '9d7006e30fdf15e9c8e03e62534b3a3e', - 'type': 'MD5' - } - ], - 'xsi:type': 'FileObjectType'} - } + { + 'malware_instance_object_attributes': { + 'id': 'maec-tst-obj-1', + 'properties': { + 'hashes': + [ + { + 'simple_hash_value': '9d7006e30fdf15e9c8e03e62534b3a3e', + 'type': 'MD5' + } + ], + 'xsi:type': 'FileObjectType' + } + } } ] } } -class PythonMAECEtreeTests(unittest.TestCase): - XML = ( + def test_add_name_type(self): + maec_malware_instance = MAECInstance() + maec_malware_instance.add_name("Poison Ivy Variant v4392-acc") + maec_malware_instance.add_type("Remote Access Trojan") + maec_xml = text_type(maec_malware_instance.to_xml()) + self.assertTrue("Poison Ivy Variant v4392-acc" in maec_xml) + self.assertTrue("Remote Access Trojan" in maec_xml) + + +@unittest.skipIf(condition=maec_present is False, reason="These tests require the 'maec' library.") +class PythonMAECInPackageTests(unittest.TestCase): + XML = StringIO( """ - + + + Poison Ivy Variant v4392-acc + + + + Remote Access Trojan + Poison Ivy Variant v4392-acc + + + + + + + """ + ) + XML_MAEC = StringIO( + """ + - - - - - - - MD5 - 9d7006e30fdf15e9c8e03e62534b3a3e - - - - - - - + http://stix.mitre.org/TTP-1 http://stix.mitre.org/XMLSchema/ttp/1.2/ttp.xsd + http://stix.mitre.org/common-1 http://stix.mitre.org/XMLSchema/common/1.2/stix_common.xsd + http://stix.mitre.org/default_vocabularies-1 http://stix.mitre.org/XMLSchema/default_vocabularies/1.2.0/stix_default_vocabularies.xsd + http://stix.mitre.org/stix-1 http://stix.mitre.org/XMLSchema/core/1.2/stix_core.xsd + http://stix.mitre.org/extensions/Malware#MAEC4.1-1 http://stix.mitre.org/XMLSchema/extensions/malware/maec_4.1/1.0/maec_4.1_malware.xsd + http://maec.mitre.org/XMLSchema/maec-package-2 http://maec.mitre.org/language/version4.1/maec_package_schema.xsd" + id="example:Package-2b8fb66f-b6b3-4d40-865a-33e4a5ee1246" + version="1.1.1" + > + + + Poison Ivy Variant v4392-acc + + + + Remote Access Trojan + Poison Ivy Variant v4392-acc + + + + + + + + + + + + + + """ ) - def _test_xml(self, obj): - xml = obj.to_xml() - parser = stix.utils.parser.get_xml_parser() - tree = etree.parse(StringIO(xml), parser=parser) - root = tree.getroot() - - xpath = "//cyboxCommon:Type" - nodes = root.xpath(xpath, namespaces={'cyboxCommon': 'http://cybox.mitre.org/common-2'}) - - self.assertTrue(nodes is not None) - self.assertEqual(len(nodes), 1) - self.assertEqual(nodes[0].text, "MD5") + def test_parse_malware(self): + """Test parsing a normal MalwareInstance from XML + """ + stix_pkg = STIXPackage.from_xml(self.XML) + 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.ttp[0].behavior.malware_instances[0].to_dict() + self.assertTrue('names' in mw) - def test_etree(self): - parser = stix.utils.parser.get_xml_parser() - tree = etree.parse(StringIO(self.XML), parser=parser) - ext = MAECInstance() - ext.maec = tree - self._test_xml(ext) +@unittest.skipIf(condition=maec_present is True, reason="These tests require the 'maec' library to be missing.") +class PythonMAECNotInstalledTest(unittest.TestCase): + def test_parsing_maec_fails(self): + try: + STIXPackage.from_xml(PythonMAECInPackageTests.XML_MAEC) + except ImportError as e: + self.assertTrue(all(x in str(e) for x in ("No module named", "maec"))) - def test_etree_dict(self): - parser = stix.utils.parser.get_xml_parser() - tree = etree.parse(StringIO(self.XML), parser=parser) - ext = MAECInstance() - ext.maec = tree + def test_handling_maec_object_fails(self): + try: + MAECInstance().from_dict(PythonMAECTests._full_dict) + except ImportError as e: + self.assertTrue(all(x in str(e) for x in ("No module named", "maec"))) - d = ext.to_dict() - ext2 = MAECInstance.from_dict(d) - self._test_xml(ext2) + def test_setting_maec_property_fails(self): + try: + m = MAECInstance() + m.maec = "foo" + except ImportError as e: + self.assertTrue(all(x in str(e) for x in ("No module named", "maec"))) if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/stix/test/extensions/marking/__init__.py b/stix/test/extensions/marking/__init__.py index e69de29b..9b80d3dd 100644 --- a/stix/test/extensions/marking/__init__.py +++ b/stix/test/extensions/marking/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# See LICENSE.txt for complete terms. diff --git a/stix/test/extensions/marking/ais.py b/stix/test/extensions/marking/ais.py new file mode 100644 index 00000000..7f7a8327 --- /dev/null +++ b/stix/test/extensions/marking/ais.py @@ -0,0 +1,92 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# See LICENSE.txt for complete terms. + +import unittest + +from stix.test import EntityTestCase + +from stix.core import STIXPackage +from stix.extensions.marking import ais + + +class AISMarkingStructureNotProprietaryTests(EntityTestCase, unittest.TestCase): + klass = ais.AISMarkingStructure + _full_dict = { + 'not_proprietary': + { + 'cisa_proprietary': 'false', + 'ais_consent': {'consent': 'NONE'}, + 'tlp_marking': {'color': 'GREEN'} + }, + 'xsi:type': 'AIS:AISMarkingStructure' + } + + +class AISMarkingStructureIsProprietaryTests(EntityTestCase, unittest.TestCase): + klass = ais.AISMarkingStructure + _full_dict = { + 'is_proprietary': + { + 'cisa_proprietary': 'true', + 'ais_consent': {'consent': 'EVERYONE'}, + 'tlp_marking': {'color': 'AMBER'} + }, + 'xsi:type': 'AIS:AISMarkingStructure' + } + + +class AISMarkingGeneralTests(unittest.TestCase): + """General tests for ais module helpers.""" + + @classmethod + def setUpClass(cls): + cls.INDUSTRY_SECTORS = (ais.CHEMICAL_SECTOR, ais.COMMERCIAL_FACILITIES_SECTOR, + ais.COMMUNICATIONS_SECTOR, + ais.CRITICAL_MANUFACTURING_SECTOR, + ais.DAMS_SECTOR, ais.DEFENSE_INDUSTRIAL_BASE_SECTOR, + ais.EMERGENCY_SERVICES_SECTOR, ais.ENERGY_SECTOR, + ais.FINANCIAL_SERVICES_SECTOR, + ais.FOOD_AND_AGRICULTURE_SECTOR, + ais.GOVERNMENT_FACILITIES_SECTOR, + ais.HEALTH_CARE_AND_PUBLIC_HEALTH_SECTOR, + ais.INFORMATION_TECHNOLOGY_SECTOR, + ais.NUCLEAR_REACTORS_MATERIALS_AND_WASTE_SECTOR, + ais.TRANSPORTATION_SYSTEMS_SECTOR, ais.OTHER, + ais.WATER_AND_WASTEWATER_SYSTEMS_SECTOR) + + def test_validate_and_create_industry_type(self): + self.assertRaises(ValueError, ais._validate_and_create_industry_type, []) + self.assertRaises(ValueError, ais._validate_and_create_industry_type, "") + self.assertRaises(ValueError, ais._validate_and_create_industry_type, [""]) + self.assertRaises(ValueError, ais._validate_and_create_industry_type, ["Energy Sector", "Others", "Dams Sector"]) + self.assertRaises(ValueError, ais._validate_and_create_industry_type, "Energy Sector| Others|Dams Sector") + self.assertRaises(ValueError, ais._validate_and_create_industry_type, 3) + self.assertRaises(ValueError, ais._validate_and_create_industry_type, "|") + self.assertRaises(ValueError, ais._validate_and_create_industry_type, ["Energy Sector|Dams Sector"]) + + self.assertEqual(ais._validate_and_create_industry_type("eNergY sectOr"), ais.ENERGY_SECTOR) + self.assertEqual(ais._validate_and_create_industry_type("eNergY sectOr |Dams sector"), "Energy Sector|Dams Sector") + self.assertEqual(ais._validate_and_create_industry_type(["eNergY sectOr ", " Dams sectOr "]), "Energy Sector|Dams Sector") + self.assertEqual(ais._validate_and_create_industry_type([ais.ENERGY_SECTOR, ais.DAMS_SECTOR]), "Energy Sector|Dams Sector") + self.assertEqual(ais._validate_and_create_industry_type("Energy Sector|Dams Sector"), "Energy Sector|Dams Sector") + + for idx, x in enumerate(self.INDUSTRY_SECTORS): + self.assertEqual(ais._validate_and_create_industry_type(x), self.INDUSTRY_SECTORS[idx]) + + def test_add_ais_marking(self): + PACKAGE = STIXPackage() + + # Missing kwarg. + self.assertRaises(ValueError, ais.add_ais_marking, PACKAGE, True, 'NONE', 'GREEN', + country_name_code='US', + country_name_code_type='ISO 3166-1 alpha-2', + admin_area_name_code='US-DC', + admin_area_name_code_type='ISO 3166-2', + organisation_name='NCCIC') + + def test_bad_values_raises_error(self): + self.assertRaises(ValueError, ais.TLPMarkingType, "ORANGE") + self.assertRaises(ValueError, ais.AISConsentType, "WHAT?") + +if __name__ == "__main__": + unittest.main() diff --git a/stix/test/extensions/marking/simple_marking_test.py b/stix/test/extensions/marking/simple_marking_test.py index 755a76c8..7e434099 100644 --- a/stix/test/extensions/marking/simple_marking_test.py +++ b/stix/test/extensions/marking/simple_marking_test.py @@ -1,3 +1,6 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# See LICENSE.txt for complete terms. + import unittest from stix.test import EntityTestCase diff --git a/stix/test/extensions/marking/tlp_test.py b/stix/test/extensions/marking/tlp_test.py index 5ba879b4..8388908c 100644 --- a/stix/test/extensions/marking/tlp_test.py +++ b/stix/test/extensions/marking/tlp_test.py @@ -1,3 +1,6 @@ +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. +# See LICENSE.txt for complete terms. + import unittest from stix.test import EntityTestCase diff --git a/stix/test/extensions/structured_coa/__init__.py b/stix/test/extensions/structured_coa/__init__.py index 462a1719..9a9569c5 100644 --- a/stix/test/extensions/structured_coa/__init__.py +++ b/stix/test/extensions/structured_coa/__init__.py @@ -1,2 +1,2 @@ # Copyright (c) 2015, The MITRE Corporation. All rights reserved. -# See LICENSE.txt for complete terms. \ No newline at end of file +# See LICENSE.txt for complete terms. diff --git a/stix/test/extensions/structured_coa/generic_test.py b/stix/test/extensions/structured_coa/generic_test.py index 155409b2..bcd6ac63 100644 --- a/stix/test/extensions/structured_coa/generic_test.py +++ b/stix/test/extensions/structured_coa/generic_test.py @@ -5,6 +5,7 @@ from stix.extensions.structured_coa.generic_structured_coa import GenericStructuredCOA from stix.test import EntityTestCase + class GenericStructuredCOATests(EntityTestCase, unittest.TestCase): klass = GenericStructuredCOA _full_dict = { diff --git a/stix/test/extensions/test_mechanisms/__init__.py b/stix/test/extensions/test_mechanisms/__init__.py index 462a1719..9a9569c5 100644 --- a/stix/test/extensions/test_mechanisms/__init__.py +++ b/stix/test/extensions/test_mechanisms/__init__.py @@ -1,2 +1,2 @@ # Copyright (c) 2015, The MITRE Corporation. All rights reserved. -# See LICENSE.txt for complete terms. \ No newline at end of file +# See LICENSE.txt for complete terms. diff --git a/stix/test/extensions/test_mechanisms/openioc_test.py b/stix/test/extensions/test_mechanisms/openioc_test.py index 36fc19dc..8edaa213 100644 --- a/stix/test/extensions/test_mechanisms/openioc_test.py +++ b/stix/test/extensions/test_mechanisms/openioc_test.py @@ -1,12 +1,15 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. import unittest -import StringIO import lxml -from stix import utils +from mixbox import idgen +from mixbox.namespaces import Namespace +from mixbox.vendor.six import StringIO, BytesIO +import mixbox.xml + from stix.test import EntityTestCase from stix.extensions.test_mechanism.open_ioc_2010_test_mechanism import OpenIOCTestMechanism @@ -38,12 +41,11 @@ class OpenIOCTestMechanismTests(EntityTestCase, unittest.TestCase): class OpenIOCEtreeTests(unittest.TestCase): DESCRIPTION = "Finds Zeus variants, twexts, sdra64, ntos" XML = ( - """ + r""" Zeus @@ -117,35 +119,37 @@ class OpenIOCEtreeTests(unittest.TestCase): ) def setUp(self): - utils.set_id_namespace({"http://schemas.mandiant.com/2010/ioc": "mandiant-openioc"}) + ioc_ns = Namespace("http://stix.mitre.org/extensions/TestMechanism#OpenIOC2010-1", + "stix-openioc", '') + idgen.set_id_namespace(ioc_ns) def tearDown(self): - utils.set_id_namespace(utils.EXAMPLE_NAMESPACE) + idgen.set_id_namespace(idgen.EXAMPLE_NAMESPACE) def _test_xml(self, obj): xml = obj.to_xml() - parser = utils.parser.get_xml_parser() - tree = lxml.etree.parse(StringIO.StringIO(xml), parser=parser) + parser = mixbox.xml.get_xml_parser() + tree = lxml.etree.parse(BytesIO(xml), parser=parser) root = tree.getroot() - xpath = "//openioc:description" - nodes = root.xpath(xpath, namespaces={'openioc': 'http://schemas.mandiant.com/2010/ioc'}) + xpath = "//stix-openioc:ioc//description" + nodes = root.xpath(xpath, namespaces={'stix-openioc': 'http://stix.mitre.org/extensions/TestMechanism#OpenIOC2010-1'}) self.assertTrue(nodes is not None) self.assertEqual(len(nodes), 1) self.assertEqual(nodes[0].text, self.DESCRIPTION) def test_etree(self): - parser = utils.parser.get_xml_parser() - tree = lxml.etree.parse(StringIO.StringIO(self.XML), parser=parser) + parser = mixbox.xml.get_xml_parser() + tree = lxml.etree.parse(StringIO(self.XML), parser=parser) ext = OpenIOCTestMechanism() ext.ioc = tree self._test_xml(ext) def test_etree_dict(self): - parser = utils.parser.get_xml_parser() - tree = lxml.etree.parse(StringIO.StringIO(self.XML), parser=parser) + parser = mixbox.xml.get_xml_parser() + tree = lxml.etree.parse(StringIO(self.XML), parser=parser) ext = OpenIOCTestMechanism() ext.ioc = tree diff --git a/stix/test/incident_test.py b/stix/test/incident_test.py index 90bdd57d..55209a28 100644 --- a/stix/test/incident_test.py +++ b/stix/test/incident_test.py @@ -1,14 +1,17 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. import unittest -import StringIO + +from mixbox.vendor.six import StringIO from cybox.common import StructuredText -from stix.test import EntityTestCase, TypedListTestCase, data_marking_test +from stix.test import EntityTestCase +from stix.test import data_marking_test from stix.test.common import ( - confidence_test, information_source_test, statement_test, related_test + confidence_test, information_source_test, related_test, identity_test, + statement_test ) import stix.common.vocabs as vocabs @@ -64,14 +67,6 @@ class COATakenTest(EntityTestCase, unittest.TestCase): } -class COAsTakenTest(TypedListTestCase, unittest.TestCase): - klass = incident._COAsTaken - - _full_dict = [ - COATakenTest._full_dict, - ] - - class COARequestedTest(EntityTestCase, unittest.TestCase): klass = incident.COARequested @@ -89,15 +84,6 @@ class COARequestedTest(EntityTestCase, unittest.TestCase): } -class COAsRequestedTest(TypedListTestCase, unittest.TestCase): - klass = incident._COAsRequested - - _full_dict = [ - COARequestedTest._full_dict, - ] - - - class JournalEntryTest(EntityTestCase, unittest.TestCase): klass = history.JournalEntry @@ -161,28 +147,13 @@ class LeveragedTTPsTest(EntityTestCase, unittest.TestCase): } -class ExternalIDsTest(TypedListTestCase, unittest.TestCase): - klass = incident._ExternalIDs +class ExternalIDTest(EntityTestCase, unittest.TestCase): + klass = incident.ExternalID - _full_dict = [ - { - 'source': 'foo', - 'value': '478392-feb3ca-98a9ef-984392742' - }, - { - 'source': 'bar', - 'value': '478392-feb3ca-98a9ef-984392742' - }, - ] - - -class VictimsTest(TypedListTestCase, unittest.TestCase): - klass = incident._Victims - - _full_dict = [ - {'name': 'Spooderman'}, - {'name': 'Spooderman'} - ] + _full_dict = { + 'source': 'foo', + 'value': '478392-feb3ca-98a9ef-984392742' + } class TimeTest(EntityTestCase, unittest.TestCase): @@ -212,19 +183,6 @@ class CategoriesTest(EntityTestCase, unittest.TestCase): ] -class InformationSourcesTest(TypedListTestCase, unittest.TestCase): - klass = incident._InformationSources - - _full_dict = [ - { - 'description': 'Test', - 'identity': { - 'name': 'Spooderman' - } - }, - ] - - class TotalLossEstimationTest(EntityTestCase, unittest.TestCase): klass = impact_assessment.TotalLossEstimation @@ -360,6 +318,8 @@ class AffectedAssetTest(EntityTestCase, unittest.TestCase): _full_dict = { 'type': AssetTypeTest._full_dict, + 'description': 'Foo', + 'business_function_or_role': 'Bar', 'nature_of_security_effect': NatureOfSecurityEffectTest._full_dict, 'ownership_class': { 'value': 'Unknown', @@ -405,25 +365,6 @@ class RelatedIncidentsTests(EntityTestCase, unittest.TestCase): } -class IntendedEffectsTests(TypedListTestCase, unittest.TestCase): - klass = incident._IntendedEffects - - _full_dict = [ - statement_test.StatementTests._full_dict - ] - - -class DiscoveryMethodsTests(TypedListTestCase, unittest.TestCase): - klass = incident.DiscoveryMethods - - _full_dict = [ - { - 'value': 'Unknown', - 'xsi:type': 'stixVocabs:LocationClassVocab-1.0' - } - ] - - class IncidentTest(EntityTestCase, unittest.TestCase): klass = incident.Incident _full_dict = { @@ -434,19 +375,19 @@ class IncidentTest(EntityTestCase, unittest.TestCase): 'description': 'The Datacenter was broken into.', 'short_description': 'Short Description Title', 'handling': data_marking_test.MarkingTests._full_dict, - 'external_ids': ExternalIDsTest._full_dict, + 'external_ids': [ExternalIDTest._full_dict], 'attributed_threat_actors': AttributedThreatActorsTest._full_dict, 'categories': CategoriesTest._full_dict, - 'coa_taken': COAsTakenTest._full_dict, - 'coa_requested': COAsRequestedTest._full_dict, - 'coordinators': InformationSourcesTest._full_dict, + 'coa_taken': [COATakenTest._full_dict], + 'coa_requested': [COARequestedTest._full_dict], + 'coordinators': [information_source_test.InformationSourceTests._full_dict], 'impact_assessment': ImpactAssessmentTest._full_dict, 'leveraged_ttps': LeveragedTTPsTest._full_dict, 'related_indicators': RelatedIndicatorsTest._full_dict, 'reporter': information_source_test.InformationSourceTests._full_dict, - 'responders': InformationSourcesTest._full_dict, + 'responders': [information_source_test.InformationSourceTests._full_dict], 'time': TimeTest._full_dict, - 'victims': VictimsTest._full_dict, + 'victims': [identity_test.IdentityTests._full_dict], 'information_source': information_source_test.InformationSourceTests._full_dict, 'security_compromise': { "value": "Suspected", @@ -460,9 +401,15 @@ class IncidentTest(EntityTestCase, unittest.TestCase): 'affected_assets': AffectedAssetsTest._full_dict, 'related_observables': RelatedObservablesTest._full_dict, 'related_incidents': RelatedIncidentsTests._full_dict, - 'intended_effects': IntendedEffectsTests._full_dict, - 'discovery_methods': DiscoveryMethodsTests._full_dict, - 'confidence': confidence_test.ConfidenceTests._full_dict + 'intended_effects': [statement_test.StatementTests._full_dict], + 'discovery_methods': [{ + "value": "Security Alarm", + "xsi:type": "stixVocabs:DiscoveryMethodVocab-2.0" + }], + 'confidence': confidence_test.ConfidenceTests._full_dict, + 'related_packages': related_test.RelatedPackageRefsTests._full_dict, + 'contacts': [information_source_test.InformationSourceTests._full_dict], + 'url': 'http://www.example.com/' } def test_parse_category(self): @@ -486,7 +433,7 @@ def test_description_output(self): assets.add_Affected_Asset(asset) incident.Affected_Assets = assets - s = StringIO.StringIO() + s = StringIO() incident.export(s.write, 0, {'http://stix.mitre.org/Incident-1': 'incident'}) xml = s.getvalue() @@ -509,7 +456,7 @@ def test_add_related_observable(self): # Test that this fails self.assertRaises( - ValueError, + TypeError, i.add_related_observable, "THIS SHOULD FAIL" ) @@ -530,7 +477,7 @@ def test_add_related_indicator(self): # Test that this fails self.assertRaises( - ValueError, + TypeError, i.add_related_indicator, "THIS SHOULD FAIL" ) diff --git a/stix/test/indicator_test.py b/stix/test/indicator_test.py index c8dc3e55..daf4bb1a 100644 --- a/stix/test/indicator_test.py +++ b/stix/test/indicator_test.py @@ -1,10 +1,20 @@ # Copyright (c) 2015, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from datetime import datetime import unittest -from stix.indicator import Indicator -from stix.test import EntityTestCase, round_trip, round_trip_dict +from cybox.core import Observable, ObservableComposition +from cybox.objects.file_object import File +import mixbox.typedlist +from mixbox.vendor.six import text_type + +from stix.common.vocabs import IndicatorType, VocabString +from stix.indicator import Indicator, RelatedCampaignRefs, ValidTime + +from stix.test import EntityTestCase +from stix.test.common import related_test + class IndicatorTest(EntityTestCase, unittest.TestCase): klass = Indicator @@ -25,7 +35,6 @@ class IndicatorTest(EntityTestCase, unittest.TestCase): def test_base(self): self._test_partial_dict(self._base_dict) - def test_negate(self): d = dict(self._base_dict.items()) d['negate'] = False @@ -33,6 +42,8 @@ def test_negate(self): d2 = self.klass.from_dict(d).to_dict() self.assertTrue('negate' not in d2) + o2 = self.klass.from_dict(d).to_obj() + self.assertTrue(o2.negate is None) def test_indicator_types(self): d = { @@ -179,7 +190,7 @@ def test_producer(self): ] } }, - 'xsi:type': 'ciqIdentity:CIQIdentity3.0InstanceType' + 'xsi:type': 'stix-ciqidentity:CIQIdentity3.0InstanceType' }, 'time': {'produced_time': '2014-02-21T10:16:14.947201'} } @@ -409,5 +420,110 @@ def test_related_packages(self): self._test_partial_dict(d) + def test_indicator_type_hashing(self): + # https://github.com/STIXProject/python-stix/issues/338 + vocab_str = IndicatorType(value=IndicatorType.TERM_C2) + indicator_set = set([vocab_str]) + + def test_datetime_format(self): + indicator = Indicator(title="title") + valid_time = ValidTime(start_time=datetime.strptime("2010-03-05", + "%Y-%m-%d")) + indicator.add_valid_time_position(valid_time) + + ixml = indicator.to_xml() + self.assertTrue("2010-03-05T" in text_type(ixml)) + + def test_observables_property_empty(self): + ind = Indicator() + ind2 = Indicator.from_dict(ind.to_dict()) + + self.assertEqual([], ind2.observables) + + def test_observables_property_composition(self): + f1 = File() + f1.file_name = "README.txt" + f2 = File() + f2.file_name = "README2.txt" + obs1 = Observable(f1) + obs2 = Observable(f2) + + comp = Observable(ObservableComposition('AND', [obs1, obs2])) + + ind = Indicator() + ind.observable = comp + ind2 = Indicator.from_dict(ind.to_dict()) + self.assertEqual([obs1.to_dict(), obs2.to_dict()], + [x.to_dict() for x in ind2.observables]) + + def test_observables_property_standard(self): + f = File() + f.file_name = "README.txt" + obs = Observable(f) + ind = Indicator() + ind.observable = obs + + ind2 = Indicator.from_dict(ind.to_dict()) + + self.assertEqual([obs.to_dict()], + [x.to_dict() for x in ind2.observables]) + + def test_set_indicator_observables_to_single_observable(self): + # https://github.com/STIXProject/python-stix/issues/325 + i = Indicator() + o1 = Observable() + o2 = Observable() + + i.observables = o1 + self.assertEqual(type([]), type(i.observables)) + self.assertEqual(1, len(i.observables)) + + def test_set_indicator_observables_to_list_of_two_observables(self): + # https://github.com/STIXProject/python-stix/issues/325 + i = Indicator() + o1 = Observable() + o2 = Observable() + + i.observables = [o1, o2] + self.assertEqual(mixbox.typedlist.TypedList, type(i.observables)) + self.assertEqual(2, len(i.observables)) + + def test_set_indicator_observables_to_list_of_one_observable(self): + # https://github.com/STIXProject/python-stix/issues/325 + i = Indicator() + o1 = Observable() + o2 = Observable() + + i.observables = [o1] + self.assertEqual(type([]), type(i.observables)) + self.assertEqual(1, len(i.observables)) + + +class RelatedCampaignReferencesTests(unittest.TestCase, EntityTestCase): + klass = RelatedCampaignRefs + _full_dict = { + 'related_campaigns': [ + related_test.RelatedCampaignRefTests._full_dict + ] + } + + def test_add_campaign(self): + from stix.campaign import Campaign + + l = RelatedCampaignRefs() + l.append(Campaign()) + + self.assertEqual(1, len(l)) + + def test_append_bad_type(self): + l = RelatedCampaignRefs() + + self.assertRaises( + TypeError, + l.append, + Indicator() + ) + + if __name__ == "__main__": unittest.main() diff --git a/stix/test/threat_actor_test.py b/stix/test/threat_actor_test.py index c61d26e9..73a3e93d 100644 --- a/stix/test/threat_actor_test.py +++ b/stix/test/threat_actor_test.py @@ -1,9 +1,10 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. import unittest -from stix.test import EntityTestCase, TypedListTestCase, data_marking_test +from stix.test import EntityTestCase, TypedListTestCase +from stix.test import data_marking_test from stix.test.common import ( confidence_test, information_source_test, related_test,identity_test ) @@ -12,69 +13,87 @@ class TypesTests(TypedListTestCase, unittest.TestCase): - klass = ta._Types + klass = ta.ThreatActor - _full_dict = [ + _partial_dict = [ { 'value': { - "value" : "Hacker", - "xsi:type" : "stixVocabs:ThreatActorTypeVocab-1.0" + "value": "Hacker", + "xsi:type": "stixVocabs:ThreatActorTypeVocab-1.0" } }, ] + _full_dict = { + "types": _partial_dict + } + class MotivationsTests(TypedListTestCase, unittest.TestCase): - klass = ta._Motivations + klass = ta.ThreatActor - _full_dict = [ - { + _partial_dict = [{ 'value': { "value" : "Ego", "xsi:type" : "stixVocabs:MotivationVocab-1.1" } - }, - ] + }] + + _full_dict = { + "motivations": _partial_dict + } class SophisticationTests(TypedListTestCase, unittest.TestCase): - klass = ta._Sophistications + klass = ta.ThreatActor - _full_dict = [ - { - 'value': { - "value" : "Novice", - "xsi:type" : "stixVocabs:ThreatActorSophisticationVocab-1.0" - } - }, + _partial_dict = [ + { + 'value': { + "value": "Novice", + "xsi:type": "stixVocabs:ThreatActorSophisticationVocab-1.0" + } + } ] + _full_dict = { + "sophistications": _partial_dict + } + class IntendedEffectsTests(TypedListTestCase, unittest.TestCase): - klass = ta._IntendedEffects + klass = ta.ThreatActor - _full_dict = [ + _partial_dict = [ { 'value': { - "value" : "Destruction", - "xsi:type" : "stixVocabs:IntendedEffectVocab-1.0" + "value": "Destruction", + "xsi:type": "stixVocabs:IntendedEffectVocab-1.0" } } ] + _full_dict = { + "intended_effects": _partial_dict + } + class PlanningAndOperationalSupportTests(TypedListTestCase, unittest.TestCase): - klass = ta._PlanningAndOperationalSupports + klass = ta.ThreatActor - _full_dict = [ + _partial_dict = [ { 'value': { - "value" : "Data Exploitation", - "xsi:type" : 'stixVocabs:PlanningAndOperationalSupportVocab-1.0.1' + "value": "Data Exploitation", + "xsi:type": 'stixVocabs:PlanningAndOperationalSupportVocab-1.0.1' } - }, + } ] + _full_dict = { + "planning_and_operational_supports": _partial_dict + } + class ObservedTTPsTests(EntityTestCase, unittest.TestCase): klass = ta.ObservedTTPs @@ -114,16 +133,16 @@ class ThreatActorTests(EntityTestCase, unittest.TestCase): _full_dict = { 'id': 'example:ThreatActor-1', 'timestamp': "2014-01-31T06:14:46", - 'version': '1.1', + 'version': '1.1.1', 'title': "BadGuy1", 'description': "This is a long description about a threat actor.", 'short_description': "A bad guy", 'identity': identity_test.IdentityTests._full_dict, - 'types': TypesTests._full_dict, - 'motivations': MotivationsTests._full_dict, - 'sophistications': SophisticationTests._full_dict, - 'intended_effects': IntendedEffectsTests._full_dict, - 'planning_and_operational_supports': PlanningAndOperationalSupportTests._full_dict, + 'types': TypesTests._partial_dict, + 'motivations': MotivationsTests._partial_dict, + 'sophistications': SophisticationTests._partial_dict, + 'intended_effects': IntendedEffectsTests._partial_dict, + 'planning_and_operational_supports': PlanningAndOperationalSupportTests._partial_dict, 'observed_ttps': ObservedTTPsTests._full_dict, 'associated_campaigns': AssocaitedCampaignsTests._full_dict, 'associated_actors': AssociatedActorsTests._full_dict, diff --git a/stix/test/ttp_test.py b/stix/test/ttp_test.py index 73808775..624e38de 100644 --- a/stix/test/ttp_test.py +++ b/stix/test/ttp_test.py @@ -9,7 +9,7 @@ import stix.ttp as ttp from stix.ttp import ( resource, infrastructure, exploit_targets, malware_instance, exploit, - attack_pattern, behavior + attack_pattern, behavior, victim_targeting ) @@ -41,9 +41,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" @@ -60,7 +60,13 @@ class ResourcesTests(EntityTestCase, unittest.TestCase): 'personas': PersonasTests._full_dict, 'tools': [ { - 'title': "Tool" + 'title': "Tool", + 'type': [ + { + 'value': 'Malware', + 'xsi:type': 'stixVocabs:AttackerToolTypeVocab-1.0' + } + ] } ], 'infrastructure': InfrastructureTests._full_dict @@ -70,7 +76,7 @@ class ResourcesTests(EntityTestCase, unittest.TestCase): class MalwareInstanceTests(EntityTestCase, unittest.TestCase): klass = malware_instance.MalwareInstance - _full_dict = _full_dict = { + _full_dict = { 'id': 'example:test-1', 'title': 'Title', 'description': 'Description', @@ -135,6 +141,44 @@ class BehaviorTests(EntityTestCase, unittest.TestCase): 'attack_patterns': AttackPatternsTests._full_dict } + +class VictimTargetingTests(EntityTestCase, unittest.TestCase): + klass = victim_targeting.VictimTargeting + + _full_dict = { + 'identity': { + 'specification': { + 'organisation_info': { + 'industry_type': 'Electricity, Industrial Control Systems' + } + }, + 'xsi:type': 'stix-ciqidentity:CIQIdentity3.0InstanceType' + }, + 'targeted_systems': [ + { + 'value': 'Industrial Control Systems', + 'xsi:type': 'stixVocabs:SystemTypeVocab-1.0' + } + ], + 'targeted_information': [ + { + 'value': 'Information Assets - Intellectual Property', + 'xsi:type': 'stixVocabs:InformationTypeVocab-1.0' + } + ], + 'targeted_technical_details': { + 'cybox_major_version': '2', + 'cybox_minor_version': '1', + 'cybox_update_version': '0', + 'observables': [ + { + 'idref': "example:Observable-2" + } + ] + } + } + + class TTPTests(EntityTestCase, unittest.TestCase): klass = ttp.TTP _full_dict = { @@ -146,8 +190,10 @@ class TTPTests(EntityTestCase, unittest.TestCase): 'resources': ResourcesTests._full_dict, 'handling': data_marking_test.MarkingTests._full_dict, 'exploit_targets': ExploitTargetsTests._full_dict, - 'behavior': BehaviorTests._full_dict + 'behavior': BehaviorTests._full_dict, + 'victim_targeting': VictimTargetingTests._full_dict } + if __name__ == "__main__": unittest.main() diff --git a/stix/test/utils/nsparser_test.py b/stix/test/utils/nsparser_test.py index d3f7452d..0e5c14fe 100644 --- a/stix/test/utils/nsparser_test.py +++ b/stix/test/utils/nsparser_test.py @@ -1,90 +1,27 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. # stdlib -import StringIO import unittest -import warnings # external import lxml.etree +from mixbox.vendor.six import StringIO # internal -import stix +import mixbox.namespaces from stix.core import STIXPackage -from stix.utils import nsparser - - -NSMAP = { - "test:a": "a", - "test:b": "b", - "test:c": "c" -} - - -SCHEMALOCS = { - "test:a": "/dev/null", - "test:b": "/dev/null", - "test:c": "/dev/null" -} - - -class A(stix.Entity): - _namespace = "test:a" - _XSI_TYPE = "a:AType" - - -class B(A): - _namespace = "test:b" - _XSI_TYPE = "b:BType" - - -class C(B): - _namespace = "test:c" - _XSI_TYPE = "c:CType" +from stix.utils import silence_warnings class NamespaceInfoTests(unittest.TestCase): - def test_nsinfo_collect(self): - """Tests that the NamespaceInfo.collect() method correctly ascends the MRO - of input objects. - - """ - nsinfo = nsparser.NamespaceInfo() - - # Collect classes - nsinfo.collect(C()) - - # Parse collected classes - nsinfo._parse_collected_classes() - - self.assertEqual(len(nsinfo._collected_namespaces), 4) # noqa - - def test_namespace_collect(self): - """Test that NamespaceInfo correctly pulls namespaces from all classes - in an objects MRO. - - """ - nsinfo = nsparser.NamespaceInfo() - - # Collect classes - nsinfo.collect(C()) - - # finalize the namespace dictionary - nsinfo.finalize(ns_dict=NSMAP, schemaloc_dict=SCHEMALOCS) - namespaces = nsinfo.finalized_namespaces.values() - - self.assertTrue(all(ns in namespaces for ns in NSMAP.iterkeys())) + @silence_warnings def test_user_provided_ns(self): """Test that user-provided namespaces are serialized. """ p = STIXPackage() - nsinfo = nsparser.NamespaceInfo() - - # Collect classes - nsinfo.collect(p) TEST_PREFIX = 'test' TEST_NS = 'a:unit:test' @@ -96,22 +33,14 @@ def test_user_provided_ns(self): NEW_STIX_NS: NEW_STIX_PREFIX } - finalized = nsinfo._finalize_namespaces(ns_dict=test_dict) - nsinfo.finalized_namespaces - - self.assertEqual(finalized.get(TEST_PREFIX), TEST_NS) - self.assertEqual(finalized.get(NEW_STIX_PREFIX), NEW_STIX_NS) - - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - xml = p.to_xml(ns_dict=test_dict) - # Parse the exported document and make sure that the namespaces # made it through the serialization process. + xml = p.to_xml(ns_dict=test_dict) e = lxml.etree.XML(xml) self.assertEqual(e.nsmap.get(TEST_PREFIX), TEST_NS) self.assertEqual(e.nsmap.get(NEW_STIX_PREFIX), NEW_STIX_NS) + @silence_warnings def test_duplicate_ns_prefix(self): """Test that duplicate namespace prefix mappings raise errors. @@ -120,30 +49,36 @@ def test_duplicate_ns_prefix(self): bad = {'bad:ns': 'stix'} # 'stix' is already default ns prefix self.assertRaises( - nsparser.DuplicatePrefixError, + mixbox.namespaces.DuplicatePrefixError, p.to_xml, ns_dict=bad ) # Build a valid stix document that has a default namespace remapped - # to another namespace. We remap 'cybox' to a bogus ns here. + # to another namespace. We remap 'stixCommon' to a bogus ns here. xml = ( """""" + timestamp="2015-04-09T14:22:25.620831"> + + A unit test + + """ ) - sio = StringIO.StringIO(xml) + sio = StringIO(xml) p = STIXPackage.from_xml(sio) # Exporting should raise an error. self.assertRaises( - nsparser.DuplicatePrefixError, + mixbox.namespaces.DuplicatePrefixError, p.to_xml ) + @silence_warnings def test_parsed_namespaces(self): """Test that non-default namespaces make it through the parse-serialize process. @@ -164,16 +99,13 @@ def test_parsed_namespaces(self): xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="example:Package-e2454ee8-e59c-43ac-a085-46ae4516fd6e" version="1.1.1" - timestamp="2015-04-09T14:22:25.620831+00:00"/>""" + timestamp="2015-04-09T14:22:25.620831"/>""" ) - sio = StringIO.StringIO(xml) + sio = StringIO(xml) p = STIXPackage.from_xml(sio) - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - serialized = p.to_xml() - + serialized = p.to_xml() e = lxml.etree.XML(serialized) self.assertEqual(e.nsmap.get('TEST'), 'a:test') self.assertEqual(e.nsmap.get('FOO'), 'a:foo') @@ -181,4 +113,4 @@ def test_parsed_namespaces(self): if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/stix/threat_actor/__init__.py b/stix/threat_actor/__init__.py index 53346b94..8ee721af 100644 --- a/stix/threat_actor/__init__.py +++ b/stix/threat_actor/__init__.py @@ -1,44 +1,64 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# mixbox +from mixbox import fields + +# internal import stix -from stix.data_marking import Marking import stix.bindings.threat_actor as threat_actor_binding -from stix.common import vocabs, Confidence, Identity, Statement +from stix.common import vocabs, Confidence, Statement +from stix.common.identity import Identity, IdentityFactory from stix.common.related import ( GenericRelationshipList, RelatedCampaign, RelatedPackageRefs, RelatedTTP, RelatedThreatActor ) +from stix.common.statement import StatementField +from stix.common.information_source import InformationSource + class ObservedTTPs(GenericRelationshipList): _namespace = 'http://stix.mitre.org/ThreatActor-1' _binding = threat_actor_binding _binding_class = threat_actor_binding.ObservedTTPsType - _binding_var = "Observed_TTP" - _contained_type = RelatedTTP - _inner_name = "ttps" + + observed_ttp = fields.TypedField("Observed_TTP", RelatedTTP, multiple=True, key_name="ttps") class AssociatedActors(GenericRelationshipList): _namespace = 'http://stix.mitre.org/ThreatActor-1' _binding = threat_actor_binding _binding_class = threat_actor_binding.AssociatedActorsType - _binding_var = "Associated_Actor" - _contained_type = RelatedThreatActor - _inner_name = "threat_actors" + + associated_actor = fields.TypedField("Associated_Actor", RelatedThreatActor, multiple=True, key_name="threat_actors") class AssociatedCampaigns(GenericRelationshipList): _namespace = 'http://stix.mitre.org/ThreatActor-1' _binding = threat_actor_binding _binding_class = threat_actor_binding.AssociatedCampaignsType - _binding_var = "Associated_Campaign" - _contained_type = RelatedCampaign - _inner_name = "campaigns" + + associated_campaign = fields.TypedField("Associated_Campaign", RelatedCampaign, multiple=True, key_name="campaigns") class ThreatActor(stix.BaseCoreComponent): + """Implementation of the STIX Threat Actor. + + Args: + id_ (optional): An identifier. If ``None``, a value will be generated + via ``mixbox.idgen.create_id()``. If set, this will unset the + ``idref`` property. + idref (optional): An identifier reference. If set this will unset the + ``id_`` property. + timestamp (optional): A timestamp value. Can be an instance of + ``datetime.datetime`` or ``str``. + description: A description of the purpose or intent of this object. + short_description: A short description of the intent + or purpose of this object. + title: The title of this object. + + """ _binding = threat_actor_binding _binding_class = threat_actor_binding.ThreatActorType _namespace = 'http://stix.mitre.org/ThreatActor-1' @@ -46,6 +66,19 @@ class ThreatActor(stix.BaseCoreComponent): _ALL_VERSIONS = ("1.0", "1.0.1", "1.1", "1.1.1") _ID_PREFIX = 'threatactor' + identity = fields.TypedField("Identity", Identity, factory=IdentityFactory) + types = StatementField("Type", Statement, vocab_type=vocabs.ThreatActorType, multiple=True, key_name="types") + motivations = StatementField("Motivation", Statement, vocab_type=vocabs.Motivation, multiple=True, key_name="motivations") + sophistications = StatementField("Sophistication", Statement, vocab_type=vocabs.ThreatActorSophistication, multiple=True, key_name="sophistications") + intended_effects = StatementField("Intended_Effect", Statement, vocab_type=vocabs.IntendedEffect, multiple=True, key_name="intended_effects") + planning_and_operational_supports = StatementField("Planning_And_Operational_Support", Statement, vocab_type=vocabs.PlanningAndOperationalSupport, multiple=True, key_name="planning_and_operational_supports") + confidence = fields.TypedField("Confidence", Confidence) + observed_ttps = fields.TypedField("Observed_TTPs", ObservedTTPs) + associated_campaigns = fields.TypedField("Associated_Campaigns", AssociatedCampaigns) + associated_actors = fields.TypedField("Associated_Actors", AssociatedActors) + related_packages = fields.TypedField("Related_Packages", RelatedPackageRefs) + information_source = fields.TypedField("Information_Source", InformationSource) + def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): @@ -58,209 +91,53 @@ def __init__(self, id_=None, idref=None, timestamp=None, title=None, short_description=short_description ) - self.identity = None - self.types = None - self.motivations = None - self.sophistications = None - self.intended_effects = None - self.planning_and_operational_supports = None - self.handling = None - self.confidence = None self.observed_ttps = ObservedTTPs() self.associated_campaigns = AssociatedCampaigns() self.associated_actors = AssociatedActors() self.related_packages = RelatedPackageRefs() - - @property - def identity(self): - return self._identity - - @identity.setter - def identity(self, value): - self._set_var(Identity, try_cast=False, identity=value) - - @property - def types(self): - return self._types - - @types.setter - def types(self, value): - self._types = _Types(value) def add_type(self, value): - self.types.append(value) + """Adds a :class:`.VocabString` object to the :attr:`types` collection. - @property - def motivations(self): - return self._motivations - - @motivations.setter - def motivations(self, value): - self._motivations = _Motivations(value) + If set to a string, an attempt will be made to convert it into an + instance of :class:`.ThreatActorType`. + + """ + self.types.append(value) def add_motivation(self, value): + """Adds a :class:`.Motivation` object to the :attr:`motivations` + collection. + + """ self.motivations.append(value) - @property - def sophistications(self): - return self._sophistications - - @sophistications.setter - def sophistications(self, value): - self._sophistications = _Sophistications(value) - def add_sophistication(self, value): - self._sophistications.append(value) + """Adds a :class:`.VocabString` object to the :attr:`sophistications` + collection. - @property - def intended_effects(self): - return self._intended_effects - - @intended_effects.setter - def intended_effects(self, value): - self._intended_effects = _IntendedEffects(value) - - def add_intended_effect(self, value): - self.intended_effects.append(value) + If `value` is a string, an attempt will be made to convert it to an + instance of :class:`.ThreatActorSophistication`. - @property - def planning_and_operational_supports(self): - return self._planning_and_operational_supports - - @planning_and_operational_supports.setter - def planning_and_operational_supports(self, value): - self._planning_and_operational_supports = _PlanningAndOperationalSupports(value) + """ + self.sophistications.append(value) - def add_planning_and_operational_support(self, value): - self.planning_and_operational_supports.append(value) - - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() - - super(ThreatActor, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if self.identity: - return_obj.Identity = self.identity.to_obj(ns_info=ns_info) - if self.types: - return_obj.Type = self.types.to_obj(ns_info=ns_info) - if self.motivations: - return_obj.Motivation = self.motivations.to_obj(ns_info=ns_info) - if self.sophistications: - return_obj.Sophistication = self.sophistications.to_obj(ns_info=ns_info) - if self.intended_effects: - return_obj.Intended_Effect = self.intended_effects.to_obj(ns_info=ns_info) - if self.planning_and_operational_supports: - return_obj.Planning_And_Operational_Support = \ - self.planning_and_operational_supports.to_obj(ns_info=ns_info) - if self.observed_ttps: - return_obj.Observed_TTPs = self.observed_ttps.to_obj(ns_info=ns_info) - if self.associated_campaigns: - return_obj.Associated_Campaigns = self.associated_campaigns.to_obj(ns_info=ns_info) - if self.associated_actors: - return_obj.Associated_Actors = self.associated_actors.to_obj(ns_info=ns_info) - if self.handling: - return_obj.Handling = self.handling.to_obj(ns_info=ns_info) - if self.confidence: - return_obj.Confidence = self.confidence.to_obj(ns_info=ns_info) - if self.related_packages: - return_obj.Related_Packages = self.related_packages.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - super(ThreatActor, cls).from_obj(obj, return_obj=return_obj) - - if isinstance(obj, cls._binding_class): # ThreatActorType properties - return_obj.identity = Identity.from_obj(obj.Identity) - return_obj.types = _Types.from_obj(obj.Type) - return_obj.motivations = _Motivations.from_obj(obj.Motivation) - return_obj.sophistications = _Sophistications.from_obj(obj.Sophistication) - return_obj.intended_effects = _IntendedEffects.from_obj(obj.Intended_Effect) - return_obj.planning_and_operational_supports = \ - _PlanningAndOperationalSupports.from_obj(obj.Planning_And_Operational_Support) - return_obj.observed_ttps = ObservedTTPs.from_obj(obj.Observed_TTPs) - return_obj.associated_campaigns = AssociatedCampaigns.from_obj(obj.Associated_Campaigns) - return_obj.associated_actors = AssociatedActors.from_obj(obj.Associated_Actors) - return_obj.handling = Marking.from_obj(obj.Handling) - return_obj.confidence = Confidence.from_obj(obj.Confidence) - return_obj.related_packages = RelatedPackageRefs.from_obj(obj.Related_Packages) - - return return_obj - - def to_dict(self): - return super(ThreatActor, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - super(ThreatActor, cls).from_dict(dict_repr, return_obj=return_obj) - - get = dict_repr.get - return_obj.identity = Identity.from_dict(get('identity')) - return_obj.types = _Types.from_dict(get('types')) - return_obj.motivations = _Motivations.from_dict(get('motivations')) - return_obj.sophistications = _Sophistications.from_dict(get('sophistications')) - return_obj.intended_effects = _IntendedEffects.from_dict(get('intended_effects')) - return_obj.planning_and_operational_supports = \ - _PlanningAndOperationalSupports.from_dict(get('planning_and_operational_supports')) - return_obj.observed_ttps = ObservedTTPs.from_dict(get('observed_ttps')) - return_obj.associated_campaigns = AssociatedCampaigns.from_dict(get('associated_campaigns')) - return_obj.associated_actors = AssociatedActors.from_dict(get('associated_actors')) - return_obj.handling = Marking.from_dict(get('handling')) - return_obj.confidence = Confidence.from_dict(get('confidence')) - return_obj.related_packages = RelatedPackageRefs.from_dict(get('related_packages')) - - return return_obj - - -# NOT ACTUAL STIX TYPES! -class _Sophistications(stix.TypedList): - _contained_type = Statement - - def _fix_value(self, value): - sophistication = vocabs.ThreatActorSophistication(value) - return Statement(value=sophistication) - - -class _Motivations(stix.TypedList): - _contained_type = Statement - - def _fix_value(self, value): - motivation = vocabs.Motivation(value) - return Statement(value=motivation) - - -class _IntendedEffects(stix.TypedList): - _contained_type = Statement - - def _fix_value(self, value): - intended_effect = vocabs.IntendedEffect(value) - return Statement(value=intended_effect) - + def add_intended_effect(self, value): + """Adds a :class:`.Statement` object to the :attr:`intended_effects` + collection. -class _PlanningAndOperationalSupports(stix.TypedList): - _contained_type = Statement + If `value` is a string, an attempt will be made to convert it into an + instance of :class:`.Statement`. - def _fix_value(self, value): - pos = vocabs.PlanningAndOperationalSupport(value) - return Statement(value=pos) + """ + self.intended_effects.append(value) + def add_planning_and_operational_support(self, value): + """Adds a :class:`.VocabString` object to the + :attr:`planning_and_operational_supports` collection. -class _Types(stix.TypedList): - _contained_type = Statement + If `value` is a string, an attempt will be made to convert it to an + instance of :class:`.PlanningAndOperationalSupport`. - def _fix_value(self, value): - type_ = vocabs.ThreatActorType(value) - return Statement(value=type_) + """ + self.planning_and_operational_supports.append(value) diff --git a/stix/ttp/__init__.py b/stix/ttp/__init__.py index 19656ffa..d019545c 100644 --- a/stix/ttp/__init__.py +++ b/stix/ttp/__init__.py @@ -1,19 +1,43 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + # internal import stix import stix.bindings.ttp as ttp_binding -from stix.common import vocabs, Statement -from stix.data_marking import Marking +from stix.common import vocabs +from stix.common import Statement +from stix.common.kill_chains import KillChainPhasesReference +from stix.common.related import RelatedPackageRefs +from stix.common.statement import StatementField +from stix.ttp.related_ttps import RelatedTTPs +from stix.ttp.exploit_targets import ExploitTargets # relative from .behavior import Behavior from .resource import Resource from .victim_targeting import VictimTargeting +from stix.common.information_source import InformationSource class TTP(stix.BaseCoreComponent): + """Implementation of the STIX TTP. + + Args: + id_ (optional): An identifier. If ``None``, a value will be generated + via ``mixbox.idgen.create_id()``. If set, this will unset the + ``idref`` property. + idref (optional): An identifier reference. If set this will unset the + ``id_`` property. + timestamp (optional): A timestamp value. Can be an instance of + ``datetime.datetime`` or ``str``. + description: A description of the purpose or intent of this object. + short_description: A short description of the intent + or purpose of this object. + title: The title of this object. + + """ _binding = ttp_binding _binding_class = _binding.TTPType _namespace = "http://stix.mitre.org/TTP-1" @@ -21,6 +45,16 @@ class TTP(stix.BaseCoreComponent): _ALL_VERSIONS = ("1.0", "1.0.1", "1.1", "1.1.1") _ID_PREFIX = "ttp" + behavior = fields.TypedField("Behavior", Behavior) + related_ttps = fields.TypedField("Related_TTPs", RelatedTTPs) + intended_effects = StatementField("Intended_Effect", Statement, vocab_type=vocabs.IntendedEffect, multiple=True) + resources = fields.TypedField("Resources", Resource) + victim_targeting = fields.TypedField("Victim_Targeting", VictimTargeting) + exploit_targets = fields.TypedField("Exploit_Targets", ExploitTargets) + related_packages = fields.TypedField("Related_Packages", RelatedPackageRefs) + kill_chain_phases = fields.TypedField("Kill_Chain_Phases", KillChainPhasesReference) + information_source = fields.TypedField("Information_Source", InformationSource) + def __init__(self, id_=None, idref=None, timestamp=None, title=None, description=None, short_description=None): @@ -33,156 +67,82 @@ def __init__(self, id_=None, idref=None, timestamp=None, title=None, short_description=short_description ) - self.behavior = None - self.related_ttps = None - self.intended_effects = None - self.resources = None - self.victim_targeting = None - self.handling = None + self.related_packages = RelatedPackageRefs() self.exploit_targets = ExploitTargets() + self.related_ttps = RelatedTTPs() + self.kill_chain_phases = KillChainPhasesReference() - @property - def behavior(self): - return self._behavior - - @behavior.setter - def behavior(self, value): - self._set_var(Behavior, try_cast=False, behavior=value) - - @property - def related_ttps(self): - return self._related_ttps - - @related_ttps.setter - def related_ttps(self, value): - if isinstance(value, RelatedTTPs): - self._related_ttps = value - else: - self._related_ttps = RelatedTTPs(value) - - @property - def exploit_targets(self): - return self._exploit_targets + def add_related_ttp(self, value): + """Adds an Related TTP to the :attr:`related_ttps` list + property of this :class:`TTP`. - @exploit_targets.setter - def exploit_targets(self, value): - if isinstance(value, ExploitTargets): - self._exploit_targets = value - else: - self._exploit_targets = ExploitTargets(value) - - @property - def intended_effects(self): - return self._intended_effects - - @intended_effects.setter - def intended_effects(self, value): - self._intended_effects = _IntendedEffects(value) - - def add_intended_effect(self, value): - self.intended_effects.append(value) + The `TTP` parameter must be an instance of + :class:`.RelatedTTP` or :class:`TTP`. - @property - def resources(self): - return self._resources + If the `TTP` parameter is ``None``, no item wil be added to the + ``related_ttps`` list property. - @resources.setter - def resources(self, value): - self._set_var(Resource, resources=value) + Calling this method is the same as calling ``append()`` on the + ``related_ttps`` property. - @property - def victim_targeting(self): - return self._victim_targeting + See Also: + The :class:`RelatedTTPs` documentation. - @victim_targeting.setter - def victim_targeting(self, value): - self._set_var(VictimTargeting, try_cast=False, victim_targeting=value) + Note: + If the `TTP` parameter is not an instance of + :class:`.RelatedTTP` an attempt will be + made to convert it to one. - @property - def handling(self): - return self._handling + Args: + value: An instance of :class:`TTP` or + :class:`.RelatedTTP`. - @handling.setter - def handling(self, value): - self._set_var(Marking, try_cast=False, handling=value) + Raises: + ValueError: If the `TTP` parameter cannot be converted into + an instance of :class:`.RelatedTTP` - def to_obj(self, return_obj=None, ns_info=None): - if not return_obj: - return_obj = self._binding_class() + """ + self.related_ttps.append(value) - super(TTP, self).to_obj(return_obj=return_obj, ns_info=ns_info) + def add_exploit_target(self, value): + """Adds a :class:`.ExploitTarget` object to the :attr:`exploit_targets` + collection. - if self.behavior: - return_obj.Behavior = self.behavior.to_obj(ns_info=ns_info) - if self.related_ttps: - return_obj.Related_TTPs = self.related_ttps.to_obj(ns_info=ns_info) - if self.exploit_targets: - return_obj.Exploit_Targets = self.exploit_targets.to_obj(ns_info=ns_info) - if self.intended_effects: - return_obj.Intended_Effect = self.intended_effects.to_obj(ns_info=ns_info) - if self.resources: - return_obj.Resources = self.resources.to_obj(ns_info=ns_info) - if self.victim_targeting: - return_obj.Victim_Targeting = self.victim_targeting.to_obj(ns_info=ns_info) - if self.handling: - return_obj.Handling = self.handling.to_obj(ns_info=ns_info) + """ + self.exploit_targets.append(value) - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - super(TTP, cls).from_obj(obj, return_obj=return_obj) - - if isinstance(obj, cls._binding_class): - return_obj.behavior = Behavior.from_obj(obj.Behavior) - return_obj.related_ttps = RelatedTTPs.from_obj(obj.Related_TTPs) - return_obj.exploit_targets = ExploitTargets.from_obj(obj.Exploit_Targets) - return_obj.resources = Resource.from_obj(obj.Resources) - return_obj.victim_targeting = VictimTargeting.from_obj(obj.Victim_Targeting) - return_obj.handling = Marking.from_obj(obj.Handling) - return_obj.intended_effects = _IntendedEffects.from_obj(obj.Intended_Effect) - - return return_obj - - def to_dict(self): - return super(TTP, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() + def add_intended_effect(self, value): + """Adds a :class:`.Statement` object to the :attr:`intended_effects` + collection. - super(TTP, cls).from_dict(dict_repr, return_obj=return_obj) + If `value` is a string, an attempt will be made to convert it into an + instance of :class:`.Statement`. - get = dict_repr.get - return_obj.behavior = Behavior.from_dict(get('behavior')) - return_obj.related_ttps = RelatedTTPs.from_dict(get('related_ttps')) - return_obj.exploit_targets = ExploitTargets.from_dict(get('exploit_targets')) - return_obj.intended_effects = _IntendedEffects.from_dict(get('intended_effects')) - return_obj.resources = Resource.from_dict(get('resources')) - return_obj.victim_targeting = VictimTargeting.from_dict(get('victim_targeting')) - return_obj.handling = Marking.from_dict(get('handling')) + """ + self.intended_effects.append(value) - return return_obj + def add_kill_chain_phase(self, value): + """Adds a :class:`.KillChainPhaseReference` to the + :attr:`kill_chain_phases` collection. + Args: + value: A :class:`.KillChainPhase`, :class:`.KillChainPhaseReference` + or a ``str`` representing the phase_id of. Note that you if you + are defining a custom Kill Chain, you need to add it to the + STIX package separately. + """ + self.kill_chain_phases.append(value) -# NOT ACTUAL STIX TYPE -class _IntendedEffects(stix.TypedList): - _contained_type = Statement + def add_related_package(self, value): + """Adds a :class:`.RelatedPackageRef` object to the + :attr:`related_packages` collection. - def _fix_value(self, value): - intended_effect = vocabs.IntendedEffect(value) - return Statement(value=intended_effect) + Args: + value: A :class:`.RelatedPackageRef` or a :class:`.STIXPackage` + object. + """ + self.related_packages.append(value) # Avoid circular imports from .related_ttps import RelatedTTPs diff --git a/stix/ttp/attack_pattern.py b/stix/ttp/attack_pattern.py index eef1b585..cf950086 100644 --- a/stix/ttp/attack_pattern.py +++ b/stix/ttp/attack_pattern.py @@ -1,93 +1,33 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# internal import stix from stix.common import StructuredText + +# bindings import stix.bindings.ttp as ttp_binding +from mixbox import fields + class AttackPattern(stix.Entity): _binding = ttp_binding _binding_class = _binding.AttackPatternType _namespace = "http://stix.mitre.org/TTP-1" - def __init__(self, id_=None, title=None, description=None, short_description=None): + id_ = fields.IdField("id") + idref = fields.IdrefField("idref") + title = fields.TypedField("Title") + capec_id = fields.TypedField("capec_id") + description = fields.TypedField("Description", type_="stix.common.StructuredText") + short_description = fields.TypedField("Short_Description", type_="stix.common.StructuredText") + + def __init__(self, id_=None, idref=None, title=None, description=None, short_description=None): + super(AttackPattern, self).__init__() + self.id_ = id_ - self.capec_id = None + self.idref = idref self.title = title self.description = description self.short_description = short_description - - @property - def title(self): - return self._title - - @title.setter - def title(self, value): - self._title = value - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - @property - def short_description(self): - return self._short_description - - @short_description.setter - def short_description(self, value): - self._set_var(StructuredText, short_description=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(AttackPattern, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.id = self.id_ - return_obj.capec_id = self.capec_id - return_obj.Title = self.title - - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.short_description: - return_obj.Short_Description = self.short_description.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.id_ = obj.id - return_obj.capec_id = obj.capec_id - return_obj.title = obj.Title - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.short_description = StructuredText.from_obj(obj.Short_Description) - - return return_obj - - def to_dict(self): - return super(AttackPattern, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - return_obj.id_ = dict_repr.get('id') - return_obj.capec_id = dict_repr.get('capec_id') - return_obj.title = dict_repr.get('title') - return_obj.description = StructuredText.from_dict(dict_repr.get('description')) - return_obj.short_description = StructuredText.from_dict(dict_repr.get('short_description')) - - return return_obj diff --git a/stix/ttp/behavior.py b/stix/ttp/behavior.py index f564c5b9..35861e53 100644 --- a/stix/ttp/behavior.py +++ b/stix/ttp/behavior.py @@ -1,9 +1,14 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. + +# mixbox +from mixbox import fields + +# stix import stix import stix.bindings.ttp as ttp_binding -from .malware_instance import MalwareInstance +from .malware_instance import MalwareInstance, MalwareInstanceFactory from .exploit import Exploit from .attack_pattern import AttackPattern @@ -13,117 +18,47 @@ class Behavior(stix.Entity): _binding_class = _binding.BehaviorType _namespace = "http://stix.mitre.org/TTP-1" - def __init__(self, malware_instances=None, attack_patterns=None, exploits=None): - self.malware_instances = malware_instances - self.attack_patterns = attack_patterns - self.exploits = exploits + malware_instances = fields.TypedField("Malware", type_="stix.ttp.behavior.MalwareInstances", key_name="malware_instances") + attack_patterns = fields.TypedField("Attack_Patterns", type_="stix.ttp.behavior.AttackPatterns") + exploits = fields.TypedField("Exploits", type_="stix.ttp.behavior.Exploits") - @property - def malware_instances(self): - return self._malware_instances + def __init__(self, malware_instances=None, attack_patterns=None, exploits=None): + super(Behavior, self).__init__() + self.malware_instances = malware_instances or MalwareInstances() + self.attack_patterns = attack_patterns or AttackPatterns() + self.exploits = exploits or Exploits() - @malware_instances.setter - def malware_instances(self, value): - self._malware_instances = MalwareInstances(value) def add_malware_instance(self, malware): self.malware_instances.append(malware) - @property - def attack_patterns(self): - return self._attack_patterns - - @attack_patterns.setter - def attack_patterns(self, value): - self._attack_patterns = AttackPatterns(value) - def add_attack_pattern(self, attack_pattern): self.attack_patterns.append(attack_pattern) - @property - def exploits(self): - return self._exploits - - @exploits.setter - def exploits(self, value): - self._exploits = Exploits(value) - def add_exploit(self, exploit): self.exploits.append(exploit) - def to_obj(self, return_obj=None, ns_info=None): - super(Behavior, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.malware_instances: - return_obj.Malware = self.malware_instances.to_obj(ns_info=ns_info) - if self.exploits: - return_obj.Exploits = self.exploits.to_obj(ns_info=ns_info) - if self.attack_patterns: - return_obj.Attack_Patterns = self.attack_patterns.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.malware_instances = MalwareInstances.from_obj(obj.Malware) - return_obj.exploits = Exploits.from_obj(obj.Exploits) - return_obj.attack_patterns = AttackPatterns.from_obj(obj.Attack_Patterns) - - return return_obj - - def to_dict(self): - return super(Behavior, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - get = dict_repr.get - - return_obj.malware_instances = MalwareInstances.from_dict(get('malware_instances')) - return_obj.exploits = Exploits.from_dict(get('exploits')) - return_obj.attack_patterns = AttackPatterns.from_dict(get('attack_patterns')) - - return return_obj - class Exploits(stix.EntityList): _namespace = "http://stix.mitre.org/TTP-1" _contained_type = Exploit _binding = ttp_binding _binding_class = _binding.ExploitsType - _binding_var = "Exploit" - _inner_name = "exploits" - _dict_as_list = True + + exploit = fields.TypedField("Exploit", Exploit, multiple=True, key_name="exploits") class MalwareInstances(stix.EntityList): _namespace = "http://stix.mitre.org/TTP-1" - _contained_type = MalwareInstance _binding = ttp_binding _binding_class = _binding.MalwareType - _binding_var = "Malware_Instance" - _inner_name = "malware_instances" - _dict_as_list = True + + malware_instance = fields.TypedField("Malware_Instance", MalwareInstance, multiple=True, factory=MalwareInstanceFactory, key_name="malware_instances") class AttackPatterns(stix.EntityList): _namespace = "http://stix.mitre.org/TTP-1" - _contained_type = AttackPattern _binding = ttp_binding _binding_class = _binding.AttackPatternsType - _binding_var = "Attack_Pattern" - _inner_name = "attack_patterns" - _dict_as_list = True + + attack_pattern = fields.TypedField("Attack_Pattern", AttackPattern, multiple=True, key_name="attack_patterns") diff --git a/stix/ttp/exploit.py b/stix/ttp/exploit.py index 0d55aaf7..dbd36944 100644 --- a/stix/ttp/exploit.py +++ b/stix/ttp/exploit.py @@ -1,90 +1,31 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# internal import stix -import stix.utils from stix.common import StructuredText + +# bindings import stix.bindings.ttp as ttp_binding +from mixbox import fields + class Exploit(stix.Entity): _binding = ttp_binding _binding_class = _binding.ExploitType _namespace = "http://stix.mitre.org/TTP-1" - def __init__(self, id_=None, title=None, description=None, short_description=None): + id_ = fields.IdField("id") + idref = fields.IdrefField("idref") + title = fields.TypedField("Title") + description = fields.TypedField("Description", type_="stix.common.StructuredText") + short_description = fields.TypedField("Short_Description", type_="stix.common.StructuredText") + + def __init__(self, id_=None, idref=None, title=None, description=None, short_description=None): + super(Exploit, self).__init__() self.id_ = id_ + self.idref = idref self.title = title self.description = description self.short_description = short_description - - @property - def title(self): - return self._title - - @title.setter - def title(self, value): - self._title = value - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - @property - def short_description(self): - return self._short_description - - @short_description.setter - def short_description(self, value): - self._set_var(StructuredText, short_description=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(Exploit, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.id = self.id_ - return_obj.Title = self.title - - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.short_description: - return_obj.Short_Description = self.short_description.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.id_ = obj.id - return_obj.title = obj.Title - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.short_description = StructuredText.from_obj(obj.Short_Description) - - return return_obj - - def to_dict(self): - return super(Exploit, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - return_obj.id_ = dict_repr.get('id') - return_obj.title = dict_repr.get('title') - return_obj.description = StructuredText.from_dict(dict_repr.get('description')) - return_obj.short_description = StructuredText.from_dict(dict_repr.get('short_description')) - - return return_obj diff --git a/stix/ttp/exploit_targets.py b/stix/ttp/exploit_targets.py index 3caf4206..24b165f6 100644 --- a/stix/ttp/exploit_targets.py +++ b/stix/ttp/exploit_targets.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix.bindings.ttp as ttp_binding from stix.common.related import GenericRelationshipList, RelatedExploitTarget @@ -9,6 +11,5 @@ class ExploitTargets(GenericRelationshipList): _namespace = "http://stix.mitre.org/TTP-1" _binding = ttp_binding _binding_class = _binding.ExploitTargetsType - _binding_var = "Exploit_Target" - _contained_type = RelatedExploitTarget - _inner_name = "exploit_targets" + + exploit_target = fields.TypedField("Exploit_Target", RelatedExploitTarget, multiple=True, key_name="exploit_targets") diff --git a/stix/ttp/infrastructure.py b/stix/ttp/infrastructure.py index d067ce01..d43428d0 100644 --- a/stix/ttp/infrastructure.py +++ b/stix/ttp/infrastructure.py @@ -1,7 +1,10 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -# external +# mixbox +from mixbox import fields + +# cybox from cybox.core import Observables # internal @@ -16,119 +19,36 @@ class Infrastructure(stix.Entity): _binding_class = _binding.InfrastructureType _namespace = "http://stix.mitre.org/TTP-1" - def __init__(self, id_=None, title=None, description=None, short_description=None): + id_ = fields.IdField("id") + idref = fields.IdrefField("idref") + title = fields.TypedField("Title") + description = fields.TypedField("Description", StructuredText) + short_description = fields.TypedField("Short_Description", StructuredText) + types = fields.TypedField("Type", VocabString, multiple=True, key_name="types") + observable_characterization = fields.TypedField("Observable_Characterization", Observables) + + def __init__(self, id_=None, idref=None, title=None, description=None, + short_description=None): + + super(Infrastructure, self).__init__() + self.id_ = id_ + self.idref = idref self.title = title self.description = description self.short_description = short_description - self.types = None - self.observable_characterization = None - - @property - def title(self): - return self._title - - @title.setter - def title(self, value): - self._title = value - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - @property - def short_description(self): - return self._short_description - - @short_description.setter - def short_description(self, value): - self._set_var(StructuredText, short_description=value) - - @property - def types(self): - return self._types - - @types.setter - def types(self, value): - self._types = InfraStructureTypes(value) def add_type(self, type_): self.types.append(type_) - @property - def observable_characterization(self): - return self._observable_characterization - - @observable_characterization.setter - def observable_characterization(self, value): - self._set_var(Observables, observable_characterization=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(Infrastructure, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - return_obj.id = self.id_ - return_obj.Title = self.title - - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.short_description: - return_obj.Short_Description = self.short_description.to_obj(ns_info=ns_info) - if self.types: - return_obj.Type = [x.to_obj(ns_info=ns_info) for x in self.types] - if self.observable_characterization: - return_obj.Observable_Characterization = self.observable_characterization.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.id_ = obj.id - return_obj.title = obj.Title - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.short_description = StructuredText.from_obj(obj.Short_Description) - return_obj.observable_characterization = Observables.from_obj(obj.Observable_Characterization) - - if obj.Type: - return_obj.types = [VocabString.from_obj(x) for x in obj.Type] - - return return_obj - - def to_dict(self): - return super(Infrastructure, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - if not return_obj: - return_obj = cls() - - return_obj.id_ = dict_repr.get('id') - return_obj.title = dict_repr.get('title') - return_obj.description = StructuredText.from_dict(dict_repr.get('description')) - return_obj.short_description = StructuredText.from_dict(dict_repr.get('short_description')) - return_obj.types = [VocabString.from_dict(x) for x in dict_repr.get('types', [])] - return_obj.observable_characterization = Observables.from_dict(dict_repr.get('observable_characterization')) - - return return_obj - class InfraStructureTypes(stix.EntityList): _namespace = "http://stix.mitre.org/TTP-1" _contained_type = VocabString - _dict_as_list = True + + @classmethod + def _dict_as_list(cls): + return True def _fix_value(self, value): return AttackerInfrastructureType(value) diff --git a/stix/ttp/malware_instance.py b/stix/ttp/malware_instance.py index fc921471..14c301ea 100644 --- a/stix/ttp/malware_instance.py +++ b/stix/ttp/malware_instance.py @@ -1,175 +1,67 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# internal import stix -import stix.utils as utils -from stix.common import vocabs, StructuredText, VocabString +from stix.common import vocabs +from stix.common import StructuredText, VocabString + +# bindings import stix.bindings.ttp as ttp_binding +from mixbox import fields, entities + class MalwareInstance(stix.Entity): _binding = ttp_binding _binding_class = _binding.MalwareInstanceType _namespace = "http://stix.mitre.org/TTP-1" - - def __init__(self, id_=None, title=None, description=None, short_description=None): + _XSI_TYPE = None # defined by subclasses + + id_ = fields.IdField("id") + idref = fields.IdrefField("idref") + title = fields.TypedField("Title") + description = fields.TypedField("Description", type_="stix.common.StructuredText") + short_description = fields.TypedField("Short_Description", type_="stix.common.StructuredText") + names = vocabs.VocabField("Name", type_=VocabString, multiple=True, key_name="names") + types = vocabs.VocabField("Type", type_=vocabs.MalwareType, multiple=True, key_name="types") + + def __init__(self, id_=None, idref=None, title=None, description=None, short_description=None): + super(MalwareInstance, self).__init__() self.id_ = id_ + self.idref = idref self.title = title self.description = description self.short_description = short_description - self.names = None - self.types = None - - @property - def title(self): - return self._title - - @title.setter - def title(self, value): - self._title = value - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._set_var(StructuredText, description=value) - - @property - def short_description(self): - return self._short_description - - @short_description.setter - def short_description(self, value): - self._set_var(StructuredText, short_description=value) - - @property - def names(self): - return self._names - - @names.setter - def names(self, value): - self._names = MalwareNames(value) def add_name(self, name): - self._names.append(name) - - @property - def types(self): - return self._types - - @types.setter - def types(self, value): - self._types = MalwareTypes(value) + self.names.append(name) def add_type(self, type_): - self._types.append(type_) + self.types.append(type_) @staticmethod def lookup_class(xsi_type): if not xsi_type: raise ValueError("xsi:type is required") - for (k, v) in _EXTENSION_MAP.iteritems(): - # TODO: for now we ignore the prefix and just check for - # a partial match - if xsi_type in k: - return v - - raise ValueError("Unregistered xsi:type %s" % xsi_type) - - def to_obj(self, return_obj=None, ns_info=None): - super(MalwareInstance, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - return_obj.id = self.id_ - return_obj.Title = self.title - - if self.description: - return_obj.Description = self.description.to_obj(ns_info=ns_info) - if self.short_description: - return_obj.Short_Description = self.short_description.to_obj(ns_info=ns_info) - if self.names: - return_obj.Name = self.names.to_obj(ns_info=ns_info) - if self.types: - return_obj.Type = self.types.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - from stix.extensions.malware import maec_4_1_malware # noqa - - if not obj: - return None - - if not return_obj: - try: - klass = MalwareInstance.lookup_class(obj.xml_type) - return_obj = klass.from_obj(obj) - except AttributeError: - return_obj = MalwareInstance.from_obj(obj, cls()) - else: - return_obj.id_ = obj.id - return_obj.title = obj.Title - return_obj.description = StructuredText.from_obj(obj.Description) - return_obj.short_description = StructuredText.from_obj(obj.Short_Description) - return_obj.names = MalwareNames.from_obj(obj.Name) - return_obj.types = MalwareTypes.from_obj(obj.Type) - - return return_obj + return stix.lookup_extension(xsi_type) def to_dict(self): - d = utils.to_dict(self) + d = super(MalwareInstance, self).to_dict() - if getattr(self, '_XSI_TYPE', None): - d['xsi:type'] = self._XSI_TYPE + if self._XSI_TYPE: + d["xsi:type"] = self._XSI_TYPE return d - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - from stix.extensions.malware import maec_4_1_malware # noqa - - if not dict_repr: - return None - - get = dict_repr.get - - if not return_obj: - xsi_type = get('xsi:type') - if xsi_type: - klass = MalwareInstance.lookup_class(get('xsi:type')) - return_obj = klass.from_dict(dict_repr) - else: - return_obj = MalwareInstance.from_dict(dict_repr, cls()) - else: - return_obj.id_ = get('id') - return_obj.title = get('title') - return_obj.description = StructuredText.from_dict(get('description')) - return_obj.short_description = StructuredText.from_dict(get('short_description')) - return_obj.names = MalwareNames.from_dict(get('names')) - return_obj.types = MalwareTypes.from_dict(get('types')) - - return return_obj - -class MalwareNames(stix.TypedList): - _contained_type = VocabString - - -class MalwareTypes(stix.TypedList): - _contained_type = VocabString - - def _fix_value(self, value): - return vocabs.MalwareType(value) - - -#: Mapping of malware instance extension types to classes -_EXTENSION_MAP = {} +class MalwareInstanceFactory(entities.EntityFactory): + @classmethod + def entity_class(cls, key): + from stix.extensions.malware.maec_4_1_malware import MAECInstance # noqa + return stix.lookup_extension(key, default=MalwareInstance) -def add_extension(cls): - _EXTENSION_MAP[cls._XSI_TYPE] = cls # noqa +# Backwards compatibility +add_extension = stix.add_extension diff --git a/stix/ttp/related_ttps.py b/stix/ttp/related_ttps.py index 2dda8cfe..29164739 100644 --- a/stix/ttp/related_ttps.py +++ b/stix/ttp/related_ttps.py @@ -1,6 +1,8 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +from mixbox import fields + import stix.bindings.ttp as ttp_binding from stix.common.related import GenericRelationshipList, RelatedTTP @@ -9,6 +11,6 @@ class RelatedTTPs(GenericRelationshipList): _namespace = "http://stix.mitre.org/TTP-1" _binding = ttp_binding _binding_class = _binding.RelatedTTPsType - _binding_var = "Related_TTP" - _contained_type = RelatedTTP - _inner_name = "ttps" + + related_ttp = fields.TypedField("Related_TTP", RelatedTTP, multiple=True, key_name="ttps") + diff --git a/stix/ttp/resource.py b/stix/ttp/resource.py index ce63576e..3722355a 100644 --- a/stix/ttp/resource.py +++ b/stix/ttp/resource.py @@ -1,122 +1,59 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. +# mixbox +from mixbox import fields +from mixbox import typedlist + # internal import stix -from stix.common import ToolInformation, Identity +from stix.common import ToolInformation +from stix.common.identity import Identity, IdentityFactory import stix.bindings.ttp as ttp_binding # relative from .infrastructure import Infrastructure -class Resource(stix.Entity): - _binding = ttp_binding - _binding_class = _binding.ResourceType - _namespace = "http://stix.mitre.org/TTP-1" - - def __init__(self, tools=None, infrastructure=None, personas=None): - self.tools = tools - self.infrastructure = infrastructure - self.personas = personas - - @property - def tools(self): - return self._tools - - @tools.setter - def tools(self, value): - self._tools = Tools(value) - - def add_tool(self, tool): - self.tools.append(tool) - - @property - def infrastructure(self): - return self._infrastructure - - @infrastructure.setter - def infrastructure(self, value): - self._infrastructure = value - - @property - def personas(self): - return self._personas - - @personas.setter - def personas(self, value): - self._personas = Personas(value) - - def add_persona(self, persona): - self.personas.append(persona) - - def to_obj(self, return_obj=None, ns_info=None): - super(Resource, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.tools: - return_obj.Tools = self.tools.to_obj(ns_info=ns_info) - if self.infrastructure: - return_obj.Infrastructure = self.infrastructure.to_obj(ns_info=ns_info) - if self.personas: - return_obj.Personas = self.personas.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - - if not return_obj: - return_obj = cls() - - return_obj.infrastructure = Infrastructure.from_obj(obj.Infrastructure) - return_obj.tools = Tools.from_obj(obj.Tools) - return_obj.personas = Personas.from_obj(obj.Personas) +class _IdentityList(typedlist.TypedList): + def __init__(self, *args): + super(_IdentityList, self).__init__(type=Identity, *args) - return return_obj - - def to_dict(self): - return super(Resource, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - get = dict_repr.get - - return_obj.tools = Tools.from_dict(get('tools')) - return_obj.infrastructure = Infrastructure.from_dict(get('infrastructure')) - return_obj.personas = Personas.from_dict(get('personas')) - - return return_obj + def _fix_value(self, value): + return Identity(name=value) class Personas(stix.EntityList): _namespace = "http://stix.mitre.org/TTP-1" - _contained_type = Identity _binding = ttp_binding _binding_class = _binding.PersonasType - _binding_var = "Persona" - _inner_name = "personas" - _dict_as_list = True - def _fix_value(self, value): - return Identity(name=value) + persona = fields.TypedField("Persona", Identity, multiple=True, factory=IdentityFactory, listfunc=_IdentityList) class Tools(stix.EntityList): _namespace = "http://stix.mitre.org/TTP-1" - _contained_type = ToolInformation _binding = ttp_binding _binding_class = _binding.ToolsType - _binding_var = "Tool" - _inner_name = "tools" - _dict_as_list = True + + tool = fields.TypedField("Tool", ToolInformation, multiple=True) + + @classmethod + def _dict_as_list(cls): + return True + + +class Resource(stix.Entity): + _binding = ttp_binding + _binding_class = _binding.ResourceType + _namespace = "http://stix.mitre.org/TTP-1" + + tools = fields.TypedField("Tools", Tools) + infrastructure = fields.TypedField("Infrastructure", Infrastructure) + personas = fields.TypedField("Personas", Personas) + + def __init__(self, tools=None, infrastructure=None, personas=None): + super(Resource, self).__init__() + self.tools = tools + self.infrastructure = infrastructure + self.personas = personas diff --git a/stix/ttp/victim_targeting.py b/stix/ttp/victim_targeting.py index 738d7e6c..5e3a03d0 100644 --- a/stix/ttp/victim_targeting.py +++ b/stix/ttp/victim_targeting.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. # external @@ -6,8 +6,10 @@ # internal import stix -from stix.common import vocabs, VocabString, Identity import stix.bindings.ttp as ttp_binding +from stix.common import vocabs +from stix.common.identity import Identity, IdentityFactory +from mixbox import fields class VictimTargeting(stix.Entity): @@ -15,102 +17,16 @@ class VictimTargeting(stix.Entity): _binding_class = _binding.VictimTargetingType _namespace = "http://stix.mitre.org/TTP-1" - def __init__(self): - self.identity = None - self.targeted_systems = None - self.targeted_information = None - self.targeted_technical_details = None - - @property - def targeted_systems(self): - return self._targeted_systems + identity = fields.TypedField("Identity", Identity, factory=IdentityFactory) + targeted_systems = vocabs.VocabField("Targeted_Systems", vocabs.SystemType, multiple=True) + targeted_information = vocabs.VocabField("Targeted_Information", vocabs.InformationType, multiple=True) + targeted_technical_details = fields.TypedField("Targeted_Technical_Details", Observables) - @targeted_systems.setter - def targeted_systems(self, value): - self._targeted_systems = TargetedSystems(value) + def __init__(self): + super(VictimTargeting, self).__init__() def add_targeted_system(self, system): - self._targeted_systems.append(system) - - @property - def targeted_information(self): - return self._targeted_information - - @targeted_information.setter - def targeted_information(self, value): - self._targeted_information = TargetedInformation(value) + self.targeted_systems.append(system) def add_targeted_information(self, targeted_information): - self._targeted_information.append(targeted_information) - - @property - def targeted_technical_details(self): - return self._targeted_technical_details - - @targeted_technical_details.setter - def targeted_technical_details(self, value): - self._set_var(Observables, targeted_technical_details=value) - - def to_obj(self, return_obj=None, ns_info=None): - super(VictimTargeting, self).to_obj(return_obj=return_obj, ns_info=ns_info) - - if not return_obj: - return_obj = self._binding_class() - - if self.identity: - return_obj.Identity = self.identity.to_obj(ns_info=ns_info) - if self.targeted_information: - return_obj.Targeted_Information = self.targeted_information.to_obj(ns_info=ns_info) - if self.targeted_systems: - return_obj.Targeted_Systems = self.targeted_systems.to_obj(ns_info=ns_info) - if self.targeted_technical_details: - return_obj.Targeted_Technical_Details = self.targeted_technical_details.to_obj(ns_info=ns_info) - - return return_obj - - @classmethod - def from_obj(cls, obj, return_obj=None): - if not obj: - return None - if not return_obj: - return_obj = cls() - - return_obj.identity = Identity.from_obj(obj.Identity) - return_obj.targeted_technical_details = Observables.from_obj(obj.Targeted_Technical_Details) - return_obj.targeted_systems = TargetedSystems.from_obj(obj.Targeted_Systems) - return_obj.targeted_information = TargetedInformation.from_obj(obj.Targeted_Information) - - return return_obj - - def to_dict(self): - return super(VictimTargeting, self).to_dict() - - @classmethod - def from_dict(cls, dict_repr, return_obj=None): - if not dict_repr: - return None - - if not return_obj: - return_obj = cls() - - get = dict_repr.get - return_obj.identity = Identity.from_dict(get('identity')) - return_obj.targeted_systems = TargetedSystems.from_dict(get('targeted_systems')) - return_obj.targeted_information = TargetedInformation.from_dict(get('targeted_information')) - return_obj.targeted_technical_details = Observables.from_dict(get('targeted_technical_details')) - - return return_obj - - -class TargetedSystems(stix.TypedList): - _contained_type = VocabString - - def _fix_value(self, value): - return vocabs.SystemType(value) - - -class TargetedInformation(stix.TypedList): - _contained_type = VocabString - - def _fix_value(self, value): - return vocabs.InformationType(value) + self.targeted_information.append(targeted_information) diff --git a/stix/utils/__init__.py b/stix/utils/__init__.py index 1422f2ee..bc20610b 100644 --- a/stix/utils/__init__.py +++ b/stix/utils/__init__.py @@ -1,19 +1,18 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -# stdlib -import collections import contextlib +import functools import keyword import warnings -# external -import cybox import lxml.etree -# internal +from mixbox.entities import Entity, EntityList +import mixbox.xml +from mixbox.vendor.six import iteritems, string_types + import stix -import stix.xmlconst as xmlconst # relative from . import dates @@ -36,7 +35,52 @@ def ignored(*exceptions): pass +def raise_warnings(func): + """Function decorator that causes all Python warnings to be raised as + exceptions in the wrapped function. + + Example: + >>> @raise_warnings + >>> def foo(): + >>> warnings.warn("this will raise an exception") + + """ + @functools.wraps(func) + def inner(*args, **kwargs): + with warnings.catch_warnings(): + warnings.simplefilter('error') + return func(*args, **kwargs) + return inner + + +def silence_warnings(func): + """Function decorator that silences/ignores all Python warnings in the + wrapped function. + + Example: + >>> @silence_warnings + >>> def foo(): + >>> warnings.warn("this will not appear") + + """ + @functools.wraps(func) + def inner(*args, **kwargs): + with warnings.catch_warnings(record=True): + warnings.simplefilter('always') + return func(*args, **kwargs) + return inner + + def is_cdata(text): + """Returns ``True`` if `text` contains a CDATA block. + + Example: + >>> is_cdata("") + True + >>> is_cdata("NOPE") + False + + """ if not text: return False @@ -66,7 +110,7 @@ def strip_cdata(text): def cdata(text): - """Wraps the input `text` in a block. + """Wraps the input `text` in a ```` block. If the text contains CDATA sections already, they are stripped and replaced by the application of an outer-most CDATA block. @@ -75,7 +119,7 @@ def cdata(text): text: A string to wrap in a CDATA block. Returns: - The `text` value wrapped in + The `text` value wrapped in ```` """ if not text: @@ -94,24 +138,26 @@ def is_stix(entity): def is_cybox(entity): - """Returns true if `entity` is an instance of :class:`cybox.Entity`.""" - return isinstance(entity, cybox.Entity) + """Returns true if `entity` is a Cybox object""" + try: + return entity.__module__.startswith("cybox.") + except AttributeError: + return False def is_entity(entity): """Returns true if `entity` is an instance of :class:`.Entity` or - :class:`cybox.Entity`. - + :class:`mixbox.Entity`. """ - return isinstance(entity, (cybox.Entity, stix.Entity)) + return isinstance(entity, (Entity, stix.Entity)) def is_entitylist(entity): """Returns true if `entity` is an instance of :class:`.EntityList` - or :class:`cybox.EntityList`. + or :class:`mixbox.entities.EntityList`. """ - return isinstance(entity, (cybox.EntityList, stix.EntityList)) + return isinstance(entity, (EntityList, stix.EntityList)) def is_typedlist(entity): @@ -176,7 +222,7 @@ def is_sequence(item): ``tuple``). String types will return ``False``. """ - return hasattr(item, "__iter__") + return hasattr(item, "__iter__") and not isinstance(item, string_types) def check_version(expected, found): @@ -207,7 +253,7 @@ def iter_vars(obj): def check(name): return name not in ('__input_namespaces__', '__input_schemalocations__') - instance_vars = obj.__dict__.iteritems() + instance_vars = iteritems(vars(obj)) return ((attr_name(name), val) for name, val in instance_vars if check(name)) @@ -231,16 +277,6 @@ def is_bool(obj): return isinstance(obj, bool) -def is_element(obj): - """Returns ``True`` if `obj` is an lxml ``Element``.""" - return isinstance(obj, lxml.etree._Element) # noqa - - -def is_etree(obj): - """Returns ``True`` if `obj` is an lxml ``ElementTree``.""" - return isinstance(obj, lxml.etree._ElementTree) # noqa - - def has_value(var): """Returns ``True`` if `var` is not ``None`` and not empty.""" if var is None: @@ -249,6 +285,7 @@ def has_value(var): return bool(var) or (var in (False, 0)) +@silence_warnings def to_dict(entity, skip=()): """Returns a dictionary representation of `entity`. This will iterate over the instance vars of `entity` and construct keys and values from those @@ -267,33 +304,26 @@ def to_dict(entity, skip=()): def dict_iter(items): return [x.to_dict() if is_dictable(x) else x for x in items] - def dictify(entity): - d = {} - for name, field in iter_vars(entity): - key = key_name(name) - - if key in skip or not has_value(field): - continue - - if is_dictable(field): - d[key] = field.to_dict() - elif is_timestamp(field): - d[key] = dates.serialize_value(field) - elif is_date(field): - d[key] = dates.serialize_date(field) - elif is_element(field) or is_etree(field): - d[key] = lxml.etree.tostring(field) - elif is_sequence(field): - d[key] = dict_iter(field) - else: - d[key] = field - - return d d = {} - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - d.update(dictify(entity)) + for name, field in iter_vars(entity): + key = key_name(name) + + if key in skip or not has_value(field): + continue + + if is_dictable(field): + d[key] = field.to_dict() + elif is_timestamp(field): + d[key] = dates.serialize_value(field) + elif is_date(field): + d[key] = dates.serialize_date(field) + elif mixbox.xml.is_element(field) or mixbox.xml.is_etree(field): + d[key] = lxml.etree.tostring(field) + elif is_sequence(field): + d[key] = dict_iter(field) + else: + d[key] = field return d @@ -307,10 +337,10 @@ def xml_bool(value): if value is None: return None - if value in xmlconst.FALSE: + if value in mixbox.xml.FALSE: return False - if value in xmlconst.TRUE: + if value in mixbox.xml.TRUE: return True error = "Unable to determine the xml boolean value of '{0}'".format(value) @@ -334,9 +364,20 @@ def cast_var(item, klass, arg=None): return klass(**kwarg) # klass(value='foobar') +def remove_entries(d, keys): + """Removes all the `keys` from the dictionary `d`. + + Args: + d: A dictionary. + keys: An iterable collection of dictionary keys to remove. + + """ + for key in keys: + d.pop(key, None) + + # Namespace flattening from .nsparser import * # noqa from .dates import * # noqa -from .idgen import * # noqa -from .nsparser import * # noqa +from .parser import * # noqa from .walk import * # noqa diff --git a/stix/utils/dates.py b/stix/utils/dates.py index 55fc4cf9..4195cf10 100644 --- a/stix/utils/dates.py +++ b/stix/utils/dates.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. # stdlib @@ -83,5 +83,5 @@ def serialize_date(value): def now(): - """Returns the current UTC datetime.datetime.""" + """Returns the current UTC ``datetime.datetime`` timestamp.""" return datetime.datetime.now(tz=dateutil.tz.tzutc()) diff --git a/stix/utils/nsparser.py b/stix/utils/nsparser.py index eb4050ec..482bdb50 100644 --- a/stix/utils/nsparser.py +++ b/stix/utils/nsparser.py @@ -1,542 +1,52 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. - -# stdlib -import collections -import warnings -import itertools - -# external -import cybox -import cybox.core -import cybox.common -import cybox.utils - -# internal -import stix - -# relative -from . import ignored, idgen -from .walk import iterwalk - - -class DuplicatePrefixError(Exception): - def __init__(self, message, prefix, namespaces): - super(DuplicatePrefixError, self).__init__(message) - self.prefix = prefix - self.namespaces = namespaces - - -class NamespaceInfo(object): - # These appear in every exported document - - _BASELINE_NAMESPACES = { - 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', - 'stix': 'http://stix.mitre.org/stix-1', - 'stixCommon': 'http://stix.mitre.org/common-1', - 'stixVocabs': 'http://stix.mitre.org/default_vocabularies-1', - 'cybox': 'http://cybox.mitre.org/cybox-2', - 'cyboxCommon': 'http://cybox.mitre.org/common-2', - 'cyboxVocabs': 'http://cybox.mitre.org/default_vocabularies-2' - } - - def __init__(self): - # Namespaces that are "collected" from the Python objects during - # serialization. Key is the namespace alias/prefix. Value is the - # namespace. There are many classes without a defined prefix, so - # the ``None`` prefix is predefined as a ``set()``. - self._collected_namespaces = {None: set()} - - # Namespaces and schemalocations that are attached to STIX/CybOX - # entities when parsed from an external source. - self._input_namespaces = {} - self._input_schemalocs = {} - - # A list of classes that have been visited/seen during the namespace - # collection process. This speeds up the collect() method. - self._collected_classes = set() - - # Namespaces and schemalocations that will appear in the output - # XML document. - self.finalized_namespaces = None - self.finalized_schemalocs = None - - # Namespace dictionary that gets passed to the bindings. - self.binding_namespaces = None - - def update(self, ns_info): - self._collected_namespaces.update(ns_info._collected_namespaces) # noqa - self._input_namespaces.update(ns_info._input_namespaces) # noqa - self._input_schemalocs.update(ns_info._input_schemalocs) # noqa - - def _parse_collected_classes(self): - collected = self._collected_classes - entity_klasses = (stix.Entity, cybox.Entity) - - # Generator which yields all stix.Entity and cybox.Entity subclasses - # that were collected. - entity_subclasses = ( - klass for klass in collected if issubclass(klass, entity_klasses) - ) - - # Local function for adding namespaces that have no defined prefix - # mapping at the class-level. These will be resolved in the - # self._finalize_namespaces() function. - no_alias = self._collected_namespaces[None].add - - for klass in entity_subclasses: - # Prevents exception being raised if/when - # collections.MutableSequence or another base class appears in the - # MRO. - ns = getattr(klass, "_namespace", None) - if not ns: - continue - - # cybox.objects.* ObjectProperties derivations have an _XSI_NS - # class-level attribute which holds the namespace alias to be - # used for its namespace. - alias = getattr(klass, "_XSI_NS", None) - if alias: - self._collected_namespaces[alias] = ns - continue - - # Many stix/cybox entity classes have an _XSI_TYPE attribute that - # contains a `prefix:namespace` formatted QNAME for the - # associated xsi:type. - xsi_type = getattr(klass, "_XSI_TYPE", None) - if not xsi_type: - no_alias(ns) - continue - - # Attempt to split the xsi:type attribute value into the ns alias - # and the typename. - typeinfo = xsi_type.split(":") - if len(typeinfo) == 2: - self._collected_namespaces[typeinfo[0]] = ns - else: - no_alias(ns) - - def _fix_example_namespace(self): - """Attempts to resolve issues where our samples use - 'http://example.com/' for our example namespace but python-stix uses - 'http://example.com' by removing the former. - - """ - ex_api_ns = idgen.EXAMPLE_NAMESPACE.keys()[0] - ex_prefix = 'example' # Example ns prefix - id_alias = idgen.get_id_namespace_alias() - - # If the ID namespace alias doesn't match the example alias, return. - if id_alias != ex_prefix: - return - - # If the example namespace prefix isn't in the parsed namespace - # prefixes, return. - if ex_prefix not in self._input_namespaces: - return - - self._input_namespaces[ex_prefix] = ex_api_ns - - def _check_namespaces(self, ns_dict): - """Check that all the prefixes in `ns_dict` are mapped to only - one namespace. - - Args: - ns_dict: A ``prefix: [namespaces]`` dictionary. - - Raises: - ` .DuplicatePrefixError: If a prefix is mapped to more than one - namespace. - - """ - for prefix, namespaces in ns_dict.iteritems(): - if len(namespaces) == 1: - continue - - error = "Namespace prefix '{0}' mapped to multiple namespaces: {1}" - error = error.format(prefix, namespaces) - - raise DuplicatePrefixError( - message=error, - prefix=prefix, - namespaces=tuple(namespaces) - ) - - def _resolve_unprefixed(self, no_prefix): - """Resolve namespace aliases for the unprefixed namespaces found on - collected python-stix objects. - - Args: - A collection of namespaces that were not mapped to a namespace - prefix by a python Object. - - """ - collected_unprefixed = {} - - for ns in no_prefix: - alias = DEFAULT_STIX_NAMESPACES[ns] - collected_unprefixed[alias] = ns - - return collected_unprefixed - - def _finalize_namespaces(self, ns_dict=None): - """Returns a dictionary of namespaces to be exported with an XML - document. - - This loops over all the namespaces that were discovered and built - during the execution of ``collect()`` and - ``_parse_collected_classes()`` and attempts to merge them all. - - Returns: - An ``alias: namespace`` dictionary containing all namespaces - required to be present on an exported document. - - Raises: - .DuplicatePrefixError: If namespace prefix was mapped to more than - one namespace. - - """ - if not ns_dict: - ns_dict = {} - - # Copy and flip the input dictionary from ns=>alias to alias=>ns - user_namespaces = {} - for ns, alias in ns_dict.iteritems(): - user_namespaces[alias] = ns - - # Our return value - ns_dict = collections.defaultdict(set) - - # Add the ID namespaces - id_alias = idgen.get_id_namespace_alias() - id_ns = idgen.get_id_namespace() - ns_dict[id_alias].add(id_ns) - - # Build namespace dictionaries from the collected Entity objects. - collected_prefixed = dict(self._collected_namespaces.iteritems()) - - # Pop the unprefixed entries. - no_prefix = collected_prefixed.pop(None, ()) - - # Resolve namespace aliases for the unprefixed namespaces. - collected_unprefixed = self._resolve_unprefixed(no_prefix) - - # Remap the example namespace to the one expected by the APIs if the - # sample example namespace is found. - self._fix_example_namespace() - - # All the namespaces dictionaries we need to merge and export. - namespace_dicts = itertools.chain( - self._BASELINE_NAMESPACES.iteritems(), - self._input_namespaces.iteritems(), - collected_prefixed.iteritems(), - collected_unprefixed.iteritems(), - user_namespaces.iteritems() - ) - - # Build our merged namespace dictionary. It will be inspected for - # duplicate ns prefix mappings. - for alias, ns in namespace_dicts: - ns_dict[alias].add(ns) - - # Check that all the prefixes are mapped to only one namespace - self._check_namespaces(ns_dict) - - # Flatten the dictionary by popping the namespace from the namespace - # set values in ns_dict. - flattened = {} - for alias, ns_set in ns_dict.iteritems(): - flattened[alias] = ns_set.pop() - - # Return the flattened dictionary - return flattened - - def _finalize_schemalocs(self, schemaloc_dict=None): - # If schemaloc_dict was passed in, make a copy so we don't mistakenly - # modify the original. - if schemaloc_dict: - schemaloc_dict = dict(schemaloc_dict.iteritems()) - else: - schemaloc_dict = {} - - # Get our id namespace - id_ns = idgen.get_id_namespace() - - # Build our schemalocation dictionary! - # - # Initialize it from values found in the parsed, input schemalocations - # (if there are any) and the schemaloc_dict parameter values (if there - # are any). - # - # If there is a schemalocation found in both the parsed schemalocs and - # the schema_loc dict, use the schemaloc_dict value. - for ns, loc in self._input_schemalocs.iteritems(): - if ns in schemaloc_dict: - continue - schemaloc_dict[ns] = loc - - # Iterate over the finalized namespaces for a document and attempt - # to map them to schemalocations. Warn if the namespace should have a - # schemalocation and we can't find it anywhere. - nsset = set(self.finalized_namespaces.itervalues()) - for ns in nsset: - if ns in DEFAULT_STIX_SCHEMALOCATIONS: - schemaloc_dict[ns] = DEFAULT_STIX_SCHEMALOCATIONS[ns] - elif ns in schemaloc_dict: - continue - elif (ns == id_ns) or (ns in XML_NAMESPACES): - continue - else: - error = "Unable to map namespace '{0}' to schemaLocation" - warnings.warn(error.format(ns)) - - return schemaloc_dict - - def _finalize_binding_namespaces(self): - """Returns a namespace-to-prefix dictionary view of the - finalized_namespaces (which are mapped prefix-to-namespace). - - The bindings expect an NS-to-prefix mapping, while our ns processing - code builds dictionaries that map prefix-to-Namespace(s). Because of - this, we need to flip our dictionaries before handing them off to the - bindings for serialization. - - """ - if not self.finalized_namespaces: - return {} # TODO: Should this return the DEFAULT_STIX_NAMESPACES? - - binding_namespaces = {} - for alias, ns in self.finalized_namespaces.iteritems(): - binding_namespaces[ns] = alias - - # Always use the default STIX prefixes for STIX namespaces. - # This is because of xsi:type prefixes used by the STIX/CybOX user-level - # API classes. - binding_namespaces.update(DEFAULT_STIX_NAMESPACES) - - return binding_namespaces - - def finalize(self, ns_dict=None, schemaloc_dict=None): - self._parse_collected_classes() - self.finalized_namespaces = self._finalize_namespaces(ns_dict) - self.finalized_schemalocs = self._finalize_schemalocs(schemaloc_dict) - self.binding_namespaces = self._finalize_binding_namespaces() - - def collect(self, entity): - # Collect all the classes we need to inspect for namespace information - self._collected_classes.update(entity.__class__.__mro__) - - # Collect the input namespaces if this entity came from some external - # source. - if hasattr(entity, "__input_namespaces__"): - self._input_namespaces.update(entity.__input_namespaces__) - - # Collect the input schemalocation information if this entity came - # from some external source. - if hasattr(entity, "__input_schemalocations__"): - self._input_schemalocs.update(entity.__input_schemalocations__) - - -class NamespaceParser(object): - def __init__(self): - pass - - def get_namespaces(self, entity, ns_dict=None): - ns_info = NamespaceInfo() - - for node in iterwalk(entity): - ns_info.collect(node) - - ns_info.finalize(ns_dict=ns_dict) - return ns_info.finalized_namespaces - - def get_namespace_schemalocation_dict(self, entity, ns_dict=None, schemaloc_dict=None): - ns_info = NamespaceInfo() - - for node in iterwalk(entity): - ns_info.collect(node) - - ns_info.finalize(ns_dict=ns_dict, schemaloc_dict=schemaloc_dict) - return ns_info.finalized_schemalocs - - def get_xmlns_str(self, ns_dict): - pairs = sorted(ns_dict.iteritems()) - return "\n\t".join( - 'xmlns:%s="%s"' % (alias, ns) for alias, ns in pairs - ) - - def get_schemaloc_str(self, schemaloc_dict): - if not schemaloc_dict: - return "" - - schemaloc_str_start = 'xsi:schemaLocation="\n\t' - schemaloc_str_end = '"' - - pairs = sorted(schemaloc_dict.iteritems()) - schemaloc_str_content = "\n\t".join( - "%s %s" % (ns, loc) for ns, loc in pairs - ) - - return schemaloc_str_start + schemaloc_str_content + schemaloc_str_end - - def get_namespace_def_str(self, namespaces, schemaloc_dict): - if not any((namespaces, schemaloc_dict)): - return "" - - parts = ( - self.get_xmlns_str(namespaces), - self.get_schemaloc_str(schemaloc_dict) - ) - - return "\n\t".join(parts) - - -#: Schema locations for standard XML namespaces -XML_NAMESPACES = { - 'http://www.w3.org/2001/XMLSchema-instance': 'xsi', - 'http://www.w3.org/2001/XMLSchema': 'xs', - 'http://www.w3.org/1999/xlink': 'xlink', - 'http://www.w3.org/2000/09/xmldsig#': 'ds' -} - -#: Schema locations for namespaces defined by the STIX language -STIX_NS_TO_SCHEMALOCATION = { - 'http://data-marking.mitre.org/Marking-1': 'http://stix.mitre.org/XMLSchema/data_marking/1.1.1/data_marking.xsd', - 'http://data-marking.mitre.org/extensions/MarkingStructure#Simple-1': 'http://stix.mitre.org/XMLSchema/extensions/marking/simple/1.1.1/simple_marking.xsd', - 'http://data-marking.mitre.org/extensions/MarkingStructure#TLP-1': 'http://stix.mitre.org/XMLSchema/extensions/marking/tlp/1.1.1/tlp_marking.xsd', - 'http://data-marking.mitre.org/extensions/MarkingStructure#Terms_Of_Use-1': 'http://stix.mitre.org/XMLSchema/extensions/marking/terms_of_use/1.0.1/terms_of_use_marking.xsd', - 'http://stix.mitre.org/Campaign-1': 'http://stix.mitre.org/XMLSchema/campaign/1.1.1/campaign.xsd', - 'http://stix.mitre.org/CourseOfAction-1': 'http://stix.mitre.org/XMLSchema/course_of_action/1.1.1/course_of_action.xsd', - 'http://stix.mitre.org/ExploitTarget-1': 'http://stix.mitre.org/XMLSchema/exploit_target/1.1.1/exploit_target.xsd', - 'http://stix.mitre.org/Incident-1': 'http://stix.mitre.org/XMLSchema/incident/1.1.1/incident.xsd', - 'http://stix.mitre.org/Indicator-2': 'http://stix.mitre.org/XMLSchema/indicator/2.1.1/indicator.xsd', - 'http://stix.mitre.org/TTP-1': 'http://stix.mitre.org/XMLSchema/ttp/1.1.1/ttp.xsd', - 'http://stix.mitre.org/ThreatActor-1': 'http://stix.mitre.org/XMLSchema/threat_actor/1.1.1/threat_actor.xsd', - 'http://stix.mitre.org/common-1': 'http://stix.mitre.org/XMLSchema/common/1.1.1/stix_common.xsd', - 'http://stix.mitre.org/default_vocabularies-1': 'http://stix.mitre.org/XMLSchema/default_vocabularies/1.1.1/stix_default_vocabularies.xsd', - 'http://stix.mitre.org/extensions/AP#CAPEC2.7-1': 'http://stix.mitre.org/XMLSchema/extensions/attack_pattern/capec_2.7/1.0.1/capec_2.7_attack_pattern.xsd', - 'http://stix.mitre.org/extensions/Address#CIQAddress3.0-1': 'http://stix.mitre.org/XMLSchema/extensions/address/ciq_3.0/1.1.1/ciq_3.0_address.xsd', - 'http://stix.mitre.org/extensions/Identity#CIQIdentity3.0-1': 'http://stix.mitre.org/XMLSchema/extensions/identity/ciq_3.0/1.1.1/ciq_3.0_identity.xsd', - 'http://stix.mitre.org/extensions/Malware#MAEC4.1-1': 'http://stix.mitre.org/XMLSchema/extensions/malware/maec_4.1/1.0.1/maec_4.1_malware.xsd', - 'http://stix.mitre.org/extensions/StructuredCOA#Generic-1': 'http://stix.mitre.org/XMLSchema/extensions/structured_coa/generic/1.1.1/generic_structured_coa.xsd', - 'http://stix.mitre.org/extensions/TestMechanism#Generic-1': 'http://stix.mitre.org/XMLSchema/extensions/test_mechanism/generic/1.1.1/generic_test_mechanism.xsd', - 'http://stix.mitre.org/extensions/TestMechanism#OVAL5.10-1': 'http://stix.mitre.org/XMLSchema/extensions/test_mechanism/oval_5.10/1.1.1/oval_5.10_test_mechanism.xsd', - 'http://stix.mitre.org/extensions/TestMechanism#OpenIOC2010-1': 'http://stix.mitre.org/XMLSchema/extensions/test_mechanism/open_ioc_2010/1.1.1/open_ioc_2010_test_mechanism.xsd', - 'http://stix.mitre.org/extensions/TestMechanism#Snort-1': 'http://stix.mitre.org/XMLSchema/extensions/test_mechanism/snort/1.1.1/snort_test_mechanism.xsd', - 'http://stix.mitre.org/extensions/TestMechanism#YARA-1': 'http://stix.mitre.org/XMLSchema/extensions/test_mechanism/yara/1.1.1/yara_test_mechanism.xsd', - 'http://stix.mitre.org/extensions/Vulnerability#CVRF-1': 'http://stix.mitre.org/XMLSchema/extensions/vulnerability/cvrf_1.1/1.1.1/cvrf_1.1_vulnerability.xsd', - 'http://stix.mitre.org/stix-1': 'http://stix.mitre.org/XMLSchema/core/1.1.1/stix_core.xsd' -} - -#: Schema locations for namespaces defined by the CybOX language -CYBOX_NS_TO_SCHEMALOCATION = dict( - (ns, loc) for ns, _, loc in cybox.utils.nsparser.NS_LIST if loc -) - -#: Schema locations for namespaces not defined by STIX, but hosted on the STIX website -EXT_NS_TO_SCHEMALOCATION = { - 'urn:oasis:names:tc:ciq:xal:3': 'http://stix.mitre.org/XMLSchema/external/oasis_ciq_3.0/xAL.xsd', - 'urn:oasis:names:tc:ciq:xpil:3': 'http://stix.mitre.org/XMLSchema/external/oasis_ciq_3.0/xPIL.xsd', - 'urn:oasis:names:tc:ciq:xnl:3': 'http://stix.mitre.org/XMLSchema/external/oasis_ciq_3.0/xNL.xsd' -} - -#: Default namespace->alias mappings. These can be overriden by user-provided dictionaries on export. -DEFAULT_STIX_NS_TO_PREFIX = { - 'http://cybox.mitre.org/common-2': 'cyboxCommon', - 'http://cybox.mitre.org/cybox-2': 'cybox', - 'http://data-marking.mitre.org/Marking-1': 'marking', - 'http://data-marking.mitre.org/extensions/MarkingStructure#Simple-1': 'simpleMarking', - 'http://data-marking.mitre.org/extensions/MarkingStructure#TLP-1': 'tlpMarking', - 'http://data-marking.mitre.org/extensions/MarkingStructure#Terms_Of_Use-1': 'TOUMarking', - 'http://stix.mitre.org/Campaign-1': 'campaign', - 'http://stix.mitre.org/CourseOfAction-1': 'coa', - 'http://stix.mitre.org/ExploitTarget-1': 'et', - 'http://stix.mitre.org/Incident-1': 'incident', - 'http://stix.mitre.org/Indicator-2': 'indicator', - 'http://stix.mitre.org/TTP-1': 'ttp', - 'http://stix.mitre.org/ThreatActor-1': 'ta', - 'http://stix.mitre.org/stix-1': 'stix', - 'http://stix.mitre.org/common-1': 'stixCommon', - 'http://stix.mitre.org/default_vocabularies-1': 'stixVocabs', - 'http://stix.mitre.org/extensions/AP#CAPEC2.7-1': 'stix-capec', - 'http://stix.mitre.org/extensions/Address#CIQAddress3.0-1': 'stix-ciqaddress', - 'http://stix.mitre.org/extensions/Identity#CIQIdentity3.0-1': 'ciqIdentity', - 'http://stix.mitre.org/extensions/Malware#MAEC4.1-1': 'stix-maec', - 'http://stix.mitre.org/extensions/StructuredCOA#Generic-1': 'genericStructuredCOA', - 'http://stix.mitre.org/extensions/TestMechanism#Generic-1': 'genericTM', - 'http://stix.mitre.org/extensions/TestMechanism#OVAL5.10-1': 'stix-oval', - 'http://stix.mitre.org/extensions/TestMechanism#OpenIOC2010-1': 'stix-openioc', - 'http://stix.mitre.org/extensions/TestMechanism#Snort-1': 'snortTM', - 'http://stix.mitre.org/extensions/TestMechanism#YARA-1': 'yaraTM', - 'http://stix.mitre.org/extensions/Vulnerability#CVRF-1': 'stix-cvrf' -} - -#: Mapping of extension namespaces to their (typical) prefixes. -DEFAULT_EXT_TO_PREFIX = { - 'http://capec.mitre.org/capec-2': 'capec', - 'http://maec.mitre.org/XMLSchema/maec-package-2': 'maecPackage', - 'http://oval.mitre.org/XMLSchema/oval-definitions-5': 'oval-def', - 'http://oval.mitre.org/XMLSchema/oval-variables-5': 'oval-var', - 'http://schemas.mandiant.com/2010/ioc': 'ioc', - 'http://schemas.mandiant.com/2010/ioc/TR/': 'ioc-tr', - 'http://www.icasi.org/CVRF/schema/cvrf/1.1': 'cvrf', - 'urn:oasis:names:tc:ciq:xal:3': 'xal', - 'urn:oasis:names:tc:ciq:xpil:3': 'xpil', - 'urn:oasis:names:tc:ciq:xnl:3': 'xnl' -} - -#: Mapping of CybOX namespaces to default aliases -DEFAULT_CYBOX_NAMESPACES = dict( - (ns, alias) for (ns, alias, _) in cybox.utils.nsparser.NS_LIST -) - - -#: Mapping of all STIX/STIX Extension/CybOX/XML namespaces -DEFAULT_STIX_NAMESPACES = dict( - itertools.chain( - DEFAULT_CYBOX_NAMESPACES.iteritems(), - XML_NAMESPACES.iteritems(), - DEFAULT_STIX_NS_TO_PREFIX.iteritems(), - DEFAULT_EXT_TO_PREFIX.iteritems() - ) -) - -#: Prefix-to-namespace mapping of the `DEFAULT_STIX_NAMESPACES` mapping -DEFAULT_STIX_PREFIX_TO_NAMESPACE = dict( - (alias, ns) for ns, alias in DEFAULT_STIX_NAMESPACES.iteritems() -) - -#: Tuple of all keys found in `DEFAULT_STIX_NAMESPACES` mapping. -DEFAULT_STIX_NAMESPACES_TUPLE = tuple(DEFAULT_STIX_NAMESPACES.keys()) - -#: Mapping of STIX/CybOX/STIX Extension namespaces to canonical schema locations -DEFAULT_STIX_SCHEMALOCATIONS = dict( - itertools.chain( - STIX_NS_TO_SCHEMALOCATION.iteritems(), - EXT_NS_TO_SCHEMALOCATION.iteritems(), - CYBOX_NS_TO_SCHEMALOCATION.iteritems(), - ) -) - -# python-maec support code -with ignored(ImportError): - from maec.utils.nsparser import NS_LIST - - ns_to_prefix = dict( - (ns, prefix) for (ns, prefix, _) in NS_LIST - ) - - del ns_to_prefix['http://maec.mitre.org/default_vocabularies-1'] - - prefix_to_ns = dict( - (prefix, ns) for (ns, prefix) in ns_to_prefix.iteritems() - ) - - ns_to_schemalocation = dict( - (ns, schemaloc) for ns, _, schemaloc in NS_LIST if schemaloc - ) - - DEFAULT_STIX_NAMESPACES.update(ns_to_prefix) - DEFAULT_STIX_PREFIX_TO_NAMESPACE.update(prefix_to_ns) - DEFAULT_STIX_NAMESPACES_TUPLE = tuple(DEFAULT_STIX_NAMESPACES.iterkeys()) - DEFAULT_STIX_SCHEMALOCATIONS.update(ns_to_schemalocation) +""" +This module automatically registers all STIX namespaces into mixbox. +""" + +import mixbox.namespaces + +Namespace = mixbox.namespaces.Namespace + +NS_CAMPAIGN_OBJECT = Namespace("http://stix.mitre.org/Campaign-1", "campaign", "http://stix.mitre.org/XMLSchema/campaign/1.1.1/campaign.xsd") +NS_CAPEC_OBJECT = Namespace("http://capec.mitre.org/capec-2", "capec", "") +NS_CIQIDENTITY_OBJECT = Namespace("http://stix.mitre.org/extensions/Identity#CIQIdentity3.0-1", "stix-ciqidentity", "http://stix.mitre.org/XMLSchema/extensions/identity/ciq_3.0/1.1.1/ciq_3.0_identity.xsd") +NS_COA_OBJECT = Namespace("http://stix.mitre.org/CourseOfAction-1", "coa", "http://stix.mitre.org/XMLSchema/course_of_action/1.1.1/course_of_action.xsd") +NS_CVRF_OBJECT = Namespace("http://www.icasi.org/CVRF/schema/cvrf/1.1", "cvrf", "") +NS_ET_OBJECT = Namespace("http://stix.mitre.org/ExploitTarget-1", "et", "http://stix.mitre.org/XMLSchema/exploit_target/1.1.1/exploit_target.xsd") +NS_GENERICSTRUCTUREDCOA_OBJECT = Namespace("http://stix.mitre.org/extensions/StructuredCOA#Generic-1", "genericStructuredCOA", "http://stix.mitre.org/XMLSchema/extensions/structured_coa/generic/1.1.1/generic_structured_coa.xsd") +NS_GENERICTM_OBJECT = Namespace("http://stix.mitre.org/extensions/TestMechanism#Generic-1", "genericTM", "http://stix.mitre.org/XMLSchema/extensions/test_mechanism/generic/1.1.1/generic_test_mechanism.xsd") +NS_INCIDENT_OBJECT = Namespace("http://stix.mitre.org/Incident-1", "incident", "http://stix.mitre.org/XMLSchema/incident/1.1.1/incident.xsd") +NS_INDICATOR_OBJECT = Namespace("http://stix.mitre.org/Indicator-2", "indicator", "http://stix.mitre.org/XMLSchema/indicator/2.1.1/indicator.xsd") +NS_IOC_OBJECT = Namespace("http://schemas.mandiant.com/2010/ioc", "ioc", "") +NS_IOCTR_OBJECT = Namespace("http://schemas.mandiant.com/2010/ioc/TR/", "ioc-tr", "") +NS_MARKING_OBJECT = Namespace("http://data-marking.mitre.org/Marking-1", "marking", "http://stix.mitre.org/XMLSchema/data_marking/1.1.1/data_marking.xsd") +NS_OVALDEF_OBJECT = Namespace("http://oval.mitre.org/XMLSchema/oval-definitions-5", "oval-def", "") +NS_OVALVAR_OBJECT = Namespace("http://oval.mitre.org/XMLSchema/oval-variables-5", "oval-var", "") +NS_SIMPLEMARKING_OBJECT = Namespace("http://data-marking.mitre.org/extensions/MarkingStructure#Simple-1", "simpleMarking", "http://stix.mitre.org/XMLSchema/extensions/marking/simple/1.1.1/simple_marking.xsd") +NS_SNORTTM_OBJECT = Namespace("http://stix.mitre.org/extensions/TestMechanism#Snort-1", "snortTM", "http://stix.mitre.org/XMLSchema/extensions/test_mechanism/snort/1.1.1/snort_test_mechanism.xsd") +NS_STIX_OBJECT = Namespace("http://stix.mitre.org/stix-1", "stix", "http://stix.mitre.org/XMLSchema/core/1.1.1/stix_core.xsd") +NS_STIXCAPEC_OBJECT = Namespace("http://stix.mitre.org/extensions/AP#CAPEC2.7-1", "stix-capec", "http://stix.mitre.org/XMLSchema/extensions/attack_pattern/capec_2.7/1.0.1/capec_2.7_attack_pattern.xsd") +NS_STIXCIQADDRESS_OBJECT = Namespace("http://stix.mitre.org/extensions/Address#CIQAddress3.0-1", "stix-ciqaddress", "http://stix.mitre.org/XMLSchema/extensions/address/ciq_3.0/1.1.1/ciq_3.0_address.xsd") +NS_STIXCVRF_OBJECT = Namespace("http://stix.mitre.org/extensions/Vulnerability#CVRF-1", "stix-cvrf", "http://stix.mitre.org/XMLSchema/extensions/vulnerability/cvrf_1.1/1.1.1/cvrf_1.1_vulnerability.xsd") +NS_STIXMAEC_OBJECT = Namespace("http://stix.mitre.org/extensions/Malware#MAEC4.1-1", "stix-maec", "http://stix.mitre.org/XMLSchema/extensions/malware/maec_4.1/1.0.1/maec_4.1_malware.xsd") +NS_STIXOPENIOC_OBJECT = Namespace("http://stix.mitre.org/extensions/TestMechanism#OpenIOC2010-1", "stix-openioc", "http://stix.mitre.org/XMLSchema/extensions/test_mechanism/open_ioc_2010/1.1.1/open_ioc_2010_test_mechanism.xsd") +NS_STIXOVAL_OBJECT = Namespace("http://stix.mitre.org/extensions/TestMechanism#OVAL5.10-1", "stix-oval", "http://stix.mitre.org/XMLSchema/extensions/test_mechanism/oval_5.10/1.1.1/oval_5.10_test_mechanism.xsd") +NS_STIXCOMMON_OBJECT = Namespace("http://stix.mitre.org/common-1", "stixCommon", "http://stix.mitre.org/XMLSchema/common/1.1.1/stix_common.xsd") +NS_STIXVOCABS_OBJECT = Namespace("http://stix.mitre.org/default_vocabularies-1", "stixVocabs", "http://stix.mitre.org/XMLSchema/default_vocabularies/1.1.1/stix_default_vocabularies.xsd") +NS_TA_OBJECT = Namespace("http://stix.mitre.org/ThreatActor-1", "ta", "http://stix.mitre.org/XMLSchema/threat_actor/1.1.1/threat_actor.xsd") +NS_TLPMARKING_OBJECT = Namespace("http://data-marking.mitre.org/extensions/MarkingStructure#TLP-1", "tlpMarking", "http://stix.mitre.org/XMLSchema/extensions/marking/tlp/1.1.1/tlp_marking.xsd") +NS_TOUMARKING_OBJECT = Namespace("http://data-marking.mitre.org/extensions/MarkingStructure#Terms_Of_Use-1", "TOUMarking", "http://stix.mitre.org/XMLSchema/extensions/marking/terms_of_use/1.0.1/terms_of_use_marking.xsd") +NS_TTP_OBJECT = Namespace("http://stix.mitre.org/TTP-1", "ttp", "http://stix.mitre.org/XMLSchema/ttp/1.1.1/ttp.xsd") +NS_XAL_OBJECT = Namespace("urn:oasis:names:tc:ciq:xal:3", "xal", "http://stix.mitre.org/XMLSchema/external/oasis_ciq_3.0/xAL.xsd") +NS_XNL_OBJECT = Namespace("urn:oasis:names:tc:ciq:xnl:3", "xnl", "http://stix.mitre.org/XMLSchema/external/oasis_ciq_3.0/xNL.xsd") +NS_XPIL_OBJECT = Namespace("urn:oasis:names:tc:ciq:xpil:3", "xpil", "http://stix.mitre.org/XMLSchema/external/oasis_ciq_3.0/xPIL.xsd") +NS_YARATM_OBJECT = Namespace("http://stix.mitre.org/extensions/TestMechanism#YARA-1", "yaraTM", "http://stix.mitre.org/XMLSchema/extensions/test_mechanism/yara/1.1.1/yara_test_mechanism.xsd") + +STIX_NAMESPACES = mixbox.namespaces.NamespaceSet() + +# Magic to automatically register all Namespaces defined in this module. +for k, v in list(globals().items()): + if k.startswith('NS_'): + mixbox.namespaces.register_namespace(v) + STIX_NAMESPACES.add_namespace(v) diff --git a/stix/utils/parser.py b/stix/utils/parser.py index f79365a6..698aad56 100644 --- a/stix/utils/parser.py +++ b/stix/utils/parser.py @@ -1,261 +1,28 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -# stdlib -from distutils.version import StrictVersion - -# external -from lxml import etree - -# internal import stix -import stix.xmlconst as xmlconst - -# relative -from . import ignored, is_etree, is_element - - -class UnknownVersionError(Exception): - """Raised when a parsed STIX document contains no version information.""" - pass - - -class UnsupportedVersionError(Exception): - """Raised when a parsed STIX document contains a version that is - not supported by this verison of python-stix. - - """ - def __init__(self, message, expected=None, found=None): - super(UnsupportedVersionError, self).__init__(message) - self.expected = expected - self.found = found - - -class UnsupportedRootElementError(Exception): - """Raised when an input STIX document does not contain a supported root- - level element. - - """ - def __init__(self, message, expected=None, found=None): - super(UnsupportedRootElementError, self).__init__(message) - self.expected = expected - self.found = found +from stix.xmlconst import TAG_STIX_PACKAGE +import mixbox.parser +# Import these from mixbox for backward compatibility +from mixbox.parser import (UnknownVersionError, UnsupportedVersionError, + UnsupportedRootElementError) # Alias for backwards compatibility UnsupportedRootElement = UnsupportedRootElementError -def get_xml_parser(encoding=None): - """Returns an ``etree.ETCompatXMLParser`` instance.""" - parser = etree.ETCompatXMLParser( - huge_tree=True, - remove_comments=True, - strip_cdata=False, - remove_blank_text=True, - resolve_entities=False, - encoding=encoding - ) - - return parser - - -def get_etree(doc, encoding=None): - if is_etree(doc): - return doc - elif is_element(doc): - return etree.ElementTree(doc) - else: - parser = get_xml_parser(encoding=encoding) - return etree.parse(doc, parser=parser) - - -def get_etree_root(doc, encoding=None): - """Returns an instance of lxml.etree._Element for the given `doc` input. - - Args: - doc: The input XML document. Can be an instance of - ``lxml.etree._Element``, ``lxml.etree._ElementTree``, a file-like - object, or a string filename. - encoding: The character encoding of `doc`. If ``None``, an attempt - will be made to determine the character encoding by the XML - parser. - - Returns: - An ``lxml.etree._Element`` instance for `doc`. - - Raises: - IOError: If `doc` cannot be found. - lxml.ParseError: If `doc` is a malformed XML document. - - """ - tree = get_etree(doc, encoding) - root = tree.getroot() - - return root - - -def get_schemaloc_pairs(node): - """Parses the xsi:schemaLocation attribute on `node`. - - Returns: - A list of (ns, schemaLocation) tuples for the node. - - Raises: - KeyError: If `node` does not have an xsi:schemaLocation attribute. - - """ - schemalocs = node.attrib[xmlconst.TAG_SCHEMALOCATION] - l = schemalocs.split() - return zip(l[::2], l[1::2]) - - -def get_document_version(doc): - root = get_etree_root(doc) - - if 'version' in root.attrib: - return root.attrib['version'] - - raise UnknownVersionError( - "Unable to determine the version if the input STIX document: no " - "version attribute found on the root element." - ) - - -def root_tag(doc): - root = get_etree_root(doc) - return root.tag - - -def is_stix(doc): - root = root_tag(doc) - return root == xmlconst.TAG_STIX_PACKAGE - - -class EntityParser(object): - def __init__(self): - pass - - def _check_version(self, tree): - """Returns true of the instance document @tree is a version supported - by python-stix. - - """ - document_version = get_document_version(tree) - supported = stix.supported_stix_version() - - if StrictVersion(supported) == StrictVersion(document_version): - return - - error = ( - "Your python-stix library supports STIX %s. Document version was " - "%s" % (supported, document_version) - ) - - raise UnsupportedVersionError( - message=error, - expected=supported, - found=document_version - ) - - def _check_root(self, tree): - if is_stix(tree): - return - - error = "Document root element must be an instance of STIX_Package" - raise UnsupportedRootElement( - message=error, - expected=xmlconst.TAG_STIX_PACKAGE, - found=root_tag(tree), - ) - - def _apply_input_namespaces(self, tree, entity): - root = get_etree_root(tree) - entity.__input_namespaces__ = dict(root.nsmap.iteritems()) - - def _apply_input_schemalocations(self, tree, entity): - """Attaches an __input_schemalocations__ dictionary to the input entity. - - Args: - tree: The input etree instance - entity: The entity to attach the schemlocation dictionary to - - """ - root = get_etree_root(tree) - - with ignored(KeyError): - pairs = get_schemaloc_pairs(root) - entity.__input_schemalocations__ = dict(pairs) - - def parse_xml_to_obj(self, xml_file, check_version=True, check_root=True, - encoding=None): - """Creates a STIX binding object from the supplied xml file. - - Args: - xml_file: A filename/path or a file-like object representing a STIX - instance document - check_version: Inspect the version before parsing. - check_root: Inspect the root element before parsing. - encoding: The character encoding of the input `xml_file`. - - Raises: - .UnknownVersionError: If `check_version` is ``True`` and `xml_file` - does not contain STIX version information. - .UnsupportedVersionError: If `check_version` is ``False`` and - `xml_file` contains an unsupported STIX version. - .UnsupportedRootElement: If `check_root` is ``True`` and `xml_file` - contains an invalid root element. - - """ - root = get_etree_root(xml_file, encoding=encoding) - - if check_version: - self._check_version(root) - - if check_root: - self._check_root(root) - - import stix.bindings.stix_core as stix_core_binding - stix_package_obj = stix_core_binding.STIXType().factory() - stix_package_obj.build(root) - - return stix_package_obj - - def parse_xml(self, xml_file, check_version=True, check_root=True, - encoding=None): - """Creates a python-stix STIXPackage object from the supplied xml_file. - - Args: - xml_file: A filename/path or a file-like object representing a STIX - instance document - check_version: Inspect the version before parsing. - check_root: Inspect the root element before parsing. - encoding: The character encoding of the input `xml_file`. If - ``None``, an attempt will be made to determine the input - character encoding. +class EntityParser(mixbox.parser.EntityParser): - Raises: - .UnknownVersionError: If `check_version` is ``True`` and `xml_file` - does not contain STIX version information. - .UnsupportedVersionError: If `check_version` is ``False`` and - `xml_file` contains an unsupported STIX version. - .UnsupportedRootElement: If `check_root` is ``True`` and `xml_file` - contains an invalid root element. + def supported_tags(self): + return [TAG_STIX_PACKAGE] - """ - root = get_etree_root(xml_file, encoding=encoding) + def get_version(self, root): + return root.attrib.get('version') - stix_package_obj = self.parse_xml_to_obj( - xml_file=root, - check_version=check_version, - check_root=check_root, - encoding=encoding - ) - - from stix.core import STIXPackage # resolve circular dependencies - stix_package = STIXPackage().from_obj(stix_package_obj) + def supported_versions(self, tag=TAG_STIX_PACKAGE): + return stix.supported_stix_version() - self._apply_input_namespaces(root, stix_package) - self._apply_input_schemalocations(root, stix_package) - - return stix_package + def get_entity_class(self, tag=TAG_STIX_PACKAGE): + return stix.core.STIXPackage diff --git a/stix/utils/walk.py b/stix/utils/walk.py index 6965a20f..f995677f 100644 --- a/stix/utils/walk.py +++ b/stix/utils/walk.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. # stdlib @@ -6,6 +6,7 @@ # external from cybox.common import ObjectProperties +from mixbox.vendor.six import iteritems # internal from . import is_entity, is_entitylist, attr_name, is_sequence @@ -25,9 +26,15 @@ def _is_skippable(owner, varname, varobj): def _iter_vars(obj): - instance_vars = getattr(obj, "__dict__", {}).iteritems() - typed_fields = getattr(obj, "_fields", {}).iteritems() - return itertools.chain(instance_vars, typed_fields) + attrs = [] + + if hasattr(obj, "__dict__"): + attrs.append(iteritems(vars(obj))) + + if hasattr(obj, "_fields"): + attrs.append(iteritems(obj._fields)) + + return itertools.chain.from_iterable(attrs) def iterwalk(obj): diff --git a/stix/version.py b/stix/version.py index e4f82811..9eb1b590 100644 --- a/stix/version.py +++ b/stix/version.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. -__version__ = "1.1.1.6.dev0" +__version__ = "1.1.1.19" diff --git a/stix/xmlconst.py b/stix/xmlconst.py index e508eeb2..bffe65c5 100644 --- a/stix/xmlconst.py +++ b/stix/xmlconst.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, The MITRE Corporation. All rights reserved. +# Copyright (c) 2016, The MITRE Corporation. All rights reserved. # See LICENSE.txt for complete terms. # XML NAMESPACES diff --git a/tox.ini b/tox.ini index 59c9179a..275e8bdf 100644 --- a/tox.ini +++ b/tox.ini @@ -1,24 +1,49 @@ [tox] -envlist = py26, py27, lxml23 +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 # We call this "lxml23" instead of "rhel6", since RHEL6 ships with LXML 2.2.3. # python-stix requires at least 2.3. [testenv:lxml23] -basepython=python2.6 +basepython=python2.7 commands = nosetests stix deps = + # Pin specific versions of LXML and python-dateutil lxml==2.3 python-dateutil==1.4.1 - cybox - maec - nose + -rrequirements.txt + +# Test the behavior when MAEC is not installed in the environment. +[testenv:no-maec] +commands = + nosetests stix +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, packaging + 3.4: py34, no-maec + 3.5: py35, no-maec + 3.6: py36, no-maec, packaging + 3.7: py37, no-maec + 3.8: py38, no-maec