Intezer v2

Malware detection and analysis based on code reuse.

Forensics & Malware Analysis · Intezer

Details

IDIntezer v2
ProviderSymphony Talent Group
CategoryForensics & Malware Analysis
From Version5.0.0
Docker Imagedemisto/py3-tools:1.0.0.114656
Supported ModulesAgentix XSIAM

README

Use the Intezer v2 integration to detect and analyze malware, based on code reuse.

Configure Intezer v2 in Cortex

Parameter Description Required
API Key   True
Intezer Analyze Base URL The API address to intezer Analyze - i.e. https://analyze.intezer.com/api/ False
Use system proxy settings   False
Trust any certificate (not secure)   False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

intezer-analyze-by-hash


Checks file reputation of the given hash, supports SHA256, SHA1 and MD5

Base Command

intezer-analyze-by-hash

Input

Argument Name Description Required
file_hash Hash of the file to query. Supports SHA256, MD5 and SHA1. Required
wait_for_result Waits for analysis result, support polling Optional
interval Number of seconds between poll requests Optional
timeout Number of seconds until polling timeout Optional

Context Output

Path Type Description
Intezer.Analysis.ID string Intezer analysis id
Intezer.Analysis.Status string status of the analysis
Intezer.Analysis.Type string type of the analysis

Command Example

!intezer-analyze-by-hash file_hash="<file hash>"

Context Example

{
    "Intezer.Analysis": {
        "Status": "Created", 
        "type": "File", 
        "ID": "59e2f081-45f3-4822-bf45-407670dcb4d7"
    }
}

Human Readable Output

Analysis created successfully: 59e2f081-45f3-4822-bf45-407670dcb4d7

intezer-analyze-url


Checks file reputation of the given URL

Notice: Submitting indicators using this command might make the indicator data publicly available. See the vendor’s documentation for more details.

Base Command

intezer-analyze-url

Input

Argument Name Description Required
Url Url to query. Required

Context Output

Path Type Description
Intezer.Analysis.ID string Intezer analysis id
Intezer.Analysis.Status string status of the analysis
Intezer.Analysis.Type string type of the analysis
URL.Data string The submitted Url
URL.Malicious.Vendor string For malicious Url, the vendor that made the decision
URL.Metadata Unknown Metadata returned from Intezer analysis
URL.ExistsInIntezer Boolean Does the url exists on intezer

Command Example

!intezer-analyze-url url="<url>"

Context Example

{
    "Intezer.Analysis": {
        "Status": "Created", 
        "type": "Url", 
        "ID": "59e2f081-45f3-4822-bf45-407670dcb4d7"
    }
}

Human Readable Output

Analysis created successfully: 59e2f081-45f3-4822-bf45-407670dcb4d7

intezer-get-latest-report


Checks file reputation of the given hash, supports SHA256, SHA1 and MD5 by looking at the latest available report

Base Command

intezer-get-latest-report

Input

Argument Name Description Required
file_hash Hash of the file to query. Supports SHA256, MD5 and SHA1. Required

Context Output

Path Type Description
File.SHA256 string Hash SHA256
File.Malicious.Vendor string For malicious files, the vendor that made the decision
DBotScore.Indicator string The indicator we tested
DBotScore.Type string The type of the indicator
DBotScore.Vendor string Vendor used to calculate the score
DBotScore.Score number The actual score
File.Metadata Unknown Metadata returned from Intezer analysis (analysis id, analysis url, family, family type, sha256, verdict, sub_verdict). Metadata will be returned only for supported files.
File.ExistsInIntezer Boolean Does the file exists on intezer genome database

Command Example

intezer-get-latest-report file_hash="8cbf90aeab2c93b2819fcfd6262b2cdb"

Context Example

{
    "DBotScore": {
        "Vendor": "Intezer", 
        "Indicator": "<some sha>>", 
        "Score": 0, 
        "Type": "hash"
    }, 
    "File": {
        "ExistsInIntezer": true, 
        "SHA256": "<some sha256>", 
        "Metadata": {
            "analysis_id": "006c54ba-3159-43a0-98a0-1c5032145f47", 
            "sub_verdict": "known_malicious", 
            "analysis_url": "https://analyze.intezer.com/analyses/006c54ba-3159-43a0-98a0-1c5032145f47", 
            "verdict": "malicious", 
            "family_id": "0b13c0d4-7779-4c06-98fa-4d33ca98f8a9",
            "family_name": "WannaCry",
            "sha256": "<some sha256>",
            "is_private": true, 
            "analysis_time": "Wed, 19 Jun 2019 07:48:12 GMT"
        }
    }
}

Human Readable Output

Intezer File analysis result
----
SHA256: some-sha256
Verdict: malicious (known_malicious)
Family: WannaCry


Analysis Report
---
analysis_id 006c54ba-3159-43a0-98a0-1c5032145f47
analysis_time Tue, 29 Jun 2021 13:40:01 GMT
analysis_url https://analyze.intezer.com/analyses/006c54ba-3159-43a0-98a0-1c5032145f47
family_id 0b13c0d4-7779-4c06-98fa-4d33ca98f8a9
family_name WannaCry
is_private false
sha256          some-sha256
sub_verdict known_malicious
verdict         malicious

intezer-analyze-by-file


Checks file reputation for uploaded file (up to 150MB)

Base Command

intezer-analyze-by-file

Input

Argument Name Description Required
file_entry_id The file entry id to upload. Required
related_alert_ids An array of alert ids to associate with the file analysis. Required

Context Output

Path Type Description
Intezer.Analysis.ID string Intezer analysis id
Intezer.Analysis.Status string status of the analysis
Intezer.Analysis.Type string type of the analysis
File.SHA256 string Hash SHA256
File.Malicious.Vendor string For malicious files, the vendor that made the decision
File.Metadata Unknown Metadata returned from Intezer analysis (analysis id, analysis url, family, family type, sha256, verdict, sub_verdict). Metadata will be returned only for supported files.
File.ExistsInIntezer Boolean Does the file exists on intezer genome database

Command Example

intezer-analyze-by-file file_entry_id=1188@6

Context Example

{
    "Intezer.Analysis": {
        "Status": "Created", 
        "type": "File", 
        "ID": "675515a1-62e9-4d55-880c-fd46a7963a56"
    }
}

Human Readable Output

Analysis created successfully: 675515a1-62e9-4d55-880c-fd46a7963a56

intezer-get-endpoint-analysis-result


Check the endpoint analysis status and get analysis result, supports polling.

Base Command

intezer-get-endpoint-analysis-result

Input

Argument Name Description Required
analysis_id The analysis ID we want to get results for. Required
wait_for_result Waits for analysis result, support polling Optional
interval Number of seconds between poll requests Optional
timeout Number of seconds until polling timeout Optional

Context Output

Path Type Description
DBotScore.Indicator string The indicator we tested
DBotScore.Type string The type of the indicator
DBotScore.Vendor string Vendor used to calculate the score
DBotScore.Score number The actual score
Endpoint.Metadata Unknown Metadata returned from Intezer analysis (endpoint analysis id, endpoint analysis url, families, verdict, host_name)

Command Example

intezer-get-endpoint-analysis-result analysis_id="9e3acdc3-b7ea-412b-88ae-7103eebc9398" wait_for_result="true"

intezer-get-url-analysis-result


Check the url analysis status and get analysis result, supports polling.

Base Command

intezer-get-url-analysis-result

Input

Argument Name Description Required
analysis_id The analysis ID we want to get results for. Required
wait_for_result Waits for analysis result, support polling Optional
interval Number of seconds between poll requests Optional
timeout Number of seconds until polling timeout Optional

Context Output

Path Type Description
DBotScore.Indicator string The indicator we tested
DBotScore.Type string The type of the indicator
DBotScore.Vendor string Vendor used to calculate the score
DBotScore.Score number The actual score
URL.Data string The submitted Url
URL.Malicious.Vendor string For malicious Url, the vendor that made the decision
URL.Metadata Unknown Metadata returned from Intezer analysis
URL.ExistsInIntezer Boolean Does the url exists on intezer

Command Example

intezer-get-url-analysis-result analysis_id="9e3acdc3-b7ea-412b-88ae-7103eebc9398" wait_for_result="true"

intezer-get-file-analysis-result


Check the file analysis status and get analysis result, supports polling.

Base Command

intezer-get-file-analysis-result

Input

Argument Name Description Required
analysis_id The analysis ID we want to get results for. Required
wait_for_result Waits for analysis result, support polling Optional
interval Number of seconds between poll requests Optional
timeout Number of seconds until polling timeout Optional

Context Output

Path Type Description  
DBotScore.Indicator string The indicator we tested  
DBotScore.Type string The type of the indicator  
DBotScore.Vendor string Vendor used to calculate the score  
DBotScore.Score number The actual score  
File.SHA256 string Hash SHA256  
File.SHA1 string Hash SHA1  
File.MD5 string Hash MD5  
File.Malicious.Vendor string For malicious files, the vendor that made the decision  
File.Metadata Unknown Metadata returned from Intezer analysis (analysis id, analysis url, family, family type, sha256, verdict, sub_verdict). Metadata will be returned only for supported files.  
File.ExistsInIntezer Boolean Does the file exists on intezer genome database  

