SecurityTrails

This integration provides API access to the SecurityTrails platform.

Utilities · SecurityTrails

Details

IDSecurityTrails
ProviderRecorded Future
CategoryUtilities
From Version6.0.0
Docker Imagedemisto/python3:3.12.8.3296088
Supported ModulesAgentix XSIAM

README

This integration provides API access to the SecurityTrails platform.
This integration was integrated and tested with V1 of SecurityTrails

Configure SecurityTrails in Cortex

Parameter Required
API Key True
Trust any certificate (not secure) False
Use system proxy settings False
Fetch indicators False

Commands

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

securitytrails-get-subdomains


Returns child and sibling subdomains for a given hostname.

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

Base Command

securitytrails-get-subdomains

Input

Argument Name Description Required
hostname The hostname. Required
children_only Only return children subdomains. Possible values are: true, false. Default is true. Optional

Context Output

Path Type Description
SecurityTrails.Domain.name string Hostname
SecurityTrails.Domain.subdomains unknown Subdomains
Domain.Name string Domain name
Domain.Subdomains string Subdomains
SecurityTrails.Domain.subdomain_count number Subdomain Count

Command Example


#### Human Readable Output

### securitytrails-get-domain-details

***
Returns the current data about the given hostname. In addition to the current data, you also get the current statistics associated with a particular record. For example, for a records you'll get how many other hostnames have the same IP.

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

#### Base Command

`securitytrails-get-domain-details`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| hostname | The hostname. | Required |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| SecurityTrails.Domain.name | string | Domain name |
| SecurityTrails.Domain.alexa_rank | number | Alexa rank |
| SecurityTrails.Domain.apex_domain | string | Apex domain |
| SecurityTrails.Domain.current_dns | unknown | Current DNS records |
| SecurityTrails.Domain.subdomain_count | number | Subdomain count |
| Domain.Name | string | Domain name |
| Domain.NameServers | string | Name servers |

#### Command Example

Human Readable Output

securitytrails-get-tags


Returns tags for a given hostname

Base Command

securitytrails-get-tags

Input

Argument Name Description Required
hostname Hostname. Required

Context Output

Path Type Description
SecurityTrails.Domain.name string Domain name
SecurityTrails.Domain.tags unknown Domain tags
Domain.Name string Domain name
Domain.Tags string Domain tags

Command Example


#### Human Readable Output

### securitytrails-get-company-details

***
Returns details for a company domain.

#### Base Command

`securitytrails-get-company-details`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| domain | Domain. | Required |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| SecurityTrails.Domain.name | string | Domain name |
| SecurityTrails.Domain.company | string | Company name |
| Domain.Name | string | Domain name |
| Domain.Organization | string | Organization |
| Domain.Registrant.Name | string | Domain registrant name |
| WHOIS.Registrant.Name | string | Domain registrant name |

#### Command Example

Human Readable Output

securitytrails-get-company-associated-ips


Returns associated IPs for a company domain. The data is based on whois data with the names matched to the domains.

Base Command

securitytrails-get-company-associated-ips

Input

Argument Name Description Required
domain Domain. Required

Context Output

Path Type Description
SecurityTrails.Domain.name string Domain name
SecurityTrails.Domain.assocaitedips unknown Associated IPs
SecurityTrails.Domain.assocaitedip_count number Associated IP Count

Command Example


#### Human Readable Output

### securitytrails-get-domain-whois

***
Returns the current WHOIS data about a given hostname with the stats merged together

#### Base Command

`securitytrails-get-domain-whois`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| hostname | Hostname. | Required |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| SecurityTrails.Domain.expiresDate | date | Expires date |
| SecurityTrails.Domain.nameServers | unknown | Name servers |
| SecurityTrails.Domain.updatedDate | date | Updated date |
| SecurityTrails.Domain.name | string | Domain name |
| SecurityTrails.Domain.status | string | Status |
| SecurityTrails.Domain.contacts.countryCode | string | Country code |
| SecurityTrails.Domain.contacts.organization_count | number | Organization count |
| SecurityTrails.Domain.contacts.telephone | string | Telephone |
| SecurityTrails.Domain.contacts.postalCode_count | number | Postal code count |
| SecurityTrails.Domain.contacts.fax_count | number | Fax count |
| SecurityTrails.Domain.contacts.street1 | string | Street 1 |
| SecurityTrails.Domain.contacts.state | string | State |
| SecurityTrails.Domain.contacts.organization | string | Organization |
| SecurityTrails.Domain.contacts.telephone_count | number | Telephone count |
| SecurityTrails.Domain.contacts.country | string | Country |
| SecurityTrails.Domain.contacts.postalCode | string | Postcode |
| SecurityTrails.Domain.contacts.type | string | Type |
| SecurityTrails.Domain.contacts.city_count | number | City count |
| SecurityTrails.Domain.contacts.name_count | number | Name count |
| SecurityTrails.Domain.contacts.email | string | Email |
| SecurityTrails.Domain.contacts.fax | string | Fax |
| SecurityTrails.Domain.contacts.street1_count | number | Street 1 count |
| SecurityTrails.Domain.private_registration | boolean | Private registration |
| SecurityTrails.Domain.createdDate | date | Created date |
| SecurityTrails.Domain.registrarName | string | Registrar name |
| SecurityTrails.Domain.contactEmail | string | Contact email |
| Domain.Admin.Country | string | Country |
| Domain.Admin.Email | string | Email |
| Domain.Admin.Name | string | Name |
| Domain.Admin.Phone | string | Phone |
| Domain.DomainStatus | string | Status |
| Domain.Name | string | Name |
| Domain.NameServers | string | Name server |
| Domain.UpdatedDate | date | Updated date |
| Domain.WHOIS.CreationDate | date | Creation date |
| Domain.WHOIS.DomainStatus | string | Status |
| Domain.WHOIS.ExpirationDate | date | Expiration date |
| Domain.WHOIS.NameServers | string | Name servers |
| Domain.WHOIS.Registrar.Name | string | Name |
| Domain.WHOIS.UpdatedDate | date | Updated date |

#### Command Example

Human Readable Output

securitytrails-get-dns-history


Lists out specific historical information about the given hostname parameter. In addition of fetching the historical data for a particular type, the count statistic is returned as well, which represents the number of that particular resource against current data.

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

Base Command

securitytrails-get-dns-history

Input

Argument Name Description Required
hostname Hostname. Required
type Type. Possible values are: a, aaaa, mx, ns, soa, txt. Default is a. Required

Context Output

Path Type Description
SecurityTrails.Domain.a_history_records.first_seen string First seen
SecurityTrails.Domain.a_history_records.last_seen string Last seen
SecurityTrails.Domain.a_history_records.organizations unknown Organizations
SecurityTrails.Domain.a_history_records.type string Type
SecurityTrails.Domain.a_history_records.values.ip string IP
SecurityTrails.Domain.a_history_records.values.ipv6 string IPv6
SecurityTrails.Domain.mx_history_records.values.host string Host
SecurityTrails.Domain.mx_history_records.values.mx_count number MX count
SecurityTrails.Domain.mx_history_records.values.priority number Priority
SecurityTrails.Domain.name string Name
SecurityTrails.Domain.ns_history_records.values.nameserver string Name server
SecurityTrails.Domain.ns_history_records.values.nameserver_count number Name server count
SecurityTrails.Domain.soa_history_records.values.email string Email
SecurityTrails.Domain.soa_history_records.values.email_count number Email count
SecurityTrails.Domain.soa_history_records.values.ttl number TTL
SecurityTrails.Domain.txt_history_records.values.value string Value
SecurityTrails.Domain.a_history_record_pages number A record pages count
SecurityTrails.Domain.aaaa_history_record_pages number AAAA record pages count
SecurityTrails.Domain.mx_history_record_pages number MX record pages count
SecurityTrails.Domain.ns_history_record_pages number NS record pages count
SecurityTrails.Domain.soa_history_record_pages number SOA record pages count
SecurityTrails.Domain.txt_history_record_pages number TXT record pages count

