├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── docs ├── Makefile ├── api │ ├── clients.rst │ ├── common.rst │ ├── constants.rst │ ├── libtaxii.rst │ ├── messages_10.rst │ ├── messages_11.rst │ ├── modules.rst │ ├── query.rst │ └── validation.rst ├── changes.rst ├── conf.py ├── getting_started.rst ├── index.rst ├── installation.rst ├── make.bat └── scripts.rst ├── libtaxii ├── __init__.py ├── clients.py ├── common.py ├── constants.py ├── messages.py ├── messages_10.py ├── messages_11.py ├── scripts │ ├── __init__.py │ ├── collection_information_client.py │ ├── discovery_client.py │ ├── discovery_client_10.py │ ├── feed_information_client_10.py │ ├── fulfillment_client.py │ ├── inbox_client.py │ ├── inbox_client_10.py │ ├── poll_client.py │ ├── poll_client_10.py │ └── query_client.py ├── taxii_default_query.py ├── test │ ├── __init__.py │ ├── argument_parser_test.py │ ├── clients_test.py │ ├── data │ │ └── configuration.ini │ ├── input │ │ └── 1.1 │ │ │ ├── Collection_Information_Response.xml │ │ │ ├── Discovery_Response.xml │ │ │ ├── Inbox_Message.xml │ │ │ ├── Subscription_Management_Request.xml │ │ │ └── Subscription_Management_Response.xml │ ├── messages_10_test.py │ ├── messages_11_test.py │ ├── output │ │ └── 1.1 │ │ │ └── readme.md │ ├── test_clients.py │ ├── test_xml_encoding.py │ ├── to_text_11_test.py │ └── validation_test.py ├── validation.py ├── version.py └── xsd │ ├── TAXII_DefaultQuery_Schema.xsd │ ├── TAXII_XMLMessageBinding_Schema.xsd │ ├── TAXII_XMLMessageBinding_Schema_11.xsd │ └── xmldsig-core-schema.xsd ├── requirements.txt ├── setup.cfg ├── setup.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | *.swo 4 | build/ 5 | dist/ 6 | libtaxii.egg-info/ 7 | venv/ 8 | .pytest_cache 9 | .tox 10 | output.txt 11 | libtaxii/test/output/ 12 | docs/_build/ 13 | .idea 14 | .cache 15 | .coverage 16 | htmlcov/ 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | language: python 3 | cache: pip 4 | dist: xenial 5 | python: 6 | - "2.7" 7 | - "3.4" 8 | - "3.5" 9 | - "3.6" 10 | - "3.7" 11 | - "3.8" 12 | matrix: 13 | include: 14 | # https://blog.travis-ci.com/2019-04-15-xenial-default-build-environment 15 | # https://travis-ci.community/t/issue-with-python-2-6-on-linux/3861/2 16 | - python: 2.6 17 | dist: trusty 18 | install: 19 | - pip install -U pip setuptools 20 | - pip install tox-travis 21 | script: tox 22 | notifications: 23 | email: 24 | - stix-commits-list@groups.mitre.org 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, The MITRE Corporation 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of The MITRE Corporation nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | include README.rst 3 | include libtaxii/xsd/*.xsd 4 | include requirements.txt 5 | recursive-include docs * 6 | recursive-exclude docs *.pyc 7 | recursive-exclude docs *.pyo 8 | prune docs/_build 9 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | libtaxii 2 | ======== 3 | 4 | A Python library for handling `Trusted Automated eXchange of Indicator Information (TAXII™) `_ v1.x Messages and invoking TAXII Services. 5 | 6 | :Source: https://github.com/TAXIIProject/libtaxii/ 7 | :Documentation: https://libtaxii.readthedocs.io/ 8 | :Information: https://taxiiproject.github.io/ 9 | :Download: https://pypi.python.org/pypi/libtaxii/ 10 | 11 | |travis badge| |landscape.io badge| |version badge| |downloads badge| 12 | 13 | .. |travis badge| image:: https://api.travis-ci.org/TAXIIProject/libtaxii.svg?branch=master 14 | :target: https://travis-ci.org/TAXIIProject/libtaxii 15 | :alt: Build Status 16 | .. |landscape.io badge| image:: https://landscape.io/github/TAXIIProject/libtaxii/master/landscape.svg?style=flat 17 | :target: https://landscape.io/github/TAXIIProject/libtaxii/master 18 | :alt: Code Health 19 | .. |version badge| image:: https://img.shields.io/pypi/v/libtaxii.svg?maxAge=3600 20 | :target: https://pypi.python.org/pypi/libtaxii/ 21 | .. |downloads badge| image:: https://img.shields.io/pypi/dm/libtaxii.svg?maxAge=3600 22 | :target: https://pypi.python.org/pypi/libtaxii/ 23 | 24 | Overview 25 | -------- 26 | 27 | A primary goal of libtaxii is to remain faithful to both the TAXII 28 | specifications and to customary Python practices. libtaxii is designed to be 29 | intuitive both to Python developers and XML developers. 30 | 31 | 32 | Installation 33 | ------------ 34 | 35 | Use pip to install or upgrade libtaxii:: 36 | 37 | $ pip install libtaxii [--upgrade] 38 | 39 | For more information, see the `Installation instructions 40 | `_. 41 | 42 | 43 | Getting Started 44 | --------------- 45 | 46 | Read the `Getting Started guide 47 | `_. 48 | 49 | 50 | Layout 51 | ------ 52 | 53 | The libtaxii repository has the following layout: 54 | 55 | * ``docs/`` - Used to build the `documentation 56 | `_. 57 | * ``libtaxii/`` - The main libtaxii source. 58 | * ``libtaxii/tests/`` - libtaxii tests. 59 | * ``xsd/`` - A copy of the TAXII XML Schemas, used by libtaxii for validation. 60 | 61 | 62 | Versioning 63 | ---------- 64 | 65 | Releases of libtaxii are given ``major.minor.revision`` version numbers, where 66 | ``major`` and ``minor`` correspond to the TAXII version being supported. The 67 | ``revision``` number is used to indicate new versions of libtaxii. 68 | 69 | 70 | Feedback 71 | -------- 72 | 73 | Please provide feedback and/or comments on open issues to taxii@mitre.org. 74 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/libtaxii.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/libtaxii.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/libtaxii" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/libtaxii" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/api/clients.rst: -------------------------------------------------------------------------------- 1 | clients Module 2 | ============== 3 | 4 | .. module:: libtaxii.clients 5 | 6 | 7 | Classes 8 | ------- 9 | 10 | .. autoclass:: libtaxii.clients.HttpClient 11 | :members: set_auth_type, set_auth_credentials, set_proxy, set_use_https, 12 | set_verify_server, call_taxii_service2 13 | 14 | 15 | Examples 16 | -------- 17 | 18 | TAXII clients have three types of authentication credentials: None, HTTP Basic, and TLS Certificate. This section demonstrates usage of all three auth types. 19 | 20 | All examples assume the following imports: 21 | 22 | .. code-block:: python 23 | 24 | import libtaxii as t 25 | import libtaxii.messages_11 as tm11 26 | import libtaxii.clients as tc 27 | from libtaxii.common import generate_message_id 28 | from libtaxii.constants import * 29 | from dateutil.tz import tzutc 30 | 31 | Using No Credentials 32 | ******************** 33 | 34 | .. code-block:: python 35 | 36 | client = tc.HttpClient() 37 | client.set_auth_type(tc.HttpClient.AUTH_NONE) 38 | client.set_use_https(False) 39 | 40 | discovery_request = tm11.DiscoveryRequest(generate_message_id()) 41 | discovery_xml = discovery_request.to_xml(pretty_print=True) 42 | 43 | http_resp = client.call_taxii_service2('hailataxii.com', '/taxii-discovery-service', VID_TAXII_XML_11, discovery_xml) 44 | taxii_message = t.get_message_from_http_response(http_resp, discovery_request.message_id) 45 | print taxii_message.to_xml(pretty_print=True) 46 | 47 | Using Basic HTTP Auth 48 | ********************* 49 | 50 | .. code-block:: python 51 | 52 | client = tc.HttpClient() 53 | client.set_auth_type(tc.HttpClient.AUTH_BASIC) 54 | client.set_auth_credentials({'username': 'guest', 'password': 'guest'}) 55 | 56 | discovery_request = tm11.DiscoveryRequest(generate_message_id()) 57 | discovery_xml = discovery_request.to_xml(pretty_print=True) 58 | 59 | http_resp = client.call_taxii_service2('hailataxii.com', '/taxii-discovery-service', VID_TAXII_XML_11, discovery_xml) 60 | taxii_message = t.get_message_from_http_response(http_resp, discovery_request.message_id) 61 | print taxii_message.to_xml(pretty_print=True) 62 | 63 | Using TLS Certificate Auth 64 | ************************** 65 | 66 | Note: The following code is provided as an example of this authentication method, but will not work as-is, because Hail A Taxii does not support TLS. 67 | 68 | .. code-block:: python 69 | 70 | client = tc.HttpClient() 71 | client.set_use_https(True) 72 | client.set_auth_type(tc.HttpClient.AUTH_CERT) 73 | client.set_auth_credentials({'key_file': '../PATH_TO_KEY_FILE.key', 'cert_file': '../PATH_TO_CERT_FILE.crt'}) 74 | 75 | discovery_request = tm11.DiscoveryRequest(generate_message_id()) 76 | discovery_xml = discovery_request.to_xml(pretty_print=True) 77 | 78 | http_resp = client.call_taxii_service2('hailataxii.com', '/taxii-discovery-service/', VID_TAXII_XML_11, discovery_xml) 79 | taxii_message = t.get_message_from_http_response(http_resp, discovery_request.message_id) 80 | print taxii_message.to_xml(pretty_print=True) 81 | -------------------------------------------------------------------------------- /docs/api/common.rst: -------------------------------------------------------------------------------- 1 | common Module 2 | ============= 3 | 4 | .. automodule:: libtaxii.common 5 | 6 | 7 | Functions 8 | --------- 9 | .. autofunction:: get_xml_parser 10 | .. autofunction:: set_xml_parser 11 | 12 | Classes 13 | ------- 14 | 15 | .. autoclass:: TAXIIBase 16 | :members: 17 | -------------------------------------------------------------------------------- /docs/api/constants.rst: -------------------------------------------------------------------------------- 1 | constants Module 2 | ================ 3 | 4 | .. module:: libtaxii.constants 5 | 6 | Constants 7 | --------- 8 | 9 | Version IDs 10 | *********** 11 | 12 | The following constants can be used as TAXII Version IDs 13 | 14 | .. autodata:: VID_TAXII_SERVICES_10 15 | .. autodata:: VID_TAXII_SERVICES_11 16 | .. autodata:: VID_TAXII_XML_10 17 | .. autodata:: VID_TAXII_XML_11 18 | .. autodata:: VID_TAXII_HTTP_10 19 | .. autodata:: VID_TAXII_HTTPS_10 20 | 21 | The following are third-party Version IDs included in libtaxii for convenience. 22 | 23 | .. autodata:: VID_CERT_EU_JSON_10 24 | 25 | 26 | Content Binding IDs 27 | ******************* 28 | 29 | The following constants should be used as the Content Binding ID for STIX XML. 30 | 31 | .. autodata:: CB_STIX_XML_10 32 | .. autodata:: CB_STIX_XML_101 33 | .. autodata:: CB_STIX_XML_11 34 | .. autodata:: CB_STIX_XML_111 35 | .. autodata:: CB_STIX_XML_12 36 | 37 | These other Content Binding IDs are included for convenience as well. 38 | 39 | .. autodata:: CB_CAP_11 40 | .. autodata:: CB_XENC_122002 41 | .. autodata:: CB_SMIME 42 | 43 | Namespace Map 44 | ************* 45 | This constant contains commonly namespaces and aliases in TAXII. 46 | 47 | .. autodata:: NS_MAP 48 | 49 | Message Types 50 | ************* 51 | 52 | .. autodata:: MSG_STATUS_MESSAGE 53 | .. autodata:: MSG_DISCOVERY_REQUEST 54 | .. autodata:: MSG_DISCOVERY_RESPONSE 55 | .. autodata:: MSG_FEED_INFORMATION_REQUEST 56 | .. autodata:: MSG_FEED_INFORMATION_RESPONSE 57 | .. autodata:: MSG_MANAGE_FEED_SUBSCRIPTION_REQUEST 58 | .. autodata:: MSG_MANAGE_FEED_SUBSCRIPTION_RESPONSE 59 | .. autodata:: MSG_POLL_REQUEST 60 | .. autodata:: MSG_POLL_RESPONSE 61 | .. autodata:: MSG_INBOX_MESSAGE 62 | 63 | .. autodata:: MSG_TYPES_10 64 | 65 | .. autodata:: MSG_POLL_FULFILLMENT_REQUEST 66 | .. autodata:: MSG_COLLECTION_INFORMATION_REQUEST 67 | .. autodata:: MSG_COLLECTION_INFORMATION_RESPONSE 68 | .. autodata:: MSG_MANAGE_COLLECTION_SUBSCRIPTION_REQUEST 69 | .. autodata:: MSG_MANAGE_COLLECTION_SUBSCRIPTION_RESPONSE 70 | 71 | .. autodata:: MSG_TYPES_11 72 | 73 | 74 | Status Types 75 | ************ 76 | 77 | These constants are used in :py:class:`StatusMessage`. 78 | 79 | .. autodata:: ST_BAD_MESSAGE 80 | .. autodata:: ST_DENIED 81 | .. autodata:: ST_FAILURE 82 | .. autodata:: ST_NOT_FOUND 83 | .. autodata:: ST_POLLING_UNSUPPORTED 84 | .. autodata:: ST_RETRY 85 | .. autodata:: ST_SUCCESS 86 | .. autodata:: ST_UNAUTHORIZED 87 | .. autodata:: ST_UNSUPPORTED_MESSAGE_BINDING 88 | .. autodata:: ST_UNSUPPORTED_CONTENT_BINDING 89 | .. autodata:: ST_UNSUPPORTED_PROTOCOL 90 | 91 | .. autodata:: ST_TYPES_10 92 | 93 | .. autodata:: ST_ASYNCHRONOUS_POLL_ERROR 94 | .. autodata:: ST_DESTINATION_COLLECTION_ERROR 95 | .. autodata:: ST_INVALID_RESPONSE_PART 96 | .. autodata:: ST_NETWORK_ERROR 97 | .. autodata:: ST_PENDING 98 | .. autodata:: ST_UNSUPPORTED_QUERY 99 | 100 | .. autodata:: ST_TYPES_11 101 | 102 | 103 | Subscription Actions 104 | ******************** 105 | 106 | These constants are used in :py:class:`ManageFeedSubscriptionRequest` 107 | 108 | .. autodata:: ACT_SUBSCRIBE 109 | .. autodata:: ACT_UNSUBSCRIBE 110 | .. autodata:: ACT_STATUS 111 | 112 | .. autodata:: ACT_TYPES_10 113 | 114 | .. autodata:: ACT_PAUSE 115 | .. autodata:: ACT_RESUME 116 | 117 | .. autodata:: ACT_TYPES_11 118 | 119 | 120 | Service Types 121 | **************** 122 | 123 | These constants are used to indicate the type of service. 124 | 125 | .. autodata:: SVC_INBOX 126 | .. autodata:: SVC_POLL 127 | .. autodata:: SVC_FEED_MANAGEMENT 128 | .. autodata:: SVC_DISCOVERY 129 | 130 | .. autodata:: SVC_TYPES_10 131 | 132 | .. autodata:: SVC_COLLECTION_MANAGEMENT 133 | 134 | .. autodata:: SVC_TYPES_11 135 | 136 | Subscription Statuses 137 | ********************* 138 | 139 | These constants are used in :py:class:`ManageCollectionSubscriptionResponse` 140 | 141 | .. autodata:: SS_ACTIVE 142 | .. autodata:: SS_PAUSED 143 | .. autodata:: SS_UNSUBSCRIBED 144 | 145 | .. autodata:: SS_TYPES_11 146 | 147 | 148 | Response Types 149 | ************** 150 | 151 | These constants are used to indicate the type of response returned. 152 | 153 | .. autodata:: RT_FULL 154 | .. autodata:: RT_COUNT_ONLY 155 | 156 | .. autodata:: RT_TYPES_11 157 | 158 | 159 | Collection Types 160 | **************** 161 | 162 | These constants are used to indicate the type of collection. 163 | 164 | .. autodata:: CT_DATA_FEED 165 | .. autodata:: CT_DATA_SET 166 | 167 | .. autodata:: CT_TYPES_11 168 | 169 | Status Details 170 | ************** 171 | 172 | These constants are used in :py:class:`StatusMessage`. 173 | 174 | .. autodata:: SD_ACCEPTABLE_DESTINATION 175 | .. autodata:: SD_MAX_PART_NUMBER 176 | .. autodata:: SD_ITEM 177 | .. autodata:: SD_ESTIMATED_WAIT 178 | .. autodata:: SD_RESULT_ID 179 | .. autodata:: SD_WILL_PUSH 180 | .. autodata:: SD_SUPPORTED_BINDING 181 | .. autodata:: SD_SUPPORTED_CONTENT 182 | .. autodata:: SD_SUPPORTED_PROTOCOL 183 | .. autodata:: SD_SUPPORTED_QUERY 184 | 185 | .. autodata:: SD_TYPES_11 186 | 187 | .. autodata:: SD_CAPABILITY_MODULE 188 | .. autodata:: SD_PREFERRED_SCOPE 189 | .. autodata:: SD_ALLOWED_SCOPE 190 | .. autodata:: SD_TARGETING_EXPRESSION_ID 191 | 192 | Query Formats 193 | ************* 194 | 195 | These constants are used to indicate query format. 196 | 197 | ..autodata:: FID_TAXII_DEFAULT_QUERY_10 198 | 199 | Query Capability Modules 200 | ************************ 201 | 202 | These constants are used to indicate TAXII Default Query Capability Modules 203 | 204 | .. autodata:: CM_CORE 205 | .. autodata:: CM_REGEX 206 | .. autodata:: CM_TIMESTAMP 207 | 208 | .. autodata:: CM_IDS 209 | 210 | Query Operators 211 | *************** 212 | 213 | These constants are used to identify the operator in :py:class`Criteria` 214 | 215 | .. autodata:: OP_OR 216 | .. autodata:: OP_AND 217 | 218 | .. autodata:: OP_TYPES 219 | 220 | Query Status Types 221 | ****************** 222 | 223 | TAXII Default Query 1.0 identifies three additional Status Types: 224 | 225 | .. autodata:: ST_UNSUPPORTED_CAPABILITY_MODULE 226 | .. autodata:: ST_UNSUPPORTED_TARGETING_EXPRESSION 227 | .. autodata:: ST_UNSUPPORTED_TARGETING_EXPRESSION_ID 228 | 229 | 230 | Query Parameters 231 | **************** 232 | 233 | These constants are used to identify parameters. 234 | 235 | .. autodata:: P_VALUE 236 | .. autodata:: P_MATCH_TYPE 237 | .. autodata:: P_CASE_SENSITIVE 238 | 239 | .. autodata:: P_NAMES 240 | 241 | Query Relationships 242 | ******************* 243 | 244 | These constants are used to identify relationships 245 | 246 | .. autodata:: R_EQUALS 247 | .. autodata:: R_NOT_EQUALS 248 | .. autodata:: R_GREATER_THAN 249 | .. autodata:: R_GREATER_THAN_OR_EQUAL 250 | .. autodata:: R_LESS_THAN 251 | .. autodata:: R_LESS_THAN_OR_EQUAL 252 | .. autodata:: R_DOES_NOT_EXIST 253 | .. autodata:: R_EXISTS 254 | .. autodata:: R_BEGINS_WITH 255 | .. autodata:: R_ENDS_WITH 256 | .. autodata:: R_CONTAINS 257 | .. autodata:: R_MATCHES 258 | 259 | .. autodata:: R_NAMES 260 | -------------------------------------------------------------------------------- /docs/api/libtaxii.rst: -------------------------------------------------------------------------------- 1 | libtaxii Module 2 | =============== 3 | 4 | .. module:: libtaxii 5 | 6 | Functions 7 | --------- 8 | 9 | .. autofunction:: get_message_from_http_response 10 | -------------------------------------------------------------------------------- /docs/api/messages_10.rst: -------------------------------------------------------------------------------- 1 | messages_10 Module 2 | ================== 3 | 4 | .. automodule:: libtaxii.messages_10 5 | 6 | .. note:: 7 | 8 | The examples on this page assume that you have run the equivalent of 9 | 10 | .. code-block:: python 11 | 12 | import datetime 13 | from dateutil.tz import tzutc 14 | import libtaxii as t 15 | import libtaxii.messages_10 as tm10 16 | from libtaxii.constants import * 17 | 18 | .. testsetup:: 19 | 20 | import datetime 21 | from dateutil.tz import tzutc 22 | import libtaxii as t 23 | import libtaxii.messages_10 as tm10 24 | from libtaxii.constants import * 25 | 26 | Status Message 27 | -------------- 28 | 29 | .. autoclass:: StatusMessage 30 | 31 | **Example:** 32 | 33 | .. testcode:: 34 | 35 | status_message1 = tm10.StatusMessage( 36 | message_id=tm10.generate_message_id(), 37 | in_response_to="12345", 38 | status_type=ST_SUCCESS, 39 | status_detail='Machine-processable info here!', 40 | message='This is a message.') 41 | 42 | 43 | Discovery Request 44 | ----------------- 45 | 46 | .. autoclass:: DiscoveryRequest 47 | 48 | **Example:** 49 | 50 | .. testcode:: 51 | 52 | ext_headers = {'name1': 'val1', 'name2': 'val2'} 53 | discovery_request = tm10.DiscoveryRequest( 54 | message_id=tm10.generate_message_id(), 55 | extended_headers=ext_headers) 56 | 57 | 58 | Discovery Response 59 | ------------------ 60 | 61 | .. autoclass:: DiscoveryResponse 62 | .. autoclass:: ServiceInstance 63 | 64 | **Example:** 65 | 66 | .. testcode:: 67 | 68 | discovery_response = tm10.DiscoveryResponse( 69 | message_id=tm10.generate_message_id(), 70 | in_response_to=discovery_request.message_id) 71 | 72 | service_instance = tm10.ServiceInstance( 73 | service_type=SVC_INBOX, 74 | services_version=VID_TAXII_SERVICES_10, 75 | protocol_binding=VID_TAXII_HTTPS_10, 76 | service_address='https://example.com/inbox/', 77 | message_bindings=[VID_TAXII_XML_10], 78 | inbox_service_accepted_content=[CB_STIX_XML_10], 79 | available=True, 80 | message='This is a sample inbox service instance') 81 | 82 | discovery_response.service_instances.append(service_instance) 83 | 84 | # Alternatively, you could define the service instance(s) first and use the 85 | # following: 86 | 87 | service_instance_list = [service_instance] 88 | discovery_response = tm10.DiscoveryResponse( 89 | message_id=tm10.generate_message_id(), 90 | in_response_to=discovery_request.message_id, 91 | service_instances=service_instance_list) 92 | 93 | 94 | Feed Information Request 95 | ------------------------ 96 | 97 | .. autoclass:: FeedInformationRequest 98 | 99 | **Example:** 100 | 101 | .. testcode:: 102 | 103 | ext_headers = {'name1': 'val1', 'name2': 'val2'} 104 | feed_information_request= tm10.FeedInformationRequest( 105 | message_id=tm10.generate_message_id(), 106 | extended_headers=ext_headers) 107 | 108 | 109 | Feed Information Response 110 | ------------------------- 111 | 112 | .. autoclass:: FeedInformationResponse 113 | .. autoclass:: FeedInformation 114 | .. autoclass:: PushMethod 115 | .. autoclass:: PollingServiceInstance 116 | .. autoclass:: SubscriptionMethod 117 | 118 | **Example:** 119 | 120 | .. testcode:: 121 | 122 | push_method1 = tm10.PushMethod( 123 | push_protocol=VID_TAXII_HTTP_10, 124 | push_message_bindings=[VID_TAXII_XML_10]) 125 | 126 | polling_service1 = tm10.PollingServiceInstance( 127 | poll_protocol=VID_TAXII_HTTP_10, 128 | poll_address='http://example.com/PollService/', 129 | poll_message_bindings=[VID_TAXII_XML_10]) 130 | 131 | subscription_service1 = tm10.SubscriptionMethod( 132 | subscription_protocol=VID_TAXII_HTTP_10, 133 | subscription_address='http://example.com/SubsService/', 134 | subscription_message_bindings=[VID_TAXII_XML_10]) 135 | 136 | feed1 = tm10.FeedInformation( 137 | feed_name='Feed1', 138 | feed_description='Description of a feed', 139 | supported_contents=[CB_STIX_XML_10], 140 | available=True, 141 | push_methods=[push_method1], 142 | polling_service_instances=[polling_service1], 143 | subscription_methods=[subscription_service1]) 144 | 145 | feed_information_response1 = tm10.FeedInformationResponse( 146 | message_id=tm10.generate_message_id(), 147 | in_response_to=tm10.generate_message_id(), 148 | feed_informations=[feed1]) 149 | 150 | 151 | Manage Feed Subscription Request 152 | -------------------------------- 153 | 154 | .. autoclass:: ManageFeedSubscriptionRequest 155 | 156 | **Example:** 157 | 158 | .. testcode:: 159 | 160 | delivery_parameters1 = tm10.DeliveryParameters( 161 | inbox_protocol=VID_TAXII_HTTP_10, 162 | inbox_address='http://example.com/inbox', 163 | delivery_message_binding=VID_TAXII_XML_10, 164 | content_bindings=[CB_STIX_XML_10]) 165 | 166 | manage_feed_subscription_request1 = tm10.ManageFeedSubscriptionRequest( 167 | message_id=tm10.generate_message_id(), 168 | feed_name='SomeFeedName', 169 | action=ACT_UNSUBSCRIBE, 170 | subscription_id='SubsId056', 171 | delivery_parameters=delivery_parameters1) 172 | 173 | 174 | Manage Feed Subscription Response 175 | --------------------------------- 176 | 177 | .. autoclass:: ManageFeedSubscriptionResponse 178 | .. autoclass:: SubscriptionInstance 179 | .. autoclass:: PollInstance 180 | 181 | **Example:** 182 | 183 | .. testcode:: 184 | 185 | poll_instance1 = tm10.PollInstance( 186 | poll_protocol=VID_TAXII_HTTP_10, 187 | poll_address='http://example.com/poll', 188 | poll_message_bindings=[VID_TAXII_XML_10]) 189 | 190 | subscription_instance1 = tm10.SubscriptionInstance( 191 | subscription_id='SubsId234', 192 | delivery_parameters=[delivery_parameters1], 193 | poll_instances=[poll_instance1]) 194 | 195 | manage_feed_subscription_response1 = tm10.ManageFeedSubscriptionResponse( 196 | message_id=tm10.generate_message_id(), 197 | in_response_to="12345", 198 | feed_name='Feed001', 199 | message='This is a message', 200 | subscription_instances=[subscription_instance1]) 201 | 202 | 203 | Poll Request 204 | ------------ 205 | 206 | .. autoclass:: PollRequest 207 | 208 | **Example:** 209 | 210 | .. testcode:: 211 | 212 | poll_request1 = tm10.PollRequest( 213 | message_id=tm10.generate_message_id(), 214 | feed_name='TheFeedToPoll', 215 | exclusive_begin_timestamp_label=datetime.datetime.now(tzutc()), 216 | inclusive_end_timestamp_label=datetime.datetime.now(tzutc()), 217 | subscription_id='SubsId002', 218 | content_bindings=[CB_STIX_XML_10]) 219 | 220 | 221 | Poll Response 222 | ------------- 223 | 224 | .. autoclass:: PollResponse 225 | 226 | **Example:** 227 | 228 | .. testcode:: 229 | 230 | poll_response1 = tm10.PollResponse( 231 | message_id=tm10.generate_message_id(), 232 | in_response_to="12345", 233 | feed_name='FeedName', 234 | inclusive_begin_timestamp_label=datetime.datetime.now(tzutc()), 235 | inclusive_end_timestamp_label=datetime.datetime.now(tzutc()), 236 | subscription_id='SubsId001', 237 | message='This is a message.', 238 | content_blocks=[]) 239 | 240 | 241 | Inbox Message 242 | ------------- 243 | 244 | .. autoclass:: InboxMessage 245 | .. autoclass:: SubscriptionInformation 246 | 247 | **Example:** 248 | 249 | .. testcode:: 250 | 251 | cb1 = tm10.ContentBlock(CB_STIX_XML_11, "") 252 | 253 | subscription_information1 = tm10.SubscriptionInformation( 254 | feed_name='SomeFeedName', 255 | subscription_id='SubsId021', 256 | inclusive_begin_timestamp_label=datetime.datetime.now(tzutc()), 257 | inclusive_end_timestamp_label=datetime.datetime.now(tzutc())) 258 | 259 | inbox_message1 = tm10.InboxMessage( 260 | message_id=tm10.generate_message_id(), 261 | message='This is a message.', 262 | subscription_information=subscription_information1, 263 | content_blocks=[cb1]) 264 | 265 | 266 | Other Classes 267 | ------------- 268 | 269 | .. autoclass:: TAXIIMessage 270 | 271 | .. autoclass:: ContentBlock 272 | 273 | **Example:** 274 | 275 | .. testcode:: 276 | 277 | cb1 = tm10.ContentBlock( 278 | content_binding=CB_STIX_XML_10, 279 | content='') 280 | 281 | .. autoclass:: DeliveryParameters 282 | 283 | 284 | Functions 285 | --------- 286 | 287 | .. autofunction:: generate_message_id 288 | .. autofunction:: validate_xml 289 | .. autofunction:: get_message_from_xml 290 | .. autofunction:: get_message_from_dict 291 | .. autofunction:: get_message_from_json 292 | 293 | -------------------------------------------------------------------------------- /docs/api/messages_11.rst: -------------------------------------------------------------------------------- 1 | messages_11 Module 2 | ================== 3 | 4 | .. automodule:: libtaxii.messages_11 5 | 6 | .. note:: 7 | 8 | The examples on this page assume that you have run the equivalent of 9 | 10 | .. code-block:: python 11 | 12 | import datetime 13 | from dateutil.tz import tzutc 14 | import libtaxii as t 15 | import libtaxii.messages_11 as tm11 16 | from libtaxii.constants import * 17 | 18 | .. testsetup:: 19 | 20 | import datetime 21 | from dateutil.tz import tzutc 22 | import libtaxii as t 23 | import libtaxii.messages_11 as tm11 24 | from libtaxii.constants import * 25 | 26 | Status Message 27 | -------------- 28 | 29 | .. autoclass:: StatusMessage 30 | 31 | **Example:** 32 | 33 | .. testcode:: 34 | 35 | sm03 = tm11.StatusMessage( 36 | message_id='SM03', 37 | in_response_to=tm11.generate_message_id(), 38 | status_type=ST_DESTINATION_COLLECTION_ERROR, 39 | status_detail={'ACCEPTABLE_DESTINATION': ['Collection1','Collection2']}) 40 | 41 | 42 | Discovery Request 43 | ----------------- 44 | 45 | .. autoclass:: DiscoveryRequest 46 | 47 | **Example:** 48 | 49 | .. testcode:: 50 | 51 | headers={'ext_header1': 'value1', 'ext_header2': 'value2'} 52 | discovery_request = tm11.DiscoveryRequest( 53 | message_id=tm11.generate_message_id(), 54 | extended_headers=headers) 55 | 56 | 57 | Discovery Response 58 | ------------------ 59 | 60 | .. autoclass:: DiscoveryResponse 61 | .. autoclass:: ServiceInstance 62 | 63 | **Example:** 64 | 65 | .. testcode:: 66 | 67 | discovery_response = tm11.DiscoveryResponse( 68 | message_id=tm11.generate_message_id(), 69 | in_response_to=discovery_request.message_id) 70 | 71 | service_instance = tm11.ServiceInstance( 72 | service_type=SVC_POLL, 73 | services_version=VID_TAXII_SERVICES_11, 74 | protocol_binding=VID_TAXII_HTTP_10, 75 | service_address='http://example.com/poll/', 76 | message_bindings=[VID_TAXII_XML_11], 77 | available=True, 78 | message='This is a message.', 79 | #supported_query=[tdq1], 80 | ) 81 | 82 | discovery_response.service_instances.append(service_instance) 83 | 84 | # Alternatively, you could define the service instance(s) first and use the 85 | # following: 86 | 87 | service_instance_list = [service_instance] 88 | discovery_response = tm11.DiscoveryResponse( 89 | message_id=tm11.generate_message_id(), 90 | in_response_to=discovery_request.message_id, 91 | service_instances=service_instance_list) 92 | 93 | 94 | Collection Information Request 95 | ------------------------------ 96 | 97 | .. autoclass:: CollectionInformationRequest 98 | 99 | **Example:** 100 | 101 | .. testcode:: 102 | 103 | ext_headers = {'name1': 'val1', 'name2': 'val2'} 104 | collection_information_request = tm11.CollectionInformationRequest( 105 | message_id='CIReq01', 106 | extended_headers=ext_headers) 107 | 108 | 109 | Collection Information Response 110 | ------------------------------- 111 | 112 | .. autoclass:: CollectionInformationResponse 113 | .. autoclass:: CollectionInformation 114 | .. autoclass:: PushMethod 115 | .. autoclass:: PollingServiceInstance 116 | .. autoclass:: SubscriptionMethod 117 | .. autoclass:: ReceivingInboxService 118 | 119 | **Example:** 120 | 121 | .. testcode:: 122 | 123 | push_method1 = tm11.PushMethod( 124 | push_protocol=VID_TAXII_HTTP_10, 125 | push_message_bindings=[VID_TAXII_XML_11]) 126 | 127 | poll_service1 = tm11.PollingServiceInstance( 128 | poll_protocol=VID_TAXII_HTTPS_10, 129 | poll_address='https://example.com/PollService1', 130 | poll_message_bindings=[VID_TAXII_XML_11]) 131 | 132 | poll_service2 = tm11.PollingServiceInstance( 133 | poll_protocol=VID_TAXII_HTTPS_10, 134 | poll_address='https://example.com/PollService2', 135 | poll_message_bindings=[VID_TAXII_XML_11]) 136 | 137 | subs_method1 = tm11.SubscriptionMethod( 138 | subscription_protocol=VID_TAXII_HTTPS_10, 139 | subscription_address='https://example.com/SubscriptionService', 140 | subscription_message_bindings=[VID_TAXII_XML_11]) 141 | 142 | inbox_service1 = tm11.ReceivingInboxService( 143 | inbox_protocol=VID_TAXII_HTTPS_10, 144 | inbox_address='https://example.com/InboxService', 145 | inbox_message_bindings=[VID_TAXII_XML_11], 146 | supported_contents=None) 147 | 148 | collection1 = tm11.CollectionInformation( 149 | collection_name='collection1', 150 | collection_description='This is a collection', 151 | supported_contents=[tm11.ContentBinding(CB_STIX_XML_101)], 152 | available=False, 153 | push_methods=[push_method1], 154 | polling_service_instances=[poll_service1, poll_service2], 155 | subscription_methods=[subs_method1], 156 | collection_volume=4, 157 | collection_type=CT_DATA_FEED, 158 | receiving_inbox_services=[inbox_service1]) 159 | 160 | collection_response1 = tm11.CollectionInformationResponse( 161 | message_id='CIR01', 162 | in_response_to='0', 163 | collection_informations=[collection1]) 164 | 165 | 166 | Manage Collection Subscription Request 167 | -------------------------------------- 168 | 169 | .. autoclass:: ManageCollectionSubscriptionRequest 170 | 171 | **Example:** 172 | 173 | .. testcode:: 174 | 175 | subscription_parameters1 = tm11.SubscriptionParameters() 176 | push_parameters1 = tm11.PushParameters("", "", "") 177 | 178 | subs_req1 = tm11.ManageCollectionSubscriptionRequest( 179 | message_id='SubsReq01', 180 | action=ACT_SUBSCRIBE, 181 | collection_name='collection1', 182 | subscription_parameters=subscription_parameters1, 183 | push_parameters=push_parameters1) 184 | 185 | 186 | Manage Collection Subscription Response 187 | --------------------------------------- 188 | 189 | .. autoclass:: ManageCollectionSubscriptionResponse 190 | .. autoclass:: SubscriptionInstance 191 | .. autoclass:: PollInstance 192 | 193 | **Example:** 194 | 195 | .. testcode:: 196 | 197 | subscription_parameters1 = tm11.SubscriptionParameters() 198 | push_parameters1 = tm11.PushParameters("", "", "") 199 | 200 | 201 | poll_instance1 = tm11.PollInstance( 202 | poll_protocol=VID_TAXII_HTTPS_10, 203 | poll_address='https://example.com/poll1/', 204 | poll_message_bindings=[VID_TAXII_XML_11]) 205 | 206 | subs1 = tm11.SubscriptionInstance( 207 | subscription_id='Subs001', 208 | status=SS_ACTIVE, 209 | subscription_parameters=subscription_parameters1, 210 | push_parameters=push_parameters1, 211 | poll_instances=[poll_instance1]) 212 | 213 | subs_resp1 = tm11.ManageCollectionSubscriptionResponse( 214 | message_id='SubsResp01', 215 | in_response_to='xyz', 216 | collection_name='abc123', 217 | message='Hullo!', 218 | subscription_instances=[subs1]) 219 | 220 | 221 | Poll Request 222 | ------------ 223 | 224 | .. autoclass:: PollRequest 225 | .. autoclass:: PollParameters 226 | 227 | **Example:** 228 | 229 | .. testcode:: 230 | 231 | delivery_parameters1 = tm11.DeliveryParameters( 232 | inbox_protocol=VID_TAXII_HTTPS_10, 233 | inbox_address='https://example.com/inboxAddress/', 234 | delivery_message_binding=VID_TAXII_XML_11) 235 | 236 | poll_params1 = tm11.PollParameters( 237 | allow_asynch=False, 238 | response_type=RT_COUNT_ONLY, 239 | content_bindings=[tm11.ContentBinding(binding_id=CB_STIX_XML_11)], 240 | #query=query1, 241 | delivery_parameters=delivery_parameters1) 242 | 243 | poll_req3 = tm11.PollRequest( 244 | message_id='PollReq03', 245 | collection_name='collection100', 246 | exclusive_begin_timestamp_label=datetime.datetime.now(tzutc()), 247 | inclusive_end_timestamp_label=datetime.datetime.now(tzutc()), 248 | poll_parameters=poll_params1) 249 | 250 | 251 | Poll Response 252 | ------------- 253 | 254 | .. autoclass:: PollResponse 255 | 256 | **Example:** 257 | 258 | .. testcode:: 259 | 260 | cb1 = tm11.ContentBlock(CB_STIX_XML_11, "") 261 | cb2 = tm11.ContentBlock(CB_STIX_XML_11, "") 262 | 263 | count = tm11.RecordCount(record_count=22, partial_count=False) 264 | 265 | poll_resp1 = tm11.PollResponse( 266 | message_id='PollResp1', 267 | in_response_to='tmp', 268 | collection_name='blah', 269 | exclusive_begin_timestamp_label=datetime.datetime.now(tzutc()), 270 | inclusive_end_timestamp_label=datetime.datetime.now(tzutc()), 271 | subscription_id='24', 272 | message='This is a test message', 273 | content_blocks=[cb1, cb2], 274 | more=True, 275 | result_id='123', 276 | result_part_number=1, 277 | record_count=count) 278 | 279 | 280 | Inbox Message 281 | ------------- 282 | 283 | .. autoclass:: InboxMessage 284 | .. autoclass:: SubscriptionInformation 285 | 286 | **Example:** 287 | 288 | .. testcode:: 289 | 290 | cb1 = tm11.ContentBlock(CB_STIX_XML_11, "") 291 | cb2 = tm11.ContentBlock(CB_STIX_XML_11, "") 292 | 293 | subs_info1 = tm11.SubscriptionInformation( 294 | collection_name='SomeCollectionName', 295 | subscription_id='SubsId021', 296 | exclusive_begin_timestamp_label=datetime.datetime.now(tzutc()), 297 | inclusive_end_timestamp_label=datetime.datetime.now(tzutc())) 298 | 299 | inbox1 = tm11.InboxMessage( 300 | message_id='Inbox1', 301 | result_id='123', 302 | destination_collection_names=['collection1','collection2'], 303 | message='Hello!', 304 | subscription_information=subs_info1, 305 | record_count=tm11.RecordCount(22, partial_count=True), 306 | content_blocks=[cb1, cb2]) 307 | 308 | 309 | Poll Fulfillment Request 310 | ------------------------ 311 | 312 | .. autoclass:: PollFulfillmentRequest 313 | 314 | **Example:** 315 | 316 | .. testcode:: 317 | 318 | pf1 = tm11.PollFulfillmentRequest( 319 | message_id='pf1', 320 | collection_name='1-800-collection', 321 | result_id='123', 322 | result_part_number=1) 323 | 324 | 325 | Other Classes 326 | ------------- 327 | 328 | .. autoclass:: TAXIIMessage 329 | 330 | .. autoclass:: ContentBinding 331 | .. autoclass:: ContentBlock 332 | 333 | **Example:** 334 | 335 | .. testcode:: 336 | 337 | cb001 = tm11.ContentBlock( 338 | content_binding=tm11.ContentBinding(CB_STIX_XML_11), 339 | content='', 340 | timestamp_label=datetime.datetime.now(tzutc()), 341 | message='Hullo!', 342 | padding='The quick brown fox jumped over the lazy dogs.') 343 | 344 | .. autoclass:: DeliveryParameters 345 | .. autoclass:: PushParameters 346 | .. autoclass:: RecordCount 347 | .. autoclass:: SubscriptionParameters 348 | 349 | 350 | Functions 351 | --------- 352 | 353 | .. autofunction:: generate_message_id 354 | .. autofunction:: validate_xml 355 | .. autofunction:: get_message_from_xml 356 | .. autofunction:: get_message_from_dict 357 | .. autofunction:: get_message_from_json 358 | -------------------------------------------------------------------------------- /docs/api/modules.rst: -------------------------------------------------------------------------------- 1 | .. _apidoc: 2 | 3 | API Documentation 4 | ----------------- 5 | 6 | .. toctree:: 7 | 8 | libtaxii 9 | common 10 | constants 11 | clients 12 | messages_10 13 | messages_11 14 | query 15 | validation 16 | -------------------------------------------------------------------------------- /docs/api/query.rst: -------------------------------------------------------------------------------- 1 | taxii_default_query Module 2 | ========================== 3 | 4 | .. automodule:: libtaxii.taxii_default_query 5 | 6 | Classes 7 | ------- 8 | 9 | Default Query 10 | ************* 11 | 12 | .. autoclass:: DefaultQuery 13 | :show-inheritance: 14 | :members: 15 | 16 | **Example** 17 | 18 | .. code-block:: python 19 | 20 | import libtaxii.taxii_default_query as tdq 21 | from libtaxii.taxii_default_query import Test 22 | from libtaxii.taxii_default_query import Criterion 23 | from libtaxii.taxii_default_query import Criteria 24 | from libtaxii.constants import * 25 | import datetime 26 | 27 | ############################################################################## 28 | # A Taxii Default Query *Test* gives the consumer granular control over the 29 | # Target of a Query by applying unambiguos relationship requirements specified 30 | # using a standardized vocabulary. 31 | 32 | # Each Relationship (e.g. equals, matches, greater_than, etc.) in a Capability 33 | # Module defines a set of paramater fields, capable of expressing that 34 | # relation. 35 | 36 | # The *equals* relationship, of the Core Capability Module, returns True if 37 | # the target matches the value exactly. If the target merely contains the 38 | # value (but does not match exactly) the relationship Test returns False. 39 | test_equals = Test(capability_id=CM_CORE, 40 | relationship='equals', 41 | parameters={'value': 'Test value', 42 | 'match_type': 'case_sensitive_string'}) 43 | 44 | # The *matches* relationship, in the context of the Regular Expression 45 | # Capability Module, returns true if the target matches the regular expression 46 | # contained in the value. 47 | test_matches = Test(capability_id=CM_REGEX, 48 | relationship='matches', 49 | parameters={'value': '[A-Z]*', 50 | 'case_sensitive': True}) 51 | 52 | # The *greater than* relationship, in the context of the Timestamp Capability 53 | # Module returns True if the target's timestamp indicates a later time than 54 | # that specified by this value. This relationship is only valid for timestamp 55 | # comparisons. 56 | test_timestamp = Test(capability_id=CM_TIMESTAMP, 57 | relationship='greater_than', 58 | parameters={'value': datetime.datetime.now()}) 59 | 60 | 61 | ############################################################################## 62 | # A *Criterion* specifies how a Target is evaluated against a Test. Within a 63 | # Criterion, the Target is used to identify a specific region of a record to 64 | # which the Test should be applied. Slash Notation Targeting Expression syntax, 65 | # in conjunction with a Targeting Expression Vocabulary, are used to form a 66 | # Targeting Expression 67 | 68 | # A Multi-field Wildcard (**). This indicates any Node or series of Nodes, 69 | # specified by double asterisks. 70 | criterion1 = Criterion(target='**', 71 | test=test_equals) 72 | 73 | # Indicates that *id* fields in the STIX Indicator construct are in scope 74 | criterion2 = Criterion(target='STIX_Package/Indicators/Indicator/@id', 75 | test=test_matches) 76 | 77 | # Indicates that all STIX Description fields are in scope 78 | criterion3 = Criterion(target='**/Description', 79 | test=test_timestamp) 80 | 81 | 82 | ############################################################################## 83 | # *Criteria* consist of a logical operator (and/or) that should be applied to 84 | # child Criteria and Criterion to determine whether content matches this query. 85 | 86 | criteria1 = Criteria(operator=OP_AND, 87 | criterion=[criterion1]) 88 | 89 | criteria2 = Criteria(operator=OP_OR, 90 | criterion=[criterion1, criterion2, criterion3]) 91 | 92 | criteria3 = Criteria(operator=OP_AND, 93 | criterion=[criterion1, criterion3], 94 | criteria=[criteria2]) 95 | 96 | 97 | ############################################################################## 98 | # query1, query2 and query3 would be able to be used in TAXII requests that 99 | # contain queries (e.g., PollRequest Messages) 100 | query1 = tdq.DefaultQuery(targeting_expression_id=CB_STIX_XML_111, 101 | criteria=criteria1) 102 | 103 | query2 = tdq.DefaultQuery(targeting_expression_id=CB_STIX_XML_111, 104 | criteria=criteria3) 105 | 106 | query3 = tdq.DefaultQuery(targeting_expression_id=CB_STIX_XML_111, 107 | criteria=criteria2) 108 | 109 | 110 | Default Query Info 111 | ****************** 112 | 113 | .. autoclass:: DefaultQueryInfo 114 | :show-inheritance: 115 | :members: 116 | 117 | **Example** 118 | 119 | .. code-block:: python 120 | 121 | import libtaxii.taxii_default_query as tdq 122 | from libtaxii.taxii_default_query import TargetingExpressionInfo 123 | from libtaxii.constants import * 124 | 125 | ############################################################################## 126 | # *TargetingExpressionInfo* describes which expressions are available to 127 | # a consumer when submitting a query to a taxii service. A 128 | # `targetting_expression_id` indicates a suppoted targetting vocabulary 129 | # TargetingExpressionInfo also contains the permissible scope of queries. 130 | 131 | # This example has no preferred scope, and allows any scope 132 | tei_01 = TargetingExpressionInfo( 133 | targeting_expression_id=CB_STIX_XML_111, 134 | preferred_scope=[], 135 | allowed_scope=['**']) 136 | 137 | # This example prefers the Indicator scope and allows no other scope 138 | tei_02 = TargetingExpressionInfo( 139 | targeting_expression_id=CB_STIX_XML_111, 140 | preferred_scope=['STIX_Package/Indicators/Indicator/**'], 141 | allowed_scope=[]) 142 | 143 | 144 | ############################################################################## 145 | # *DefaultQueryInfo* describes the TAXII Default Queries that are supported 146 | # using a list of TargetExpressionInfo objects, and a list of capability 147 | # module identifiers. 148 | tdqi1 = tdq.DefaultQueryInfo( 149 | targeting_expression_infos=[tei_01, tei_02], 150 | capability_modules=[CM_CORE]) 151 | -------------------------------------------------------------------------------- /docs/api/validation.rst: -------------------------------------------------------------------------------- 1 | validation Module 2 | ================= 3 | 4 | .. _apivalidation: 5 | 6 | .. currentmodule:: libtaxii.validation 7 | 8 | Validate TAXII Content 9 | ---------------------- 10 | 11 | .. autoclass:: SchemaValidator 12 | :members: 13 | :exclude-members: TAXII_10_SCHEMA, TAXII_11_SCHEMA 14 | .. autodata:: TAXII_10_SCHEMA 15 | :annotation: Use TAXII 1.0 schema for validation. 16 | .. autodata:: TAXII_11_SCHEMA 17 | :annotation: Use TAXII 1.1 schema for validation. 18 | .. autoclass:: TAXII10Validator 19 | :show-inheritance: 20 | .. autoclass:: TAXII11Validator 21 | :show-inheritance: 22 | 23 | -------------------------------------------------------------------------------- /docs/changes.rst: -------------------------------------------------------------------------------- 1 | Release Notes 2 | ============= 3 | 4 | 1.1.119 (2021-04-15) 5 | -------------------- 6 | `(diff) `__ 7 | 8 | - #248 Writing content blocks should not be dependent on value of "more" (@brlogan) 9 | 10 | 11 | 1.1.118 (2020-10-19) 12 | -------------------- 13 | `(diff) `__ 14 | 15 | - #247 [CVE-2020-27197] Avoid SSRF on parsing XML (@orsinium) 16 | 17 | 18 | 1.1.117 (2020-05-26) 19 | -------------------- 20 | `(diff) `__ 21 | 22 | - #244 SSL Verify Server not working correctly (@motok) (@nschwane) 23 | - #245 Unicode `lxml.etree.SerialisationError` on lxml 4.5.0+ (@advptr) 24 | 25 | 26 | 1.1.116 (2020-02-14) 27 | -------------------- 28 | `(diff) `__ 29 | 30 | - #240 PY3 Compatibility changes for HTTP Response Body (@nschwane) 31 | 32 | 33 | 1.1.115 (2019-11-12) 34 | -------------------- 35 | `(diff) `__ 36 | 37 | - #239 Convert the HTTP response body to a string type (PY3 this will be bytes) (@sddj) 38 | 39 | 40 | 1.1.114 (2019-07-26) 41 | -------------------- 42 | `(diff) `__ 43 | 44 | - #237 Support converting dicts to content bindings (@danielsamuels) 45 | - #238 Provide XMLParser copies instead of reusing the cached instance. Prevents future messages to lose namespace 46 | 47 | 48 | 1.1.113 (2019-04-11) 49 | -------------------- 50 | `(diff) `__ 51 | 52 | - #234 Add ability to load a configuration file when executing a script 53 | - #232 Fix TLS handshake failure when a server requires SNI (@marcelslotema) 54 | 55 | 56 | 1.1.112 (2018-11-27) 57 | -------------------- 58 | `(diff) `__ 59 | 60 | - #227 Fixes to poll_client script (Python3 compatibility) 61 | - #226 Clean-up documentation warnings 62 | - #228 Fix 'HTTPMessage' has no attribute 'getheader' (Python3 compatibility) 63 | - #225 Fix checks that involve xpath (lxml) to prevent FutureWarning message 64 | - #230 Fix parsing status message round-trip (@danielsamuels) 65 | 66 | 67 | 1.1.111 (2017-06-07) 68 | -------------------- 69 | `(diff) `__ 70 | 71 | - Fix #222, #224 - Update clients.py to work with Python 2.6, 3.3, 3.5, and 3.6. 72 | - Fix #221 - Add Python 3.6 support. 73 | - Fix #219 - Handle Unicode- and byte-strings consistently. 74 | - Fix #214 - Add timeout parameter to call_taxii_service2 (@mbekavac) 75 | - Fix #192 - Add support for STIX 1.2. 76 | - Add user_agent parameter to call_taxii_service2 (@kralka) 77 | 78 | 79 | 1.1.110 (2016-09-08) 80 | -------------------- 81 | `(diff) `__ 82 | 83 | - Fix #210 - Use hailataxii.com in examples instead of taxiitest.mitre.org (@clenk) 84 | - Fix #183 - Update incorrect comment (@daybarr) 85 | - Fix SMIME Content Binding ID typo (@brlogan) 86 | 87 | 88 | 1.1.109 (2015-11-16) 89 | -------------------- 90 | `(diff) `__ 91 | 92 | - Fix #203 - Fix SSL context on older Python 2.7 versions (@traut) 93 | 94 | 95 | 1.1.108 (2015-10-29) 96 | -------------------- 97 | `(diff) `__ 98 | 99 | - Support password-protected SSL keys (@traut) 100 | - Fix #200 - Bad encodings no longer generate Exceptions (@MarkDavidson) 101 | 102 | 103 | 1.1.107 (2015-08-05) 104 | -------------------- 105 | `(diff) `__ 106 | 107 | - Fix #184 - Use proxy for both HTTP and HTTPS (@nadavc) 108 | - Fix #187 - Handle numeric values in taxii_default_query (@stkyle) 109 | - Update Example Query documentation (@stkyle) 110 | - Fix #189 - Update how constants are used and referenced (@stkyle) 111 | - Show HTTP error code in StatusMessage.message (@ahippo) 112 | - Python 3 compatibility (@rjprins) 113 | 114 | 115 | 1.1.106 116 | ------- 117 | `(diff) `__ 118 | 119 | - Thank you to the multiple contributors for this release: @traut, @gtback, @wbolster, and @MarkDavidson, and thank you to those who filed issues that were fixed in this release. 120 | - Timestamp labels can now be provided as a string or as a python datetime object. Previously, only datetime objects were permitted. 121 | - Some big changes to TAXII Client command line args. Deprecated URL components (e.g., --host, --port) in favor of specifying a single url (--url) 122 | - Added a TAXII Inbox 1.0 client 123 | - Decreased the likelihood of future message ID collisions 124 | - A variety of improvements in the following areas: data validation, typos, documentation, DRY-ness, overall repo quality (thanks @gtback) 125 | - Multiple code cleanup changes (Thanks in part to @traut of IntelWorks) 126 | 127 | 128 | 1.1.105 129 | ------- 130 | `(diff) `__ 131 | 132 | - Fixed multiple XML parsing related vulnerabilities (Thanks @guidovranken of IntelWorks for the vulnerability research!) 133 | 134 | 135 | 1.1.104 136 | ------- 137 | `(diff) `__ 138 | 139 | - Fixed a bug where libtaxii did not properly handle XML values for Extended Headers 140 | - Added checking for required status detail keys in Status Messages 141 | - Improved data validation in various places, fixed various bugs, and improved documentation 142 | - Improved filename generation in scripts (Thanks @guidovranken!) 143 | 144 | 145 | 1.1.103 146 | ------- 147 | `(diff) `__ 148 | 149 | In terms of code organization, there are a few big changes beginning to 150 | take place in this version of libtaxii. Constants and commonly used classes/functions 151 | are being moved to common locations (libtaxii.constants and libtaxii.common, respectively). 152 | Also, nested classes (e.g., messages_11.DiscoveryResponse.ServiceInstance) have been de-nested 153 | (e.g., is now messages_11.ServiceInstance). All of these changes are intended to make 154 | using libtaxii easier. For the time being, backward compatibility has been maintained, but 155 | many of these changes may result in a backward compatibility breaking change in a future, 156 | major release of libtaxii. 157 | 158 | Major changes: 159 | 160 | - libtaxii.constants, a new source file, was created. The definition for all constants in libtaxii have been moved to libtaxii.constants. Aliases to the previous definition locations have been retained for backward compatibility and may be removed in a future major release. 161 | - libtaxii.common, a new source file for containing classes and methods common to TAXII, was created. Some common classes and functions have been moved into libtaxii.common, and more will be moved over time. Aliases to the previous classes and functions have been retained for backward compatibility and may be removed in a future major release. (Thanks, @gtback!) 162 | - Promoted nested classes to module-level classes in messages_10, messages_11, and taxii_default_query. Aliases to the previous, nested, classes have been retained for backward compatibility and may be removed in a future major release. (Thanks, @gtback!) 163 | - A ‘to_text()’ method has been added to all TAXII Message classes. ‘to_text()’ provides a “nicely formatted” human readable representation of a TAXII Message and its components. The ‘to_text()’ method was added to support libtaxii’s scripts. There is no ‘from_text()’ method as this is not intended to be a serialization/deserialization feature, but a readability feature. 164 | - Lowered the required version of lxml to 2.2.3, the latest available on RHEL 6. (Thanks to @mblayman for raising this) 165 | - Lowered the required version of python-dateutil to 1.4.1, the latest available on RHEL 6. (Thanks to @mblayman for raising this) 166 | - TAXII 1.1 StatusMessages now raise a ValueError when required a Status Detail is not set. 167 | - TAXII XML Validation has a new methodology: See validation.SchemaValidator (http://libtaxii.readthedocs.org/en/latest/api/validation.html#libtaxii.validation.SchemaValidator) 168 | - Related: validate_xml(…) has been deprecated and may be removed in a future major release. 169 | 170 | Minor changes: 171 | 172 | - Tons of PEP8 improvements (Thanks, @gtback!) 173 | - TAXII Scripts have been entirely reorganized to be more DRY. 174 | - Added two constants for Proxy Settings (SYSTEM_PROXY and NO_PROXY). These supersede the need to use either None or ‘noproxy’, which were not as clear to developers. 175 | - Improved documentation, Tox usage, and Travis-CI usage. (Thanks, @gtback!) 176 | - SMIME Content Binding added (application/x-pks7-mime) 177 | - For Python 2.6, argparse is now a requirement 178 | - Added constants for TAXII Default Query Parameters and Relationships 179 | 180 | Bug fixes: 181 | 182 | - In messages_11.PollResponse, the result_part_number parameter is now set by the constructor. 183 | 184 | 185 | 1.1.102 186 | ------- 187 | `(diff) `__ 188 | 189 | The biggest change was the addition of scripts to libtaxii. Now when you install libtaxii, you get 190 | a number of scripts that are by default configured to hit the TAXII Test server (taxiitest.mitre.org). 191 | You can specify a number of parameters on the command line to change where/how the scripts connect. 192 | The scripts are: 193 | 194 | - discovery_client - Calls a TAXII 1.1 Discovery Service 195 | - fulfillment_client - Calls a TAXII 1.1 Poll Service for Poll Fulfillment 196 | - inbox_client - Calls a TAXII 1.1 Inbox Service. Comes packaged with a STIX document to use by default. 197 | - poll_client - Calls a TAXII 1.1 Poll Service 198 | - poll_client_10 - Calls a TAXII 1.0 Poll Service (Note: Does not work with taxiitest.mitre.org, as taxiitest.mitre.org is TAXII 1.1 only) 199 | - query_client - Calls a TAXII 1.1 Poll Service with a query on IP or File Hash (Note: As of 6/11/2014; Works with the master branch of YETI, and will work with YETI after the next release of YETI) 200 | 201 | We also had a number of bug fixes and impprovements for this version of libtaxii: 202 | 203 | - Unicode strings work round trip (Hat tip to Ben Yates for reporting the issue) 204 | - Added TONS of documentation (http://libtaxii.readthedocs.org/en/latest/index.html). Big thanks to @gtback and @slsnow! 205 | - Fixed some issues in ContentBlock.content where certain data was not serializing/deserializing properly 206 | - Streamlined serialization of XML documents to avoid a double-parse in certain cases 207 | - Added a Content Binding ID for STIX XML 1.1.1 208 | - Added an optional pretty_print argument to all to_xml() functions. e.g., to_xml(pretty_print=True) 209 | - Added the three TAXII Default Query Status Type to libtaxii.taxii_default_query 210 | - Fixed a bug where custom Status Types were prohibited 211 | - Added Travis CI 212 | 213 | 214 | 1.1.101 215 | ------- 216 | 217 | `(diff) `__ 218 | 219 | Lots of changes in this release, including some important bug fixes. 220 | 221 | - The equals method for all TAXII Messages was fixed (previous it would 222 | incorrectly return True in many cases). 223 | - Fixed various serialization/deserialization issues uncovered by the now 224 | correctly implemented equals methods. 225 | - Added a defined Content-Type for TAXII XML 1.1. 226 | - Corrected the value of ST\_UNSUPPORTED\_PROTOCOL. 227 | - Fixed a bug when parsing non-TAXII responses. 228 | - Fixed a bug where the Subscription ID was not allowed to be none in 229 | ManageFeedSubscriptionRequest (The Subscription ID must be None for 230 | subscription requests with an action of SUBSCRIBE). 231 | - Fixed a bug where DeliveryParameters were not permitted to be None in a 232 | ManageFeedSubscriptionRequest. 233 | - Added code to permit the setting of certain HTTP Headers (Accept, 234 | X-TAXII-Accept). 235 | - Improved libtaxii's handling of non-XML content that looks like XML 236 | - Added Constants for TAXII Headers (and updated the code to use them). 237 | - Improved handling of non-registered Query formats (now an exception is 238 | raised; previously None was returned). 239 | - libtaxii now provides an X-TAXII-Services header. 240 | 241 | 242 | 1.1.100 243 | ------- 244 | 245 | `(diff) `__ 246 | 247 | *This version contains known bugs. Use a more recent version of libtaxii 248 | when possible.* 249 | 250 | - First release that supports TAXII 1.1. 251 | - No changes to TAXII 1.0 code. 252 | - Added documentation for Messages 1.1 API and TAXII Default Query. 253 | 254 | 255 | 1.0.107 256 | ------- 257 | 258 | `(diff) `__ 259 | 260 | - Fixed an issue that was causing invalid TAXII XML to be generated 261 | (Thanks [@JamesNK](https://github.com/JamesNK)). 262 | - Fixed an issue in the messages test suite that caused the invalid XML 263 | to not be caught. 264 | 265 | 266 | 1.0.106 267 | ------- 268 | 269 | `(diff) `__ 270 | 271 | - Added validation to messages.py. This should not cause any backwards 272 | compatibility issues, but there may be things we didn't catch. Please 273 | report any instances of this via the issue tracker. 274 | - Modified the internals of ``from_dict()`` and ``from_xml()`` in many 275 | cases to support how validation now works. 276 | - Added constructor arguments to HttpClient. Default behavior is still 277 | the same. 278 | - Added the ability to specify whether or not an HTTP Server's SSL 279 | Certificate should be verified. 280 | - Prettified some of the documentation. 281 | - Added documentation in certain places where there was none previously. 282 | 283 | 284 | 1.0.105 285 | ------- 286 | 287 | `(diff) `__ 288 | 289 | - Added support for JSON (Thanks to [@ics](https://github.com/ics), 290 | Alex Ciobanu of CERT EU). 291 | - callTaxiiService2 now supports user-specified content\_types (Thanks 292 | to Alex Ciobanu of CERT EU). 293 | - Fixed `Issue #18 `__, 294 | libtaxii.messages now permits users to specify any lxml parser for 295 | parsing XML. A default parser is used when one is not specified, 296 | which is unchanged from previous usage. 297 | 298 | 299 | 1.0.104 300 | ------- 301 | 302 | `(diff) `__ 303 | 304 | - Many of the comments were aligned with PEP8 guidelines (thanks 305 | [@gtback](https://github.com/gtback)!) 306 | - Added a new authentication mechanism (AUTH\_CERT\_BASIC) to 307 | clients.py. This authentication mechanism supports Certificate 308 | Authentication plus HTTP Basic authentication. 309 | - Added clients.HttpClient.callTaxiiService2, which supersedes 310 | callTaxiiService. The previous version of callTaxiiService couldn't 311 | handle proxies well, which now have better support. 312 | - Added better proxy support to client.HttpClient via the setProxy() 313 | function. 314 | 315 | 316 | 1.0.103 317 | ------- 318 | 319 | `(diff) `__ 320 | 321 | This version fixes a schema validation bug. Schema validation did not work 322 | prior to this version. 323 | 324 | 325 | 1.0.102 326 | ------- 327 | 328 | `(diff) `__ 329 | 330 | This version adds better proxy support to libtaxii in libtaxii.clients. A 331 | function to set a proxy (setProxy) was added as well as a new callTaxiiService2 332 | function that can properly use proxies. The original callTaxiiService function 333 | did not support proxies well. The APIs have the full documentation for 334 | callTaxiiService, callTaxiiService2, and setProxy (`Client API 335 | `__). 336 | 337 | 338 | 1.0.101 339 | ------- 340 | 341 | `(diff) `__ 342 | 343 | This version added missing source files for distribution on PyPI. No 344 | functionality changes were made. 345 | 346 | 347 | 1.0.100 348 | ------- 349 | 350 | `(diff) `__ 351 | 352 | Version 1.0.100 represents the first TAXII 1.0 compliant version of libtaxii. 353 | This version removes all code not compliant with TAXII 1.0. 354 | 355 | 356 | 1.0.090 357 | ------- 358 | 359 | `(diff) `__ 360 | 361 | This version of libtaxii has components that are TAXII 1.0 conformant and 362 | experimental functionality that conforms to a draft version of TAXII. This 363 | version should only be used to transition from 1.0.000draft to 1.0.100. 364 | 365 | 366 | 1.0.000draft 367 | ------------ 368 | 369 | This version of libtaxii represents experimental functionality that conforms to 370 | a draft version of TAXII. This code should no longer be used. For those using 371 | this code, you should upgrade to 1.0.090 and migrate your code to use the TAXII 372 | 1.0 components, then transition to 1.0.100. 373 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, The MITRE Corporation 2 | # For license information, see the LICENSE.txt file 3 | 4 | import os 5 | 6 | import libtaxii 7 | 8 | project = u'libtaxii' 9 | copyright = u'2017, The MITRE Corporation' 10 | version = libtaxii.__version__ 11 | release = version 12 | 13 | extensions = [ 14 | 'sphinx.ext.autodoc', 15 | 'sphinx.ext.doctest', 16 | 'sphinx.ext.ifconfig', 17 | 'sphinx.ext.intersphinx', 18 | 'sphinx.ext.viewcode', 19 | 'sphinx.ext.napoleon', 20 | ] 21 | 22 | intersphinx_mapping = { 23 | 'python': ('https://docs.python.org/', None), 24 | } 25 | 26 | templates_path = ['_templates'] 27 | source_suffix = '.rst' 28 | master_doc = 'index' 29 | 30 | rst_prolog = """ 31 | **Version**: {0} 32 | """.format(release) 33 | 34 | exclude_patterns = [ 35 | '_build', 36 | ] 37 | 38 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 39 | if not on_rtd: 40 | import sphinx_rtd_theme 41 | html_theme = 'sphinx_rtd_theme' 42 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 43 | else: 44 | html_theme = 'default' 45 | 46 | latex_elements = {} 47 | latex_documents = [ 48 | ('index', 'libtaxii.tex', u'libtaxii Documentation', 49 | u'The MITRE Corporation', 'manual'), 50 | ] 51 | -------------------------------------------------------------------------------- /docs/getting_started.rst: -------------------------------------------------------------------------------- 1 | Getting Started 2 | =============== 3 | 4 | This page gives an introduction to **libtaxii** and how to use it. Please note 5 | that this page is being actively worked on and feedback is welcome. 6 | 7 | 8 | Modules 9 | ------- 10 | 11 | The libtaxii library contains the following modules: 12 | 13 | * **libtaxii** - Contains version info and some methods for getting TAXII Messages 14 | from HTTP responses. (Implemented in ``libtaxii/__init__.py``) 15 | * **libtaxii.clients.** - TAXII HTTP and HTTPS clients. (Implemented in 16 | ``libtaxii/clients.py``) 17 | * **libtaxii.common** - Contains functions and classes useful for all versions of TAXII 18 | * **libtaxii.constants** - Contains constants for TAXII 19 | * **libtaxii.messages_10** - Creating, handling, and parsing TAXII 1.0 20 | messages. (Implemented in ``libtaxii/messages_10.py``) 21 | * **libtaxii.messages_11** - Creating, handling, and parsing TAXII 1.1 22 | messages. (Implemented in ``libtaxii/messages_11.py``) 23 | * **libtaxii.taxii_default_query** - Creating, handling and parsing TAXII 24 | Default Queries. (Implemented in ``libtaxii/taxii_default_query.py``) *New in 25 | libtaxii 1.1.100*. 26 | * **libtaxii.validation** - Common data validation functions used across 27 | libtaxii. (Implemented in ``libtaxii/validation.py``) 28 | 29 | TAXII Messages Module Structure 30 | ------------------------------- 31 | 32 | In the TAXII message modules (:py:mod:`libtaxii.messages_10` and 33 | :py:mod:`libtaxii.messages_11`), there is a class corresponding to each type of 34 | TAXII message. For example, there is a ``DiscoveryRequest`` class for the 35 | Discovery Request message: 36 | 37 | .. code:: python 38 | 39 | import libtaxii.messages_11 as tm11 40 | discovery_request = tm11.DiscoveryRequest( ... ) 41 | 42 | For types that can been used across multiple messages (e.g., a Content Block 43 | can exist in both Poll Response and Inbox Message), the corresponding class 44 | (``ContentBlock``) is (and always has always been) defined at the module level. 45 | 46 | .. code:: python 47 | 48 | content_block = tm11.ContentBlock( ... ) 49 | 50 | Other types that are used exclusively within a particular TAXII message type 51 | were previously defined as nested classes on the corresponding message class; 52 | however, they are now defined at the top level of the module. For example, a 53 | Service Instance is only used in a Discovery Response message, so the class 54 | representing a Service Instance, now just ``ServiceInstance``, was previously 55 | ``DiscoveryResponse.ServiceInstance``. The latter name still works for backward 56 | compatibilty reasons, but is deprecated and may be removed in the future. 57 | 58 | .. code:: python 59 | 60 | service_instance = tm11.ServiceInstance( ... ) 61 | service_instance = tm11.DiscoveryRequest.ServiceInstance( ... ) 62 | 63 | See the :ref:`API Documentation ` for proper constructor arguments for 64 | each type above. 65 | 66 | 67 | TAXII Message Serialization and Deserialization 68 | ----------------------------------------------- 69 | 70 | Each class in the message modules has serialization and deserialization methods 71 | for XML Strings, Python dictionaries, and LXML ElementTrees. All serialization 72 | methods (``to_*()``) are instance methods called on specific objects (e.g., 73 | ``discovery_request.to_xml()``). Deserialization methods (``from_*()``) are 74 | class methods and should be called on the class itself (e.g., 75 | ``tm11.DiscoveryRequest.from_xml(xml_string)``). 76 | 77 | Each class in messages.py defines the following: 78 | 79 | * ``from_xml(xml_string)`` - Creates an instance of the class from an XML String. 80 | * ``to_xml()`` - Creates the XML representation of an instance of a class. 81 | * ``from_dict(dictionary)`` - Creates an instance of the class from a Python dictionary. 82 | * ``to_dict()`` - Creates the Python dictionary representation of an instance of a class. 83 | * ``from_etree(lxml_etree)`` - Creates an instance of the class from an LXML Etree. 84 | * ``to_etree()`` - Creates the LXML Etree representation of an instance of a class. 85 | 86 | To create a TAXII Message from XML: 87 | 88 | .. code:: python 89 | 90 | xml_string = '' # Note: Invalid XML 91 | discovery_response = tm11.DiscoveryResponse.from_xml(xml_string) 92 | 93 | To create an XML string from a TAXII Message: 94 | 95 | .. code:: python 96 | 97 | new_xml_string = discovery_response.to_xml() 98 | 99 | The same approach can be used for Python dictionaries: 100 | 101 | .. code:: python 102 | 103 | msg_dict = { ... } # Note: Invalid dictionary syntax 104 | discovery_response = tm11.DiscoveryResponse.from_dict(msg_dict) 105 | new_dict = discovery_response.to_dict() 106 | 107 | and for LXML ElementTrees: 108 | 109 | .. code:: python 110 | 111 | msg_etree = etree.Element( ... ) # Note: Invalid Element constructor 112 | discovery_response = tm11.DiscoveryResponse.from_etree(msg_etree) 113 | new_etree = discovery_response.to_etree() 114 | 115 | Schema Validating TAXII Messages 116 | -------------------------------- 117 | You can use libtaxii to Schema Validate XML, etree, and file representations of TAXII Messages. 118 | XML Schema validation cannot be performed on a TAXII Message Python object, since XML Schema validation 119 | can only be performed on XML. 120 | 121 | A full code example of XML Schema validation can be found in :ref:`API Documentation ` 122 | 123 | 124 | TAXII Clients 125 | ------------- 126 | 127 | The **libtaxii.clients** module defines a single class ``HttpClient`` capable 128 | of invoking TAXII services over both HTTP and HTTPS. The client is a fairly 129 | straighforward wrapper around Python's builtin ``httplib`` and supports the use 130 | of of both HTTP Basic and TLS Certificate authentication. 131 | 132 | Example usage of clients: 133 | 134 | .. code:: python 135 | 136 | import libtaxii as t 137 | import libtaxii.clients as tc 138 | import libtaxii.messages_11 as tm11 139 | from libtaxii.constants import * 140 | 141 | client = tc.HttpClient() 142 | client.set_auth_type(tc.HttpClient.AUTH_BASIC) 143 | client.set_use_https(True) 144 | client.set_auth_credentials({'username': 'MyUsername', 'password': 'MyPassword'}) 145 | 146 | discovery_request = tm11.DiscoveryRequest(tm11.generate_message_id()) 147 | discovery_xml = discovery_request.to_xml() 148 | 149 | http_resp = client.call_taxii_service2('example.com', '/pollservice/', VID_TAXII_XML_11, discovery_xml) 150 | taxii_message = t.get_message_from_http_response(http_resp, discovery_request.message_id) 151 | print taxii_message.to_xml() 152 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | libtaxii |release| Documentation 2 | ================================ 3 | 4 | **libtaxii** is a Python library that provides: 5 | 6 | 1. An object representation of TAXII Messages 7 | 2. Serialization/deserialization to and from XML and Python dictionaries 8 | 3. An HTTP/HTTPS TAXII Client 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | installation 14 | getting_started 15 | scripts 16 | changes 17 | 18 | API Reference 19 | ============= 20 | 21 | .. toctree:: 22 | :maxdepth: 2 23 | 24 | api/modules 25 | 26 | Indices and tables 27 | ================== 28 | 29 | * :ref:`genindex` 30 | * :ref:`modindex` 31 | * :ref:`search` 32 | 33 | Offline Documentation 34 | ===================== 35 | To download the latest documentation for offline use, please use one of these links: 36 | 37 | * `PDF `_ 38 | * `HTML `_ 39 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | Installation 4 | ============ 5 | 6 | Recommended Installation 7 | ------------------------ 8 | 9 | Use pip_: 10 | 11 | .. code-block:: bash 12 | 13 | $ pip install libtaxii 14 | 15 | You might also want to consider using a virtualenv_. 16 | 17 | .. _pip: http://pip.readthedocs.org/ 18 | .. _virtualenv: http://virtualenv.readthedocs.org/ 19 | 20 | 21 | Dependencies 22 | ------------ 23 | 24 | The libtaxii library is developed on Python 2.7 and tested against both 25 | Python 2.6 and 2.7. Besides the Python Standard Library, libtaxii relies on 26 | the following Python libraries: 27 | 28 | * lxml_ - A Pythonic binding for the C libraries **libxml2** and 29 | **libxslt**. 30 | * python-dateutil_ - A library for parsing datetime information. 31 | 32 | Each of these can be installed with ``pip`` or by manually downloading packages 33 | from PyPI. On Windows, you will probably have the most luck using `pre-compiled 34 | binaries`_ for ``lxml``. On Ubuntu (12.04 or 14.04), you should make sure the 35 | following packages are installed before attempting to compile ``lxml`` from 36 | source: 37 | 38 | * libxml2-dev 39 | * libxslt1-dev 40 | * zlib1g-dev 41 | 42 | .. note:: 43 | 44 | In libtaxii 1.0.101 and earlier, the M2Crypto library was also required. 45 | This dependency was removed as of libtaxii 1.0.102. 46 | 47 | .. warning:: 48 | 49 | Users have encountered errors with versions of libxml2 (a dependency of 50 | lxml) prior to version 2.9.1. The default version of libxml2 provided on 51 | Ubuntu 12.04 is currently 2.7.8. Users are encouraged to upgrade libxml2 52 | manually if they have any issues. Ubuntu 14.04 provides libxml2 version 53 | 2.9.1. 54 | 55 | .. _lxml: http://lxml.de/ 56 | .. _python-dateutil: http://labix.org/python-dateutil 57 | .. _pre-compiled binaries: http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 58 | .. _M2Crypto: http://chandlerproject.org/Projects/MeTooCrypto 59 | 60 | 61 | Manual Installation 62 | ------------------- 63 | 64 | If you are unable to use pip, you can also install libtaxii with setuptools_. 65 | If you don't already have setuptools installed, please install it before 66 | continuing. 67 | 68 | 1. Download and install the dependencies_ above. Although setuptools will 69 | generally install dependencies automatically, installing the dependencies 70 | manually beforehand helps distinguish errors in dependency installation from 71 | errors in libtaxii installation. Make sure you check to ensure the 72 | versions you install are compatible with the version of libtaxii you plan 73 | to install. 74 | 75 | 2. Download the desired version of libtaxii from PyPI_ or the GitHub releases_ 76 | page. The steps below assume you are using the |release| release. 77 | 78 | 3. Extract the downloaded file. This will leave you with a directory named 79 | libtaxii-|release|. 80 | 81 | .. parsed-literal:: 82 | $ tar -zxf libtaxii-|release|.tar.gz 83 | $ ls 84 | libtaxii-|release| libtaxii-|release|.tar.gz 85 | 86 | OR 87 | 88 | .. parsed-literal:: 89 | $ unzip libtaxii-|release|.zip 90 | $ ls 91 | libtaxii-|release| libtaxii-|release|.zip 92 | 93 | 4. Run the installation script. 94 | 95 | .. parsed-literal:: 96 | $ cd libtaxii-|release| 97 | $ python setup.py install 98 | 99 | 5. Test the installation. 100 | 101 | .. parsed-literal:: 102 | $ python 103 | Python 2.7.6 (default, Mar 22 2014, 22:59:56) 104 | [GCC 4.8.2] on linux2 105 | Type "help", "copyright", "credits" or "license" for more information. 106 | >>> import libtaxii 107 | >>> 108 | 109 | If you don't see an ``ImportError``, the installation was successful. 110 | 111 | .. _setuptools: https://pypi.python.org/pypi/setuptools/ 112 | .. _PyPI: https://pypi.python.org/pypi/libtaxii/ 113 | .. _releases: https://github.com/TAXIIProject/libtaxii/releases 114 | 115 | 116 | Further Information 117 | ------------------- 118 | 119 | If you're new to installing Python packages, you can learn more at the `Python 120 | Packaging User Guide`_, specifically the `Installing Python Packages`_ section. 121 | 122 | .. _Python Packaging User Guide: http://python-packaging-user-guide.readthedocs.org/ 123 | .. _Installing Python Packages: http://python-packaging-user-guide.readthedocs.org/en/latest/tutorial.html#installing-python-packages 124 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\libtaxii.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\libtaxii.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /docs/scripts.rst: -------------------------------------------------------------------------------- 1 | Scripts 2 | ======= 3 | 4 | This page provides documentation on the scripts that are included with libtaxii. All 5 | clients are configured to use the Hail A TAXII server (http://hailataxii.com/) by 6 | default; provide command line options for specifying most aspects of the script (e.g., 7 | host, port, client certs, username/password, HTTP or HTTPS, etc); and support TAXII 1.1 8 | unless otherwise noted. 9 | 10 | Note that the scripts *should* be callable from anywhere on the command line as long as you have 11 | the python scripts directory on your path. 12 | 13 | Script Listing 14 | -------------- 15 | 16 | * **discovery_client** - Issues a Discovery Request to a Discovery Service 17 | * **fulfillment_client** - Issues a Poll Fulfillment Request to a Poll Service and writes the resulting content to file 18 | * **inbox_client** - Issues an Inbox Message with one Content Block to an Inbox Service 19 | * **poll_client** - Issues a Poll Request to a Poll Service and writes the resulting content to file 20 | * **poll_client_10** - Issues a Poll Request to a TAXII 1.0 Poll Service and writes the resulting content to file 21 | * **query_client** - Issues a Query for an IP Address or Hash to a Poll Service and writes the resulting content to file 22 | 23 | Common Command Line Arguments 24 | ----------------------------- 25 | All scripts use these command line arguments: 26 | 27 | * ``-h, --help`` - Shows help text 28 | * ``-u, --url`` - Specified the URL to connect to. 29 | * ``--cert`` - Specifies the file location of the certificate to use for 30 | authentication. If provided, ``--key`` must also be provided. 31 | * ``--key`` - Specifies the file location of the key to use for authentication. 32 | * ``--username`` - Specifies the username to use for authentication. If 33 | provided, ``--pass`` must also be provided. 34 | * ``--pass`` - Specifies the password to use for authentication. 35 | * ``--proxy`` - Specifies proxy settings (e.g. ``http://proxy.example.com:80/``, 36 | or ``noproxy`` to not use any proxy). If omitted, the system's proxy settings 37 | will be used. 38 | * ``--xml-output`` - Specifies that the XML messages should be printed instead of the default textual representation 39 | 40 | Note: As of libtaxii 1.1.106, the following arguments are now deprecated in favor of ``--url`` 41 | 42 | * ``--host`` - Specifies the host to connect to (e.g., ``hailataxii.com``) 43 | * ``--port`` - Specifies the port to connect on (e.g., ``80``) 44 | * ``--path`` - Specifies the path portion of the URL to connect to 45 | (e.g., ``/services/discovery``) 46 | * ``--https`` - Specifies whether to use HTTPS or not (e.g., True or False) 47 | 48 | For example, to call the discovery_client using all these arguments, you would do: 49 | ``discovery_client --url http://hailataxii.com/taxii-discovery-service --cert MyCert.crt --key MyKey.key --username foo --pass bar --proxy http://myproxy.example.com:80 --xml-output`` 50 | 51 | Additional Discovery Client Command Line Arguments 52 | -------------------------------------------------- 53 | The Discovery Client does not use any additional command line arguments. 54 | 55 | Additional Poll Fulfillment Client Command Line Arguments 56 | --------------------------------------------------------- 57 | In addition to the command line arguments listed above, the Poll Fulfillment Client uses these: 58 | 59 | * ``--collection`` - The collection being requested 60 | * ``--result_id`` - The result id being requested (required) 61 | * ``--result_part_number`` - The result part number being requested (defaults to 1) 62 | 63 | Example: ``fulfillment_client --collection MyCollection --result_id someId --result_part_number 1`` 64 | 65 | Additional Inbox Client Command Line Arguments 66 | ---------------------------------------------- 67 | In addition to the command line arguments listed above, the Inbox Client uses these: 68 | 69 | * ``--content-binding`` - The Content Binding ID to use for the Content Block (Defaults to STIX XML 1.1) 70 | * ``--subtype`` - The Content Binding ID subtype to use for the Content Block (Optional; Defaults to None) 71 | * ``--content-file`` - The file location (e.g., /tmp/mydata) containing the data to send in the Content Block. Defaults to a built-in STIX 1.1 XML document. 72 | * ``--dcn`` - The Destination Collection Name that is specified in the Inbox Message, requesting that the recipient 73 | make the sent content available on the specified Destination Collection Name. TAXII supports multiple DCNs, but 74 | this script only supports one. 75 | 76 | Example: ``inbox_client --content-binding urn:stix.mitre.org:xml:1.1 --content-file stix_file.xml`` 77 | 78 | Additional Poll Client Command Line Arguments 79 | --------------------------------------------- 80 | In addition to the command line arguments listed above, the Poll Client uses these: 81 | 82 | * ``--collection`` - The Collection Name to Poll. Defaults to 'default' 83 | * ``--begin_timestamp`` - The Begin Timestamp Label to used bound the Poll Request. Defaults to None. 84 | * ``--end_timestamp`` - The End Timestamp Label to used bound the Poll Request. Defaults to None. 85 | * ``--subscription-id`` - The Subscription ID for this Poll Request 86 | * ``--dest-dir`` - The directory to save Content Blocks to. Defaults to the current directory. 87 | 88 | Example: ``poll_client --collection MyCollection`` 89 | 90 | Additional Poll Client 1.0 Command Line Arguments 91 | ------------------------------------------------- 92 | In addition to the command line arguments listed above, the Poll Client 1.0 uses these: 93 | 94 | * ``--feed`` - The Data Feed to Poll. Defaults to 'default' 95 | * ``--begin_timestamp`` - The Begin Timestamp Label to used bound the Poll Request. Defaults to None. 96 | * ``--end_timestamp`` - The End Timestamp Label to used bound the Poll Request. Defaults to None. 97 | * ``--subscription-id`` - The Subscription ID to use when polling 98 | * ``--dest-dir`` - The directory to save Content Blocks to. Defaults to the current directory. 99 | 100 | Example: ``poll_client_10 --feed MyFeedName --subscription-id SomeSubscriptionId`` 101 | 102 | Additional Query Client Command Line Arguments 103 | ---------------------------------------------- 104 | In addition to the command line arguments listed above, the Query Client uses these: 105 | 106 | * ``--collection`` - The collection to Poll (recall that a query is part of a Poll Request). Defaults to 'default_queryable'. 107 | * ``--allow_asynch`` - Whether asynchronous Polling is supported. Defaults to True (Use the Poll Fulfillment client to request asynchronous results!) 108 | * ``--ip`` - The IP to query on. One of --ip or --hash must be specified. 109 | * ``--hash`` - The file hash to query on. One of --ip or --hash must be specified. 110 | * ``--dest-dir`` - The directory to save Content Blocks to. Defaults to the current directory. 111 | 112 | Example: ``query_client --collection MyQueryCollection --ip 10.0.0.0`` 113 | -------------------------------------------------------------------------------- /libtaxii/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, The MITRE Corporation 2 | # For license information, see the LICENSE.txt file 3 | 4 | """ 5 | The main libtaxii module 6 | """ 7 | 8 | import six 9 | from six.moves import urllib 10 | 11 | import libtaxii.messages_10 as tm10 12 | import libtaxii.messages_11 as tm11 13 | import libtaxii.clients as tc 14 | from .constants import * 15 | 16 | import cgi 17 | 18 | from .version import __version__ # noqa 19 | 20 | 21 | def get_message_from_http_response(http_response, in_response_to): 22 | """Create a TAXII message from an HTTPResponse object. 23 | 24 | This function parses the :py:class:`httplib.HTTPResponse` by reading the 25 | X-TAXII-Content-Type HTTP header to determine if the message binding is 26 | supported. If the X-TAXII-Content-Type header is present and the value 27 | indicates a supported Message Binding, this function will attempt to parse 28 | the HTTP Response body. 29 | 30 | If the X-TAXII-Content-Type header is not present, this function will 31 | attempt to build a Failure Status Message per the HTTP Binding 1.0 32 | specification. 33 | 34 | If the X-TAXII-Content-Type header is present and indicates an unsupported 35 | Message Binding, this function will raise a ValueError. 36 | 37 | Args: 38 | http_response (httplib.HTTPResponse): the HTTP response to 39 | parse 40 | in_response_to (str): the default value for in_response_to 41 | """ 42 | if isinstance(http_response, six.moves.http_client.HTTPResponse): 43 | return get_message_from_httplib_http_response(http_response, in_response_to) 44 | elif isinstance(http_response, urllib.error.HTTPError): 45 | return get_message_from_urllib2_httperror(http_response, in_response_to) 46 | elif isinstance(http_response, urllib.response.addinfourl): 47 | return get_message_from_urllib_addinfourl(http_response, in_response_to) 48 | else: 49 | raise ValueError('Unsupported response type: %s.' % http_response.__class__.__name__) 50 | 51 | 52 | def get_message_from_urllib2_httperror(http_response, in_response_to): 53 | """ This function should not be called by libtaxii users directly. """ 54 | info = http_response.info() 55 | 56 | if hasattr(info, 'getheader'): 57 | taxii_content_type = info.getheader('X-TAXII-Content-Type') 58 | _, params = cgi.parse_header(info.getheader('Content-Type')) 59 | else: 60 | taxii_content_type = info.get('X-TAXII-Content-Type') 61 | _, params = cgi.parse_header(info.get('Content-Type')) 62 | 63 | encoding = params.get('charset', 'utf-8') 64 | response_message = six.ensure_text(http_response.read(), errors='replace') 65 | 66 | if taxii_content_type is None: 67 | m = str(http_response) + '\r\n' + str(http_response.info()) + '\r\n' + response_message 68 | return tm11.StatusMessage(message_id='0', in_response_to=in_response_to, status_type=ST_FAILURE, message=m) 69 | elif taxii_content_type == VID_TAXII_XML_10: # It's a TAXII XML 1.0 message 70 | return tm10.get_message_from_xml(response_message, encoding) 71 | elif taxii_content_type == VID_TAXII_XML_11: # It's a TAXII XML 1.1 message 72 | return tm11.get_message_from_xml(response_message, encoding) 73 | elif taxii_content_type == VID_CERT_EU_JSON_10: 74 | return tm10.get_message_from_json(response_message, encoding) 75 | else: 76 | raise ValueError('Unsupported X-TAXII-Content-Type: %s' % taxii_content_type) 77 | 78 | 79 | def get_message_from_urllib_addinfourl(http_response, in_response_to): 80 | """ This function should not be called by libtaxii users directly. """ 81 | info = http_response.info() 82 | 83 | if hasattr(info, 'getheader'): 84 | taxii_content_type = info.getheader('X-TAXII-Content-Type') 85 | _, params = cgi.parse_header(info.getheader('Content-Type')) 86 | else: 87 | taxii_content_type = info.get('X-TAXII-Content-Type') 88 | _, params = cgi.parse_header(info.get('Content-Type')) 89 | 90 | encoding = params.get('charset', 'utf-8') 91 | response_message = six.ensure_text(http_response.read(), errors='replace') 92 | 93 | if taxii_content_type is None: # Treat it as a Failure Status Message, per the spec 94 | 95 | message = [] 96 | header_dict = six.iteritems(http_response.info().dict) 97 | for k, v in header_dict: 98 | message.append(k + ': ' + v + '\r\n') 99 | message.append('\r\n') 100 | message.append(response_message) 101 | 102 | m = ''.join(message) 103 | 104 | return tm11.StatusMessage(message_id='0', in_response_to=in_response_to, status_type=ST_FAILURE, message=m) 105 | 106 | elif taxii_content_type == VID_TAXII_XML_10: # It's a TAXII XML 1.0 message 107 | return tm10.get_message_from_xml(response_message, encoding) 108 | elif taxii_content_type == VID_TAXII_XML_11: # It's a TAXII XML 1.1 message 109 | return tm11.get_message_from_xml(response_message, encoding) 110 | elif taxii_content_type == VID_CERT_EU_JSON_10: 111 | return tm10.get_message_from_json(response_message, encoding) 112 | else: 113 | raise ValueError('Unsupported X-TAXII-Content-Type: %s' % taxii_content_type) 114 | 115 | 116 | def get_message_from_httplib_http_response(http_response, in_response_to): 117 | """ This function should not be called by libtaxii users directly. """ 118 | if hasattr(http_response, 'getheader'): 119 | taxii_content_type = http_response.getheader('X-TAXII-Content-Type') 120 | _, params = cgi.parse_header(http_response.getheader('Content-Type')) 121 | else: 122 | taxii_content_type = http_response.get('X-TAXII-Content-Type') 123 | _, params = cgi.parse_header(http_response.get('Content-Type')) 124 | 125 | encoding = params.get('charset', 'utf-8') 126 | response_message = six.ensure_text(http_response.read(), errors='replace') 127 | 128 | if taxii_content_type is None: # Treat it as a Failure Status Message, per the spec 129 | 130 | message = [] 131 | header_tuples = http_response.getheaders() 132 | for k, v in header_tuples: 133 | message.append(k + ': ' + v + '\r\n') 134 | message.append('\r\n') 135 | message.append(response_message) 136 | 137 | m = ''.join(message) 138 | 139 | return tm11.StatusMessage(message_id='0', in_response_to=in_response_to, status_type=ST_FAILURE, message=m) 140 | 141 | elif taxii_content_type == VID_TAXII_XML_10: # It's a TAXII XML 1.0 message 142 | return tm10.get_message_from_xml(response_message, encoding) 143 | elif taxii_content_type == VID_TAXII_XML_11: # It's a TAXII XML 1.1 message 144 | return tm11.get_message_from_xml(response_message, encoding) 145 | else: 146 | raise ValueError('Unsupported X-TAXII-Content-Type: %s' % taxii_content_type) 147 | -------------------------------------------------------------------------------- /libtaxii/common.py: -------------------------------------------------------------------------------- 1 | """ 2 | Common utility classes and functions used throughout libtaxii. 3 | """ 4 | 5 | from operator import attrgetter 6 | import re 7 | import sys 8 | from uuid import uuid4 9 | 10 | import dateutil.parser 11 | from lxml import etree 12 | import six 13 | from six.moves.urllib.parse import urlparse 14 | 15 | try: 16 | import simplejson as json 17 | except ImportError: 18 | import json 19 | 20 | from libtaxii.constants import * 21 | 22 | _XML_PARSER = None 23 | 24 | 25 | def parse(s, allow_file=True, allow_url=False): 26 | """ 27 | Uses the default parser to parse a string or file-like object 28 | 29 | :param s: The XML String or File-like object to parse. 30 | :param allow_file: Allow `s` to be a file path. 31 | :param allow_url: Allow `s` to be a URL. 32 | :return: an etree._Element 33 | """ 34 | # Do a simple validation that the given string (or URL) 35 | # has no protocol specified. Anything without parseable protocol 36 | # will be interpreted by lxml as string instead or path of external URL. 37 | if not allow_url and isinstance(s, six.string_types): 38 | parsed = urlparse(s) 39 | if parsed.scheme: 40 | raise ValueError('external URLs are not allowed') 41 | 42 | parser = get_xml_parser() 43 | 44 | # parse from string if no external paths allowed 45 | if not allow_file and not allow_url: 46 | return etree.fromstring(s, parser) 47 | 48 | # try to parse from file or string if files are allowed 49 | try: 50 | return etree.parse(s, parser).getroot() 51 | except IOError: 52 | return etree.XML(s, parser) 53 | 54 | 55 | def parse_xml_string(xmlstr): 56 | """Parse an XML string (binary or unicode) with the default parser. 57 | 58 | :param xmlstr: An XML String to parse 59 | :return: an etree._Element 60 | """ 61 | if isinstance(xmlstr, six.binary_type): 62 | xmlstr = six.BytesIO(xmlstr) 63 | elif isinstance(xmlstr, six.text_type): 64 | # LXML doesn't accept Unicode strings with an explicit encoding, so 65 | # try to detect and encode to bytes before passing to LXML. 66 | encoding = re.findall(r'encoding="([0-9A-Za-z_\-]+)"', xmlstr[:50], re.I) 67 | # re.findall returns a list of matching strings. We only care about the 68 | # first one. 69 | if encoding: 70 | xmlstr = six.BytesIO(xmlstr.encode(encoding[0])) 71 | else: 72 | xmlstr = six.StringIO(xmlstr) 73 | 74 | return parse(xmlstr, allow_file=True) 75 | 76 | 77 | def get_xml_parser(): 78 | """Return the XML parser currently in use. 79 | 80 | If one has not already been set (via :py:func:`set_xml_parser()`), a new 81 | ``etree.XMLParser`` is constructed with ``no_network=True`` and 82 | ``huge_tree=False``. 83 | """ 84 | global _XML_PARSER 85 | if _XML_PARSER is None: 86 | _XML_PARSER = etree.XMLParser( 87 | attribute_defaults=False, 88 | dtd_validation=False, 89 | load_dtd=False, 90 | no_network=True, 91 | ns_clean=True, 92 | recover=False, 93 | remove_blank_text=False, 94 | remove_comments=False, 95 | remove_pis=False, 96 | strip_cdata=True, 97 | compact=True, 98 | # collect_ids=True, 99 | resolve_entities=False, 100 | huge_tree=False 101 | ) 102 | return _XML_PARSER.copy() 103 | 104 | 105 | def set_xml_parser(xml_parser=None): 106 | """Set the libtaxii.messages XML parser. 107 | 108 | Args: 109 | xml_parser (etree.XMLParser): The parser to use to parse TAXII XML. 110 | """ 111 | global _XML_PARSER 112 | _XML_PARSER = xml_parser 113 | 114 | 115 | def parse_datetime_string(datetime_string): 116 | """Parse a string into a :py:class:`datetime.datetime`. 117 | 118 | libtaxii users should not need to use this function directly. 119 | """ 120 | if not datetime_string: 121 | return None 122 | return dateutil.parser.parse(datetime_string) 123 | 124 | 125 | def generate_message_id(maxlen=5, version=VID_TAXII_SERVICES_10): 126 | """Generate a TAXII Message ID. 127 | 128 | Args: 129 | maxlen (int): maximum length of the ID, in characters 130 | 131 | Example: 132 | .. code-block:: python 133 | 134 | msg_id = tm11.generate_message_id() 135 | message = tm11.DiscoveryRequest(msg_id) 136 | # Or... 137 | message = tm11.DiscoveryRequest(tm11.generate_message_id()) 138 | """ 139 | if version == VID_TAXII_SERVICES_10: 140 | message_id = str(uuid4().int % sys.maxsize) 141 | elif version == VID_TAXII_SERVICES_11: 142 | message_id = str(uuid4()) 143 | else: 144 | raise ValueError('Unknown TAXII Version: %s. Must be a TAXII Services Version ID!' % version) 145 | return message_id 146 | 147 | 148 | def append_any_content_etree(etree_elt, content): 149 | """ 150 | General method for adding content to an etree element. This method can handle: 151 | * etree._ElementTree 152 | * etree._Element 153 | * any python type that can be cast to str 154 | * str 155 | 156 | 157 | :param etree_elt: The etree to append the content to 158 | :param content: The content to append 159 | :return: The etree_elt 160 | """ 161 | 162 | if isinstance(content, etree._ElementTree): # If content is an element tree, append the root element 163 | etree_elt.append(content.getroot()) 164 | return etree_elt 165 | 166 | if isinstance(content, etree._Element): # If content is an element, append it 167 | etree_elt.append(content) 168 | return etree_elt 169 | 170 | if not isinstance(content, six.string_types): # If content is a non-string, cast it to string and set etree_elt.text 171 | etree_elt.text = str(content) 172 | return etree_elt 173 | 174 | # If content is a string, need to check if it's XML or not 175 | try: 176 | etree_elt.append(etree.XML(content, get_xml_parser())) 177 | except etree.XMLSyntaxError: 178 | etree_elt.text = content 179 | 180 | return etree_elt 181 | 182 | 183 | def gen_filename(collection_name, format_part, date_string, extension): 184 | """ 185 | Creates a filename based on various properties of a Poll Request and Content Block 186 | 187 | :param collection_name: The collection name 188 | :param format_part: The format part (e.g., '_STIX_10_') 189 | :param date_string: A datestring 190 | :param extension: The file extension to use 191 | :return: A string containing the generated filename 192 | """ 193 | if six.PY3: 194 | return (collection_name.lstrip(".") + 195 | format_part + 196 | re.sub(r"[^a-zA-Z0-9]", "_", date_string) + extension 197 | ).translate('/\\:*?"<>|') 198 | else: 199 | return (collection_name.lstrip(".") + 200 | format_part + 201 | re.sub(r"[^a-zA-Z0-9]", "_", date_string) + extension 202 | ).translate(None, '/\\:*?"<>|') 203 | 204 | 205 | class TAXIIBase(object): 206 | 207 | """ 208 | Base class for all TAXII Messages and Message component types. 209 | 210 | libtaxii users should not need to use this class directly. 211 | """ 212 | 213 | @property 214 | def sort_key(self): 215 | """ 216 | This property allows list of TAXII objects to be compared efficiently. 217 | The __eq__ method uses this property to sort the lists before 218 | comparisons are made. 219 | 220 | Subclasses must implement this property. 221 | """ 222 | raise NotImplementedError() 223 | 224 | def to_etree(self): 225 | """Create an etree representation of this class. 226 | 227 | Subclasses must implement this method. 228 | """ 229 | raise NotImplementedError() 230 | 231 | def to_dict(self): 232 | """Create a dictionary representation of this class. 233 | 234 | Subclasses must implement this method. 235 | """ 236 | raise NotImplementedError() 237 | 238 | def to_json(self): 239 | """Create a JSON object of this class. 240 | 241 | Assumes any binary content will be UTF-8 encoded. 242 | """ 243 | content_dict = self.to_dict() 244 | 245 | _decode_binary_fields(content_dict) 246 | 247 | return json.dumps(content_dict) 248 | 249 | def to_xml(self, pretty_print=False): 250 | """Create an XML representation of this class. 251 | 252 | Subclasses should not need to implement this method. 253 | """ 254 | return etree.tostring(self.to_etree(), pretty_print=pretty_print, encoding='utf-8') 255 | 256 | def to_text(self, line_prepend=''): 257 | """Create a nice looking (this is a subjective term!) 258 | textual representation of this class. Subclasses should 259 | implement this method. 260 | 261 | Note that this is just a convenience method for making 262 | TAXII Messages nice to read for humans and may change 263 | drastically in future versions of libtaxii. 264 | """ 265 | raise NotImplementedError() 266 | 267 | @classmethod 268 | def from_etree(cls, src_etree): 269 | """Create an instance of this class from an etree. 270 | 271 | Subclasses must implement this method. 272 | """ 273 | raise NotImplementedError() 274 | 275 | @classmethod 276 | def from_dict(cls, d): 277 | """Create an instance of this class from a dictionary. 278 | 279 | Subclasses must implement this method. 280 | """ 281 | raise NotImplementedError() 282 | 283 | @classmethod 284 | def from_xml(cls, xml): 285 | """Create an instance of this class from XML. 286 | 287 | Subclasses should not need to implement this method. 288 | """ 289 | etree_xml = parse_xml_string(xml) 290 | return cls.from_etree(etree_xml) 291 | 292 | # Just noting that there is not a from_text() method. I also 293 | # don't think there will ever be one. 294 | 295 | def __str__(self): 296 | return self.to_xml(pretty_print=True) 297 | 298 | def __eq__(self, other, debug=False): 299 | """ 300 | Generic method used to check equality of objects of any TAXII type. 301 | 302 | Also allows for ``print``-based debugging output showing differences. 303 | 304 | In order for subclasses to use this function, they must meet the 305 | following criteria: 306 | 1. All class properties start with one underscore. 307 | 2. The sort_key property is implemented. 308 | 309 | Args: 310 | self (object): this object 311 | other (object): the object to compare ``self`` against. 312 | debug (bool): Whether or not to print debug statements as the 313 | equality comparison is performed. 314 | """ 315 | if other is None: 316 | if debug: 317 | print('other was None!') 318 | return False 319 | 320 | if self.__class__.__name__ != other.__class__.__name__: 321 | if debug: 322 | print('class names not equal: %s != %s' % (self.__class__.__name__, other.__class__.__name__)) 323 | return False 324 | 325 | # Get all member properties that start with '_' 326 | members = [attr for attr in vars(self) if attr.startswith('_') and not attr.startswith('__')] 327 | for member in members: 328 | if debug: 329 | print('member name: %s' % member) 330 | self_value = getattr(self, member) 331 | other_value = getattr(other, member) 332 | 333 | if isinstance(self_value, TAXIIBase): 334 | # A debuggable equals comparison can be made 335 | eq = self_value.__eq__(other_value, debug) 336 | elif isinstance(self_value, list): 337 | # We have lists to compare 338 | if len(self_value) != len(other_value): 339 | # Lengths not equal 340 | member = member + ' lengths' 341 | self_value = len(self_value) 342 | other_value = len(other_value) 343 | eq = False 344 | elif len(self_value) == 0: 345 | # Both lists are of size 0, and therefore equal 346 | eq = True 347 | else: 348 | # Equal sized, non-0 length lists. The list might contain 349 | # TAXIIBase objects, or it might not. Peek at the first 350 | # item to see whether it is a TAXIIBase object or not. 351 | if isinstance(self_value[0], TAXIIBase): 352 | # All TAXIIBase objects have the 'sort_key' property implemented 353 | self_value = sorted(self_value, key=attrgetter('sort_key')) 354 | other_value = sorted(other_value, key=attrgetter('sort_key')) 355 | for self_item, other_item in six.moves.zip(self_value, other_value): 356 | # Compare the ordered lists element by element 357 | eq = self_item.__eq__(other_item, debug) 358 | else: 359 | # Assume they don't... just do a set comparison 360 | eq = set(self_value) == set(other_value) 361 | elif isinstance(self_value, dict): 362 | # Dictionary to compare 363 | if len(set(self_value.keys()) - set(other_value.keys())) != 0: 364 | if debug: 365 | print('dict keys not equal: %s != %s' % (self_value, other_value)) 366 | eq = False 367 | for k, v in six.iteritems(self_value): 368 | if other_value[k] != v: 369 | if debug: 370 | print('dict values not equal: %s != %s' % (v, other_value[k])) 371 | eq = False 372 | eq = True 373 | elif isinstance(self_value, etree._Element): 374 | # Non-TAXII etree element (i.e. STIX) 375 | eq = (etree.tostring(self_value, encoding='utf-8') == etree.tostring(other_value, encoding='utf-8')) 376 | else: 377 | # Do a direct comparison 378 | eq = (self_value == other_value) 379 | 380 | # TODO: is this duplicate? 381 | if not eq: 382 | if debug: 383 | print('%s was not equal: %s != %s' % (member, self_value, other_value)) 384 | return False 385 | 386 | return True 387 | 388 | def __ne__(self, other, debug=False): 389 | return not self.__eq__(other, debug) 390 | 391 | 392 | def get_required(etree_xml, xpath, ns_map): 393 | elements = etree_xml.xpath(xpath, namespaces=ns_map) 394 | if len(elements) == 0: 395 | raise ValueError('Element "%s" is required' % xpath) 396 | return elements[0] 397 | 398 | 399 | def get_optional(etree_xml, xpath, ns_map): 400 | try: 401 | return get_required(etree_xml, xpath, ns_map) 402 | except ValueError: 403 | pass 404 | 405 | 406 | def get_optional_text(etree_xml, xpath, ns_map): 407 | try: 408 | return get_required(etree_xml, xpath, ns_map).text 409 | except ValueError: 410 | pass 411 | 412 | 413 | def _decode_binary_fields(dict_obj): 414 | """Given a dict, decode any binary values, assuming UTF-8 encoding. 415 | Will recurse into nested dicts. 416 | Modifies the values in-place. 417 | """ 418 | for key, value in dict_obj.items(): 419 | 420 | if isinstance(value, six.binary_type): 421 | dict_obj[key] = value.decode('utf-8') 422 | 423 | elif isinstance(value, dict): 424 | _decode_binary_fields(value) 425 | 426 | 427 | def stringify_content(content): 428 | """Always a string or raises an error. 429 | Returns the string representation and whether the data is XML. 430 | """ 431 | # If it's an etree, it's definitely XML 432 | if isinstance(content, etree._ElementTree): 433 | return content.getroot(), True 434 | 435 | if isinstance(content, etree._Element): 436 | return content, True 437 | 438 | if hasattr(content, 'read'): # The content is file-like 439 | try: # Try to parse as XML 440 | xml = parse(content, allow_file=True) 441 | return xml, True 442 | except etree.XMLSyntaxError: # Content is not well-formed XML; just treat as a string 443 | return content.read(), False 444 | else: # The Content is not file-like 445 | try: # Attempt to parse string as XML 446 | xml = parse_xml_string(content) 447 | return xml, True 448 | except etree.XMLSyntaxError: # Content is not well-formed XML; just treat as a string 449 | if isinstance(content, six.string_types): # It's a string of some kind, unicode or otherwise 450 | return content, False 451 | else: # It's some other datatype that needs casting to string 452 | return str(content), False 453 | -------------------------------------------------------------------------------- /libtaxii/constants.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, The MITRE Corporation 2 | # For license information, see the LICENSE.txt file 3 | 4 | #: Namespace map of namespaces libtaxii knows about 5 | NS_MAP = { 6 | 'taxii': 'http://taxii.mitre.org/messages/taxii_xml_binding-1', 7 | 'taxii_11': 'http://taxii.mitre.org/messages/taxii_xml_binding-1.1', 8 | 'tdq': 'http://taxii.mitre.org/query/taxii_default_query-1', 9 | } 10 | 11 | #: alias for NS_MAP for backward compatibility 12 | ns_map = NS_MAP 13 | 14 | #: Constant identifying a Status Message 15 | MSG_STATUS_MESSAGE = 'Status_Message' 16 | #: Constant identifying a Discovery Request Message 17 | MSG_DISCOVERY_REQUEST = 'Discovery_Request' 18 | #: Constant identifying a Discovery Response Message 19 | MSG_DISCOVERY_RESPONSE = 'Discovery_Response' 20 | #: Constant identifying a Feed Information Request Message 21 | MSG_FEED_INFORMATION_REQUEST = 'Feed_Information_Request' 22 | #: Constant identifying a Feed Information Response Message 23 | MSG_FEED_INFORMATION_RESPONSE = 'Feed_Information_Response' 24 | #: Constant identifying a Subscription Management Request Message 25 | MSG_MANAGE_FEED_SUBSCRIPTION_REQUEST = 'Subscription_Management_Request' 26 | #: Constant identifying a Subscription Management Response Message 27 | MSG_MANAGE_FEED_SUBSCRIPTION_RESPONSE = 'Subscription_Management_Response' 28 | #: Constant identifying a Poll Request Message 29 | MSG_POLL_REQUEST = 'Poll_Request' 30 | #: Constant identifying a Poll Response Message 31 | MSG_POLL_RESPONSE = 'Poll_Response' 32 | #: Constant identifying a Inbox Message 33 | MSG_INBOX_MESSAGE = 'Inbox_Message' 34 | 35 | #: TAXII 1.0 Message Types 36 | MSG_TYPES_10 = (MSG_STATUS_MESSAGE, MSG_DISCOVERY_REQUEST, MSG_DISCOVERY_RESPONSE, 37 | MSG_FEED_INFORMATION_REQUEST, MSG_FEED_INFORMATION_RESPONSE, 38 | MSG_MANAGE_FEED_SUBSCRIPTION_REQUEST, 39 | MSG_MANAGE_FEED_SUBSCRIPTION_RESPONSE, MSG_POLL_REQUEST, 40 | MSG_POLL_RESPONSE, MSG_INBOX_MESSAGE) 41 | 42 | # New Message Types in TAXII 1.1 43 | 44 | #: Constant identifying a Status Message 45 | MSG_POLL_FULFILLMENT_REQUEST = 'Poll_Fulfillment' 46 | #: Constant identifying a Collection Information Request 47 | MSG_COLLECTION_INFORMATION_REQUEST = 'Collection_Information_Request' 48 | #: Constant identifying a Collection Information Response 49 | MSG_COLLECTION_INFORMATION_RESPONSE = 'Collection_Information_Response' 50 | #: Constant identifying a Subscription Request 51 | MSG_MANAGE_COLLECTION_SUBSCRIPTION_REQUEST = 'Subscription_Management_Request' 52 | #: Constant identifying a Subscription Response 53 | MSG_MANAGE_COLLECTION_SUBSCRIPTION_RESPONSE = 'Subscription_Management_Response' 54 | 55 | #: Tuple of all TAXII 1.1 Message Types 56 | MSG_TYPES_11 = (MSG_STATUS_MESSAGE, MSG_DISCOVERY_REQUEST, MSG_DISCOVERY_RESPONSE, 57 | MSG_COLLECTION_INFORMATION_REQUEST, MSG_COLLECTION_INFORMATION_RESPONSE, 58 | MSG_MANAGE_COLLECTION_SUBSCRIPTION_REQUEST, 59 | MSG_MANAGE_COLLECTION_SUBSCRIPTION_RESPONSE, MSG_POLL_REQUEST, 60 | MSG_POLL_RESPONSE, MSG_INBOX_MESSAGE, MSG_POLL_FULFILLMENT_REQUEST) 61 | 62 | # TAXII 1.0 Status Types 63 | 64 | #: Constant identifying a Status Type of Bad Message 65 | ST_BAD_MESSAGE = 'BAD_MESSAGE' 66 | #: Constant identifying a Status Type of Denied 67 | ST_DENIED = 'DENIED' 68 | #: Constant identifying a Status Type of Failure 69 | ST_FAILURE = 'FAILURE' 70 | #: Constant identifying a Status Type of Not Found 71 | ST_NOT_FOUND = 'NOT_FOUND' 72 | #: Constant identifying a Status Type of Polling Unsupported 73 | ST_POLLING_UNSUPPORTED = 'POLLING_UNSUPPORTED' 74 | #: Constant identifying a Status Type of Retry 75 | ST_RETRY = 'RETRY' 76 | #: Constant identifying a Status Type of Success 77 | ST_SUCCESS = 'SUCCESS' 78 | #: Constant identifying a Status Type of Unauthorized 79 | ST_UNAUTHORIZED = 'UNAUTHORIZED' 80 | #: Constant identifying a Status Type of Unsupported Message Binding 81 | ST_UNSUPPORTED_MESSAGE_BINDING = 'UNSUPPORTED_MESSAGE' 82 | #: Constant identifying a Status Type of Unsupported Content Binding 83 | ST_UNSUPPORTED_CONTENT_BINDING = 'UNSUPPORTED_CONTENT' 84 | #: Constant identifying a Status Type of Unsupported Protocol Binding 85 | ST_UNSUPPORTED_PROTOCOL = 'UNSUPPORTED_PROTOCOL' 86 | 87 | #: Tuple of all TAXII 1.0 Status Types 88 | ST_TYPES_10 = (ST_BAD_MESSAGE, ST_DENIED, ST_FAILURE, ST_NOT_FOUND, 89 | ST_POLLING_UNSUPPORTED, ST_RETRY, ST_SUCCESS, ST_UNAUTHORIZED, 90 | ST_UNSUPPORTED_MESSAGE_BINDING, ST_UNSUPPORTED_CONTENT_BINDING, 91 | ST_UNSUPPORTED_PROTOCOL) 92 | 93 | # New Status Types in TAXII 1.1 94 | 95 | #: Constant identifying a Status Type of Asynchronous Poll Error 96 | ST_ASYNCHRONOUS_POLL_ERROR = 'ASYNCHRONOUS_POLL_ERROR' 97 | #: Constant identifying a Status Type of Destination Collection Error 98 | ST_DESTINATION_COLLECTION_ERROR = 'DESTINATION_COLLECTION_ERROR' 99 | #: Constant identifying a Status Type of Invalid Response Part 100 | ST_INVALID_RESPONSE_PART = 'INVALID_RESPONSE_PART' 101 | #: Constant identifying a Status Type of Network Error 102 | ST_NETWORK_ERROR = 'NETWORK_ERROR' 103 | #: Constant identifying a Status Type of Pending 104 | ST_PENDING = 'PENDING' 105 | #: Constant identifying a Status Type of Unsupported Query Format 106 | ST_UNSUPPORTED_QUERY = 'UNSUPPORTED_QUERY' 107 | 108 | #: Tuple of all TAXII 1.1 Status types 109 | ST_TYPES_11 = (ST_ASYNCHRONOUS_POLL_ERROR, ST_BAD_MESSAGE, ST_DENIED, 110 | ST_DESTINATION_COLLECTION_ERROR, ST_FAILURE, ST_INVALID_RESPONSE_PART, 111 | ST_NETWORK_ERROR, ST_NOT_FOUND, ST_PENDING, ST_POLLING_UNSUPPORTED, 112 | ST_RETRY, ST_SUCCESS, ST_UNAUTHORIZED, ST_UNSUPPORTED_MESSAGE_BINDING, 113 | ST_UNSUPPORTED_CONTENT_BINDING, ST_UNSUPPORTED_PROTOCOL, 114 | ST_UNSUPPORTED_QUERY) 115 | 116 | # TAXII 1.0 Action Types 117 | 118 | #: Constant identifying an Action of Subscribe 119 | ACT_SUBSCRIBE = 'SUBSCRIBE' 120 | #: Constant identifying an Action of Unsubscribe 121 | ACT_UNSUBSCRIBE = 'UNSUBSCRIBE' 122 | #: Constant identifying an Action of Status 123 | ACT_STATUS = 'STATUS' 124 | 125 | #: Tuple of all TAXII 1.0 Action Types 126 | ACT_TYPES_10 = (ACT_SUBSCRIBE, ACT_UNSUBSCRIBE, ACT_STATUS) 127 | 128 | #: Constant identifying an Action of Pause 129 | ACT_PAUSE = 'PAUSE' 130 | #: Constant identifying an Action of Resume 131 | ACT_RESUME = 'RESUME' 132 | 133 | #: Tuple of all TAXII 1.1 Action types 134 | ACT_TYPES_11 = (ACT_SUBSCRIBE, ACT_PAUSE, ACT_RESUME, ACT_UNSUBSCRIBE, ACT_STATUS) 135 | 136 | # TAXII 1.0 Service Types 137 | 138 | #: Constant identifying a Service Type of Inbox 139 | SVC_INBOX = 'INBOX' 140 | #: Constant identifying a Service Type of Poll 141 | SVC_POLL = 'POLL' 142 | #: Constant identifying a Service Type of Feed Management 143 | SVC_FEED_MANAGEMENT = 'FEED_MANAGEMENT' 144 | #: Constant identifying a Service Type of Discovery 145 | SVC_DISCOVERY = 'DISCOVERY' 146 | 147 | #: Tuple of all TAXII 1.0 Service Types 148 | SVC_TYPES_10 = (SVC_INBOX, SVC_POLL, SVC_FEED_MANAGEMENT, SVC_DISCOVERY) 149 | 150 | # Renamed Status Types in TAXII 1.1 151 | #: Constant identifying a Service Type of Collection Management. 152 | #: "Feed Management" was renamed to "Collection Management" in TAXII 1.1. 153 | SVC_COLLECTION_MANAGEMENT = 'COLLECTION_MANAGEMENT' 154 | 155 | #: Tuple of all TAXII 1.1 Service Types 156 | SVC_TYPES_11 = (SVC_INBOX, SVC_POLL, SVC_COLLECTION_MANAGEMENT, SVC_DISCOVERY) 157 | 158 | # TAXII 1.1 Subscription Statuses 159 | 160 | #: Subscription Status of Active 161 | SS_ACTIVE = 'ACTIVE' 162 | #: Subscription Status of Paused 163 | SS_PAUSED = 'PAUSED' 164 | #: Subscription Status of Unsubscribed 165 | SS_UNSUBSCRIBED = 'UNSUBSCRIBED' 166 | 167 | #: Tuple of all TAXII 1.1 Subscription Statues 168 | SS_TYPES_11 = (SS_ACTIVE, SS_PAUSED, SS_UNSUBSCRIBED) 169 | 170 | # TAXII 1.1 Response Types 171 | 172 | #: Constant identifying a response type of Full 173 | RT_FULL = 'FULL' 174 | #: Constant identifying a response type of Count only 175 | RT_COUNT_ONLY = 'COUNT_ONLY' 176 | 177 | #: Tuple of all TAXII 1.1 Response Types 178 | RT_TYPES_11 = (RT_FULL, RT_COUNT_ONLY) 179 | 180 | # TAXII 1.1 Response Types 181 | 182 | #: Constant identifying a collection type of Data Feed 183 | CT_DATA_FEED = 'DATA_FEED' 184 | #: Constant identifying a collection type of Data Set 185 | CT_DATA_SET = 'DATA_SET' 186 | 187 | #: Tuple of all TAXII 1.1 Collection Types 188 | CT_TYPES_11 = (CT_DATA_FEED, CT_DATA_SET) 189 | 190 | # TAXII 1.1 Status Detail Keys 191 | 192 | #: Constant Identifying the Acceptable Destination Status Detail 193 | SD_ACCEPTABLE_DESTINATION = 'ACCEPTABLE_DESTINATION' 194 | #: Constant Identifying the Max Part Number Status Detail 195 | SD_MAX_PART_NUMBER = 'MAX_PART_NUMBER' 196 | #: Constant Identifying the Item Status Detail 197 | SD_ITEM = 'ITEM' 198 | #: Constant Identifying the Estimated Wait Status Detail 199 | SD_ESTIMATED_WAIT = 'ESTIMATED_WAIT' 200 | #: Constant Identifying the Result ID Status Detail 201 | SD_RESULT_ID = 'RESULT_ID' 202 | #: Constant Identifying the Will Push Status Detail 203 | SD_WILL_PUSH = 'WILL_PUSH' 204 | #: Constant Identifying the Supported Binding Status Detail 205 | SD_SUPPORTED_BINDING = 'SUPPORTED_BINDING' 206 | #: Constant Identifying the Supported Content Status Detail 207 | SD_SUPPORTED_CONTENT = 'SUPPORTED_CONTENT' 208 | #: Constant Identifying the Supported Protocol Status Detail 209 | SD_SUPPORTED_PROTOCOL = 'SUPPORTED_PROTOCOL' 210 | #: Constant Identifying the Supported Query Status Detail 211 | SD_SUPPORTED_QUERY = 'SUPPORTED_QUERY' 212 | 213 | #: Tuple of all TAXII 1.1 Status Detail Keys 214 | SD_TYPES_11 = (SD_ACCEPTABLE_DESTINATION, SD_MAX_PART_NUMBER, SD_ITEM, 215 | SD_ESTIMATED_WAIT, SD_RESULT_ID, SD_WILL_PUSH, 216 | SD_SUPPORTED_BINDING, SD_SUPPORTED_CONTENT, SD_SUPPORTED_PROTOCOL, 217 | SD_SUPPORTED_QUERY) 218 | 219 | #: (For TAXII Default Query) Constant identifying supported Capability Modules 220 | SD_CAPABILITY_MODULE = 'CAPABILITY_MODULE' 221 | #: (For TAXII Default Query) Constant identifying Preferred Scopes 222 | SD_PREFERRED_SCOPE = 'PREFERRED_SCOPE' 223 | #: (For TAXII Default Query) Constant identifying Allowed Scopes 224 | SD_ALLOWED_SCOPE = 'ALLOWED_SCOPE' 225 | #: (For TAXII Default Query) Constant identifying supported Targeting Expression IDs 226 | SD_TARGETING_EXPRESSION_ID = 'TARGETING_EXPRESSION_ID' 227 | 228 | #: Format ID for this version of TAXII Default Query 229 | FID_TAXII_DEFAULT_QUERY_10 = 'urn:taxii.mitre.org:query:default:1.0' 230 | 231 | # Capability Module IDs 232 | #: Capability Module ID for Core 233 | CM_CORE = 'urn:taxii.mitre.org:query:capability:core-1' 234 | #: Capability Module ID for Regex 235 | CM_REGEX = 'urn:taxii.mitre.org:query:capability:regex-1' 236 | #: Capability Module ID for Timestamp 237 | CM_TIMESTAMP = 'urn:taxii.mitre.org:query:capability:timestamp-1' 238 | 239 | #: Tuple of all capability modules defined in TAXII Default Query 1.0 240 | CM_IDS = (CM_CORE, CM_REGEX, CM_TIMESTAMP) 241 | 242 | # Operators 243 | #: Operator OR 244 | OP_OR = 'OR' 245 | #: Operator AND 246 | OP_AND = 'AND' 247 | 248 | #: Tuple of all operators 249 | OP_TYPES = (OP_OR, OP_AND) 250 | 251 | 252 | #: Status Type indicating an unsupported capability module 253 | ST_UNSUPPORTED_CAPABILITY_MODULE = 'UNSUPPORTED_CAPABILITY_MODULE' 254 | #: Status Type indicating an unsupported targeting expression 255 | ST_UNSUPPORTED_TARGETING_EXPRESSION = 'UNSUPPORTED_TARGETING_EXPRESSION' 256 | #: Status Type indicating an unsupported targeting expression id 257 | ST_UNSUPPORTED_TARGETING_EXPRESSION_ID = 'UNSUPPORTED_TARGETING_EXPRESSION_ID' 258 | 259 | #: Parameter name: value 260 | P_VALUE = 'value' 261 | #: Parameter name: match_type 262 | P_MATCH_TYPE = 'match_type' 263 | #: Parameter name: case_sensitive 264 | P_CASE_SENSITIVE = 'case_sensitive' 265 | 266 | #: Tuple of all parameter names 267 | P_NAMES = (P_VALUE, P_MATCH_TYPE, P_CASE_SENSITIVE) 268 | 269 | #: Relationship name: equals 270 | R_EQUALS = 'equals' 271 | #: Relationship name: not_requals 272 | R_NOT_EQUALS = 'not_equals' 273 | #: Relationship name: greater_than 274 | R_GREATER_THAN = 'greater_than' 275 | #: Relationship name: greater_than_or_equal 276 | R_GREATER_THAN_OR_EQUAL = 'greater_than_or_equal' 277 | #: Relationship name: less_than 278 | R_LESS_THAN = 'less_than' 279 | #: Relationship name: less_than_or_equal 280 | R_LESS_THAN_OR_EQUAL = 'less_than_or_equal' 281 | #: Relationship name: does_not_exist 282 | R_DOES_NOT_EXIST = 'does_not_exist' 283 | #: Relationship name: exists 284 | R_EXISTS = 'exists' 285 | #: Relationship name: begins_with 286 | R_BEGINS_WITH = 'begins_with' 287 | #: Relationship name: ends_with 288 | R_ENDS_WITH = 'ends_with' 289 | #: Relationship name: contains 290 | R_CONTAINS = 'contains' 291 | #: Relationship name: matches 292 | R_MATCHES = 'matches' 293 | 294 | #: Tuple of all relationship names 295 | R_NAMES = (R_EQUALS, R_NOT_EQUALS, R_GREATER_THAN, 296 | R_GREATER_THAN_OR_EQUAL, R_LESS_THAN, 297 | R_LESS_THAN_OR_EQUAL, R_DOES_NOT_EXIST, 298 | R_EXISTS, R_BEGINS_WITH, R_ENDS_WITH, 299 | R_CONTAINS, R_MATCHES) 300 | 301 | # TAXII Version IDs # 302 | 303 | #: Version ID for the TAXII Services Specification 1.0 304 | VID_TAXII_SERVICES_10 = 'urn:taxii.mitre.org:services:1.0' 305 | #: Version ID for the TAXII Services Specification 1.1 306 | VID_TAXII_SERVICES_11 = 'urn:taxii.mitre.org:services:1.1' 307 | #: Version ID for the TAXII XML Message Binding Specification 1.0 308 | VID_TAXII_XML_10 = 'urn:taxii.mitre.org:message:xml:1.0' 309 | #: Version ID for the TAXII XML Message Binding Specification 1.1 310 | VID_TAXII_XML_11 = 'urn:taxii.mitre.org:message:xml:1.1' 311 | #: Version ID for the TAXII HTTP Protocol Binding Specification 1.0 312 | VID_TAXII_HTTP_10 = 'urn:taxii.mitre.org:protocol:http:1.0' 313 | #: Version ID for the TAXII HTTPS Protocol Binding Specification 1.0 314 | VID_TAXII_HTTPS_10 = 'urn:taxii.mitre.org:protocol:https:1.0' 315 | 316 | # Third Party Version IDs 317 | #: Version ID for the CERT EU JSON Message Binding 318 | VID_CERT_EU_JSON_10 = 'urn:cert.europa.eu:message:json:1.0' 319 | 320 | 321 | # TAXII Content Bindings # 322 | 323 | #: Content Binding ID for STIX XML 1.0 324 | CB_STIX_XML_10 = 'urn:stix.mitre.org:xml:1.0' 325 | #: Content Binding ID for STIX XML 1.0.1 326 | CB_STIX_XML_101 = 'urn:stix.mitre.org:xml:1.0.1' 327 | #: Content Binding ID for STIX XML 1.1 328 | CB_STIX_XML_11 = 'urn:stix.mitre.org:xml:1.1' 329 | #: Content Binding ID for STIX XML 1.1.1 330 | CB_STIX_XML_111 = 'urn:stix.mitre.org:xml:1.1.1' 331 | #: Content Binding ID for STIX XML 1.2 332 | CB_STIX_XML_12 = 'urn:stix.mitre.org:xml:1.2' 333 | #: Content Binding ID for CAP 1.1 334 | CB_CAP_11 = 'urn:oasis:names:tc:emergency:cap:1.1' 335 | #: Content Binding ID for XML Encryption 336 | CB_XENC_122002 = 'http://www.w3.org/2001/04/xmlenc#' 337 | #: Content Binding ID for SMIME 338 | CB_SMIME = 'application/x-pkcs7-mime' 339 | 340 | STD_INDENT = ' ' # A "Standard Indent" to use for to_text() methods 341 | -------------------------------------------------------------------------------- /libtaxii/messages.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, The MITRE Corporation 2 | # For license information, see the LICENSE.txt file 3 | 4 | """Backwards compatibility for TAXII 1.0. 5 | 6 | All TAXII Message classes used to be in this file. Since TAXII 1.1 support 7 | was added (in messages_11.py), the contents of this file were moved to 8 | messages_10.py. This file allows existing code (referring to libtaxii.messages) 9 | to continue working as before. 10 | """ 11 | 12 | 13 | from libtaxii.messages_10 import * 14 | -------------------------------------------------------------------------------- /libtaxii/scripts/collection_information_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | import libtaxii.messages_11 as tm11 7 | from libtaxii.scripts import TaxiiScript 8 | 9 | 10 | class CollectionInformationClient11Script(TaxiiScript): 11 | """Collection Information Request Client""" 12 | 13 | parser_description = \ 14 | 'The TAXII 1.1 Collection Information Client sends a Collection Information Request ' \ 15 | 'to a TAXII Server and then prints the resulting Collection Information Response to ' \ 16 | 'standard out.' 17 | 18 | path = '/taxii-data' 19 | 20 | def create_request_message(self, args): 21 | message_id = tm11.generate_message_id() 22 | return tm11.CollectionInformationRequest(message_id) 23 | 24 | 25 | def main(): 26 | """Send a Collection Information Request to a Taxii 1.0 Service""" 27 | script = CollectionInformationClient11Script() 28 | script() 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /libtaxii/scripts/discovery_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | from libtaxii.scripts import TaxiiScript 7 | import libtaxii.messages_11 as tm11 8 | import libtaxii.taxii_default_query as tdq 9 | 10 | 11 | class DiscoveryClient11Script(TaxiiScript): 12 | parser_description = 'The TAXII 1.1 Discovery Client sends a Discovery Request message to a TAXII Server and ' \ 13 | 'prints out the Discovery Response message to standard out.' 14 | path = '/taxii-discovery-service' 15 | 16 | def create_request_message(self, args): 17 | return tm11.DiscoveryRequest(message_id=tm11.generate_message_id()) 18 | 19 | 20 | def main(): 21 | script = DiscoveryClient11Script() 22 | script() 23 | 24 | if __name__ == "__main__": 25 | main() 26 | -------------------------------------------------------------------------------- /libtaxii/scripts/discovery_client_10.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | from libtaxii.scripts import TaxiiScript 7 | import libtaxii.messages_10 as tm10 8 | from libtaxii.common import generate_message_id 9 | from libtaxii.constants import * 10 | 11 | class DiscoveryClient10Script(TaxiiScript): 12 | taxii_version = VID_TAXII_XML_10 13 | parser_description = 'The TAXII 1.0 Discovery Client sends a Discovery Request message to a TAXII Server and ' \ 14 | 'prints out the Discovery Response message to standard out.' 15 | path = '/taxii-discovery-service' 16 | 17 | def create_request_message(self, args): 18 | return tm10.DiscoveryRequest(message_id=generate_message_id()) 19 | 20 | 21 | def main(): 22 | script = DiscoveryClient10Script() 23 | script() 24 | 25 | if __name__ == "__main__": 26 | main() 27 | -------------------------------------------------------------------------------- /libtaxii/scripts/feed_information_client_10.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | import libtaxii.messages_10 as tm10 7 | from libtaxii.scripts import TaxiiScript 8 | from libtaxii.constants import VID_TAXII_XML_10 9 | 10 | 11 | class FeedInformationClient10Script(TaxiiScript): 12 | """Feed Information Request Client""" 13 | taxii_version = VID_TAXII_XML_10 14 | parser_description = \ 15 | 'The TAXII 1.0 Feed Information Client sends a Feed Information ' \ 16 | 'Request message to a TAXII Server and prints the Feed Information ' \ 17 | 'Response message to standard out.' 18 | 19 | path = '/taxii-data' 20 | 21 | def create_request_message(self, args): 22 | message_id = tm10.generate_message_id() 23 | return tm10.FeedInformationRequest(message_id) 24 | 25 | 26 | def main(): 27 | """Send a Feed Information Request to a Taxii 1.0 Service""" 28 | script = FeedInformationClient10Script() 29 | script() 30 | 31 | if __name__ == "__main__": 32 | main() 33 | -------------------------------------------------------------------------------- /libtaxii/scripts/fulfillment_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | import libtaxii.messages_11 as tm11 7 | from libtaxii.scripts import TaxiiScript, add_poll_response_args 8 | from libtaxii.constants import * 9 | 10 | 11 | class FulfillmentClient11Script(TaxiiScript): 12 | parser_description = 'The TAXII 1.1 Poll Fulfillment Client sends a Poll Fulfillment Request to a TAXII Server ' \ 13 | 'and prints out the Poll Response to standard out, saving the Content Blocks to disk (' \ 14 | 'depending on command line arguments).' 15 | path = '/taxii-data' 16 | 17 | def get_arg_parser(self, *args, **kwargs): 18 | parser = super(FulfillmentClient11Script, self).get_arg_parser(*args, **kwargs) 19 | parser.add_argument("--collection", 20 | dest="collection", 21 | default="default", 22 | help="Data Collection that this Fulfillment request applies to. Defaults to 'default'.") 23 | parser.add_argument("--result-id", 24 | dest="result_id", 25 | required=True, help="The result_id being requested.") 26 | parser.add_argument("--result-part-number", 27 | dest="result_part_number", 28 | type=int, 29 | default=1, 30 | help="The part number being requested. Defaults to '1'.") 31 | add_poll_response_args(parser) 32 | return parser 33 | 34 | def create_request_message(self, args): 35 | poll_fulf_req = tm11.PollFulfillmentRequest(message_id=tm11.generate_message_id(), 36 | collection_name=args.collection, 37 | result_id=args.result_id, 38 | result_part_number=args.result_part_number) 39 | return poll_fulf_req 40 | 41 | def handle_response(self, response, args): 42 | super(FulfillmentClient11Script, self).handle_response(response, args) 43 | if response.message_type == MSG_POLL_RESPONSE: 44 | if response.more: 45 | print("This response has More=True, to request additional parts, use the following command:") 46 | print(" fulfillment_client --collection %s --result-id %s --result-part-number %s\r\n" % \ 47 | (response.collection_name, response.result_id, response.result_part_number + 1)) 48 | self.write_cbs_from_poll_response_11(response, dest_dir=args.dest_dir, write_type_=args.write_type) 49 | 50 | 51 | def main(): 52 | script = FulfillmentClient11Script() 53 | script() 54 | 55 | if __name__ == "__main__": 56 | main() 57 | -------------------------------------------------------------------------------- /libtaxii/scripts/inbox_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | from libtaxii.scripts import TaxiiScript 7 | import libtaxii.messages_11 as tm11 8 | from libtaxii.constants import * 9 | 10 | class InboxClient11Script(TaxiiScript): 11 | parser_description = 'The TAXII 1.1 Inbox Client sends an Inbox Message to a TAXII Server and prints the ' \ 12 | 'Status Message response to standard out. The Inbox Client has a "built in" STIX document ' \ 13 | 'that is sent by default.' 14 | path = '/taxii-data' 15 | 16 | # http://stix.mitre.org/language/version1.1.1/#samples 17 | # http://stix.mitre.org/language/version1.1.1/stix_v1.0_samples_20130408.zip 18 | stix_watchlist = ''' 19 | 36 | 55 | 56 | Example watchlist that contains domain information. 57 | Indicators - Watchlist 58 | 59 | 60 | 62 | Domain Watchlist 63 | Sample domain Indicator for this watchlist 64 | 65 | 66 | 67 | malicious1.example.com##comma##malicious2.example.com##comma##malicious3.example.com 69 | 70 | 71 | 72 | 73 | 74 | ''' 75 | 76 | def get_arg_parser(self, *args, **kwargs): 77 | parser = super(InboxClient11Script, self).get_arg_parser(*args, **kwargs) 78 | parser.add_argument("--content-binding", 79 | dest="content_binding", 80 | default=CB_STIX_XML_111, 81 | help="Content binding of the Content Block to send. Defaults to %s" % CB_STIX_XML_111) 82 | parser.add_argument("--subtype", 83 | dest="subtype", 84 | default=None, 85 | help="The subtype of the Content Binding. Defaults to None") 86 | parser.add_argument("--content-file", 87 | dest="content_file", 88 | default=self.stix_watchlist, 89 | help="Content of the Content Block to send. Defaults to a STIX watchlist.") 90 | parser.add_argument("--dcn", 91 | dest="dcn", 92 | default=None, 93 | help="The Destination Collection Name for this Inbox Message. Defaults to None. " 94 | "This script only supports one Destination Collection Name") 95 | return parser 96 | 97 | def create_request_message(self, args): 98 | if args.content_file is self.stix_watchlist: 99 | data = self.stix_watchlist 100 | else: 101 | with open(args.content_file, 'r') as f: 102 | data = f.read() 103 | 104 | cb = tm11.ContentBlock(tm11.ContentBinding(args.content_binding), data) 105 | if args.subtype is not None: 106 | cb.content_binding.subtype_ids.append(args.subtype) 107 | 108 | inbox_message = tm11.InboxMessage(message_id=tm11.generate_message_id(), content_blocks=[cb]) 109 | if args.dcn: 110 | inbox_message.destination_collection_names.append(args.dcn) 111 | 112 | return inbox_message 113 | 114 | 115 | def main(): 116 | script = InboxClient11Script() 117 | script() 118 | 119 | if __name__ == "__main__": 120 | main() 121 | -------------------------------------------------------------------------------- /libtaxii/scripts/inbox_client_10.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | from libtaxii.scripts import TaxiiScript 7 | import libtaxii.messages_10 as tm10 8 | from libtaxii.constants import * 9 | from libtaxii.common import generate_message_id 10 | 11 | 12 | class InboxClient10Script(TaxiiScript): 13 | taxii_version = VID_TAXII_XML_10 14 | 15 | parser_description = 'The TAXII 1.0 Inbox Client sends an Inbox Message to a TAXII Server and prints the ' \ 16 | 'Status Message response to standard out. The Inbox Client has a "built in" STIX document ' \ 17 | 'that is sent by default.' 18 | path = '/taxii-data' 19 | 20 | # http://stix.mitre.org/language/version1.1.1/#samples 21 | # http://stix.mitre.org/language/version1.1.1/stix_v1.0_samples_20130408.zip 22 | stix_watchlist = ''' 23 | 40 | 59 | 60 | Example watchlist that contains domain information. 61 | Indicators - Watchlist 62 | 63 | 64 | 66 | Domain Watchlist 67 | Sample domain Indicator for this watchlist 68 | 69 | 70 | 71 | malicious1.example.com##comma##malicious2.example.com##comma##malicious3.example.com 73 | 74 | 75 | 76 | 77 | 78 | ''' 79 | 80 | def get_arg_parser(self, *args, **kwargs): 81 | parser = super(InboxClient10Script, self).get_arg_parser(*args, **kwargs) 82 | parser.add_argument("--content-binding", 83 | dest="content_binding", 84 | default=CB_STIX_XML_111, 85 | help="Content binding of the Content Block to send. Defaults to %s" % CB_STIX_XML_111) 86 | parser.add_argument("--subtype", 87 | dest="subtype", 88 | default=None, 89 | help="The subtype of the Content Binding. Defaults to None") 90 | parser.add_argument("--content-file", 91 | dest="content_file", 92 | default=self.stix_watchlist, 93 | help="Content of the Content Block to send. Defaults to a STIX watchlist.") 94 | return parser 95 | 96 | def create_request_message(self, args): 97 | if args.content_file is self.stix_watchlist: 98 | data = self.stix_watchlist 99 | else: 100 | with open(args.content_file, 'r') as f: 101 | data = f.read() 102 | 103 | cb = tm10.ContentBlock(args.content_binding, data) 104 | if args.subtype is not None: 105 | cb.content_binding.subtype_ids.append(args.subtype) 106 | 107 | inbox_message = tm10.InboxMessage(message_id=generate_message_id(), content_blocks=[cb]) 108 | 109 | return inbox_message 110 | 111 | 112 | def main(): 113 | script = InboxClient10Script() 114 | script() 115 | 116 | if __name__ == "__main__": 117 | main() 118 | -------------------------------------------------------------------------------- /libtaxii/scripts/poll_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | from libtaxii.scripts import TaxiiScript, add_poll_response_args 7 | import libtaxii.messages_11 as tm11 8 | import os 9 | import sys 10 | import dateutil.parser 11 | import datetime 12 | from libtaxii.common import gen_filename 13 | from libtaxii.constants import * 14 | 15 | 16 | class PollClient11Script(TaxiiScript): 17 | parser_description = ('The TAXII 1.1 Poll Client sends a Poll Request to a TAXII Poll Service then, depending on' 18 | ' the provided command line arguments, writes the Content Blocks in the response to disk.' 19 | ' Various options for the Poll Request can be set on the command line.') 20 | path = '/taxii-data' 21 | 22 | def get_arg_parser(self, *args, **kwargs): 23 | parser = super(PollClient11Script, self).get_arg_parser(*args, **kwargs) 24 | parser.add_argument("--collection", 25 | dest="collection", 26 | default="default", 27 | help="Data Collection to poll. Defaults to 'default'.") 28 | parser.add_argument("--begin-timestamp", 29 | dest="begin_ts", 30 | default=None, 31 | help="The begin timestamp (format: YYYY-MM-DDTHH:MM:SS.ssssss+/-hh:mm) " 32 | "for the poll request. Defaults to None.") 33 | parser.add_argument("--end-timestamp", 34 | dest="end_ts", 35 | default=None, 36 | help="The end timestamp (format: YYYY-MM-DDTHH:MM:SS.ssssss+/-hh:mm) " 37 | "for the poll request. Defaults to None.") 38 | parser.add_argument("--subscription-id", 39 | dest="subscription_id", 40 | default=None, 41 | help="The Subscription ID for the poll request. Defaults to None.") 42 | add_poll_response_args(parser) 43 | return parser 44 | 45 | def create_request_message(self, args): 46 | try: 47 | if args.begin_ts: 48 | begin_ts = dateutil.parser.parse(args.begin_ts) 49 | if not begin_ts.tzinfo: 50 | raise ValueError 51 | else: 52 | begin_ts = None 53 | 54 | if args.end_ts: 55 | end_ts = dateutil.parser.parse(args.end_ts) 56 | if not end_ts.tzinfo: 57 | raise ValueError 58 | else: 59 | end_ts = None 60 | except ValueError: 61 | print("Unable to parse timestamp value. Timestamp should include both date and time " 62 | "information along with a timezone or UTC offset (e.g., YYYY-MM-DDTHH:MM:SS.ssssss+/-hh:mm). " 63 | "Aborting poll.") 64 | sys.exit() 65 | 66 | create_kwargs = {'message_id': tm11.generate_message_id(), 67 | 'collection_name': args.collection, 68 | 'exclusive_begin_timestamp_label': begin_ts, 69 | 'inclusive_end_timestamp_label': end_ts} 70 | 71 | if args.subscription_id: 72 | create_kwargs['subscription_id'] = args.subscription_id 73 | else: 74 | create_kwargs['poll_parameters'] = tm11.PollRequest.PollParameters() 75 | poll_req = tm11.PollRequest(**create_kwargs) 76 | return poll_req 77 | 78 | def handle_response(self, response, args): 79 | super(PollClient11Script, self).handle_response(response, args) 80 | 81 | if response.message_type == MSG_POLL_RESPONSE: 82 | if response.more: 83 | print("This response has More=True, to request additional parts, use the following command:") 84 | print(" fulfillment_client --collection %s --result-id %s --result-part-number %s\r\n" % 85 | (response.collection_name, response.result_id, response.result_part_number + 1)) 86 | 87 | self.write_cbs_from_poll_response_11(response, dest_dir=args.dest_dir, write_type_=args.write_type) 88 | 89 | 90 | def main(): 91 | script = PollClient11Script() 92 | script() 93 | 94 | 95 | if __name__ == "__main__": 96 | main() 97 | -------------------------------------------------------------------------------- /libtaxii/scripts/poll_client_10.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | from libtaxii.scripts import TaxiiScript, add_poll_response_args 7 | import libtaxii.messages_10 as tm10 8 | import dateutil.parser 9 | import datetime 10 | import sys 11 | import os 12 | from libtaxii.common import gen_filename 13 | from libtaxii.constants import * 14 | 15 | 16 | class PollClient10Script(TaxiiScript): 17 | taxii_version = VID_TAXII_XML_10 18 | parser_description = ('The TAXII 1.0 Poll Client sends a Poll Request message to a TAXII Server and then' 19 | 'prints the Poll Response message to standard out, saving the Content Blocks to disk (' 20 | 'depending on the command line arguments).') 21 | path = '/taxii-data' 22 | 23 | def get_arg_parser(self, *args, **kwargs): 24 | parser = super(PollClient10Script, self).get_arg_parser(*args, **kwargs) 25 | parser.add_argument("--feed", 26 | dest="feed", 27 | default="default", 28 | help="Data Collection to poll. Defaults to 'default'.") 29 | parser.add_argument("--begin-timestamp", 30 | dest="begin_ts", 31 | default=None, 32 | help="The begin timestamp (format: YYYY-MM-DDTHH:MM:SS.ssssss+/-hh:mm) for the poll " 33 | "request. Defaults to None.") 34 | parser.add_argument("--end-timestamp", 35 | dest="end_ts", 36 | default=None, 37 | help="The end timestamp (format: YYYY-MM-DDTHH:MM:SS.ssssss+/-hh:mm) for the poll request. " 38 | "Defaults to None.") 39 | parser.add_argument("--subscription-id", 40 | dest="subs_id", 41 | default=None, 42 | help="The subscription ID to use. Defaults to None") 43 | add_poll_response_args(parser) 44 | return parser 45 | 46 | def create_request_message(self, args): 47 | try: 48 | if args.begin_ts: 49 | begin_ts = dateutil.parser.parse(args.begin_ts) 50 | if not begin_ts.tzinfo: 51 | raise ValueError 52 | else: 53 | begin_ts = None 54 | 55 | if args.end_ts: 56 | end_ts = dateutil.parser.parse(args.end_ts) 57 | if not end_ts.tzinfo: 58 | raise ValueError 59 | else: 60 | end_ts = None 61 | except ValueError: 62 | print("Unable to parse timestamp value. Timestamp should include both date and time information along " 63 | "with a timezone or UTC offset (e.g., YYYY-MM-DDTHH:MM:SS.ssssss+/-hh:mm). Aborting poll.") 64 | sys.exit() 65 | 66 | poll_req = tm10.PollRequest(message_id=tm10.generate_message_id(), 67 | feed_name=args.feed, 68 | exclusive_begin_timestamp_label=begin_ts, 69 | inclusive_end_timestamp_label=end_ts, 70 | subscription_id=args.subs_id) 71 | return poll_req 72 | 73 | def handle_response(self, response, args): 74 | super(PollClient10Script, self).handle_response(response, args) 75 | if response.message_type == tm10.MSG_POLL_RESPONSE: 76 | self.write_cbs_from_poll_response_10(response, dest_dir=args.dest_dir, write_type_=args.write_type) 77 | 78 | 79 | def main(): 80 | script = PollClient10Script() 81 | script() 82 | 83 | 84 | if __name__ == "__main__": 85 | main() 86 | -------------------------------------------------------------------------------- /libtaxii/scripts/query_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | import libtaxii.taxii_default_query as tdq 7 | from libtaxii.constants import * 8 | from .poll_client import PollClient11Script 9 | 10 | 11 | class QueryClient11Script(PollClient11Script): 12 | parser_description = "The TAXII 1.1 Query Client sends a Poll Request message to a TAXII Server (queries are " \ 13 | "contained in Poll Request messages) and prints out the resulting Poll Response message" \ 14 | "to standard out. The Content Blocks are saved to disk (depending on the command line" \ 15 | "options)." 16 | path = '/taxii-data' 17 | 18 | def get_arg_parser(self, *args, **kwargs): 19 | parser = super(QueryClient11Script, self).get_arg_parser(*args, **kwargs) 20 | parser.add_argument("--tev", 21 | dest="tev", 22 | default=CB_STIX_XML_111, 23 | help="Indicate which Targeting Expression Vocabulary is being used. Defaults to " 24 | "STIX XML 1.1.1") 25 | parser.add_argument("--target", 26 | dest="target", 27 | default="**/@id", 28 | help="The targeting expression to use. Defaults to **/@id (Any id, anywhere).") 29 | parser.add_argument("--rel", 30 | dest="relationship", 31 | default="equals", 32 | help="The relationship to use (e.g., equals). Defaults to equals.") 33 | parser.add_argument("--cm", 34 | dest="capability_module", 35 | default=CM_CORE, 36 | help="The capability module to use. Defaults to CORE.") 37 | # Parameters - optional depending on what relationship is chosen 38 | parser.add_argument("--value", 39 | dest=tdq.P_VALUE, 40 | default=None, 41 | help="The value to look for. Required (or not) and allowed values depend " 42 | "on the relationship.") 43 | parser.add_argument("--match-type", 44 | dest=tdq.P_MATCH_TYPE, 45 | default='case_insensitive_string', 46 | choices=['case_sensitive_string', 'case_insensitive_string', 'number'], 47 | help="The match type. Required (or not) and allowed values depend on the relationship. " 48 | "Defaults to \'case_insensitive_string\'") 49 | parser.add_argument("--case-sensitive", 50 | dest=tdq.P_CASE_SENSITIVE, 51 | default=None, 52 | choices=[True, False], 53 | help="Whether the match is case sensitive. Required (or not) and allowed values " 54 | "depend on the relationship. Defaults to \'None\'") 55 | return parser 56 | 57 | def create_request_message(self, args): 58 | msg = super(QueryClient11Script, self).create_request_message(args) 59 | if args.subscription_id is not None: 60 | return msg # Query goes in Poll Parameters, which can't be specified with a subscription 61 | 62 | capability_module = tdq.capability_modules.get(args.capability_module, None) 63 | if capability_module is None: 64 | raise ValueError("Unknown Capability Module specified: %s" % args.capability_module) 65 | 66 | relationship = capability_module.relationships.get(args.relationship, None) 67 | if relationship is None: 68 | raise ValueError("Unknown Relationship: %s" % args.relationship) 69 | 70 | params = {} 71 | 72 | for parameter in tdq.P_NAMES: 73 | param_obj = relationship.parameters.get(parameter, None) # Will either be a parameter object or None 74 | param_value = getattr(args, parameter) # Will either be a value or None 75 | 76 | if param_obj and not param_value: 77 | raise ValueError('The parameter "%s" is needed and was not specified. Specify using --%s ' % 78 | (parameter, parameter.replace('_', '-'))) 79 | if param_value and not param_obj: 80 | raise ValueError('The parameter %s was specified and is not needed' % parameter) 81 | 82 | if param_obj: 83 | param_obj.verify(param_value) 84 | params[parameter] = param_value 85 | 86 | test = tdq.Test(capability_id=capability_module.capability_module_id, 87 | relationship=relationship.name, 88 | parameters=params) 89 | 90 | criterion = tdq.Criterion(target=args.target, test=test) 91 | criteria = tdq.Criteria(operator=OP_AND, criterion=[criterion]) 92 | q = tdq.DefaultQuery(args.tev, criteria) 93 | msg.poll_parameters.query = q 94 | 95 | return msg 96 | 97 | 98 | 99 | def main(): 100 | script = QueryClient11Script() 101 | script() 102 | 103 | if __name__ == "__main__": 104 | main() 105 | -------------------------------------------------------------------------------- /libtaxii/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TAXIIProject/libtaxii/41f413357f4aaaaf99e37152558bb7464dbf5768/libtaxii/test/__init__.py -------------------------------------------------------------------------------- /libtaxii/test/argument_parser_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | 4 | import libtaxii.scripts.poll_client as pc11 5 | 6 | 7 | class ArgumentParserTests(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.module_path = os.path.dirname(__file__) 11 | 12 | def test_valid_argument_passing_poll_client11(self): 13 | script = pc11.PollClient11Script() 14 | arg_parse = script.get_arg_parser(script.parser_description, path=script.path) 15 | namespace = arg_parse.parse_args( 16 | [ 17 | "--from-file", os.path.join(self.module_path, "data", "configuration.ini") 18 | ] 19 | ) 20 | self.assertEqual(namespace.url, "http://hailataxii2.com:80") 21 | self.assertEqual(namespace.path, "/taxii-data") 22 | self.assertEqual(namespace.port, 80) 23 | self.assertEqual(namespace.password, "myS3crEtp@asswOrd!") 24 | 25 | def test_argument_override_poll_client11(self): 26 | script = pc11.PollClient11Script() 27 | arg_parse = script.get_arg_parser(script.parser_description, path=script.path) 28 | namespace = arg_parse.parse_args( 29 | [ 30 | "-u", "http://hailataxii.com:80", 31 | "--from-file", os.path.join(self.module_path, "data", "configuration.ini") 32 | ] 33 | ) 34 | self.assertEqual(namespace.url, "http://hailataxii2.com:80") # note the argument was overwritten 35 | self.assertEqual(namespace.path, "/taxii-data") 36 | self.assertEqual(namespace.port, 80) 37 | self.assertEqual(namespace.password, "myS3crEtp@asswOrd!") 38 | -------------------------------------------------------------------------------- /libtaxii/test/clients_test.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, The MITRE Corporation 2 | # For license information, see the LICENSE.txt file 3 | 4 | import datetime 5 | 6 | from dateutil.tz import tzutc 7 | 8 | import libtaxii as t 9 | import libtaxii.clients as tc 10 | import libtaxii.messages as tm 11 | from libtaxii.constants import * 12 | 13 | # NOTE: This cannot currently be run as a unit test because it needs a server 14 | # to connect to. 15 | def client_example(): 16 | 17 | # Create the TAXII HTTPS Client 18 | client = tc.HttpClient() 19 | 20 | # Uncomment to use HTTPS 21 | client.set_use_https(True) 22 | 23 | # Uncomment to use basic authentication 24 | # client.set_auth_type(tc.HttpClient.AUTH_BASIC) 25 | # client.set_auth_credentials({'username':'some_username', 'password':'some_password'}) 26 | 27 | # Uncomment to use certificate-based authentication 28 | client.set_auth_type(tc.HttpClient.AUTH_CERT) 29 | client.set_auth_credentials({'key_file': 'keyfile', 30 | 'cert_file': 'certfile'}) 31 | 32 | # Uncomment to set a proxy 33 | # client.set_proxy(tc.HttpClient.PROXY_HTTP, 'http://proxy.company.com:80') 34 | 35 | # Create the poll request 36 | poll_request1 = tm.PollRequest(message_id=tm.generate_message_id(), feed_name='TheFeedToPoll') 37 | 38 | # Call without a proxy 39 | http_response = client.call_taxii_service2('hostname', '/poll_service_path/', VID_TAXII_XML_10, poll_request1.to_xml()) 40 | 41 | print(http_response.__class__.__name__) 42 | 43 | taxii_message = t.get_message_from_http_response(http_response, 44 | poll_request1.message_id) 45 | print((taxii_message.to_xml())) 46 | 47 | if __name__ == "__main__": 48 | client_example() 49 | -------------------------------------------------------------------------------- /libtaxii/test/data/configuration.ini: -------------------------------------------------------------------------------- 1 | [libtaxii] 2 | url = http://hailataxii2.com:80 3 | pass = myS3crEtp@asswOrd! 4 | -------------------------------------------------------------------------------- /libtaxii/test/input/1.1/Collection_Information_Response.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Warrgghghglble. 5 | 6 | 7 | So improve information. Much amaze. 8 | 9 | 10 | 11 | 12 | 13 | 14 | urn:taxii.mitre.org:protocol:http:1.0 15 | https://example.com/inbox/ 16 | urn:taxii.mitre.org:message:xml:1.1 17 | 18 | 19 | urn:taxii.mitre.org:protocol:http:1.0 20 | https://example.com/inbox/ 21 | urn:taxii.mitre.org:message:xml:1.1 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /libtaxii/test/input/1.1/Discovery_Response.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | urn:taxii.mitre.org:protocol:http:1.0 6 | http://taxiitest.mitre.org/services/inbox/default 7 | urn:taxii.mitre.org:message:xml:1.1 8 | 9 | 10 | 11 | urn:taxii.mitre.org:protocol:http:1.0 12 | http://taxiitest.mitre.org/services/poll 13 | urn:taxii.mitre.org:message:xml:1.1 14 | 15 | 16 | urn:taxii.mitre.org:protocol:http:1.0 17 | http://taxiitest.mitre.org/services/discovery 18 | urn:taxii.mitre.org:message:xml:1.1 19 | 20 | 21 | -------------------------------------------------------------------------------- /libtaxii/test/input/1.1/Inbox_Message.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | collection1 4 | collection2 5 | Hello! 6 | 7 | SubsId021 8 | 2014-10-21T15:21:50.895-04:00 9 | 2014-10-21T15:21:50.895-04:00 10 | 11 | 22 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 2014-10-21T19:21:50.805Z 20 | Hullo! 21 | The quick brown fox jumped over the lazy dogs. 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /libtaxii/test/input/1.1/Subscription_Management_Request.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FULL 5 | 6 | 7 | 8 | 9 | ** 10 | 11 | Test value 12 | case_sensitive_string 13 | 14 | 15 | 16 | **/Description 17 | 18 | Tue Oct 21 15:34:33 EDT 2014 19 | 20 | 21 | 22 | 23 | ** 24 | 25 | Test value 26 | case_sensitive_string 27 | 28 | 29 | 30 | STIX_Package/Indicators/Indicator/@id 31 | 32 | [A-Z]* 33 | true 34 | 35 | 36 | 37 | **/Description 38 | 39 | Tue Oct 21 15:34:33 EDT 2014 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /libtaxii/test/input/1.1/Subscription_Management_Response.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hullo! 4 | 5 | Subs001 6 | 7 | COUNT_ONLY 8 | 9 | 10 | 11 | 12 | 13 | ** 14 | 15 | Test value 16 | case_sensitive_string 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | urn:taxii.mitre.org:protocol:https:1.0 25 | https://example.com/inboxAddress/ 26 | urn:taxii.mitre.org:message:xml:1.1 27 | 28 | 29 | urn:taxii.mitre.org:protocol:https:1.0 30 | https://example.com/poll1/ 31 | urn:taxii.mitre.org:message:xml:1.1 32 | 33 | 34 | urn:taxii.mitre.org:protocol:https:1.0 35 | https://example.com/poll2/ 36 | urn:taxii.mitre.org:message:xml:1.1 37 | 38 | 39 | urn:taxii.mitre.org:protocol:https:1.0 40 | https://example.com/poll3/ 41 | urn:taxii.mitre.org:message:xml:1.1 42 | 43 | 44 | 45 | Subs001 46 | 47 | COUNT_ONLY 48 | 49 | 50 | 51 | 52 | 53 | ** 54 | 55 | Test value 56 | case_sensitive_string 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | urn:taxii.mitre.org:protocol:https:1.0 65 | https://example.com/inboxAddress/ 66 | urn:taxii.mitre.org:message:xml:1.1 67 | 68 | 69 | 70 | Subs001 71 | 72 | 73 | -------------------------------------------------------------------------------- /libtaxii/test/output/1.1/readme.md: -------------------------------------------------------------------------------- 1 | Placeholder for Test output files. -------------------------------------------------------------------------------- /libtaxii/test/test_clients.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from lxml import etree 4 | 5 | from libtaxii.clients import VerifiableHTTPSConnection 6 | 7 | 8 | def test_connection(): 9 | # This is a basic test just to confirm that we pass the right arguments 10 | # in the right order to a (non-TAXII) HTTPS server 11 | 12 | conn = VerifiableHTTPSConnection("https://httpbin.org/", 443) 13 | -------------------------------------------------------------------------------- /libtaxii/test/test_xml_encoding.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from lxml import etree 4 | 5 | from libtaxii.common import parse_xml_string 6 | from libtaxii.constants import CB_STIX_XML_111 7 | from libtaxii.messages_10 import ContentBlock as ContentBlock10 8 | from libtaxii.messages_11 import get_message_from_xml, ContentBlock as ContentBlock11 9 | 10 | 11 | discovery_request_with_encoding_bytes = b""" 12 | 16 | """ 17 | 18 | discovery_request_with_encoding_unicode = """ 19 | 23 | """ 24 | 25 | stix_package_bytes = b""" 26 | 29 | 30 | Test Package 31 | 32 | 33 | """ 34 | 35 | stix_package_unicode = """ 36 | 39 | 40 | Test Package 41 | 42 | 43 | """ 44 | 45 | 46 | def test_parsing_byte_string_with_encoding(): 47 | x = parse_xml_string(b"""""") 48 | assert x.tag == "foo" 49 | 50 | 51 | def test_parsing_byte_string_without_encoding(): 52 | x = parse_xml_string(b"""""") 53 | assert x.tag == "bar" 54 | 55 | 56 | def test_parsing_unicode_string_with_encoding(): 57 | x = parse_xml_string("""""") 58 | assert x.tag == "baz" 59 | 60 | 61 | def test_parsing_unicode_string_without_encoding(): 62 | x = parse_xml_string("""""") 63 | assert x.tag == "quz" 64 | 65 | 66 | def test_get_xml_from_byte_string(): 67 | req = get_message_from_xml(discovery_request_with_encoding_bytes) 68 | 69 | assert req is not None 70 | assert req.message_id == "331bf15a-76a0-4e29-8444-6e986e514e29" 71 | 72 | 73 | def test_get_xml_from_unicode_string(): 74 | req = get_message_from_xml(discovery_request_with_encoding_unicode) 75 | 76 | assert req is not None 77 | assert req.message_id == "331bf15a-76a0-4e29-8444-6e986e514e29" 78 | 79 | 80 | def test_content_block_11_bytes(): 81 | content_block = ContentBlock11(CB_STIX_XML_111, stix_package_bytes) 82 | 83 | assert content_block.content_is_xml is True 84 | assert b"Indicator-ba1d406e-937c-414f-9231-6e1dbe64fe8b" in content_block.content 85 | 86 | 87 | def test_content_block_11_unicode(): 88 | content_block = ContentBlock11(CB_STIX_XML_111, stix_package_unicode) 89 | 90 | assert content_block.content_is_xml is True 91 | # Content is always in bytes 92 | assert b"Indicator-ba1d406e-937c-414f-9231-6e1dbe64fe8b" in content_block.content 93 | 94 | 95 | def test_content_block_10_bytes(): 96 | content_block = ContentBlock10(CB_STIX_XML_111, stix_package_bytes) 97 | 98 | assert content_block.content_is_xml is True 99 | assert isinstance(content_block._content, etree._Element) 100 | assert b"Indicator-ba1d406e-937c-414f-9231-6e1dbe64fe8b" in content_block.content 101 | 102 | 103 | def test_content_block_10_unicode(): 104 | content_block = ContentBlock10(CB_STIX_XML_111, stix_package_unicode) 105 | 106 | assert content_block.content_is_xml is True 107 | # Content is always in bytes 108 | assert isinstance(content_block._content, etree._Element) 109 | assert b"Indicator-ba1d406e-937c-414f-9231-6e1dbe64fe8b" in content_block.content 110 | -------------------------------------------------------------------------------- /libtaxii/test/to_text_11_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | import glob 7 | import os 8 | import libtaxii.messages_11 as tm11 9 | 10 | 11 | input_path = os.path.join('input', '1.1') 12 | output_path = os.path.join('output', '1.1') 13 | 14 | 15 | def main(): 16 | input_fns = glob.glob(os.path.join(input_path, '*.xml')) 17 | 18 | for input_fn in input_fns: 19 | with open(input_fn, 'r') as f: 20 | text = f.read() 21 | 22 | # parse the file to a TAXII message/object 23 | msg = tm11.get_message_from_xml(text) 24 | 25 | # create the output files 26 | basename = os.path.splitext(os.path.basename(input_fn))[0] 27 | 28 | # write XML and text to files. 29 | xml_out = os.path.join(output_path, basename + ".xml") 30 | with open(xml_out, 'w') as f: 31 | f.write(msg.to_xml(pretty_print=True)) 32 | 33 | txt_out = os.path.join(output_path, basename + ".txt") 34 | with open(txt_out, 'w') as f: 35 | f.write(msg.to_text()) 36 | 37 | 38 | if __name__ == '__main__': 39 | main() 40 | -------------------------------------------------------------------------------- /libtaxii/test/validation_test.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, The MITRE Corporation 2 | # For license information, see the LICENSE.txt file 3 | 4 | import unittest 5 | 6 | from libtaxii.validation import do_check, message_id_regex_10 7 | 8 | 9 | class ValidationTests(unittest.TestCase): 10 | 11 | def test_numeric_regex_valid(self): 12 | # This message ID is valid. It should not raise an exception 13 | do_check("12345", "Test Value", regex_tuple=message_id_regex_10) 14 | 15 | # See: https://github.com/TAXIIProject/libtaxii/issues/166 16 | def test_numeric_regex_invalid_end(self): 17 | # This message ID is not valid. 18 | args = ("12345abcd", "Message ID") 19 | kwargs = {'regex_tuple': message_id_regex_10} 20 | self.assertRaises(ValueError, do_check, *args, **kwargs) 21 | 22 | 23 | if __name__ == '__main__': 24 | unittest.main() 25 | -------------------------------------------------------------------------------- /libtaxii/validation.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, The MITRE Corporation 2 | # For license information, see the LICENSE.txt file 3 | 4 | """ 5 | Common data validation functions used across libtaxii 6 | """ 7 | 8 | 9 | import collections 10 | import re 11 | import datetime 12 | from lxml import etree 13 | import os 14 | 15 | from .common import (parse, parse_datetime_string) 16 | import six 17 | 18 | # General purpose helper methods # 19 | 20 | RegexTuple = collections.namedtuple('_RegexTuple', ['regex', 'title']) 21 | # URI regex per http://tools.ietf.org/html/rfc3986 22 | uri_regex = RegexTuple("(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?", "URI Format") 23 | message_id_regex_10 = RegexTuple("^[0-9]+$", "Numbers only") 24 | targeting_expression_regex = RegexTuple("^(@?\w+|\*{1,2})(/(@?\w+|\*{1,2}))*$", "Targeting Expression Syntax") 25 | 26 | _none_error = "%s is not allowed to be None and the provided value was None" 27 | _type_error = "%s must be of type %s. The incorrect value was of type %s" 28 | _regex_error = "%s must be a string conforming to %s. The incorrect value was: %s" 29 | _tuple_error = "%s must be one of %s. The incorrect value was %s" 30 | 31 | 32 | def do_check(var, varname, type=None, regex_tuple=None, value_tuple=None, can_be_none=False): 33 | """ 34 | Checks supplied var against all of the supplied checks using the following 35 | process: 36 | 37 | 1. If var is iterable, call this function for every item in the iterable object 38 | 2. If the var is none and can be none, return 39 | 3. If the var is none and cannot be none, raise ValueError 40 | 4. If a type is specified, and the var is not of the specified type, raise ValueError 41 | 5. If a regex is specified, and the var doesn't match the regex, raise ValueError 42 | 6. If a value_tuple is specified, and the var is not in the value_tuple, raise ValueError 43 | 44 | varname is used in the error messages 45 | 46 | """ 47 | 48 | if isinstance(var, list) or isinstance(var, set) or isinstance(var, tuple): 49 | 50 | x = 0 51 | for item in var: 52 | do_check(item, "%s[%s]" % (varname, x), type, regex_tuple, value_tuple, can_be_none) 53 | x = x + 1 54 | 55 | return 56 | 57 | if var is None and can_be_none: 58 | return 59 | 60 | if var is None and not can_be_none: 61 | raise ValueError(_none_error % varname) 62 | 63 | if type is not None: 64 | if not isinstance(var, type): 65 | bad_type = var.__class__.__name__ 66 | raise ValueError(_type_error % (varname, type, bad_type)) 67 | 68 | if regex_tuple is not None: 69 | if not isinstance(var, six.string_types): 70 | raise ValueError('%s was about to undergo a regex check, but is not of type basestring! Regex check was not performed' % (varname)) 71 | if re.match(regex_tuple.regex, var) is None: 72 | raise ValueError(_regex_error % (varname, regex_tuple.title, var)) 73 | 74 | if value_tuple is not None: 75 | if var not in value_tuple: 76 | raise ValueError(_tuple_error % (varname, value_tuple, var)) 77 | return 78 | 79 | 80 | def check_timestamp_label(timestamp_label, varname, can_be_none=False): 81 | """ 82 | Checks the timestamp_label to see if it is a valid timestamp label 83 | using the following process: 84 | 85 | 1. If the timestamp_label is None and is allowed to be None, Pass 86 | 2. If the timestamp_label is None and is not allowed to be None, Fail 87 | 3. If the timestamp_label arg is a string, convert to datetime 88 | 4. If the timestamp_label does not have a tzinfo attribute, Fail 89 | 5. Pass 90 | """ 91 | 92 | if timestamp_label is None and can_be_none: 93 | return 94 | 95 | if timestamp_label is None and not can_be_none: 96 | raise ValueError(_none_error % varname) 97 | 98 | if isinstance(timestamp_label, six.string_types): 99 | timestamp_label = parse_datetime_string(timestamp_label) 100 | 101 | do_check(timestamp_label, varname, type=datetime.datetime, can_be_none=can_be_none) 102 | 103 | if timestamp_label.tzinfo is None: 104 | raise ValueError('%s.tzinfo must not be None!' % varname) 105 | 106 | return timestamp_label 107 | 108 | 109 | class SchemaValidationResult(object): 110 | """A wrapper for the results of schema validation.""" 111 | 112 | def __init__(self, valid, error_log): 113 | self.valid = valid 114 | self.error_log = error_log 115 | 116 | _pkg_dir = os.path.dirname(__file__) 117 | 118 | #: Automatically-calculated path to the bundled TAXII 1.0 schema. 119 | TAXII_10_SCHEMA = os.path.join(_pkg_dir, "xsd", "TAXII_XMLMessageBinding_Schema.xsd") 120 | 121 | #: Automatically-calculated path to the bundled TAXII 1.1 schema. 122 | TAXII_11_SCHEMA = os.path.join(_pkg_dir, "xsd", "TAXII_XMLMessageBinding_Schema_11.xsd") 123 | 124 | 125 | class SchemaValidator(object): 126 | """ 127 | A helper class for TAXII Schema Validation. 128 | 129 | Example: 130 | See validate_etree(...) for an example how to use this class 131 | """ 132 | 133 | # Create class-level variables equal to module-level variables for 134 | # backwards-compatibility 135 | TAXII_10_SCHEMA = TAXII_10_SCHEMA 136 | TAXII_11_SCHEMA = TAXII_11_SCHEMA 137 | 138 | def __init__(self, schema_file): 139 | """ 140 | Args: 141 | schema_file (str) - The file location of the schema to 142 | validate against. Use the TAXII_11_SCHEMA 143 | and TAXII_10_SCHEMA constants to validate 144 | against TAXII 1.1 / 1.0. This schema file 145 | will be used when validate_file/string/etree 146 | is used. 147 | """ 148 | schema_doc = parse(schema_file, allow_file=True) 149 | self.xml_schema = etree.XMLSchema(schema_doc) 150 | 151 | def validate_file(self, file_location): 152 | """ 153 | A wrapper for validate_etree. Parses file_location, 154 | turns it into an etree, then calls validate_etree( ... ) 155 | """ 156 | 157 | with open(file_location, 'r') as f: 158 | etree_xml = parse(f, allow_file=True) 159 | 160 | return self.validate_etree(etree_xml) 161 | 162 | def validate_string(self, xml_string): 163 | """ 164 | A wrapper for validate_etree. Parses xml_string, 165 | turns it into an etree, then calls validate_etree( ... ) 166 | """ 167 | etree_xml = parse(xml_string, allow_file=False) 168 | return self.validate_etree(etree_xml) 169 | 170 | def validate_etree(self, etree_xml): 171 | """Validate an LXML etree with the specified schema_file. 172 | 173 | Args: 174 | etree_xml (etree): The XML to validate. 175 | schema_file (str): The schema file to validate against 176 | 177 | Returns: 178 | A SchemaValidationResult object 179 | 180 | Raises: 181 | lxml.etree.XMLSyntaxError: When the XML to be validated is not well formed 182 | 183 | Example: 184 | .. code-block:: python 185 | 186 | from libtaxii import messages_11 187 | from libtaxii.validation import SchemaValidator, TAXII_11_SCHEMA 188 | from lxml.etree import XMLSyntaxError 189 | 190 | sv = SchemaValidator(TAXII_11_SCHEMA) 191 | 192 | try: 193 | result = sv.validate_etree(some_etree) 194 | # Note that validate_string() and validate_file() can also be used 195 | except XMLSyntaxError: 196 | # Handle this exception, which occurs when 197 | # some_xml_string is not valid XML (e.g., 'foo') 198 | 199 | if not result.valid: 200 | for error in result.error_log: 201 | print error 202 | sys.exit(1) 203 | 204 | # At this point, the XML is schema valid 205 | do_something(some_xml_string) 206 | """ 207 | valid = self.xml_schema.validate(etree_xml) 208 | return SchemaValidationResult(valid, self.xml_schema.error_log) 209 | 210 | 211 | class TAXII10Validator(SchemaValidator): 212 | """A :py:class:`SchemaValidator` that uses the TAXII 1.0 Schemas""" 213 | 214 | def __init__(self): 215 | super(TAXII10Validator, self).__init__(TAXII_10_SCHEMA) 216 | 217 | 218 | class TAXII11Validator(SchemaValidator): 219 | """A :py:class:`SchemaValidator` that uses the TAXII 1.1 Schemas""" 220 | 221 | def __init__(self): 222 | super(TAXII11Validator, self).__init__(TAXII_11_SCHEMA) 223 | -------------------------------------------------------------------------------- /libtaxii/version.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, The MITRE Corporation 2 | # For license information, see the LICENSE.txt file 3 | 4 | __version__ = "1.1.119" 5 | -------------------------------------------------------------------------------- /libtaxii/xsd/TAXII_DefaultQuery_Schema.xsd: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | An XML element. Its body consists only of the indicated XML fields. 11 | 12 | 13 | 14 | 15 | 16 | An XML AnyURI indicating the Targeting Expression Vocabulary that will be used in this query’s Target field(s). 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | The element name indicates that this is a Targeting Expression Information field. Its body consists only of the indicated XML Fields. 25 | 26 | 27 | 28 | 29 | An XML AnyURI indicating a Capability Module. 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | An XML element. This element MUST consist only of the indicated XML fields. The subfields of this Criteria are the same as the parent Criteria (e.g., this is a recursive field), though they are not listed here. 39 | 40 | 41 | 42 | 43 | An XML element. This element MUST consist only of the indicated XML fields. 44 | 45 | 46 | 47 | 48 | 49 | An XML string containing an operator. Must be one of "AND" or "OR". 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | An XML string containing a Targeting Expression identifying the region of the record that is being targeted. 64 | 65 | 66 | 67 | 68 | An XML element containing the Test. This element MUST consist only of the indicated XML fields. 69 | 70 | 71 | 72 | 73 | 74 | An XML boolean indicating whether the result of the Criterion should be negated. The default value for this field is ‘false’. 75 | 76 | 77 | 78 | 79 | 80 | 82 | 83 | An XML string containing the value of this parameter. 84 | 85 | 86 | 87 | 88 | 89 | An XML AnyURI indicating the Capability Module used in this Test. 90 | 91 | 92 | 93 | 94 | An XML string containing the relationship. 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | An XML string containing the name of this parameter. 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | An XML String containing a Targeting Expression. At least one of Preferred_Scope or Allowed_Scope MUST be present (though this is not schema enforced). 114 | 115 | 116 | 117 | 118 | An XML String containing a Targeting Expression. At least one of Preferred_Scope or Allowed_Scope MUST be present (though this is not schema enforced). 119 | 120 | 121 | 122 | 123 | 124 | An XML AnyURI containing a Targeting Expression Vocabulary ID. 125 | 126 | 127 | 128 | 129 | 130 | The element name indicates that this is a TAXII Default Query. Its body MUST consist of only the indicated XML Fields. 131 | 132 | 133 | 134 | 135 | The element name indicates that this is a query information structure. Its body consists only of the indicated fields. 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /libtaxii/xsd/xmldsig-core-schema.xsd: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | ]> 11 | 12 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 216 | 217 | 218 | 219 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Sphinx==1.6.1 2 | sphinx_rtd_theme==0.2.4 3 | pytest==3.0.7 4 | tox==2.7.0 5 | bumpversion==0.5.3 6 | 7 | -e . 8 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.1.119 3 | tag_name = {new_version} 4 | commit = True 5 | tag = True 6 | 7 | [bumpversion:file:libtaxii/version.py] 8 | 9 | [metadata] 10 | license_file = LICENSE.txt 11 | 12 | [bdist_wheel] 13 | universal = True 14 | 15 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2017, The MITRE Corporation 4 | # For license information, see the LICENSE.txt file 5 | 6 | from io import open 7 | from os.path import abspath, dirname, join 8 | import sys 9 | 10 | from setuptools import setup, find_packages 11 | 12 | BASE_DIR = dirname(abspath(__file__)) 13 | VERSION_FILE = join(BASE_DIR, 'libtaxii', 'version.py') 14 | 15 | 16 | def get_version(): 17 | with open(VERSION_FILE) as f: 18 | for line in f.readlines(): 19 | if line.startswith("__version__"): 20 | version = line.split()[-1].strip('"') 21 | return version 22 | raise AttributeError("Package does not have a __version__") 23 | 24 | 25 | def get_long_description(): 26 | with open('README.rst', encoding='utf-8') as f: 27 | return f.read() 28 | 29 | 30 | py_maj, py_minor = sys.version_info[:2] 31 | if (py_maj, py_minor) < (2, 6) or (py_maj == 3 and py_minor < 3): 32 | raise Exception('libtaxii requires Python 2.6, 2.7 or 3.3+') 33 | 34 | 35 | install_requires = [ 36 | 'python-dateutil>=1.4.1', 37 | 'six>=1.9.0', 38 | ] 39 | 40 | # lxml has dropped support for Python 2.6, 3.3 after version 4.2.6 41 | if (py_maj, py_minor) == (2, 6) or (py_maj, py_minor) == (3, 3): 42 | install_requires.append('lxml>=2.2.3,<4.3.0') 43 | # lxml has dropped support for Python 2.6, 3.3, 3.4 after version 4.4.0 44 | elif (py_maj, py_minor) == (2, 6) or (py_maj, py_minor) == (3, 4): 45 | install_requires.append('lxml>=2.2.3,<4.4.0') 46 | else: 47 | install_requires.append('lxml>=2.2.3') 48 | 49 | if (py_maj, py_minor) < (2, 7): 50 | install_requires.append('argparse') 51 | 52 | setup( 53 | name='libtaxii', 54 | description='TAXII 1.X Library.', 55 | author='The MITRE Corporation', 56 | author_email='taxii@mitre.org', 57 | url='https://taxiiproject.github.io/', 58 | version=get_version(), 59 | packages=find_packages(), 60 | license='BSD', 61 | install_requires=install_requires, 62 | scripts=[ 63 | 'libtaxii/scripts/collection_information_client.py', 64 | 'libtaxii/scripts/discovery_client.py', 65 | 'libtaxii/scripts/fulfillment_client.py', 66 | 'libtaxii/scripts/inbox_client.py', 67 | 'libtaxii/scripts/inbox_client_10.py', 68 | 'libtaxii/scripts/poll_client.py', 69 | 'libtaxii/scripts/query_client.py', 70 | 'libtaxii/scripts/discovery_client_10.py', 71 | 'libtaxii/scripts/feed_information_client_10.py', 72 | 'libtaxii/scripts/poll_client_10.py', 73 | ], 74 | entry_points={ 75 | 'console_scripts': [ 76 | 'collection_information_client = libtaxii.scripts.collection_information_client:main', 77 | 'discovery_client = libtaxii.scripts.discovery_client:main', 78 | 'fulfillment_client = libtaxii.scripts.fulfillment_client:main', 79 | 'inbox_client = libtaxii.scripts.inbox_client:main', 80 | 'inbox_client_10 = libtaxii.scripts.inbox_client_10:main', 81 | 'poll_client = libtaxii.scripts.poll_client:main', 82 | 'query_client = libtaxii.scripts.query_client:main', 83 | 'discovery_client_10 = libtaxii.scripts.discovery_client_10:main', 84 | 'feed_information_client_10 = libtaxii.scripts.feed_information_client_10:main', 85 | 'poll_client_10 = libtaxii.scripts.poll_client_10:main', 86 | ], 87 | }, 88 | package_data={'libtaxii': ['xsd/*.xsd']}, 89 | long_description=get_long_description(), 90 | keywords='taxii libtaxii', 91 | classifiers=[ 92 | 'Development Status :: 5 - Production/Stable', 93 | 'Intended Audience :: Developers', 94 | 'License :: OSI Approved :: BSD License', 95 | 'Operating System :: OS Independent', 96 | 'Programming Language :: Python :: 2', 97 | 'Programming Language :: Python :: 2.6', 98 | 'Programming Language :: Python :: 2.7', 99 | 'Programming Language :: Python :: 3', 100 | 'Programming Language :: Python :: 3.3', 101 | 'Programming Language :: Python :: 3.4', 102 | 'Programming Language :: Python :: 3.5', 103 | 'Programming Language :: Python :: 3.6', 104 | 'Programming Language :: Python :: 3.7', 105 | 'Programming Language :: Python :: 3.8', 106 | ], 107 | project_urls={ 108 | 'Documentation': 'https://libtaxii.readthedocs.io/', 109 | 'Source Code': 'https://github.com/TAXIIProject/libtaxii/', 110 | 'Bug Tracker': 'https://github.com/TAXIIProject/libtaxii/issues/', 111 | }, 112 | ) 113 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py26, py27, rhel26, py34, py35, py36, py37, py38, docs, packaging 3 | 4 | [testenv] 5 | commands = 6 | pytest libtaxii 7 | sphinx-build -b doctest docs docs/_build/doctest 8 | sphinx-build -b html docs docs/_build/html 9 | deps = -rrequirements.txt 10 | 11 | [testenv:py26] 12 | commands = 13 | pytest libtaxii 14 | deps = pytest 15 | 16 | [testenv:rhel26] 17 | commands = 18 | pytest libtaxii 19 | deps = 20 | lxml==2.2.3 21 | python-dateutil==1.4.1 22 | six==1.9.0 23 | pytest 24 | 25 | [testenv:docs] 26 | commands = 27 | sphinx-build -b doctest docs docs/_build/doctest 28 | sphinx-build -b html docs docs/_build/html 29 | 30 | [testenv:packaging] 31 | deps = 32 | readme_renderer 33 | commands = 34 | python setup.py check -r -s 35 | 36 | [travis] 37 | python = 38 | 2.6: py26, rhel26 39 | 2.7: py27, docs, packaging 40 | 3.4: py34 41 | 3.5: py35 42 | 3.6: py36, docs, packaging 43 | 3.7: py37 44 | 3.8: py38 45 | --------------------------------------------------------------------------------