IntSights Deprecated

Deprecated. Use Rapid7 Threat Command instead.

Data Enrichment & Threat Intelligence · Rapid7 - Threat Command (IntSights)

Details

IDIntSights
ProviderRapid7
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/python3:3.10.10.51930
Supported ModulesAgentix XSIAM

README

Use IntSights to manage and mitigate threats.
This integration was tested with Intsights API version 3.

Configure IntSights in Cortex

Parameter Required
Server URL (e.g. https://192.168.0.1) True
Credentials True
Password True
Alert type to fetch as incidents, allowed: “AttackIndication”, “DataLeakage”, “Phishing”, “BrandSecurity”, “ExploitableData”, “VIP” False
Minimum Alert severity level to fetch incidents incidents from, allowed values are: ‘All’, ‘Low’, ‘Medium’,’High’(Setting to All will fetch all incidents) False
Trust any certificate (not secure) False
Use system proxy settings False
Fetch incidents False
First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days) False
Max fetch False
Incident type False
Sub Account ID (MSSP accounts only) 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.

intsights-get-alert-image


Returns an image of an alert by ID.

Base Command

intsights-get-alert-image

Input

Argument Name Description Required
image-id The ID of the image to return. Required

Context Output

There is no context output for this command.

intsights-get-alert-activities


Returns alert activities.

Base Command

intsights-get-alert-activities

Input

Argument Name Description Required
alert-id The ID of the alert. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Activities.Type string The type of the activity.
IntSights.Alerts.Activities.Initiator string The initiator of the alert.
IntSights.Alerts.Activities.CreatedDate date The date the alert was created.
IntSights.Alerts.Activities.UpdateDate date The date the alert was updated.
IntSights.Alerts.Activities.RemediationBlocklistUpdate string The remediation blocked list update.
IntSights.Alerts.Activities.AskTheAnalyst.Replies string The replies to questions of the analyst.
IntSights.Alerts.Activities.Mail.Replies string The replies to an email.
IntSights.Alerts.Activities.ReadBy string The alert that was read by.

intsights-assign-alert


Assigns an alert.

Base Command

intsights-assign-alert

Input

Argument Name Description Required
alert-id The unique ID of the Alert. Required
assignee-email The user email of the assignee. Required
is-mssp-optional Whether the assigned user is an MSSP user. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Assignees.AssigneeID string The ID of the assignee.

intsights-unassign-alert


Unassigns an alert from a user.

Base Command

intsights-unassign-alert

Input

Argument Name Description Required
alert-id The unique ID of the alert. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.

intsights-send-mail


Sends an email containing a question and details of the alert.

Base Command

intsights-send-mail

Input

Argument Name Description Required
alert-id The unique ID of the alert. Required
emails The destination email addresses array (comma-separated). Required
content The content added to the alert details. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the Alert.
IntSights.Alerts.Mail.EmailID string The ID of the email.
IntSights.Alerts.Question string Details of the question.

intsights-ask-the-analyst


Sends a question to the IntSights analyst about the requested alert.

Base Command

intsights-ask-the-analyst

Input

Argument Name Description Required
alert-id The unique ID of the alert. Required
question Question to ask the Intsights analyst about the requested alert. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the Alert.
IntSights.Alerts.Question string Details of the question.

intsights-add-tag-to-alert


Adds a tag to the alert.

Base Command

intsights-add-tag-to-alert

Input

Argument Name Description Required
alert-id The ID of the unique alert. Required
tag-name The new tag string. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Tags.TagName string The name of the tag.
IntSights.Alerts.Tags.ID string The ID of the Tag.

intsights-remove-tag-from-alert


Removes a tag from the specified alert.

Base Command

intsights-remove-tag-from-alert

Input

Argument Name Description Required
alert-id The unique ID of the alert. Required
tag-id The unique ID of the tag to remove. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Tags.ID string The ID of the tag.

intsights-add-comment-to-alert


Adds a comment to a specified alert.

Base Command

intsights-add-comment-to-alert

Input

Argument Name Description Required
alert-id The unique ID of the alert. Required
comment The comment to add to the alert. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Comment string The comment in the alert.

intsights-update-alert-severity


Changes the severity of a specified alert.

Base Command

intsights-update-alert-severity

Input

Argument Name Description Required
alert-id The unique ID of the alert. Required
severity The severity of the alert. Can be: “High”, “Medium”, or “Low”. Possible values are: High, Medium, Low. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Severity string The severity of the alert.

intsights-get-alert-by-id


Returns the alert object by alert ID.

Base Command

intsights-get-alert-by-id

Input

Argument Name Description Required
alert-id The unique ID of the alert. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Severity string The severity of the alert.
IntSights.Alerts.Type string The type of the alert.
IntSights.Alerts.FoundDate date The date that the alert was found.
IntSights.Alerts.SourceType string The source type of the alert.
IntSights.Alerts.SourceURL string The source URL of the alert.
IntSights.Alerts.SourceEmail string The source email of the alert.
IntSights.Alerts.SourceNetworkType string The network type of the alert.
IntSights.Alerts.IsClosed boolean Whether or not the alert is closed.
IntSights.Alerts.IsFlagged boolean Whether or not the alert is flagged.
IntSights.Alerts.Tags.CreatedBy string Name of the service for which the tag was created.
IntSights.Alerts.Tag.Name string Name of the tag.
IntSights.Alerts.Tag.ID string The ID of the tag.
IntSights.Alerts.Images string The ID of the images.
IntSights.Alerts.Description string The description of the alert.
IntSights.Alerts.Title string The title of the alert.
IntSights.Alerts.TakedownStatus string The TakedownStatus of the alert.
IntSights.Alerts.SubType string The sub type of the alert.

intsights-get-ioc-by-value


Searches for an exact IOC value.

Base Command

intsights-get-ioc-by-value

Input

Argument Name Description Required
value The IOC value for which to search. Required

Context Output

Path Type Description
IntSights.Iocs.Value string The value of the IOC.
IntSights.Iocs.Type string The type of the IOC.
IntSights.Iocs.FirstSeen date The date the IOC was first seen.
IntSights.Iocs.LastSeen date The date the IOC was last seen.
IntSights.Iocs.LastUpdatedDate date The date the IOC was last updated.
IntSights.Iocs.SourceID string The source ID of the IOC.
IntSights.Iocs.SourceName string The source name of the IOC.
IntSights.Iocs.SourceConfidenceLevel string The confidence level of the IOC source.
IntSights.Iocs.Severity string The severity of the IOC.
IntSights.Iocs.Status string The status of the IOC.
IntSights.Iocs.Sources.name string The source name of the IOC.
IntSights.Iocs.Sources.confidenceLevel string The confidence level of the IOC source.
IntSights.Iocs.Sources.id string The source id of the IOC.
IntSights.Iocs.tags Array The tags of the IOC.
DBotScore.Indicator String The indicator that was tested.
DBotScore.Type String The type of the indicator.
DBotScore.Vendor String The vendor used to calculate the score.
DBotScore.Score Number The actual score.
File.Name String The full file name (including file extension).
File.Malicious.Vendor String The vendor that reported the file as malicious.
File.Malicious.Description String A description explaining why the file was determined to be malicious.
File.MD5 String The MD5 hash of the file.
File.SHA1 String The SHA1 hash of the file.
File.SHA256 String The SHA256 hash of the file.
URL.Data String The URL.
URL.Malicious.Vendor String The vendor reporting the URL as malicious.
URL.Malicious.Description String A description of the malicious URL.
IP.Malicious.Vendor String The vendor reporting the IP address as malicious.
IP.Malicious.Description String A description explaining why the IP address was reported as malicious.
IP.Address String IP address.
Domain.Name String The domain name. For example, “google.com”.
Domain.Malicious.Vendor String The vendor reporting the domain as malicious.
Domain.Malicious.Description String A description explaining why the domain was reported as malicious.

intsights-get-iocs


Returns count totals of the available IOCs.

Base Command

intsights-get-iocs

Input