Command Example


#### Human Readable Output

### securitytrails-get-whois-history

***
Returns historical WHOIS information about the given domain.

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

#### Base Command

`securitytrails-get-whois-history`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| hostname | Hostname. | Required |
| page | The page of the returned results, starting at 1. A page returns 100 results. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| SecurityTrails.Domain.name | string | Name |
| SecurityTrails.Domain.whois_history.contact.telephone | string | Telephone |
| SecurityTrails.Domain.whois_history.contact.city | string | City |
| SecurityTrails.Domain.whois_history.contact.name | string | Name |
| SecurityTrails.Domain.whois_history.contact.street1 | string | Street 1 |
| SecurityTrails.Domain.whois_history.contact.state | string | State |
| SecurityTrails.Domain.whois_history.contact.organization | string | Organization |
| SecurityTrails.Domain.whois_history.contact.country | string | Country |
| SecurityTrails.Domain.whois_history.contact.postalCode | string | Postal code |
| SecurityTrails.Domain.whois_history.contact.type | string | Type |
| SecurityTrails.Domain.whois_history.contact.email | string | Email |
| SecurityTrails.Domain.whois_history.contact.fax | string | Fax |
| SecurityTrails.Domain.whois_history.started | number | Started |
| SecurityTrails.Domain.whois_history.expiresDate | number | Expires date |
| SecurityTrails.Domain.whois_history.domain | string | Domain |
| SecurityTrails.Domain.whois_history.nameServers | string | Name servers |
| SecurityTrails.Domain.whois_history.gtld | boolean | GTLD |
| SecurityTrails.Domain.whois_history.updatedDate | number | Updated date |
| SecurityTrails.Domain.whois_history.status | string | Status |
| SecurityTrails.Domain.whois_history.full_domain | string | Full domain |
| SecurityTrails.Domain.whois_history.createdDate | number | Created date |
| SecurityTrails.Domain.whois_history.registrarName | string | Registrar name |
| SecurityTrails.Domain.whois_history.ended | number | Ended date |
| SecurityTrails.Domain.whois_history_count | number | WHOIS history count |
| Domain.Name | string | Name |
| Domain.WHOIS/History.Admin.Email | string | Email |
| Domain.WHOIS/History.Admin.Name | string | Name |
| Domain.WHOIS/History.Admin.Phone | string | Phone |
| Domain.WHOIS/History.CreationDate | date | Creation date |
| Domain.WHOIS/History.DomainStatus | string | Status |
| Domain.WHOIS/History.ExpirationDate | date | Expiration date |
| Domain.WHOIS/History.NameServers | string | Name servers |
| Domain.WHOIS/History.Registrant.Email | string | Email |
| Domain.WHOIS/History.Registrant.Name | string | Name |
| Domain.WHOIS/History.Registrant.Phone | string | Phone |
| Domain.WHOIS/History.Registrar.Email | string | Email |
| Domain.WHOIS/History.Registrar.Name | string | Name |
| Domain.WHOIS/History.Registrar.Phone | string | Phone |
| Domain.WHOIS/History.UpdatedDate | date | Updated date |

#### Command Example

Human Readable Output

securitytrails-get-ip-neighbors


Returns the neighbors in any given IP level range and essentially allows you to explore closeby IP addresses. It will divide the range into 16 groups. Example: a /28 would be divided into 16 /32 blocks or a /24 would be divided into 16 /28 blocks

Base Command

securitytrails-get-ip-neighbors

Input

Argument Name Description Required
ipaddress Starting IP address (optionally with CIDR subnet mask). Required

Context Output

Path Type Description
SecurityTrails.IP.ip string IP address
SecurityTrails.IP.block.active_egress boolean Active Egress
SecurityTrails.IP.block.hostnames string Hostnames
SecurityTrails.IP.block.ports number Port
SecurityTrails.IP.block.sites number Sites
IP.Address string Address

Command Example


#### Human Readable Output

### securitytrails-search-domain

***
Filter and search specific records using DSL - a powerful SQL like query interface to the data via certain API end points.

#### Base Command