Command Example

intezer-get-file-analysis-result analysis_id="9e3acdc3-b7ea-412b-88ae-7103eebc9398" wait_for_result="true"

intezer-get-analysis-result


Check the analysis status and get analysis result, support file and endpoint analysis

Base Command

intezer-get-analysis-result

Input

Argument Name Description Required
analysis_id The analysis ID we want to get results for. Optional
analysis_type The type of the analysis. Possible values are: File, Endpoint, Url. Default is File. Optional
indicator_name indicator to classify. Optional

Context Output

Path Type Description
DBotScore.Indicator string The indicator we tested
DBotScore.Type string The type of the indicator
DBotScore.Vendor string Vendor used to calculate the score
DBotScore.Score number The actual score
File.SHA256 string Hash SHA256
File.SHA1 string Hash SHA1
File.MD5 string Hash MD5
File.Malicious.Vendor string For malicious files, the vendor that made the decision
File.Metadata Unknown Metadata returned from Intezer analysis (analysis id, analysis url, family, family type, sha256, verdict, sub_verdict). Metadata will be returned only for supported files.
File.ExistsInIntezer Boolean Does the file exists on intezer genome database
URL.Data string The submitted Url
Url.URL string The submitted Url (deprecated)
URL.Malicious.Vendor string For malicious Url, the vendor that made the decision
Url.Malicious.Vendor string For malicious Url, the vendor that made the decision (deprecated)
URL.Metadata Unknown Metadata returned from Intezer analysis
Url.Metadata Unknown Metadata returned from Intezer analysis (deprecated)
URL.ExistsInIntezer Boolean Does the url exists on intezer
Url.ExistsInIntezer Boolean Does the url exists on intezer (deprecated)
Endpoint.Metadata Unknown Metadata returned from Intezer analysis (endpoint analysis id, endpoint analysis url, families, verdict, host_name)

Command Example

intezer-get-analysis-result analysis_id="9e3acdc3-b7ea-412b-88ae-7103eebc9398"

Context Example

{
    "DBotScore": {
        "Vendor": "Intezer", 
        "Indicator": "<some sha>>", 
        "Score": 0, 
        "Type": "hash"
    }, 
    "File": {
        "ExistsInIntezer": true, 
        "SHA256": "<some sha256>", 
        "Metadata": {
            "analysis_id": "006c54ba-3159-43a0-98a0-1c5032145f47", 
            "sub_verdict": "known_malicious", 
            "analysis_url": "https://analyze.intezer.com/analyses/006c54ba-3159-43a0-98a0-1c5032145f47", 
            "verdict": "malicious", 
            "family_id": "0b13c0d4-7779-4c06-98fa-4d33ca98f8a9",
            "family_name": "WannaCry",
            "sha256": "<some sha256>",
            "is_private": true, 
            "analysis_time": "Wed, 19 Jun 2019 07:48:12 GMT"
        }
    },
    "Url: {
        "ExistsInIntezer": true,
        "URL": "foo.com",
        "Metadata": {
        "analysis_id": "70d09f68-c7a3-43a3-a8de-07ec31fbf4ed",
        "domain_info": {
            "creation_date": "1997-08-13 04:00:00.000000",
            "domain_name": "foo.com",
            "registrar": "TUCOWS, INC."
        },
        "indicators": [
        {
            "classification": "informative",
            "text": "URL is accessible"
        },
        {
            "classification": "informative",
            "text": "Assigned IPv4 domain"
        },
        {
            "classification": "informative",
            "text": "Vaild IPv4 domain"
        }
        ],
        "ip": "34.206.39.153",
        "redirect_chain": [
        {
            "response_status": 301,
            "url": "https://foo.com/"
        },
        {
            "response_status": 200,
            "url": "http://www.foo.com/"
        }
        ],
        "scanned_url": "http://www.foo.com/",
        "submitted_url": "foo.com",
        "downloaded_file": {
            "analysis_id": "8db9a401-a142-41be-9a31-8e5f3642db62",
            "analysis_summary": {
               "verdict_description": "This file contains code from malicious software, therefore it's very likely that it's malicious.",
               "verdict_name": "malicious",
               "verdict_title": "Malicious",
               "verdict_type": "malicious"
            },
            "sha256": "4293c1d8574dc87c58360d6bac3daa182f64f7785c9d41da5e0741d2b1817fc7"
         },
        "summary": {
            "description": "No suspicious activity was detected for this URL",
            "title": "No Threats",
            "verdict_name": "no_threats",
            "verdict_type": "no_threats"
        }
    }
}

Human Readable Output

Intezer File analysis result
----
SHA256: some-sha256
Verdict: malicious (known_malicious)
Family: WannaCry


Analysis Report
---
analysis_id 006c54ba-3159-43a0-98a0-1c5032145f47
analysis_time Tue, 29 Jun 2021 13:40:01 GMT
analysis_url https://analyze.intezer.com/analyses/006c54ba-3159-43a0-98a0-1c5032145f47
family_id 0b13c0d4-7779-4c06-98fa-4d33ca98f8a9
family_name WannaCry
is_private false
sha256          some-sha256
sub_verdict known_malicious
verdict         malicious

intezer-get-sub-analyses


Get a list of the analysis sub analyses

Base Command

intezer-get-sub-analyses

Input

Argument Name Description Required
analysis_id The analysis ID we want to get the sub analyses for. Required

Context Output

Path Type Description
Intezer.Analysis.ID string Intezer analysis id
Intezer.Analysis.SubAnalysesIDs Unknown List of all sub analyses of the give analysis

Command Example

intezer-get-sub-analyses analysis_id=006c54ba-3159-43a0-98a0-1c5032145f47

Context Example

{
    "Intezer.Analysis": {
        "Status": "Done", 
        "type": "File", 
        "ID": "675515a1-62e9-4d55-880c-fd46a7963a56",
        "SubAnalysesIDs": [
            "2bf5baa9-6964-4171-b060-5e3d8de8741f"
        ]
    }
}

Human Readable Output

Sub Analyses -
[
...
List of analyses ids
...
]

intezer-get-family-info


Get family information from Intezer Analyze

Base Command

intezer-get-family-info

Input

Argument Name Description Required
family_id The Family ID. Required

Context Output

Path Type Description
Intezer.Family.ID string Family id in intezer genome database
Intezer.Family.Name string Family name
Intezer.Family.Type string Family Type

Command Example

intezer-get-family-info family_id=006c54ba-3159-43a0-98a0-1c5032145f47

Context Example

{
    "Intezer.Family": {
        "ID": "e710e4b3-3dd1-40ff-be74-9d8a95466ae4", 
        "Type": "malware", 
        "Name": "CobaltStrike"
    }
}

Human Readable Output

Family Info
---

FamilyId    006c54ba-3159-43a0-98a0-1c5032145f47
FamilyName  Some Family Name
FamilyType  Malware

intezer-get-analysis-code-reuse


Get All code reuse report for an analysis or sub analysis
To get the code reuse results of a sub analysis you also must specify the “parent analysis”,

For example - If you ran the command intezer-get-sub-analyses analysis_id=123
and got the sub analysis 456, you need to specify both in the command

Base Command

intezer-get-analysis-code-reuse

Input

Argument Name Description Required
analysis_id The analysis ID (parent analysis in case we’re trying to get sub abalysis) we want to get the code reuse for. Required
sub_analysis_id The Sub Analysis we want to get the code reuse for. Optional

Context Output

Path Type Description
Intezer.Analysis.ID string The composed analysis ID
Intezer.Analysis.CodeReuse Unknown General Code Reuse of the analysis
Intezer.Analysis.CodeReuseFamilies Unknown List of the families appearing in the code reuse
Intezer.Analysis.SubAnalyses.CodeReuse Unknown General Code Reuse of the analysis
Intezer.Analysis.SubAnalyses.CodeReuseFamilies Unknown List of the families appearing in the code reuse
Intezer.Analysis.SubAnalyses.RootAnalysis string The Composed analysis id

Command Example

# Get the code reuse of an analysis
intezer-get-analysis-code-reuse analysis_id=<Root analysis>

# Get the root analysis sub analyses
intezer-get-sub-analyses analysis_id=<Root analysis>

# Use one of the results to get the sub analysis code reuse
intezer-get-analysis-code-reuse analysis_id=<Root analysis> sub_analysis_id=<Sub Analysis Id>

Context Example