Argument Name Description Required
type The type of the IOC. Can be: “Urls”, “Hashes”, “IpAddresses”, or “domains”. Possible values are: Urls, Hashes, IpAddresses, Domains. Optional
limit The maximum number of results from 1-1000. Default is 1000. Optional
severity The severity level of the IOC. Can be: “High”, “Medium”, or “Low”. Possible values are: High, Medium, Low. Optional
source-ID The source of the IOC. Optional
first-seen-from Beginning of the date range when the IOC was first seen (MM/DD/YYYY). Default is 0. Optional
first-seen-to End of the date range when the IOC was first seen (MM/DD/YYYY). Default is 0. Optional
last-seen-from Beginning of the date range when the IOC was last seen (MM/DD/YYYY). Default is 0. Optional
last-updated-from Beginning of the date range when the IOC was last updated (YYYY-MM-DD). Optional
last-seen-to End of the date range when the IOC was last seen (MM/DD/YYYY). Default is 0. Optional

Context Output

Path Type Description
IntSights.Iocs.Value string The value of the IOC.
IntSights.Iocs.Type string The type of the IOC.
IntSights.Iocs.FirstSeen date The date the IOC was first seen.
IntSights.Iocs.LastSeen date The date the IOC was last seen.
IntSights.Iocs.LastUpdatedDate date The date the IOC was last updated.
IntSights.Iocs.SourceID string The source ID of the IOC.
IntSights.Iocs.SourceName string The source name of the IOC.
IntSights.Iocs.SourceConfidenceLevel string The confidence level of the IOC source.
IntSights.Iocs.Severity string The severity of the IOC.
IntSights.Iocs.Status string The status of the IOC.
IntSights.Iocs.Sources.name string The source name of the IOC.
IntSights.Iocs.Sources.confidenceLevel string The confidence level of the IOC source.
IntSights.Iocs.Sources.id string The source id of the IOC.
IntSights.Iocs.tags Array The tags of the IOC.
DBotScore.Indicator String The indicator that was tested.
DBotScore.Type String The type of the indicator.
DBotScore.Vendor String The vendor used to calculate the score.
DBotScore.Score Number The actual score.
File.Name String The full file name (including file extension).
File.Malicious.Vendor String The vendor that reported the file as malicious.
File.Malicious.Description String A description explaining why the file was determined to be malicious.
File.MD5 String The MD5 hash of the file.
File.SHA1 String The SHA1 hash of the file.
File.SHA256 String The SHA256 hash of the file.
URL.Data String The URL.
URL.Malicious.Vendor String The vendor reporting the URL as malicious.
URL.Malicious.Description String A description of the malicious URL.
IP.Malicious.Vendor String The vendor reporting the IP address as malicious.
IP.Malicious.Description String A description explaining why the IP address was reported as malicious.
IP.Address String IP address.
Domain.Name String The domain name. For example, “google.com”.
Domain.Malicious.Vendor String The vendor reporting the domain as malicious.
Domain.Malicious.Description String A description explaining why the domain was reported as malicious.

intsights-get-alerts


Returns alerts.

Base Command

intsights-get-alerts

Input

Argument Name Description Required
alert-type The type of the alert. Can be: “AttackIndication”, “DataLeakage”, “Phishing”, “BrandSecurity”, “ExploitableData”, “VIP”. Possible values are: AttackIndication, DataLeakage, Phishing, BrandSecurity, ExploitableData, VIP. Optional
severity The severity of the alert. Can be: “High”, “Medium”, or “Low”. Possible values are: High, Medium, Low. Optional
source-type The source type of the alert. Can be: “ApplicationStores”, “BlackMarkets”, “HackingForums”, “SocialMedia”, “PasteSites”, or “Others”. Possible values are: ApplicationStores, BlackMarkets, HackingForums, SocialMedia, PasteSites, Others. Optional
network-type The network type of the alert. Can be: “ClearWeb”, or “DarkWeb”. Possible values are: ClearWeb, DarkWeb. Optional
source-date-from The start date for which to fetch in Millisecond Timestamp in UNIX. Optional
source-date-to The end date for which to fetch in Millisecond Timestamp in UNIX. Optional
found-date-from The start date for which fetch in Millisecond Timestamp in UNIX. Optional
found-date-to The end date for which fetch in Millisecond Timestamp in UNIX. Optional
assigned Whether to show assigned or unassigned alerts. Optional
is-flagged Whether to show flagged or unflagged alerts. Optional
is-closed Whether to show closed/open alerts. Optional
time-delta Shows alerts within a specified time delta, given in days. Optional

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Severity string The severity of the alert.
IntSights.Alerts.Type string The type of the alert.
IntSights.Alerts.FoundDate date The date that the alert was found.
IntSights.Alerts.SourceType string The source type of the alert.
IntSights.Alerts.SourceURL string The source URL of the alert.
IntSights.Alerts.SourceEmail string The source email of the alert.
IntSights.Alerts.SourceNetworkType string The network type of the alert.
IntSights.Alerts.IsClosed boolean Whether or not the alert is closed.
IntSights.Alerts.IsFlagged boolean Whether or not the alert is flagged.
IntSights.Alerts.Tags.CreatedBy string Name of the service that the tag was created.
IntSights.Alerts.Tag.Name string Name of the tag.
IntSights.Alerts.Tag.ID string The ID of the tag.
IntSights.Alerts.Images string The ID of each image.
IntSights.Alerts.Description string The description of the alert.
IntSights.Alerts.Title string The title of the alert.
IntSights.Alerts.TakedownStatus string The TakedownStatus of the alert.
IntSights.Alerts.SubType string The sub type of the alert.

intsights-alert-takedown-request


Requests an alert takedown.

Base Command

intsights-alert-takedown-request

Input

Argument Name Description Required
alert-id The ID of the alert. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.

intsights-get-alert-takedown-status


Returns the alert takedown status.

Base Command

intsights-get-alert-takedown-status

Input

Argument Name Description Required
alert-id The ID of the alert. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.TakedownStatus string The status of the takedown.

intsights-update-ioc-blocklist-status


Updates the IOC block list status.

Base Command

intsights-update-ioc-blocklist-status

Input

Argument Name Description Required
alert-id The ID of the alert. Required
type A comma separated list of each type of IOC. Options: Domains, IPs, URLs. Required
value A comma separated list of the value of the IOCs. Required
blocklist-status A comma separated list of the IOCs block list status. Options: Sent, NotSent. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Status string The status of the block list.

intsights-get-ioc-blocklist-status


Returns the status of the IOC block list.

Base Command

intsights-get-ioc-blocklist-status

Input

Argument Name Description Required
alert-id The ID of the alert. Required

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Status string The status of the block list.

intsights-close-alert


Closes an alert

Base Command

intsights-close-alert

Input

Argument Name Description Required
alert-id The ID of the alert. Required
reason The reason to close the alert. Can be: “ProblemSolved”, “InformationalOnly”, “ProblemWeAreAlreadyAwareOf”, “CompanyOwnedDomain”, “LegitimateApplication/Profile”, “NotRelatedToMyCompany”, “FalsePositive”, or “Other”. Possible values are: ProblemSolved, InformationalOnly, ProblemWeAreAlreadyAwareOf, CompanyOwnedDomain, LegitimateApplication/Profile, NotRelatedToMyCompany, FalsePositive, Other. Required
free-text The comments in the alert. Optional
is-hidden The hidden status of the alert. Deletes an alert from the account instance - only when reason is a FalsePositive). Possible values are: True, False. Default is False. Optional
rate The rate of the alert. Optional

Context Output

Path Type Description
IntSights.Alerts.ID string The ID of the alert.
IntSights.Alerts.Closed.Reason string The closed reason of the alert.

intsights-mssp-get-sub-accounts


Returns all Managed Security Service Provider’s (MSSP) sub accounts.

Base Command

intsights-mssp-get-sub-accounts

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
IntSights.MsspAccount.ID String The ID of IntSights MSSP sub account.
IntSights.MsspAccount.Status String The enabled status of IntSights MSSP sub account
IntSights.MsspAccount.AssetsCount Number The assets count of IntSights MSSP sub account.
IntSights.MsspAccount.AssetLimit Number The asset limit of IntSights MSSP sub account.
IntSights.MsspAccount.CompanyName String The company name of IntSights MSSP sub account.

intsights-request-ioc-enrichment


Request and receive enrichment of an IOC.

Base Command

intsights-request-ioc-enrichment

Input

Argument Name Description Required
value The IOC value for which to enrich. Required

Context Output