`securitytrails-search-domain`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| include_ips | Resolves any A records and additionally returns IP addresses. Possible values are: false, true. Default is false. | Optional |
| page | The page of the returned results, starting at 1. A page returns 100 results. | Optional |
| scroll | Request scrolling. Only supported when query is used and not filter. See the Scrolling API endpoint. Possible values are: false, true. Default is false. | Optional |
| query | The DSL query you want to run (https://docs.securitytrails.com/docs/how-to-use-the-dsl). | Optional |
| filter | JSON dicitonary of filter terms (https://docs.securitytrails.com/reference#domain-search). Can not be used together with query. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| SecurityTrails.Domain.Search.alexa_rank | number | Alexa rank |
| SecurityTrails.Domain.Search.computed.company_name | string | Company name |
| SecurityTrails.Domain.Search.host_provider | string | Host provider |
| SecurityTrails.Domain.Search.hostname | string | Hostname |
| SecurityTrails.Domain.Search.mail_provider.[0] | string | Mail provider |
| SecurityTrails.Domain.Search.whois.createdDate | number | Created date |
| SecurityTrails.Domain.Search.whois.expiresDate | number | Expires date |
| SecurityTrails.Domain.Search.whois.registrar | string | Registrar |

#### Command Example

Human Readable Output

securitytrails-statistics-domain


Domain statistics

Base Command

securitytrails-statistics-domain

Input

Argument Name Description Required
query The DSL query you want to run (https://docs.securitytrails.com/docs/how-to-use-the-dsl). Optional
filter JSON dicitonary of filter terms (https://docs.securitytrails.com/reference#domain-search). Can not be used together with query. Optional

Context Output

Path Type Description
SecurityTrails.Domain.Search.DomainStats.domain_count number Domain count
SecurityTrails.Domain.Search.DomainStats.hostname_count.relation string Relation
SecurityTrails.Domain.Search.DomainStats.hostname_count.value number Value
SecurityTrails.Domain.Search.DomainStats.tld_count number TLD count
SecurityTrails.Domain.Search.DomainStats.top_organizations.count number Count
SecurityTrails.Domain.Search.DomainStats.top_organizations.key string Key
SecurityTrails.Domain.Search.DomainStats.whois_organization_count number WHOIS count

Command Example


#### Human Readable Output

### securitytrails-get-associated-domains

***
Find all domains that are related to a hostname you input. Limited to 10000 results.

#### Base Command

`securitytrails-get-associated-domains`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| hostname | Hostname. | Required |
| page | The page of the returned results, starting at 1. A page returns 100 results. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| SecurityTrails.Domain.associated_domains.alexa_rank | number | Alexa Rank |
| SecurityTrails.Domain.associated_domains.computed.company_name | string | Company Name |
| SecurityTrails.Domain.associated_domains.host_provider | string | Host Provider |
| SecurityTrails.Domain.associated_domains.hostname | string | Hostname |
| SecurityTrails.Domain.associated_domains.mail_provider | string | Mail Provider |
| SecurityTrails.Domain.associated_domains.whois.createdDate | number | Created Date |
| SecurityTrails.Domain.associated_domains.whois.expiresDate | number | Expires Date |
| SecurityTrails.Domain.associated_domains.whois.registrar | string | Registrar |
| SecurityTrails.Domain.associated_domain_count | number | Associated Domain Count |

#### Command Example

Human Readable Output

securitytrails-search-ip


Search for IP addresses. A maximum of 10000 results can be retrieved.

Base Command

securitytrails-search-ip

Input

Argument Name Description Required
page The page of the returned results, starting at 1. A page returns 100 results. Default is 1. Optional
query The DSL query you want to run (https://docs.securitytrails.com/docs/how-to-use-the-dsl). Required

Context Output

Path Type Description
SecurityTrails.IP.Search.ip string IP Address
SecurityTrails.IP.Search.ports.port number Port
SecurityTrails.IP.Search.ports.date_checked number Date checked
SecurityTrails.IP.Search.ptr string PTR Record
IP.Address string Address
IP.Hostname string Hostname
IP.Ports string Ports

Command Example


#### Human Readable Output

### securitytrails-statistics-ip

***
Statistics like Reverse DNS pattern identification (RDNS entries are grouped and displayed as x), ports (number of open ports found) or total results are returned

#### Base Command

`securitytrails-statistics-ip`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| query | The DSL query you want to run (https://docs.securitytrails.com/docs/how-to-use-the-dsl). | Required |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| SecurityTrails.IP.Search.IPStats.ports.count | number | Count |
| SecurityTrails.IP.Search.IPStats.ports.key | number | Key |
| SecurityTrails.IP.Search.IPStats.top_ptr_patterns.count | number | Count |
| SecurityTrails.IP.Search.IPStats.top_ptr_patterns.key | string | Key |
| SecurityTrails.IP.Search.IPStats.total.relation | string | Relation |
| SecurityTrails.IP.Search.IPStats.total.value | number | Value |

#### Command Example

Human Readable Output

securitytrails-get-ip-whois


Returns IPs information based on whois information.

Base Command

securitytrails-get-ip-whois

Input

Argument Name Description Required
ipaddress IP Address. Required

Context Output

Path Type Description
SecurityTrails.IP.contact_email string Email
SecurityTrails.IP.contacts.email string Email
SecurityTrails.IP.contacts.organization string Organization
SecurityTrails.IP.contacts.telephone string Telephone
SecurityTrails.IP.contacts.type string Type
SecurityTrails.IP.ip string IP
SecurityTrails.IP.source string Source

Command Example


#### Human Readable Output

### securitytrails-get-useragents

***
Fetch user agents seen during the last 30 days for a specific IPv4 address. It shows devices with egressing traffic based on large scale web server logs. The number of results is not limited.

#### Base Command

`securitytrails-get-useragents`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| ipaddress | IP Address. | Required |
| page | The page of the returned results, starting at 1. A page returns 100 results. Default is 1. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| SecurityTrails.IP.ip | string | IP Address |
| SecurityTrails.IP.useragent_records_count | number | Count |
| SecurityTrails.IP.useragents.browser_family | string | Browser Family |
| SecurityTrails.IP.useragents.client.engine | string | Client Engine |
| SecurityTrails.IP.useragents.client.engine_version | string | Client Engine Version |
| SecurityTrails.IP.useragents.client.name | string | Client Engine Name |
| SecurityTrails.IP.useragents.client.type | string | Client Engine Type |
| SecurityTrails.IP.useragents.client.version | string | Client Version |
| SecurityTrails.IP.useragents.device.brand | string | Device Brand |
| SecurityTrails.IP.useragents.device.model | string | Device Model |
| SecurityTrails.IP.useragents.device.type | string | Device Type |
| SecurityTrails.IP.useragents.lastseen | string | Last Seen |
| SecurityTrails.IP.useragents.os.name | string | OS Name |
| SecurityTrails.IP.useragents.os.platform | string | OS Platform |
| SecurityTrails.IP.useragents.os.version | string | OS Version |
| SecurityTrails.IP.useragents.os_family | string | OS Family |
| SecurityTrails.IP.useragents.user_agent | string | User Agent |

#### Command Example

Human Readable Output

domain


Provides data enrichment for domains.

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 name to enrich. Required

Context Output

Path Type Description
Domain.Name String The domain name, for example: “google.com”.
Domain.DNS String A list of IP objects resolved by DNS.
Domain.DomainStatus Datte The status of the domain.
Domain.NameServers Unknown (List<String>) Name servers of the domain.
Domain.Organization String The organization of the domain.
Domain.Subdomains Unknown (List<String>) Subdomains of the domain.
Domain.Admin.Country String The country of the domain administrator.
Domain.Admin.Email String The email address of the domain administrator.
Domain.Admin.Name String The name of the domain administrator.
Domain.Admin.Phone String The phone number of the domain administrator.
Domain.Registrant.Country String The country of the registrant.
Domain.Registrant.Email String The email address of the registrant.
Domain.Registrant.Name String The name of the registrant.
Domain.Registrant.Phone String The phone number for receiving abuse reports.
Domain.Tags Unknown (List) Tags of the domain.
Domain.WHOIS.DomainStatus String The status of the domain.
Domain.WHOIS.NameServers String (List<String>) Name servers of the domain.
Domain.WHOIS.CreationDate Date The date that the domain was created.
Domain.WHOIS.UpdatedDate Date The date that the domain was last updated.
Domain.WHOIS.ExpirationDate Date The expiration date of the domain.
Domain.WHOIS.Registrant.Name String The name of the registrant.
Domain.WHOIS.Registrant.Email String The email address of the registrant.
Domain.WHOIS.Registrant.Phone String The phone number of the registrant.
Domain.WHOIS.Registrar.Name String The name of the registrar, for example: `GoDaddy`
Domain.WHOIS.Registrar.Email String The email address of the contact.
Domain.WHOIS.Registrar.Phone String The phone number of contact.
Domain.WHOIS.Admin.Name String The name of the domain administrator.
Domain.WHOIS.Admin.Email String The email address of the domain administrator.
Domain.WHOIS.Admin.Phone String The phone number of the domain administrator.
DBotScore.Indicator String The indicator that was tested.
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor used to calculate the score.
DBotScore.Score Number The actual score.

Command Example


#### Human Readable Output

### securitytrails-sql-query

***
Queries the SecurityTrails SQL endpoint. The SecurityTrails SQL API provides a powerful SQL-like query interface to data via certain API endpoints. For a full reference of properties and operators please check the following link: https://securitytrails.com/reference/sql

#### Base Command

`securitytrails-sql-query`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| sql | The SQL query to execute (example: SELECT attribute FROM table WHERE condition = "value"). Possible values are: . | Required |
| timeout | Read timeout for calls (default is 20 seconds). Possible values are: . Default is 20. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| Securitytrails.SQL.total | Number | The total number of hits discovered |
| Securitytrails.SQL.records | Unknown | The records returned |
| Securitytrails.SQL.id | String | The ID to use for further GET calls to retrieve more results |
| Securitytrails.SQL.query | String | The original query used |
| Securitytrails.SQL.pages | Number | The total number of pages that would need to be called to retrieve the rest of the results |

#### Command Example

Human Readable Output

securitytrails-sql-get-next


Retrieves the next page of results returned from a SQL query where the results exceeded the last page.

Base Command

securitytrails-sql-get-next

Input

Argument Name Description Required
id The ID to use to retrieve the next page of results. Possible values are: . Required
timeout Read timeout for calls (default is 20 seconds). Possible values are: . Default is 20. Optional

Context Output

Path Type Description
Securitytrails.SQL.total number The total number of hits discovered
Securitytrails.SQL.records unknown The records returned
Securitytrails.SQL.id string The ID to use for further GET calls to retrieve more results
Securitytrails.SQL.query string The original query used

Command Example

``````

Human Readable Output

Configuration parameters

  • apikey — API Key (required)
  • timeout — Timeout
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • integrationReliability — Source Reliability
  • feedExpirationPolicy
  • feedExpirationInterval

Commands (19)

  • domain

    Provides data enrichment for domains.

  • securitytrails-get-associated-domains

    Find all domains that are related to a hostname you input. Limited to 10000 results.

  • securitytrails-get-company-associated-ips

    Returns associated IPs for a company domain. The data is based on whois data with the names matched to the domains.

  • securitytrails-get-company-details

    Returns details for a company domain.

  • securitytrails-get-dns-history

    Lists out specific historical information about the given hostname parameter. In addition of fetching the historical data for a particular type, the count statistic is returned as well, which represents the number of that particular resource against current data.

  • securitytrails-get-domain-details

    Returns the current data about the given hostname. In addition to the current data, you also get the current statistics associated with a particular record. For example, for a records you'll get how many other hostnames have the same IP.

  • securitytrails-get-domain-whois

    Returns the current WHOIS data about a given hostname with the stats merged together.

  • securitytrails-get-ip-neighbors

    Returns the neighbors in any given IP level range and essentially allows you to explore closeby IP addresses. It will divide the range into 16 groups. Example: a /28 would be divided into 16 /32 blocks or a /24 would be divided into 16 /28 blocks.

  • securitytrails-get-ip-whois

    Returns IPs information based on whois information.

  • securitytrails-get-subdomains

    Returns child and sibling subdomains for a given hostname.

  • securitytrails-get-tags

    Returns tags for a given hostname.

  • securitytrails-get-useragents

    Fetch user agents seen during the last 30 days for a specific IPv4 address. It shows devices with egressing traffic based on large scale web server logs. The number of results is not limited.

  • securitytrails-get-whois-history

    Returns historical WHOIS information about the given domain.

  • securitytrails-search-domain

    Filter and search specific records using DSL - a powerful SQL like query interface to the data via certain API end points.

  • securitytrails-search-ip

    Search for IP addresses. A maximum of 10000 results can be retrieved.

  • securitytrails-sql-get-next

    Retrieves the next page of results returned from a SQL query where the results exceeded the last page.

  • securitytrails-sql-query

    Queries the SecurityTrails SQL endpoint. The SecurityTrails SQL API provides a powerful SQL-like query interface to data via certain API endpoints. For a full reference of properties and operators please check the following link: https://securitytrails.com/reference/sql

  • securitytrails-statistics-domain

    Domain statistics.

  • securitytrails-statistics-ip

    Statistics like Reverse DNS pattern identification (RDNS entries are grouped and displayed as x), ports (number of open ports found) or total results are returned.

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

""" CONSTANTS """

removed_keys = ["endpoint", "domain", "hostname"]

""" CLIENT CLASS """


class Client(BaseClient):
    def __init__(self, base_url, verify=True, proxy=False, ok_codes=(), headers=None, auth=None, timeout=10):
        super().__init__(base_url, verify=verify, proxy=proxy, ok_codes=ok_codes, headers=headers, auth=auth)
        self.timeout = timeout

    def domain_tags(self, hostname: str = None):
        res = self._http_request("GET", f"domain/{hostname}/tags", ok_codes=(200, 403), timeout=self.timeout)
        return res.get("tags", [])

    def domain_details(self, hostname: str = None):
        return self._http_request("GET", f"domain/{hostname}", ok_codes=(200, 403), timeout=self.timeout)

    def domain_subdomains(self, hostname: str = None, children_only: str = "true"):
        query_string = {"children_only": children_only}
        res = self._http_request(
            "GET", f"domain/{hostname}/subdomains", params=query_string, ok_codes=(200, 403), timeout=self.timeout
        )
        return res

    def associated_domains(self, hostname: str = None, page: int = 1):
        params = {"page": page}
        res = self._http_request("GET", f"domain/{hostname}/associated", params=params, ok_codes=(200, 403), timeout=self.timeout)
        return res

    def get_ssl_certificates(self, query_type: str = "stream", hostname: str = None, params: dict = None):
        # There's a bug in the API where the result is malformed.
        if query_type == "paged":
            res = self._http_request(
                "GET",
                f"domain/{hostname}/ssl",
                params=params or {},
                ok_codes=(200, 403),
                resp_type="response",
                timeout=self.timeout,
            )
        elif query_type == "stream":
            res = self._http_request(
                "GET",
                f"domain/{hostname}/ssl_stream",
                params=params,
                ok_codes=(200, 403),
                resp_type="response",
                timeout=self.timeout,
            )
        else:
            res = {}
            demisto.debug(f"The {query_type=} didn't match any value. {res=}")

        return res

    def get_company(self, domain: str = None):
        res = self._http_request("GET", f"company/{domain}", ok_codes=(200, 403), timeout=self.timeout)
        return res.get("record", {})

    def get_useragents(self, ip_address: str = None, params: dict = None):
        return self._http_request(
            "GET", f"ips/{ip_address}/useragents", params=params or {}, ok_codes=(200, 403), timeout=self.timeout
        )

    def get_company_associated_ips(self, domain: str = None):
        res = self._http_request("GET", f"company/{domain}/associated-ips", ok_codes=(200, 403), timeout=self.timeout)
        return res.get("record", {})

    def get_whois(self, query_type: str = "domain", hostname: str = None):
        if query_type == "domain":  # noqa: RET503
            return self._http_request("GET", f"domain/{hostname}/whois", ok_codes=(200, 403), timeout=self.timeout)
        elif query_type == "ip":
            return self._http_request("GET", f"ips/{hostname}/whois", ok_codes=(200, 403), timeout=self.timeout)

    def get_dns_history(self, hostname: str = None, record_type: str = None, page: int = 1):
        params = {"page": page}
        return self._http_request(
            "GET", f"history/{hostname}/dns/{record_type}", params=params, ok_codes=(200, 403), timeout=self.timeout
        )

    def get_whois_history(self, hostname: str = None, page: int = 1):
        params = {"page": page}
        res = self._http_request("GET", f"history/{hostname}/whois", params=params, ok_codes=(200, 403), timeout=self.timeout)
        return res.get("result")

    def get_ip_neighbors(self, ipaddress: str = None):
        res = self._http_request("GET", f"ips/nearby/{ipaddress}", ok_codes=(200, 403), timeout=self.timeout)
        return res.get("blocks")

    def query(self, query_type: str = "domain_search", body: dict = None, params: dict = None):
        if query_type == "domain_search":  # noqa: RET503
            return self._http_request(
                "POST", "domains/list", params=params, json_data=body, ok_codes=(200, 403), timeout=self.timeout
            )
        elif query_type == "domain_stats":
            return self._http_request("POST", "domains/stats", json_data=body, ok_codes=(200, 403), timeout=self.timeout)
        elif query_type == "ip_search":
            return self._http_request(
                "POST", "ips/list", params=params, json_data=body, ok_codes=(200, 403), timeout=self.timeout
            )
        elif query_type == "ip_stats":
            return self._http_request(
                "POST", "ips/stats", params=params, json_data=body, ok_codes=(200, 403), timeout=self.timeout
            )

    def sql(self, sql: dict = None, timeout: int = 20):
        return self._http_request("POST", "query/scroll", json_data=sql, timeout=self.timeout)

    def sql_next(self, next_id: str = None, timeout: int = 20):
        return self._http_request("GET", f"query/scroll/{next_id}", timeout=self.timeout)


""" HELPER FUNCTIONS """


#################################
# Standard Context Outputs
#################################


def create_standard_domain_context(domain_data):
    command_results = CommandResults(
        outputs_prefix="Domain",
        outputs_key_field="Name",
        outputs=domain_data,
        readable_output=tableToMarkdown("Domain(s):", domain_data),
    )
    return_results(command_results)


def create_standard_ip_context(ip_data):
    command_results = CommandResults(
        outputs_prefix="IP",
        outputs_key_field="Address",
        outputs=ip_data,
        readable_output=tableToMarkdown("IP Address(es):", ip_data),
    )
    return_results(command_results)


def domain_command(client, args):
    domains = argToList(args.get("domain"))
    command_results: List[CommandResults] = []
    for domain in domains:
        try:
            domain_details = client.domain_details(hostname=domain)
        except Exception:
            demisto.info(f"No information found for domain: {domain}")
            return_results(f"No information found for domain: {domain}")
            continue
        domain_subdomains = client.domain_subdomains(hostname=domain)
        domain_whois = client.get_whois(query_type="domain", hostname=domain)
        domain_tags = client.domain_tags(hostname=domain)
        admin_contact = [
            {"Name": x.get("name"), "Email": x.get("email"), "Phone": x.get("telephone"), "Country": x.get("country")}
            for x in domain_whois.get("contacts", [])
            if "admin" in x.get("type", "").lower()
        ]
        registrant_contact = [
            {
                "Name": x.get("name", None),
                "Email": x.get("email", None),
                "Phone": x.get("telephone", None),
                "Country": x.get("country", None),
            }
            for x in domain_whois.get("contacts", [])
            if "registrant" in x.get("type", "").lower()
        ]
        registrar_contact = [
            {
                "Name": x.get("name", None),
                "Email": x.get("email", None),
                "Phone": x.get("telephone", None),
                "Country": x.get("country", None),
            }
            for x in domain_whois.get("contacts", [])
            if "registrar" in x.get("type", "").lower()
        ]
        domain_data = {
            "Name": domain,
            "DNS": ",".join([x.get("ip", "") for x in domain_details.get("current_dns", {}).get("a", {}).get("values", [])]),
            "NameServers": ",".join(
                [x.get("nameserver", "") for x in domain_details.get("current_dns", {}).get("ns", {}).get("values", [])]
            ),
            "Organization": domain_details.get("name", None),
            "Subdomains": ",".join(domain_subdomains.get("subdomains", [])),
            "WHOIS": {
                "DomainStatus": domain_whois.get("status"),
                "NameServers": ",".join(domain_whois.get("nameServers")) if domain_whois.get("nameServers") else None,
                "CreationDate": domain_whois.get("createdDate"),
                "UpdatedDate": domain_whois.get("updatedDate"),
                "ExpirationDate": domain_whois.get("expiresData"),
                "Registrant": {
                    "Name": registrant_contact[0].get("Name", None) if registrant_contact else None,
                    "Email": registrant_contact[0].get("Email", None) if registrant_contact else None,
                    "Phone": registrant_contact[0].get("Phone", None) if registrant_contact else None,
                },
                "Registrar": {
                    "Name": registrar_contact[0].get("Name", None) if registrar_contact else None,
                    "Email": registrar_contact[0].get("Email", None) if registrar_contact else None,
                    "Phone": registrar_contact[0].get("Phone", None) if registrar_contact else None,
                },
                "Admin": {
                    "Name": admin_contact[0].get("Name", None) if admin_contact else None,
                    "Email": admin_contact[0].get("Email", None) if admin_contact else None,
                    "Phone": admin_contact[0].get("Phone", None) if admin_contact else None,
                },
            },
            "Tags": ",".join(domain_tags),
            "Admin": {
                "Country": admin_contact[0].get("Country", None) if admin_contact else None,
                "Name": admin_contact[0].get("Name", None) if admin_contact else None,
                "Email": admin_contact[0].get("Email", None) if admin_contact else None,
                "Phone": admin_contact[0].get("Phone", None) if admin_contact else None,
            },
            "Registrant": {
                "Country": registrant_contact[0].get("Country", None) if registrant_contact else None,
                "Name": registrant_contact[0].get("Name", None) if registrant_contact else None,
                "Email": registrant_contact[0].get("Email", None) if registrant_contact else None,
                "Phone": registrant_contact[0].get("Phone", None) if registrant_contact else None,
            },
        }
        dbot_score = Common.DBotScore(
            indicator=domain,
            indicator_type=DBotScoreType.DOMAIN,
            integration_name="SecurityTrails",
            score=Common.DBotScore.NONE,
            reliability=demisto.params().get("integrationReliability"),
        )
        domain_indicator = Common.Domain(domain=domain, dbot_score=dbot_score)
        md = tableToMarkdown(f"Domain {domain}:", domain_data)
        result = CommandResults(
            outputs_prefix="Domain", outputs_key_field="Name", outputs=domain_data, indicator=domain_indicator, readable_output=md
        )
        command_results.append(result)

    return_results(command_results)


#################################
# Company endpoints
#################################


def get_company_details_command(client, args):
    domain = args.get("domain")
    res = client.get_company(domain=domain)
    readable_output = f"### Company for {domain}: {res.get('name', None)}"
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain",
        outputs_key_field="name",
        outputs={"name": domain, "company": res.get("name", None)},
        readable_output=readable_output,
    )
    return_results(command_results)
    create_standard_domain_context(
        domain_data={
            "Name": domain,
            "Organization": res.get("name", None),
            "Registrant": {"Name": res.get("name", None)},
            "WHOIS": {"Registrant": {"Name": res.get("name", None)}},
        }
    )


def get_company_associated_ips_command(client, args):
    domain = args.get("domain")
    res = client.get_company_associated_ips(domain=domain)
    readable_output = tableToMarkdown(f"Associated IPs for {domain}", res)
    output_data = {"name": domain, "associatedips": res, "associatedips_count": len(res)}

    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain", outputs_key_field="name", outputs=output_data, readable_output=readable_output
    )
    return_results(command_results)


#################################
# Domain endpoints
#################################


def domain_details_command(client, args):
    hostname = args.get("hostname")
    res = client.domain_details(hostname=hostname)
    res = {k: v for k, v in res.items() if k not in removed_keys}
    res["name"] = hostname
    output_data = sorted(
        [{"Type": k, "Record Count": len(v.get("values", []))} for k, v in res.get("current_dns", {}).items()],
        key=lambda x: x["Type"],
    )
    readable_output = tableToMarkdown(f"Domain details for {hostname}:", output_data, ["Type", "Record Count"])
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain", outputs_key_field="name", outputs=res, readable_output=readable_output
    )
    return_results(command_results)

    create_standard_domain_context(
        domain_data={
            "Name": hostname,
            "NameServers": ", ".join(
                [x.get("nameserver", None) for x in res.get("current_dns", {}).get("ns", {}).get("values", [])]
            ),
        }
    )


def domains_subdomains_command(client, args):
    hostname = args.get("hostname")
    children_only = args.get("children_only", "true")
    res = client.domain_subdomains(hostname=hostname, children_only=children_only)
    subdomains = res.get("subdomains", [])
    md = tableToMarkdown(f"Subdomains for {hostname}:", [{"Subdomain": x} for x in subdomains])
    output_data = {"name": hostname, "subdomains": subdomains, "subdomains_count": len(subdomains)}
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain", outputs_key_field="name", outputs=output_data, readable_output=md
    )
    return_results(command_results)


def get_domain_tags_command(client, args):
    hostname = args.get("hostname")
    res = client.domain_tags(hostname=hostname)
    tags = ", ".join(res)
    readable_output = f"### Tags for {hostname}:\n\n{tags}"
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain",
        outputs_key_field="name",
        outputs={"name": hostname, "tags": res},
        readable_output=readable_output,
    )
    return_results(command_results)
    create_standard_domain_context(domain_data={"Name": hostname, "Tags": tags})


def get_whois_command(client, args):
    command = demisto.command()
    if command == "securitytrails-get-domain-whois":
        hostname = args.get("hostname")
        res = client.get_whois(query_type="domain", hostname=hostname)
        res = {k: v for k, v in res.items() if k not in removed_keys}
        res["name"] = hostname
        readable_output = tableToMarkdown(f"WHOIS data for {hostname}", res)
        command_results = CommandResults(
            outputs_prefix="SecurityTrails.Domain", outputs_key_field="name", outputs=res, readable_output=readable_output
        )
        return_results(command_results)
        contacts = res.get("contacts", [])
        domain_data = {
            "Name": hostname,
            "UpdatedDate": res.get("updatedDate"),
            "DomainStatus": res.get("status"),
            "WHOIS": {
                "DomainStatus": res.get("status"),
                "CreationDate": res.get("createdDate"),
                "UpdatedDate": res.get("updatedDate"),
                "ExpirationDate": res.get("expiresDate"),
                "Registrar": {"Name": res.get("registrarName")},
            },
        }
        if res.get("nameServers", None):
            name_servers = ", ".join(x for x in res.get("nameServers", []))
            domain_data["NameServers"] = name_servers
            domain_data["WHOIS"]["NameServers"] = name_servers

        # Find the admin contact
        admin = None
        for contact in contacts:
            if (contact.get("type").lower()).startswith("admin"):
                admin = contact
                break
        if admin:
            domain_data["Admin"] = {
                "Country": admin.get("country", None),
                "Email": admin.get("email", None),
                "Name": admin.get("name", None),
                "Phone": admin.get("telephone", None),
            }
        create_standard_domain_context(domain_data=domain_data)

    elif command == "securitytrails-get-ip-whois":
        ip_address = args.get("ipaddress")
        res = client.get_whois(query_type="ip", hostname=ip_address)
        res = res.get("record", {})
        res = {k: v for k, v in res.items() if k not in removed_keys}
        res["ip"] = ip_address
        readable_output = tableToMarkdown(f"WHOIS data for {ip_address}", res)
        command_results = CommandResults(
            outputs_prefix="SecurityTrails.IP", outputs_key_field="ip", outputs=res, readable_output=readable_output
        )

        return_results(command_results)
        ip_data = {"Address": ip_address}
        create_standard_ip_context(ip_data=ip_data)


def domain_search_command(client, args):
    include_ips = argToBoolean(args.get("include_ips", "false"))
    page = int(args.get("page", 1))
    scroll = args.get("include_ips", "false") == "true"
    query = args.get("query", None)
    filter = args.get("filter", None)
    if not query and not filter:
        return_error("You must provide at least a query or a filter")
    params = {"include_ips": include_ips, "page": page, "scroll": scroll}
    body = {}
    if query:
        body["query"] = query
    elif filter:
        body["filter"] = filter

    res = client.query(query_type="domain_search", params=params, body=body)
    records = res.get("records")
    record_count = res.get("record_count")
    md = tableToMarkdown(f"Domain DSL Search Results ({record_count} record(s)):", records)
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain.Search", outputs_key_field="hostname", outputs=records, readable_output=md
    )
    return_results(command_results)


