ThreatQ v2

A threat intelligence platform that collects and interprets intelligence data from open sources and manages indicator scoring, types, and attributes.

Data Enrichment & Threat Intelligence · ThreatQ

Details

IDThreatQ v2
ProviderSecuronix
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10325753
Supported ModulesAgentix XSIAM

README

A threat intelligence platform that collects and interprets intelligence data from open sources and manages indicator scoring, types, and attributes.

This integration was integrated and tested with API versions 4 and 5 of ThreatQ.

Configure ThreatQ v2 on Cortex XSOAR

  1. Navigate to Settings > Integrations  > Servers & Services.
  2. Search for ThreatQ v2.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • ThreatQ server URL (e.g. https://192.168.1.136)
    • ThreatQ client ID
    • Email
    • Indicator threshold (minimum TQ score to consider the indicator malicious).
    • Trust any certificate (not secure)
    • Use system proxy settings
  4. Click Test to validate the new instance.

Commands

You can execute these commands from the Cortex XSOAR 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.

  1. threatq-search-by-name: threatq-search-by-name
  2. Check an IP address: ip
  3. Check a URL: url 
  4. Check a file: file
  5. Check an email: email
  6. Check a domain: domain
  7. Create an indicator: threatq-create-indicator
  8. Add an attribute: threatq-add-attribute 
  9. Modify an attribute: threatq-modify-attribute
  10. Link two objects: threatq-link-objects
  11. Create an adversary: threatq-create-adversary
  12. Create an event: threatq-create-event
  13. Get related indicators: threatq-get-related-indicators
  14. Update an indicator status: threatq-update-status
  15. Get related events: threatq-get-related-events
  16. Get related adversaries: threatq-get-related-adversaries
  17. Upload a file: threatq-upload-file
  18. Search by Object type and ID: threatq-search-by-id
  19. Unlink two objects: threatq-unlink-objects
  20. Delete an object: threatq-delete-object
  21. Add a source to an object: threatq-add-source
  22. Delete a source from an object: threatq-delete-source
  23. Delete an attribute: threatq-delete-attribute
  24. Edit an adversary: threatq-edit-adversary
  25. Edit an indicator: threatq-edit-indicator
  26. Edit an event: threatq-edit-event
  27. Update a score of an indictor: threatq-update-score
  28. Download a file to Cortex XSOAR: threatq-download-file
  29. Get all indicators: threatq-get-all-indicators:
  30. Get a list of events: threatq-get-all-events
  31. Get a list of all adversaries: threatq-get-all-adversaries

1. Search for object by name


Searches for objects by name in the ThreatQ repository.

Base Command

threatq-search-by-name

Input
Argument Name Description Required
name Name of the object to search. Required
limit The maximum number of records to retrieve. Optional

 

Context Output
Path Type Description
ThreatQ.Indicator.ID Number The ID of the Indicator.
ThreatQ.Indicator.Value String The value of the Indicator.
ThreatQ.Event.ID Number The ID of the Event.
ThreatQ.Event.Title String The title of the Event.
ThreatQ.Adversary.ID Number The ID of the Adversary.
ThreatQ.Adversary.Name String The name of the Adversary.

 

Command Example
  !threatq-search-by-name name=test limit=6
Human Readable Output

search-by-name.png

2. Check an IP address


Checks the reputation of an IP address in ThreatQ.

Base Command

ip

Input
Argument Name Description Required
ip The IP address to check. Required

 

Context Output
Path Type Description
DBotScore.Indicator String The value of the indicator.
DBotScore.Type String The type of the indicator.
DBotScore.Vendor String The vendor of the indicator.
DBotScore.Score Number The DBot Score of the indicator.
IP.Address String The IP Address.
IP.Malicious.Vendor String The IP address of the Vendor.
IP.Malicious.Description String The description of the Malicious IP address.
ThreatQ.Indicator.ID Number The ID of the Indicator.
ThreatQ.Indicator.Value String The value of the indicator.
ThreatQ.Indicator.Source.ID Number The source ID of the indicator.
ThreatQ.Indicator.Source.Name String The source name of the indicator.
ThreatQ.Indicator.Attribute.ID Number The attribute ID of the indicator.
ThreatQ.Indicator.Attribute.Value String The attribute value of the indicator.
ThreatQ.Indicator.Attribute.Name String The attribute name of the indicator.
ThreatQ.Indicator.CreatedAt Date The creation date of the indicator.
ThreatQ.Indicator.UpdatedAt Date The last update date of the indicator.
ThreatQ.Indicator.Status String The status of the indicator.
ThreatQ.Indicator.TQScore Number The ThreatQ score of the indicator.
ThreatQ.Indicator.Description String The description of the indicator.
ThreatQ.Indicator.Type String The type of the indicator.

 

Command Example
  !ip ip=91.140.64.113
Human Readable Output

ip.png

3. Check a URL


Checks the reputation of a URL in ThreatQ.

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

Base Command

url

Input
Argument Name Description Required
url The URL to check. Required

 

Context Output
Path Type Description
DBotScore.Indicator String The value of the indicator.
DBotScore.Type String The type of the indicator.
DBotScore.Vendor String The vendor of the indicator.
DBotScore.Score Number The DBot Score of the indicator.
URL.Data String The URL.
URL.Malicious.Vendor String The vendor of the malicious URL.
URL.Malicious.Description String The description of the malicious URL.
ThreatQ.Indicator.ID Number The ID of the indicator.
ThreatQ.Indicator.Value String The value of the indicator.
ThreatQ.Indicator.Source.ID Number The source of the indicator.
ThreatQ.Indicator.Source.Name String The source of the indicator.
ThreatQ.Indicator.Attribute.ID Number The attribute ID of the indicator.
ThreatQ.Indicator.Attribute.Value String The attribute value of the indicator.
ThreatQ.Indicator.Attribute.Name String The attribute name of the indicator.
ThreatQ.Indicator.CreatedAt Date The creation date of the indicator.
ThreatQ.Indicator.UpdatedAt Date The last update date of the indicator.
ThreatQ.Indicator.Status String The status of the indicator.
ThreatQ.Indicator.TQScore Number The ThreatQ score of the indicator.
ThreatQ.Indicator.Description String The description of the indicator.
ThreatQ.Indicator.Type String The type of the indicator.

 

Command Example
  !url url=https://www.paloaltonetworks.com/
Human Readable Output

url.png

4. Check a file


Checks the reputation of a file in ThreatQ.

Base Command

file

Input
Argument Name Description Required
file The MD5, SHA-1 or SHA-256 file to check. Required

 

Context Output
Path Type Description
DBotScore.Indicator String The value of the indicator.
DBotScore.Type String The type of the indicator.
DBotScore.Vendor String The vendor of the indicator.
DBotScore.Score Number The DBot Score of the indicator.
File.Name String The name of the file.
File.MD5 String The MD5 of the file.
File.SHA1 String The SHA1 of the file.
File.SHA256 String The SHA256 of the file.
File.SHA512 String The SHA512 of the file.
File.Path String The path of the file.
File.Malicious.Vendor String The vendor of the malicious file.
File.Malicious.Description String The description of the malicious file.
ThreatQ.Indicator.ID Number The ID of the indicator.
ThreatQ.Indicator.Value String The value of the indicator.
ThreatQ.Indicator.Source.ID Number The source ID of the indicator.
ThreatQ.Indicator.Source.Name String The source name of the indicator.
ThreatQ.Indicator.Attribute.ID Number The attribute ID of the indicator.
ThreatQ.Indicator.Attribute.Value String The attribute value of the indicator.
ThreatQ.Indicator.Attribute.Name String The attribute name of the indicator.
ThreatQ.Indicator.CreatedAt Date The creation date of the indicator.
ThreatQ.Indicator.UpdatedAt Date The last update date of the indicator.
ThreatQ.Indicator.Status String The status of the indicator.
ThreatQ.Indicator.TQScore Number The ThreatQ score of the indicator.
ThreatQ.Indicator.Description String The description of the indicator.
ThreatQ.Indicator.Type String The type of the indicator.

 

Command Example
  !file file=a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
Human Readable Output

file.png

5. Check an email


Checks the reputation of an email in ThreatQ.

Base Command

email

Input
Argument Name Description Required
email The email address to check. Required

 

Context Output
Path Type Description
DBotScore.Indicator String The value of the indicator.
DBotScore.Type String The type of the indicator.
DBotScore.Vendor String The vendor of the indicator.
DBotScore.Score Number The DBot Score of the indicator.
Account.Email.Address String The Email Address.
Account.Malicious.Vendor String The vendor of the malicious account.
Account.Malicious.Description String The description of the malicious account.
ThreatQ.Indicator.ID Number The ID of the indicator.
ThreatQ.Indicator.Value String The value of the indicator.
ThreatQ.Indicator.Source.ID Number The source ID of the indicator.
ThreatQ.Indicator.Source.Name String The source name of the indicator.
ThreatQ.Indicator.Attribute.ID Number The attribute ID of the indicator.
ThreatQ.Indicator.Attribute.Value String The attribute value of the indicator.
ThreatQ.Indicator.Attribute.Name String The attribute name of the indicator.
ThreatQ.Indicator.CreatedAt Date The creation date of the indicator.
ThreatQ.Indicator.UpdatedAt Date The last update date of the indicator.
ThreatQ.Indicator.Status String The status of the indicator.
ThreatQ.Indicator.TQScore Number The ThreatQ score of the indicator.
ThreatQ.Indicator.Description String The description of the indicator.
ThreatQ.Indicator.Type String The type of the indicator.

 

Command Example
  !email email=example.gmail.com
Human Readable Output

email.png

6. Check a domain


Checks the reputation of a domain in ThreatQ.

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

Base Command

domain

Input
Argument Name Description Required
domain The domain or FQDN to check. Required

 

Context Output
Path Type Description
DBotScore.Indicator String The value of the indicator.
DBotScore.Vendor String The vendor of the indicator.
DBotScore.Type String The type of the indicator.
DBotScore.Score Number The DBot Score of the indicator.
Domain.Name String The name of the domain.
Domain.Malicious.Vendor String The vendor of the malicious domain.
Domain.Malicious.Description String The description of the malicious domain.
ThreatQ.Indicator.ID Number The ID of the indicator.
ThreatQ.Indicator.Value String The value of the indicator.
ThreatQ.Indicator.Source.ID Number The source ID of the indicator.
ThreatQ.Indicator.Source.Name String The source name of the indicator.
ThreatQ.Indicator.Attribute.ID Number The attribute ID of the indicator.
ThreatQ.Indicator.Attribute.Value String The attribute value of the indicator.
ThreatQ.Indicator.Attribute.Name String The attribute name of the indicator.
ThreatQ.Indicator.CreatedAt Date The creation date of the indicator.
ThreatQ.Indicator.UpdatedAt Date The last update date of the indicator.
ThreatQ.Indicator.Status String The status of the indicator.
ThreatQ.Indicator.TQScore Number The ThreatQ score of the indicator.
ThreatQ.Indicator.Description String The description of the indicator.
ThreatQ.Indicator.Type String The type of the indicator.

 

Command Example
!domain domain=www.testdomain.com
Human Readable Output

domain.png

7. Create an indicator


Creates a new indicator in ThreatQ.

Base Command

threatq-create-indicator

Input
Argument Name Description Required
type The type of indicator, such as email address, IP address, Registry key, binary string, and so on. Required
status The status of the indicator. Can be: "Active", "Expired", "Indirect", "Review", or "Whitelisted". Required
value The value of the indicator. Required
sources List of Sources names, separated by commas. Optional
attributes_names Attributes names list, separated by commas. The i-th element in the attributes names list corresponds to the i-th element in the attributes values list. Optional
attributes_values Attributes values list, separated by commas. The i-th element in the attributes values list corresponds to the i-th element in the attributes names list. Optional

 

Context Output
Path Type Description
ThreatQ.Indicator.ID Number The ID of the indicator.
ThreatQ.Indicator.Value String The value of the indicator.
ThreatQ.Indicator.Source.ID Number The source ID of the indicator.
ThreatQ.Indicator.Source.Name String The source name of the indicator.
ThreatQ.Indicator.Attribute.ID Number The attribute ID of the indicator.
ThreatQ.Indicator.Attribute.Value String The attribute value of the indicator.
ThreatQ.Indicator.Attribute.Name String The attribute name of the indicator.
ThreatQ.Indicator.CreatedAt Date The creation date of the indicator.
ThreatQ.Indicator.UpdatedAt Date The last update date of the indicator.
ThreatQ.Indicator.Status String The status of the indicator.
ThreatQ.Indicator.TQScore Number The ThreatQ score of the indicator.
ThreatQ.Indicator.Description String The description of the indicator.
ThreatQ.Indicator.Type String The type of the indicator.

 

Command Example
  !threatq-create-indicator value=232.12.34.135 status=Review type="IP Address" attributes_names=TestAttr1,TestAttr2 attributes_values=Val1,Val2 sources=arian@demisto.com
Human Readable Output

create-indicator.png

8. Add an attribute 


Adds an attribute to an object in ThreatQ.

Base Command

threatq-add-attribute

Input
Argument Name Description Required
name The name of the attribute to add. Required
value The value of the attribute to add. Required
obj_type The type of the object to add. Can be: "indicator", "event", "adversary", or "attachment". Required
obj_id The ID of the Object. Required

 

Context Output

There are no context output for this command.

 

Command Example
  !threatq-add-attribute obj_type=indicator obj_id=173317 name=TestAttr3 value=Val3
Human Readable Output

add-attribute.png

9. Modify an attribute


Modifies an attribute for an object in ThreatQ.

Base Command

threatq-modify-attribute

Input
Argument Name Description Required
obj_type The type of the object. Can be: "indicator", "event", "adversary", or "attachment". Required
obj_id The ID of the object. Required
attribute_id The ID of the attribute to modify. Required
attribute_value The new value of the attribute. Required

 

Command Example
  !threatq-modify-attribute attribute_id=996895 attribute_value=NewVal obj_id=173317 obj_type=indicator
Human Readable Output

modify-attribute.png

10. Link two objects


Links two objects together in ThreatQ.

Base Command

threatq-link-objects

Input
Argument Name Description Required
obj1_id The ID of the first object. Required
obj2_id The ID of the second object. Required
obj1_type The type of the first object. Can be: "indicator", "adversary", or "event". Required
obj2_type The type of the second object. Can be: "indicator", "adversary", or "event". Required

 

Command Example
  !threatq-link-objects obj1_id=173317 obj1_type=indicator obj2_id=1 obj2_type=adversary
Human Readable Output

link-objects.png

11. Create an adversary


Creates a new adversary in ThreatQ.

Base Command

threatq-create-adversary

Input
Argument Name Description Required
name Name of the adversary to create. Required
sources List of sources names, separated by commas. Optional
attributes_names List of attributes names, separated by commas. The i-th element in the attributes names list corresponds to the i-th element in the attributes values list. Optional
attributes_values List of attributes values, separated by commas. The i-th element in the attributes values list corresponds to the i-th element in the attributes names list. Optional

 

Context Output
Path Type Description
ThreatQ.Adversary.Name string The name of the adversary.
ThreatQ.Adversary.ID number The ID of the adversary.
ThreatQ.Adversary.Source.ID number The source ID of the adversary.
ThreatQ.Adversary.Source.Name string The source name of the adversary.
ThreatQ.Adversary.Attribute.ID number The ID of the adversary's attribute.
ThreatQ.Adversary.Attribute.Name string The name of the adversary's attribute.
ThreatQ.Adversary.Attribute.Value string The value of the adversary's attribute.
ThreatQ.Adversary.UpdatedAt date The creation date of the adversary.
ThreatQ.Adversary.CreatedAt date The last update date of the adversary.

 

Command Example
  !threatq-create-adversary name="Ruth Testman"
Human Readable Output

create-adversary.png

12. Create an event


Creates a new event in ThreatQ.

Base Command

threatq-create-event

Input
Argument Name Description Required
title Title of the event. Required
type The type of the event, such as malware, watchlist, command and control, and so on. Required
date Date that event happened. Can be: YYYY-mm-dd HH:MM:SS, YYYY-mm-dd Required
sources List of sources names, separated by commas. Optional
attributes_names List of attributes names, separated by commas. The i-th element in the attributes names list corresponds to the i-th element in the attributes values list. Optional
attributes_values List of attributes values, separated by commas. The i-th element in the attributes values list corresponds to the i-th element in the attributes names list. Optional

 

Context Output
Path Type Description
ThreatQ.Event.ID number The ID of the event.
ThreatQ.Event.Source.ID number The source ID of the event.
ThreatQ.Event.Source.Name string The source name of the event.
ThreatQ.Event.Attribute.ID number The ID of the event attribute.
ThreatQ.Event.Attribute.Name string The name of the event attribute.
ThreatQ.Event.Attribute.Value string The attribute value of the event.
ThreatQ.Event.UpdatedAt date The last update date of the event.
ThreatQ.Event.CreatedAt date The creation date of the event.
ThreatQ.Event.Type string The type of the event.
ThreatQ.Event.Description string The description of the event.
ThreatQ.Event.Title string The title of the event.
ThreatQ.Event.Occurred date The date of the event that happened.

 

Command Example
  !threatq-create-event date="2019-09-30 20:00:00" title="Offra Alta" type=Incident
Human Readable Output

create-event.png

13. Get related indicators


Retrieves related indicators for an object in ThreatQ.

Base Command

threatq-get-related-indicators

Input
Argument Name Description Required
obj_id The ID of the object. Required
obj_type The type of the object. Can be: "indicator", "event", or "adversary". Required

 

Context Output
Path Type Description
ThreatQ.Indicator.RelatedIndicator.ID number The ID of the related indicator.
ThreatQ.Indicator.RelatedIndicator.Source.ID number The source ID of the related indicator.
ThreatQ.Indicator.RelatedIndicator.Source.Name string The source name of the related indicator.
ThreatQ.Indicator.RelatedIndicator.Attribute.ID number The attribute ID of the related indicator.
ThreatQ.Indicator.RelatedIndicator.Attribute.Name string The attribute name of the related indicator.
ThreatQ.Indicator.RelatedIndicator.Attribute.Value string The attribute value of the related indicator.
ThreatQ.Indicator.RelatedIndicator.UpdatedAt date The last update date of the related indicator.
ThreatQ.Indicator.RelatedIndicator.CreatedAt date The creation date of the related indicator.
ThreatQ.Indicator.RelatedIndicator.Type string The type of the related indicator.
ThreatQ.Indicator.RelatedIndicator.Description string The description of the related indicator.
ThreatQ.Indicator.RelatedIndicator.Value string The value of the related indicator.
ThreatQ.Indicator.RelatedIndicator.Status string The status of the related indicator.
ThreatQ.Indicator.RelatedIndicator.TQScore number The ThreatQ score of the related indicator.
ThreatQ.Indicator.ID number The ID of the indicator.
ThreatQ.Event.RelatedIndicator.ID number The ID of the related indicator.
ThreatQ.Event.RelatedIndicator.Source.ID number The source ID of the related indicator.
ThreatQ.Event.RelatedIndicator.Source.Name string The source name of the related indicator.
ThreatQ.Event.RelatedIndicator.Attribute.ID number The attribute ID of the related indicator.
ThreatQ.Event.RelatedIndicator.Attribute.Name string The attribute name of the related indicator.
ThreatQ.Event.RelatedIndicator.Attribute.Value string The attribute value of the related indicator.
ThreatQ.Event.RelatedIndicator.UpdatedAt date The last update date of the related indicator.
ThreatQ.Event.RelatedIndicator.CreatedAt date The creation date of the related indicator.
ThreatQ.Event.RelatedIndicator.Type string The type of the related indicator.
ThreatQ.Event.RelatedIndicator.Description string The description of the related indicator.
ThreatQ.Event.RelatedIndicator.Value string The value of the related indicator.
ThreatQ.Event.RelatedIndicator.Status string The status of the related indicator.
ThreatQ.Event.RelatedIndicator.TQScore number The ThreatQ score of the related indicator.
ThreatQ.Event.ID number ID of the Event.
ThreatQ.Adversary.RelatedIndicator.ID number ID of the related indicator.
ThreatQ.Adversary.RelatedIndicator.Source.ID number Source ID of the related indicator.
ThreatQ.Adversary.RelatedIndicator.Source.Name string Source name of the related indicator.
ThreatQ.Adversary.RelatedIndicator.Attribute.ID number ID attribute of the related indicator.
ThreatQ.Adversary.RelatedIndicator.Attribute.Name string Attribute name of the related indicator.
ThreatQ.Adversary.RelatedIndicator.Attribute.Value string Attribute value of the related indicator.
ThreatQ.Adversary.RelatedIndicator.UpdatedAt date The last update date of the related indicator.
ThreatQ.Adversary.RelatedIndicator.CreatedAt date The creation date of the related indicator.
ThreatQ.Adversary.RelatedIndicator.Type string The type of the related indicator.
ThreatQ.Adversary.RelatedIndicator.Description string Description of the related indicator.
ThreatQ.Adversary.RelatedIndicator.Value string The value of the related indicator.
ThreatQ.Adversary.RelatedIndicator.Status string The status of the related indicator.
ThreatQ.Adversary.RelatedIndicator.TQScore number The ThreatQ score of the related indicator.
ThreatQ.Adversary.ID number ID of the Adversary.

 

Command Example
  !threatq-get-related-indicators obj_id=1 obj_type=adversary
Human Readable Output

get-related-indicators.png

14. Update an indicator status


Updates an indicator status in ThreatQ.

Base Command

threatq-update-status

Input
Argument Name Description Required
id The ID of the indicator. Required
status The new status of the indicator. Can be: "Active", "Expired", "Indirect", "Review", or "Whitelisted". Required

 

Context Output
Path Type Description
ThreatQ.Indicator.ID Number ID of the indicator.
ThreatQ.Indicator.Status String Status of the indicator.

 

Command Example
  !threatq-update-status id=173317 status=Whitelisted
Human Readable Output

update-status.png

15. Get related events


Retrieves related events of an object in ThreatQ.

Base Command

threatq-get-related-events

Input
Argument Name Description Required
obj_id ID of the object. Required
obj_type The type of the object. Can be: "indicator", "event", or "adversary". Required

 

Context Output
Path Type Description
ThreatQ.Indicator.RelatedEvent.ID number ID of the related event.
ThreatQ.Indicator.RelatedEvent.Source.ID number Source ID of the related event.
ThreatQ.Indicator.RelatedEvent.Source.Name string Source name of the related event.
ThreatQ.Indicator.RelatedEvent.Attribute.ID number The attribute ID of the related event.
ThreatQ.Indicator.RelatedEvent.Attribute.Name string The attribute name of the related event.
ThreatQ.Indicator.RelatedEvent.Attribute.Value string The attribute value of the related event.
ThreatQ.Indicator.RelatedEvent.UpdatedAt date The last update date of the related event.
ThreatQ.Indicator.RelatedEvent.CreatedAt date The creation date of the related event.
ThreatQ.Indicator.RelatedEvent.Description string Description of the related event.
ThreatQ.Indicator.RelatedEvent.Title string The title of the related event.
ThreatQ.Indicator.RelatedEvent.Occurred date The date of occurrence of the related event.
ThreatQ.Indicator.RelatedEvent.Type string The type of the related event.
ThreatQ.Indicator.ID number The ID of the Indicator.
ThreatQ.Event.RelatedEvent.ID number The ID of the related event.
ThreatQ.Event.RelatedEvent.Source.ID number The source ID of the related event.
ThreatQ.Event.RelatedEvent.Source.Name string The source name of the related event.
ThreatQ.Event.RelatedEvent.Attribute.ID number The attribute ID of the related event.
ThreatQ.Event.RelatedEvent.Attribute.Name string The attribute name of the related event.
ThreatQ.Event.RelatedEvent.Attribute.Value string The attribute value of the related event.
ThreatQ.Event.RelatedEvent.UpdatedAt date The last update date of the related event.
ThreatQ.Event.RelatedEvent.CreatedAt date The creation date of the related event.
ThreatQ.Event.RelatedEvent.Description string The description of the related event.
ThreatQ.Event.RelatedEvent.Title string The title of the related event.
ThreatQ.Event.RelatedEvent.Occurred date The date of occurrence of the related event.
ThreatQ.Event.RelatedEvent.Type string The type of the related event.
ThreatQ.Event.ID number The ID of the Event.
ThreatQ.Adversary.RelatedEvent.ID number The ID of the related event.
ThreatQ.Adversary.RelatedEvent.Source.ID number The source ID of the related event.
ThreatQ.Adversary.RelatedEvent.Source.Name string The source name of the related event.
ThreatQ.Adversary.RelatedEvent.Attribute.ID number The attribute ID of the of the related event.
ThreatQ.Adversary.RelatedEvent.Attribute.Name string The attribute name of the related event.
ThreatQ.Adversary.RelatedEvent.Attribute.Value string The attribute value of the related event.
ThreatQ.Adversary.RelatedEvent.UpdatedAt date The last update date of the related event.
ThreatQ.Adversary.RelatedEvent.CreatedAt date The creation date of the related event.
ThreatQ.Adversary.RelatedEvent.Description string The description of the related event.
ThreatQ.Adversary.RelatedEvent.Title string The title of the related event.
ThreatQ.Adversary.RelatedEvent.Occurred date The date of occurrence of the related event.
ThreatQ.Adversary.RelatedEvent.Type string The type of the related event.
ThreatQ.Adversary.ID number ID of the Adversary.

 

Command Example
  !threatq-get-related-events obj_id=1 obj_type=adversary
Human Readable Output

get-related-events.png

16. Get related adversaries


Retrieve related adversaries from an object in ThreatQ.

Base Command

threatq-get-related-adversaries

Input
Argument Name Description Required
obj_id ID of the object. Required
obj_type The type of the object. Can be: "indicator", "event", or "adversary". Required

 

Context Output
Path Type Description
ThreatQ.Indicator.RelatedAdversary.ID number ID of the related adversary.
ThreatQ.Indicator.RelatedAdversary.Source.ID number Source ID of the related adversary.
ThreatQ.Indicator.RelatedAdversary.Source.Name string The Source name of the related adversary.
ThreatQ.Indicator.RelatedAdversary.Attribute.ID number The attribute ID of the related adversary.
ThreatQ.Indicator.RelatedAdversary.Attribute.Name string The attribute name of the related adversary.
ThreatQ.Indicator.RelatedAdversary.Attribute.Value string The attribute value of the related adversary.
ThreatQ.Indicator.RelatedAdversary.UpdatedAt date The last update date of the related adversary.
ThreatQ.Indicator.RelatedAdversary.CreatedAt date The creation date of the related adversary.
ThreatQ.Indicator.RelatedAdversary.Name string The name of the related adversary.
ThreatQ.Indicator.ID number The ID of the Indicator.
ThreatQ.Event.RelatedAdversary.ID number The ID of the related adversary.
ThreatQ.Event.RelatedAdversary.Source.ID number The source ID of the related adversary.
ThreatQ.Event.RelatedAdversary.Source.Name string The source name of the related adversary.
ThreatQ.Event.RelatedAdversary.Attribute.ID number The attribute ID of the related adversary.
ThreatQ.Event.RelatedAdversary.Attribute.Name string The Attribute name of the related adversary.
ThreatQ.Event.RelatedAdversary.Attribute.Value string The attribute value of the related adversary.
ThreatQ.Event.RelatedAdversary.UpdatedAt date The last update date of the related adversary.
ThreatQ.Event.RelatedAdversary.CreatedAt date The creation date of the related adversary.
ThreatQ.Event.RelatedAdversary.Name string The name of the related adversary.
ThreatQ.Event.ID number The ID of the Event.
ThreatQ.Adversary.RelatedAdversary.ID number The ID of the Related adversary.
ThreatQ.Adversary.RelatedAdversary.Source.ID number The source ID of the related adversary.
ThreatQ.Adversary.RelatedAdversary.Source.Name string The source name of the related adversary.
ThreatQ.Adversary.RelatedAdversary.Attribute.ID number The attribute ID of the related adversary.
ThreatQ.Adversary.RelatedAdversary.Attribute.Name string The attribute name of the related adversary.
ThreatQ.Adversary.RelatedAdversary.Attribute.Value string The attribute value of the related adversary.
ThreatQ.Adversary.RelatedAdversary.UpdatedAt date The last update date of the related adversary.
ThreatQ.Adversary.RelatedAdversary.CreatedAt date The creation date of the related adversary.
ThreatQ.Adversary.RelatedAdversary.Name string The name of the related adversary.
ThreatQ.Adversary.ID number The ID of the Adversary.

 

Command Example
  !threatq-get-related-adversaries obj_id=1 obj_type=adversary
Human Readable Output

get-related-adversaries.png

17. Upload a-file


Uploads a file to ThreatQ.

Base Command

threatq-upload-file

Input
Argument Name Description Required
entry_id The file entry ID in Cortex XSOAR. Required
file_category Category of the file, such as CrowdStrike Intelligence, FireEye Analysis, PDF, and so on. Required
malware_safety_lock Zips malware files for safer downloading. Can be: "on", or "off". Default is off. Optional
title Title of the File. Default is the file name. Optional

 

Context Output
Path Type Description
ThreatQ.File.CreatedAt Date Date of the file upload.
ThreatQ.File.Size Number Size (in bytes) of the file.
ThreatQ.File.MD5 String The MD5 of the file.
ThreatQ.File.ID Number The File ID in ThreatQ.
ThreatQ.File.Name String The name of the File.
ThreatQ.File.Title String The title of the file.
ThreatQ.File.UpdatedAt Date The last update of the file.
ThreatQ.File.MalwareLocked Number Whether malware files are zipped.
ThreatQ.File.ContentType String The content type of the file.
ThreatQ.File.Category String The type of the file.
ThreatQ.File.Source.ID Number The source of the file.
ThreatQ.File.Source.Name String The source name of the file.
ThreatQ.File.Attribute.ID Number The attribute ID of the file.
ThreatQ.File.Attribute.Name String The attribute name of the file.
ThreatQ.File.Attribute.Value String The attribute value of the file.

 

Command Example
  !threatq-upload-file entry_id=5379@9da8d636-cf30-42c2-8263-d09f5268be8a file_category="Generic Text" title="File Title"
Human Readable Output

upload-file.png

18. Search by Object type and ID


Searches for an object by object type and ID. 

Base Command

threatq-search-by-id

Input
Argument Name Description Required
obj_type The type of the object. Can be: "indicator", "event", "attachment" or "adversary". Required
obj_id The ID of the Object. Required

 

Context Output
Path Type Description
ThreatQ.Indicator.ID number ID of the indicator.
ThreatQ.Indicator.Source.ID number Source ID of the indicator.
ThreatQ.Indicator.Source.Name string Source name of the indicator.
ThreatQ.Indicator.Attribute.ID number Attribute ID of the indicator.
ThreatQ.Indicator.Attribute.Name string Attribute name of the indicator.
ThreatQ.Indicator.Attribute.Value string Attribute value of the indicator.
ThreatQ.Indicator.CreatedAt date Creation date of the indicator.
ThreatQ.Indicator.UpdatedAt date Last update date of the indicator.
ThreatQ.Indicator.Description string Description of the indicator.
ThreatQ.Indicator.Value string The value of the indicator.
ThreatQ.Indicator.Status string The status of indicator.
ThreatQ.Indicator.Type string The type of the indicator. For example, IP Address.
ThreatQ.Indicator.TQScore number The ThreatQ Score of the indicator.
ThreatQ.Event.ID number The ID of the indicator.
ThreatQ.Event.Source.ID number The source ID of the indicator.
ThreatQ.Event.Source.Name string The source name of the indicator.
ThreatQ.Event.Attribute.ID number The attribute ID of the indicator.
ThreatQ.Event.Attribute.Name string The attribute name of the indicator.
ThreatQ.Event.Attribute.Value string The attribute value of the indicator.
ThreatQ.Event.UpdatedAt date The last update date of the event.
ThreatQ.Event.CreatedAt date The creation date of the event.
ThreatQ.Event.Type string The type of the event.
ThreatQ.Event.Description string Description of the event.
ThreatQ.Event.Title string The title of the event.
ThreatQ.Event.Occurred date The date that the event happened.
ThreatQ.Adversary.Name string The name of the adversary.
ThreatQ.Adversary.ID number The ID of the adversary.
ThreatQ.Adversary.Source.ID number The source of the adversary.
ThreatQ.Adversary.Source.Name string The source name of the adversary.
ThreatQ.Adversary.Attribute.ID number The attribute ID of the adversary.
ThreatQ.Adversary.Attribute.Name string The attribute name of the adversary.
ThreatQ.Adversary.Attribute.Value string The attribute value of the adversary.
ThreatQ.Adversary.UpdatedAt date The creation date of the adversary.
ThreatQ.Adversary.CreatedAt date The last update date of the adversary.
ThreatQ.File.CreatedAt Date Date of the file upload.
ThreatQ.File.Size Number Size (in bytes) of the file.
ThreatQ.File.MD5 String The MD5 hash of the file.
ThreatQ.File.ID Number The File ID in ThreatQ.
ThreatQ.File.Name String The name of the File.
ThreatQ.File.Title String The title of the file.
ThreatQ.File.UpdatedAt Date The last update of the file.
ThreatQ.File.MalwareLocked Number Whether malware files are zipped.
ThreatQ.File.ContentType String The content type of the file.
ThreatQ.File.Category String The type of the file.
ThreatQ.File.Source.ID Number The source of the file.
ThreatQ.File.Source.Name String The source name of the file.
ThreatQ.File.Attribute.ID Number The attribute ID of the file.
ThreatQ.File.Attribute.Name String The attribute name of the file.
ThreatQ.File.Attribute.Value String The attribute value of the file.

 

Command Example
  !threatq-search-by-id obj_id=173317 obj_type=indicator
Human Readable Output

search-by-id.png

19. Unlink two objects


Unlinks two objects in ThreatQ.

Base Command

threatq-unlink-objects

Input
Argument Name Description Required
obj1_id The ID of the first object. Required
obj1_type The type of the first object. Can be: "adversary", "indicator", or "event". Required
obj2_id The ID of the second object. Required
obj2_type The type of the second object. Can be: "adversary", "indicator", or "event". Required

 

Command Example
  !threatq-unlink-objects obj1_id=173317 obj1_type=indicator obj2_id=1 obj2_type=adversary
Human Readable Output

unlink-objects.png

20. Delete an object


Deletes an object in ThreatQ.

Base Command

threatq-delete-object

Input
Argument Name Description Required
obj_id ID of the Object. Required
obj_type The type of the object. Can be: "indicator", "event", "adversary" or "attachment". Required

 

Command Example
  !threatq-delete-object obj_id=104 obj_type=event
Human Readable Output

delete-object.png

21. Add a source to an object


Adds a source to an object in ThreatQ.

Base Command

threatq-add-source

Input
Argument Name Description Required
obj_id ID of an Object. Required
obj_type The type of the object. Can be: "indicator", "event", "adversary", or "attachment". Required
source The source name. Required

 

Command Example
  !threatq-add-source obj_id=173317 obj_type=indicator source="AlienVault OTX"
Human Readable Output

add-source.png

22. Delete a source from an object


Deletes a source from an object in ThreatQ.

Base Command

threatq-delete-source

Input
Argument Name Description Required
source_id ID of the source. Required
obj_id ID of the object. Required
obj_type The type of the object. Can be: "indicator", "event", "adversary", or "attachment". Required

 

Command Example
  !threatq-delete-source obj_id=173317 obj_type=indicator source_id=3333819
Human Readable Output

delete-source.png

23. Delete an attribute


Deletes an attribute from an object in ThreatQ.

Base Command

threatq-delete-attribute

Input
Argument Name Description Required
attribute_id ID of the attribute. Required
obj_id ID of the object. Required
obj_type The type of the object. Can be: "indicator", "event", "adversary", or "attachment". Required

 

Command Example
  !threatq-delete-attribute attribute_id=996896 obj_id=173317 obj_type=indicator
Human Readable Output

delete-attribute.png

24. Edit an adversary


Updates an adversary name in ThreatQ.

Base Command

threatq-edit-adversary

Input
Argument Name Description Required
id ID of the Adversary to update. Required
name Name of the new adversary. Required

 

Context Output
Path Type Description
ThreatQ.Adversary.Name string The name of the adversary.
ThreatQ.Adversary.ID number The ID of the adversary.
ThreatQ.Adversary.Source.ID number The source ID of the adversary.
ThreatQ.Adversary.Source.Name string The source name of the adversary.
ThreatQ.Adversary.Attribute.ID number The attribute ID of the adversary.
ThreatQ.Adversary.Attribute.Name string The attribute name of the adversary.
ThreatQ.Adversary.Attribute.Value string The value of the adversary.
ThreatQ.Adversary.UpdatedAt date The creation date of the adversary.
ThreatQ.Adversary.CreatedAt date The last update date of the adversary.

 

Command Example
  !threatq-edit-adversary id=23 name="New Adversary Name"
Human Readable Output

edit-adversary.png

25. Edit an indicator


Updates an indicator in ThreatQ.

Base Command

threatq-edit-indicator

Input
Argument Name Description Required
id The ID of the indicator. Required
value The value of the new indicator. Optional
type The type of the new indicator, such as email address, Filename, Binary string and so on. Optional
description The description of the indicator. Optional

 

Context Output
Path Type Description
ThreatQ.Indicator.ID number The ID of the indicator.
ThreatQ.Indicator.Source.ID number The source ID of the indicator.
ThreatQ.Indicator.Source.Name string The source name of the indicator.
ThreatQ.Indicator.Attribute.ID number The attribute ID of the indicator.
ThreatQ.Indicator.Attribute.Name string The attribute name of the indicator.
ThreatQ.Indicator.Attribute.Value string The attribute value of the indicator.
ThreatQ.Indicator.CreatedAt date The creation date of the indicator.
ThreatQ.Indicator.UpdatedAt date The last update date of the indicator.
ThreatQ.Indicator.Description string The description of the indicator.
ThreatQ.Indicator.Value string The value of the indicator.
ThreatQ.Indicator.Status string The status of the indicator.
ThreatQ.Indicator.Type string The type of the indicator. For example, IP Address.
ThreatQ.Indicator.TQScore number The ThreatQ Score of the indicator.

 

Command Example
  !threatq-edit-indicator id=173317 description="This is a new description" type="Email Address" value=goo@test.com
Human Readable Output

edit-indicator.png

26. Edit an event


Updates an event in ThreatQ.

Base Command

threatq-edit-event

Input
Argument Name Description Required
id The ID of the Event. Required
title The title of the new event. Optional
date Date that event happened. Can be: YYYY-mm-dd HH:MM:SS, YYYY-mm-dd Optional
type Type of the event, such as DoS Attack, Malware, Watchlist, and so on. Optional
description Description of the event. Optional

 

Context Output
Path Type Description
ThreatQ.Event.ID number The ID of the event.
ThreatQ.Event.Source.ID number The source ID of the event.
ThreatQ.Event.Source.Name string The source name of the event.
ThreatQ.Event.Attribute.ID number The attribute ID of the event.
ThreatQ.Event.Attribute.Name string The attribute name of the event.
ThreatQ.Event.Attribute.Value string The attribute value of the event.
ThreatQ.Event.UpdatedAt date The last update date of the event.
ThreatQ.Event.CreatedAt date The creation date of the event.
ThreatQ.Event.Type string The type of the event.
ThreatQ.Event.Description string The description of the event.
ThreatQ.Event.Title string The title of the event.
ThreatQ.Event.Occurred date The date that the event happened.

 

Command Example
  !threatq-edit-event id=1 date="2019-09-30 21:00:00" description="The event will take place in Expo Tel Aviv" type="Command and Control"
Human Readable Output

edit-event.png

27. Update a score of an indicator


Modifies an indicator's score in ThreatQ. The final indicator score is the highest of the manual and generated scores.

Base Command

threatq-update-score

Input
Argument Name Description Required
id The ID of the indicator. Required
score The manual indicator score. Can be: "Generated Score" or "1", "2", "3", "4", "5", "6", "7", "8", "9" or "10". Required

 

Context Output
Path Type Description
ThreatQ.Indicator.ID number The ID of the indicator.
ThreatQ.Indicator.Source.ID number The source ID of the indicator.
ThreatQ.Indicator.Source.Name string The source name of the indicator.
ThreatQ.Indicator.Attribute.ID number The attribute ID of the indicator.
ThreatQ.Indicator.Attribute.Name string The attribute name of the indicator.
ThreatQ.Indicator.Attribute.Value string The attribute value of the indicator.
ThreatQ.Indicator.CreatedAt date The creation date of the indicator.
ThreatQ.Indicator.UpdatedAt date The last update date of the indicator.
ThreatQ.Indicator.Description string The description of the indicator.
ThreatQ.Indicator.Value string The value of the indicator.
ThreatQ.Indicator.Status string The status of the Indicator.
ThreatQ.Indicator.Type string The type of the indicator. For example, IP Address.
ThreatQ.Indicator.TQScore number The ThreatQ Score of the indicator.

 

Command Example
  !threatq-update-score id=173317 score=2
Human Readable Output

update-score.png

28. Download a file to Cortex XSOAR


Downloads a file from ThreatQ to Cortex XSOAR.

Base Command

threatq-download-file

Input
Argument Name Description Required
id The ID of the file. Required

 

Command Example
  !threatq-download-file id=88
Human Readable Output

download-file.png

29. Get all indicators


Retrieves all indicators in ThreatQ.

Base Command

threatq-get-all-indicators

Input
Argument Name Description Required
page The result page number to return. Default is 0. Optional
limit The maximum number of indicators return. Default is 50. Optional

 

Context Output
Path Type Description
ThreatQ.Indicator.ID number ID of the indicator.
ThreatQ.Indicator.Source.ID number Source ID of the indicator.
ThreatQ.Indicator.Source.Name string Source name of the indicator.
ThreatQ.Indicator.Attribute.ID number Attribute ID of the of the indicator.
ThreatQ.Indicator.Attribute.Name string Attribute name of the indicator.
ThreatQ.Indicator.Attribute.Value string Attribute value of the indicator.
ThreatQ.Indicator.CreatedAt date The creation date of the indicator.
ThreatQ.Indicator.UpdatedAt date The last update date of the indicator.
ThreatQ.Indicator.Description string The description of the indicator.
ThreatQ.Indicator.Value string The value of the indicator.
ThreatQ.Indicator.Status string The status of the indicator.
ThreatQ.Indicator.Type string The type of the indicator. For example, IP Address.
ThreatQ.Indicator.TQScore number The ThreatQ Score of the indicator.

 

Command Example
  
  !threatq-get-all-indicators limit=30 page=10
Human Readable Output

get-all-indicators.png

30. Get a list of events


Retrieves all events in ThreatQ.

Base Command

threatq-get-all-events

Input
Argument Name Description Required
page The result page number to return. Default is 0. Optional
limit The maximum number of events to return. Default is 50. Optional

 

Context Output
Path Type Description
ThreatQ.Event.ID number The ID of the event.
ThreatQ.Event.Source.ID number The source ID of the event.
ThreatQ.Event.Source.Name string The source name of the event.
ThreatQ.Event.Attribute.ID number The attribute ID of the event.
ThreatQ.Event.Attribute.Name string The attribute name of the event.
ThreatQ.Event.Attribute.Value string The attribute value of the event.
ThreatQ.Event.UpdatedAt date The last update date of the event.
ThreatQ.Event.CreatedAt date The creation date of the event.
ThreatQ.Event.Type string The type of the event.
ThreatQ.Event.Description string The description of the event.
ThreatQ.Event.Title string The title of the event.
ThreatQ.Event.Occurred date The date the event happened.

 

Command Example
  !threatq-get-all-events limit=30 page=10
Human Readable Output

get-all-events.png

31. Get a list of all adversaries


Returns all adversaries in ThreatQ.

Base Command

threatq-get-all-adversaries

Input
Argument Name Description Required
page The result page number to return. Default is 0. Optional
limit The maximum number of objects to return in one response (maximum is 200). Optional

 

Context Output
Path Type Description
ThreatQ.Adversary.Name string The name of the adversary.
ThreatQ.Adversary.ID number The ID of the of the adversary.
ThreatQ.Adversary.Source.ID number The source ID of the adversary.
ThreatQ.Adversary.Source.Name string The source name of the adversary.
ThreatQ.Adversary.Attribute.ID number The attribute ID of the adversary.
ThreatQ.Adversary.Attribute.Name string The attribute name of the adversary.
ThreatQ.Adversary.Attribute.Value string The attribute value of the adversary.
ThreatQ.Adversary.UpdatedAt date The creation date of the adversary.
ThreatQ.Adversary.CreatedAt date The last update date of the adversary.

 

Command Example
  !threatq-get-all-events limit=30 page=10
Human Readable Output

get-all-adversaries.png

Configuration parameters

  • serverUrl — ThreatQ server URL (e.g. https://192.168.1.136) (required)
  • client_id — ThreatQ client ID (required)
  • credentials — Email (required)
  • threshold — Indicator threshold (minimum TQ score to consider the indicator malicious).
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • integrationReliability — Source Reliability
  • feedExpirationPolicy
  • feedExpirationInterval

Commands (32)

  • domain

    Checks the reputation of a domain in ThreatQ.

  • email

    Checks the reputation of an email in ThreatQ.

  • file

    Checks the reputation of a file in ThreatQ.

  • ip

    Checks the reputation of an IP address in ThreatQ.

  • threatq-add-attribute

    Adds an attribute to an object in ThreatQ.

  • threatq-add-source

    Adds a source to an object in ThreatQ.

  • threatq-advanced-search Deprecated

    Runs an advanced indicator search.

  • threatq-create-adversary

    Creates a new adversary in ThreatQ.

  • threatq-create-event

    Creates a new event in ThreatQ.

  • threatq-create-indicator

    Creates a new indicator in ThreatQ.

  • threatq-delete-attribute

    Deletes an attribute from an object in ThreatQ.

  • threatq-delete-object

    Deletes an object in ThreatQ.

  • threatq-delete-source

    Deletes a source from an object in ThreatQ.

  • threatq-download-file

    Downloads a file from ThreatQ to Demisto.

  • threatq-edit-adversary

    Updates an adversary name in ThreatQ.

  • threatq-edit-event

    Updates an event in ThreatQ.

  • threatq-edit-indicator

    Updates an indicator in ThreatQ.

  • threatq-get-all-adversaries

    Returns all adversaries in ThreatQ.

  • threatq-get-all-events

    Retrieves all events in ThreatQ.

  • threatq-get-all-indicators

    Retrieves all indicators in ThreatQ.

  • threatq-get-related-adversaries

    Retrieve related adversaries from an object in ThreatQ.

  • threatq-get-related-events

    Retrieves related events of an object in ThreatQ.

  • threatq-get-related-indicators

    Retrieves related indicators for an object in ThreatQ.

  • threatq-link-objects

    Links two objects together in ThreatQ.

  • threatq-modify-attribute

    Modifies an attribute for an object in ThreatQ.

  • threatq-search-by-id

    Searches for an object by object type and ID. Generic and DBotScore contexts also may be generated.

  • threatq-search-by-name

    Searches for objects by name in the ThreatQ repository.

  • threatq-unlink-objects

    Unlinks two objects in ThreatQ.

  • threatq-update-score

    Modifies an indicator's score in ThreatQ. The final indicator score is the highest of the manual and generated scores.

  • threatq-update-status

    Updates an indicator status in ThreatQ.

  • threatq-upload-file

    Uploads a file in ThreatQ.

  • url

    Checks the reputation of a URL in ThreatQ.

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

""" IMPORTS """
import json
import shutil

import requests
import urllib3

# disable insecure warnings
urllib3.disable_warnings()

""" GLOBAL VARIABLES """
SERVER_URL = demisto.params()["serverUrl"].rstrip("/")
API_URL = SERVER_URL + "/api"
CLIENT_ID = demisto.params()["client_id"]
EMAIL = demisto.getParam("credentials").get("identifier")
PASSWORD = demisto.getParam("credentials").get("password")
USE_SSL = not demisto.params().get("insecure", False)
THRESHOLD = int(demisto.params().get("threshold", "0"))
if THRESHOLD:
    THRESHOLD = int(THRESHOLD)

url_regex = (
    r"(?:(?:https?|ftp|hxxps?):\/\/|www\[?\.\]?|ftp\[?\.\]?)?(?:[-\w\d]+\[?\.\]?)+[-\w\d]+(?::\d+)?(?:(?:\/|\?)"
    r"[-\w\d+&@#\/%=~_$?!\-:,.\(\);]*[\w\d+&@#\/%=~_$\(\);])?"
)

REGEX_MAP = {
    "email": re.compile(emailRegex, regexFlags),
    "url": re.compile(url_regex, regexFlags),
    "md5": re.compile(r"\b[0-9a-fA-F]{32}\b", regexFlags),
    "sha1": re.compile(r"\b[0-9a-fA-F]{40}\b", regexFlags),
    "sha256": re.compile(r"\b[0-9a-fA-F]{64}\b", regexFlags),
}

TQ_TO_DEMISTO_INDICATOR_TYPES = {
    "IP Address": "ip",
    "IPv6 Address": "ip",
    "Email Address": "email",
    "URL": "url",
    "MD5": "file",
    "SHA-1": "file",
    "SHA-256": "file",
    "FQDN": "domain",
}

INDICATOR_TYPES = {
    "File Path": "file",
    "File": "file",
    "MD5": "file",
    "SHA-1": "file",
    "SHA-256": "file",
    "SHA-384": "file",
    "SHA-512": "file",
    "IP Address": "ip",
    "IPv6 Address": "ip",
    "URL": "url",
    "URL Path": "url",
    "FQDN": "domain",
    "Email Address": "email",
}

TABLE_HEADERS = {
    "indicator": ["ID", "Type", "Value", "Description", "Status", "TQScore", "CreatedAt", "UpdatedAt", "URL"],
    "adversary": ["ID", "Name", "CreatedAt", "UpdatedAt", "URL"],
    "event": ["ID", "Type", "Title", "Description", "Occurred", "CreatedAt", "UpdatedAt", "URL"],
    "attachment": [
        "ID",
        "Name",
        "Title",
        "Type",
        "Size",
        "Description",
        "MD5",
        "CreatedAt",
        "UpdatedAt",
        "MalwareLocked",
        "ContentType",
        "URL",
    ],
    "attributes": ["ID", "Name", "Value"],
    "sources": ["ID", "Name", "TLP"],
}

OBJ_DIRECTORY = {"indicator": "indicators", "adversary": "adversaries", "event": "events", "attachment": "attachments"}

RELATED_KEY = {"indicator": "RelatedIndicator", "adversary": "RelatedAdversary", "event": "RelatedEvent"}

CONTEXT_PATH = {
    "indicator": "ThreatQ.Indicator((val.ID && val.ID === obj.ID) || (val.Value && val.Value === obj.Value))",
    "adversary": "ThreatQ.Adversary(val.ID === obj.ID)",
    "event": "ThreatQ.Event(val.ID === obj.ID)",
    "attachment": "ThreatQ.File(val.ID === obj.ID)",
}

TABLE_TLP = {4: "WHITE", 3: "GREEN", 2: "AMBER", 1: "RED"}

""" HELPER FUNCTIONS """


def status_id_to_status(status_id):
    res = tq_request("GET", f"/indicator/statuses/{status_id}")
    return res.get("data").get("name")


def type_id_to_indicator_type(type_id):
    res = tq_request("GET", f"/indicator/types/{type_id}")
    return res.get("data").get("name")


def type_id_to_event_type(type_id):
    res = tq_request("GET", f"/event/types/{type_id}")
    return res.get("data").get("name")


def type_id_to_file_type(type_id):
    res = tq_request("GET", f"/attachments/types/{type_id}")
    return res.get("data").get("name")


def get_errors_string_from_bad_request(bad_request_results, status_code):
    if status_code == 404:
        return "Object does not exist.\n"

    # Errors could be retrieved in two forms:
    # 1. A dictionary of fields and errors list related to the fields, all under 'data' key in the response json object
    # 2. A list, directly within the response object

    errors_string = "Errors from service:\n\n"

    # First form
    errors_dict = bad_request_results.json().get("data", {}).get("errors", {})
    if errors_dict:
        for error_num, (key, lst) in enumerate(errors_dict.items(), 1):
            curr_error_string = "\n".join(lst) + "\n\n"
            errors_string += f"Error #{error_num}. In '{key}':\n{curr_error_string}"
        return errors_string

    # Second form
    errors_list = bad_request_results.json().get("errors", [])
    if errors_list:
        for error_num, error in enumerate(errors_list, 1):
            if isinstance(error, str):
                errors_string += f"Error #{error_num}: {error}\n"
            else:  # error is a list
                for i in range(len(error)):
                    errors_string += f"Error #{error_num}.{i}: {error[i]}\n"
        return errors_string

    return ""  # Service did not provide any errors.


def get_tlp_from_indicator(sources):
    if not sources:
        return None
    tlp = 0
    for source in sources:
        try:
            tlp = max(tlp, int(source.get("TLP")))
        except Exception as e:
            demisto.debug(f"Failed getting TLP from {source.get('Name')} source:\n{e!s}")
            continue

    return TABLE_TLP.get(tlp)


def get_generic_context(indicator, generic_context=None):
    tlp = get_tlp_from_indicator(indicator.get("Source"))
    if tlp:
        if generic_context:
            generic_context["TrafficLightProtocol"] = tlp
        else:
            generic_context = {"Data": indicator.get("Value"), "TrafficLightProtocol": tlp}
    else:
        generic_context = generic_context or {"Data": indicator.get("Value")}

    return generic_context


def tq_request(method, url_suffix, params=None, files=None, retrieve_entire_response=False, allow_redirects=True):
    api_call_headers = None
    if url_suffix != "/token":
        access_token = get_access_token()
        api_call_headers = {"Authorization": "Bearer " + access_token}

        if not files:
            params = json.dumps(params)
            api_call_headers.update({"Content-Type": "application/json"})

    demisto.debug(f"[TEST] - Sending request with url endpoint: {url_suffix}")
    response = requests.request(
        method,
        API_URL + url_suffix,
        data=params,
        headers=api_call_headers,
        verify=USE_SSL,
        files=files,
        allow_redirects=allow_redirects,
    )
    demisto.debug(f"Response status code: {response.status_code}")

    if response.status_code >= 400:
        errors_string = get_errors_string_from_bad_request(response, response.status_code)
        error_message = f"Received an error - status code [{response.status_code}].\n{errors_string}"
        return_error(error_message)

    if retrieve_entire_response:
        return response
    elif method != "DELETE":  # the DELETE request returns nothing in response
        return response.json()
    return None


def request_new_access_token():
    params = {"grant_type": "password", "email": EMAIL, "password": PASSWORD, "client_id": CLIENT_ID}
    access_token_response = tq_request("POST", "/token", params, allow_redirects=False)

    updated_integration_context = {
        "access_token": access_token_response["access_token"],
        "access_token_creation_time": int(time.time()) - 1,  # decrementing one second to be on the safe side
        "access_token_expires_in": access_token_response["expires_in"],
    }
    demisto.setIntegrationContext(updated_integration_context)
    threatq_access_token = access_token_response["access_token"]
    return threatq_access_token


def access_token_not_expired():
    epoch_time_now = time.time()
    epoch_time_when_token_granted = demisto.getIntegrationContext().get("access_token_creation_time")
    token_time_until_expiration = demisto.getIntegrationContext().get("access_token_expires_in")
    return int(epoch_time_now) - int(epoch_time_when_token_granted) < int(token_time_until_expiration)


def get_access_token():
    existing_access_token = demisto.getIntegrationContext().get("access_token")
    if existing_access_token and access_token_not_expired():
        return existing_access_token
    else:
        new_access_token = request_new_access_token()
        return new_access_token


def make_create_object_request(obj_type, params):
    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}"
    res = tq_request("POST", url_suffix, params)

    # For some reason, only while creating an indicator, the response data is a list of dicts with size 1.
    # Creating other objects simply returns one dict, as expected.
    data = res["data"][0] if obj_type == "indicator" else res["data"]
    data = data_to_demisto_format(data, obj_type)

    entry_context = {CONTEXT_PATH[obj_type]: createContext(data, removeNull=True)}

    readable_title = f"{obj_type.title()} was successfully created."
    readable = build_readable(readable_title, obj_type, data)

    return_outputs(readable, entry_context, res)


def make_edit_request_for_an_object(obj_id, obj_type, params):
    # Remove items with empty values.
    params = {k: v for k, v in params.items() if v is not None}

    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}/{obj_id}?with=attributes,sources"
    if obj_type == "indicator":
        url_suffix += ",score"

    res = tq_request("PUT", url_suffix, params)

    data = data_to_demisto_format(res["data"], obj_type)
    entry_context = {CONTEXT_PATH[obj_type]: createContext(data, removeNull=True)}

    readable_title = f"Successfully edited {obj_type} with ID {obj_id}"
    readable = build_readable(readable_title, obj_type, data)

    return_outputs(readable, entry_context, res)