Path Type Description
Domain.Name String domain name
Domain.DNS String domain dns
Domain.Resolutions String domain resolutions
Domain.Subdomains String domain subdomains
Domain.WHOIS/History String domain whois
Domain.Malicious String domain malicious
IP.Address String ip address
IP.IpDetails String ip details
IP.RelatedHashes String ip related hashes
IP.WHOIS String ip whois
IP.Malicious String ip malicious
URL.Data String URL Data
URL.AntivirusDetectedEngines String URL Antivirus Detected Engines
URL.AntivirusDetectionRatio String URL Antivirus Detection Ratio
URL.AntivirusDetections String URL Antivirus Detections
URL.AntivirusScanDate String URL Antivirus Scan Date
URL.RelatedHashes String URL Related Hashes
URL.Malicious String URL Malicious
File.Name String File Name
File.AntivirusDetectedEngines String File Antivirus Detected Engines
File.AntivirusDetectionRatio String File Antivirus Detection Ratio
File.AntivirusDetections String File Antivirus Detections
File.AntivirusScanDate String File Antivirus Scan Date
File.Malicious String File Malicious
IntSights.Iocs.Type String IntSights Iocs Type
IntSights.Iocs.Value String IntSights Iocs Value
IntSights.Iocs.FirstSeen String IntSights Iocs First Seen
IntSights.Iocs.LastSeen String IntSights Iocs Last Seen
IntSights.Iocs.Status String IntSights Iocs Status
IntSights.Iocs.Severity String IntSights Iocs Severity
IntSights.Iocs.RelatedMalwares String IntSights Iocs Related Malwares
IntSights.Iocs.Sources String IntSights Iocs Sources
IntSights.Iocs.IsKnownIoc String IntSights Iocs Is Known Ioc
IntSightsIocs.RelatedThreatActors String IntSights Iocs Related Threat Actors
IntSights.Iocs.SystemTags String IntSights Iocs SystemTags
IntSights.Iocs.Tags String IntSights Iocs Tags
IntSights.Iocs.Whitelisted String IntSights Iocs Whitelisted
IntSights.Iocs.OriginalValue String IntSights Iocs Original Value
Domain.WHOIS String Domain WHOIS

Configuration parameters

  • server — Server URL (e.g. https://192.168.0.1) (required)
  • credentials — Credentials (required)
  • type — Alert type to fetch as incidents, allowed: "AttackIndication", "DataLeakage", "Phishing", "BrandSecurity", "ExploitableData", "VIP"
  • severity_level — Minimum Alert severity level to fetch incidents incidents from, allowed values are: 'All', 'Low', 'Medium','High'(Setting to All will fetch all incidents)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • isFetch — Fetch incidents
  • first_fetch — First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)
  • max_fetch — Max fetch
  • incidentType — Incident type
  • mssp_sub_account_id — Sub Account ID (MSSP accounts only)

Commands (21)

  • intsights-add-comment-to-alert

    Adds a comment to a specified alert.

  • intsights-add-tag-to-alert

    Adds a tag to the alert.

  • intsights-alert-takedown-request

    Requests an alert takedown.

  • intsights-ask-the-analyst

    Sends a question to the IntSights analyst about the requested alert.

  • intsights-assign-alert

    Assigns an alert.

  • intsights-close-alert

    Closes an alert

  • intsights-get-alert-activities

    Returns alert activities.

  • intsights-get-alert-by-id

    Returns the alert object by alert ID.

  • intsights-get-alert-image

    Returns an image of an alert by ID.

  • intsights-get-alert-takedown-status

    Returns the alert takedown status.

  • intsights-get-alerts

    Returns alerts.

  • intsights-get-ioc-blocklist-status

    Returns the status of the IOC block list.

  • intsights-get-ioc-by-value

    Searches for an exact IOC value.

  • intsights-get-iocs

    Returns count totals of the available IOCs.

  • intsights-mssp-get-sub-accounts

    Returns all Managed Security Service Provider's (MSSP) sub accounts.

  • intsights-remove-tag-from-alert

    Removes a tag from the specified alert.

  • intsights-request-ioc-enrichment

    Request and receive enrichment of an IOC.

  • intsights-send-mail

    Sends an email containing a question and details of the alert.

  • intsights-unassign-alert

    Unassigns an alert from a user.

  • intsights-update-alert-severity

    Changes the severity of a specified alert.

  • intsights-update-ioc-blocklist-status

    Updates the IOC block list status.

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401
import os

URL = demisto.getParam('server')
if URL[-1] != '/':
    URL += '/'

if not demisto.getParam('proxy'):
    del os.environ['HTTP_PROXY']
    del os.environ['HTTPS_PROXY']
    del os.environ['http_proxy']
    del os.environ['https_proxy']

VALIDATE_CERT = not demisto.params().get('insecure', True)
USER_ID = demisto.getParam('credentials')['identifier']
PASSWORD = demisto.getParam('credentials')['password']
MSSP_ACCOUNT_ID = demisto.getParam('mssp_sub_account_id')

if USER_ID == '_api_token_key':
    authorization_header = PASSWORD
else:
    id_and_api_key = USER_ID + ':' + PASSWORD
    authorization_header = base64.b64encode(id_and_api_key.encode("utf-8")).decode()

HEADERS = {'Authorization': 'Basic {}'.format(authorization_header), 'Content-Type': 'application/json',
           'Account-Id': demisto.getParam('credentials')['identifier']}

# Change the Account-Id to the sub account id, so all actions will be on the sub account.
if MSSP_ACCOUNT_ID:
    HEADERS['Account-Id'] = MSSP_ACCOUNT_ID

IOC_TYPE_TO_DBOT_TYPE = {
    'IpAddresses': 'ip',
    'Urls': 'url',
    'Domains': 'domain',
    'Hashes': 'hash'
}

DEFAULT_TIME_RANGE = '1 day'
SEVERITY_LEVEL = {
    'All': 0,
    'Low': 1,
    'Medium': 2,
    'High': 3
}


def http_request(method, path, json_data=None, params=None, json_response=False):
    """
    Send the request to IntSights and return the JSON response
    """
    try:
        response = requests.request(method, URL + path, headers=HEADERS, json=json_data,
                                    params=params, verify=VALIDATE_CERT)
    except requests.exceptions.SSLError:
        raise Exception('Connection error in the API call to IntSights.\nCheck your not secure parameter.')
    except requests.ConnectionError:
        raise Exception('Connection error in the API call to IntSights.\nCheck your Server URL parameter.')

    if response.status_code < 200 or response.status_code > 299:
        if not (response.text == 'SeverityNotChanged' or response.text == 'TagExist'
                or response.text == 'IocBlocklistStatusNotChanged'):
            return_error('Error in API call to IntSights service %s - [%d] %s' %
                         (path, response.status_code, response.text))

    if response.status_code == 204:
        return []  # type: ignore

    if json_response:
        try:
            return response.json()
        except ValueError:
            raise Exception('Error in API call to IntSights service - check your configured URL address')

    return response


def convert_iso_string_to_python_date(date_in_iso_format):
    iso_format = "%Y-%m-%dT%H:%M:%S"
    date_in_python_format = datetime.strptime(date_in_iso_format, iso_format)
    return date_in_python_format


def convert_python_date_to_unix_millisecond(python_date_object):
    timestamp_in_unix_millisecond = date_to_timestamp(python_date_object, 'datetime.datetime')
    return timestamp_in_unix_millisecond


def increase_iso_by_x_days(date_in_iso_format, num_of_days):
    date_in_python_format = convert_iso_string_to_python_date(date_in_iso_format)
    new_date_in_python_format = date_in_python_format + timedelta(days=int(num_of_days))
    new_date_in_iso_format = new_date_in_python_format.isoformat()
    return new_date_in_iso_format


def remove_milliseconds_from_iso(date_in_iso_format):
    date_parts_arr = date_in_iso_format.split('.')
    date_in_iso_without_milliseconds = date_parts_arr[0]
    return date_in_iso_without_milliseconds


def increase_timestamp_by_x_days(date_in_unix_ms_timestamp, num_of_days):
    date_in_iso = timestamp_to_datestring(date_in_unix_ms_timestamp)
    date_in_iso_without_ms = remove_milliseconds_from_iso(date_in_iso)
    date_in_iso_plus_x_days = increase_iso_by_x_days(date_in_iso_without_ms, num_of_days)
    timestamp_in_unix_ms_plus_x_days = date_to_timestamp(date_in_iso_plus_x_days)
    return timestamp_in_unix_ms_plus_x_days


def update_params_with_end_and_start_date(params, oldest_day_to_search_in_unix_timestamp, now_date_in_unix_timestamp):
    params['foundDateFrom'] = oldest_day_to_search_in_unix_timestamp
    params['foundDateTo'] = now_date_in_unix_timestamp
    params['sourceDateFrom'] = oldest_day_to_search_in_unix_timestamp
    params['sourceDateTo'] = now_date_in_unix_timestamp