def domain_statistics_command(client, args):
    query = args.get("query", None)
    filter = args.get("filter", None)
    if not query and not filter:
        return_error("You must provide at least a query or a filter")
    body = {}
    if query:
        body["query"] = query
    elif filter:
        body["filter"] = filter

    res = client.query(query_type="domain_stats", body=body)
    res = {k: v for k, v in res.items() if k not in removed_keys}

    top_orgs = res.get("top_organizations", [])
    tld_count = res.get("tld_count", 0)
    hostname_count = res.get("hostname_count", {})
    domain_count = res.get("domain_count", 0)
    table_data = {
        "Top Organizations Count": len(top_orgs),
        "TLD Count": tld_count,
        "Hostname Count": hostname_count,
        "Domain Count": domain_count,
    }
    md = tableToMarkdown("Domain Statistics:", table_data)
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain.Search.DomainStats", outputs_key_field="hostname", outputs=res, readable_output=md
    )
    return_results(command_results)


def associated_domains_command(client, args):
    hostname = args.get("hostname")
    page = args.get("page", 1)

    res = client.associated_domains(hostname=hostname, page=page)
    records = res.get("records", [])
    record_count = res.get("record_count", 0)
    table_data = {
        "Count": record_count,
        "Domains": ", ".join([x.get("hostname") for x in records]),
        "Current Page": page,
        "Total Pages": res.get("meta", {}).get("total_pages", 1),
    }
    md = tableToMarkdown(f"{hostname} Associated Domains:", table_data, ["Count", "Current Page", "Total Pages", "Domains"])
    output_data = {"name": hostname, "associated_domains": records, "associated_domain_count": record_count}
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain", outputs_key_field="name", outputs=output_data, readable_output=md
    )
    return_results(command_results)