def make_indicator_reputation_request(indicator_type, value, generic_context):
    # Search for the indicator ID by keyword:
    body = {}
    if indicator_type == "ip":
        tq_type = "IP Address"
    elif indicator_type == "url":
        tq_type = "URL"

        is_httpx = False
        if value.startswith("http://"):
            value_without_proto = value.replace("http://", "")
            is_httpx = True
        elif value.startswith("https://"):
            value_without_proto = value.replace("https://", "")
            is_httpx = True
        else:
            value_without_proto = value
            demisto.debug("value doesn't start with either prefixes. Initializing value_without_proto to value.")

        if is_httpx:
            body = {"criteria": {"+or": [{"value": value}, {"value": value_without_proto}]}, "filters": {"type_name": tq_type}}
        else:
            body = {"criteria": {"value": value}, "filters": {"type_name": tq_type}}

    elif indicator_type == "domain":
        tq_type = "FQDN"
    elif indicator_type == "email":
        tq_type = "Email Address"

    if indicator_type == "file":
        body = {
            "criteria": {"value": value},
            "filters": {
                "+or": [
                    {"type_name": "MD5"},
                    {"type_name": "SHA-1"},
                    {"type_name": "SHA-256"},
                    {"type_name": "SHA-384"},
                    {"type_name": "SHA-512"},
                ]
            },
        }
    elif tq_type != "URL":
        body = {"criteria": {"value": value}, "filters": {"type_name": tq_type}}

    url_suffix = "/indicators/query?limit=500&offset=0&sort=id"

    res = tq_request(method="POST", url_suffix=url_suffix, params=body)

    indicators: list[dict] = []
    for obj in res.get("data", []):
        if "id" in obj:
            # Search for detailed information about the indicator
            url_suffix = f'/indicators/{obj.get("id")}?with=attributes,sources,score,type'
            res = tq_request("GET", url_suffix)
            indicators.append(indicator_data_to_demisto_format(res["data"]))
    indicators = indicators or [{"Value": value, "TQScore": -1}]

    entry_context = aggregate_search_results(
        indicators=indicators, default_indicator_type=indicator_type, generic_context=generic_context
    )

    readable = build_readable(
        readable_title=f"Search results for {indicator_type} {value}", obj_type="indicator", data=indicators
    )

    return_outputs(readable, entry_context, res)


