├── 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 |
--------------------------------------------------------------------------------