def get_ssl_certificates(client, args):
    hostname = args.get("hostname")
    include_subdomains = args.get("include_subdomains", "false") == "true"
    status = args.get("status", "valid")
    page = None
    params = {"include_subdomains": include_subdomains, "status": status}
    if "page" in args:
        page = int(args.get("page", 1))
        params["page"] = page
    query_type = "paged" if page else "stream"
    res = client.get_ssl_certificates(query_type=query_type, hostname=hostname, params=params)
    records = res.get("records", [])
    # record_count = res.get('record_count', 0)
    table_data = [
        {
            "Subject Key ID": x.get("subject_key_id"),
            "Subject Common Name": x.get("subject", {}).get("common_name"),
            "Subject Alternative Names": ", ".join(list(x.get("subject", {}).get("alt_names", []))),
            "Serial Number": x.get("serial_number"),
            "Public Key Type": x.get("public_key", {}).get("key_type"),
            "Public Key": x.get("public_key", {}).get("key"),
            "Public Key Bit Length": x.get("public_key", {}).get("bit_length"),
            "Precert": x.get("precert"),
            "Not Before": x.get("not_before"),
            "Not After": x.get("not_after"),
            "Issuer Organization": ",".join(x.get("issuer", {}).get("organization")),
            "Issuer Country": ",".join(x.get("issuer", {}).get("country")),
            "Issuer Common Name": x.get("issuer", {}).get("common_name"),
            "ID": x.get("id"),
            "Fingerprints": x.get("fingerprints"),
            "DNS Names": ",".join(x.get("dns_names")),
        }
        for x in records
    ]

    md = tableToMarkdown(
        f"SSL Certificates for {hostname}",
        table_data,
        [
            "ID",
            "Subject Key ID",
            "Subject Common Name",
            "Subject Alternative Names",
            "Serial Number",
            "Public Key Type",
            "Public Key",
            "Public Key Bit Length",
            "Precert",
            "Not Before",
            "Not After",
            "Issuer Organization",
            "Issuer Country",
            "Issuer Common Name",
            "Fingerprints",
            "DNS Names",
        ],
    )
    output_data = {"name": hostname, "ssl_certiticates": records}
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain", outputs_key_field="name", outputs=output_data, readable_output=md
    )
    return_results(command_results)