def update_params_with_delta_arg(params, time_delta_in_days_int):
    now_date_in_iso = datetime.utcnow().isoformat()
    now_date_in_iso_without_ms = remove_milliseconds_from_iso(now_date_in_iso)
    now_date_in_unix_timestamp = date_to_timestamp(now_date_in_iso_without_ms)
    oldest_day_to_search_in_unix_timestamp = increase_timestamp_by_x_days(now_date_in_unix_timestamp,
                                                                          -1 * time_delta_in_days_int)
    update_params_with_end_and_start_date(params, oldest_day_to_search_in_unix_timestamp, now_date_in_unix_timestamp)
    del params['time-delta']


def update_params_dict_according_to_delta_arg(params, time_delta_in_days_int):
    if 'foundDateFrom' in params or 'foundDateTo' in params:
        demisto.debug(
            "ERROR in get_alerts() - can't use found-date-to or found-date-from arguments with time-delta argument")
        return_error("Error: can't assign delta when assigned both found-date-to or found-date-from")
    else:
        update_params_with_delta_arg(params, time_delta_in_days_int)
    return params


def handle_filters(found_date_from=None):
    """
    Apply filters to alert list
    """
    args_camel_case = {
        'alert-type': 'alertType',
        'source-type': 'sourceType',
        'network-type': 'networkType',
        'source-date-from': 'sourceDateFrom',
        'source-date-to': 'sourceDateTo',
        'found-date-from': 'foundDateFrom',
        'found-date-to': 'foundDateTo',
        'is-flagged': 'isFlagged',
        'is-closed': 'isClosed',
        'source-ID': 'sourceId',
        'first-seen-from': 'firstSeenFrom',
        'first-seen-to': 'firstSeenTo',
        'last-seen-from': 'lastSeenFrom',
        'last-updated-from': 'lastUpdatedFrom',
        'last-seen-to': 'lastSeenTo',
        'value': 'iocValue',
    }
    params = {}
    for key in demisto.args():
        if demisto.getArg(key):
            params[args_camel_case.get(key) or key] = demisto.getArg(key)
    if demisto.getArg('time-delta'):
        time_delta_in_days = demisto.getArg('time-delta')
        update_params_dict_according_to_delta_arg(params, int(time_delta_in_days))
    elif found_date_from:
        params['foundDateFrom'] = found_date_from
    return params


def get_alerts_helper(params):
    demisto.info("Executing get_alerts with params: {}".format(params))
    response = http_request('GET', 'public/v1/data/alerts/alerts-list', params=params, json_response=True)
    alerts_human_readable = []
    alerts_context = []
    for alert_id in response:
        alert_human_readable, alert_context = get_alert_by_id_helper(alert_id)
        alerts_human_readable.append(alert_human_readable)
        alerts_context.append(alert_context)
    demisto.debug(f'{len(alerts_context)=} before filtering')
    return alerts_human_readable, alerts_context


def extract_mail(replies):
    if not replies:
        return ''
    mails = []
    for reply in replies:
        mails.append(reply.get('Email'))

    return '\n'.join(mails)


def extract_remediation(remidiations):
    if not remidiations:
        return ''
    remedies = []
    string_format = "{0} - Status: {1}"
    for remedy in remidiations:
        remedies.append(string_format.format(remedy.get('Value'), remedy.get('Status')))

    return '\n'.join(remedies)


def hash_identifier(hash_val):
    if md5Regex.match(hash_val):
        return 'MD5'
    if sha1Regex.match(hash_val):
        return 'SHA1'
    if sha256Regex.match(hash_val):
        return 'SHA256'
    return 'Unknown'


def extract_tags(tags):
    pretty_tags = []
    string_format = "ID: {0} - Name: {1}"
    for tag in tags:
        pretty_tags.append(string_format.format(tag.get('_id'), tag.get('Name')))
    return pretty_tags


def get_alerts():
    """
    Gets all alerts and returns as a list.
    """
    alerts_human_readable, alerts_context = get_alerts_helper(handle_filters())
    headers = ['ID', 'Severity', 'Type', 'FoundDate', 'SourceType', 'SourceURL',
               'SourceEmail', 'SourceNetworkType', 'IsClosed', 'Closed', 'IsFlagged', 'Images', 'Tags',
               'Description', 'Title', 'TakedownStatus', 'SubType']
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': alerts_context},
        'Contents': alerts_context,
        'HumanReadable': tableToMarkdown('IntSights Alerts', alerts_human_readable,
                                         headers=headers, removeNull=False),
        'ContentsFormat': formats['json']
    })


def alert_to_readable(alert, parse_tags):
    """
    Convert alert to readable format
    """

    is_closed = demisto.get(alert, 'IsClosed')
    if is_closed is None:
        is_closed = demisto.get(alert, 'Closed.IsClosed')

    readable = {
        'ID': demisto.get(alert, '_id'),
        'Severity': demisto.get(alert, 'Details.Severity'),
        'Type': demisto.get(alert, 'Details.Type'),
        'FoundDate': demisto.get(alert, 'FoundDate'),
        'SourceType': demisto.get(alert, 'Details.Source.Type'),
        'SourceURL': demisto.get(alert, 'Details.Source.URL'),
        'SourceEmail': demisto.get(alert, 'Details.Source.Email'),
        'SourceNetworkType': demisto.get(alert, 'Details.Source.NetworkType'),
        'IsClosed': is_closed,
        'IsFlagged': demisto.get(alert, 'IsFlagged'),
        'Assets': demisto.get(alert, 'Assets'),
        'Images': demisto.get(alert, 'Details.Images'),
        'Description': demisto.get(alert, 'Details.Description'),
        'Title': demisto.get(alert, 'Details.Title'),
        'TakedownStatus': demisto.get(alert, 'TakedownStatus'),
        'SubType': demisto.get(alert, 'Details.SubType'),
    }

    tags = demisto.get(alert, 'Details.Tags')
    if parse_tags:
        readable['Tags'] = extract_tags(tags)
    else:
        readable['Tag'] = []
        for tag in tags:
            readable['Tag'].append({'ID': tag.get('_id'), 'Name': tag.get('Name')})

    return readable


def get_alert_by_id_helper(alert_id):
    """
    Helper for getting details by ID
    """
    response = http_request('GET', 'public/v1/data/alerts/get-complete-alert/' + alert_id, json_response=True)
    return alert_to_readable(response, True), alert_to_readable(response, False)


def get_alert_by_id():
    """
    Get alert details by id
    """
    alert_id = demisto.getArg('alert-id')
    activity_hr, activity_ctx = get_alert_by_id_helper(alert_id)
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': activity_ctx},
        'Contents': activity_hr,
        'HumanReadable': tableToMarkdown('IntSights Alert Details', [activity_hr],
                                         ['ID', 'Severity', 'Type', 'FoundDate', 'SourceType', 'SourceURL',
                                          'SourceEmail', 'SourceNetworkType', 'IsClosed', 'IsFlagged',
                                          'Images', 'Tags', 'Description', 'Title', 'TakedownStatus', 'SubType']),
        'ContentsFormat': formats['json']
    })


def get_alert_image():
    """
    Retrieves the alert image by image_id
    """
    image_id = demisto.getArg('image-id')
    response = http_request('GET', 'public/v1/data/alerts/alert-image/' + image_id)
    demisto.results(fileResult(image_id + '-image.jpeg', response.content))


def ask_analyst():
    """
    Send question to an analyst about the requested alert
    """
    alert_id = demisto.getArg('alert-id')
    question = demisto.getArg('question')
    http_request('POST', 'public/v1/data/alerts/ask-the-analyst/' + alert_id, json_data={'Question': question})
    question_details = {'ID': alert_id, 'Question': question}
    title = 'IntSights Ask the Analyst: ' \
            'Your question has been successfully sent to an analyst about the requested alert'
    demisto.results(
        {
            'Type': entryTypes['note'],
            'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': question_details},
            'Contents': question_details,
            'HumanReadable': tableToMarkdown(title, [question_details], ['ID', 'Question']),
            'ContentsFormat': formats['json']
        }
    )