def create_dbot_context(indicator, ind_type, ind_score, ind_status):
    """This function converts a TQ scoring value of an indicator into a DBot score.
    The default score mapping function is: -1 -> 0, [0,3] -> 1, [4,7] -> 2, [8,10] -> 3.

    If threshold parameter is set manually, it overrides the default function definition for a
    malicious indicator, such that TQ score >= threshold if the DBot score == 3.

    Args:
        indicator (str): The indicator name
        ind_type (str): The indicator type
        ind_score (int): The indicator TQ score
        ind_status (str): The indicator status

    Returns:
        (dict). The indicator's DBotScore.

    """
    dbot_score_map = {-1: 0, 0: 1, 1: 1, 2: 1, 3: 1, 4: 2, 5: 2, 6: 2, 7: 2, 8: 2, 9: 2, 10: 2}

    ret = {
        "Vendor": "ThreatQ v2",
        "Indicator": indicator,
        "Type": ind_type,
        "Reliability": demisto.params().get("integrationReliability"),
    }

    if ind_status and ind_status.lower() == "whitelisted":
        ret["Score"] = 1
    elif ind_score >= THRESHOLD:
        ret["Score"] = 3
    else:
        ret["Score"] = dbot_score_map[ind_score]

    return ret


def get_tq_score_from_response(score_data):
    if score_data is None:
        return None
    if isinstance(score_data, dict):
        # score will be max(gen_score, manual_score)
        gen_score = str(score_data.get("generated_score"))
        manual_score = score_data.get("manual_score", 0.0)

        if manual_score:
            return float(manual_score)
        return float(gen_score)
    else:
        # score is already defined as a number
        return float(score_data)