#################################
# History endpoints
#################################


def get_dns_history_command(client, args):
    hostname = args.get("hostname")
    record_type = args.get("type")
    page = int(args.get("page", 1))
    res = client.get_dns_history(hostname=hostname, record_type=record_type, page=page)
    res = {k: v for k, v in res.items() if k not in removed_keys}
    records_list = []

    if record_type == "a":
        pull_field = "ip"
    elif record_type == "aaaa":
        pull_field = "ipv6"
    elif record_type == "mx":
        pull_field = "host"
    elif record_type == "ns":
        pull_field = "nameserver"
    elif record_type == "soa":
        pull_field = "email"
    elif record_type == "txt":
        pull_field = "value"
    else:
        pull_field = ""
        demisto.debug(f"There is no matching value for {record_type=}. {pull_field=}")
    records = res.get("records", {})
    for record in records:
        for value in record.get("values"):
            if pull_field in value:
                records_list.append({"Record Type": record_type, "Value(s)": value.get(pull_field)})
    readable_output = tableToMarkdown(f"DNS history for {hostname}:", records_list)

    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain",
        outputs_key_field="name",
        outputs={
            "name": hostname,
            f"{record_type}_history_records": res.get("records"),
            f"{record_type}_history_record_pages": res.get("pages", 1),
        },
        readable_output=readable_output,
    )
    return_results(command_results)

    latest_record = res.get("records", [])[0]
    values = latest_record.get("values", [])
    values = [values] if type(values) is dict else values
    # hosts = [x['host'] for x in values if "host" in x]
    ipv4 = [x["ip"] for x in values if "ip" in x]
    ipv6 = [x["ip"] for x in values if "ipv6" in x]
    nameservers = [x["nameserver"] for x in values if "nameserver" in x]

    domain_data = {"Name": hostname}

    if nameservers:
        domain_data["NameServers"] = ", ".join(nameservers)

    create_standard_domain_context(domain_data=domain_data)

    if ipv4:
        [create_standard_ip_context({"Address": x}) for x in ipv4]
    if ipv6:
        [create_standard_ip_context({"Address": x}) for x in ipv6]