{
    "Intezer.Analysis": {
        "Status": "Done", 
        "type": "File", 
        "ID": "675515a1-62e9-4d55-880c-fd46a7963a56",
        "SubAnalyses": [
            {
                "ID": "Some sub analysis id",
                "RootAnalysis": "675515a1-62e9-4d55-880c-fd46a7963a56",
                "CodeReuse": {
                    "common_gene_count": 10,
                    "gene_count": 100,
                    "gene_type": "native_windows",
                    "unique_gene_count": 50
                },
                "CodeReuseFamilies": [
                    {
                        "family_id": "5be245ca-793c-4991-9329-c42d6365a530",
                        "family_name": "Microsoft Corporation",
                        "family_type": "application",
                        "reused_gene_count": 8
                    }
                ]
            }
        ]
    }
}

Human Readable Output

This will show information about the analysis code reuse and families

Code Reuse
---
common_gene_count   544
gene_count          543
gene_type           native_windows
unique_gene_count   0

Families:
---

WannaCry
family_id         0b13c0d4-7779-4c06-98fa-4d33ca98f8a9
family_name         WannaCry
family_type         malware
reused_gene_count 362

Lazarus
family_id         7ae9c0f1-5e81-4ed1-928d-d966a1b1525c
family_name         Lazarus
family_type         malware
reused_gene_count 33

... More Families if available

intezer-get-analysis-metadata


Get metadata for an analysis or sub analysis
To get the metadata of a sub analysis you also must specify the “parent analysis”,

For example - If you ran the command intezer-get-sub-analyses analysis_id=123
and got the sub analysis 456, you need to specify both in the command

Base Command

intezer-get-analysis-metadata

Input

Argument Name Description Required
analysis_id The analysis ID we want to get the metadata for. Required
sub_analysis_id The Sub Analysis we want to get the metadata for. Optional

Context Output

Path Type Description
Intezer.Analysis.ID string The composed analysis ID
Intezer.Analysis.Metadata Unknown The Analysis metadata
Intezer.Analysis.SubAnalyses.Metadata Unknown The Sub Analysis metadata

Command Example

# Get the metadata of an analysis
intezer-get-analysis-metadata analysis_id=<Root analysis>

# Get the root analysis sub analyses
intezer-get-sub-analyses analysis_id=<Root analysis>

# Use one of the results to get the sub analysis code reuse
intezer-get-analysis-metadata analysis_id=<Root analysis> sub_analysis_id=<Sub Analysis Id>

Context Example

{
    "Intezer.Analysis": {
        "Status": "Done", 
        "type": "File", 
        "ID": "675515a1-62e9-4d55-880c-fd46a7963a56",
        "SubAnalyses": [
            {
                "ID": "some sub analyses id",
                "RootAnalysis": "675515a1-62e9-4d55-880c-fd46a7963a56",
                "Metadata": {
                    "sha1": "<sha1>",
                    "sha256": "<sha256>",
                    "md5": "<md5>",
                    "product": "product name",
                    "product_version": "5.4",
                    "ssdeep": "<ssdeep>",
                    "size_in_bytes": 15540,
                    "architecture": "i386",
                    "original_filename": "myfile.exe",
                    "compilation_timestamp": "2019:07:26 18:23:19+00:00",
                    "file_type": "pe",
                    "company": "Microsoft"
                }
            }
        ]
    }
}

Human Readable Output

Analysis Metadata
---

architecture         i386
company                 Microsoft Corporation
compilation_timestamp 2009:07:13 23:19:35+00:00
file_type         pe
md5                 md5
original_filename LODCTR.EXE
product                 Microsoft® Windows® Operating System
product_version         6.1.7600.16385 ^^^
sha1                 sha1
sha256                 sha256
size_in_bytes         245760
ssdeep                 ssdeep

intezer-get-analysis-iocs


Gets the list of network and files IOCs of a specific analysis id.

Base Command

intezer-get-analysis-iocs

Input

Argument Name Description Required
analysis_id The analysis ID we want to get the IOCs for. Required

Context Output

Path Type Description
Intezer.Analysis.ID string The composed analysis ID
Intezer.Analysis.IOCs Dict The Analysis IOCs

Context Example

{
    "Intezer.Analysis": {
        "Status": "Done",
        "type": "File",
        "ID": "675515a1-62e9-4d55-880c-fd46a7963a56",
        "IOCs": {
            "files": [
                {
                    "path": "test_file_1.csv",
                    "sha256": "eeb1199f7db006e4d20086171cc312cf5bdf53682cc37997223ad0c15a27dc88",
                    "verdict": "malicious",
                    "family": "Turla",
                    "type": "Main file"
                }
            ],
            "network": [
                {
                    "ioc": "1.1.1.1",
                    "source": [
                        "Network communication"
                    ],
                    "type": "ip"
                },
                {
                    "ioc": "raw.exampledomain.com",
                    "source": [
                        "Network communication"
                    ],
                    "type": "domain"
                }
            ]
        }
    }
}

Human Readable Output

### Network IOCs

| ioc                   | source                | type   |
|-----------------------|-----------------------|--------|
| 1.1.1.1.1             | Network communication | ip     |
| raw.exampledomain.com | Network communication | domain |

### Files IOCs

| family  | path             | sha256                                                           | type           | verdict   |
|---------|------------------|------------------------------------------------------------------|----------------|-----------|
| Turla   | test_file_1.csv  | eeb1199f7db006e4d20086171cc312cf5bdf53682cc37997223ad0c15a27dc88 | Main file      | malicious |

intezer-submit-alert


Submit a new alert, including the raw alert information, to Intezer for processing.

Base Command

intezer-submit-alert

Input

Argument Name Description Required
raw_alert The raw alert as it stored in the context. Required
mapping The mapping for the raw alert data. Required
source The source of the alert. Required

Context Output

Path Type Description
Intezer.Alert.ID string The Alert ID
Intezer.Alert.Status string The status of the Alert

Context Example

{
    "Intezer.Alert": {
        "Status": "Done",
        "ID": "675515a1-62e9-4d55-880c-fd46a7963a56"
    }
}

intezer-submit-suspected-phishing-email


Submit a suspicious phishing email in a raw format (.MSG or .EML) to Intezer for processing.

Base Command

intezer-submit-suspected-phishing-email

Input

Argument Name Description Required
email_file_entry_id The email file entry id to upload. Required

Context Output

Path Type Description
Intezer.Alert.ID string The Alert ID
Intezer.Alert.Status string The status of the Alert

Context Example

{
    "Intezer.Alert": {
        "Status": "Done",
        "ID": "675515a1-62e9-4d55-880c-fd46a7963a56"
    }
}

intezer-get-alert-result


Get an ingested alert triage and response information using alert ID.

Base Command

intezer-get-alert-result

Input

Argument Name Description Required
alert_id The alert id to query. Required
wait_for_result Waits for alert result, support polling. Optional

Context Output

Path Type Description
Intezer.Alert.ID string The Alert ID
Intezer.Alert.Status string The Alert status
Intezer.Alert.Result Object The full report of the alert
Intezer.Alert.Result.intezer_alert_url Object The url for the alert result on Intezer Analyze
DBotScore.Indicator string The indicator we tested
DBotScore.Type string The type of the indicator
DBotScore.Vendor string Vendor used to calculate the score
DBotScore.Score string The actual score
File.SHA256 string Hash SHA256
File.SHA1 string Hash SHA1
File.MD5 string Hash MD5
URL.Data string The tested URL
URL.Malicious.Vendor string For malicious URL, the vendor that made the decision
URL.Relationships object The relationships between two urls
Intezer.Alert.Result.raw_alert object The raw alert as submitted to Intezer
Intezer.Alert.Result.triage_result.alert_verdict_display string The verdict of the alert
Intezer.Alert.Result.source_display string The calculated verdict of the Alert
Intezer.Alert.Result.triage_result.risk_category_display string The risk category of the Alert
Intezer.Alert.Result.response.user_recommended_actions_display string The actions recommended by Intezer to be taken

Command Example

# Get an alert result
intezer-get-alert-result alert_id=<Alert ID> wait_for_result=true

Context Example