def clean_html_from_string(raw_html):
    """This function receives an HTML string of a text, and retrieves a clean string of its content.

    Args:
        raw_html: An HTML format text

    Returns:
        (string). A clean text string
    """
    if not raw_html:
        return None
    clean_r = re.compile("<.*?>")
    clean_text = re.sub(clean_r, "", raw_html)
    return clean_text


def sources_to_request_format(sources):
    if not sources:
        return []
    if isinstance(sources, str):
        sources = sources.split(",")
    return [{"name": source} for source in sources]


def sources_to_demisto_format(lst):
    if lst is None:
        return None
    return [
        {
            "Name": elem.get("name"),
            "ID": elem.get("pivot", {}).get("id"),
            "TLP": elem.get("tlp_id"),
        }
        for elem in lst
    ]


def attributes_to_request_format(attributes_names, attributes_values):
    if not attributes_names and not attributes_values:
        return []
    if isinstance(attributes_names, str):
        attributes_names = attributes_names.split(",")
    if isinstance(attributes_values, str):
        attributes_values = attributes_values.split(",")
    if not attributes_names or not attributes_values or len(attributes_names) != len(attributes_values):
        return_error("Attributes_names and attributes_values arguments must have the same length.")

    return [{"name": name, "value": val} for name, val in zip(attributes_names, attributes_values)]