def get_whois_history_command(client, args):
    hostname = args.get("hostname")
    page = int(args.get("page", 1))
    res = client.get_whois_history(hostname=hostname, page=page)
    readable_output = tableToMarkdown(f"WHOIS history for {hostname}:", res.get("items"))
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.Domain",
        outputs_key_field="name",
        outputs={"name": hostname, "WHOIS_history": res.get("items", []), "WHOIS_history_count": res.get("count", 0)},
        readable_output=readable_output,
    )
    return_results(command_results)

    domain_data = {"Name": hostname}

    contacts = res.get("items", [])[0].get("contact") if res.get("items", None) else []
    admin_contact = [x for x in contacts if x.get("type", None) == "administrativeContact"]
    admin_contact = admin_contact[0] if admin_contact else None
    registrant_contact = [x for x in contacts if x.get("type", None) == "registrant"]
    registrant_contact = registrant_contact[0] if registrant_contact else None
    registrar_contact = admin_contact if admin_contact else None

    whois_objects = []

    for x in res.get("items", []):
        whois_object = {
            "DomainStatus": ", ".join(x.get("status", [])),
            "NameServers": ", ".join(x.get("nameServers", [])),
            "CreationDate": datetime.fromtimestamp(x.get("createdDate") / 1000).strftime("%Y-%m-%dT%H:%M:%SZ")
            if x.get("createdDate", None)
            else None,
            "UpdatedDate": datetime.fromtimestamp(x.get("updatedDate") / 1000).strftime("%Y-%m-%dT%H:%M:%SZ")
            if x.get("updatedDate", None)
            else None,
            "ExpirationDate": datetime.fromtimestamp(x.get("expiresDate") / 1000).strftime("%Y-%m-%dT%H:%M:%SZ")
            if x.get("expiresDate", None)
            else None,
        }
        if admin_contact:
            whois_object["Admin"] = {  # type: ignore
                "Name": admin_contact.get("name"),
                "Email": admin_contact.get("email"),
                "Phone": admin_contact.get("telephone"),
            }
        if registrant_contact:
            whois_object["Registrant"] = {  # type: ignore
                "Name": registrant_contact.get("name"),
                "Email": registrant_contact.get("email"),
                "Phone": registrant_contact.get("telephone"),
            }
        if registrar_contact:
            whois_object["Registrar"] = {  # type: ignore
                "Name": registrar_contact.get("name"),
                "Email": registrar_contact.get("email"),
                "Phone": registrar_contact.get("telephone"),
            }
        whois_objects.append(whois_object)

    if len(whois_objects) > 0:
        domain_data["WHOIS/History"] = whois_objects
    create_standard_domain_context(domain_data=domain_data)


#################################
# IPs endpoints
#################################


def get_ip_neighbors_command(client, args):
    ipaddress = args.get("ipaddress")
    res = client.get_ip_neighbors(ipaddress=ipaddress)
    readable_output = tableToMarkdown(
        f"IP neighbors for {ipaddress}:",
        [
            {
                "IP": x.get("ip", ""),
                "Hostnames": x.get("hostnames", None),
                "Sites": x.get("sites", 0),
                "Ports": x.get("ports", None),
                "Active Egress": x.get("active_egress"),
            }
            for x in res
        ],
        ["IP", "Hostnames", "Sites", "Ports", "Active Egress"],
    )
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.IP",
        outputs_key_field="ip",
        outputs={"ip": ipaddress, "blocks": res},
        readable_output=readable_output,
    )
    return_results(command_results)
    create_standard_ip_context(ip_data=[{"Address": x.get("ip").split("/")[0]} for x in res])