{
    "Intezer.Analysis": {
        "Status": "Done", 
        "ID": "675515a1-62e9-4d55-880c-fd46a7963a56",
        "DBotScore": {
            "Vendor": "Intezer", 
            "Indicator": "<some sha>>", 
            "Score": 0, 
            "Type": "file/url/domain/ip/endpoint"
            },
        "Result": {
            "alert": {'alert_id': '111223', 'alert_title': '111223', 'alert_url': None, 'creation_time': '2023-07-24T12:26:06',
               'creation_time_display': '24 Jul 23 | 12:26 UTC', 'device': {}, 'severity': 'high',
               'severity_display': 'High'},
            'alert_id': '111223',
            'alert_sub_types': ['custom'],
            'intezer_alert_url': 'https://analyze.intezer.com/alerts/111223',
            'raw_alert': {},
            'response': {'automated_response_actions': [],
                          'iocs': {'files': [
                              {'family': None,
                               'path': 'some_path.sample',
                               'sha256': '<hash>',
                               'type': 'main_file',
                               'verdict': 'malicious'}
                          ],
                              'network': [
                                  {'ioc': '101.111.222.333', 'source': ['Network communication'],
                                   'type': 'ip'},
                              ]},
                          'status': 'escalated', 'status_display': 'Escalated',
                          'user_recommended_actions': [],
                          'user_recommended_actions_display': ''},
            'scans': [{'collection_status': 'collected', 'file_analysis': {
                'analysis_id': '<some_analysis_id>', 'analysis_time': '2023-07-24T11:00:28',
                'analysis_url': 'https://analyze.intezer.com/analyses/<some_analysis_id>',
                'file_name': 'file_name.sample',
                'iocs': {'files': [
                    {'family': None, 'path': 'some_path.sample',
                     'sha256': '<hash_of_file>', 'type': 'main_file',
                     'verdict': 'malicious'}],
                    'network': [
                        {'ioc': 'https://3-4.oss-c.malicious.domain.com/md.exe', 'source': ['Network communication'],
                         'type': 'url'}]}, 'sha256': '<hash>',
                'sub_verdict': 'known_malicious', 'ttps': [
                    {'tactic': 'Command And Control', 'tactic_id': 'TA0011', 'technique': 'Encrypted Channel',
                     'technique_id': 'T1573'}], 'sender': 'cs', 'source': 'cs',
                'source_display': 'CrowdStrike',
                'triage_result': {'alert_verdict': 'confirmed_threat', 'alert_verdict_display': 'Confirmed Threat',
                                  'risk_category': 'generic_threat', 'risk_category_display': 'Generic Threat',
                                  'risk_level': 'high',
                                  'risk_score': 20, 'ttps': [
                        {'source': 'analysis', 'tactic': 'Command And Control', 'tactic_id': 'TA0011',
                         'technique': 'Encrypted Channel', 'technique_id': 'T1573'},
                        {'source': 'analysis', 'tactic': 'Execution', 'tactic_id': 'TA0002', 'technique': 'Shared Modules',
                         'technique_id': 'T1129'}]
                                  }}}]
            }
        ]
    }
}

Human Readable Output

### Intezer Alert Result

Verdict: Confirmed Threat
Risk category: Generic Threat
Alert Link: https://analyze.intezer.com/alerts/111223

Configuration parameters

  • APIKey — API Key (required)
  • AnalyzeBaseURL — Intezer Analyze Base URL
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)

Commands (16)

  • intezer-analyze-by-file

    Checks file reputation for uploaded file (up to 150MB).

  • intezer-analyze-by-hash

    Checks file reputation of the given hash, supports SHA256, SHA1 and MD5.

  • intezer-analyze-url

    Checks reputation of an Url.

  • intezer-get-alert-result

    Get an ingested alert triage and response information using alert ID.

  • intezer-get-analysis-code-reuse

    Get All code reuse report for an analysis. To get a sub analysis code-reuse you need to specify an analysis_id and a sub_analysis id!.

  • intezer-get-analysis-iocs

    Gets the list of network and files IOCs of a specific analysis id.

  • intezer-get-analysis-metadata

    Get analysis metadata. To get a sub analysis metadata you need to specify an analysis_id and a sub_analysis id!.

  • intezer-get-analysis-result

    Check the analysis status and get analysis result, support file and endpoint analysis.

  • intezer-get-endpoint-analysis-result

    Returns the analysis result.

  • intezer-get-family-info

    Get family information from Intezer Analyze.

  • intezer-get-file-analysis-result

    Returns the analysis result.

  • intezer-get-latest-report

    Checks file reputation of the given hash, supports SHA256, SHA1 and MD5 by looking at the latest available report.

  • intezer-get-sub-analyses

    Get a list of the analysis sub analyses.

  • intezer-get-url-analysis-result

    Returns the analysis result.

  • intezer-submit-alert

    Submit a new alert, including the raw alert information, to Intezer for processing.

  • intezer-submit-suspected-phishing-email

    Submit a suspicious phishing email in a raw format (.MSG or .EML) to Intezer for processing.

from collections import defaultdict
from collections.abc import Callable
from http import HTTPStatus
from io import BytesIO

import urllib3
from CommonServerPython import *
from intezer_sdk import consts
from intezer_sdk.alerts import Alert
from intezer_sdk.analysis import FileAnalysis, UrlAnalysis
from intezer_sdk.api import IntezerApi
from intezer_sdk.endpoint_analysis import EndpointAnalysis
from intezer_sdk.errors import (
    AlertInProgressError,
    AlertNotFoundError,
    AnalysisIsAlreadyRunning,
    AnalysisIsStillRunning,
    FamilyNotFoundError,
    HashDoesNotExistError,
    InvalidApiKey,
    ServerError,
)
from intezer_sdk.family import Family
from intezer_sdk.sub_analysis import SubAnalysis
from requests import HTTPError

""" CONSTS """
# Disable insecure warnings
urllib3.disable_warnings()

IS_AVAILABLE_URL = "is-available"
REQUESTER = "xsoar"
DEFAULT_POLLING_TIMEOUT = 600
DEFAULT_POLLING_INTERVAL = 30

dbot_score_by_verdict = {
    "malicious": Common.DBotScore.BAD,
    "suspicious": Common.DBotScore.SUSPICIOUS,
    "trusted": Common.DBotScore.GOOD,
    "neutral": Common.DBotScore.GOOD,
    "no_threats": Common.DBotScore.GOOD,
}

""" HELPER FUNCTIONS """


def _get_missing_file_result(file_hash: str) -> CommandResults:
    dbot = Common.DBotScore(
        indicator=file_hash, indicator_type=DBotScoreType.FILE, integration_name="Intezer", score=Common.DBotScore.NONE
    )

    return CommandResults(
        readable_output=f"The Hash {file_hash} was not found on Intezer genome database", outputs={outputPaths["dbotscore"]: dbot}
    )


def _get_missing_url_result(url: str, ex: ServerError = None) -> CommandResults:
    dbot = Common.DBotScore(
        indicator=url, indicator_type=DBotScoreType.URL, integration_name="Intezer", score=Common.DBotScore.NONE
    )

    return CommandResults(
        readable_output=f"The Url {url} was not found on Intezer. Error {ex}", outputs={outputPaths["dbotscore"]: dbot}
    )


def _get_missing_analysis_result(analysis_id: str, sub_analysis_id: str = None) -> CommandResults:
    if not sub_analysis_id:
        output = f"The Analysis {analysis_id} was not found on Intezer Analyze"
    else:
        output = f"Could not find the analysis '{analysis_id}' or the sub analysis '{sub_analysis_id}'"

    return CommandResults(readable_output=output)


def _get_missing_endpoint_analysis_result(analysis_id: str) -> CommandResults:
    output = f"Could not find the endpoint analysis '{analysis_id}'"

    return CommandResults(readable_output=output)


def _get_missing_alert_result(alert_id: str) -> CommandResults:
    output = f"Could not find alert with the alert_id of '{alert_id}'"

    return CommandResults(readable_output=output)


def _get_missing_family_result(family_id: str) -> CommandResults:
    return CommandResults(readable_output=f"The Family {family_id} was not found on Intezer Analyze")


def _get_analysis_running_result(
    analysis_type: str, analysis_id: str = None, response: requests.Response = None
) -> CommandResults:
    if response:
        analysis_id = response.json()["result_url"].split("/")[2]

    context_json = {"ID": analysis_id, "Status": "InProgress", "Type": analysis_type}

    return CommandResults(
        outputs_prefix="Intezer.Analysis",
        outputs_key_field="ID",
        readable_output="Analysis is still in progress",
        outputs=context_json,
    )


def _get_running_alert_result(alert_id) -> CommandResults:
    return CommandResults(
        outputs_prefix="Intezer.Alert",
        outputs_key_field="ID",
        readable_output="Alert is still in progress..",
        outputs={"Intezer.Alert(val.ID && val.ID == obj.ID)": {"ID": alert_id, "Status": "InProgress"}},
    )


def _get_presentable_alert_result(alert: Alert) -> str:
    alert_result = alert.result()
    presentable_result = "## Intezer Alert result\n"
    presentable_result += f" Verdict: **{alert_result['triage_result']['alert_verdict_display']}**\n"
    presentable_result += f" Risk category: **{alert_result['triage_result']['risk_category_display']}**\n"
    if alert.family_name is not None:
        presentable_result += f"Family: **{alert.family_name}**\n"
    presentable_result += f"[Alert Link]({alert.intezer_alert_url})\n"

    return presentable_result


""" COMMANDS """


def check_is_available(args: dict, intezer_api: IntezerApi) -> str:
    try:
        response = intezer_api.get_url_result(f"/{IS_AVAILABLE_URL}")
        return "ok" if response else "Empty response from intezer service"
    except InvalidApiKey as error:
        return f"Invalid API key received.\n{error}"
    except HTTPError as error:
        return f"Error occurred when reaching Intezer Analyze. Please check Analyze Base URL. \n{error}"
    except ConnectionError as error:
        return f"Error connecting to Analyze Base url.\n{error}"


