Advanced Python Functionality ============================= API Settings The Python SDK provides a few methods that affect interactions with the ThreatConnect API. The default settings should work in most cases. Logging ------- Example of Python SDK calling log-file and debug level: .. no-test .. code-block:: python # set a destination log path and logging level tc.set_tcl_file('log/tc.log', 'debug') # set the console logging level tc.set_tcl_console_level('critical') The Python SDK allows for the setting of the log-file location and debug level. The level on the console logging can be set as well. The default logging level for each is *critical*. Activity Log ------------ Enabling the Activity Log: .. no-test .. code-block:: python tc.set_activity_log(True) Disabling the Activity Log: .. no-test .. code-block:: python tc.set_activity_log(False) The ThreatConnect platform tracks all activity by users, including API accounts. When using the API to create thousands of Indicators, the Activity Log could generate excessive data. Activity tracing is **disabled by default** in the Python SDK. This feature can be turned on by calling the ``tc.set_activity_log()`` method, passing ``True`` as the argument. To disable tracking again during the same execution period, call the ``tc.set_activity_log()`` method once more, passing ``False``. API Request Timeout ------------------- This timeout value can be changed by passing the new timeout value, in seconds, to the ``tc.set_api_request_timeout()`` method. .. no-test .. code-block:: python tc.set_api_request_timeout(15) The Python SDK uses the Request module for communicating to the API. To prevent script from hanging on a bad socket, there is a timeout value set to **30 seconds by default**. API Retries/Sleep Period ------------------------ To change the default sleep period, call the ``set_api_sleep()`` method, passing an Integer for the number of seconds to sleep. .. no-test .. code-block:: python tc.set_api_retries(3) tc.set_api_sleep(30) If the Python SDK loses network connectivity to the API server, it will automatically retry the connection. The Python SDK has a **default of 5** retries, with a **default 59-second** sleep between retries before a RuntimeError is raised. To change the default retry value, call the ``set_api_retries()`` method, passing an Integer for the number of retries. .. note:: There is a maximum window of 5 minutes before the API will reject the HMAC (hash message authentication code) header due to a time mismatch. API Result Limit ---------------- The ThreatConnect API supports a **maximum of 10,000** results to be returned per API call during pagination. The Python SDK is configured for a **default of 200** results per API request. To change the default value, call the ``set_api_result_limit()`` method, passing an Integer between 1 and 10,000. The higher the number, the less API calls will be made, but in some cases, a lower number is required due to network limitations. .. no-test .. code-block:: python tc.set_api_result_limit(10,000) Proxies ------- Proxy Setting (No Authentication) .. no-test .. code-block:: python tc.set_proxies('10.10.10.10', 8443) Proxy Setting (Authentication Provided) .. no-test .. code-block:: python tc.set_proxies('10.10.10.10', 8443, 'proxy_user', 'password123') In some environments, the server running the Python SDK does not have the required Internet access to connect to the ThreatConnect API server. In these cases, a proxy server can be used to provide the required connectivity. To configure the Python SDK to use a proxy, call the ``set_proxies()`` method, providing the proxy-server IP address and port number as parameters. If the proxy server requires authentication, also provide the proxy user and proxy password as parameters. Advanced Filtering ------------------ A list of Filters can also be retrieved by using the ``filter1.filters`` property: .. no-test .. code-block:: python owner = 'Example Community' filter1 = adversary.add_filter() filter1.add_owner(owner) filter1.add_tag('Nation State') print(filter1) The Python SDK provides a powerful filtering system. When possible, it allows the user to set API Filters that limit the results returned from the API. If further filtering is required, there are Post Filters that allow the user to further refine the result set. The API Filters in a single Filter object will **OR** the results together, while the Post Filter will **AND** the results. Printing Filter Objects After creating a Filter object, the object can be printed, which will display the number of Request objects created, as well as the supported API Filters and Post Filters. A list of Filters can also be retrieved by using the ``filter1.filters`` property. filter1.filters Resulting Output +-----------------------+----------------------------------+ | Filter Object | | +=======================+==================================+ | **Filter Properties** | | +-----------------------+----------------------------------+ | Operator | FilterSetOperator.AND | +-----------------------+----------------------------------+ | Request Objects | 1 | +-----------------------+----------------------------------+ | **Owners** | | +-----------------------+----------------------------------+ | Owner | Example Community | +-----------------------+----------------------------------+ | **Filters** | | +-----------------------+----------------------------------+ | Filter | api filter by tag "Nation State" | +-----------------------+----------------------------------+ | **API Filters** | | +-----------------------+----------------------------------+ | Filter | ``add\_adversary\_id`` | +-----------------------+----------------------------------+ | Filter | ``add\_email\_id`` | +-----------------------+----------------------------------+ | Filter | ``add\_document\_id`` | +-----------------------+----------------------------------+ | Filter | ``add\_id`` | +-----------------------+----------------------------------+ | Filter | ``add\_incident\_id`` | +-----------------------+----------------------------------+ | Filter | ``add\_indicator`` | +-----------------------+----------------------------------+ | Filter | ``add\_security\_label`` | +-----------------------+----------------------------------+ | Filter | ``add\_signature\_id`` | +-----------------------+----------------------------------+ | Filter | ``add\_threat\_id`` | +-----------------------+----------------------------------+ | Filter | ``add\_tag`` | +-----------------------+----------------------------------+ | Filter | ``add\_victim\_id`` | +-----------------------+----------------------------------+ | **Post Filters** | | +-----------------------+----------------------------------+ | Filter | ``add\_pf\_name`` | +-----------------------+----------------------------------+ | Filter | ``add\_pf\_date\_added`` | +-----------------------+----------------------------------+ Filter Object Basics ^^^^^^^^^^^^^^^^^^^^ Python SDK Filter Object Basics example: .. no-test .. code-block:: python filter1 = adversary.add_filter() filter1.add_indicator('10.20.30.40') filter1.add_victim_id(10) filter1.add_tag('Nation State') Python SDK Post Filter Basics example: .. no-test .. code-block:: python from threatconnect.Config.FilterOperator import FilterOperator filter1 = adversary.add_filter() filter1.add_pf_name('Bad Guy') filter1.add_pf_date_added('2015-06-18T20:21:45-05:00', FilterOperator.GE) As mentioned above, an API Filter will join the results. In the example, the API results will contain any Adversary that has an Association with the Indicator *10.20.30.40*, **OR** an Association with the Victim with an ID of *10*, **OR** has the Tag of *Nation State*. As mentioned above, the Post Filters will intersect the results. In the example, the API results will only contain Adversaries that have the name *"Bad Guy"* **AND** have a date added of >= *2015-06-18T20:21:45-05:00*. Owner API Filter ^^^^^^^^^^^^^^^^ The Owner API Filter is a special Filter that is applied to all other API Filters in the same Filter Object. This is due to the fact that the API supports adding the Owner as a query String. See the formatted URI examples below. Python SDK formatted URI examples: .. code:: /v2/indicators/address/10.20.30.40?owner=Example+Community .. code:: /v2/groups/adversaries/5/indicators?owner=Example+Community Indicator-Type Filter ^^^^^^^^^^^^^^^^^^^^^ An Indicator Filter object supports passing an optional IndicatorType enum argument to the ``add_filter`` method. This will filter all results in the Filter object to the Indicator Type specified. +-----------------------------+ | Supported Indicator Types | +=============================+ | ADDRESSES | +-----------------------------+ | EMAIL\_ADDRESSES | +-----------------------------+ | FILES | +-----------------------------+ | HOSTS | +-----------------------------+ | URLS | +-----------------------------+ Python SDK example filtering on supported Indicator Types: .. no-test .. code-block:: python from threatconnect.Config.IndicatorType import IndicatorType filter1 = indicators.add_filter(IndicatorType.ADDRESSES) filter1 = indicators.add_filter(IndicatorType.EMAIL_ADDRESSES) filter1 = indicators.add_filter(IndicatorType.FILES) filter1 = indicators.add_filter(IndicatorType.HOSTS) filter1 = indicators.add_filter(IndicatorType.URLS) Modified Since API Filter ^^^^^^^^^^^^^^^^^^^^^^^^^ Python SDK Modified Since API Filter: .. no-test .. code-block:: python from datetime import datetime modified_since = (datetime.isoformat(datetime(2015, 6, 17))) + 'Z' indicators.set_modified_since(modified_since) The **Modified Since** Filter applies to the entire Indicators Container but can only be used on **base** Indicator searches (e.g., ``/v2/indicators``). If a Filter on **modified since** is required on a different Indicator search, there is a Post Filter for **modified since** that works on all Indicator result sets. Multiple Filter Objects ^^^^^^^^^^^^^^^^^^^^^^^ Python SDK Multiple Filter Objects example: .. code-block:: python from threatconnect.Config.FilterOperator import FilterSetOperator from threatconnect.Config.IndicatorType import IndicatorType # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) owner = 'Example Community' indicators = tc.indicators() try: filter1 = indicators.add_filter() filter1.add_owner(owner) filter1.add_security_label('TLP Red') except AttributeError as e: print(e) sys.exit(1) try: filter2 = indicators.add_filter() filter2.add_owner(owner) filter2.add_filter_operator(FilterSetOperator.AND) filter2.add_threat_id(38) except AttributeError as e: print(e) sys.exit(1) try: filter3 = indicators.add_filter(IndicatorType.ADDRESSES) filter3.add_owner(owner) filter3.add_filter_operator(FilterSetOperator.OR) filter3.add_tag('EXAMPLE') except AttributeError as e: print(e) sys.exit(1) # add code here The Python SDK supports adding multiple Filter objects to a Resource Container. A **filter\_operator** allows a user to configure the results sets of the separate Filter objects to be **JOINED** or **INTERSECTED**. No **filter\_operator** is required on the first Filter object added. Each subsequent Filter object can be joined (FilterSetOperator.OR) or intersected (FilterSetOperator.AND). Manual API Calls ---------------- The Python SDK supports a manual way to access the API by allowing the creation of a ``RequestObject()`` and submitting these objects to the ``api_request()`` method. The returned result will be a **Python Requests** object containing the HTTP Status Code, Response Headers, and API Results. Retrieving Indicators ^^^^^^^^^^^^^^^^^^^^^ The example below demonstrates how to create a ``RequestObject`` that will retrieve all Indicators from a specified Owner: .. code-block:: python import json from threatconnect.RequestObject import RequestObject # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) owner = 'Example Community' # instantiate Request Object ro = RequestObject() # set http method for Request Object ro.set_http_method('GET') # set the owner ro.set_owner(owner) # OPTIONAL # set the Owner-Allowed flag to specify whether or not this API call supports owners ro.set_owner_allowed(True) # set the Pagination flag to specify whether or not this API call supports pagination ro.set_resource_pagination(True) # set the URI (uniform resource identifier) for the request ro.set_request_uri('/v2/indicators') # trigger the request and store the response as results results = tc.api_request(ro) if results.headers['content-type'] == 'application/json': data = results.json() print(json.dumps(data, indent=4)) Downloading Document Contents ----------------------------- The example below demonstrates how to create a ``RequestObject`` that will retrieve the contents of a document stored as a Document Resource in ThreatConnect. .. code-block:: python from threatconnect.RequestObject import RequestObject # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) owner = 'Example Community' # instantiate Request Object ro = RequestObject() # set http method for Request Object ro.set_http_method('GET') # set the owner ro.set_owner(owner) # OPTIONAL # set the Owner-Allowed flag to specify whether or not this API call supports owners ro.set_owner_allowed(True) # set the Pagination flag to specify whether or not this API call supports pagination ro.set_resource_pagination(False) # set the URI (uniform resource identifier) for the request ro.set_request_uri('/v2/groups/documents/19/download') # trigger the request and store the response as results results = tc.api_request(ro) if results.headers['content-type'] == 'application/octet-stream': file_contents = results.content # print the Document's content print(file_contents) Creating and Uploading Documents -------------------------------- The example below demonstrates how to create a ``RequestObject`` that will create a Document Resource in ThreatConnect and upload content into this Resource. .. code-block:: python import json from threatconnect.RequestObject import RequestObject # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) owner = 'Example Community' # instantiate Request Object ro = RequestObject() # set http method for Request Object ro.set_http_method('POST') # set the body of the request body = {'name': 'Raw Upload Example', 'fileName': 'raw_example.txt'} ro.set_body(json.dumps(body)) # set the content type of the request ro.set_content_type('application/json') # set the owner ro.set_owner(owner) # OPTIONAL # set the Owner-Allowed flag to specify whether or not this API call supports owners ro.set_owner_allowed(True) # set the Pagination flag to specify whether or not this API call supports pagination ro.set_resource_pagination(False) # set the URI (uniform resource identifier) for the request ro.set_request_uri('/v2/groups/documents') print(ro) # trigger the request and store the response as results results = tc.api_request(ro) if results.headers['content-type'] == 'application/json': data = results.json() print(json.dumps(data, indent=4)) # get the ID of the created document document_id = data['data']['document']['id'] # create another Request Object for uploading the document contents ro = RequestObject() ro.set_http_method('POST') # define the Request's body (this is the content that will be uploaded into the Document Resource in ThreatConnect) body = 'Raw upload example file Contents.' ro.set_body(body) ro.set_content_type('application/octet-stream') ro.set_owner(owner) ro.set_owner_allowed(True) ro.set_resource_pagination(False) # upload the Request's body into the Document Resource in ThreatConnect ro.set_request_uri('/v2/groups/documents/{0}/upload'.format(document_id)) # trigger the request to upload content into the Document Resource results = tc.api_request(ro) print('Status Code: {0}'.format(results.status_code)) Advanced Outputs Formats ------------------------ The Python SDK allows for a Resource to be returned in multiple standard formats. The SDK currently supports the following formats: * CEF (Common Event Format) * CSV (Comma-Separated Values) * JSON (JavaScript® Object Notation) * KeyVal (Key Value) * LEEF (Log Event Extended Format) CEF ^^^ Python SDK CEF Code Sample: .. code-block:: python # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) # instantiate Indicators object indicators = tc.indicators() owner = 'Example Community' try: filter1 = indicators.add_filter() filter1.add_owner(owner) filter1.add_tag('Nation State') except AttributeError as e: print(e) sys.exit(1) try: # retrieve the Indicators indicators.retrieve() except RuntimeError as e: print(e) sys.exit(1) # iterate through the Indicators for indicator in indicators: print(indicator.cef) print('') Python SDK Sample CEF Output: .. code:: text CEF:0|threatconnect|threatconnect|2|355999|TEST attribute #14|2.0|confidence="14" dateAdded="2015-06-21T10:40:33-05:00" dnsActive="None" hostName="www.badguy_014.com" lastModified="2015-06-21T10:40:33-05:00" ownerName="Example Community" type="None" weblink="https://tc.sumx.us/auth/indicators/details/host.xhtml?host\=www.badguy_014.com&owner\=Example+Community" whoisActive="None" The Python SDK provides the ``cef`` methods to output data structured in CEF, whose output is only supported on `Indicators <https://docs.threatconnect.com/en/latest/python/indicators/indicators.html#indicators>`__. The CEF-formatted data maps the ThreatConnect Resource properties to the standard fields, when possible, and then uses the extension feature to store non-standard properties. CSV ^^^ Python SDK CSV Code Sample: .. code-block:: python # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) # instantiate Indicators object indicators = tc.indicators() owner = 'Example Community' try: filter1 = indicators.add_filter() filter1.add_owner(owner) filter1.add_tag('Nation State') except AttributeError as e: print(e) sys.exit(1) try: # retrieve the Indicators indicators.retrieve() except RuntimeError as e: print(e) sys.exit(1) for indicator in indicators: print(indicator.csv_header) print(indicator.csv) print('') Python SDK Sample CSV Output: .. code:: text confidence,dateAdded,description,id,indicator,lastModified,ownerName,rating,type,weblink 14,2015-06-21T10:40:33-05:00,TEST attribute #14,355999,www.badguy.com,2015-06-21T10:40:33-05:00,Example Community,1.0,null,https://app.threatconnect.com/auth/indicators/details/host.xhtml?host=www.badguy.com&owner=Example+Community The Python SDK provides the ``csv`` and ``csv_header`` methods for CSV output, which are supported on Indicators as well as Group Resources (e.g., Adversaries, Documents, Emails, Incidents, Signatures and Threats) The ``csv_header`` method should normally be called once per result set. JSON ^^^^ Python SDK JSON Code Sample: .. code-block:: python # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) # instantiate Indicators object indicators = tc.indicators() owner = 'Example Community' try: filter1 = indicators.add_filter() filter1.add_owner(owner) filter1.add_tag('Nation State') except AttributeError as e: print(e) sys.exit(1) try: # retrieve the Indicators indicators.retrieve() except RuntimeError as e: print(e) sys.exit(1) # iterate through the Indicators for indicator in indicators: print(indicator.json) print('') Python SDK Sample JSON Output: .. code:: json { "confidence": 14, "dateAdded": "2015-06-21T10:40:33-05:00", "description": "TEST attribute #14", "dnsActive": null, "hostName": "www.badguy_014.com", "id": 355999, "lastModified": "2015-06-21T10:40:33-05:00", "ownerName": "Example Community", "rating": 1.0, "type": null, "weblink": "https://tc.sumx.us/auth/indicators/details/host.xhtml?host=www.badguy_014.com&owner=Example+Community", "whoisActive": null } | The Python SDK provides the ``json`` method for output in JSON, are supported on Indicators as well as Group Resources (e.g., Adversaries, Documents, Emails, Incidents, Signatures and Threats) | The fields in the output depend on the type of Resource that has been requested. Key Value ^^^^^^^^^ Python SDK Key Value Code Sample: .. code-block:: python # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) # instantiate Indicators object indicators = tc.indicators() owner = 'Example Community' try: filter1 = indicators.add_filter() filter1.add_owner(owner) filter1.add_tag('Nation State') except AttributeError as e: print(e) sys.exit(1) try: # retrieve the Indicators indicators.retrieve() except RuntimeError as e: print(e) sys.exit(1) # iterate through the Indicators for indicator in indicators: print(indicator.keyval) Sample Key/Value Output: .. code:: text confidence="14" dateAdded="2015-06-21T10:40:33-05:00" description="TEST attribute #14" dnsActive="None" hostName="www.badguy_014.com" id="355999" lastModified="2015-06-21T10:40:33-05:00" ownerName="Example Community" rating="1.0" type="None" weblink="https://tc.sumx.us/auth/indicators/details/host.xhtml?host=www.badguy_014.com&owner=Example+Community" whoisActive="None" The Python SDK provides the ``keyval`` method for output in the Key Value format, whose output is supported on Indicators as well as Group Resources (e.g., Adversaries, Documents, Emails, Incidents, Signatures and Threats) The fields in the output depend on the type of Resource that has been requested. LEEF ^^^^ Python SDK LEEF Code Sample: .. code-block:: python # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) # instantiate Indicators object indicators = tc.indicators() owner = 'Example Community' try: filter1 = indicators.add_filter() filter1.add_owner(owner) filter1.add_tag('Nation State') except AttributeError as e: print(e) sys.exit(1) try: # retrieve the Indicators indicators.retrieve() except RuntimeError as e: print(e) sys.exit(1) # iterate through the Indicators for indicator in indicators: print(indicator.leef) print('') Python SDK Sample LEEF Output: .. code:: text LEEF:0|threatconnect|threatconnect|2|355999|confidence="14" devTime="2015-06-21T10:40:33-05:00" description="TEST attribute #14" dnsActive="None" hostName="www.badguy_014.com" id="355999" lastModified="2015-06-21T10:40:33-05:00" ownerName="Example Community" severity="1.0" type="None" weblink="https://tc.sumx.us/auth/indicators/details/host.xhtml?host=www.badguy_014.com&owner=Example+Community" whoisActive="None" The Python SDK provides the ``leef`` method to output data structured in LEEF, whose output is only supported on `Indicators <https://docs.threatconnect.com/en/latest/python/indicators/indicators.html#indicators>`__. The LEEF-formatted data maps the ThreatConnect Resource properties to the standard fields, when possible, and then uses the custom attribute feature to store non-standard properties. Indicator Type Override ----------------------- The ``add()`` method on the ``tc.indicators()`` object allows the user to bypass the automatic Indicator identification and validation check by specifying the IndicatorType: .. code-block:: python from threatconnect.Config.IndicatorType import IndicatorType # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) # instantiate Indicators object indicators = tc.indicators() owner = 'Example Community' indicator = indicators.add('<indicator>', owner, IndicatorType.ADDRESSES) Regex Overrides --------------- Python SDK Regex Code Sample .. code-block:: python import re from threatconnect.Config.IndicatorType import IndicatorType # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) # instantiate Indicators object indicators = tc.indicators() # # override FILES Regex # md5_re = re.compile(r'^([a-fA-F\d]{32})$') sha1_re = re.compile(r'^([a-fA-F\d]{40})$') sha256_re = re.compile(r'^([a-fA-F\d]{64})$') tc.set_indicator_regex(IndicatorType.FILES, [md5_re, sha1_re, sha256_re]) # # override ADDRESSES Regex # ipv4_regex = re.compile('(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}' + '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)') ipv6_regex = re.compile('(S*([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}S*|S*(' + '[0-9a-fA-F]{1,4}:){1,7}:S*|S*([0-9a-fA-F]{1,4}:)' + '{1,6}:[0-9a-fA-F]{1,4}S*|S*([0-9a-fA-F]{1,4}:)' + '{1,5}(:[0-9a-fA-F]{1,4}){1,2}S*|S*([0-9a-fA-F]' + '{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}S*|S*(' + '[0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}S*' + '|S*([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4})' + '{1,5}S*|S*[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4})' + '{1,6})S*|S*:((:[0-9a-fA-F]{1,4}){1,7}|:)S*|::(ffff' + '(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}' + '[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[' + '0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[' + '0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[' + '0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))') tc.set_indicator_regex(IndicatorType.ADDRESSES, [ipv4_regex, ipv6_regex]) # # override HOSTS Regex # host_re = re.compile(r'\b((?:(?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+(?i)(?!exe|php|dll|doc' \ '|docx|txt|rtf|odt|xls|xlsx|ppt|pptx|bin|pcap|ioc|pdf|mdb|asp|html|xml|jpg|gif$|png' \ '|lnk|log|vbs|lco|bat|shell|quit|pdb|vbp|bdoda|bsspx|save|cpl|wav|tmp|close|ico|ini' \ '|sleep|run|dat$|scr|jar|jxr|apt|w32|css|js|xpi|class|apk|rar|zip|hlp|cpp|crl' \ '|cfg|cer|plg|lxdns|cgi|xn$)(?:xn--[a-zA-Z0-9]{2,22}|[a-zA-Z]{2,13}))(?:\s|$)') tc.set_indicator_regex(IndicatorType.HOSTS, host_re) indicator = indicators.add('new.domain.tld', owner) indicator.set_confidence(50) indicator.set_rating('2.0') try: # commit the Indicator indicator.commit() except RuntimeError as e: print('Error: {0!s}'.format(e)) sys.exit(1) The Python SDK provides the ``set_indicator_regex`` method which allows a user to override the baked-in Regular Expressions (Regexes) in the SDK with user defined compiled Regexes. The method takes an IndicatorType enum and either a single compiled Regex or a list of Regexes. If a list is provided each Regex will be checked for a match for that Indicator Type. Reporting --------- Stats Reporting ^^^^^^^^^^^^^^^ The ``tc.report.stats`` properties method provides an overview of the script results: .. code-block:: python # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) # instantiate Indicators object indicators = tc.indicators() owner = 'Example Community' filter1 = indicators.add_filter() filter1.add_owner(owner) filter1.add_tag('Nation State') try: # retrieve the Indicators indicators.retrieve() except RuntimeError as e: print('Error: {0}'.format(e)) sys.exit(1) else: print(tc.report.stats) Sample Report-Statistics Output: .. code:: text _Stats_ API Stats API Calls 32 Unfiltered Results 3 Filtered Results 3 Filters API Filters 1 Post Filters 0 Total Filters 1 HTTP Methods PUT 2 POST 11 DELETE 11 GET 8 Status Codes 200 21 201 11 Performance Stats Request Time 0:00:03.021702 Processing Time 0:00:00.014082 Run Time 0:00:03.035795 The Python SDK includes a reporting feature that provides a number of methods for reporting on the execution status of a script that uses the SDK. **Enabling Reporting** The basic data collection of the Reporting feature is always enabled, but the report-entry collection feature is disabled by default. To enable the report-entry collection feature, use the ``tc.report_enable()`` method. To disable reporting, use the ``tc.report_disable()`` method. **Statistics** The ``tc.report.stats`` properties method provides an overview of the script results. Failed Reports ^^^^^^^^^^^^^^ Python SDK failed reports example: .. no-test .. code-block:: python # iterate through the failures for fail in tc.report.failures: print(fail) print('') Sample Failed-Report Output: .. code:: text _Report Entry_ Properties Status Code : 404 Fail Msg : {"status":"Failure","message":"The requested resource was not found"} Description : api filter by incident id 708996 Resource Type : ResourceType.ADVERSARIES HTTP Settings HTTP Method GET Request URI /v2/groups/incidents/708996/groups/adversaries Request URL https://tc.sumx.us/api/v2/groups/incidents/708996/groups/adversaries?resultStart=0&resultLimit=500&createActivityLog=false Content Type None Body None Payload Payload {'resultStart': 0, 'resultLimit': 500, 'createActivityLog': 'false'} All API requests and Post Filters are stored as a report entry in the Reports object. Any request that does not receive a status code of 200, 201 or 202, is stored as a failed-report entry and can be retrieved with the ``tc.report.failures`` property method. This feature helps debug issues when receiving failures while communicating with the API. Other Reporting Features ^^^^^^^^^^^^^^^^^^^^^^^^ API Calls The number of API calls can be retrieved using the ``tc.report.api_calls`` property method of the Report object. Runtime The script execution time can be retrieved using the ``tc.report.runtime`` property method of the Report object. This method can be called anytime during the script execution to get the current runtime and at the end of the script to get the total runtime. Request Time The time spent on API requests can be retrieved using the ``tc.report.request_time`` property method of the Report object. Report Entries All report entries can be accessed via the Report generator. By iterating over ``tc.report``, each individual report entry will be returned. These report entries can be printed and the individual properties can be accessed. Gotchas ------- This section details some things to be aware of when using the Python SDK for advanced use-cases. Order is Important when Adding Attributes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you are adding attributes to an indicator using this SDK, the order in which the attributes are added can be important. This is true if one of the attributes may be improperly formatted, thus causing an API error. To illustrate this, consider the following code: .. no-test .. code-block:: python # replace the line below with the standard, TC script heading described here: # https://docs.threatconnect.com/en/latest/python/quick_start.html#standard-script-heading ... tc = ThreatConnect(api_access_id, api_secret_key, api_default_org, api_base_url) # instantiate Indicators object indicators = tc.indicators() owner = 'Example Community' # create a new file indicator indicator = indicators.add('a'*32, owner) # add a Description attribute indicator.add_attribute('Description', 'Test description') # add an ssdeep Hash attribute indicator.add_attribute('ssdeep Hash', '!!MALFORMED SSDEEP HASH!!') # add a Source attribute indicator.add_attribute('Source', 'Test source') # set the confidence rating for the indicator indicator.set_confidence(75) indicator.commit() We want to create a File Indicator, add three attributes (description, ssdeep hash, and source), and set the confidence rating. When ``indicator.commit()`` is called, it will follow these steps (the important sections are in bold): - Create the indicator - Add a description attribute - **Fail while trying to add the ssdeep Hash attribute** - **Will not add a source attribute** - Set the confidence rating The key point is that any attributes created *after* the creation of another attribute has failed will not be created. Thus, if you have an attribute that may be invalid, you should add other attributes first. Other operations like setting the confidence and threat ratings and adding tags will work properly after the creation of an attribute has failed.