def attributes_to_demisto_format(lst):
    if lst is None:
        return None
    return [{"Name": elem.get("name"), "Value": elem.get("value"), "ID": elem.get("id")} for elem in lst]


def content_type_to_demisto_format(c_type_id):
    # content_type is a file object property
    return "text/plain" if c_type_id == 1 else "text/rtf"


def malware_locked_to_request_format(state):
    # malware_locked is a file object property
    if not state:
        return None
    return 1 if state == "on" else 0


def malware_locked_to_demisto_format(state):
    return "on" if state == 1 else "off"


def parse_date(text):
    valid_formats = ["%Y-%m-%d %H:%M:%S", "%Y-%m-%d"]
    for fmt in valid_formats:
        try:
            return str(datetime.strptime(text, fmt))
        except ValueError:
            pass
    return_error(f"Time data '{text}' does not match any valid format.")
    return None


def data_to_demisto_format(data, obj_type):
    if obj_type == "indicator":
        return indicator_data_to_demisto_format(data)
    elif obj_type == "event":
        return event_data_to_demisto_format(data)
    elif obj_type == "adversary":
        return adversary_data_to_demisto_format(data)
    elif obj_type == "attachment":
        return file_data_to_demisto_format(data)
    return None


def indicator_data_to_demisto_format(data):
    ret = {
        "ID": data.get("id"),
        "UpdatedAt": data.get("updated_at"),
        "CreatedAt": data.get("created_at"),
        "Value": data.get("value"),
        "Status": status_id_to_status(data.get("status_id")),
        "Type": type_id_to_indicator_type(data.get("type_id")),
        "URL": "{}/indicators/{}/details".format(SERVER_URL, data.get("id")),
        "TQScore": get_tq_score_from_response(data.get("score")),
        "Description": clean_html_from_string(data.get("description")),
        "Source": sources_to_demisto_format(data.get("sources")),
        "Attribute": attributes_to_demisto_format(data.get("attributes")),
    }
    return ret