def analyze_by_hash_command(args: Dict[str, str], intezer_api: IntezerApi) -> CommandResults:
    file_hash = args.get("file_hash")

    if not file_hash:
        raise ValueError("Missing file hash")

    analysis = FileAnalysis(file_hash=file_hash, api=intezer_api)

    try:
        analysis.send(requester=REQUESTER)
        analysis_id = analysis.analysis_id

        context_json = {"ID": analysis.analysis_id, "Status": "Created", "Type": "File"}

        wait_for_result = args.get("wait_for_result")
        if wait_for_result and argToBoolean(wait_for_result):
            args["analysis_id"] = analysis_id

            return get_file_analysis_result_command(args, intezer_api)

        return CommandResults(
            outputs_prefix="Intezer.Analysis",
            outputs_key_field="ID",
            outputs=context_json,
            readable_output=f"Analysis created successfully: {analysis_id}",
        )
    except HashDoesNotExistError:
        return _get_missing_file_result(file_hash)
    except AnalysisIsAlreadyRunning as error:
        return _get_analysis_running_result(analysis_type="File", response=error.response)


def analyze_url_command(args: Dict[str, str], intezer_api: IntezerApi) -> CommandResults:
    url = args.get("url")

    if not url:
        raise ValueError("Missing url")

    analysis = UrlAnalysis(url=url, api=intezer_api)

    try:
        analysis.send(requester=REQUESTER)
        analysis_id = analysis.analysis_id

        wait_for_result = args.get("wait_for_result")
        if wait_for_result and argToBoolean(wait_for_result):
            args["analysis_id"] = analysis_id

            return get_url_analysis_result_command(args, intezer_api)

        context_json = {"ID": analysis.analysis_id, "Status": "Created", "Type": "Url"}

        return CommandResults(
            outputs_prefix="Intezer.Analysis",
            outputs_key_field="ID",
            outputs=context_json,
            readable_output=f"Analysis created successfully: {analysis_id}",
        )
    except AnalysisIsAlreadyRunning as error:
        return _get_analysis_running_result("Url", response=error.response)
    except ServerError as ex:
        return _get_missing_url_result(url, ex)


def get_latest_result_command(args: Dict[str, str], intezer_api: IntezerApi) -> CommandResults:
    file_hash = args.get("file_hash")

    if not file_hash:
        raise ValueError("Missing file hash")

    latest_analysis = FileAnalysis.from_latest_hash_analysis(**_get_latest_hash_analysis_options(args, intezer_api))

    if not latest_analysis:
        return _get_missing_file_result(file_hash)

    file_metadata = latest_analysis.get_root_analysis().metadata
    return enrich_dbot_and_display_file_analysis_results(latest_analysis.result(), file_metadata)


def analyze_by_uploaded_file_command(args: dict, intezer_api: IntezerApi) -> CommandResults:
    try:
        analysis = FileAnalysis(**_get_file_analysis_options(args, intezer_api))

        params = {"requester": REQUESTER}

        if args.get("related_alert_ids"):
            params["related_alert_ids"] = args["related_alert_ids"]

        analysis.send(**params)

        wait_for_result = args.get("wait_for_result")
        if wait_for_result and argToBoolean(wait_for_result):
            args["analysis_id"] = analysis.analysis_id

            return get_file_analysis_result_command(args, intezer_api)

        context_json = {"ID": analysis.analysis_id, "Status": "Created", "Type": "File"}

        return CommandResults(
            outputs_prefix="Intezer.Analysis",
            outputs_key_field="ID",
            outputs=context_json,
            readable_output=f"Analysis created successfully: {analysis.analysis_id}",
        )
    except AnalysisIsAlreadyRunning as error:
        return _get_analysis_running_result("File", response=error.response)


def check_analysis_status_and_get_results_command(args: dict, intezer_api: IntezerApi) -> List[CommandResults]:
    analysis_type = args.get("analysis_type", "File")
    analysis_ids = argToList(args.get("analysis_id"))
    indicator_name = args.get("indicator_name")

    command_results = []
    file_metadata = {}

    for analysis_id in analysis_ids:
        try:
            if analysis_type == "Endpoint":
                analysis = EndpointAnalysis.from_analysis_id(analysis_id, intezer_api)
                if not analysis:
                    command_results.append(_get_missing_endpoint_analysis_result(analysis_id))
                    continue
                analysis_result = analysis.result()
            elif analysis_type == "Url":
                analysis = UrlAnalysis.from_analysis_id(analysis_id, api=intezer_api)
                if not analysis:
                    command_results.append(_get_missing_url_result(analysis_id))
                    continue
                else:
                    analysis_result = analysis.result()
            else:
                analysis = FileAnalysis.from_analysis_id(analysis_id, api=intezer_api)
                if not analysis:
                    command_results.append(_get_missing_analysis_result(analysis_id))
                    continue
                else:
                    analysis_result = analysis.result()
                    file_metadata = analysis.get_root_analysis().metadata

            if analysis_result and analysis_type == "Endpoint":
                command_results.append(enrich_dbot_and_display_endpoint_analysis_results(analysis_result, indicator_name))
            elif analysis_result and analysis_type == "Url":
                command_results.append(enrich_dbot_and_display_url_analysis_results(analysis_result, intezer_api))
            elif analysis_result:
                command_results.append(enrich_dbot_and_display_file_analysis_results(analysis_result, file_metadata))

        except HTTPError as http_error:
            if http_error.response.status_code == HTTPStatus.CONFLICT:
                command_results.append(_get_analysis_running_result(analysis_type, analysis_id=analysis_id))
            elif http_error.response.status_code == HTTPStatus.NOT_FOUND:
                command_results.append(_get_missing_analysis_result(analysis_id))
            else:
                raise http_error
        except AnalysisIsStillRunning:
            command_results.append(_get_analysis_running_result(analysis_type, analysis_id=analysis_id))

    return command_results


def get_analysis_sub_analyses_command(args: dict, intezer_api: IntezerApi) -> CommandResults:
    analysis_id = args.get("analysis_id")

    try:
        analysis = FileAnalysis.from_analysis_id(analysis_id, api=intezer_api)
        if not analysis:
            return _get_missing_analysis_result(analysis_id=str(analysis_id))
    except AnalysisIsStillRunning:
        return _get_analysis_running_result("File", analysis_id=str(analysis_id))

    sub_analyses: List[SubAnalysis] = analysis.get_sub_analyses()

    all_sub_analyses_ids = [sub.analysis_id for sub in sub_analyses]
    sub_analyses_table = tableToMarkdown("Sub Analyses", all_sub_analyses_ids, headers=["Analysis IDs"])

    context_json = {"ID": analysis.analysis_id, "SubAnalysesIDs": all_sub_analyses_ids}

    return CommandResults(
        outputs_prefix="Intezer.Analysis",
        outputs_key_field="ID",
        readable_output=sub_analyses_table,
        outputs=context_json,
        raw_response=all_sub_analyses_ids,
    )


def get_analysis_code_reuse_command(args: dict, intezer_api: IntezerApi) -> CommandResults:
    analysis_id = args.get("analysis_id")
    sub_analysis_id = args.get("sub_analysis_id", "root")

    try:
        sub_analysis: SubAnalysis = SubAnalysis.from_analysis_id(sub_analysis_id, analysis_id, api=intezer_api)

        sub_analysis_code_reuse = sub_analysis.code_reuse
    except HTTPError as error:
        if error.response.status_code == HTTPStatus.NOT_FOUND:
            return _get_missing_analysis_result(analysis_id=str(analysis_id))
        elif error.response.status_code == HTTPStatus.CONFLICT:
            return _get_analysis_running_result("File", analysis_id=str(analysis_id))
        raise

    if not sub_analysis_code_reuse:
        return CommandResults(readable_output="No code reuse for this analysis")

    families = sub_analysis_code_reuse.pop("families") if "families" in sub_analysis_code_reuse else None

    readable_output = tableToMarkdown("Code Reuse", sub_analysis_code_reuse)

    if families:
        readable_output += "\nFamilies:\n"
        readable_output += "\n".join(tableToMarkdown(family["family_name"], family) for family in families)

    is_root = sub_analysis_id == "root"

    if is_root:
        context_json = {
            "Intezer.Analysis(obj.ID == val.ID)": {
                "ID": analysis_id,
                "CodeReuse": sub_analysis_code_reuse,
                "CodeReuseFamilies": families,
            }
        }
    else:
        context_json = {
            "Intezer.Analysis(obj.RootAnalysis == val.ID).SubAnalyses(obj.ID == val.ID)": {
                "ID": sub_analysis_id,
                "RootAnalysis": analysis_id,
                "CodeReuse": sub_analysis_code_reuse,
                "CodeReuseFamilies": families,
            }
        }

    return CommandResults(readable_output=readable_output, outputs=context_json, raw_response=sub_analysis.code_reuse)