def ip_search_command(client, args):
    page = arg_to_number(args.get("page", 1))
    query = args.get("query", None)
    params = {"page": page}
    body = {"query": query}
    res = client.query(query_type="ip_search", params=params, body=body)
    records = res.get("records")
    record_count = res.get("record_count")
    md = tableToMarkdown(f"IP DSL Search Results ({record_count} record(s)):", records)
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.IP.Search", outputs_key_field="ip", outputs=records, readable_output=md
    )
    return_results(command_results)
    create_standard_ip_context(
        ip_data=[
            {"Address": x.get("ip"), "Hostname": x.get("ptr"), "Ports": ", ".join([str(y["port"]) for y in x.get("ports")])}
            for x in records
        ]
    )


def ip_statistics_command(client, args):
    query = args.get("query")
    body = {"query": query}

    res = client.query(query_type="ip_stats", body=body)
    res = {k: v for k, v in res.items() if k not in removed_keys}

    top_ptrs = res.get("top_ptr_patterns", [])
    ports = res.get("ports", [])
    total = res.get("total", {}).get("value")
    table_data = {"Top PTRs Count": len(top_ptrs), "Ports": len(ports), "Total": total}
    md = tableToMarkdown("IP Statistics:", table_data)
    command_results = CommandResults(outputs_prefix="SecurityTrails.IP.Search.IPStats", outputs=res, readable_output=md)
    return_results(command_results)


def get_useragents_command(client, args):
    ip_address = args.get("ipaddress")
    page = arg_to_number(args.get("page", 1))
    params = {"page": page}
    res = client.get_useragents(ip_address=ip_address, params=params)
    records = res.get("records", [])
    record_count = res.get("record_count", 0)
    table_data = [
        {
            "User Agent": x.get("user_agent"),
            "OS Name": x.get("os", {}).get("name"),
            "OS Platform": x.get("os", {}).get("platform"),
            "OS Version": x.get("os", {}).get("version"),
            "Browser Family": x.get("browser_family"),
            "Last Seen": x.get("lastseen"),
            "Device Type": x.get("device", {}).get("type"),
            "Device Brand": x.get("device", {}).get("brand"),
            "Device Model": x.get("device", {}).get("model"),
            "Client Type": x.get("client", {}).get("type"),
            "Client Name": x.get("client", {}).get("name"),
            "Client Version": x.get("client", {}).get("version"),
            "Client Engine": x.get("client", {}).get("engine"),
            "Client Engine Verison": x.get("client", {}).get("engine_version"),
        }
        for x in records
    ]
    md = tableToMarkdown(
        f"User Agents for {ip_address}:",
        table_data,
        [
            "User Agent",
            "OS Name",
            "OS Platform",
            "OS Version",
            "Browser Family",
            "Last Seen",
            "Device Type",
            "Device Brand",
            "Device Model",
            "Client Type",
            "Client Name",
            "Client Version",
            "Client Engine",
            "Client Engine Verison",
        ],
    )
    output_data = {"ip": ip_address, "useragents": records, "useragent_records_count": record_count}
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.IP", outputs_key_field="ip", outputs=output_data, readable_output=md
    )
    return_results(command_results)


#################################
# Query endpoints
#################################


def query_sql_command(client, args):
    sql = args.get("sql")
    timeout = int(args.get("timeout", "20"))
    query = {"query": sql}
    res = client.sql(sql=query, timeout=timeout)
    total = res.get("total", {}).get("value")
    pages = 0
    if total:
        pages = total // 100
    output = {
        "total": res.get("total", {}).get("value"),
        "pages": pages,
        "records": res.get("records"),
        "id": res.get("id"),
        "query": res.get("query"),
    }
    readable_output = tableToMarkdown("SQL Query Results:", output)
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.SQL", outputs_key_field=["query", "id"], outputs=output, readable_output=readable_output
    )
    return_results(command_results)


def query_sql_get_next_command(client, args):
    next_id = str(args.get("id"))
    timeout = int(args.get("timeout", "20"))
    res = client.sql_next(next_id=next_id, timeout=timeout)
    output = {
        "total": res.get("total", {}).get("value"),
        "records": res.get("records"),
        "id": res.get("id"),
        "query": res.get("query"),
    }
    readable_output = tableToMarkdown("SQL Query Results:", output)
    command_results = CommandResults(
        outputs_prefix="SecurityTrails.SQL", outputs_key_field=["query", "id"], outputs=output, readable_output=readable_output
    )
    return_results(command_results)


def test_module(client):
    results = client._http_request("GET", "ping", ok_codes=(200, 403))
    if "success" in results:
        return "ok"
    else:
        return results.get("message")


""" MAIN FUNCTION """


def main() -> None:
    params = demisto.params()
    args = demisto.args()

    api_key = params.get("apikey")
    timeout = int(params.get("timeout", "10"))
    verify_certificate = not params.get("insecure", False)
    proxy = params.get("proxy", False)
    base_url = "https://api.securitytrails.com/v1/"

    commands = {
        "securitytrails-sql-query": query_sql_command,
        "securitytrails-sql-get-next": query_sql_get_next_command,
        "securitytrails-get-subdomains": domains_subdomains_command,
        "securitytrails-get-domain-details": domain_details_command,
        "securitytrails-get-tags": get_domain_tags_command,
        "securitytrails-get-company-details": get_company_details_command,
        "securitytrails-get-company-associated-ips": get_company_associated_ips_command,
        "securitytrails-get-domain-whois": get_whois_command,
        "securitytrails-get-dns-history": get_dns_history_command,
        "securitytrails-get-whois-history": get_whois_history_command,
        "securitytrails-get-ip-neighbors": get_ip_neighbors_command,
        "securitytrails-search-domain": domain_search_command,
        "securitytrails-statistics-domain": domain_statistics_command,
        "securitytrails-get-associated-domains": associated_domains_command,
        # These 2 commands have issues with the response object - error when trying to parse to JSON
        # 'securitytrails-get-ssl-certitficates': get_ssl_certificates,
        # 'securitytrails-get-ssl-certitficates-stream': get_ssl_certificates,
        "securitytrails-search-ip": ip_search_command,
        "securitytrails-statistics-ip": ip_statistics_command,
        "securitytrails-get-ip-whois": get_whois_command,
        "securitytrails-get-useragents": get_useragents_command,
        "domain": domain_command,
    }

    command = demisto.command()
    demisto.debug(f"Command being called is {command}")
    try:
        headers = {"Content-Type": "application/json", "Accept": "application/json", "APIKEY": api_key}
        client = Client(base_url=base_url, verify=verify_certificate, headers=headers, proxy=proxy, timeout=timeout)

        if demisto.command() == "test-module":
            # This is the call made when pressing the integration Test button.
            result = test_module(client)
            return_results(result)

        elif command in commands:
            commands[command](client, args)

    # Log exceptions and return errors
    except Exception as e:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f"Failed to execute {demisto.command()} command.\nError:\n{e!s}")


""" ENTRY POINT """

if __name__ in ("__main__", "__builtin__", "builtins"):
    main()