def adversary_data_to_demisto_format(data):
    ret = {
        "ID": data.get("id"),
        "UpdatedAt": data.get("updated_at"),
        "CreatedAt": data.get("created_at"),
        "Name": data.get("name"),
        "URL": "{}/adversaries/{}/details".format(SERVER_URL, data.get("id")),
        "Source": sources_to_demisto_format(data.get("sources")),
        "Attribute": attributes_to_demisto_format(data.get("attributes")),
    }
    return ret


def event_data_to_demisto_format(data):
    ret = {
        "ID": data.get("id"),
        "UpdatedAt": data.get("updated_at"),
        "CreatedAt": data.get("created_at"),
        "Title": data.get("title"),
        "Occurred": data.get("happened_at"),
        "Type": type_id_to_event_type(data.get("type_id")),
        "URL": "{}/events/{}/details".format(SERVER_URL, data.get("id")),
        "Description": clean_html_from_string(data.get("description")),
        "Source": sources_to_demisto_format(data.get("sources")),
        "Attribute": attributes_to_demisto_format(data.get("attributes")),
    }
    return ret


def file_data_to_demisto_format(data):
    raw = {
        "ID": data.get("id"),
        "CreatedAt": data.get("created_at"),
        "UpdatedAt": data.get("updated_at"),
        "Size": data.get("file_size"),
        "MD5": data.get("hash"),
        "Type": type_id_to_file_type(data.get("type_id")),
        "URL": "{}/files/{}/details".format(SERVER_URL, data.get("id")),
        "Name": data.get("name"),
        "Title": data.get("title"),
        "Description": data.get("description"),
        "ContentType": content_type_to_demisto_format(data.get("content_type_id")),
        "MalwareLocked": malware_locked_to_demisto_format(data.get("malware_locked")),
        "Source": sources_to_demisto_format(data.get("sources")),
        "Attribute": attributes_to_demisto_format(data.get("attributes")),
    }

    return raw


def get_pivot_id(obj1_type, obj1_id, obj2_type, obj2_id):
    # A pivot id represents a connection between two objects.

    url_suffix = f"/{OBJ_DIRECTORY[obj1_type]}/{obj1_id}/{OBJ_DIRECTORY[obj2_type]}"
    res = tq_request("GET", url_suffix)

    for related_object in res["data"]:  # res['data'] contains all the related objects of obj_id1
        if int(related_object.get("id")) == int(obj2_id):
            return int(related_object["pivot"]["id"])
    return_error("Command failed - objects are not related.")
    return None


def get_malicious_data(tq_score):
    malicious_data = {"Malicious": {"Vendor": "ThreatQ v2", "Description": f"Score from ThreatQ is {tq_score}"}}
    return malicious_data


def set_indicator_entry_context(indicator_type, indicator, generic_context):
    dbot_context = create_dbot_context(
        indicator.get("Value"), indicator_type, indicator.get("TQScore", -1), indicator.get("Status")
    )

    indicator_type = INDICATOR_TYPES.get(indicator_type) or indicator_type
    generic_context_path = outputPaths.get(indicator_type, "Indicator(val.ID && val.ID == obj.ID)")
    integration_context_path = CONTEXT_PATH["indicator"]

    if dbot_context.get("Score") == 3:
        malicious_data = get_malicious_data(indicator.get("TQScore", -1))
        generic_context.update(malicious_data)

    ec = {generic_context_path: generic_context, "DBotScore": dbot_context}
    if indicator:
        ec[integration_context_path] = indicator

    return ec


def build_readable_for_search_by_name(indicator_context, event_context, adversary_context, file_context):
    if not (indicator_context or event_context or adversary_context or file_context):
        return "No results."

    human_readable = ""
    if indicator_context:
        human_readable += tableToMarkdown("Search Results - Indicators", indicator_context)
    if event_context:
        human_readable += tableToMarkdown("Search Results - Events", event_context)
    if adversary_context:
        human_readable += tableToMarkdown("Search Results - Adversaries", adversary_context)
    if file_context:
        human_readable += tableToMarkdown("Search Results - Files", file_context)

    return human_readable


def build_readable(readable_title, obj_type, data, metadata=None):
    if isinstance(data, dict):  # One object data
        readable = tableToMarkdown(
            name=readable_title,
            t=data,
            headers=TABLE_HEADERS[obj_type],
            headerTransform=pascalToSpace,
            removeNull=True,
            metadata=metadata,
        )
        if "Attribute" in data:
            readable += tableToMarkdown(
                name="Attributes",
                t=data["Attribute"],
                headers=TABLE_HEADERS["attributes"],
                removeNull=True,
                headerTransform=pascalToSpace,
                metadata=metadata,
            )
        if "Source" in data:
            readable += tableToMarkdown(
                name="Sources",
                t=data["Source"],
                headers=TABLE_HEADERS["sources"],
                removeNull=True,
                headerTransform=pascalToSpace,
                metadata=metadata,
            )
        if "URL" in data:
            url_in_markdown_format = "[{}]({})".format(data["URL"], data["URL"])
            readable = readable.replace(data["URL"], url_in_markdown_format)

    else:  # 'data' is a list of objects
        if len(data) == 1:
            return build_readable(readable_title, obj_type, data[0], metadata=None)
        readable = tableToMarkdown(
            name=readable_title,
            t=data,
            headers=TABLE_HEADERS[obj_type],
            headerTransform=pascalToSpace,
            removeNull=True,
            metadata=metadata,
        )
        for elem in data:
            url_in_markdown_format = "[{}]({})".format(elem["URL"], elem["URL"])
            readable = readable.replace(elem["URL"], url_in_markdown_format)

    return readable