def get_analysis_metadata_command(args: dict, intezer_api: IntezerApi) -> CommandResults:
    analysis_id = args.get("analysis_id")
    sub_analysis_id = args.get("sub_analysis_id", "root")

    try:
        sub_analysis: SubAnalysis = SubAnalysis(
            analysis_id=sub_analysis_id,
            composed_analysis_id=analysis_id,
            sha256="",
            source="",
            extraction_info=None,
            api=intezer_api,
        )

        sub_analysis_metadata = sub_analysis.metadata
    except HTTPError as error:
        if error.response.status_code == HTTPStatus.NOT_FOUND:
            return _get_missing_analysis_result(analysis_id=str(analysis_id))
        elif error.response.status_code == HTTPStatus.CONFLICT:
            return _get_analysis_running_result("File", analysis_id=str(analysis_id))
        raise
    metadata_table = tableToMarkdown("Analysis Metadata", sub_analysis_metadata)

    is_root = sub_analysis_id == "root"

    if is_root:
        context_json = {"Intezer.Analysis(obj.ID == val.ID)": {"ID": analysis_id, "Metadata": sub_analysis_metadata}}
    else:
        context_json = {
            "Intezer.Analysis(obj.RootAnalysis == val.ID).SubAnalyses(obj.ID == val.ID)": {
                "ID": sub_analysis_id,
                "RootAnalysis": analysis_id,
                "Metadata": sub_analysis_metadata,
            }
        }

    return CommandResults(readable_output=metadata_table, outputs=context_json, raw_response=sub_analysis_metadata)


def get_analysis_iocs_command(args: dict, intezer_api: IntezerApi) -> CommandResults:
    analysis_id = args.get("analysis_id")

    try:
        analysis = FileAnalysis.from_analysis_id(analysis_id, api=intezer_api)
    except HTTPError as error:
        if error.response.status_code == HTTPStatus.CONFLICT:
            return _get_analysis_running_result("File", analysis_id=str(analysis_id))
        raise

    if not analysis:
        return _get_missing_analysis_result(analysis_id=str(analysis_id))

    iocs = analysis.iocs
    readable_output = ""
    if iocs:
        if network_iocs := iocs.get("network"):
            readable_output += tableToMarkdown("Network IOCs", network_iocs)
        if files_iocs := iocs.get("files"):
            readable_output += tableToMarkdown("Files IOCs", files_iocs)
    else:
        readable_output = "No IOCs found"

    context_json = {"Intezer.Analysis(obj.ID == val.ID)": {"ID": analysis_id, "IOCs": iocs}}

    return CommandResults(readable_output=readable_output, outputs=context_json, raw_response=iocs)


def get_family_info_command(args: dict, intezer_api: IntezerApi) -> CommandResults:
    family_id = args.get("family_id")
    family = Family(family_id, api=intezer_api)

    try:
        family.fetch_info()
    except FamilyNotFoundError:
        return _get_missing_family_result(str(family_id))

    output = {"ID": family_id, "Name": family.name, "Type": family.type}

    markdown = tableToMarkdown("Family Info", output)

    return CommandResults(readable_output=markdown, outputs_prefix="Intezer.Family", outputs=output)


def submit_alert_command(args: dict, intezer_api: IntezerApi) -> CommandResults:
    raw_alert_data = args.get("raw_alert")
    definition_mapping = json.loads(args["mapping"])
    source = args.get("source")

    if isinstance(raw_alert_data, str):
        raw_alert_data = json.loads(raw_alert_data)

    alert = Alert.send(raw_alert=raw_alert_data, source=source, alert_mapping=definition_mapping, api=intezer_api)

    alert_id = alert.alert_id

    context_json = {"ID": alert_id, "Status": "Created"}

    return CommandResults(
        outputs_prefix="Intezer.Alert",
        outputs_key_field="ID",
        outputs=context_json,
        readable_output=f"Alert created successfully: {alert_id}",
    )


def submit_suspected_phishing_email_command(args: dict, intezer_api: IntezerApi) -> CommandResults:
    file_entry_id = args.get("email_file_entry_id")
    file_data = demisto.getFilePath(file_entry_id)

    with open(file_data["path"], "rb") as email_file:
        raw_email = BytesIO(email_file.read())
        alert = Alert.send_phishing_email(raw_email=raw_email, api=intezer_api)

        context_json = {"ID": alert.alert_id, "Status": "Created"}

        return CommandResults(
            outputs_prefix="Intezer.Alert",
            outputs_key_field="ID",
            outputs=context_json,
            readable_output=f"Suspected email was sent successfully, alert_id: {alert.alert_id}",
        )


@polling_function(
    name="intezer-get-alert-result",
    poll_message="Fetching Intezer alert. Please wait...",
    polling_arg_name="wait_for_result",
    timeout=arg_to_number(demisto.args().get("timeout", DEFAULT_POLLING_TIMEOUT), arg_name="timeout"),
    interval=arg_to_number(demisto.args().get("interval", DEFAULT_POLLING_INTERVAL), arg_name="interval"),
)
def get_alert_result_command(args: dict, intezer_api: IntezerApi) -> PollResult:
    alert_id = args["alert_id"]

    try:
        alert = Alert.from_id(alert_id=alert_id, api=intezer_api, fetch_scans=True)

        return PollResult(response=enrich_dbot_and_display_alert_results(alert, intezer_api))

    except AlertNotFoundError:
        return PollResult(response=_get_missing_alert_result(alert_id=alert_id))

    except AlertInProgressError:
        return PollResult(continue_to_poll=True, response=_get_running_alert_result(alert_id=alert_id))


@polling_function(
    name="intezer-get-file-analysis-result",
    poll_message="Fetching Intezer analysis. Please wait...",
    polling_arg_name="wait_for_result",
    timeout=arg_to_number(demisto.args().get("timeout", DEFAULT_POLLING_TIMEOUT), arg_name="timeout"),
    interval=arg_to_number(demisto.args().get("interval", DEFAULT_POLLING_INTERVAL), arg_name="interval"),
)
def get_file_analysis_result_command(args: dict, intezer_api: IntezerApi) -> PollResult:
    analysis_id = str(args.get("analysis_id"))

    try:
        analysis = FileAnalysis.from_analysis_id(analysis_id, api=intezer_api)
        if not analysis:
            return PollResult(response=_get_missing_analysis_result(analysis_id=analysis_id))

        analysis_result = analysis.result()
        file_metadata = analysis.get_root_analysis().metadata

        return PollResult(response=enrich_dbot_and_display_file_analysis_results(analysis_result, file_metadata))
    except HTTPError as http_error:
        if http_error.response.status_code == HTTPStatus.CONFLICT:
            return PollResult(continue_to_poll=True, response=_get_analysis_running_result("File", analysis_id=analysis_id))

        elif http_error.response.status_code == HTTPStatus.NOT_FOUND:
            return PollResult(response=_get_missing_analysis_result(analysis_id=analysis_id))
        else:
            raise http_error
    except AnalysisIsStillRunning:
        return PollResult(continue_to_poll=True, response=_get_analysis_running_result("File", analysis_id=analysis_id))


@polling_function(
    name="intezer-get-url-analysis-result",
    poll_message="Fetching Intezer analysis. Please wait...",
    polling_arg_name="wait_for_result",
    timeout=arg_to_number(demisto.args().get("timeout", DEFAULT_POLLING_TIMEOUT), arg_name="timeout"),
    interval=arg_to_number(demisto.args().get("interval", DEFAULT_POLLING_INTERVAL), arg_name="interval"),
)
def get_url_analysis_result_command(args: dict, intezer_api: IntezerApi) -> PollResult:
    analysis_id = str(args.get("analysis_id"))

    try:
        analysis = UrlAnalysis.from_analysis_id(analysis_id, api=intezer_api)

        if not analysis:
            return PollResult(response=_get_missing_analysis_result(analysis_id=analysis_id))

        analysis_result = analysis.result()

        return PollResult(response=enrich_dbot_and_display_url_analysis_results(analysis_result, intezer_api))
    except HTTPError as http_error:
        if http_error.response.status_code == HTTPStatus.CONFLICT:
            return PollResult(continue_to_poll=True, response=_get_analysis_running_result("Url", analysis_id=analysis_id))

        elif http_error.response.status_code == HTTPStatus.NOT_FOUND:
            return PollResult(response=_get_missing_analysis_result(analysis_id=analysis_id))
        else:
            raise http_error
    except AnalysisIsStillRunning:
        return PollResult(continue_to_poll=True, response=_get_analysis_running_result("Url", analysis_id=analysis_id))