def get_alert_activity():
    """
    Retrieves the alert activity by alert-id
    """
    alert_id = demisto.getArg('alert-id')
    response = http_request('GET', 'public/v1/data/alerts/activity-log/' + alert_id, json_response=True)

    alert = {'ID': alert_id, 'Activities': []}
    if not response:
        demisto.results({
            'Type': entryTypes['note'],
            'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': alert},
            'Contents': response,
            'HumanReadable': 'Alert {} does not have activities.'.format(alert_id),
            'ContentsFormat': formats['json']
        })
    else:
        human_readable_arr = []
        for activity in response:
            alert['Activities'].append({
                'ID': demisto.get(activity, '_id'),
                'Type': demisto.get(activity, 'Type'),
                'Initiator': demisto.get(activity, 'Initiator'),
                'CreatedDate': demisto.get(activity, 'CreatedDate'),
                'UpdateDate': demisto.get(activity, 'UpdateDate'),
                'RemediationBlocklistUpdate': demisto.get(activity, 'AdditionalInformation.RemediationBlocklistUpdate'),
                'AskTheAnalyst': {'Replies': demisto.get(activity, 'AdditionalInformation.AskTheAnalyst.Replies')},
                'Mail': {'Replies': demisto.get(activity, 'AdditionalInformation.Mail.Replies')},
                'ReadBy': demisto.get(activity, 'ReadBy')
            })
            human_readable_arr.append({
                'ID': demisto.get(activity, '_id'),
                'Type': demisto.get(activity, 'Type'),
                'Initiator': demisto.get(activity, 'Initiator'),
                'CreatedDate': demisto.get(activity, 'CreatedDate'),
                'UpdateDate': demisto.get(activity, 'UpdateDate'),
                'RemediationBlocklistUpdate': extract_remediation(
                    demisto.get(activity, 'AdditionalInformation.RemediationBlocklistUpdate'))
                if demisto.get(activity, 'AdditionalInformation') else '',
                'AskTheAnalyst': {'Replies': demisto.get(activity, 'AdditionalInformation.AskTheAnalyst.Replies')},
                'Mail': extract_mail(
                    demisto.get(activity, 'AdditionalInformation.Mail.Replies'))
                if demisto.get(activity, 'AdditionalInformation.Mail') else '',
                'ReadBy': demisto.get(activity, 'ReadBy')
            })

        headers = ['ID', 'Type', 'Initiator', 'CreatedDate', 'UpdateDate',
                   'RemediationBlocklistUpdate', 'AskTheAnalyst', 'Mail', 'ReadBy']
        human_readable = tableToMarkdown('IntSights Alert {} Activity Log'.format(alert_id),
                                         t=human_readable_arr, headers=headers),

        demisto.results({
            'Type': entryTypes['note'],
            'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': alert},
            'Contents': response,
            'HumanReadable': human_readable,
            'ContentsFormat': formats['json']
        })


def change_severity():
    """
    Change severity of an alert
    """
    alert_id = demisto.getArg('alert-id')
    severity = demisto.getArg('severity')
    http_request('PATCH', 'public/v1/data/alerts/change-severity/' + alert_id, json_data={'Severity': severity})
    severity_details = {'ID': alert_id, 'Severity': severity}

    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': severity_details},
        'Contents': severity_details,
        'HumanReadable': tableToMarkdown(
            'IntSights Update Alert Severity: The Alert severity has been successfully updated.', [severity_details],
            ['ID', 'Severity']),
        'ContentsFormat': formats['json']
    })


def get_assignee_id(assignee_email):
    response = http_request('GET', 'public/v1/account/users-details', json_response=True)
    for user in response:
        if assignee_email == user.get('Email', ''):
            return user.get('_id')

    raise Exception('user not found')


def assign_alert():
    """
    Assign alert to an Assignee ID
    """
    alert_id = demisto.getArg('alert-id')
    assignee_email = demisto.getArg('assignee-email')
    is_mssp = demisto.getArg('is-mssp-optional')
    assignee_id = get_assignee_id(assignee_email)
    assign_details = {'ID': alert_id, 'Assignees.AssigneeID': assignee_id}

    url = 'public/v1/data/alerts/assign-alert/' + alert_id
    if is_mssp:
        url += '?IsMssp=' + is_mssp
    http_request('PATCH', url, json_data={'AssigneeID': assignee_id})
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': assign_details},
        'Contents': assign_details,
        'HumanReadable': tableToMarkdown(
            'IntSights Assign Alert: The Alert has been successfully assigned to assigneeID', [assign_details],
            ['ID', 'Assignees.AssigneeID']),
        'ContentsFormat': formats['json']
    })


def unassign_alert():
    """
    Unassign an alert
    """
    alert_id = demisto.getArg('alert-id')
    http_request('PATCH', 'public/v1/data/alerts/unassign-alert/' + alert_id)
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': {'ID': alert_id}},
        'Contents': {'ID': alert_id},
        'HumanReadable': 'Alert id: ' + alert_id + ' successfully unassigned',
        'ContentsFormat': formats['json']
    })


def close_alert():
    """
    Close an alert
    """
    alert_id = demisto.getArg('alert-id')
    reason = demisto.getArg('reason')
    free_text = demisto.getArg('free-text')
    is_hidden = demisto.getArg('is-hidden') == 'True'
    rate = demisto.getArg('rate')
    close_details = {'ID': alert_id, 'Close Reason': reason, 'Closed FreeText': free_text, 'Closed Rate': rate,
                     'IsHidden': is_hidden}
    close_details_context = {'ID': alert_id, 'Closed': {'Reason': reason, 'FreeText': free_text, 'Rate': rate},
                             'IsHidden': is_hidden}
    url = 'public/v1/data/alerts/close-alert/' + alert_id
    json_data = {'Reason': reason}

    if free_text:
        json_data['FreeText'] = free_text
    if is_hidden:
        json_data['IsHidden'] = is_hidden
    if rate:
        json_data['Rate'] = rate

    http_request('PATCH', url, json_data)
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': close_details},
        'Contents': close_details_context,
        'HumanReadable': tableToMarkdown('IntSights Close Alert: The Alert has successfully been closed.',
                                         [close_details],
                                         ['ID', 'Close Reason', 'Closed FreeText', 'Closed Rate', 'IsHidden']),
        'ContentsFormat': formats['json']
    })


def send_mail():
    """
    Send email with the alert details and a question
    """
    alert_id = demisto.getArg('alert-id')
    emails = argToList(demisto.getArg('emails'))
    content = demisto.getArg('content')
    http_request('POST', 'public/v1/data/alerts/send-mail/' + alert_id, {'Emails': emails, 'Content': content})
    context = {
        'ID': alert_id,
        'EmailID': emails,
        'Question': content
    }
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': context},
        'Contents': context,
        'HumanReadable': 'Email with content (' + content + ') sent to emails',
        'ContentsFormat': formats['json']
    })


def get_tag_id(alert_id, tag_name):
    response = http_request('GET', 'public/v1/data/alerts/get-complete-alert/' + alert_id, json_response=True)

    details = response.get('Details', {})
    tags = details.get('Tags', [])
    for tag in tags:
        if tag.get('Name', '') == tag_name:
            return tag.get('_id', '')

    return 'Not found'


def add_tag():
    """
    Adds a tag to the alert
    """
    alert_id = demisto.getArg('alert-id')
    tag_name = demisto.getArg('tag-name')
    http_request('PATCH', 'public/v1/data/alerts/add-tag/' + alert_id, json_data={'TagName': tag_name})
    tag_info = {
        'TagName': tag_name,
        'ID': get_tag_id(alert_id, tag_name)
    }
    context = {
        'ID': alert_id,
        'Tags': tag_info
    }
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': context},
        'Contents': context,
        'HumanReadable': 'Tag (' + tag_name + ') added to alert id: ' + alert_id,
        'ContentsFormat': formats['json']
    })


def remove_tag():
    """
    Removes a tag from an alert
    """
    alert_id = demisto.getArg('alert-id')
    tag_id = demisto.getArg('tag-id')
    http_request('PATCH', 'public/v1/data/alerts/remove-tag/' + alert_id, json_data={'TagID': tag_id})
    context = {
        'ID': alert_id,
        'Tags': {'ID': tag_id}
    }
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': context},
        'Contents': context,
        'HumanReadable': 'Tag id: ' + tag_id + ' removed from alert id: ' + alert_id,
        'ContentsFormat': formats['json']
    })