""" COMMANDS """


def test_module():
    token = request_new_access_token()
    threshold = demisto.params().get("threshold") or "8"
    threshold_is_integer = isinstance(threshold, int) or (isinstance(threshold, str) and threshold.isdigit())
    if token and threshold_is_integer and 0 <= int(threshold) <= 10:
        demisto.results("ok")


def get_indicator_type_id(indicator_name: str) -> str:
    indicator_types_res = tq_request(method="GET", url_suffix="/indicator/types", retrieve_entire_response=True)
    try:
        indicator_types = indicator_types_res.json().get("data")
    except ValueError:
        raise ValueError(
            f"Could not parse data from ThreatQ [Status code: {indicator_types_res.status_code}]"
            f"\n[Error Message: {indicator_types_res.text}]"
        )

    for indicator in indicator_types:
        if indicator.get("name", "").lower() == indicator_name.lower():
            return indicator.get("id")

    raise ValueError("Could not find indicator")


def aggregate_search_results(indicators, default_indicator_type, generic_context=None):
    entry_context = []
    for i in indicators:
        generic_context = get_generic_context(i, generic_context)
        entry_context.append(
            set_indicator_entry_context(
                indicator_type=i.get("Type") or default_indicator_type, indicator=i, generic_context=generic_context
            )
        )

    aggregated: dict = {}
    for entry in entry_context:
        for key, value in entry.items():
            if key in aggregated:
                aggregated[key].append(value)
            else:
                aggregated[key] = [value]

    return aggregated


def get_search_body(query, indicator_type):
    search_body = {
        "indicators": [
            [
                {
                    "field": "indicator_type",
                    "operator": "is",
                    "value": indicator_type if indicator_type.isdigit() else get_indicator_type_id(indicator_type),
                },
                {"field": "indicator_value", "operator": "like", "value": str(query)},
            ]
        ]
    }
    return search_body


def advance_search_command():
    args = demisto.args()
    limit = args.get("limit", 10)
    query = args.get("query")
    indicator_type = args.get("indicator_type")

    search_body = get_search_body(query, indicator_type)
    if limit and isinstance(limit, str) and not limit.isdigit():
        return_error("limit argument must be an integer.")

    res = tq_request(
        method="POST", url_suffix=f"/search/advanced?limit={limit}", params=search_body, retrieve_entire_response=True
    )
    try:
        search_results = res.json().get("data")
    except ValueError:
        raise ValueError(f"Could not parse data from ThreatQ [Status code: {res.status_code}]\n[Error Message: {res.text}]")

    if not isinstance(search_results, list):
        search_results = [search_results]

    indicators: list[dict] = []
    for obj in search_results:
        # Search for detailed information about the indicator
        url_suffix = f"/indicators/{obj.get('id')}?with=attributes,sources,score,type"
        search_results = res = tq_request("GET", url_suffix)
        indicators.append(indicator_data_to_demisto_format(res.get("data")))

    indicators = indicators or [{"Value": query, "TQScore": -1}]
    entry_context = aggregate_search_results(indicators=indicators, default_indicator_type=indicator_type)

    readable = build_readable(readable_title=f'Search results for "{query}":', obj_type="indicator", data=indicators)

    return_outputs(readable, entry_context, search_results)


def search_by_name_command():
    args = demisto.args()
    name = args.get("name")
    limit = args.get("limit", "10")

    if limit and isinstance(limit, str) and not limit.isdigit():
        return_error("limit argument must be an integer.")

    url_suffix = f"/search?query={name}&limit={limit}"
    res = tq_request("GET", url_suffix)

    indicator_context = [{"ID": e["id"], "Value": e["value"]} for e in res["data"] if e["object"] == "indicator"]
    event_context = [{"ID": e["id"], "Title": e["value"]} for e in res["data"] if e["object"] == "event"]
    adversary_context = [{"ID": e["id"], "Name": e["value"]} for e in res["data"] if e["object"] == "adversary"]
    file_context = [{"ID": e["id"], "Name": e["value"].split()[1]} for e in res["data"] if e["object"] == "attachment"]
    # file value in response is returned in the form ["title" name], thus we use the split method above

    entry_context = {
        CONTEXT_PATH["indicator"]: indicator_context,
        CONTEXT_PATH["event"]: event_context,
        CONTEXT_PATH["adversary"]: adversary_context,
        CONTEXT_PATH["attachment"]: file_context,
    }

    # Remove items with empty values:
    entry_context = {k: v for k, v in entry_context.items() if v}

    readable = build_readable_for_search_by_name(indicator_context, event_context, adversary_context, file_context)

    return_outputs(readable, entry_context, res)


def search_by_id_command():
    args = demisto.args()
    obj_type = args.get("obj_type")
    obj_id = args.get("obj_id")

    if isinstance(obj_id, str) and not obj_id.isdigit():
        return_error("obj_id argument must be an integer.")

    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}/{obj_id}?with=attributes,sources"
    if obj_type == "indicator":
        url_suffix += ",score,type"

    res = tq_request("GET", url_suffix)
    data = data_to_demisto_format(res["data"], obj_type)

    ec = {CONTEXT_PATH[obj_type]: createContext(data, removeNull=True)}

    if obj_type == "indicator":
        indicator_type = TQ_TO_DEMISTO_INDICATOR_TYPES.get(data["Type"])
        if indicator_type is not None:
            ec["DBotScore"] = create_dbot_context(data["Value"], indicator_type, data.get("TQScore", -1), data.get("Status"))

    readable_title = f"Search results for {obj_type} with ID {obj_id}"
    readable = build_readable(readable_title, obj_type, data)

    return_outputs(readable, ec, res)


def create_indicator_command():
    args = demisto.args()
    indicator_type = args.get("type")
    status = args.get("status")
    value = args.get("value")
    sources = args.get("sources")
    attributes_names = args.get("attributes_names")
    attributes_values = args.get("attributes_values")

    params = {
        "type": indicator_type,
        "status": status,
        "value": value,
        "sources": sources_to_request_format(sources),
        "attributes": attributes_to_request_format(attributes_names, attributes_values),
    }

    make_create_object_request("indicator", params)


def create_adversary_command():
    args = demisto.args()
    name = args.get("name")
    sources = args.get("sources")
    attributes_names = args.get("attributes_names")
    attributes_values = args.get("attributes_values")

    params = {
        "name": name,
        "sources": sources_to_request_format(sources),
        "attributes": attributes_to_request_format(attributes_names, attributes_values),
    }

    make_create_object_request("adversary", params)


def create_event_command():
    args = demisto.args()
    event_type = args.get("type")
    title = args.get("title")
    date = args.get("date")
    sources = args.get("sources")
    attributes_names = args.get("attributes_names")
    attributes_values = args.get("attributes_values")

    params = {
        "title": title,
        "type": event_type,
        "happened_at": parse_date(date),
        "sources": sources_to_request_format(sources),
        "attributes": attributes_to_request_format(attributes_names, attributes_values),
    }

    make_create_object_request("event", params)


def edit_indicator_command():
    args = demisto.args()
    indicator_id = args.get("id")
    value = args.get("value")
    indicator_type = args.get("type")
    description = args.get("description")

    if isinstance(indicator_id, str) and not indicator_id.isdigit():
        return_error("id argument must be an integer.")

    params = {"value": value, "type": indicator_type, "description": description}

    make_edit_request_for_an_object(indicator_id, "indicator", params)


def edit_adversary_command():
    args = demisto.args()
    adversary_id = args.get("id")
    name = args.get("name")

    if isinstance(adversary_id, str) and not adversary_id.isdigit():
        return_error("id argument must be an integer.")

    params = {"name": name}

    make_edit_request_for_an_object(adversary_id, "adversary", params)


def edit_event_command():
    args = demisto.args()
    event_id = args.get("id")
    event_type = args.get("type")
    title = args.get("title")
    date = args.get("date")
    description = args.get("description")

    if isinstance(event_id, str) and not event_id.isdigit():
        return_error("id argument must be an integer.")

    params = {"title": title, "happened_at": parse_date(date) if date else None, "type": event_type, "description": description}

    make_edit_request_for_an_object(event_id, "event", params)


def delete_object_command():
    args = demisto.args()
    obj_type = args.get("obj_type")
    obj_id = args.get("obj_id")

    if isinstance(obj_id, str) and not obj_id.isdigit():
        return_error("obj_id argument must be an integer.")

    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}/{obj_id}"
    tq_request("DELETE", url_suffix)
    demisto.results(f"Successfully deleted {obj_type} with ID {obj_id}.")


def get_related_objs_command(related_type):
    args = demisto.args()
    obj_type = args.get("obj_type")
    obj_id = args.get("obj_id")

    if isinstance(obj_id, str) and not obj_id.isdigit():
        return_error("obj_id argument must be an integer.")

    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}/{obj_id}/{OBJ_DIRECTORY[related_type]}?with=sources"
    if related_type == "indicator":
        url_suffix += ",score"
    res = tq_request("GET", url_suffix)

    info = [data_to_demisto_format(obj, related_type) for obj in res["data"]]
    info = createContext(info, removeNull=True)
    data = {RELATED_KEY[related_type]: createContext(info, removeNull=True), "ID": int(obj_id)}
    ec = {CONTEXT_PATH[obj_type]: data} if info else {}

    readable_title = f"Related {related_type} type objects of {obj_type} with ID {obj_id}"
    readable = build_readable(readable_title, related_type, data[RELATED_KEY[related_type]])

    return_outputs(readable, ec, res)


def link_objects_command():
    args = demisto.args()
    obj1_type = args.get("obj1_type")
    obj1_id = args.get("obj1_id")
    obj2_type = args.get("obj2_type")
    obj2_id = args.get("obj2_id")

    if (isinstance(obj1_id, str) and not obj1_id.isdigit()) or (isinstance(obj2_id, str) and not obj2_id.isdigit()):
        return_error("obj1_id, obj2_id arguments must be integers.")

    if obj1_type == obj2_type and obj1_id == obj2_id:
        return_error("Cannot link an object to itself.")

    url_suffix = f"/{OBJ_DIRECTORY[obj1_type]}/{obj1_id}/{OBJ_DIRECTORY[obj2_type]}"
    params = {"id": obj2_id}
    tq_request("POST", url_suffix, params)
    demisto.results(f"Successfully linked {obj1_type} with ID {obj1_id} and {obj2_type} with ID {obj2_id}.")


def unlink_objects_command():
    args = demisto.args()
    obj1_type = args.get("obj1_type")
    obj1_id = args.get("obj1_id")
    obj2_type = args.get("obj2_type")
    obj2_id = args.get("obj2_id")

    if (isinstance(obj1_id, str) and not obj1_id.isdigit()) or (isinstance(obj2_id, str) and not obj2_id.isdigit()):
        return_error("obj1_id, obj2_id arguments must be integers.")

    if obj1_type == obj2_type and obj1_id == obj2_id:
        return_error("An object cannot be linked to itself.")

    p_id = get_pivot_id(obj1_type, obj1_id, obj2_type, obj2_id)
    url_suffix = f"/{OBJ_DIRECTORY[obj1_type]}/{obj1_id}/{OBJ_DIRECTORY[obj2_type]}"
    tq_request("DELETE", url_suffix, params=[p_id])
    demisto.results(f"Successfully unlinked {obj1_type} with ID {obj1_id} and {obj2_type} with ID {obj2_id}.")