@polling_function(
    name="intezer-get-endpoint-analysis-result",
    poll_message="Fetching Intezer analysis. Please wait...",
    polling_arg_name="wait_for_result",
    timeout=arg_to_number(demisto.args().get("timeout", DEFAULT_POLLING_TIMEOUT), arg_name="timeout"),
    interval=arg_to_number(demisto.args().get("interval", DEFAULT_POLLING_INTERVAL), arg_name="interval"),
)
def get_endpoint_analysis_result_command(args: dict, intezer_api: IntezerApi) -> PollResult:
    analysis_id = str(args.get("analysis_id"))
    indicator_name = args.get("indicator_name")

    try:
        analysis = EndpointAnalysis.from_analysis_id(analysis_id, api=intezer_api)
        if not analysis:
            return PollResult(response=_get_missing_endpoint_analysis_result(analysis_id=analysis_id))

        analysis_result = analysis.result()

        return PollResult(response=enrich_dbot_and_display_endpoint_analysis_results(analysis_result, indicator_name))
    except HTTPError as http_error:
        if http_error.response.status_code == HTTPStatus.CONFLICT:
            return PollResult(continue_to_poll=True, response=_get_analysis_running_result("Endpoint", analysis_id=analysis_id))

        elif http_error.response.status_code == HTTPStatus.NOT_FOUND:
            return PollResult(response=_get_missing_endpoint_analysis_result(analysis_id=analysis_id))
        else:
            raise http_error
    except AnalysisIsStillRunning:
        return PollResult(continue_to_poll=True, response=_get_analysis_running_result("Endpoint", analysis_id=analysis_id))


# region Enrich DBot


def enrich_dbot_and_display_file_analysis_results(intezer_result: dict, file_metadata: dict) -> CommandResults:
    verdict = intezer_result.get("verdict")
    sha256 = intezer_result.get("sha256")
    analysis_id = intezer_result.get("analysis_id")
    md5 = file_metadata.get("md5")
    sha1 = file_metadata.get("sha1")

    dbot_entry, file = _get_dbot_score_and_file_entries(intezer_result, file_metadata)

    if verdict == "malicious":
        file["Malicious"] = {"Vendor": "Intezer"}

    intezer_result["sha1"] = sha1
    intezer_result["md5"] = md5

    presentable_result = _file_analysis_presentable_code(intezer_result, sha256, verdict)

    return CommandResults(
        readable_output=presentable_result,
        raw_response=intezer_result,
        outputs={
            outputPaths["dbotscore"]: dbot_entry,
            outputPaths["file"]: file,
            "Intezer.Analysis(val.ID && val.ID == obj.ID)": {"ID": analysis_id, "Status": "Done"},
        },
    )


def _get_dbot_score_and_file_entries(file_analysis_result: dict, file_metadata: dict) -> tuple[List[dict], dict]:
    verdict: str = file_analysis_result.get("verdict", "")
    sha256 = file_metadata.get("sha256")
    md5 = file_metadata.get("md5")
    sha1 = file_metadata.get("sha1")

    dbot = [
        {"Vendor": "Intezer", "Type": "file", "Indicator": sha256, "Score": dbot_score_by_verdict.get(verdict, 0)},
        {"Vendor": "Intezer", "Type": "file", "Indicator": sha1, "Score": dbot_score_by_verdict.get(verdict, 0)},
        {"Vendor": "Intezer", "Type": "file", "Indicator": md5, "Score": dbot_score_by_verdict.get(verdict, 0)},
    ]
    file = {"SHA256": sha256, "MD5": md5, "SHA1": sha1, "Metadata": file_analysis_result, "ExistsInIntezer": True}

    return dbot, file


def _file_analysis_presentable_code(intezer_result: dict, sha256: str = None, verdict: str = None):
    if not sha256:
        sha256 = intezer_result["sha256"]
    if not verdict:
        verdict = intezer_result["verdict"]

    md = tableToMarkdown("Analysis Report", intezer_result, url_keys=["analysis_url"])
    presentable_result = "## Intezer File analysis result\n"
    presentable_result += f" SHA256: {sha256}\n"
    presentable_result += f' Verdict: **{verdict}** ({intezer_result["sub_verdict"]})\n'
    if "family_name" in intezer_result:
        presentable_result += f'Family: **{intezer_result["family_name"]}**\n'
    presentable_result += f'[Analysis Link]({intezer_result["analysis_url"]})\n'
    presentable_result += md
    return presentable_result


def get_indicator_text(classification: str, indicators: dict) -> str:
    if classification in indicators:
        return f'{classification.capitalize()}: {", ".join(indicators[classification])}'
    return ""


def enrich_dbot_and_display_url_analysis_results(intezer_result, intezer_api):
    summary = intezer_result.pop("summary")
    _refine_gene_counts(summary)

    intezer_result.update(summary)
    verdict = summary["verdict_type"]
    submitted_url = intezer_result["submitted_url"]
    scanned_url = intezer_result["scanned_url"]
    analysis_id = intezer_result["analysis_id"]

    dbot = [{"Vendor": "Intezer", "Type": "Url", "Indicator": submitted_url, "Score": dbot_score_by_verdict.get(verdict, 0)}]

    if scanned_url != submitted_url:
        dbot.append(
            {"Vendor": "Intezer", "Type": "Url", "Indicator": scanned_url, "Score": dbot_score_by_verdict.get(verdict, 0)}
        )

    url = {"URL": submitted_url, "Data": submitted_url, "Metadata": intezer_result, "ExistsInIntezer": True}

    if verdict == "malicious":
        url["Malicious"] = {"Vendor": "Intezer"}

    if "redirect_chain" in intezer_result:
        redirect_chain = "".join(f'{node["response_status"]}: {node["url"]}' for node in intezer_result["redirect_chain"])
        intezer_result["redirect_chain"] = redirect_chain

    if "indicators" in intezer_result:
        indicators: Dict[str, List[str]] = defaultdict(list)
        for indicator in intezer_result["indicators"]:
            indicators[indicator["classification"]].append(indicator["text"])
        indicators_text = [
            get_indicator_text("malicious", indicators),
            get_indicator_text("suspicious", indicators),
            get_indicator_text("informative", indicators),
        ]

        intezer_result["indicators"] = "\n".join(indicator_text for indicator_text in indicators_text if indicator_text)

    presentable_result = "## Intezer Url analysis result\n"
    presentable_result += f" Url: {submitted_url}\n"
    presentable_result += f' Verdict: **{verdict}** ({summary["verdict_name"]})\n'
    presentable_result += f'[Analysis Link]({intezer_result["analysis_url"]})\n'

    downloaded_file_presentable_result = ""
    file_entry: dict = {}
    if "downloaded_file" in intezer_result:
        downloaded_file = intezer_result.pop("downloaded_file")

        if "sha256" in downloaded_file:
            presentable_result += f'Downloaded file SHA256: {downloaded_file["sha256"]}\n'

        if "analysis_summary" in downloaded_file:
            presentable_result += f'Downloaded file Verdict: **{downloaded_file["analysis_summary"]["verdict_type"]}**\n'

        if "analysis_id" in downloaded_file:
            downloaded_file_analysis = FileAnalysis.from_analysis_id(downloaded_file["analysis_id"], intezer_api)
            download_file_result = downloaded_file_analysis.result()
            intezer_result["downloaded_file"] = download_file_result
            metadata = downloaded_file_analysis.get_root_analysis().metadata

            file_dbot_entry, file_entry = _get_dbot_score_and_file_entries(download_file_result, metadata)

            sha1 = metadata.get("sha1")
            md5 = metadata.get("md5")
            download_file_result["sha1"] = sha1
            download_file_result["md5"] = md5
            downloaded_file_presentable_result = _file_analysis_presentable_code(download_file_result)

            dbot.extend(file_dbot_entry)
            file_entry = {outputPaths["file"]: file_entry}

    md = tableToMarkdown("Analysis Report", intezer_result, url_keys=["analysis_url"])
    presentable_result += md + downloaded_file_presentable_result

    return CommandResults(
        readable_output=presentable_result,
        raw_response=intezer_result,
        outputs={
            outputPaths["dbotscore"]: dbot,
            outputPaths["url"]: url,
            **file_entry,
            "Intezer.Analysis(val.ID && val.ID == obj.ID)": {"ID": analysis_id, "Status": "Done"},
        },
    )


def _refine_gene_counts(summary: dict):
    summary.pop("main_connection_gene_count", None)
    summary.pop("main_connection_gene_percentage", None)
    summary.pop("main_connection", None)
    summary.pop("main_connection_family_id", None)
    summary.pop("main_connection_software_type", None)
    summary.pop("main_connection_classification", None)


def _get_file_analysis_options(args: dict, intezer_api: IntezerApi) -> dict:
    file_id = args.get("file_entry_id")
    file_data = demisto.getFilePath(file_id)

    zip_password = args.get("zip_password")
    disable_dynamic_execution = args.get("disable_dynamic_execution")
    disable_static_extraction = args.get("disable_static_extraction")
    sandbox_command_line_arguments = args.get("sandbox_command_line_arguments")

    analysis_options = {"file_path": file_data["path"], "file_name": file_data["name"], "api": intezer_api}

    if zip_password:
        analysis_options["zip_password"] = zip_password

    if disable_dynamic_execution:
        analysis_options["disable_dynamic_execution"] = argToBoolean(disable_dynamic_execution)

    if disable_static_extraction:
        analysis_options["disable_static_extraction"] = argToBoolean(disable_static_extraction)

    if sandbox_command_line_arguments:
        analysis_options["sandbox_command_line_arguments"] = sandbox_command_line_arguments

    return analysis_options