def add_comment():
    """
    Adds a comment to an alert
    """
    alert_id = demisto.getArg('alert-id')
    comment = demisto.getArg('comment')
    http_request('PATCH', 'public/v1/data/alerts/add-comment/' + alert_id, json_data={'Comment': comment})
    context = {
        'ID': alert_id,
        'Comment': comment
    }
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': context},
        'Contents': context,
        'HumanReadable': 'Succesfully added comment "' + comment + '" to alert id: ' + alert_id,
        'ContentsFormat': formats['json']
    })


def ioc_to_readable(ioc_data):
    """
    Convert IOC to readable format
    """
    reported_feeds = demisto.get(ioc_data, 'reportedFeeds', demisto.get(ioc_data, 'sources', [{}]))
    single_reported_feed = reported_feeds[0] if isinstance(reported_feeds, list) else reported_feeds
    ioc_context = {
        'ID': demisto.get(ioc_data, '_id'),
        'Type': demisto.get(ioc_data, 'type'),
        'Value': demisto.get(ioc_data, 'value'),
        'FirstSeen': demisto.get(ioc_data, 'firstSeen'),
        'LastSeen': demisto.get(ioc_data, 'lastSeen'),
        'LastUpdateDate': demisto.get(ioc_data, 'lastUpdateDate'),
        'Status': demisto.get(ioc_data, 'status'),
        'Severity': demisto.get(ioc_data, 'severity'),
        'RelatedMalware': demisto.get(ioc_data, 'relatedMalware'),
        'RelatedCampaigns': demisto.get(ioc_data, 'relatedCampaigns'),
        'Geolocation': demisto.get(ioc_data, 'geolocation', demisto.get(ioc_data, 'Geolocation')),
        'RelatedThreatActors': demisto.get(ioc_data, 'relatedThreatActors'),
        'Sources': reported_feeds,
        'SourceName': single_reported_feed.get('name'),  # bw compatibility
        'SourceConfidence': single_reported_feed.get('confidenceLevel'),
        'SourceID': single_reported_feed.get('id'),
        'Tags': demisto.get(ioc_data, 'tags'),
        'Whitelisted': demisto.get(ioc_data, 'Whitelisted', demisto.get(ioc_data, 'whitelisted')),
    }
    ioc_readable = {
        'ID': demisto.get(ioc_data, '_id'),
        'AccountID': demisto.get(ioc_context, 'AccountID'),
        'Type': demisto.get(ioc_context, 'Type'),
        'Value': demisto.get(ioc_context, 'Value'),
        'FirstSeen': demisto.get(ioc_context, 'FirstSeen'),
        'LastSeen': demisto.get(ioc_context, 'LastSeen'),
        'Status': demisto.get(ioc_context, 'Status'),
        'Severity': demisto.get(ioc_context, 'Severity'),
        'Geolocation': demisto.get(ioc_context, 'Geolocation'),
        'SourceName': demisto.get(ioc_context, 'SourceName'),
        'SourceID': demisto.get(ioc_context, 'SourceID'),
        'SourceConfidence': demisto.get(ioc_context, 'SourceConfidence')
    }
    dbot_score = {
        'Indicator': ioc_context['Value'],
        'Type': IOC_TYPE_TO_DBOT_TYPE[ioc_context['Type']],
        'Vendor': 'IntSights',
        'Score': translate_severity(ioc_readable['Severity'])
    }
    malicious_dict = {
        'Vendor': 'IntSights',
        'Description': 'IntSights severity level is High'
    }
    domain = {}
    if ioc_context['Type'] == 'Domains':
        domain['Name'] = ioc_context['Value']
        ioc_context['Domain'] = ioc_context['Value']
        if translate_severity(ioc_readable['Severity']) == 3:
            domain['Malicious'] = malicious_dict

    ip_info = {}
    if ioc_context['Type'] == 'IpAddresses':
        ip_info['Address'] = ioc_context['Value']
        if translate_severity(ioc_readable['Severity']) == 3:
            ip_info['Malicious'] = malicious_dict

    url_info = {}
    if ioc_context['Type'] == 'Urls':
        url_info['Data'] = ioc_context['Value']
        if translate_severity(ioc_readable['Severity']) == 3:
            url_info['Malicious'] = malicious_dict

    hash_info = {}
    if ioc_context['Type'] == 'Hashes':
        hash_info['Name'] = ioc_context['Value']
        hash_info[hash_identifier(ioc_context['Value'])] = ioc_context['Value']
        if translate_severity(ioc_readable['Severity']) == 3:
            hash_info['Malicious'] = malicious_dict

    return ioc_context, ioc_readable, dbot_score, domain, ip_info, url_info, hash_info


def search_for_ioc():
    """
    Search for IOC by value
    """
    response = http_request('GET', 'public/v3/iocs/ioc-by-value', params=handle_filters(), json_response=True)

    if response:
        ioc_context, ioc_readable, dbot_score, domain, ip_info, url_info, hash_info = ioc_to_readable(response)

        demisto.results(
            {
                'Type': entryTypes['note'],
                'EntryContext': {
                    'IntSights.Iocs(val.Value === obj.Value)': ioc_context,
                    'DBotScore': dbot_score,
                    'Domain': domain,
                    'IP': ip_info,
                    'URL': url_info,
                    'File': hash_info
                },
                'Contents': response,
                'HumanReadable': tableToMarkdown('IOC Information', [ioc_readable],
                                                 ['Type', 'Value', 'FirstSeen',
                                                  'LastSeen', 'Status', 'Severity', 'SourceID', 'SourceName',
                                                  'SourceConfidence', 'GeoLocation']),
                'ContentsFormat': formats['json']
            }
        )
    else:
        results_for_no_content('IOC Information', 'Could not get any results.')


def results_for_no_content(cmd_name, additional_information):
    demisto.results(
        {
            'Type': entryTypes['note'],
            'EntryContext': {'IntSights': {}},
            'Contents': {},
            'HumanReadable': '### {} \n\n {}'.format(cmd_name, additional_information),
            'ContentsFormat': formats['json']
        }
    )