def update_score_command():
    # Note: We can't update DBot Score because API doesn't retrieve the indicator value.
    args = demisto.args()
    indicator_id = args.get("id")
    score = args.get("score")

    if isinstance(indicator_id, str) and not indicator_id.isdigit():
        return_error("id argument must be an integer.")

    if isinstance(score, str) and not score.isdigit():  # User chose 'Generated Score' option
        manual_score = None
    else:
        manual_score = int(score)

    url_suffix = f"/indicator/{indicator_id}/scores"
    params = {"manual_score": manual_score}

    res = tq_request("PUT", url_suffix, params)

    data = {"ID": int(indicator_id), "TQScore": get_tq_score_from_response(res["data"])}

    ec = {CONTEXT_PATH["indicator"]: data}

    readable = (
        "Successfully updated score of indicator with ID {} to {}. "
        "Notice that final score is the maximum between "
        "manual and generated scores.".format(indicator_id, int(data["TQScore"]))
    )

    return_outputs(readable, ec, res)


def add_source_command():
    args = demisto.args()
    source = args.get("source")
    obj_id = args.get("obj_id")
    obj_type = args.get("obj_type")

    if isinstance(obj_id, str) and not obj_id.isdigit():
        return_error("obj_id argument must be an integer.")

    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}/{obj_id}/sources"
    params = {"name": source}

    tq_request("POST", url_suffix, params)
    demisto.results(f"Successfully added source {source} to {obj_type} with ID {obj_id}.")


def delete_source_command():
    args = demisto.args()
    source_id = args.get("source_id")
    obj_id = args.get("obj_id")
    obj_type = args.get("obj_type")

    if isinstance(obj_id, str) and not obj_id.isdigit():
        return_error("obj_id argument must be an integer.")
    if isinstance(source_id, str) and not source_id.isdigit():
        return_error("source_id argument must be an integer.")

    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}/{obj_id}/sources/{source_id}"

    tq_request("DELETE", url_suffix)
    demisto.results(f"Successfully deleted source #{source_id} from {obj_type} with ID {obj_id}.")


def add_attribute_command():
    args = demisto.args()
    attribute_name = args.get("name")
    attribute_value = args.get("value")
    obj_type = args.get("obj_type")
    obj_id = args.get("obj_id")

    if isinstance(obj_id, str) and not obj_id.isdigit():
        return_error("obj_id argument must be an integer.")

    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}/{obj_id}/attributes"
    params = {"name": attribute_name, "value": attribute_value}

    tq_request("POST", url_suffix, params)
    demisto.results(f"Successfully added attribute to {obj_type} with ID {obj_id}.")


def modify_attribute_command():
    args = demisto.args()
    attribute_id = args.get("attribute_id")
    attribute_value = args.get("attribute_value")
    obj_type = args.get("obj_type")
    obj_id = args.get("obj_id")

    if isinstance(obj_id, str) and not obj_id.isdigit():
        return_error("obj_id argument must be an integer.")
    if isinstance(attribute_id, str) and not attribute_id.isdigit():
        return_error("attribute_id argument must be an integer.")

    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}/{obj_id}/attributes/{attribute_id}"
    params = {"value": attribute_value}

    tq_request("PUT", url_suffix, params)

    demisto.results(f"Successfully modified attribute #{attribute_id} of {obj_type} with ID {obj_id}.")


def delete_attribute_command():
    args = demisto.args()
    attribute_id = args.get("attribute_id")
    obj_type = args.get("obj_type")
    obj_id = args.get("obj_id")

    if isinstance(obj_id, str) and not obj_id.isdigit():
        return_error("obj_id argument must be an integer.")
    if isinstance(attribute_id, str) and not attribute_id.isdigit():
        return_error("attribute_id argument must be an integer.")

    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}/{obj_id}/attributes/{attribute_id}"

    tq_request("DELETE", url_suffix)
    demisto.results(f"Successfully deleted attribute #{attribute_id} from {obj_type} with ID {obj_id}.")


def update_status_command():
    args = demisto.args()
    indicator_id = args.get("id")
    status = args.get("status")

    if isinstance(indicator_id, str) and not indicator_id.isdigit():
        return_error("id argument must be an integer.")

    url_suffix = f"/indicators/{indicator_id}"
    params = {"status": status}

    res = tq_request("PUT", url_suffix, params)

    data = {
        "ID": int(indicator_id),
        "Status": status_id_to_status(res["data"].get("status_id")),
    }

    ec = {CONTEXT_PATH["indicator"]: data}

    readable = f"Successfully updated status of indicator with ID {indicator_id} to {status}."

    return_outputs(readable, ec, res)


def upload_file_command():
    args = demisto.args()
    entry_id = args.get("entry_id")
    title = args.get("title")
    malware_safety_lock = args.get("malware_safety_lock", "off")
    file_category = args.get("file_category")

    file_info = demisto.getFilePath(entry_id)

    if not title:
        title = file_info["name"]

    params = {
        "name": file_info["name"],
        "title": title,
        "type": file_category,
        "malware_locked": malware_locked_to_request_format(malware_safety_lock),
    }

    try:
        shutil.copy(file_info["path"], file_info["name"])
    except Exception as e:
        return_error(f"Failed to prepare file for upload. Error message: {e!s}")

    try:
        with open(file_info["name"], "rb") as f:
            files = {"file": f}
            url_suffix = "/attachments"
            res = tq_request("POST", url_suffix, params, files=files)
    finally:
        shutil.rmtree(file_info["name"], ignore_errors=True)

    data = file_data_to_demisto_format(res["data"])

    ec = {CONTEXT_PATH["attachment"]: data}

    readable_title = "Successfully uploaded file {}.".format(file_info["name"])
    readable = build_readable(readable_title, "attachment", data)

    return_outputs(readable, ec, res)


def download_file_command():
    args = demisto.args()
    file_id = args.get("id")

    if isinstance(file_id, str) and not file_id.isdigit():
        return_error("id argument must be an integer.")

    url_suffix = f"/attachments/{file_id}/download"

    res = tq_request("GET", url_suffix, retrieve_entire_response=True)

    # 'Content-Disposition' value is of the form: attachment; filename="filename.txt"
    # Since we don't have the file name anywhere else in the response object, we parse it from this entry.
    filename = res.headers.get("Content-Disposition", "").split('"')[1]
    content = res.content

    demisto.results(fileResult(filename, content))


def get_all_objs_command(obj_type):
    args = demisto.args()
    page = int(args.get("page", 0))
    limit = int(args.get("limit", 50))
    if limit > 200:
        limit = 200

    url_suffix = f"/{OBJ_DIRECTORY[obj_type]}?with=attributes,sources"
    if obj_type == "indicator":
        url_suffix += ",score"
    res = tq_request("GET", url_suffix)

    from_index = min(page, len(res["data"]))
    to_index = min(from_index + limit, len(res["data"]))

    data = [data_to_demisto_format(obj, obj_type) for obj in res["data"][from_index:to_index]]
    ec = {CONTEXT_PATH[obj_type]: createContext(data, removeNull=True)} if data else {}

    readable_title = f"List of all objects of type {obj_type} - {from_index}-{to_index - 1}"
    metadata = "Total number of objects is {}".format(len(res["data"]))
    readable = build_readable(readable_title, obj_type, data, metadata=metadata)

    return_outputs(readable, ec, res)


def get_ip_reputation():
    args = demisto.args()
    ips = argToList(args.get("ip"))

    for ip in ips:
        if not is_ip_valid(ip, accept_v6_ips=True):
            return_error(f"{ip} is not a valid IP address.")

        generic_context = {"Address": ip}

        make_indicator_reputation_request(indicator_type="ip", value=ip, generic_context=generic_context)


def get_url_reputation():
    args = demisto.args()
    urls = argToList(args.get("url"))

    for url in urls:
        if not REGEX_MAP["url"].match(url):
            return_error(f"{url} is not a valid URL.")

        generic_context = {"Data": url}

        make_indicator_reputation_request(indicator_type="url", value=url, generic_context=generic_context)


def get_email_reputation():
    args = demisto.args()
    emails = argToList(args.get("email"))

    for email in emails:
        if not REGEX_MAP["email"].match(email):
            return_error(f"{email} is not a valid email address.")

        generic_context = {"Address": email}

        make_indicator_reputation_request(indicator_type="email", value=email, generic_context=generic_context)


def get_domain_reputation():
    args = demisto.args()
    domains = argToList(args.get("domain"))

    for domain in domains:
        generic_context = {"Name": domain}
        make_indicator_reputation_request(indicator_type="domain", value=domain, generic_context=generic_context)


def get_file_reputation():
    args = demisto.args()
    files = argToList(args.get("file"))

    for file in files:
        for fmt in ["md5", "sha1", "sha256"]:
            if REGEX_MAP[fmt].match(file):
                break
        else:
            return_error(f"{file} is not a valid file format.")

    generic_context = createContext(
        {
            "MD5": file if fmt == "md5" else None,
            "SHA1": file if fmt == "sha1" else None,
            "SHA256": file if fmt == "sha256" else None,
        },
        removeNull=True,
    )

    make_indicator_reputation_request(indicator_type="file", value=file, generic_context=generic_context)


""" EXECUTION CODE """
command = demisto.command()
LOG(f"command is {demisto.command()}")
try:
    handle_proxy()
    if command == "test-module":
        test_module()
    elif command == "threatq-advanced-search":
        advance_search_command()
    elif command == "threatq-search-by-name":
        search_by_name_command()
    elif command == "threatq-search-by-id":
        search_by_id_command()
    elif command == "threatq-create-indicator":
        create_indicator_command()
    elif command == "threatq-create-event":
        create_event_command()
    elif command == "threatq-create-adversary":
        create_adversary_command()
    elif command == "threatq-edit-indicator":
        edit_indicator_command()
    elif command == "threatq-edit-event":
        edit_event_command()
    elif command == "threatq-edit-adversary":
        edit_adversary_command()
    elif command == "threatq-delete-object":
        delete_object_command()
    elif command == "threatq-get-related-indicators":
        get_related_objs_command("indicator")
    elif command == "threatq-get-related-events":
        get_related_objs_command("event")
    elif command == "threatq-get-related-adversaries":
        get_related_objs_command("adversary")
    elif command == "threatq-link-objects":
        link_objects_command()
    elif command == "threatq-unlink-objects":
        unlink_objects_command()
    elif command == "threatq-update-score":
        update_score_command()
    elif command == "threatq-add-source":
        add_source_command()
    elif command == "threatq-delete-source":
        delete_source_command()
    elif command == "threatq-add-attribute":
        add_attribute_command()
    elif command == "threatq-modify-attribute":
        modify_attribute_command()
    elif command == "threatq-delete-attribute":
        delete_attribute_command()
    elif command == "threatq-update-status":
        update_status_command()
    elif command == "threatq-upload-file":
        upload_file_command()
    elif command == "threatq-download-file":
        download_file_command()
    elif command == "threatq-get-all-indicators":
        get_all_objs_command("indicator")
    elif command == "threatq-get-all-events":
        get_all_objs_command("event")
    elif command == "threatq-get-all-adversaries":
        get_all_objs_command("adversary")
    elif command == "ip":
        get_ip_reputation()
    elif command == "domain":
        get_domain_reputation()
    elif command == "email":
        get_email_reputation()
    elif command == "url":
        get_url_reputation()
    elif command == "file":
        get_file_reputation()

except Exception as ex:
    return_error(str(ex))