def _get_latest_hash_analysis_options(args: dict, intezer_api: IntezerApi) -> dict:
    latest_hash_analysis_options = {"file_hash": args.get("file_hash"), "api": intezer_api, "requester": REQUESTER}

    should_get_only_private_analysis = args.get("should_get_only_private_analysis")
    if should_get_only_private_analysis:
        latest_hash_analysis_options["private_only"] = argToBoolean(should_get_only_private_analysis)

    return latest_hash_analysis_options


def enrich_dbot_and_display_endpoint_analysis_results(intezer_result, indicator_name=None) -> CommandResults:
    verdict = intezer_result["verdict"]
    computer_name = intezer_result["computer_name"]
    analysis_id = intezer_result["analysis_id"]

    dbot = {
        "Vendor": "Intezer",
        "Type": "hostname",
        "Indicator": indicator_name if indicator_name else computer_name,
        "Score": dbot_score_by_verdict.get(verdict, 0),
    }

    endpoint = {"Metadata": intezer_result}

    presentable_result = "## Intezer Endpoint analysis result\n"
    presentable_result += f"Host Name: {computer_name}\n"
    presentable_result += f" Verdict: **{verdict}**\n"
    if intezer_result.get("families") is not None:
        presentable_result += f'Families: **{intezer_result["families"]}**\n'
    presentable_result += f' Scan Time: {intezer_result["scan_start_time"]}\n'
    presentable_result += f'[Analysis Link]({intezer_result["analysis_url"]})\n'

    return CommandResults(
        readable_output=presentable_result,
        raw_response=intezer_result,
        outputs={
            outputPaths["dbotscore"]: dbot,
            "Endpoint": endpoint,
            "Intezer.Analysis(val.ID && val.ID == obj.ID)": {"ID": analysis_id, "Status": "Done"},
        },
    )


def enrich_dbot_and_display_alert_results(alert: Alert, intezer_api: IntezerApi):
    command_results = []

    # Running over all the alert scans, not including the artifact scans
    for analysis in alert.scans:
        if isinstance(analysis, FileAnalysis):
            command_results.append(_get_return_command_for_file_analysis(analysis))

        elif isinstance(analysis, UrlAnalysis):
            command_results.append(_get_return_command_for_url_analysis(analysis))

            analysis_result = analysis.result()

            if "downloaded_file" in analysis_result and "analysis_id" in analysis_result["downloaded_file"]:
                downloaded_file_analysis = FileAnalysis.from_analysis_id(
                    analysis_result["downloaded_file"]["analysis_id"], api=intezer_api
                )
                command_results.append(_get_return_command_for_file_analysis(downloaded_file_analysis))

        elif isinstance(analysis, EndpointAnalysis):
            command_results.append(_get_return_command_for_endpoint_analysis(analysis))

    alert_scans = alert.result().get("scans", [])

    # Running over artifact analyses
    for scan in alert_scans:
        if scan["scan_type"] != "artifact":
            continue

        artifact_analysis = scan["artifact_analysis"]

        if artifact_analysis["artifact_type"] not in ("ip", "domain"):
            continue

        command_results.append(_get_return_command_for_artifact_analysis(artifact_analysis))

    command_results.append(
        CommandResults(
            outputs_prefix="Intezer.Alert",
            outputs_key_field="ID",
            readable_output=_get_presentable_alert_result(alert),
            outputs={"ID": alert.alert_id, "Status": "Done", "Result": alert.result()},
        )
    )
    return_results(command_results)


def _get_return_command_for_file_analysis(analysis: FileAnalysis):
    file_metadata = analysis.get_root_analysis().metadata
    file_analysis_result = analysis.result()
    verdict = file_analysis_result.get("verdict", "")
    sha256 = file_metadata.get("sha256")
    md5 = file_metadata.get("md5")
    sha1 = file_metadata.get("sha1")

    dbot_score = Common.DBotScore(
        indicator=sha256,
        indicator_type=DBotScoreType.FILE,
        integration_name="Intezer",
        score=dbot_score_by_verdict.get(verdict, 0),
    )

    file = Common.File(md5=md5, sha1=sha1, sha256=sha256, dbot_score=dbot_score)
    return CommandResults(outputs_prefix="Intezer.File", outputs_key_field="sha256", indicator=file)


def _get_return_command_for_url_analysis(analysis: UrlAnalysis):
    url_analysis_result = analysis.result()

    submitted_url = url_analysis_result["submitted_url"]
    scanned_url = url_analysis_result["scanned_url"]
    verdict = url_analysis_result["summary"]["verdict_type"]
    relationships = []

    dbot_score = Common.DBotScore(
        indicator=scanned_url,
        indicator_type=DBotScoreType.URL,
        integration_name="Intezer",
        score=dbot_score_by_verdict.get(verdict, 0),
    )

    if scanned_url != submitted_url:
        relationships.append(
            EntityRelationship(
                name="contains",
                entity_a=submitted_url,
                entity_a_type=FeedIndicatorType.URL,
                entity_b=scanned_url,
                entity_b_type=FeedIndicatorType.URL,
            )
        )

    url = Common.URL(url=submitted_url, relationships=relationships, dbot_score=dbot_score)

    return CommandResults(outputs_prefix="Intezer.URL", outputs_key_field="url", indicator=url)


def _get_return_command_for_endpoint_analysis(analysis: EndpointAnalysis):
    endpoint_analysis_result = analysis.result()
    verdict = endpoint_analysis_result["verdict"]
    computer_name = endpoint_analysis_result["computer_name"]

    dbot_score = Common.DBotScore(
        indicator=computer_name,
        indicator_type=DBotScoreType.CUSTOM,
        integration_name="Intezer",
        score=dbot_score_by_verdict.get(verdict, 0),
    )

    endpoint = {"Metadata": endpoint_analysis_result}

    return CommandResults(outputs_prefix="Intezer.Endpoint", outputs={outputPaths["dbotscore"]: dbot_score, "endpoint": endpoint})


def _get_return_command_for_artifact_analysis(artifact_analysis: dict):
    artifact_type = DBotScoreType.DOMAIN if artifact_analysis["artifact_type"] == "domain" else DBotScoreType.IP

    dbot_score = Common.DBotScore(
        indicator=artifact_analysis["artifact_value"],
        indicator_type=artifact_type,
        integration_name="Intezer",
        score=dbot_score_by_verdict.get(artifact_analysis["verdict"], 0),
    )

    indicator: Union[Common.Domain, Common.IP] | None = None

    if artifact_analysis["artifact_type"] == "domain":
        indicator = Common.Domain(domain=artifact_analysis["artifact_value"], dbot_score=dbot_score)
    elif artifact_analysis["artifact_type"] == "ip":
        indicator = Common.IP(ip=artifact_analysis["artifact_value"], dbot_score=dbot_score)
    else:
        return CommandResults()

    return CommandResults(indicator=indicator)


# endregion

""" EXECUTION CODE """


def main():  # pragma: no cover
    command = None

    try:
        handle_proxy()

        intezer_api_key = demisto.getParam("APIKey")
        intezer_base_url_param = demisto.getParam("AnalyzeBaseURL")
        use_ssl = not demisto.params().get("insecure", False)
        analyze_base_url = intezer_base_url_param or consts.BASE_URL

        intezer_api = IntezerApi(consts.API_VERSION, intezer_api_key, analyze_base_url, use_ssl, user_agent=get_pack_version())

        command_handlers: Dict[
            str, Callable[[IntezerApi, dict], Union[List[CommandResults], CommandResults, PollResult, str]]
        ] = {
            "test-module": check_is_available,
            "intezer-submit-alert": submit_alert_command,
            "intezer-submit-suspected-phishing-email": submit_suspected_phishing_email_command,
            "intezer-analyze-by-hash": analyze_by_hash_command,
            "intezer-analyze-by-file": analyze_by_uploaded_file_command,
            "intezer-analyze-url": analyze_url_command,
            "intezer-get-latest-report": get_latest_result_command,
            "intezer-get-analysis-result": check_analysis_status_and_get_results_command,
            "intezer-get-sub-analyses": get_analysis_sub_analyses_command,
            "intezer-get-analysis-code-reuse": get_analysis_code_reuse_command,
            "intezer-get-analysis-metadata": get_analysis_metadata_command,
            "intezer-get-analysis-iocs": get_analysis_iocs_command,
            "intezer-get-family-info": get_family_info_command,
            "intezer-get-file-analysis-result": get_file_analysis_result_command,
            "intezer-get-url-analysis-result": get_url_analysis_result_command,
            "intezer-get-endpoint-analysis-result": get_endpoint_analysis_result_command,
            "intezer-get-alert-result": get_alert_result_command,
        }

        command = demisto.command()
        command_handler = command_handlers[command]
        command_results = command_handler(demisto.args(), intezer_api)
        return_results(command_results)

    except Exception as e:
        return_error(f"Failed to execute {command} command. Error: {e!s}")


# python2 uses __builtin__ python3 uses builtins
if __name__ == "__builtin__" or __name__ == "builtins":
    main()