def ioc_enrichment_to_readable(ioc_data):
    """
    Convert IOC to readable format
    """
    ioc_context = {
        'Type': demisto.get(ioc_data, 'Data.Type'),
        'Value': demisto.get(ioc_data, 'Data.Value'),
        'FirstSeen': demisto.get(ioc_data, 'Data.FirstSeen'),
        'LastSeen': demisto.get(ioc_data, 'Data.LastSeen'),
        'Status': demisto.get(ioc_data, 'Status'),
        'Severity': demisto.get(ioc_data, 'Data.Severity.Value'),
        'RelatedMalwares': demisto.get(ioc_data, 'Data.RelatedMalwares'),
        'Sources': demisto.get(ioc_data, 'Data.Sources'),
        'IsKnownIoc': demisto.get(ioc_data, 'Data.IsKnownIoc'),
        'RelatedThreatActors': demisto.get(ioc_data, 'Data.RelatedThreatActors'),
        'SystemTags': demisto.get(ioc_data, 'Data.SystemTags'),
        'Tags': demisto.get(ioc_data, 'Data.Tags'),
        'Whitelisted': demisto.get(ioc_data, 'Data.Whitelisted'),
        'OriginalValue': demisto.get(ioc_data, 'Data.OriginalValue'),

    }
    ioc_readable = {
        'Type': demisto.get(ioc_data, 'Data.Type'),
        'Value': demisto.get(ioc_data, 'Data.Value'),
        'FirstSeen': demisto.get(ioc_data, 'Data.FirstSeen'),
        'LastSeen': demisto.get(ioc_data, 'Data.LastSeen'),
        'Status': demisto.get(ioc_data, 'Status'),
        'Severity': demisto.get(ioc_data, 'Data.Severity.Value'),
        'RelatedMalwares': demisto.get(ioc_data, 'Data.RelatedMalwares'),
        'Sources': demisto.get(ioc_data, 'Data.Sources'),
        'IsKnownIoc': demisto.get(ioc_data, 'Data.IsKnownIoc'),
        'RelatedThreatActors': demisto.get(ioc_data, 'Data.RelatedThreatActors'),
        'SystemTags': demisto.get(ioc_data, 'Data.SystemTags'),
        'Tags': demisto.get(ioc_data, 'Data.Tags'),
        'Whitelisted': demisto.get(ioc_data, 'Data.Whitelisted'),
        'OriginalValue': demisto.get(ioc_data, 'OriginalValue'),

    }
    dbot_score = {
        'Indicator': ioc_context['Value'],
        'Type': IOC_TYPE_TO_DBOT_TYPE[ioc_context['Type']],
        'Vendor': 'IntSights',
        'Score': translate_severity(ioc_readable['Severity'])
    }
    malicious_dict = {
        'Vendor': 'IntSights',
        'Description': 'IntSights severity level is High'
    }
    domain = {}
    if ioc_context['Type'] == 'Domains':
        domain['Name'] = ioc_context['Value']
        if translate_severity(ioc_readable['Severity']) == 3:
            domain['Malicious'] = malicious_dict
        domain['DNS'] = demisto.get(ioc_data, 'Data.DnsRecords')
        domain['Resolutions'] = demisto.get(ioc_data, 'Data.Resolutions')
        domain['Subdomains'] = demisto.get(ioc_data, 'Data.Subdomains')
        domain['WHOIS/History'] = demisto.get(ioc_data, 'Data.Whois.History')
        domain['WHOIS'] = {
            'Registrant': {
                'Name': demisto.get(ioc_data, 'Data.Whois.Current.RegistrantDetails.Name'),
                'Email': demisto.get(ioc_data, 'Data.Whois.Current.RegistrantDetails.Email'),
                'Phone': demisto.get(ioc_data, 'Data.Whois.Current.RegistrantDetails.Telephone'),
            },
            'DomainStatus': ', '.join(demisto.get(ioc_data, 'Data.Whois.Current.RegistrationDetails.Statuses')),
            'NameServers': ', '.join(demisto.get(ioc_data, 'Data.Whois.Current.RegistrationDetails.NameServers')),
            'CreationDate': demisto.get(ioc_data, 'Data.Whois.Current.RegistrationDetails.CreatedDate'),
            'UpdatedDate': demisto.get(ioc_data, 'Data.Whois.Current.RegistrationDetails.UpdatedDate'),
            'ExpirationDate': demisto.get(ioc_data, 'Data.Whois.Current.RegistrationDetails.ExpiresDate')
        }

    ip_info = {}
    if ioc_context['Type'] == 'IpAddresses':
        ip_info['Address'] = ioc_context['Value']
        if translate_severity(ioc_readable['Severity']) == 3:
            ip_info['Malicious'] = malicious_dict

        ip_info['IpDetails'] = demisto.get(ioc_data, 'Data.IpDetails')
        ip_info['RelatedHashes'] = demisto.get(ioc_data, 'Data.RelatedHashes')
        ip_info['WHOIS'] = {
            'NetworkDetails': demisto.get(ioc_data, 'Data.Whois.NetworkDetails'),
            'RegistrantDetails': demisto.get(ioc_data, 'Data.Whois.RegistrantDetails')
        }

    url_info = {}
    if ioc_context['Type'] == 'Urls':
        url_info['Data'] = ioc_context['Value']
        if translate_severity(ioc_readable['Severity']) == 3:
            url_info['Malicious'] = malicious_dict

        url_info['AntivirusDetectedEngines'] = demisto.get(ioc_data, 'Data.AntivirusDetectedEngines')
        url_info['AntivirusDetectionRatio'] = demisto.get(ioc_data, 'Data.AntivirusDetectionRatio')
        url_info['AntivirusDetections'] = demisto.get(ioc_data, 'Data.AntivirusDetections')
        url_info['AntivirusScanDate'] = demisto.get(ioc_data, 'Data.AntivirusScanDate')
        url_info['RelatedHashes'] = {
            'communicating': demisto.get(ioc_data, 'Data.RelatedHashes.communicating'),
            'downloaded': demisto.get(ioc_data, 'Data.RelatedHashes.downloaded'),
            'referencing': demisto.get(ioc_data, 'Data.RelatedHashes.referencing'),
        }

    hash_info = {}
    if ioc_context['Type'] == 'Hashes':
        hash_info['Name'] = ioc_context['Value']
        hash_info[hash_identifier(ioc_context['Value'])] = ioc_context['Value']
        if translate_severity(ioc_readable['Severity']) == 3:
            hash_info['Malicious'] = malicious_dict

        hash_info['AntivirusDetectedEngines'] = demisto.get(ioc_data, 'Data.AntivirusDetectedEngines')
        hash_info['AntivirusDetectionRatio'] = demisto.get(ioc_data, 'Data.AntivirusDetectionRatio')
        hash_info['AntivirusDetections'] = demisto.get(ioc_data, 'Data.AntivirusDetections')
        hash_info['AntivirusScanDate'] = demisto.get(ioc_data, 'Data.AntivirusScanDate')

    return ioc_context, ioc_readable, dbot_score, domain, ip_info, url_info, hash_info


def request_for_ioc_enrichment():
    """
    Request for IOC enrichment
    """
    ioc_value = demisto.getArg('value')
    request_url = 'public/v1/iocs/enrich/{}'.format(ioc_value)

    response = http_request('GET', request_url, json_response=True)
    status = response.get('Status')
    if status == 'Done':
        ioc_context, ioc_readable, dbot_score, domain, ip_info, url_info, hash_info = ioc_enrichment_to_readable(
            response)

        demisto.results(
            {
                'Type': entryTypes['note'],
                'EntryContext': {
                    'IntSights.Iocs(val.ID === obj.ID)': ioc_context,
                    'DBotScore': dbot_score,
                    'Domain': domain,
                    'IP': ip_info,
                    'URL': url_info,
                    'File': hash_info
                },
                'Contents': response,
                'HumanReadable': tableToMarkdown('IOC Enrichment', ioc_readable),
                'ContentsFormat': formats['json']
            }
        )
    elif status == 'Queued' or status == 'InProgress':
        demisto.results(
            {
                'Type': entryTypes['note'],
                'EntryContext': {
                    'IntSights.Iocs(val.ID === obj.ID)': {
                        'Value': demisto.get(response, 'OriginalValue'),
                        'Status': demisto.get(response, 'Status')
                    },
                },
                'Contents': response,
                'ContentsFormat': formats['json']
            }
        )
    elif status == 'QuotaExceeded':
        raise Exception('Could not get any results. Reason: Quota exceded.')
    else:
        reason = response.get('FailedReason', '')
        raise Exception('Could not get any results. Reason: {}.'.format(reason))


def translate_severity(sev):
    """
    Translate alert severity to demisto
    """
    if sev in ['Medium', 'High']:
        return 3
    if sev == 'Low':
        return 2
    return 0


def fetch_incidents():
    """
    Fetch incidents for Demisto
    """
    last_run = demisto.getLastRun()
    demisto.info("IntSight fetch last run time is: {}".format(str(last_run)))
    if not last_run or 'time' not in last_run:
        first_fetch_param = demisto.params().get('first_fetch', DEFAULT_TIME_RANGE)
        first_fetch_date = arg_to_datetime(first_fetch_param, 'first_fetch')
        fetch_delta = int(first_fetch_date.timestamp() * 1000)  # type:ignore
        demisto.debug(f'First fetch, using {fetch_delta=}')

    else:
        fetch_delta = last_run.get('time')

    current_fetch = fetch_delta
    alert_type = demisto.getParam('type')
    min_severity_level = demisto.params().get('severity_level', 'All')
    if min_severity_level not in SEVERITY_LEVEL:
        raise Exception("Minimum Alert severity level to fetch incidents incidents from, allowed values are: All,"
                        " Low, Medium, High. (Setting to All will fetch all incidents)")
    demisto.debug(f'{min_severity_level=}')

    _, alerts_context = get_alerts_helper(handle_filters(fetch_delta))
    incidents = []
    for alert in alerts_context:
        if SEVERITY_LEVEL[min_severity_level] <= SEVERITY_LEVEL[alert.get('Severity', 'Low')]:
            if not alert_type or alert_type.lower() == alert.get('Type', '').lower():
                incidents.append({
                    'name': '{type} - {id}'.format(type=alert.get('Type', 'Type not found'), id=alert.get('ID')),
                    'occurred': alert.get('FoundDate'),
                    'severity': translate_severity(alert.get('Severity')),
                    'rawJSON': json.dumps(alert)
                })
                alert_timestamp = date_to_timestamp(alert.get('FoundDate'), date_format='%Y-%m-%dT%H:%M:%S.%fZ')
                if alert_timestamp > current_fetch:
                    current_fetch = alert_timestamp
            else:
                demisto.debug(f'dropping incident with id {alert.get("ID")} because of alert_type filter')
        else:
            demisto.debug(f'dropping incident with id {alert.get("ID")} because of severity filter')
    demisto.debug(f'returning {len(incidents)} incidents')
    demisto.incidents(incidents)
    demisto.setLastRun({'time': current_fetch + 1000})


