├── README.md ├── datalake-pipeline-api ├── python │ ├── Pipfile │ ├── README.md │ └── Pipfile.lock └── README.md ├── oat-pipeline-api ├── python │ ├── Pipfile │ ├── README.md │ └── Pipfile.lock └── README.md ├── sandbox-submission ├── python │ ├── Pipfile │ ├── README.md │ ├── Pipfile.lock │ └── sandbox_submission.py └── README.md ├── check-incident-details ├── python │ ├── Pipfile │ ├── README.md │ ├── check_incident_details.py │ └── Pipfile.lock └── README.md ├── detection-and-response ├── python │ ├── Pipfile │ ├── README.md │ └── Pipfile.lock └── README.md ├── ioc-intelligence-sweeping ├── python │ ├── Pipfile │ ├── README.md │ ├── Pipfile.lock │ └── ioc_intelligence_sweeping.py └── README.md ├── v1-events-to-elasticsearch ├── python │ ├── Pipfile │ ├── README.md │ ├── Pipfile.lock │ └── v1_events_to_elasticsearch.py └── README.md ├── documentation-security-posture ├── python │ ├── Pipfile │ ├── security_posture.yaml │ ├── README.md │ └── documentation_security_posture.py └── README.md └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # tm-v1-api-cookbook 2 | This repository contains Trend Vision One API cookbook sample code. Please visit user case directories to find each sample code. 3 | -------------------------------------------------------------------------------- /datalake-pipeline-api/python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | 9 | 10 | [requires] 11 | python_version = "3.9" 12 | -------------------------------------------------------------------------------- /oat-pipeline-api/python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | 9 | [dev-packages] 10 | 11 | [requires] 12 | python_version = "3.9" 13 | -------------------------------------------------------------------------------- /sandbox-submission/python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | 9 | [dev-packages] 10 | 11 | [requires] 12 | python_version = "3.9" 13 | -------------------------------------------------------------------------------- /check-incident-details/python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | 9 | [dev-packages] 10 | 11 | [requires] 12 | python_version = "3.9" 13 | -------------------------------------------------------------------------------- /detection-and-response/python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | 9 | [dev-packages] 10 | 11 | [requires] 12 | python_version = "3.9" 13 | -------------------------------------------------------------------------------- /ioc-intelligence-sweeping/python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | 9 | [dev-packages] 10 | 11 | [requires] 12 | python_version = "3.9" 13 | -------------------------------------------------------------------------------- /v1-events-to-elasticsearch/python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | elasticsearch = "*" 9 | 10 | [dev-packages] 11 | 12 | [requires] 13 | python_version = "3.9" 14 | -------------------------------------------------------------------------------- /documentation-security-posture/python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | pandas = "*" 9 | openpyxl = "*" 10 | python-pptx = "*" 11 | PyYAML = "*" 12 | 13 | [dev-packages] 14 | 15 | [requires] 16 | python_version = "3.9" 17 | -------------------------------------------------------------------------------- /check-incident-details/README.md: -------------------------------------------------------------------------------- 1 | # Modify alert status after checking alert details 2 | This task retrieves data about incident-related Workbench alerts and then modifies the status of these alerts after investigation is completed. 3 | ```mermaid 4 | graph LR; 5 | s[Start] --> a1[Retrieve
Workbench alerts]; 6 | a1 --> a2[Parse
alert details]; 7 | a2 --> a3[Update
alert status]; 8 | a3 --> a4[List
alert details]; 9 | a4 --> e[End]; 10 | ``` 11 | 12 | ## Related APIs 13 | - [Get alerts list](https://automation.trendmicro.com/xdr/api-v3#tag/Workbench/paths/~1v3.0~1workbench~1alerts/get) 14 | - [Modify alert status](https://automation.trendmicro.com/xdr/api-v3#tag/Workbench/paths/~1v3.0~1workbench~1alerts~1{id}/patch) 15 | 16 | ## Required products 17 | - At least one Trend Micro product that connects to Trend Vision One 18 | 19 | ## Sample code 20 | - [Python](python/) 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Trend Micro Incorporated 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /documentation-security-posture/README.md: -------------------------------------------------------------------------------- 1 | # Create custom reports in XLSX or PPTX format using data from the Security Posture API 2 | This cookbook queries the Security Posture API once a day and creates custom reports in PPTX or XLSX format with the retrieved data. Each security metric is stored in a separate sheet inside XLSX workbooks or an individual slide in PPTX slides. 3 | ```mermaid 4 | graph LR; 5 | s[Start] --> cmd{Execute command}; 6 | cmd -- Get security posture information --> sp[Get
Security posture
information]; 7 | sp --> file{Does the
XLSX report exist?}; 8 | file -- Yes --> a[Append row to the
existing XLSX file]; 9 | file -- No --> c[Create new XLSX File]; 10 | cmd -- Generate PPTX report --> r[Read existing XLSX file]; 11 | r --> v[Create a new PPTX file
and insert charts]; 12 | c --> e[End]; 13 | a --> e[End]; 14 | v --> e[End]; 15 | ``` 16 | 17 | ## Related APIs 18 | - [Get security posture data](https://automation.trendmicro.com/xdr/api-beta#tag/Security-Posture/paths/~1beta~1xdr~1riskInsights~1securityPosture/get) 19 | 20 | ## Required products 21 | - At least one Trend Micro product that connects to Trend Vision One 22 | 23 | ## Sample code 24 | - [Python](python/) 25 | -------------------------------------------------------------------------------- /datalake-pipeline-api/README.md: -------------------------------------------------------------------------------- 1 | # Datalake Pipeline API Cookbook 2 | This sample demonstrates how to use the Trend Vision One API to manage data pipelines, retrieve data packages, and continuously capture data events. 3 | ```mermaid 4 | flowchart TD 5 | A[Start] --> C{Command Type} 6 | 7 | C -->|register| D[Register Data Pipeline] 8 | C -->|list| E[List Data Pipelines] 9 | C -->|get| F[Get Pipeline Details] 10 | C -->|update| G[Update Pipeline] 11 | C -->|delete| H[Delete Pipeline] 12 | C -->|list-packages| I[List Pipeline Packages] 13 | C -->|get-package| J[Get Single Package] 14 | C -->|continuous-get-packages| K[Start Continuous Capture] 15 | 16 | D --> V 17 | E --> V 18 | F --> V 19 | G --> V 20 | H --> V 21 | I --> V 22 | J --> V 23 | 24 | K --> Q[Initialize Capture Parameters] 25 | Q --> R[Wait for Interval] 26 | R --> S[Fetch Packages] 27 | S --> T[Save Packages to Bundle] 28 | T --> U{Continue Capturing?} 29 | U -->|Yes| R 30 | U -->|No| V[End] 31 | ``` 32 | 33 | ## Related APIs 34 | - [Data Pipeline Management API](https://automation.trendmicro.com/xdr/api-v3/#tag/Datalake-Pipeline) 35 | 36 | ## Required products 37 | - At least one product connected to Trend Vision One 38 | 39 | ## Sample code 40 | - [Python](python/) 41 | -------------------------------------------------------------------------------- /oat-pipeline-api/README.md: -------------------------------------------------------------------------------- 1 | # Observed Attack Techniques Data Pipeline 2 | This sample demonstrates how to use the Trend Vision One API to manage Observed Attack Techniques data pipelines, retrieve packages of Observed Attack Techniques data, and continuously capture Observed Attack Techniques events. 3 | ```mermaid 4 | flowchart TD 5 | A[Start] --> C{Command Type} 6 | 7 | C -->|register| D[Register Data Pipeline] 8 | C -->|list| E[List Data Pipelines] 9 | C -->|get| F[Get Pipeline Details] 10 | C -->|delete| G[Delete Pipeline] 11 | C -->|list-packages| H[List Pipeline Packages] 12 | C -->|get-package| I[Get Single Package] 13 | C -->|continuous-get-packages| J[Start Continuous Capture] 14 | 15 | D --> V 16 | E --> V 17 | F --> V 18 | G --> V 19 | H --> V 20 | I --> V 21 | 22 | J --> Q[Initialize Capture Parameters] 23 | Q --> R[Wait for Interval] 24 | R --> S[Fetch Packages] 25 | S --> T[Save Packages to Bundle] 26 | T --> U{Continue Capturing?} 27 | U -->|Yes| R 28 | U -->|No| V[End] 29 | ``` 30 | 31 | ## Related APIs 32 | - [Get Observed Attack Techniques events](https://automation.trendmicro.com/xdr/api-v3#tag/Observed-Attack-Techniques/paths/~1v3.0~1oat~1detections/get) 33 | 34 | ## Required products 35 | - At least one product connected to Trend Vision One 36 | 37 | ## Sample code 38 | - [Python](python/) 39 | -------------------------------------------------------------------------------- /v1-events-to-elasticsearch/README.md: -------------------------------------------------------------------------------- 1 | # Send Workbench alerts, audit logs, and other detection data to Elasticsearch 2 | This task retrieves Workbench alerts, Observed Attack Technique events, detections and audit logs to Elasticsearch. 3 | ```mermaid 4 | graph LR; 5 | s[Start] --> wb[Retrieve
Workbench alerts]; 6 | wb --> oat[Retrieve
Observed Attack Techniques
events]; 7 | oat --> opt{Do you need
other detection data?}; 8 | opt -- Yes --> d[Retrieve file and web
detection data]; 9 | d --> opt2{Do you need
audit logs?}; 10 | opt -- No --> opt2; 11 | opt2 -- Yes --> audit[Retrieve audit logs]; 12 | audit --> cd[Convert data to format
required for indexing]; 13 | opt2 -- No --> cd; 14 | cd --> ie[Index the data
in Elasticsearch]; 15 | ie --> e[End]; 16 | ``` 17 | 18 | ## Related APIs 19 | - [Get alerts list](https://automation.trendmicro.com/xdr/api-v3#tag/Workbench/paths/~1v3.0~1workbench~1alerts/get) 20 | - [Get Observed Attack Techniques events](https://automation.trendmicro.com/xdr/api-v3#tag/Observed-Attack-Techniques/paths/~1v3.0~1oat~1detections/get) 21 | - [Get detection data](https://automation.trendmicro.com/xdr/api-v3#tag/Search/paths/~1v3.0~1search~1detections/get) 22 | - [Get entries from audit logs](https://automation.trendmicro.com/xdr/api-v3#tag/Audit-Logs/paths/~1v3.0~1audit~1logs/get) 23 | 24 | ## Required products 25 | - At least one Trend Micro product that connects to Trend Vision One 26 | 27 | ## Sample code 28 | - [Python](python/) 29 | -------------------------------------------------------------------------------- /ioc-intelligence-sweeping/README.md: -------------------------------------------------------------------------------- 1 | # Perform IoC Sweeping from a CSV or STIX (2.x) file 2 | This task imports IoCs from STIX (2.x) or CSV files into a custom intelligence report, starts a sweeping task, and then checks for matched indicators. 3 | ```mermaid 4 | graph LR; 5 | s(Start)-->action_upload_file[Upload
CSV / STIX file]; 6 | action_upload_file-->action_trigger_sweeping[Trigger IoC sweeping
for uploaded file]; 7 | action_trigger_sweeping-->action_wait_sweeping[Wait for
sweeping completion]; 8 | action_wait_sweeping-->action_fetch_result[Fetch
sweeping result]; 9 | action_fetch_result-->check_sweeping_hit{Does sweeping
have any matched indicators?}; 10 | check_sweeping_hit--Yes-->action_download_result[Download
sweeping result]; 11 | action_download_result-->e(End); 12 | check_sweeping_hit--No-->e; 13 | ``` 14 | 15 | ## Related APIs 16 | - [Import STIX and CSV files as custom intelligence reports](https://automation.trendmicro.com/xdr/api-v3#tag/Intelligence-Reports/paths/~1v3.0~1threatintel~1intelligenceReports/post) 17 | - [Trigger sweeping task](https://automation.trendmicro.com/xdr/api-v3#tag/Intelligence-Reports/paths/~1v3.0~1threatintel~1intelligenceReports~1sweep/post) 18 | - [Get task results](https://automation.trendmicro.com/xdr/api-v3#tag/Intelligence-Reports/paths/~1v3.0~1threatintel~1tasks~1{id}/get) 19 | 20 | ## Required products 21 | - At least one of the following: Deep Security, Trend Cloud One - Workload Security, Trend Micro Apex One, Trend Micro Apex One (Mac), XDR Endpoint Sensor 22 | 23 | ## Sample code 24 | - [Python](python/) 25 | -------------------------------------------------------------------------------- /check-incident-details/python/README.md: -------------------------------------------------------------------------------- 1 | # Cookbook: "Modify alert status after checking alert details" 2 | 3 | ## System Requirements 4 | 5 | - Python 3.9 or later 6 | 7 | ## Environment Setup 8 | 9 | 1. Install `pipenv`. 10 | ```text 11 | $ pip install pipenv 12 | ``` 13 | 2. Create a virtual environment for installing packages and managing dependencies. 14 | ```text 15 | $ pipenv install 16 | ``` 17 | 3. Modify the settings in `check_incident_details.py` to match your environment. 18 | ```python 19 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 20 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 21 | V1_UA = os.environ.get('TMV1_UA', f'Trend Vision One API Cookbook ({os.path.basename(__file__)})') 22 | ``` 23 | Alternatively, you can set these as environment variables or script command parameters. 24 | 25 | ## Sample Script 26 | 27 | 1. Activate the virtual environment associated with your project. 28 | ```text 29 | $ pipenv shell 30 | ``` 31 | 2. Run a sample script with parameters. 32 | The following script retrieves information about all Workbench alerts from the last three days. 33 | ```text 34 | (python) $ python check_incident_details.py -d 3 35 | ``` 36 | 37 | ## Expected Results 38 | 39 | The following sample code retrieves Workbench alert details and writes the information to `stdout`. 40 | 41 | ```text 42 | 43 | Target Workbench alerts: 44 | [ 45 | // Workbench alert ID list 46 | ] 47 | 48 | Details for target Workbench alerts: 49 | [ 50 | { 51 | // Workbench details 52 | }, 53 | { 54 | // Workbench details 55 | }, 56 | ... 57 | ] 58 | ``` 59 | -------------------------------------------------------------------------------- /detection-and-response/python/README.md: -------------------------------------------------------------------------------- 1 | # Cookbook: "Take a response action on the highlighted object in a Workbench alert" 2 | 3 | ## System Requirements 4 | 5 | - Python 3.9 or later 6 | 7 | ## Environment Setup 8 | 9 | 1. Install `pipenv`. 10 | ```text 11 | $ pip install pipenv 12 | ``` 13 | 2. Create a virtual environment for installing packages and managing dependencies. 14 | ```text 15 | $ pipenv install 16 | ``` 17 | 3. Modify the settings in `detection_and_response.py` to match your environment. 18 | ```python 19 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 20 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 21 | V1_UA = os.environ.get('TMV1_UA', f'Trend Vision One API Cookbook ({os.path.basename(__file__)})') 22 | V1_WAIT_TASK_INTERVAL = int(os.environ.get('TMV1_WAIT_TASK_INTERVAL', 10)) 23 | V1_WAIT_TASK_RETRY = int(os.environ.get('TMV1_WAIT_TASK_RETRY', 12)) 24 | ``` 25 | Alternatively, you can set these as environment variables or script command parameters. 26 | 27 | ## Sample Script 28 | 29 | 1. Activate the virtual environment associated with your project. 30 | ```text 31 | $ pipenv shell 32 | ``` 33 | 2. Run a sample script with parameters. 34 | The following script executes detection and response commands for Workbench alerts that were received in the last three days. 35 | ```text 36 | (python) $ python detection_and_response.py -d 3 37 | ``` 38 | 39 | ## Expected Results 40 | 41 | The following sample code writes information about new Workbench alerts and response actions taken on affected entities to `stdout`. 42 | 43 | ```text 44 | Retrieved workbench alerts: 45 | Added suspicious objects: 46 | Tasks running: ; Waiting interval (seconds): ; Number of intervals: . 47 | Isolated endpoints: 48 | Tasks running: ; Waiting interval (seconds): ; Number of intervals: . 49 | Quarantined emails: 50 | Tasks running: ; Waiting interval (seconds): ; Number of intervals: . 51 | 52 | Alert ID: 53 | Object "" added to Suspicious Object List successfully. 54 | Endpoint "" isolated successfully. 55 | Message for "" quarantined successfully. 56 | ``` 57 | -------------------------------------------------------------------------------- /sandbox-submission/README.md: -------------------------------------------------------------------------------- 1 | # Submit object to Sandbox Analysis 2 | This task submits files or URLs to the sandbox and retrieves the analysis results if there are submissions available in the daily reserve. If the risk level of the submitted objects is is equal or higher to 'low', this task also downloads an analysis report. 3 | ```mermaid 4 | graph LR; 5 | s(Start)-->check_available_count{Are there submissions available
in the daily reserve?}; 6 | check_available_count--Yes-->check_submission_type{Submission
type}; 7 | check_available_count--No-->e(End); 8 | check_submission_type--File-->action_submit_file[Submit file]; 9 | check_submission_type--URL-->action_submit_url[Submit URL]; 10 | action_submit_file-->action_wait_complete[Wait for
analysis completion]; 11 | action_submit_url-->action_wait_complete; 12 | action_wait_complete-->check_task_status{Task
status}; 13 | check_task_status--Successful-->action_fetch_result[Get
analysis result]; 14 | check_task_status--Unsuccessful-->check_error_code{Error
code}; 15 | check_error_code--Unsupported-->e; 16 | check_error_code--Internal
Server Error-->check_submission_type; 17 | action_fetch_result-->check_risk_level{Risk
level}; 18 | check_risk_level--High / Medium / Low-->action_download_report[Download
analysis report]; 19 | check_risk_level--No risk-->e; 20 | action_download_report-->e; 21 | ``` 22 | 23 | ## Related APIs 24 | - [Get daily reserve](https://automation.trendmicro.com/xdr/api-v3#tag/Sandbox-Analysis/paths/~1v3.0~1sandbox~1submissionUsage/get) 25 | - [Submit file to sandbox](https://automation.trendmicro.com/xdr/api-v3#tag/Sandbox-Analysis/paths/~1v3.0~1sandbox~1files~1analyze/post) 26 | - [Submit URLs to sandbox](https://automation.trendmicro.com/xdr/api-v3#tag/Sandbox-Analysis/paths/~1v3.0~1sandbox~1urls~1analyze/post) 27 | - [Get submission status](https://automation.trendmicro.com/xdr/api-v3#tag/Sandbox-Analysis/paths/~1v3.0~1sandbox~1tasks~1{id}/get) 28 | - [Get analysis results](https://automation.trendmicro.com/xdr/api-v3#tag/Sandbox-Analysis/paths/~1v3.0~1sandbox~1analysisResults~1{id}/get) 29 | - [Download analysis results](https://automation.trendmicro.com/xdr/api-v3#tag/Sandbox-Analysis/paths/~1v3.0~1sandbox~1analysisResults~1{id}~1report/get) 30 | - [Download Investigation Package](https://automation.trendmicro.com/xdr/api-v3#tag/Sandbox-Analysis/paths/~1v3.0~1sandbox~1analysisResults~1{id}~1investigationPackage/get) 31 | 32 | ## Required products 33 | - None required 34 | 35 | ## Sample code 36 | - [Python](python/) 37 | -------------------------------------------------------------------------------- /ioc-intelligence-sweeping/python/README.md: -------------------------------------------------------------------------------- 1 | # Cookbook: "Perform IoC Sweeping from a CSV or STIX (2.x) file" 2 | 3 | ## System Requirements 4 | 5 | - Python 3.9 or later 6 | 7 | ## Environment Setup 8 | 9 | 1. Install `pipenv`. 10 | ```text 11 | $ pip install pipenv 12 | ``` 13 | 2. Create a virtual environment for installing packages and managing dependencies. 14 | ```text 15 | $ pipenv install 16 | ``` 17 | 3. Modify the settings in `ioc_intelligence_sweeping.py` to match your environment. 18 | ```python 19 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 20 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 21 | V1_UA = os.environ.get('TMV1_UA', f'Trend Vision One API Cookbook ({os.path.basename(__file__)})') 22 | V1_WAIT_TASK_INTERVAL = int(os.environ.get('TMV1_WAIT_TASK_INTERVAL', 10)) 23 | V1_WAIT_TASK_RETRY = int(os.environ.get('TMV1_WAIT_TASK_RETRY', 12)) 24 | ``` 25 | Alternatively, you can set these as environment variables or script command parameters. 26 | 27 | ## Sample Script 28 | 29 | 1. Activate the virtual environment associated with your project. 30 | ```text 31 | $ pipenv shell 32 | ``` 33 | 2. Run a sample script with parameters. 34 | The following script imports "stix.json" as "stix.json", and sets up "stix.json"'s file type to STIX. 35 | ```text 36 | (python) $ python intelligence_sweeping.py -n stix.json stix < stix.json 37 | (python) $ python intelligence_sweeping.py stix stix.json 38 | ``` 39 | The following script imports "reports.csv" as "reports.csv", sets up "reports.csv"'s file type to CSV, and creates the report "sample_report". 40 | ```text 41 | (python) $ python intelligence_sweeping.py -r sample_report -n reports.csv csv < reports.csv 42 | (python) $ python intelligence_sweeping.py -r sample_report csv reports.csv 43 | ``` 44 | 45 | 46 | ## Expected Results 47 | 48 | The following sample code imports IoCs from STIX or CSV file into a custom intelligence report, starts a sweeping task, downloads any matched indicators to "intelligence\_report\_sweep_\.json", and writes the information to `stdout`. 49 | 50 | ```text 51 | Tasks running: {number}; Waiting interval (seconds): {interval}; Number of intervals: {interval_count}. 52 | 53 | The sweeping task based on custom intelligence report "" has matched indicators. Sweeping result saved in "intelligence_report_sweep_.json". 54 | The sweeping task based on custom intelligence report "" does not have any matched indicators. 55 | ``` 56 | -------------------------------------------------------------------------------- /detection-and-response/README.md: -------------------------------------------------------------------------------- 1 | # Take a response action on the highlighted object in a Workbench alert 2 | This task identifies the highlighted object in a Workbench alert and then takes a response action on that object. 3 | ```mermaid 4 | graph LR; 5 | s(Start)-->action_list_alerts[Retrieve
new Workbench alerts]; 6 | action_list_alerts-->check_new_alerts{Are new alerts
available?}; 7 | check_new_alerts--Yes-->action_update_status[Update
alert status]; 8 | action_update_status-->check_indicator_type{Indicators'
type}; 9 | check_indicator_type--Supported
type-->action_register_iocs[Register IoCs as
suspicious objects]; 10 | action_register_iocs-->check_impact_scope{Impact
scope}; 11 | check_indicator_type--Others-->check_impact_scope; 12 | check_impact_scope--Email-->action_search_email[Search
email message details]; 13 | action_search_email-->action_quarantine_email[Create quarantine
email message task]; 14 | check_impact_scope--Endpoint-->check_supported_products{Supported
product}; 15 | check_supported_products--Yes-->action_query_endpoint[Query
endpoint information]; 16 | action_query_endpoint-->action_isolate_endpoint[Create isolate
endpoint task]; 17 | action_quarantine_email-->action_wait_complete[Wait for
response]; 18 | action_isolate_endpoint-->action_wait_complete; 19 | action_wait_complete-->action_add_note[Add
alert notes]; 20 | check_impact_scope--Others-->action_add_note; 21 | check_supported_products--No-->action_add_note; 22 | action_add_note-->e(End); 23 | check_new_alerts--No-->e; 24 | ``` 25 | 26 | ## Related APIs 27 | - [Get alerts list](https://automation.trendmicro.com/xdr/api-v3#tag/Workbench/paths/~1v3.0~1workbench~1alerts/get) 28 | - [Modify alert status](https://automation.trendmicro.com/xdr/api-v3#tag/Workbench/paths/~1v3.0~1workbench~1alerts~1{id}/patch) 29 | - [Get endpoint data](https://automation.trendmicro.com/xdr/api-v3#tag/Search/paths/~1v3.0~1eiqs~1endpoints/get) 30 | - [Isolate endpoints](https://automation.trendmicro.com/xdr/api-v3#tag/Endpoint/paths/~1v3.0~1response~1endpoints~1isolate/post) 31 | - [Get email activity data](https://automation.trendmicro.com/xdr/api-v3#tag/Search/paths/~1v3.0~1search~1emailActivities/get) 32 | - [Quarantine email message](https://automation.trendmicro.com/xdr/api-v3#tag/Email/paths/~1v3.0~1response~1emails~1quarantine/post) 33 | 34 | ## Required products 35 | - At least one of the following: Deep Security, Trend Cloud One - Workload Security, Trend Micro Apex One, Trend Micro Apex One (Mac), XDR Endpoint Sensor 36 | - Cloud App Security 37 | 38 | ## Sample code 39 | - [Python](python/) 40 | -------------------------------------------------------------------------------- /documentation-security-posture/python/security_posture.yaml: -------------------------------------------------------------------------------- 1 | sheet names: 2 | High Impact Risk Events Threat Detection: HIREs Threat Detection 3 | High Impact Risk Events Security Configuration: HIREs Security Configuration 4 | High Impact Risk Events System Configuration: HIREs System Configuration 5 | High Impact Risk Events Vulnerability Detection: HIREs Vulnerability Detection 6 | High Impact Risk Events Anomaly Detection: HIREs Anomaly Detection 7 | High Impact Risk Events Account Compromise: HIREs Account Compromise 8 | High Impact Risk Events Cloud App Activity: HIREs Cloud App ACTs 9 | High Impact Risk Events XDR Detection: HIREs XDR Detection 10 | High Impact Risk Events Predictive Analytics: HIREs Predictive Analytics 11 | Vulnerability Assessment Coverage Rate: VA Coverage Rate 12 | CVE Management Metrics Average Unpatched Days: CVE MM AVG. Unpatched Days 13 | CVE Management Metrics Vulnerable Endpoint Rate: CVE MM Vulnerable Endpoint Rate 14 | CVE Management Metrics Legacy OS Endpoint Count: CVE MM Legacy OS Endpoint Count 15 | CVE Management Metrics Mttp Days: CVE MM Mttp Days 16 | Exposure Status Cloud Asset Misconfiguration: ES Cloud Asset Misconfiguration 17 | Exposure Status Unexpected Internet Facing Interface: ES Unexpected INET Facing IF 18 | Exposure Status Insecure Host Connection: ES Insecure Host Connection 19 | Exposure Status Domain Account Misconfiguration: ES Domain Acct. Misconfig 20 | Security Configuration Status Endpoint Agent: SCS EP Agent 21 | Security Configuration Status Endpoint Agent Version: SCS EP Agent Version 22 | Security Configuration Status Endpoint Agent Feature Apex One Adoption Rate: SCS EP Agent A1 Adoption Rate 23 | Security Configuration Status Endpoint Agent Feature Apex One Saas Adoption Rate: SCS EPA A1 Saas Adoption Rate 24 | Security Configuration Status Endpoint Agent Feature Apex One On Premises Adoption Rate: SCS EPA A1 OnPre Adoption Rate 25 | Security Configuration Status Endpoint Agent Feature Cloud One Adoption Rate: SCS EP Agent C1 Adoption Rate 26 | Security Configuration Status Endpoint Agent Feature Standard Protection Adoption Rate: SCS EP Agent SP Adoption Rate 27 | Security Configuration Status Endpoint Agent Feature Deep Software Adoption Rate: SCS EP Agent DS Adoption Rate 28 | Security Configuration Status Endpoint Agent Feature Workload Adoption Rate: SCS EPA Workload Adoption Rate 29 | Security Configuration Status Endpoint Agent Feature Server Workload Protection Adoption Rate: SCS EP Agent SWP Adoption Rate 30 | Security Configuration Status Virtual Patching: SCS Virtual Patching 31 | Security Configuration Status Email Sensor Exchange: SCS Email Sensor Exchange 32 | Security Configuration Status Email Sensor Gmail: SCS Email Sensor Gmail 33 | Security Configuration Status Cloud Apps: SCS Cloud Apps 34 | -------------------------------------------------------------------------------- /sandbox-submission/python/README.md: -------------------------------------------------------------------------------- 1 | # Cookbook: "Submit object to Sandbox Analysis" 2 | 3 | ## System Requirements 4 | 5 | - Python 3.9 or later 6 | 7 | ## Environment Setup 8 | 9 | 1. Install `pipenv`. 10 | ```text 11 | $ pip install pipenv 12 | ``` 13 | 2. Create a virtual environment for installing packages and managing dependencies. 14 | ```text 15 | $ pipenv install 16 | ``` 17 | 3. Modify the settings in `sandbox-submission.py` to match your environment. 18 | ```python 19 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 20 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 21 | V1_UA = os.environ.get('TMV1_UA', f'Trend Vision One API Cookbook ({os.path.basename(__file__)})') 22 | V1_WAIT_TASK_INTERVAL = int(os.environ.get('TMV1_WAIT_TASK_INTERVAL', 10)) 23 | V1_WAIT_TASK_RETRY = int(os.environ.get('TMV1_WAIT_TASK_RETRY', 12)) 24 | V1_ANALYZE_INTERVAL = int(os.environ.get('TMV1_ANALYZE_INTERVAL', 300)) 25 | V1_ANALYZE_RETRY = int(os.environ.get('TMV1_ANALYZE_RETRY', 3)) 26 | ``` 27 | Alternatively, you can set these as environment variables or script command parameters. 28 | 29 | ## Sample Script 30 | 31 | 1. Activate the virtual environment associated with your project. 32 | ```text 33 | $ pipenv shell 34 | ``` 35 | 2. Run a sample script with parameters. 36 | The following script submits a password-protected file and the file's password to Sandbox Analysis. 37 | ```text 38 | (python) $ sandbox-submission.py file -n -p < 39 | (python) $ sandbox-submission.py file -p 40 | ``` 41 | The following script submits two URLs to Sandbox Analysis. 42 | ```text 43 | (python) $ sandbox-submission.py url 44 | ``` 45 | 46 | ## Expected Results 47 | 48 | The following sample code writes the submissions available in the daily reserve, how many objects are being sent to Sandbox Analysis, and the analysis results to `stdout`. 49 | 50 | ```text 51 | Submissions available: 52 | Submitting 1 file.../Submitting url(s)... 53 | Tasks running: ; Waiting interval (seconds): ; Number of intervals: . 54 | 55 | Analyzing: ""; Task status: succeeded; Risk level: ; Analysis report saved to: "sandbox_analysis_file_.pdf". 56 | Analyzing: ""; Task status: succeeded; Risk level: ; Analysis report saved to: "sandbox_analysis_url_.pdf". 57 | Analyzing: ""; Task status: succeeded; Risk level: . 58 | ``` 59 | The sample code also downloads an analysis report to "sandbox\_analysis\__\.json" if the risk level of the submitted objects is is equal or higher to 'low'. -------------------------------------------------------------------------------- /v1-events-to-elasticsearch/python/README.md: -------------------------------------------------------------------------------- 1 | # Cookbook: "Send Workbench alerts, audit logs, and other detection data to Elasticsearch" 2 | 3 | ## System Requirements 4 | 5 | - Python 3.9 or later 6 | - Elasticsearch 8.4.1 or later 7 | 8 | ## Environment Setup 9 | 10 | 1. Install `pipenv`. 11 | ```text 12 | $ pip install pipenv 13 | ``` 14 | 2. Create a virtual environment for installing packages and managing dependencies. 15 | ```text 16 | $ pipenv install 17 | ``` 18 | 3. Modify the settings in `v1_events_to_elasticsearch.py` to match your environment. 19 | ```python 20 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 21 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 22 | V1_UA = os.environ.get('TMV1_UA', f'Trend Vision One API Cookbook ({os.path.basename(__file__)})') 23 | ES_URL = os.environ.get('TMV1_ELASTICSEARCH_URL', 'http://localhost:9200') 24 | ES_INDEX_PREFIX = os.environ.get('TMV1_ELASTICSEARCH_INDEX_PREFIX', 'tmv1_') 25 | ES_USER = os.environ.get('TMV1_ELASTICSEARCH_USER') 26 | ES_PASSWORD = os.environ.get('TMV1_ELASTICSEARCH_PASSWORD') 27 | ES_CAFILE = os.environ.get('TMV1_ELASTICSEARCH_CAFILE') 28 | ES_CAPATH = os.environ.get('TMV1_ELASTICSEARCH_CAPATH') 29 | ES_CERTFILE = os.environ.get('TMV1_ELASTICSEARCH_CERTFILE') 30 | ES_KEYFILE = os.environ.get('TMV1_ELASTICSEARCH_KEYFILE') 31 | ``` 32 | Alternatively, you can set these as environment variables or script command parameters. 33 | 34 | ## Sample Script 35 | 36 | 1. Activate the virtual environment associated with your project. 37 | ```text 38 | $ pipenv shell 39 | ``` 40 | 2. Run a sample script with parameters. 41 | The following script sends data (Workbench alerts, Observed Attack Technique events, audit logs, and other detections) from the last five days to Elasticsearch. 42 | ```text 43 | (python) $ python v1_events_to_elasticsearch.py -d 5 -D -a 44 | ``` 45 | 46 | ## Expected Results 47 | 48 | The following sample code writes the number of retrieved Workbench alerts, Observed Attack Technique events, audit logs, and detections to `stdout`. 49 | 50 | ```text 51 | Retrieved workbench alerts: 52 | Retrieved Observed Attack Techniques events: 53 | Retrieved detections: 54 | Retrieved audit logs: 55 | ``` 56 | 57 | The sample code also indexes the data in Elasticsearch. You can perform the following actions. 58 | 59 | - Verify that the Trend Vision One indices exist (Management > Elasticsearch > Index Management). 60 | - tmv1\_workbench 61 | - tmv1\_observed\_techniques 62 | - tmv1\_detection 63 | - tmv1\_audit\_logs 64 | 65 | You can replace the prefix `tmv1_` with another lowercase string. 66 | 67 | - Check the following data fields: 68 | - All indices: The time field is "esBaseDateTime". 69 | - "workbench" index: A new field called "impactScope.\" exists. This is renamed from "impactScope.entityValue" to the "\" specified by the "entityType" field. 70 | - "workbench" index: A new field called "indicators.\" exists. This is renamed from "indicators.value" to the "\" specified by the "type" field combined with the the type name of the value. 71 | - "workbench" index: A new field called "severity" exists. This is renamed to "severityString". 72 | - "observed techniques" index: A new field called "filters.highlightedObjects.\" exists. This is renamed from "filters.highlightedObjects.value" to the "\" specified by the "type" field. 73 | 74 | The value of a "text" field is converted to string when necessary. 75 | 76 | - "observed techniques" index: A new field called "detail" exists. This is renamed to the "\" specified by the "source" field. 77 | -------------------------------------------------------------------------------- /oat-pipeline-api/python/README.md: -------------------------------------------------------------------------------- 1 | # Cookbook: Observed Attack Techniques Pipeline API 2 | 3 | ## System Requirements 4 | 5 | - Python 3.9 or later 6 | 7 | 8 | ## Environment Setup 9 | 10 | 1. Clone this repository or download the script and install dependencies using pipenv: 11 | ```bash 12 | # Install pipenv if you don't have it 13 | pip install pipenv 14 | ``` 15 | 16 | 2. Create a virtual environment for installing packages and managing dependencies. 17 | ```bash 18 | # Install the dependencies from Pipfile 19 | pipenv install 20 | 21 | # Activate the virtual environment 22 | pipenv shell 23 | ``` 24 | 25 | 3. Modify the settings in `oat_pipeline_api.py` to match your environment. 26 | ```python 27 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 28 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 29 | V1_UA = os.environ.get('TMV1_UA', f'Trend Vision One API Cookbook({os.path.basename(__file__)})') 30 | ``` 31 | ## Sample Script 32 | 1. The script provides a command-line interface for interacting with the Observed Attack Techniques Pipeline API. 33 | 34 | ```bash 35 | $ python oat_pipeline_api.py -u -f [OPTIONS] 36 | ``` 37 | 38 | 2. Example commands: 39 | The following examples demonstrate all available operations with the Observed Attack Techniques Pipeline API. 40 | ```bash 41 | # 1. Register a data pipeline 42 | python oat_pipeline_api.py register '["medium", "high", "critical"]' -d "My OAT pipeline" 43 | 44 | # 2. List all data pipelines 45 | python oat_pipeline_api.py list 46 | 47 | # 3. Get specific pipeline information 48 | python oat_pipeline_api.py get 49 | 50 | # 4. Update pipeline settings 51 | python oat_pipeline_api.py update '["low", "medium", "high"]' "Updated description" 52 | 53 | # 5. Delete pipeline(s) 54 | python oat_pipeline_api.py delete 55 | 56 | # 6. List available packages from a pipeline 57 | python oat_pipeline_api.py list-packages -s 2023-10-01T00:00:00Z -e 2023-10-01T23:59:59Z -t 500 58 | 59 | # 7. Get specific package content 60 | python oat_pipeline_api.py get-package 61 | 62 | # 8. Continuous package retrieval (runs until Ctrl+C) 63 | python oat_pipeline_api.py continuous-get-packages -v 180 -t 500 -b 100 64 | ``` 65 | 66 | ## Expected Results 67 | 68 | The script performs operations based on the command and outputs results to stdout. Additionally, it logs all operations to a log file. 69 | 70 | ```text 71 | # For get command: 72 | { 73 | "riskLevels": [ 74 | "medium", 75 | "low" 76 | ], 77 | "hasDetail": false, 78 | "registeredDateTime": "2023-10-17T08:18:49Z", 79 | "description": "test" 80 | } 81 | ``` 82 | ```text 83 | 84 | # For delete command: 85 | { 86 | "Responses": [ 87 | { 88 | "status": 204 89 | }, 90 | ] 91 | } 92 | ``` 93 | ```text 94 | # For list-packages command: 95 | { 96 | "items": [ 97 | { 98 | "id": "2022092911-91664a49-5284-400c-8e81-f2e8a8ed4322", 99 | "createdDateTime": "2023-10-12T02:15:23Z" 100 | }, 101 | ... 102 | ], 103 | "totalCount": 274, 104 | "count": 50, 105 | "requestedDateTime": "2023-10-17T09:11:57Z", 106 | "nextLink": "https://api.xdr.trendmicro.com/beta/xdr/oat/dataPipelines/2ba69db9-5111-40f6-9a39-653e1031ec56/packages?startDateTime=2023-10-12T02:00:00Z&endDateTime=2023-10-17T02:59:59Z&top=50&pageToken=1697185272.500556", 107 | "latestPackageCreatedDateTime": "2023-10-13T08:21:12Z" 108 | } 109 | ``` 110 | ```text 111 | # For continuous-get-packages command: 112 | start capturing..., next request is around: 2022-09-29T18:55:00Z 113 | - next request is around: 2022-09-29T18:58:00Z 114 | - next request is around: 2022-09-29T19:01:00Z 115 | - next request is around: 2022-09-29T19:04:00Z 116 | ^Cstop capturing 117 | ``` 118 | 119 | The script also includes retry logic for certain HTTP status codes (429, 502, 504) and will raise exceptions for other HTTP errors. -------------------------------------------------------------------------------- /datalake-pipeline-api/python/README.md: -------------------------------------------------------------------------------- 1 | # Cookbook: "Datalake Pipeline API" 2 | ## System Requirements 3 | - Python 3.9 or higher 4 | 5 | ## Environment Setup 6 | 7 | 1. Clone this repository or download the script and install dependencies using pipenv: 8 | 9 | ```bash 10 | # Install pipenv if you don't have it 11 | pip install pipenv 12 | ``` 13 | 2. Create a virtual environment for installing packages and managing dependencies. 14 | ```bash 15 | # Install the dependencies from Pipfile 16 | pipenv install 17 | 18 | # Activate the virtual environment 19 | pipenv shell 20 | ``` 21 | 3. Modify the settings in `datalake_pipeline_api.py` to match your environment. 22 | ```python 23 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 24 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 25 | V1_UA = os.environ.get('TMV1_UA', f'Trend Vision One API Cookbook({os.path.basename(__file__)})') 26 | ``` 27 | ## Sample Script 28 | 1. The script provides a command-line interface for interacting with the Datalake Pipeline API. 29 | 30 | ```bash 31 | python datalake_pipeline_api.py -u -f [OPTIONS] 32 | ``` 33 | 2. Example commands: 34 | The following examples demonstrate all available operations with the Datalake Pipeline API. 35 | ```bash 36 | # 1. Register (bind a data type to a pipeline) 37 | python datalake_pipeline_api.py register -t telemetry -d "My telemetry pipeline" -s endpoint -s email 38 | 39 | # 2. List all bound data pipelines 40 | python datalake_pipeline_api.py list 41 | 42 | # 3. Get specific pipeline information 43 | python datalake_pipeline_api.py get 44 | 45 | # 4. Update pipeline settings 46 | python datalake_pipeline_api.py update -t detection -d "Updated description" 47 | 48 | # 5. Delete (unbind) pipeline(s) 49 | python datalake_pipeline_api.py delete 50 | 51 | # 6. List available packages from a pipeline 52 | python datalake_pipeline_api.py list-packages -s 2023-10-01T00:00:00Z -e 2023-10-01T23:59:59Z 53 | 54 | # 7. Get specific package content 55 | python datalake_pipeline_api.py get-package 56 | 57 | # 8. Continuous package retrieval (runs until Ctrl+C) 58 | python datalake_pipeline_api.py continuous-get-packages -v 180 -t 500 59 | ``` 60 | ## Expected Results 61 | The script outputs results for each of the 8 available operations: 62 | 63 | ### 1. Register Command 64 | ```text 65 | request: register, args: type=telemetry, description=My telemetry pipeline 66 | resp=, trace-id=12345678-1234-1234-1234-123456789abc 67 | https://api.xdr.trendmicro.com/v3.0/datalake/dataPipelines/a1b2c3d4-e5f6-7890-abcd-ef1234567890 68 | ``` 69 | 70 | ### 2. List Command 71 | ```text 72 | request: list 73 | resp=, trace-id=12345678-1234-1234-1234-123456789abc 74 | { 75 | "items": [ 76 | { 77 | "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", 78 | "type": "telemetry", 79 | "subType": ["endpoint", "email"], 80 | "description": "My telemetry pipeline", 81 | "registeredDateTime": "2023-10-17T08:18:49Z" 82 | } 83 | ] 84 | } 85 | ``` 86 | 87 | ### 3. Get Command 88 | ```text 89 | request: get,args: pipeline_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890 90 | resp=, trace-id=12345678-1234-1234-1234-123456789abc 91 | { 92 | "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", 93 | "type": "telemetry", 94 | "subType": ["endpoint", "email"], 95 | "description": "My telemetry pipeline", 96 | "registeredDateTime": "2023-10-17T08:18:49Z" 97 | } 98 | ``` 99 | 100 | ### 4. Update Command 101 | ```text 102 | request: update, args: pipeline_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890, type=detection, sub_type=None, description=Updated description 103 | resp=, trace-id=12345678-1234-1234-1234-123456789abc 104 | Status Code: 200, Response Text: {"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","type":"detection","description":"Updated description"}, trace-id=12345678-1234-1234-1234-123456789abc 105 | { 106 | "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", 107 | "type": "detection", 108 | "description": "Updated description", 109 | "registeredDateTime": "2023-10-17T08:18:49Z" 110 | } 111 | ``` 112 | 113 | ### 5. Delete Command 114 | ```text 115 | request: delete, args: pipeline_id_list=['a1b2c3d4-e5f6-7890-abcd-ef1234567890'] 116 | resp=, trace-id=12345678-1234-1234-1234-123456789abc 117 | [ 118 | { 119 | "status": 200, 120 | "message": "Pipeline(s) successfully deleted" 121 | } 122 | ] 123 | ``` 124 | 125 | ### 6. List-packages Command 126 | ```text 127 | request: list-packages, args: pipeline_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890, start_datetime=2023-10-01T00:00:00Z, end_datetime=2023-10-01T23:59:59Z, top=500, 128 | resp=, trace-id=12345678-1234-1234-1234-123456789abc 129 | { 130 | "items": [ 131 | { 132 | "id": "20231001-package-id-123", 133 | "createdDateTime": "2023-10-01T12:30:45Z" 134 | }, 135 | { 136 | "id": "20231001-package-id-456", 137 | "createdDateTime": "2023-10-01T14:15:30Z" 138 | } 139 | ], 140 | "totalCount": 2, 141 | "requestedDateTime": "2023-10-17T09:11:57Z" 142 | } 143 | ``` 144 | 145 | ### 7. Get-package Command 146 | ```text 147 | request: get-package, args: pipeline_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890, package_id=20231001-package-id-123, output=/path/to/output.gz 148 | resp=, trace-id=12345678-1234-1234-1234-123456789abc 149 | ``` 150 | *Package content is written to the specified output file* 151 | 152 | ### 8. Continuous-get-packages Command 153 | ```text 154 | start capturing..., next request is around: 2023-10-17T12:03:00Z 155 | resp=, trace-id=12345678-1234-1234-1234-123456789abc 156 | - saved package: 20231017-package-id-789 157 | - saved package: 20231017-package-id-abc 158 | captured packages until: 2023-10-17T12:03:00Z 159 | - captured package count: 2 160 | - bundle path: /output/bundle-2023-10-17T12:00:00Z-2023-10-17T12:03:00Z.gz 161 | - next request is around: 2023-10-17T12:06:00Z 162 | ^Cstop capturing 163 | Stop capturing 164 | ``` -------------------------------------------------------------------------------- /check-incident-details/python/check_incident_details.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import datetime 3 | import json 4 | import os 5 | 6 | import requests 7 | 8 | # Setting variables 9 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 10 | # Specify the correct domain name for your region in V1_URL 11 | # ref: https://automation.trendmicro.com/xdr/Guides/Regional-Domains 12 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 13 | # This value is used for User-Agent header in API requests. So you can 14 | # customize this value to describe your company name, integration tool name, 15 | # and so on as you like. 16 | # default: "Trend Vision One API Cookbook ({script_name})" 17 | V1_UA = os.environ.get('TMV1_UA', 'Trend Vision One API Cookbook ' 18 | f'({os.path.basename(__file__)})') 19 | 20 | 21 | def is_aware_datetime(d): 22 | return (d.tzinfo is not None) and (d.tzinfo.utcoffset(d) is not None) 23 | 24 | 25 | def get_datetime_param(d): 26 | if not is_aware_datetime(d): 27 | d = d.astimezone() 28 | d = d.astimezone(datetime.timezone.utc) 29 | d = d.isoformat(timespec='seconds').replace('+00:00', 'Z') 30 | return d 31 | 32 | 33 | class TmV1Client: 34 | base_url_default = V1_URL 35 | WB_STATUS_NEW = 'New' 36 | WB_STATUS_IN_PROGRESS = 'In Progress' 37 | 38 | def __init__(self, token, base_url=None): 39 | if not token: 40 | raise ValueError('Authentication token missing') 41 | self.token = token 42 | self.base_url = base_url or TmV1Client.base_url_default 43 | 44 | def make_headers(self, **kwargs): 45 | headers = {} 46 | use_token = kwargs.pop('use_token', True) 47 | if use_token: 48 | headers['Authorization'] = 'Bearer ' + self.token 49 | if 'files' not in kwargs: 50 | headers['Content-Type'] = 'application/json;charset=utf-8' 51 | headers['User-Agent'] = V1_UA 52 | return headers 53 | 54 | def get(self, url_or_path, use_token=True, **kwargs): 55 | kwargs.setdefault('headers', {}).update( 56 | self.make_headers(use_token=use_token) 57 | ) 58 | url = (self.base_url + url_or_path if url_or_path.startswith('/') else 59 | url_or_path) 60 | r = requests.get(url, **kwargs) 61 | if 200 == r.status_code: 62 | if 'application/json' in r.headers.get('Content-Type', ''): 63 | return r.json() 64 | return r.content 65 | raise RuntimeError(f'Request unsuccessful (GET {url_or_path}):' 66 | f' {r.status_code} {r.text}') 67 | 68 | def patch(self, path, **kwargs): 69 | kwargs.setdefault('headers', {}).update(self.make_headers()) 70 | r = requests.patch(self.base_url + path, **kwargs) 71 | if 204 == r.status_code: 72 | return 73 | raise RuntimeError(f'Request unsuccessful (PATCH {path}):' 74 | f' {r.status_code} {r.text}') 75 | 76 | def get_items(self, path, **kwargs): 77 | items = [] 78 | next_link = None 79 | while True: 80 | if next_link is None: 81 | r = self.get(path, **kwargs) 82 | else: 83 | r = self.get(next_link, 84 | **{'headers': kwargs.get('headers', {})}) 85 | items.extend(r['items']) 86 | if 'nextLink' not in r: 87 | break 88 | next_link = r['nextLink'] 89 | return items 90 | 91 | def get_workbench_alerts(self, start=None, end=None): 92 | params = {} 93 | if start is not None: 94 | params['startDateTime'] = get_datetime_param(start) 95 | if end is not None: 96 | params['endDateTime'] = get_datetime_param(end) 97 | return self.get_items('/v3.0/workbench/alerts', params=params) 98 | 99 | def update_workbench_alert(self, alert_id, status): 100 | return self.patch(f'/v3.0/workbench/alerts/{alert_id}', 101 | json={'investigationStatus': status}) 102 | 103 | 104 | def main(start, end, days, v1_token, v1_url): 105 | if end is None: 106 | end = datetime.datetime.now(datetime.timezone.utc) 107 | else: 108 | end = datetime.datetime.fromisoformat(end) 109 | if start is None: 110 | start = end + datetime.timedelta(days=-days) 111 | else: 112 | start = datetime.datetime.fromisoformat(start) 113 | v1 = TmV1Client(v1_token, v1_url) 114 | 115 | wb_records = v1.get_workbench_alerts(start, end) 116 | if wb_records: 117 | print('') 118 | print('Target Workbench alerts:') 119 | print(json.dumps([x['id'] for x in wb_records], indent=2)) 120 | print('') 121 | records_list = [] 122 | for record in wb_records: 123 | wb_id = record['id'] 124 | records_list.append(record) 125 | if TmV1Client.WB_STATUS_NEW == record['investigationStatus']: 126 | v1.update_workbench_alert(wb_id, 127 | TmV1Client.WB_STATUS_IN_PROGRESS) 128 | print('Details of target Workbench alerts:') 129 | print(json.dumps(records_list, indent=2)) 130 | else: 131 | print('No Workbench alerts found') 132 | 133 | 134 | if __name__ == '__main__': 135 | parser = argparse.ArgumentParser( 136 | description='Modify alert status after checking alert details', 137 | epilog=(f'Example: python {os.path.basename(__file__)} ' 138 | '-e 2021-04-12T14:28:00.123456+00:00 -d 5')) 139 | parser.add_argument( 140 | '-t', '--v1-token', default=V1_TOKEN, 141 | help='Authentication token of your Trend Vision One user account') 142 | parser.add_argument( 143 | '-u', '--v1-url', default=TmV1Client.base_url_default, 144 | help=('URL of the Trend Micro One server for your region.' 145 | f' The default value is "{TmV1Client.base_url_default}"')) 146 | parser.add_argument( 147 | '-s', '--start', 148 | help=('Timestamp in ISO 8601 format that indicates the start of' 149 | ' the data retrieval time range')) 150 | parser.add_argument( 151 | '-e', '--end', 152 | help=('Timestamp in ISO 8601 format that indicates the end of the data' 153 | ' retrieval time range. The default value is the current time')) 154 | parser.add_argument( 155 | '-d', '--days', type=int, default=5, 156 | help=('Number of days before the end time of the data retrieval' 157 | ' time range. The default value is 5.')) 158 | main(**vars(parser.parse_args())) 159 | -------------------------------------------------------------------------------- /documentation-security-posture/python/README.md: -------------------------------------------------------------------------------- 1 | # Cookbook: "Create custom reports in XLSX or PPTX format using data from the Security Posture API" 2 | 3 | ## System Requirements 4 | 5 | - Python 3.9 or later 6 | 7 | ## Environment Setup 8 | 9 | 1. Install `pipenv`. 10 | ```text 11 | $ pip install pipenv 12 | ``` 13 | 2. Create a virtual environment for installing packages and managing dependencies. 14 | ```text 15 | $ pipenv install 16 | ``` 17 | 3. Modify the settings in `documentation-security-posture.py` to match your environment. 18 | ```python 19 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 20 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 21 | V1_UA = os.environ.get('TMV1_UA', f'Trend Vision One API Cookbook ({os.path.basename(__file__)})') 22 | V1_XLSX_FILENAME = os.environ.get('TMV1_XLSX_FILENAME', 'security_posture.xlsx') 23 | V1_PPTX_FILENAME = os.environ.get('TMV1_PPTX_FILENAME', 'security_posture.pptx') 24 | V1_YAML_FILENAME = os.environ.get('TMV1_YAML_FILENAME', 'security_posture.yaml') 25 | ``` 26 | Alternatively, you can set these as environment variables or script command parameters. 27 | 28 | 29 | ## Sample Script 30 | 31 | 1. Activate the virtual environment associated with your project. 32 | ```text 33 | $ pipenv shell 34 | ``` 35 | 2. Run the sample script using any of the available parameters. 36 | - Get information about your current security posture and create XLSX file with the information. 37 | ```text 38 | (python) $ python documentation_security_posture.py export -s 39 | ``` 40 | - Generate a report in PPTX format based on the information from the generated XLSX report. 41 | ```text 42 | (python) $ python documentation_security_posture.py report -s -p 43 | ``` 44 | 45 | ## Expected Results 46 | ### Custom report file in XLSX format 47 | The sample script generates an XLSX file (`security_posture.xlsx`) that includes various security metrics, such as: 48 | 49 | - General 50 | - Risk Index 51 | - Risk Category Level 52 | - High Impact Risk Events Threat Detection 53 | - High Impact Risk Events Security Configuration 54 | - High Impact Risk Events System Configuration 55 | - High Impact Risk Events Vulnerability Detection 56 | - High Impact Risk Events Anomaly Detection 57 | - High Impact Risk Events Account Compromise 58 | - High Impact Risk Events Cloud App Activity 59 | - High Impact Risk Events XDR Detection 60 | - Vulnerability Assessment Coverage Rate 61 | - CVE Management Metrics Count 62 | - CVE Management Metrics Average Unpatched Days 63 | - CVE Management Metrics Density 64 | - CVE Management Metrics Vulnerable Endpoint Rate 65 | - CVE Management Metrics Legacy OS Endpoint Count 66 | - CVE Management Metrics Mttp Days 67 | - Exposure Status Cloud Asset Misconfiguration 68 | - Exposure Status Unexpected Internet Facing Interface 69 | - Exposure Status Insecure Host Connection 70 | - Exposure Status Domain Account Misconfiguration 71 | - Exposure Status 72 | - Security Configuration Status Endpoint Agent 73 | - Security Configuration Status Endpoint Agent Version 74 | - Security Configuration Status Endpoint Agent Feature Apex One Adoption Rate 75 | - Security Configuration Status Endpoint Agent Feature Apex One Saas Adoption Rate 76 | - Security Configuration Status Endpoint Agent Feature Apex One On Premises Adoption Rate 77 | - Security Configuration Status Endpoint Agent Feature Cloud One Adoption Rate 78 | - Security Configuration Status Endpoint Agent Feature Standard Protection Adoption Rate 79 | - Security Configuration Status Endpoint Agent Feature Deep Software Adoption Rate 80 | - Security Configuration Status Endpoint Agent Feature Workload Adoption Rate 81 | - Security Configuration Status Endpoint Agent Feature Server Workload Protection Adoption Rate 82 | - Security Configuration Status Virtual Patching 83 | - Security Configuration Status Email Sensor Exchange 84 | - Security Configuration Status Email Sensor Gmail 85 | - Security Configuration Status Cloud Apps 86 | 87 | 88 | 89 | Each security metric, except for general, is stored in a separate worksheet inside XLSX workbooks. Every worksheet contains a row with the `createdDateTime` field of the response. 90 | 91 | Notes: 92 | - Due to limitations with Microsoft Excel, worksheet names exceeding 31 characters are automatically shortened. For more information about how the script handles worksheet names, see [Configuration File](#configuration-file). 93 | - If the new worksheet name exceeds 31 characters, the sample script outputs the following error to `stderr`: 94 | ```text 95 | RuntimeError: worksheet name '' exceeds the 31 character maximum. 96 | ``` 97 | - If multiple worksheets have the same name, the script outputs the following error to `stderr`: 98 | ```text 99 | RuntimeError: worksheet '' already exists. 100 | ``` 101 | - In cases where the data structure of the response is not supported, the script outputs the following error to `stderr`: 102 | ```text 103 | ValueError: Data structure not supported. 104 | ``` 105 | 106 | ### Custom report file in PPTX format 107 | The sample code can also generate a custom report in PPTX format (`security_posture.pptx`) based on the information from the generated XLSX report (`security_posture.xlsx`). 108 | 109 | The PPTX report presents each of the security metrics from the XLSX report in a line chart format, ordered chronologically by date. 110 | 111 | Note: In the `Risk Category Level` data source, the line chart values are replaced as follows: 112 | - low - `0` 113 | - medium - `1` 114 | - high - `2` 115 | 116 | ## Configuration File 117 | To manage worksheet names longer than 31 characters, the sample code uses a configuration file (`security_posture.yaml`). 118 | 119 | The following is an example of a configuration file: 120 | 121 | ```text 122 | worksheet names: 123 | High Impact Risk Events Threat Detection: HIREs Threat Detection 124 | High Impact Risk Events Security Configuration: HIREs Security Configuration 125 | High Impact Risk Events System Configuration: HIREs System Configuration 126 | High Impact Risk Events Vulnerability Detection: HIREs Vulnerability Detection 127 | High Impact Risk Events Anomaly Detection: HIREs Anomaly Detection 128 | High Impact Risk Events Account Compromise: HIREs Account Compromise 129 | High Impact Risk Events Cloud App Activity: HIREs Cloud App ACTs 130 | High Impact Risk Events XDR Detection: HIREs XDR Detection 131 | Vulnerability Assessment Coverage Rate: VA Coverage Rate 132 | CVE Management Metrics Average Unpatched Days: CVE MM AVG. Unpatched Days 133 | CVE Management Metrics Vulnerable Endpoint Rate: CVE MM Vulnerable Endpoint Rate 134 | CVE Management Metrics Legacy OS Endpoint Count: CVE MM Legacy OS Endpoint Count 135 | CVE Management Metrics Mttp Days: CVE MM Mttp Days 136 | Exposure Status Cloud Asset Misconfiguration: ES Cloud Asset Misconfiguration 137 | Exposure Status Unexpected Internet Facing Interface: ES Unexpected INET Facing IF 138 | Exposure Status Insecure Host Connection: ES Insecure Host Connection 139 | Exposure Status Domain Account Misconfiguration: ES Domain Acct. Misconfig 140 | Security Configuration Status Endpoint Agent: SCS EP Agent 141 | Security Configuration Status Endpoint Agent Version: SCS EP Agent Version 142 | Security Configuration Status Endpoint Agent Feature Apex One Adoption Rate: SCS EP Agent A1 Adoption Rate 143 | Security Configuration Status Endpoint Agent Feature Apex One Saas Adoption Rate: SCS EPA A1 Saas Adoption Rate 144 | Security Configuration Status Endpoint Agent Feature Apex One On Premises Adoption Rate: SCS EPA A1 OnPre Adoption Rate 145 | Security Configuration Status Endpoint Agent Feature Cloud One Adoption Rate: SCS EP Agent C1 Adoption Rate 146 | Security Configuration Status Endpoint Agent Feature Standard Protection Adoption Rate: SCS EP Agent SP Adoption Rate 147 | Security Configuration Status Endpoint Agent Feature Deep Software Adoption Rate: SCS EP Agent DS Adoption Rate 148 | Security Configuration Status Endpoint Agent Feature Workload Adoption Rate: SCS EPA Workload Adoption Rate 149 | Security Configuration Status Endpoint Agent Feature Server Workload Protection Adoption Rate: SCS EP Agent SWP Adoption Rate 150 | Security Configuration Status Virtual Patching: SCS Virtual Patching 151 | Security Configuration Status Email Sensor Exchange: SCS Email Sensor Exchange 152 | Security Configuration Status Email Sensor Gmail: SCS Email Sensor Gmail 153 | Security Configuration Status Cloud Apps: SCS Cloud Apps 154 | ``` 155 | -------------------------------------------------------------------------------- /oat-pipeline-api/python/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "1841259a6294939b3dde91acf33c2e02af40cae97022c9b3da3f7f2bb07c77c5" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", 22 | "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" 23 | ], 24 | "markers": "python_version >= '3.6'", 25 | "version": "==2025.1.31" 26 | }, 27 | "charset-normalizer": { 28 | "hashes": [ 29 | "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", 30 | "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa", 31 | "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a", 32 | "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", 33 | "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b", 34 | "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", 35 | "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", 36 | "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", 37 | "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", 38 | "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", 39 | "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", 40 | "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", 41 | "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", 42 | "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", 43 | "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", 44 | "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", 45 | "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", 46 | "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", 47 | "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", 48 | "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", 49 | "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e", 50 | "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a", 51 | "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4", 52 | "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca", 53 | "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", 54 | "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", 55 | "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", 56 | "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", 57 | "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", 58 | "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", 59 | "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", 60 | "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", 61 | "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", 62 | "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", 63 | "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", 64 | "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd", 65 | "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c", 66 | "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", 67 | "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", 68 | "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", 69 | "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", 70 | "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824", 71 | "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", 72 | "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf", 73 | "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487", 74 | "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d", 75 | "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd", 76 | "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", 77 | "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534", 78 | "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", 79 | "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", 80 | "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", 81 | "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd", 82 | "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", 83 | "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9", 84 | "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", 85 | "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", 86 | "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d", 87 | "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", 88 | "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", 89 | "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", 90 | "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", 91 | "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", 92 | "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", 93 | "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8", 94 | "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", 95 | "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", 96 | "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", 97 | "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", 98 | "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", 99 | "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", 100 | "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", 101 | "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", 102 | "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", 103 | "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", 104 | "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", 105 | "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", 106 | "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e", 107 | "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6", 108 | "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", 109 | "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", 110 | "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e", 111 | "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", 112 | "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", 113 | "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c", 114 | "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", 115 | "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", 116 | "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089", 117 | "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", 118 | "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e", 119 | "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", 120 | "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616" 121 | ], 122 | "markers": "python_version >= '3.7'", 123 | "version": "==3.4.1" 124 | }, 125 | "idna": { 126 | "hashes": [ 127 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", 128 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" 129 | ], 130 | "markers": "python_version >= '3.6'", 131 | "version": "==3.10" 132 | }, 133 | "requests": { 134 | "hashes": [ 135 | "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", 136 | "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" 137 | ], 138 | "index": "pypi", 139 | "markers": "python_version >= '3.8'", 140 | "version": "==2.32.3" 141 | }, 142 | "urllib3": { 143 | "hashes": [ 144 | "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", 145 | "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813" 146 | ], 147 | "markers": "python_version >= '3.9'", 148 | "version": "==2.4.0" 149 | } 150 | }, 151 | "develop": {} 152 | } 153 | -------------------------------------------------------------------------------- /check-incident-details/python/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "b8c2e1580c53e383cfe4254c1f16560b855d984fde8b2beb3bf6ee8fc2fe5a22" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", 22 | "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" 23 | ], 24 | "markers": "python_version >= '3.6'", 25 | "version": "==2024.8.30" 26 | }, 27 | "charset-normalizer": { 28 | "hashes": [ 29 | "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", 30 | "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", 31 | "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", 32 | "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", 33 | "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", 34 | "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", 35 | "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", 36 | "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", 37 | "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", 38 | "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", 39 | "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", 40 | "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", 41 | "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", 42 | "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", 43 | "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", 44 | "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", 45 | "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", 46 | "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", 47 | "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", 48 | "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", 49 | "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", 50 | "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", 51 | "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", 52 | "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", 53 | "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", 54 | "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", 55 | "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", 56 | "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", 57 | "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", 58 | "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", 59 | "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", 60 | "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", 61 | "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", 62 | "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", 63 | "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", 64 | "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", 65 | "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", 66 | "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", 67 | "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", 68 | "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", 69 | "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", 70 | "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", 71 | "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", 72 | "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", 73 | "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", 74 | "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", 75 | "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", 76 | "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", 77 | "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", 78 | "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", 79 | "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", 80 | "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", 81 | "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", 82 | "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", 83 | "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", 84 | "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", 85 | "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", 86 | "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", 87 | "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", 88 | "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", 89 | "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", 90 | "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", 91 | "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", 92 | "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", 93 | "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", 94 | "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", 95 | "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", 96 | "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", 97 | "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", 98 | "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", 99 | "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", 100 | "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", 101 | "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", 102 | "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", 103 | "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", 104 | "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", 105 | "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", 106 | "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", 107 | "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", 108 | "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", 109 | "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", 110 | "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", 111 | "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", 112 | "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", 113 | "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", 114 | "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", 115 | "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", 116 | "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", 117 | "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", 118 | "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", 119 | "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", 120 | "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", 121 | "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", 122 | "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", 123 | "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", 124 | "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", 125 | "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", 126 | "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", 127 | "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", 128 | "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", 129 | "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", 130 | "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", 131 | "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", 132 | "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", 133 | "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" 134 | ], 135 | "markers": "python_full_version >= '3.7.0'", 136 | "version": "==3.4.0" 137 | }, 138 | "idna": { 139 | "hashes": [ 140 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", 141 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" 142 | ], 143 | "markers": "python_version >= '3.6'", 144 | "version": "==3.10" 145 | }, 146 | "requests": { 147 | "hashes": [ 148 | "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", 149 | "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" 150 | ], 151 | "index": "pypi", 152 | "markers": "python_version >= '3.8'", 153 | "version": "==2.32.3" 154 | }, 155 | "urllib3": { 156 | "hashes": [ 157 | "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", 158 | "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" 159 | ], 160 | "markers": "python_version >= '3.8'", 161 | "version": "==2.2.3" 162 | } 163 | }, 164 | "develop": {} 165 | } 166 | -------------------------------------------------------------------------------- /detection-and-response/python/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "b8c2e1580c53e383cfe4254c1f16560b855d984fde8b2beb3bf6ee8fc2fe5a22" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", 22 | "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" 23 | ], 24 | "markers": "python_version >= '3.6'", 25 | "version": "==2024.8.30" 26 | }, 27 | "charset-normalizer": { 28 | "hashes": [ 29 | "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", 30 | "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", 31 | "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", 32 | "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", 33 | "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", 34 | "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", 35 | "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", 36 | "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", 37 | "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", 38 | "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", 39 | "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", 40 | "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", 41 | "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", 42 | "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", 43 | "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", 44 | "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", 45 | "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", 46 | "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", 47 | "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", 48 | "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", 49 | "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", 50 | "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", 51 | "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", 52 | "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", 53 | "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", 54 | "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", 55 | "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", 56 | "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", 57 | "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", 58 | "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", 59 | "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", 60 | "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", 61 | "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", 62 | "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", 63 | "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", 64 | "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", 65 | "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", 66 | "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", 67 | "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", 68 | "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", 69 | "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", 70 | "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", 71 | "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", 72 | "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", 73 | "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", 74 | "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", 75 | "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", 76 | "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", 77 | "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", 78 | "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", 79 | "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", 80 | "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", 81 | "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", 82 | "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", 83 | "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", 84 | "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", 85 | "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", 86 | "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", 87 | "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", 88 | "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", 89 | "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", 90 | "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", 91 | "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", 92 | "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", 93 | "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", 94 | "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", 95 | "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", 96 | "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", 97 | "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", 98 | "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", 99 | "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", 100 | "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", 101 | "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", 102 | "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", 103 | "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", 104 | "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", 105 | "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", 106 | "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", 107 | "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", 108 | "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", 109 | "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", 110 | "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", 111 | "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", 112 | "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", 113 | "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", 114 | "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", 115 | "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", 116 | "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", 117 | "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", 118 | "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", 119 | "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", 120 | "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", 121 | "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", 122 | "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", 123 | "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", 124 | "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", 125 | "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", 126 | "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", 127 | "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", 128 | "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", 129 | "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", 130 | "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", 131 | "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", 132 | "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", 133 | "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" 134 | ], 135 | "markers": "python_full_version >= '3.7.0'", 136 | "version": "==3.4.0" 137 | }, 138 | "idna": { 139 | "hashes": [ 140 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", 141 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" 142 | ], 143 | "markers": "python_version >= '3.6'", 144 | "version": "==3.10" 145 | }, 146 | "requests": { 147 | "hashes": [ 148 | "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", 149 | "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" 150 | ], 151 | "index": "pypi", 152 | "markers": "python_version >= '3.8'", 153 | "version": "==2.32.3" 154 | }, 155 | "urllib3": { 156 | "hashes": [ 157 | "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", 158 | "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" 159 | ], 160 | "markers": "python_version >= '3.8'", 161 | "version": "==2.2.3" 162 | } 163 | }, 164 | "develop": {} 165 | } 166 | -------------------------------------------------------------------------------- /sandbox-submission/python/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "b8c2e1580c53e383cfe4254c1f16560b855d984fde8b2beb3bf6ee8fc2fe5a22" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", 22 | "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" 23 | ], 24 | "markers": "python_version >= '3.6'", 25 | "version": "==2024.8.30" 26 | }, 27 | "charset-normalizer": { 28 | "hashes": [ 29 | "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", 30 | "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", 31 | "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", 32 | "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", 33 | "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", 34 | "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", 35 | "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", 36 | "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", 37 | "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", 38 | "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", 39 | "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", 40 | "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", 41 | "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", 42 | "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", 43 | "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", 44 | "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", 45 | "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", 46 | "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", 47 | "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", 48 | "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", 49 | "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", 50 | "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", 51 | "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", 52 | "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", 53 | "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", 54 | "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", 55 | "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", 56 | "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", 57 | "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", 58 | "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", 59 | "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", 60 | "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", 61 | "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", 62 | "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", 63 | "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", 64 | "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", 65 | "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", 66 | "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", 67 | "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", 68 | "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", 69 | "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", 70 | "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", 71 | "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", 72 | "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", 73 | "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", 74 | "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", 75 | "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", 76 | "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", 77 | "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", 78 | "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", 79 | "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", 80 | "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", 81 | "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", 82 | "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", 83 | "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", 84 | "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", 85 | "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", 86 | "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", 87 | "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", 88 | "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", 89 | "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", 90 | "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", 91 | "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", 92 | "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", 93 | "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", 94 | "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", 95 | "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", 96 | "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", 97 | "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", 98 | "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", 99 | "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", 100 | "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", 101 | "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", 102 | "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", 103 | "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", 104 | "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", 105 | "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", 106 | "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", 107 | "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", 108 | "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", 109 | "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", 110 | "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", 111 | "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", 112 | "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", 113 | "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", 114 | "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", 115 | "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", 116 | "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", 117 | "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", 118 | "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", 119 | "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", 120 | "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", 121 | "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", 122 | "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", 123 | "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", 124 | "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", 125 | "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", 126 | "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", 127 | "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", 128 | "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", 129 | "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", 130 | "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", 131 | "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", 132 | "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", 133 | "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" 134 | ], 135 | "markers": "python_full_version >= '3.7.0'", 136 | "version": "==3.4.0" 137 | }, 138 | "idna": { 139 | "hashes": [ 140 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", 141 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" 142 | ], 143 | "markers": "python_version >= '3.6'", 144 | "version": "==3.10" 145 | }, 146 | "requests": { 147 | "hashes": [ 148 | "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", 149 | "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" 150 | ], 151 | "index": "pypi", 152 | "markers": "python_version >= '3.8'", 153 | "version": "==2.32.3" 154 | }, 155 | "urllib3": { 156 | "hashes": [ 157 | "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", 158 | "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" 159 | ], 160 | "markers": "python_version >= '3.8'", 161 | "version": "==2.2.3" 162 | } 163 | }, 164 | "develop": {} 165 | } 166 | -------------------------------------------------------------------------------- /ioc-intelligence-sweeping/python/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "b8c2e1580c53e383cfe4254c1f16560b855d984fde8b2beb3bf6ee8fc2fe5a22" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", 22 | "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" 23 | ], 24 | "markers": "python_version >= '3.6'", 25 | "version": "==2024.8.30" 26 | }, 27 | "charset-normalizer": { 28 | "hashes": [ 29 | "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", 30 | "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", 31 | "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", 32 | "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", 33 | "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", 34 | "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", 35 | "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", 36 | "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", 37 | "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", 38 | "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", 39 | "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", 40 | "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", 41 | "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", 42 | "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", 43 | "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", 44 | "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", 45 | "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", 46 | "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", 47 | "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", 48 | "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", 49 | "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", 50 | "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", 51 | "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", 52 | "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", 53 | "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", 54 | "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", 55 | "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", 56 | "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", 57 | "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", 58 | "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", 59 | "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", 60 | "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", 61 | "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", 62 | "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", 63 | "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", 64 | "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", 65 | "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", 66 | "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", 67 | "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", 68 | "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", 69 | "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", 70 | "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", 71 | "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", 72 | "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", 73 | "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", 74 | "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", 75 | "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", 76 | "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", 77 | "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", 78 | "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", 79 | "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", 80 | "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", 81 | "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", 82 | "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", 83 | "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", 84 | "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", 85 | "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", 86 | "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", 87 | "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", 88 | "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", 89 | "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", 90 | "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", 91 | "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", 92 | "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", 93 | "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", 94 | "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", 95 | "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", 96 | "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", 97 | "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", 98 | "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", 99 | "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", 100 | "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", 101 | "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", 102 | "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", 103 | "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", 104 | "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", 105 | "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", 106 | "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", 107 | "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", 108 | "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", 109 | "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", 110 | "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", 111 | "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", 112 | "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", 113 | "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", 114 | "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", 115 | "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", 116 | "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", 117 | "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", 118 | "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", 119 | "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", 120 | "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", 121 | "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", 122 | "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", 123 | "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", 124 | "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", 125 | "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", 126 | "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", 127 | "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", 128 | "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", 129 | "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", 130 | "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", 131 | "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", 132 | "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", 133 | "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" 134 | ], 135 | "markers": "python_full_version >= '3.7.0'", 136 | "version": "==3.4.0" 137 | }, 138 | "idna": { 139 | "hashes": [ 140 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", 141 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" 142 | ], 143 | "markers": "python_version >= '3.6'", 144 | "version": "==3.10" 145 | }, 146 | "requests": { 147 | "hashes": [ 148 | "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", 149 | "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" 150 | ], 151 | "index": "pypi", 152 | "markers": "python_version >= '3.8'", 153 | "version": "==2.32.3" 154 | }, 155 | "urllib3": { 156 | "hashes": [ 157 | "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", 158 | "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" 159 | ], 160 | "markers": "python_version >= '3.8'", 161 | "version": "==2.2.3" 162 | } 163 | }, 164 | "develop": {} 165 | } 166 | -------------------------------------------------------------------------------- /datalake-pipeline-api/python/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "4a5b9a7c147d83c3aa7be8e851b730856147b076675bb4473b0337132a3f5f21" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", 22 | "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" 23 | ], 24 | "markers": "python_version >= '3.6'", 25 | "version": "==2025.1.31" 26 | }, 27 | "charset-normalizer": { 28 | "hashes": [ 29 | "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", 30 | "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa", 31 | "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a", 32 | "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", 33 | "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b", 34 | "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", 35 | "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", 36 | "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", 37 | "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", 38 | "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", 39 | "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", 40 | "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", 41 | "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", 42 | "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", 43 | "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", 44 | "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", 45 | "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", 46 | "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", 47 | "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", 48 | "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", 49 | "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e", 50 | "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a", 51 | "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4", 52 | "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca", 53 | "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", 54 | "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", 55 | "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", 56 | "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", 57 | "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", 58 | "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", 59 | "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", 60 | "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", 61 | "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", 62 | "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", 63 | "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", 64 | "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd", 65 | "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c", 66 | "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", 67 | "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", 68 | "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", 69 | "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", 70 | "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824", 71 | "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", 72 | "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf", 73 | "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487", 74 | "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d", 75 | "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd", 76 | "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", 77 | "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534", 78 | "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", 79 | "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", 80 | "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", 81 | "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd", 82 | "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", 83 | "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9", 84 | "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", 85 | "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", 86 | "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d", 87 | "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", 88 | "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", 89 | "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", 90 | "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", 91 | "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", 92 | "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", 93 | "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8", 94 | "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", 95 | "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", 96 | "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", 97 | "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", 98 | "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", 99 | "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", 100 | "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", 101 | "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", 102 | "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", 103 | "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", 104 | "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", 105 | "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", 106 | "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e", 107 | "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6", 108 | "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", 109 | "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", 110 | "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e", 111 | "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", 112 | "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", 113 | "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c", 114 | "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", 115 | "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", 116 | "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089", 117 | "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", 118 | "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e", 119 | "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", 120 | "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616" 121 | ], 122 | "markers": "python_version >= '3.7'", 123 | "version": "==3.4.1" 124 | }, 125 | "idna": { 126 | "hashes": [ 127 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", 128 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" 129 | ], 130 | "markers": "python_version >= '3.6'", 131 | "version": "==3.10" 132 | }, 133 | "requests": { 134 | "hashes": [ 135 | "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", 136 | "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" 137 | ], 138 | "index": "pypi", 139 | "markers": "python_version >= '3.8'", 140 | "version": "==2.32.3" 141 | }, 142 | "urllib3": { 143 | "hashes": [ 144 | "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", 145 | "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813" 146 | ], 147 | "markers": "python_version >= '3.9'", 148 | "version": "==2.4.0" 149 | } 150 | }, 151 | "develop": { 152 | "iniconfig": { 153 | "hashes": [ 154 | "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", 155 | "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" 156 | ], 157 | "markers": "python_version >= '3.8'", 158 | "version": "==2.1.0" 159 | }, 160 | "packaging": { 161 | "hashes": [ 162 | "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", 163 | "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" 164 | ], 165 | "markers": "python_version >= '3.8'", 166 | "version": "==25.0" 167 | }, 168 | "pluggy": { 169 | "hashes": [ 170 | "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", 171 | "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" 172 | ], 173 | "markers": "python_version >= '3.8'", 174 | "version": "==1.5.0" 175 | }, 176 | "pytest": { 177 | "hashes": [ 178 | "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", 179 | "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845" 180 | ], 181 | "index": "pypi", 182 | "markers": "python_version >= '3.8'", 183 | "version": "==8.3.5" 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /v1-events-to-elasticsearch/python/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "4f34ae03a2e5509960e4c37a9e72f56de3cb8cffae86edc44c077b47ce9db08c" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", 22 | "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" 23 | ], 24 | "markers": "python_version >= '3.6'", 25 | "version": "==2024.8.30" 26 | }, 27 | "charset-normalizer": { 28 | "hashes": [ 29 | "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", 30 | "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", 31 | "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", 32 | "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", 33 | "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", 34 | "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", 35 | "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", 36 | "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", 37 | "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", 38 | "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", 39 | "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", 40 | "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", 41 | "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", 42 | "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", 43 | "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", 44 | "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", 45 | "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", 46 | "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", 47 | "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", 48 | "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", 49 | "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", 50 | "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", 51 | "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", 52 | "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", 53 | "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", 54 | "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", 55 | "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", 56 | "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", 57 | "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", 58 | "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", 59 | "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", 60 | "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", 61 | "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", 62 | "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", 63 | "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", 64 | "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", 65 | "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", 66 | "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", 67 | "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", 68 | "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", 69 | "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", 70 | "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", 71 | "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", 72 | "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", 73 | "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", 74 | "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", 75 | "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", 76 | "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", 77 | "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", 78 | "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", 79 | "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", 80 | "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", 81 | "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", 82 | "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", 83 | "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", 84 | "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", 85 | "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", 86 | "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", 87 | "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", 88 | "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", 89 | "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", 90 | "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", 91 | "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", 92 | "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", 93 | "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", 94 | "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", 95 | "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", 96 | "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", 97 | "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", 98 | "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", 99 | "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", 100 | "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", 101 | "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", 102 | "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", 103 | "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", 104 | "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", 105 | "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", 106 | "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", 107 | "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", 108 | "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", 109 | "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", 110 | "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", 111 | "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", 112 | "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", 113 | "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", 114 | "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", 115 | "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", 116 | "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", 117 | "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", 118 | "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", 119 | "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", 120 | "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", 121 | "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", 122 | "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", 123 | "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", 124 | "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", 125 | "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", 126 | "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", 127 | "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", 128 | "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", 129 | "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", 130 | "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", 131 | "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", 132 | "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", 133 | "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" 134 | ], 135 | "markers": "python_full_version >= '3.7.0'", 136 | "version": "==3.4.0" 137 | }, 138 | "elastic-transport": { 139 | "hashes": [ 140 | "sha256:9cac4ab5cf9402668cf305ae0b7d93ddc0c7b61461d6d1027850db6da9cc5742", 141 | "sha256:b5e82ff1679d8c7705a03fd85c7f6ef85d6689721762d41228dd312e34f331fc" 142 | ], 143 | "markers": "python_version >= '3.8'", 144 | "version": "==8.15.1" 145 | }, 146 | "elasticsearch": { 147 | "hashes": [ 148 | "sha256:02a0476e98768a30d7926335fc0d305c04fdb928eea1354c6e6040d8c2814569", 149 | "sha256:40c0d312f8adf8bdc81795bc16a0b546ddf544cb1f90e829a244e4780c4dbfd8" 150 | ], 151 | "index": "pypi", 152 | "markers": "python_version >= '3.8'", 153 | "version": "==8.15.1" 154 | }, 155 | "idna": { 156 | "hashes": [ 157 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", 158 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" 159 | ], 160 | "markers": "python_version >= '3.6'", 161 | "version": "==3.10" 162 | }, 163 | "requests": { 164 | "hashes": [ 165 | "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", 166 | "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" 167 | ], 168 | "index": "pypi", 169 | "markers": "python_version >= '3.8'", 170 | "version": "==2.32.3" 171 | }, 172 | "urllib3": { 173 | "hashes": [ 174 | "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", 175 | "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" 176 | ], 177 | "markers": "python_version >= '3.8'", 178 | "version": "==2.2.3" 179 | } 180 | }, 181 | "develop": {} 182 | } 183 | -------------------------------------------------------------------------------- /ioc-intelligence-sweeping/python/ioc_intelligence_sweeping.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import argparse 5 | 6 | import requests 7 | 8 | # Setting variables 9 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 10 | # Specify the correct domain name for your region in V1_URL 11 | # ref: https://automation.trendmicro.com/xdr/Guides/Regional-Domains 12 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 13 | # This value is used for User-Agent header in API requests. So you can 14 | # customize this value to describe your company name, integration tool name, 15 | # and so on as you like. 16 | # default: "Trend Vision One API Cookbook ({script_name})" 17 | V1_UA = os.environ.get('TMV1_UA', 'Trend Vision One API Cookbook ' 18 | f'({os.path.basename(__file__)})') 19 | V1_WAIT_TASK_INTERVAL = int(os.environ.get('TMV1_WAIT_TASK_INTERVAL', 10)) 20 | V1_WAIT_TASK_RETRY = int(os.environ.get('TMV1_WAIT_TASK_RETRY', 12)) 21 | 22 | 23 | def is_container(v): 24 | try: 25 | if isinstance(v, (str, bytes)): 26 | return False 27 | iter(v) 28 | except TypeError: 29 | return False 30 | return True 31 | 32 | 33 | def get_multiple_request(name, args): 34 | r = [] 35 | if is_container(args): 36 | r.extend({name: v} for v in args) 37 | elif isinstance(args, str): 38 | r.append({name: args}) 39 | return r 40 | 41 | 42 | def get_filter_arg(name, value, enclose="'", equal='eq'): 43 | if not is_container(value): 44 | value = [value] 45 | param = ' or '.join(f'{name} {equal} {enclose}{x}{enclose}' for x in value) 46 | if 1 < len(value): 47 | param = '('+param+')' 48 | return param 49 | 50 | 51 | class TmV1Client: 52 | base_url_default = V1_URL 53 | intelligence_report_content_types = { 54 | 'stix': 'application/stix+json', 55 | 'csv': 'text/csv' 56 | } 57 | 58 | def __init__(self, token, base_url=None): 59 | if not token: 60 | raise ValueError('Authentication token missing') 61 | self.token = token 62 | self.base_url = base_url or TmV1Client.base_url_default 63 | 64 | def make_headers(self, **kwargs): 65 | headers = {} 66 | use_token = kwargs.pop('use_token', True) 67 | if use_token: 68 | headers['Authorization'] = 'Bearer ' + self.token 69 | if 'files' not in kwargs: 70 | headers['Content-Type'] = 'application/json;charset=utf-8' 71 | headers['User-Agent'] = V1_UA 72 | return headers 73 | 74 | def get(self, url_or_path, use_token=True, **kwargs): 75 | kwargs.setdefault('headers', {}).update( 76 | self.make_headers(use_token=use_token) 77 | ) 78 | url = (self.base_url + url_or_path if url_or_path.startswith('/') else 79 | url_or_path) 80 | r = requests.get(url, **kwargs) 81 | if 200 == r.status_code: 82 | if 'application/json' in r.headers.get('Content-Type', ''): 83 | return r.json() 84 | return r.content 85 | raise RuntimeError(f'Request unsuccessful (GET {url_or_path}):' 86 | f' {r.status_code} {r.text}') 87 | 88 | def post(self, path, **kwargs): 89 | kwargs.setdefault('headers', {}).update(self.make_headers(**kwargs)) 90 | r = requests.post(self.base_url + path, **kwargs) 91 | if ((r.status_code in [200, 201, 202, 207]) and 92 | ('application/json' in r.headers.get('Content-Type', ''))): 93 | return r.status_code, r.headers, r.json() 94 | elif r.status_code in [201, 202]: 95 | return r.status_code, r.headers, r.content 96 | raise RuntimeError(f'Request unsuccessful (POST {path}):' 97 | f' {r.status_code} {r.headers} {r.text}') 98 | 99 | def get_items(self, path, **kwargs): 100 | items = [] 101 | next_link = None 102 | while True: 103 | if next_link is None: 104 | r = self.get(path, **kwargs) 105 | else: 106 | r = self.get(next_link, 107 | **{'headers': kwargs.get('headers', {})}) 108 | items.extend(r['items']) 109 | if 'nextLink' not in r: 110 | break 111 | next_link = r['nextLink'] 112 | return items 113 | 114 | def get_from_post_response(self, status_code, headers, body): 115 | if (201 == status_code) and ('Location' in headers): 116 | return self.get(headers['Location']) 117 | if (202 == status_code) and ('Operation-Location' in headers): 118 | return self.get(headers['Operation-Location']) 119 | # response APIs return 'Operation-Location' for duplicated tasks 120 | if (400 == status_code) and ('Operation-Location' in headers): 121 | if isinstance(body, dict): 122 | error = body.get('error', {}) 123 | code = error.get('code', '') 124 | message = error.get('message', '') 125 | if ('TaskError' == code) and ('Task duplication.' == message): 126 | return self.get(headers['Operation-Location']) 127 | 128 | def post_multiple(self, path, **kwargs): 129 | json_ = kwargs.get('json') 130 | if (json_ is not None) and (not isinstance(json_, list)): 131 | raise ValueError('json is not list.') 132 | (_, _, r) = self.post(path, **kwargs) 133 | if json_ is None: 134 | json_ = [None] * len(r) 135 | if len(json_) == len(r): 136 | return [*zip(json_, r)] 137 | raise ValueError('The request and the response do not have the same ' 138 | 'length') 139 | 140 | def get_from_post_multiple_response(self, response): 141 | items = [None] * len(response) 142 | for i, (_, r) in enumerate(response): 143 | headers = dict((h['name'], h['value']) 144 | for h in r.get('headers', [])) 145 | body = r.get('body') 146 | items[i] = self.get_from_post_response(r['status'], headers, body) 147 | return items 148 | 149 | def import_intelligence_report(self, file_obj, file_name, content_type, 150 | report_name=None): 151 | if content_type == 'stix': 152 | report_name = report_name or '' 153 | elif not report_name: 154 | raise ValueError('report_name must be specified for ' 155 | f'{content_type}') 156 | data = {'reportName': report_name} 157 | if content_type not in self.intelligence_report_content_types: 158 | raise ValueError('content_type must be' 159 | f' {self.intelligence_report_content_types}.') 160 | t = self.intelligence_report_content_types[content_type] 161 | if not file_name: 162 | file_name = file_bytes.name 163 | files = {'file': (file_name, file_obj, t)} 164 | return self.post_multiple('/v3.0/threatintel/intelligenceReports', 165 | data=data, files=files) 166 | 167 | def sweep_by_intelligence_reports(self, report_id): 168 | request = get_multiple_request('id', report_id) 169 | for r in request: 170 | r['sweepType'] = 'manual' 171 | return self.post_multiple( 172 | '/v3.0/threatintel/intelligenceReports/sweep', json=request 173 | ) 174 | 175 | def get_threatintel_tasks(self, ids=None): 176 | params = {} 177 | if ids is not None: 178 | params['filter'] = get_filter_arg('id', ids) 179 | return self.get_items('/v3.0/threatintel/tasks', params=params) 180 | 181 | 182 | def wait_threatintel_tasks(v1, tasks): 183 | count = 0 184 | while True: 185 | running_task_indexes = [ 186 | i for i, t in enumerate(tasks) 187 | if t and (t['status'] in ['notstarted', 'running']) 188 | ] 189 | if not running_task_indexes: 190 | break 191 | if not (count < V1_WAIT_TASK_RETRY): 192 | break 193 | count += 1 194 | print((f'Tasks running: {len(running_task_indexes)}; ' 195 | f'Waiting interval (seconds): {V1_WAIT_TASK_INTERVAL}; ' 196 | f'Number of intervals: {count}.')) 197 | time.sleep(V1_WAIT_TASK_INTERVAL) 198 | response = v1.get_threatintel_tasks([tasks[i]['id'] 199 | for i in running_task_indexes]) 200 | for i, r in enumerate(response): 201 | tasks[running_task_indexes[i]].update(r) 202 | finished = not running_task_indexes 203 | return finished 204 | 205 | 206 | def import_and_sweep(v1, infile, name, content_type, report_name): 207 | # Import custom intelligence report 208 | import_response = v1.import_intelligence_report( 209 | infile, name, content_type, report_name 210 | ) 211 | reports = v1.get_from_post_multiple_response(import_response) 212 | 213 | # Sweep by imported custom intelligence report 214 | imported_report_indexes = [i for i, r in enumerate(reports) 215 | if r is not None] 216 | sweep_response = v1.sweep_by_intelligence_reports( 217 | [reports[i]['id'] for i in imported_report_indexes] 218 | ) 219 | tasks = v1.get_from_post_multiple_response(sweep_response) 220 | wait_threatintel_tasks(v1, tasks) 221 | 222 | # Download when speeping is hit 223 | hit_task_indexes = [i for i, t in enumerate(tasks) 224 | if ((t is not None) and 225 | ('succeeded' == t['status']) and 226 | (t['isHit'] is True)) 227 | ] 228 | file_names = [] 229 | for i in hit_task_indexes: 230 | task = tasks[i] 231 | report_id = task['reportId'] 232 | file_name = f'intelligence_report_sweep_{report_id}.json' 233 | response = v1.get(task['resourceLocation'], use_token=False) 234 | with open(file_name, 'wb') as f: 235 | f.write(response) 236 | file_names.append(file_name) 237 | 238 | r = [] 239 | for i, ((_, import_res), report) in enumerate(zip(import_response, 240 | reports)): 241 | if i not in imported_report_indexes: 242 | r.append((import_res, report, None, None, None)) 243 | continue 244 | j = imported_report_indexes.index(i) 245 | (_, sweep_res) = sweep_response[j] 246 | task = tasks[j] 247 | if task is None: 248 | r.append((import_res, report, sweep_res, None, None)) 249 | continue 250 | if j not in hit_task_indexes: 251 | r.append((import_res, report, sweep_res, task, None)) 252 | continue 253 | k = hit_task_indexes.index(j) 254 | r.append((import_res, report, sweep_res, task, file_names[k])) 255 | return r 256 | 257 | 258 | def main(v1_token, v1_url, content_type, infile, name, report_name): 259 | if infile.isatty(): 260 | raise ValueError('sys.stdin has no input') 261 | if not name: 262 | if sys.stdin.name == infile.name: 263 | raise ValueError(f'file_name must be specified for {infile.name}') 264 | name = os.path.basename(infile.name) 265 | 266 | v1 = TmV1Client(v1_token, v1_url) 267 | results = import_and_sweep(v1, infile, name, content_type, report_name) 268 | 269 | print('') 270 | for (import_res, report, sweep_res, task, file_name) in results: 271 | if report is None: 272 | error = import_res.get('body', {}).get('error', '') 273 | print(f'Unable to import report. Error code: {error}') 274 | continue 275 | if task is None: 276 | error = sweep_res.get('body', {}).get('error', '') 277 | print('Unable to start sweeping task based on custom ' 278 | f'intelligence report "{report["id"]}". Error code: {error}') 279 | continue 280 | status = task['status'] 281 | if not ('succeeded' == status): 282 | if status in ['notstarted', 'running']: 283 | print('The status of the sweeping task based on custom ' 284 | f'intelligence report "{report["id"]}" is ' 285 | f'"{status}". Task ID: {task["id"]}') 286 | elif 'failed' == status: 287 | error = task.get('error', {}) 288 | error_code = error.get('code', '') 289 | print('The status of the sweeping task based on custom ' 290 | f'intelligence report "{report["id"]}" is ' 291 | f'"{status}". Error code: {error}') 292 | continue 293 | if not file_name: 294 | print('The sweeping task based on custom intelligence report ' 295 | f'"{report["id"]}" does not have any matched indicators. ' 296 | f'Task status: {status}') 297 | continue 298 | print('The sweeping task based on custom intelligence report ' 299 | f'"{report["id"]}" has matched indicators. Results saved in ' 300 | f'"{file_name}". Task status: {status}.') 301 | 302 | 303 | if __name__ == '__main__': 304 | parser = argparse.ArgumentParser( 305 | description=('Import IoCs from STIX or CSV file into a custom' 306 | ' intelligence report, start a sweeping task, and' 307 | ' download a file ("intelligence_report_sweep_' 308 | '.json") with the matched indicators.'), 309 | epilog=(f'Example: python {os.path.basename(__file__)} ' 310 | '-f report-a stix < "stix.json"')) 311 | parser.add_argument( 312 | '-t', '--v1-token', default=V1_TOKEN, 313 | help='Authentication token of your Trend Vision One user account') 314 | parser.add_argument( 315 | '-u', '--v1-url', default=TmV1Client.base_url_default, 316 | help=('URL of the Trend Vision One server for your region.' 317 | f' The default value is "{TmV1Client.base_url_default}"')) 318 | parser.add_argument( 319 | 'content_type', 320 | choices=TmV1Client.intelligence_report_content_types, 321 | help=('File type of the file to be imported into a custom' 322 | ' intelligence report.')) 323 | parser.add_argument( 324 | 'infile', nargs='?', 325 | type=argparse.FileType('rb'), default=sys.stdin.buffer, 326 | help=('File to be imported into a custom intelligence report.' 327 | ' The default value is stdin.')) 328 | parser.add_argument( 329 | '-n', '--name', 330 | help=('Name of the file to be imported. If no value is specified,' 331 | " the last element of the file path path passed to 'infile' is" 332 | " used. When the value of 'infile' is stdin, you must specify" 333 | ' a name.')) 334 | parser.add_argument( 335 | '-r', '--report-name', 336 | help='Name of the imported intelligence report.') 337 | main(**vars(parser.parse_args())) 338 | -------------------------------------------------------------------------------- /sandbox-submission/python/sandbox_submission.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import base64 4 | import time 5 | import argparse 6 | 7 | import requests 8 | 9 | # Setting variables 10 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 11 | # Specify the correct domain name for your region in V1_URL 12 | # ref: https://automation.trendmicro.com/xdr/Guides/Regional-Domains 13 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 14 | # This value is used for User-Agent header in API requests. So you can 15 | # customize this value to describe your company name, integration tool name, 16 | # and so on as you like. 17 | # default: "Trend Vision One API Cookbook ({script_name})" 18 | V1_UA = os.environ.get('TMV1_UA', 'Trend Vision One API Cookbook ' 19 | f'({os.path.basename(__file__)})') 20 | V1_WAIT_TASK_INTERVAL = int(os.environ.get('TMV1_WAIT_TASK_INTERVAL', 10)) 21 | V1_WAIT_TASK_RETRY = int(os.environ.get('TMV1_WAIT_TASK_RETRY', 12)) 22 | V1_ANALYZE_INTERVAL = int(os.environ.get('TMV1_ANALYZE_INTERVAL', 300)) 23 | V1_ANALYZE_RETRY = int(os.environ.get('TMV1_ANALYZE_RETRY', 3)) 24 | 25 | 26 | def is_container(v): 27 | try: 28 | if isinstance(v, (str, bytes)): 29 | return False 30 | iter(v) 31 | except TypeError: 32 | return False 33 | return True 34 | 35 | 36 | def get_multiple_request(name, args): 37 | r = [] 38 | if is_container(args): 39 | r.extend({name: v} for v in args) 40 | elif isinstance(args, str): 41 | r.append({name: args}) 42 | return r 43 | 44 | 45 | def get_filter_arg(name, value, enclose="'", equal='eq'): 46 | if not is_container(value): 47 | value = [value] 48 | param = ' or '.join(f'{name} {equal} {enclose}{x}{enclose}' for x in value) 49 | if 1 < len(value): 50 | param = '('+param+')' 51 | return param 52 | 53 | 54 | class TmV1Client: 55 | base_url_default = V1_URL 56 | WB_STATUS_NEW = 'New' 57 | WB_STATUS_IN_PROGRESS = 'In Progress' 58 | 59 | def __init__(self, token, base_url=None): 60 | if not token: 61 | raise ValueError('Authentication token missing') 62 | self.token = token 63 | self.base_url = base_url or TmV1Client.base_url_default 64 | 65 | def make_headers(self, **kwargs): 66 | headers = {} 67 | use_token = kwargs.pop('use_token', True) 68 | if use_token: 69 | headers['Authorization'] = 'Bearer ' + self.token 70 | if 'files' not in kwargs: 71 | headers['Content-Type'] = 'application/json;charset=utf-8' 72 | headers['User-Agent'] = V1_UA 73 | return headers 74 | 75 | def get(self, url_or_path, use_token=True, **kwargs): 76 | kwargs.setdefault('headers', {}).update( 77 | self.make_headers(use_token=use_token) 78 | ) 79 | url = (self.base_url + url_or_path if url_or_path.startswith('/') else 80 | url_or_path) 81 | r = requests.get(url, **kwargs) 82 | if 200 == r.status_code: 83 | if 'application/json' in r.headers.get('Content-Type', ''): 84 | return r.json() 85 | return r.content 86 | raise RuntimeError(f'Request unsuccessful (GET {url_or_path}):' 87 | f' {r.status_code} {r.text}') 88 | 89 | def post(self, path, **kwargs): 90 | kwargs.setdefault('headers', {}).update(self.make_headers(**kwargs)) 91 | r = requests.post(self.base_url + path, **kwargs) 92 | if ((r.status_code in [200, 201, 202, 207]) and 93 | ('application/json' in r.headers.get('Content-Type', ''))): 94 | return r.status_code, r.headers, r.json() 95 | elif r.status_code in [201, 202]: 96 | return r.status_code, r.headers, r.content 97 | raise RuntimeError(f'Request unsuccessful (POST {path}):' 98 | f' {r.status_code} {r.headers} {r.text}') 99 | 100 | def get_items(self, path, **kwargs): 101 | items = [] 102 | next_link = None 103 | while True: 104 | if next_link is None: 105 | r = self.get(path, **kwargs) 106 | else: 107 | r = self.get(next_link, 108 | **{'headers': kwargs.get('headers', {})}) 109 | items.extend(r['items']) 110 | if 'nextLink' not in r: 111 | break 112 | next_link = r['nextLink'] 113 | return items 114 | 115 | def get_from_post_response(self, status_code, headers, body): 116 | if (201 == status_code) and ('Location' in headers): 117 | return self.get(headers['Location']) 118 | if (202 == status_code) and ('Operation-Location' in headers): 119 | return self.get(headers['Operation-Location']) 120 | # response APIs return 'Operation-Location' for duplicated tasks 121 | if (400 == status_code) and ('Operation-Location' in headers): 122 | if isinstance(body, dict): 123 | error = body.get('error', {}) 124 | code = error.get('code', '') 125 | message = error.get('message', '') 126 | if ('TaskError' == code) and ('Task duplication.' == message): 127 | return self.get(headers['Operation-Location']) 128 | 129 | def post_multiple(self, path, **kwargs): 130 | json_ = kwargs.get('json') 131 | if (json_ is not None) and (not isinstance(json_, list)): 132 | raise ValueError('json is not list.') 133 | (_, _, r) = self.post(path, **kwargs) 134 | if json_ is None: 135 | json_ = [None] * len(r) 136 | if len(json_) == len(r): 137 | return [*zip(json_, r)] 138 | raise ValueError('The request and the response do not have the same ' 139 | 'length') 140 | 141 | def get_from_post_multiple_response(self, response): 142 | items = [None] * len(response) 143 | for i, (_, r) in enumerate(response): 144 | headers = dict((h['name'], h['value']) 145 | for h in r.get('headers', [])) 146 | body = r.get('body') 147 | items[i] = self.get_from_post_response(r['status'], headers, body) 148 | return items 149 | 150 | def get_sandbox_sumbission_usage(self): 151 | return self.get('/v3.0/sandbox/submissionUsage') 152 | 153 | def analyze_file(self, file_obj, file_name, archive_password=None, 154 | document_password=None): 155 | data = {} 156 | if archive_password is not None: 157 | data['archivePassword'] = base64.b64encode( 158 | archive_password.encode() 159 | ).decode() 160 | if document_password is not None: 161 | data['documentPassword'] = base64.b64encode( 162 | document_password.encode() 163 | ).decode() 164 | if not file_name: 165 | file_name = file_obj.name 166 | files = {'file': (file_name, file_obj, 'application/octet-stream')} 167 | return self.post('/v3.0/sandbox/files/analyze', data=data, files=files) 168 | 169 | def analyze_url(self, url): 170 | request = get_multiple_request('url', url) 171 | return self.post_multiple('/v3.0/sandbox/urls/analyze', json=request) 172 | 173 | def get_sandbox_tasks(self, ids=None): 174 | params = {} 175 | if ids is not None: 176 | params['filter'] = get_filter_arg('id', ids) 177 | return self.get_items('/v3.0/sandbox/tasks', params=params) 178 | 179 | def get_sandbox_analysis_result(self, analysis_result_id): 180 | return self.get(f'/v3.0/sandbox/analysisResults/{analysis_result_id}') 181 | 182 | def get_sandbox_analysis_report(self, analysis_result_id): 183 | return self.get( 184 | f'/v3.0/sandbox/analysisResults/{analysis_result_id}/report' 185 | ) 186 | 187 | 188 | def wait_sandbox_tasks(v1, tasks): 189 | count = 0 190 | while True: 191 | running_task_indexes = [ 192 | i for i, t in enumerate(tasks) 193 | if t and ('running' == t['status']) 194 | ] 195 | if not running_task_indexes: 196 | break 197 | if not (count < V1_WAIT_TASK_RETRY): 198 | break 199 | count += 1 200 | print((f'Tasks running: {len(running_task_indexes)}; ' 201 | f'Waiting interval (seconds): {V1_WAIT_TASK_INTERVAL}; ' 202 | f'Number of intervals: {count}.')) 203 | time.sleep(V1_WAIT_TASK_INTERVAL) 204 | response = v1.get_sandbox_tasks([tasks[i]['id'] 205 | for i in running_task_indexes]) 206 | for i, r in enumerate(response): 207 | tasks[running_task_indexes[i]].update(r) 208 | finished = not running_task_indexes 209 | if not finished: 210 | print('Tasks not finished') 211 | return finished 212 | 213 | 214 | def fetch_analysis_result(v1, task): 215 | if 'succeeded' == task['status']: 216 | if 'resourceLocation' in task: 217 | return v1.get(task['resourceLocation']) 218 | 219 | 220 | def fetch_analysis_report(v1, result, file_name_prefix): 221 | id_ = result['id'] 222 | if result['riskLevel'] in ['high', 'medium', 'low']: 223 | response = v1.get_sandbox_analysis_report(id_) 224 | dest_name = f'{file_name_prefix}_{id_}.pdf' 225 | with open(dest_name, 'wb') as f: 226 | f.write(response) 227 | return dest_name 228 | 229 | 230 | def need_retry(task): 231 | if 'error' in task: 232 | return 'InternalServerError' == task['error']['code'] 233 | 234 | 235 | def analyze_file(v1, infile, name, archive_password, document_password): 236 | file_name_prefix = 'sandbox_analysis_file' 237 | count = 0 238 | while True: 239 | status_code, headers, response = v1.analyze_file( 240 | infile, name, archive_password, document_password 241 | ) 242 | task = v1.get_from_post_response(status_code, headers, response) 243 | if task is None: 244 | return (name, response, None, None, None) 245 | wait_sandbox_tasks(v1, [task]) 246 | result = fetch_analysis_result(v1, task) 247 | if result is None: 248 | if not (need_retry(task) and (count < V1_ANALYZE_RETRY)): 249 | return (name, response, task, None, None) 250 | count += 1 251 | time.sleep(V1_ANALYZE_INTERVAL) 252 | continue 253 | file_name = fetch_analysis_report(v1, result, file_name_prefix) 254 | if file_name is None: 255 | return (name, response, task, result, None) 256 | return (name, response, task, result, file_name) 257 | 258 | 259 | def analyze_urls(v1, url): 260 | r = [] 261 | file_name_prefix = 'sandbox_analysis_url' 262 | count = 0 263 | while True: 264 | response = v1.analyze_url(url) 265 | tasks = v1.get_from_post_multiple_response(response) 266 | wait_sandbox_tasks(v1, tasks) 267 | 268 | url = [] 269 | for (req, res), task in zip(response, tasks): 270 | name = req['url'] 271 | if task is None: 272 | r.append((name, res, None, None, None)) 273 | continue 274 | result = fetch_analysis_result(v1, task) 275 | if result is None: 276 | if need_retry(task): 277 | # set to url to be retried 278 | url.append(req['url']) 279 | else: 280 | r.append((name, res, task, None, None)) 281 | continue 282 | dest_name = fetch_analysis_report(v1, result, file_name_prefix) 283 | if dest_name is None: 284 | r.append((name, res, task, result, None)) 285 | continue 286 | r.append((name, res, task, result, dest_name)) 287 | if not url: 288 | break 289 | if not (count < V1_ANALYZE_RETRY): 290 | break 291 | count += 1 292 | time.sleep(V1_ANALYZE_INTERVAL) 293 | return r 294 | 295 | 296 | def main(v1_token, v1_url, infile=None, name=None, archive_password=None, 297 | document_password=None, url=None): 298 | v1 = TmV1Client(v1_token, v1_url) 299 | 300 | r = v1.get_sandbox_sumbission_usage() 301 | remaining_count = r['submissionRemainingCount'] 302 | if not (0 < remaining_count): 303 | raise RuntimeError('No submissions available in the daily reserve') 304 | print(f'Submissions available: {remaining_count}') 305 | 306 | results = [] 307 | if infile is not None: 308 | if infile.isatty(): 309 | raise ValueError('sys.stdin has no input') 310 | if not name: 311 | if sys.stdin.name == infile.name: 312 | raise ValueError('file_name must be specified for ' 313 | f'{infile.name}') 314 | name = os.path.basename(infile.name) 315 | print(f'Submitting 1 file...') 316 | r = analyze_file(v1, infile, name, archive_password, 317 | document_password) 318 | results.append(r) 319 | elif url is not None: 320 | if not url: 321 | raise ValueError('No URL specified') 322 | url_count = len(url) 323 | print(f'Submitting {url_count} URLs...') 324 | r = analyze_urls(v1, url) 325 | results.extend(r) 326 | 327 | print('') 328 | for (name, res, task, result, file_name) in results: 329 | if task is None: 330 | error = res.get('body', {}).get('error', '') 331 | print(f'Unable to start analyzing "{name}" task. ' 332 | f'Error code: {error}') 333 | continue 334 | status = task['status'] 335 | if not result: 336 | if 'running' == status: 337 | print(f'The status of the analyzing "{name}" task is ' 338 | f'"{status}". Task ID: {task["id"]}') 339 | elif 'failed' == status: 340 | error = task.get('error', {}) 341 | error_code = error.get('code', '') 342 | if 'Unsupported' == error_code: 343 | print(f'Unable to analyze "{name}". Object not supported.') 344 | else: 345 | print(f'The status of the analyzing "{name}" task is ' 346 | f'"{status}". Error code: {error}') 347 | continue 348 | risk_level = result['riskLevel'] 349 | if not file_name: 350 | print(f'Analyzing: "{name}"; Task status: {status}; ' 351 | f'Risk level: {risk_level}.') 352 | continue 353 | print(f'Analyzing: "{name}"; Task status: {status}; Risk level: ' 354 | f'{risk_level}; Analysis report saved to: {file_name}.') 355 | 356 | 357 | if __name__ == '__main__': 358 | parser = argparse.ArgumentParser( 359 | description=('Submit files or URLs to the sandbox and retrieve the' 360 | ' analysis results if there are submissions available in' 361 | " the daily reserve. When 'Risk level' is equal or" 362 | " higher to 'low', a file (\"sandbox_analysis_" 363 | '_.pdf") with the analysis results' 364 | ' is downloaded.'), 365 | epilog="Refer to examples of the operations 'file' and 'url'.") 366 | parser.add_argument( 367 | '-t', '--v1-token', default=V1_TOKEN, 368 | help='Authentication token of your Trend Vision One user account') 369 | parser.add_argument( 370 | '-u', '--v1-url', default=TmV1Client.base_url_default, 371 | help=('URL of the Trend Vision One server for your region.' 372 | f' The default value is "{TmV1Client.base_url_default}"')) 373 | subparsers = parser.add_subparsers(help='') 374 | file_parser = subparsers.add_parser( 375 | 'file', help='Submit file to the sandbox.', 376 | epilog=(f'Example: python {os.path.basename(__file__)} file' 377 | f' /path/to/file') 378 | ) 379 | file_parser.add_argument( 380 | 'infile', nargs='?', 381 | type=argparse.FileType('rb'), default=sys.stdin.buffer, 382 | help='File to be sent to the sandbox. The default value is stdin.') 383 | file_parser.add_argument( 384 | '-n', '--name', 385 | help=('Name of file to be sent to sandbox. If no value is specified,' 386 | " the last element of the file path path passed to 'infile' is" 387 | " used. When the value of 'infile' is stdin, you must specify" 388 | ' a name.')) 389 | file_parser.add_argument( 390 | '-p', '--archive-password', 391 | help='Password used to decrypt the submitted archive.') 392 | file_parser.add_argument( 393 | '-d', '--document-password', 394 | help='Password used to decrypt the submitted file.') 395 | url_parser = subparsers.add_parser( 396 | 'url', help='Submit URLs to the sandbox.', 397 | epilog=(f'Example: python {os.path.basename(__file__)} url' 398 | ' https://www.trendmicro.com') 399 | ) 400 | url_parser.add_argument( 401 | 'url', nargs='*', 402 | help=('URL to be submitted. A number of URLs can be specified.')) 403 | main(**vars(parser.parse_args())) 404 | -------------------------------------------------------------------------------- /v1-events-to-elasticsearch/python/v1_events_to_elasticsearch.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import json 3 | import argparse 4 | import os 5 | import urllib.parse 6 | import ssl 7 | import getpass 8 | 9 | import requests 10 | import elasticsearch 11 | import elasticsearch.helpers 12 | 13 | # Setting variables 14 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 15 | # Specify the correct domain name for your region in V1_URL 16 | # ref: https://automation.trendmicro.com/xdr/Guides/Regional-Domains 17 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 18 | # This value is used for User-Agent header in API requests. So you can 19 | # customize this value to describe your company name, integration tool name, 20 | # and other values as you like. 21 | # default: "Trend Vision One API Cookbook ({script_name})" 22 | V1_UA = os.environ.get('TMV1_UA', 'Trend Vision One API Cookbook ' 23 | f'({os.path.basename(__file__)})') 24 | ES_URL = os.environ.get('TMV1_ELASTICSEARCH_URL', 'http://localhost:9200') 25 | ES_INDEX_PREFIX = os.environ.get('TMV1_ELASTICSEARCH_INDEX_PREFIX', 'tmv1_') 26 | ES_USER = os.environ.get('TMV1_ELASTICSEARCH_USER') 27 | ES_PASSWORD = os.environ.get('TMV1_ELASTICSEARCH_PASSWORD') 28 | ES_CAFILE = os.environ.get('TMV1_ELASTICSEARCH_CAFILE') 29 | ES_CAPATH = os.environ.get('TMV1_ELASTICSEARCH_CAPATH') 30 | ES_CERTFILE = os.environ.get('TMV1_ELASTICSEARCH_CERTFILE') 31 | ES_KEYFILE = os.environ.get('TMV1_ELASTICSEARCH_KEYFILE') 32 | 33 | 34 | def is_aware_datetime(d): 35 | return (d.tzinfo is not None) and (d.tzinfo.utcoffset(d) is not None) 36 | 37 | 38 | def get_datetime_param(d): 39 | if not is_aware_datetime(d): 40 | d = d.astimezone() 41 | d = d.astimezone(datetime.timezone.utc) 42 | d = d.isoformat(timespec='seconds').replace('+00:00', 'Z') 43 | return d 44 | 45 | 46 | class TmV1Client: 47 | base_url_default = V1_URL 48 | oat_top = [50, 100, 200] 49 | search_top = [50, 100, 500, 1000, 5000] 50 | audit_logs_top = [50, 100, 200] 51 | 52 | def __init__(self, token, base_url=None): 53 | if not token: 54 | raise ValueError('Authentication token missing') 55 | self.token = token 56 | self.base_url = base_url or TmV1Client.base_url_default 57 | 58 | def make_headers(self, **kwargs): 59 | headers = {} 60 | use_token = kwargs.pop('use_token', True) 61 | if use_token: 62 | headers['Authorization'] = 'Bearer ' + self.token 63 | if 'files' not in kwargs: 64 | headers['Content-Type'] = 'application/json;charset=utf-8' 65 | headers['User-Agent'] = V1_UA 66 | return headers 67 | 68 | def get(self, url_or_path, use_token=True, **kwargs): 69 | kwargs.setdefault('headers', {}).update( 70 | self.make_headers(use_token=use_token) 71 | ) 72 | url = (self.base_url + url_or_path if url_or_path.startswith('/') else 73 | url_or_path) 74 | r = requests.get(url, **kwargs) 75 | if 200 == r.status_code: 76 | if 'application/json' in r.headers.get('Content-Type', ''): 77 | return r.json() 78 | return r.content 79 | raise RuntimeError(f'Request unsuccessful (GET {url_or_path}):' 80 | f' {r.status_code} {r.text}') 81 | 82 | def get_items(self, path, **kwargs): 83 | items = [] 84 | next_link = None 85 | while True: 86 | if next_link is None: 87 | r = self.get(path, **kwargs) 88 | else: 89 | r = self.get(next_link, 90 | **{'headers': kwargs.get('headers', {})}) 91 | items.extend(r['items']) 92 | if 'nextLink' not in r: 93 | break 94 | next_link = r['nextLink'] 95 | return items 96 | 97 | def get_workbench_alerts(self, start=None, end=None): 98 | params = {} 99 | if start is not None: 100 | params['startDateTime'] = get_datetime_param(start) 101 | if end is not None: 102 | params['endDateTime'] = get_datetime_param(end) 103 | return self.get_items('/v3.0/workbench/alerts', params=params) 104 | 105 | def get_oat(self, start=None, end=None, top=None): 106 | params = {} 107 | if start is not None: 108 | params['detectedStartDateTime'] = get_datetime_param(start) 109 | if end is not None: 110 | params['detectedEndDateTime'] = get_datetime_param(end) 111 | if top is not None: 112 | params['top'] = top 113 | return self.get_items('/v3.0/oat/detections', params=params) 114 | 115 | def get_detection(self, start=None, end=None, top=None): 116 | params = {} 117 | if start is not None: 118 | params['startDateTime'] = get_datetime_param(start) 119 | if end is not None: 120 | params['endDateTime'] = get_datetime_param(end) 121 | if top is not None: 122 | params['top'] = top 123 | headers = {'TMV1-QUERY': 'hostName: *'} 124 | return self.get_items('/v3.0/search/detections', 125 | params=params, headers=headers) 126 | 127 | def get_audit_logs(self, start=None, end=None, top=None): 128 | params = {'labels': 'all'} 129 | if start is not None: 130 | params['startDateTime'] = get_datetime_param(start) 131 | if end is not None: 132 | params['endDateTime'] = get_datetime_param(end) 133 | if top is not None: 134 | params['top'] = top 135 | return self.get_items('/v3.0/audit/logs', params=params) 136 | 137 | 138 | def correct_data(docs): 139 | """ 140 | This function correct VisionOne data for Elasticsearch 141 | 142 | 1. The workbench items have ['impactScope'][N]['entityValue'] and 143 | ['indicators'][N]['value'] have two kinds of types; One is string and 144 | the other is object. 145 | Because Elasticsearch cannot define the union of both string and object, 146 | this function renames the 'entityValue' and 'objectValue' fields as the 147 | same as the value of 'entityType' field and 'type' fields combined with 148 | the type name of the value, respectively. 149 | 150 | 2. The three kinds of data have different names for timestamp. 151 | This function names the same field for timestamp, 'esBaseDateTime'. 152 | 153 | 3. Both workbench and detections have the 'severity' field with different 154 | type; Workbench is string and detections is integer. 155 | Because Elasticsearch cannot define the union of both string and 156 | integer, this function name the string field to another one, 157 | 'severityString'. 158 | 159 | 4. The observed techniques have the 160 | ['filters'][N]['highligthtedObjects'][M]['value'] with different types 161 | specified by ['filters'][N]['highligthedObject'][M]['type'] field; 162 | For example, when 'type' is 'port', 'value' is integer: when 'type' is 163 | 'text', 'value' is string. 164 | Because Elasticsearch cannot define the union of all types, this 165 | function renames the value field as the same as the value of 'type' 166 | field; For example, 'type': 'host', 'host': xxx. 167 | In addition, some values, such as for the 'field' is 'ruleId', are not 168 | string type even when 'type' is 'text'. So, these values are forced to 169 | be stringized. 170 | 171 | 5. The observed techniques have the ['detail'] that has several kinds 172 | of objects with different properties by source. 173 | Because Elasticsearch cannot define the union of them, this function 174 | renames this field to the source name stored in ['source']. 175 | 176 | 6. The audit logs have the ['details'] including some values that are 177 | represented as either JSON or its stringized value. 178 | Because Elasticsearch cannot define the union of them, this function 179 | parses the stringized value to corresponding Python-typed values; For 180 | example, 'details.hasDetail' and 181 | 'details.policyList.endpointSensorDetectionAndResponseSetting'. 182 | """ 183 | for d in docs['workbench']: 184 | for entity in d['impactScope']['entities']: 185 | entity[entity['entityType']] = entity['entityValue'] 186 | del entity['entityValue'] 187 | for entity in d['indicators']: 188 | name = entity['type'] + '_' + type(entity['value']).__name__ 189 | entity[name] = entity['value'] 190 | del entity['value'] 191 | if 'severity' in d: 192 | d['severityString'] = d['severity'] 193 | del d['severity'] 194 | d['esBaseDateTime'] = d['createdDateTime'] 195 | for d in docs['observed_techniques']: 196 | d['esBaseDateTime'] = d['detectedDateTime'] 197 | for f in d.get('filters', []): 198 | for obj in f.get('highlightedObjects', []): 199 | if (('text' == obj['type']) and 200 | (not isinstance(obj['value'], str))): 201 | obj['value'] = str(obj['value']) 202 | obj[obj['type']] = obj['value'] 203 | del obj['value'] 204 | if 'detail' in d: 205 | d[d['source']] = d['detail'] 206 | del d['detail'] 207 | if 'detections' in docs: 208 | for d in docs['detections']: 209 | d['esBaseDateTime'] = d['eventTimeDT'].replace('+00:00', 'Z') 210 | if 'audit_logs' in docs: 211 | for d in docs['audit_logs']: 212 | d['esBaseDateTime'] = d['loggedDateTime'] 213 | if 'hasDetail' in d['details']: 214 | if 'True' == d['details']['hasDetail']: 215 | d['details']['hasDetail'] = True 216 | elif 'False' == d['details']['hasDetail']: 217 | d['details']['hasDetail'] = False 218 | if 'policyList' in d['details']: 219 | policyList = d['details']['policyList'] 220 | k = 'endpointSensorDetectionAndResponseSetting' 221 | if k in policyList: 222 | v = policyList[k] 223 | if str == type(v): 224 | policyList[k] = json.loads(v) 225 | 226 | 227 | def index_data_to_es(es, docs): 228 | def index_actions(name, data): 229 | for source in data: 230 | yield { 231 | '_index': name, 232 | '_op_type': 'index', 233 | '_source': source 234 | } 235 | for name, data in docs.items(): 236 | if 'observed_techniques' in name: 237 | if not es.indices.exists(index=name): 238 | es.indices.create(index=name) 239 | r = es.indices.get_settings( 240 | index=name, 241 | name='index.mapping.total_fields.limit', 242 | include_defaults=True 243 | )[name] 244 | settings = r['defaults'] 245 | settings.update(r['settings']) 246 | limit = int(settings['index']['mapping']['total_fields']['limit']) 247 | if limit < 2000: 248 | es.indices.put_settings(index=name, settings={ 249 | "index.mapping.total_fields.limit": 2000 250 | }) 251 | try: 252 | elasticsearch.helpers.bulk(es, index_actions(name, data)) 253 | except elasticsearch.helpers.BulkIndexError as e: 254 | print(f'Bulk index error: {name}') 255 | print(e.errors[0].get('index', {}).get('error', {}).get('reason')) 256 | raise e 257 | 258 | 259 | def pull_v1_data_to_es(v1, es, start, end, index_prefix, include_detections, 260 | include_audit_logs): 261 | if not es.ping(): 262 | raise RuntimeError('Elasticsearch server unavailable') 263 | docs = {} 264 | docs['workbench'] = v1.get_workbench_alerts(start, end) 265 | print(f'Retrieved workbench alerts: {len(docs["workbench"])}') 266 | docs['observed_techniques'] = v1.get_oat(start, end, 267 | TmV1Client.oat_top[-1]) 268 | print('Retrieved Observed Attack Techniques events: ' 269 | f'{len(docs["observed_techniques"])}') 270 | if include_detections: 271 | docs['detections'] = v1.get_detection(start, end, 272 | TmV1Client.search_top[-1]) 273 | print(f'Retrieved detections: {len(docs["detections"])}') 274 | if include_audit_logs: 275 | docs['audit_logs'] = v1.get_audit_logs(start, end, 276 | TmV1Client.audit_logs_top[-1]) 277 | print(f'Retrieved audit logs: {len(docs["audit_logs"])}') 278 | 279 | correct_data(docs) 280 | for name in list(docs.keys()): 281 | docs[index_prefix + name] = docs[name] 282 | del docs[name] 283 | index_data_to_es(es, docs) 284 | 285 | 286 | def main(start, end, days, v1_token, v1_url, detections, audit_logs, es_url, 287 | prefix, es_user, es_password, es_cafile, es_capath, es_certfile, 288 | es_keyfile): 289 | if end is None: 290 | end = datetime.datetime.now(datetime.timezone.utc) 291 | else: 292 | end = datetime.datetime.fromisoformat(end) 293 | if start is None: 294 | start = end + datetime.timedelta(days=-days) 295 | else: 296 | start = datetime.datetime.fromisoformat(start) 297 | host = [es_url] 298 | basic_auth = None 299 | ssl_context = None 300 | if es_user: 301 | if not es_password: 302 | es_password = getpass.getpass() 303 | basic_auth = (es_user, es_password) 304 | if 'https' == urllib.parse.urlparse(es_url).scheme: 305 | ssl_context = ssl.create_default_context( 306 | cafile=es_cafile, 307 | capath=es_capath 308 | ) 309 | if ssl_context and (es_certfile or es_keyfile): 310 | ssl_context.load_cert_chain( 311 | certfile=es_certfile, 312 | keyfile=es_keyfile 313 | ) 314 | v1 = TmV1Client(v1_token, v1_url) 315 | es = elasticsearch.Elasticsearch( 316 | host, 317 | basic_auth=basic_auth, 318 | ssl_context=ssl_context 319 | ) 320 | pull_v1_data_to_es(v1, es, start, end, prefix, detections, audit_logs) 321 | 322 | 323 | if __name__ == '__main__': 324 | parser = argparse.ArgumentParser( 325 | description=('Send Workbench alerts, other detection data and audit' 326 | ' logs to Elasticsearch'), 327 | epilog=(f'Example: python {os.path.basename(__file__)} ' 328 | '-e 2021-04-12T14:28:00.123456+00:00 -d 5 -D')) 329 | parser.add_argument( 330 | '-t', '--v1-token', default=V1_TOKEN, 331 | help='Authentication token of your Trend Vision One user account') 332 | parser.add_argument( 333 | '-u', '--v1-url', 334 | default=TmV1Client.base_url_default, 335 | help=('URL of the Trend Vision One server for your region.' 336 | f' The default value is "{TmV1Client.base_url_default}"')) 337 | parser.add_argument( 338 | '-s', '--start', 339 | help=('Timestamp in ISO 8601 format that indicates the start of' 340 | ' the data retrieval time range')) 341 | parser.add_argument( 342 | '-e', '--end', 343 | help=('Timestamp in ISO 8601 format that indicates the end of the data' 344 | ' retrieval time range. The default value is the current time.')) 345 | parser.add_argument( 346 | '-d', '--days', type=int, default=5, 347 | help=('Number of days before the end time of the data retrieval' 348 | ' time range. The default value is 5.')) 349 | parser.add_argument( 350 | '-D', '--detections', action='store_true', 351 | help=('Parameter that searches the "Detections" data via API' 352 | ' and sends matching records to Elasticsearch')) 353 | parser.add_argument( 354 | '-a', '--audit-logs', action='store_true', 355 | help=('Parameter that searches audit logs data using the API' 356 | ' and sends matching records to Elasticsearch.')) 357 | parser.add_argument( 358 | '-E', '--es-url', default=ES_URL, 359 | help=('URL of the Elasticsearch server. The default value is' 360 | f' "{ES_URL}"')) 361 | parser.add_argument( 362 | '-p', '--prefix', default=ES_INDEX_PREFIX, 363 | help=('Prefix of indices in Elasticsearch. The default value is' 364 | f' "{ES_INDEX_PREFIX}"')) 365 | parser.add_argument( 366 | '-U', '--es-user', default=ES_USER, 367 | help='Username for Elasticsearch authentication') 368 | parser.add_argument( 369 | '-P', '--es-password', default=ES_PASSWORD, 370 | help=('Password for Elasticsearch authentication. If you specify' 371 | ' the username parameter ("--es-user") but not the password' 372 | ' parameter (--es-password), the system prompts you for' 373 | ' the password.')) 374 | parser.add_argument( 375 | '-f', '--es-cafile', default=ES_CAFILE, 376 | help=('Path to a file containing a CA certificate for TLS or SSL' 377 | ' connection to Elasticsearch')) 378 | parser.add_argument( 379 | '-c', '--es-capath', default=ES_CAPATH, 380 | help=('Path to a directory containing CA certificates for TLS or SSL' 381 | ' connection to Elasticsearch')) 382 | parser.add_argument( 383 | '-C', '--es-certfile', default=ES_CERTFILE, 384 | help=('Path to a file containing a certificate for TLS or SSL' 385 | ' connection to Elasticsearch')) 386 | parser.add_argument( 387 | '-k', '--es-keyfile', default=ES_KEYFILE, 388 | help=('Path to a file containing a private key for TLS or SSL' 389 | ' connection to Elasticsearch')) 390 | main(**vars(parser.parse_args())) 391 | -------------------------------------------------------------------------------- /documentation-security-posture/python/documentation_security_posture.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import itertools 3 | import argparse 4 | import os 5 | import numbers 6 | import math 7 | 8 | import requests 9 | import pandas 10 | import pptx 11 | import pptx.chart.data 12 | import yaml 13 | 14 | # Setting variables 15 | V1_TOKEN = os.environ.get('TMV1_TOKEN', '') 16 | # Specify the correct domain name for your region in V1_URL 17 | # ref: https://automation.trendmicro.com/xdr/Guides/Regional-Domains 18 | V1_URL = os.environ.get('TMV1_URL', 'https://api.xdr.trendmicro.com') 19 | # This value is used for User-Agent header in API requests. So you can 20 | # customize this value to describe your company name, integration tool name, 21 | # and so on as you like. 22 | # default: "Trend Vision One API Cookbook ({script_name})" 23 | V1_UA = os.environ.get('TMV1_UA', 'Trend Vision One API Cookbook ' 24 | f'({os.path.basename(__file__)})') 25 | V1_XLSX_FILENAME = os.environ.get('TMV1_XLSX_FILENAME', 26 | 'security_posture.xlsx') 27 | V1_PPTX_FILENAME = os.environ.get('TMV1_PPTX_FILENAME', 28 | 'security_posture.pptx') 29 | V1_YAML_FILENAME = os.environ.get( 30 | 'TMV1_YAML_FILENAME', 31 | os.path.join( 32 | os.path.dirname(os.path.realpath(__file__)), 'security_posture.yaml' 33 | ) 34 | ) 35 | 36 | 37 | def is_container(v): 38 | try: 39 | if isinstance(v, (str, bytes)): 40 | return False 41 | iter(v) 42 | except TypeError: 43 | return False 44 | return True 45 | 46 | 47 | def split_camel_case(s): 48 | if not s: 49 | return [] 50 | indexes = [0] 51 | length = len(s) 52 | indexes.extend(i for i in range(1, length-1) if ( 53 | s[i].isupper() and (s[i-1].islower() or s[i+1].islower()) 54 | )) 55 | indexes.append(length) 56 | return [s[indexes[i]:indexes[i+1]] for i in range(len(indexes) - 1)] 57 | 58 | 59 | def unique_list(v): 60 | return list(dict.fromkeys(v)) 61 | 62 | 63 | class TmV1Client: 64 | base_url_default = V1_URL 65 | 66 | def __init__(self, token, base_url=None): 67 | if not token: 68 | raise ValueError('Authentication token missing') 69 | self.token = token 70 | self.base_url = base_url or TmV1Client.base_url_default 71 | 72 | def make_headers(self, **kwargs): 73 | headers = {} 74 | use_token = kwargs.pop('use_token', True) 75 | if use_token: 76 | headers['Authorization'] = 'Bearer ' + self.token 77 | headers['User-Agent'] = V1_UA 78 | return headers 79 | 80 | def get(self, url_or_path, use_token=True, **kwargs): 81 | kwargs.setdefault('headers', {}).update( 82 | self.make_headers(use_token=use_token) 83 | ) 84 | url = (self.base_url + url_or_path if url_or_path.startswith('/') 85 | else url_or_path) 86 | r = requests.get(url, **kwargs) 87 | if 200 == r.status_code: 88 | if 'application/json' in r.headers.get('Content-Type', ''): 89 | return r.json() 90 | return r.content 91 | raise RuntimeError(f'Request unsuccessful (GET {url_or_path}):' 92 | f' {r.status_code} {r.text}') 93 | 94 | def get_security_posture(self): 95 | return self.get('/v3.0/asrm/securityPosture') 96 | 97 | 98 | unit_suffixes = ['count', 'density', 'days', 'rate'] 99 | 100 | 101 | def get_unit(s): 102 | suffix = split_camel_case(s)[-1].lower() 103 | return suffix if suffix in unit_suffixes else '' 104 | 105 | 106 | def iter_nested_dict(value, keys=[]): 107 | """ 108 | This yields nested dicts that don't contain any dict value. 109 | After yields dicts, this removes them from their parent dict. 110 | So, this also yields these parents as a dict without any dict values. 111 | If the units (suffix) of all keys are the same, this yields the parent 112 | dict. Or, this yield a dict containing only each key-value pair. 113 | 114 | When handling a list including dicts, this replaces the list-index of 115 | each dict to the dict-key with the value of its 1st key. 116 | After that, if all dicts in a list have only one value for the same key, 117 | this combine them to one dict. 118 | When a list including dicts is empty, this skips to record it. 119 | """ 120 | if isinstance(value, dict): 121 | units = set() 122 | iters = {} 123 | key_copy = list(value.keys()) 124 | for k in key_copy: 125 | if keys and isinstance(keys[-1], int): 126 | # When parent is list, replace index to value of the 1st key 127 | keys[-1] = value[k] 128 | del value[k] 129 | continue 130 | if isinstance(value[k], list) and not value[k]: 131 | # When empty list, skip to record it 132 | del value[k] 133 | continue 134 | i = iter_nested_dict(value[k], keys + [k]) 135 | try: 136 | f = next(i) 137 | # contain dict 138 | iters[k] = itertools.chain([f], i) 139 | del value[k] 140 | except StopIteration: 141 | # not contain dict 142 | units.add(get_unit(k)) 143 | is_value_iterated = False 144 | for k in key_copy: 145 | if k in iters: 146 | yield from iters[k] 147 | continue 148 | if not is_value_iterated: 149 | if 1 == len(units): 150 | yield (keys, value) 151 | is_value_iterated = True 152 | else: 153 | if k in value: 154 | yield (keys + [k], {k: value[k]}) 155 | elif isinstance(value, list): 156 | a = list(itertools.chain.from_iterable( 157 | iter_nested_dict(v, keys + [i]) for i, v in enumerate(value) 158 | )) 159 | # check combinable; All elements are dicts that don't contain any dict 160 | # and that have only one value with the same key 161 | depth = len(keys) + 1 162 | dict_keys = set() 163 | for k, v in a: 164 | if not (len(k) == depth): 165 | raise ValueError('Data structure not supported.') 166 | dict_keys |= set(v.keys()) 167 | if 1 == len(dict_keys): 168 | # combine dicts by replacing the same key and k[-1] 169 | new_k = next(iter(dict_keys)) 170 | d = {} 171 | for (k, v) in a: 172 | d.update({k[-1]: v[new_k]}) 173 | a = [(keys + [new_k], d)] 174 | yield from a 175 | 176 | 177 | abbreviations = ['xdr', 'cve', 'os', 'edr'] 178 | 179 | 180 | def make_title(s): 181 | strings = s if is_container(s) else [s] 182 | words = list(itertools.chain.from_iterable( 183 | split_camel_case(word) for s in strings for word in s.split() 184 | )) 185 | words = [w.capitalize() if w and w[0].islower() else w for w in words] 186 | words = [w.upper() if w.lower() in abbreviations else w for w in words] 187 | return ' '.join(unique_list(words)) 188 | 189 | 190 | def change_sheet_names(dataframes, sheet_names): 191 | for sheet_name in list(dataframes.keys()): 192 | if sheet_name in sheet_names: 193 | new_sheet_name = sheet_names[sheet_name] 194 | if new_sheet_name in dataframes: 195 | raise RuntimeError(f'Worksheet {new_sheet_name} already ' 196 | 'exists.') 197 | dataframes[new_sheet_name] = dataframes.pop(sheet_name) 198 | sheet_name = new_sheet_name 199 | if 32 <= len(sheet_name): 200 | raise RuntimeError(f"Worksheet name '{sheet_name}' must exceeds " 201 | 'the 31 character maximum.') 202 | 203 | 204 | def restore_sheet_names(dataframes, sheet_names): 205 | for sheet_name in list(dataframes.keys()): 206 | found = [k for k, v in sheet_names.items() if v == sheet_name] 207 | if 1 == len(found): 208 | new_sheet_name = found[0] 209 | dataframes[new_sheet_name] = dataframes.pop(sheet_name) 210 | 211 | 212 | def load_config(path): 213 | with open(path) as f: 214 | return yaml.safe_load(f) 215 | 216 | 217 | def load_dataframes(data_source): 218 | return pandas.read_excel(data_source, engine='openpyxl', sheet_name=None) 219 | 220 | 221 | datetime_field_name = 'createdDateTime' 222 | datetime_column_name = make_title(datetime_field_name) 223 | general_fields = ['schemaVersion', 'companyId', 'companyName'] 224 | general_sheet_name = 'General' 225 | begin_column_name = 'Begin' 226 | end_column_name = 'End' 227 | 228 | 229 | def to_dataframes(d): 230 | datetime_value = datetime.datetime.fromisoformat( 231 | d.pop(datetime_field_name).replace('Z', '') 232 | ) 233 | dataframes = {} 234 | dataframes[general_sheet_name] = pandas.DataFrame({ 235 | make_title(k): [d.pop(k)] for k in general_fields if k in d 236 | }) 237 | dataframes[general_sheet_name].at[0, begin_column_name] = datetime_value 238 | dataframes[general_sheet_name].at[0, end_column_name] = datetime_value 239 | 240 | for keys, value in iter_nested_dict(d): 241 | sheet_name = make_title(keys) 242 | v = pandas.DataFrame({make_title(k): v if is_container(v) else [v] 243 | for k, v in value.items()}) 244 | v.insert(0, datetime_column_name, datetime_value) 245 | dataframes[sheet_name] = v 246 | return dataframes 247 | 248 | 249 | def concat_dataframes(*args): 250 | if args: 251 | dataframes = args[0] 252 | general = dataframes[general_sheet_name] 253 | for arg in args[1:]: 254 | for sheet_name, dataframe in arg.items(): 255 | if sheet_name in dataframes: 256 | if general_sheet_name == sheet_name: 257 | begin = dataframe.at[0, begin_column_name] 258 | if begin < general.at[0, begin_column_name]: 259 | general.at[0, begin_column_name] = begin 260 | end = dataframe.at[0, end_column_name] 261 | if general.at[0, end_column_name] < end: 262 | general.at[0, end_column_name] = end 263 | continue 264 | dataframes[sheet_name] = pandas.concat([ 265 | dataframe, dataframes[sheet_name] 266 | ]) 267 | else: 268 | dataframes[sheet_name] = dataframe 269 | return dataframes 270 | 271 | 272 | def to_excel(dataframes, data_source): 273 | kwargs = {'engine': 'openpyxl', 'mode': 'w'} 274 | if os.path.isfile(data_source): 275 | kwargs['mode'] = 'a' 276 | kwargs['if_sheet_exists'] = 'replace' 277 | with pandas.ExcelWriter(data_source, **kwargs) as w: 278 | for sheet_name, dataframe in dataframes.items(): 279 | dataframe.to_excel(w, sheet_name=sheet_name, index=False) 280 | 281 | 282 | def export(v1, data_source, config): 283 | sheet_names = config.get('sheet names', {}) 284 | previous_dataframes = [] 285 | if os.path.isfile(data_source): 286 | dataframes = load_dataframes(data_source) 287 | restore_sheet_names(dataframes, sheet_names) 288 | previous_dataframes.append(dataframes) 289 | dataframes = to_dataframes(v1.get_security_posture()) 290 | dataframes = concat_dataframes(*previous_dataframes, dataframes) 291 | change_sheet_names(dataframes, sheet_names) 292 | to_excel(dataframes, data_source) 293 | 294 | 295 | level_sheet_name = 'Risk Category Level' 296 | level_values = ['low', 'medium', 'high'] 297 | 298 | 299 | def correct_dataframes(dataframes): 300 | """ 301 | This function correct VisionOne data for presentation 302 | 303 | 1. The 'Risk Category Level' has only string values. To make a line chart, 304 | the value replaced from 'low', 'medium' and 'high' to 0, 1 and 2, 305 | respectively. 306 | 307 | 2. Some sheets have NaN values. To make line charts, the value is replaecd 308 | to empty string. 309 | """ 310 | dataframe = dataframes[level_sheet_name] 311 | for i, row in dataframe.iterrows(): 312 | for column in dataframe.columns: 313 | if datetime_column_name == column: 314 | continue 315 | dataframe.at[i, column] = level_values.index(row[column]) 316 | 317 | for dataframe in dataframes.values(): 318 | for i, row in dataframe.iterrows(): 319 | for column in dataframe.columns: 320 | if datetime_column_name == column: 321 | continue 322 | v = row[column] 323 | if isinstance(v, numbers.Number) and math.isnan(v): 324 | dataframe.at[i, column] = '' 325 | 326 | 327 | def make_chart_slide(p, title): 328 | title_only_slide_layout = p.slide_layouts[5] 329 | slide = p.slides.add_slide(title_only_slide_layout) 330 | slide.shapes.title.text = title 331 | lines = len(slide.shapes.title.text) / 30 332 | if 1 < lines: 333 | font_size = pptx.util.Pt(44 - 4*lines) 334 | slide.shapes.title.text_frame.paragraphs[0].font.size = font_size 335 | return slide 336 | 337 | 338 | def add_shape_chart_line(p, slide, dataframe): 339 | chart_data = pptx.chart.data.ChartData() 340 | for column_name, items in dataframe.items(): 341 | if datetime_column_name == column_name: 342 | # make category as date axis 343 | chart_data.categories = [item.to_pydatetime() for item in items] 344 | else: 345 | chart_data.add_series(column_name, tuple(item for item in items)) 346 | x = slide.shapes.title.left 347 | y = slide.shapes.title.top + slide.shapes.title.height 348 | cx, cy = slide.shapes.title.width, (p.slide_height - y) 349 | chart = slide.shapes.add_chart( 350 | pptx.enum.chart.XL_CHART_TYPE.LINE, x, y, cx, cy, chart_data 351 | ).chart 352 | chart.has_title = False 353 | chart.has_legend = True 354 | chart.legend.include_in_layout = True 355 | for s in chart.series: 356 | s.smooth = False 357 | chart.font.size = pptx.util.Pt(12) 358 | return chart 359 | 360 | 361 | presentation_title = 'Security Posture' 362 | 363 | 364 | def to_powerpoint(dataframes, presentation): 365 | p = pptx.Presentation() 366 | general = dataframes.pop(general_sheet_name) 367 | presentation_subtitle = os.linesep.join([ 368 | general[make_title(general_fields[2])][0], 369 | f'{begin_column_name}: {general.at[0, begin_column_name]}', 370 | f'{end_column_name}: {general.at[0, end_column_name]}' 371 | ]) 372 | title_slide_layout = p.slide_layouts[0] 373 | slide = p.slides.add_slide(title_slide_layout) 374 | slide.shapes.title.text = presentation_title 375 | slide.placeholders[1].text = presentation_subtitle 376 | for sheet_name, dataframe in dataframes.items(): 377 | slide = make_chart_slide(p, sheet_name) 378 | add_shape_chart_line(p, slide, dataframe) 379 | p.save(presentation) 380 | 381 | 382 | def report(data_source, presentation, config): 383 | sheet_names = config.get('sheet names', {}) 384 | dataframes = load_dataframes(data_source) 385 | restore_sheet_names(dataframes, sheet_names) 386 | correct_dataframes(dataframes) 387 | to_powerpoint(dataframes, presentation) 388 | 389 | 390 | def main(data_source, config, v1_token=None, v1_url=None, presentation=None): 391 | config = load_config(config) 392 | if presentation is not None: 393 | report(data_source, presentation, config) 394 | else: 395 | v1 = TmV1Client(v1_token, v1_url) 396 | export(v1, data_source, config) 397 | 398 | 399 | if __name__ == '__main__': 400 | common_parser = argparse.ArgumentParser(add_help=False) 401 | common_parser.add_argument( 402 | '-s', '--data-source', default=V1_XLSX_FILENAME, 403 | help='File name of the XLSX file with the retrieved security metrics. ' 404 | f'Default: `{V1_XLSX_FILENAME}`.' 405 | ) 406 | common_parser.add_argument( 407 | '-c', '--config', default=V1_YAML_FILENAME, 408 | help='File name of the configuration file. Default: ' 409 | f'`{V1_YAML_FILENAME}`.' 410 | ) 411 | parser = argparse.ArgumentParser( 412 | description=('Generate a custom report from the Security Posture ' 413 | 'information.'), 414 | epilog="Refer to examples of the operations 'export' and 'report'.") 415 | subparsers = parser.add_subparsers(help='') 416 | export_parser = subparsers.add_parser( 417 | 'export', parents=[common_parser], 418 | help=('Generate an XLSX file to store the retrieved security metrics ' 419 | 'from the Security Posture API.'), 420 | epilog=(f'Example: python {os.path.basename(__file__)} export ' 421 | f'-s /path/to/file') 422 | ) 423 | export_parser.add_argument( 424 | '-t', '--v1-token', default=V1_TOKEN, 425 | help='Authentication token of your Trend Vision One user account.' 426 | ) 427 | export_parser.add_argument( 428 | '-u', '--v1-url', default=TmV1Client.base_url_default, 429 | help=('URL of the Trend Vision One server for your region.' 430 | f' The default value is "{TmV1Client.base_url_default}"') 431 | ) 432 | report_parser = subparsers.add_parser( 433 | 'report', parents=[common_parser], 434 | help=('Generate a PPTX file to present the security metrics stored in ' 435 | 'the XLSX file.'), 436 | epilog=(f'Example: python {os.path.basename(__file__)} report' 437 | f'-p /path/to/file') 438 | ) 439 | report_parser.add_argument( 440 | '-p', '--presentation', default=V1_PPTX_FILENAME, 441 | help=('File name of the PPTX file with the metrics. Default: ' 442 | f'`{V1_PPTX_FILENAME}`.')) 443 | main(**vars(parser.parse_args())) 444 | --------------------------------------------------------------------------------