def get_iocs():
    """
    Gets all IOCs with the given filters
    """
    response = http_request('GET', 'public/v3/iocs', params=handle_filters(), json_response=True)
    content = response.get('content')
    domains = []
    ip_infos = []
    url_infos = []
    hash_infos = []
    dbot_scores = []
    iocs_context = []
    iocs_readable = []

    for indicator in content:
        ioc_context, ioc_readable, dbot_score, domain, ip_info, url_info, hash_info = ioc_to_readable(indicator)
        iocs_context.append(ioc_context)
        iocs_readable.append(ioc_readable)
        dbot_scores.append(dbot_score)
        domains.append(domain)
        ip_infos.append(ip_info)
        url_infos.append(url_info)
        hash_infos.append(hash_info)

    headers = ['Type', 'Value', 'FirstSeen', 'LastSeen', 'Status', 'Severity', 'SourceID', 'SourceName',
               'SourceConfidence', 'Geolocation']
    demisto.results(
        {
            'Type': entryTypes['note'],
            'EntryContext': {
                'IntSights.Iocs(val.Value && val.Value === obj.Value)': iocs_context,
                'DBotScore': dbot_scores,
                'Domain': domains,
                'IP': ip_infos,
                'URL': url_infos,
                'File': hash_infos
            },
            'Contents': response,
            'HumanReadable': tableToMarkdown('IOC Information', t=iocs_readable, headers=headers),
            'ContentsFormat': formats['json']
        }
    )


def takedown_request():
    """
    Request alert takedown
    """
    alert_id = demisto.getArg('alert-id')
    http_request('PATCH', 'public/v1/data/alerts/takedown-request/' + alert_id)
    context = {
        'ID': alert_id,
    }
    human_readable = '### IntSights Alert Takedown\n' \
                     'The Alert Takedown request has been sent successfully for {}'.format(str(alert_id))
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': context},
        'Contents': context,
        'HumanReadable': human_readable,
        'ContentsFormat': formats['json']
    })


def get_alert_takedown_status():
    """
    Get an alert's takedown status
    """
    alert_id = demisto.getArg('alert-id')
    response = http_request('GET', 'public/v1/data/alerts/takedown-status/' + alert_id)
    context = {
        'ID': alert_id,
        'TakedownStatus': response.text
    }
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': context},
        'Contents': context,
        'HumanReadable': tableToMarkdown('IntSights Alert Takedown Status', [context], ['ID', 'TakedownStatus']),
        'ContentsFormat': formats['json']
    })


def update_ioc_blocklist_status():
    alert_id = demisto.getArg('alert-id')
    types = argToList(demisto.getArg('type'))
    values = argToList(demisto.getArg('value'))
    statuses = argToList(demisto.getArg('blocklist-status'))
    if len(types) != len(values) or len(types) != len(statuses):
        return_error('The lists must be of equal length. For each IOC, provide an entry in each list.')
    data = []
    for count, type_ in enumerate(types):
        data.append({
            'Type': type_,
            'Value': values[count],
            'BlocklistStatus': statuses[count]
        })
    http_request('PATCH', 'public/v1/data/alerts/change-iocs-blocklist-status/' + alert_id, json_data={'Iocs': data})
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.Alerts(val.ID === obj.ID)': {'ID': alert_id, 'Status': statuses}},
        'Contents': {'ID': alert_id, 'Status': statuses},
        'HumanReadable': tableToMarkdown('IntSights Update IOC BlockList Status for ' + alert_id, data,
                                         ['BlocklistStatus']),
        'ContentsFormat': formats['json']
    })


def get_ioc_blocklist_status():
    alert_id = demisto.getArg('alert-id')
    response = http_request('GET', 'public/v1/data/alerts/blocklist-status/' + alert_id, json_response=True)
    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {
            'IntSights.Alerts(val.ID === obj.ID)': {'ID': alert_id, 'Status': [ioc.get('Status') for ioc in response]}},
        'Contents': response,
        'HumanReadable': tableToMarkdown('IntSights Blocklist Status for ' + alert_id, response, ['Status']),
        'ContentsFormat': formats['json']
    })


def get_mssp_sub_accounts():
    account_id = demisto.getParam('credentials')['identifier']
    MSSP_ACCOUNT_ID = demisto.getParam('mssp_sub_account_id')
    accounts = http_request('GET', 'public/v1/mssp/customers', json_response=True)
    if not accounts:
        return_error("intsights-mssp-get-sub-accounts failed to return data.")

    # Fix accounts _id keys
    for account in accounts:
        account["ID"] = account["_id"]
        del account["_id"]

    if len(accounts) < 1:
        return_error('Current MSSP Account has no sub accounts.')

    account_ids = [i["ID"] for i in accounts]
    if MSSP_ACCOUNT_ID not in account_ids:
        demisto.debug("[DEBUG] - MSSP sub accounts:" + str(accounts))
        return_error('Entered sub account id ({}) is not part of this mssp account'.format(MSSP_ACCOUNT_ID))

    for i, account in enumerate(account_ids):
        # Call account
        HEADERS['Account-Id'] = account
        account_ua = http_request('GET', 'public/v1/account/used-assets', json_response=True)

        if not account_ua:
            continue

        accounts[i].update(account_ua)

    demisto.results({
        'Type': entryTypes['note'],
        'EntryContext': {'IntSights.MsspAccount(val.ID === obj.ID)': accounts},
        'HumanReadable': tableToMarkdown('IntSights MSSP accounts used assets ' + account_id, accounts,
                                         ["ID", 'CompanyName', "Status", "AssetsLimit", "AssetsCount"]),
        'Contents': accounts,
        'ContentsFormat': formats['json']
    })

    # Restore the header
    HEADERS['Account-Id'] = MSSP_ACCOUNT_ID


def test_module():
    http_request('GET', 'public/v1/api/version')
    if demisto.params().get('isFetch'):
        min_severity_level = demisto.params().get('severity_level', 'All')
        if min_severity_level not in SEVERITY_LEVEL:
            return_error("Minimum Alert severity level to fetch incidents incidents from, allowed values are: "
                         "All, Low, Medium, High. (Setting to All will fetch all incidents)")

    demisto.results('ok')


def main():  # pragma: no cover
    try:
        if demisto.command() == 'test-module':
            test_module()
        elif demisto.command() == 'fetch-incidents':
            fetch_incidents()
        elif demisto.command() == 'intsights-mssp-get-sub-accounts':
            get_mssp_sub_accounts()
        elif demisto.command() == 'intsights-get-alerts':
            get_alerts()
        elif demisto.command() == 'intsights-get-alert-image':
            get_alert_image()
        elif demisto.command() == 'intsights-get-alert-activities':
            get_alert_activity()
        elif demisto.command() == 'intsights-assign-alert':
            assign_alert()
        elif demisto.command() == 'intsights-unassign-alert':
            unassign_alert()
        elif demisto.command() == 'intsights-send-mail':
            send_mail()
        elif demisto.command() == 'intsights-ask-the-analyst':
            ask_analyst()
        elif demisto.command() == 'intsights-add-tag-to-alert':
            add_tag()
        elif demisto.command() == 'intsights-remove-tag-from-alert':
            remove_tag()
        elif demisto.command() == 'intsights-add-comment-to-alert':
            add_comment()
        elif demisto.command() == 'intsights-update-alert-severity':
            change_severity()
        elif demisto.command() == 'intsights-get-alert-by-id':
            get_alert_by_id()
        elif demisto.command() == 'intsights-get-ioc-by-value':
            search_for_ioc()
        elif demisto.command() == 'intsights-request-ioc-enrichment':
            request_for_ioc_enrichment()
        elif demisto.command() == 'intsights-get-iocs':
            get_iocs()
        elif demisto.command() == 'intsights-alert-takedown-request':
            takedown_request()
        elif demisto.command() == 'intsights-get-alert-takedown-status':
            get_alert_takedown_status()
        elif demisto.command() == 'intsights-get-ioc-blocklist-status':
            get_ioc_blocklist_status()
        elif demisto.command() == 'intsights-update-ioc-blocklist-status':
            update_ioc_blocklist_status()
        elif demisto.command() == 'intsights-close-alert':
            close_alert()
        elif demisto.command() == 'intsights-test-action':
            pass
        else:
            raise Exception('Unrecognized command: ' + demisto.command())
    except Exception as err:
        return_error(str(err))


if __name__ in ('__main__', 'builtin', 'builtins'):
    main()