Have I Been Pwned? V2

Uses the Have I Been Pwned? service to check whether email addresses, domains, or usernames were compromised in previous breaches.

Data Enrichment & Threat Intelligence · Pwned

Details

IDHave I Been Pwned? V2
ProviderTroy Hunt
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Overview


Uses the Have I Been Pwned? service to check whether email addresses, domains, or usernames were compromised in previous breaches. Uses API v3.

Configure Have I Been Pwned? V2 on Cortex XSOAR


  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for Have I Been Pwned? V2.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • API Key
    • Maximum time per request (in seconds)
    • Email Severity: The DBot reputation for compromised emails (SUSPICIOUS or MALICIOUS)
    • Domain Severity: The DBot reputation for compromised domains (SUSPICIOUS or MALICIOUS)
    • Trust any certificate (not secure)
    • Use system proxy settings
  4. Click Test to validate the URLs, token, and connection.

Fetched Incidents Data


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. pwned-email
  2. pwned-domain
  3. email
  4. domain
  5. pwned-username

1. pwned-email


Checks if an email address was compromised.

Base Command

pwned-email

Input
Argument Name Description Required
email Comma-separated list of email addresses to check. Required
Context Output
Path Type Description
Account.Email.Pwned-V2.Compromised.Vendor String For compromised email addresses, the vendor that made the decision.
Account.Email.Pwned-V2.Compromised.Reporters String For compromised email addresses, the reporters for the vendor to make the compromised decision.
Account.Email.Address String The email address.
Email.Malicious.Vendor String For malicious email addresses, the vendor that made the decision.
Email.Malicious.Description String For malicious email addresses, the reason that the vendor made the decision.
DBotScore.Indicator String The indicator that was tested.
DBotScore.Type String The indicator type.
DBotScore.Vendor String Vendor used to calculate the score.
DBotScore.Score Number The actual score.
Command Example

!pwned-email email="michaljordan@gmail.com"

Context Example
{
    "DBotScore": {
        "Vendor": "Have I Been Pwned? V2", 
        "Indicator": "michaljordan@gmail.com", 
        "Score": 2, 
        "Type": "email"
    }, 
    "Account.Email": {
        "Pwned-V2": {
            "Compromised": {
                "Vendor": "Have I Been Pwned? V2", 
                "Reporters": "Canva, Dubsmash, Modern Business Solutions, Straffic, TestGame"
            }
        }, 
        "Address": "michaljordan@gmail.com"
    }
}
Human Readable Output

Have I Been Pwned query for email: michaljordan@gmail.com

Canva (canva.com): 137272116 records breached [Verified breach]

Date: 2019-05-24

In May 2019, the graphic design tool website Canva suffered a data breach that impacted 137 million subscribers. The exposed data included email addresses, usernames, names, cities of residence and passwords stored as bcrypt hashes for users not using social logins. The data was provided to HIBP by a source who requested it be attributed to “JimScott.Sec@protonmail.com”.
Data breached: Email addresses,Geographic locations,Names,Passwords,Usernames

Dubsmash (dubsmash.com): 161749950 records breached [Verified breach]

Date: 2018-12-01

In December 2018, the video messaging service Dubsmash suffered a data breach. The incident exposed 162 million unique email addresses alongside usernames and PBKDF2 password hashes. In 2019, the data appeared listed for sale on a dark web marketplace (along with several other large breaches) and subsequently began circulating more broadly. The data was provided to HIBP by a source who requested it to be attributed to "BenjaminBlue@exploit.im".
Data breached: Email addresses,Geographic locations,Names,Passwords,Phone numbers,Spoken languages,Usernames

Modern Business Solutions (modbsolutions.com): 58843488 records breached [Verified breach]

Date: 2016-10-08

In October 2016, a large Mongo DB file containing tens of millions of accounts was shared publicly on Twitter (the file has since been removed). The database contained over 58M unique email addresses along with IP addresses, names, home addresses, genders, job titles, dates of birth and phone numbers. The data was subsequently attributed to "Modern Business Solutions", a company that provides data storage and database hosting solutions. They’ve yet to acknowledge the incident or explain how they came to be in possession of the data.
Data breached: Dates of birth,Email addresses,Genders,IP addresses,Job titles,Names,Phone numbers,Physical addresses

Straffic (straffic.io): 48580249 records breached [Verified breach]

Date: 2020-02-14

In February 2020, Israeli marketing company Straffic exposed a database with 140GB of personal data. The publicly accessible Elasticsearch database contained over 300M rows with 49M unique email addresses. Exposed data also included names, phone numbers, physical addresses and genders. In their breach disclosure message, Straffic stated that "it is impossible to create a totally immune system, and these things can occur".
Data breached: Email addresses,Genders,Names,Phone numbers,Physical addresses

TestGame (zynga.com): 172869660 records breached [Verified breach]

Date: 2019-09-01

In September 2019, game developer TestGame (the creator of Words with Friends) suffered a data breach. The incident exposed 173M unique email addresses alongside usernames and passwords stored as salted SHA-1 hashes. The data was provided to HIBP by dehashed.com.
Data breached: Email addresses,Passwords,Phone numbers,Usernames

2. pwned-domain


Checks if a domain was compromised.

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

Base Command

pwned-domain

Input
Argument Name Description Required
domain Comma-separated list of domains to check. Required
Context Output
Path Type Description
Domain.Pwned-V2.Compromised.Vendor String For compromised domains, the vendor that made the decision.
Domain.Pwned-V2.Compromised.Reporters String For compromised domains, the reporters for the vendor to make the compromised decision.
Domain.Name String Domain name.
Domain.Malicious.Vendor String For malicious domains, the vendor that made the decision.
Domain.Malicious.Description String For malicious domains, the reason that the vendor made the decision.
DBotScore.Indicator String The indicator that was tested.
DBotScore.Type String The indicator type.
DBotScore.Vendor String Vendor used to calculate the score.
DBotScore.Score Number The actual score.
Command Example

!pwned-domain domain="adobe.com"

Context Example
{
    "Domain": {
        "Pwned-V2": {
            "Compromised": {
                "Vendor": "Have I Been Pwned? V2", 
                "Reporters": "Adobe"
            }
        }, 
        "Name": "adobe.com"
    }, 
    "DBotScore": {
        "Vendor": "Have I Been Pwned? V2", 
        "Indicator": "adobe.com", 
        "Score": 2, 
        "Type": "domain"
    }
}
Human Readable Output

Have I Been Pwned query for domain: adobe.com

Adobe (adobe.com): 152445165 records breached [Verified breach]

Date: 2013-10-04

In October 2013, 153 million Adobe accounts were breached with each containing an internal ID, username, email, encrypted password and a password hint in plain text. The password cryptography was poorly done and many were quickly resolved back to plain text. The unencrypted hints also disclosed much about the passwords adding further to the risk that hundreds of millions of Adobe customers already faced.
Data breached: Email addresses,Password hints,Passwords,Usernames

3. email


Checks if an email address was compromised.

Base Command

email

Input
Argument Name Description Required
email Comma-separated list of email addresses to check. Required
Context Output
Path Type Description
Account.Email.Pwned-V2.Compromised.Vendor String For compromised email addresses, the vendor that made the decision.
Account.Email.Pwned-V2.Compromised.Reporters String For compromised email addresses, the reporters for the vendor to make the compromised decision.
Account.Email.Address String The email address.
Email.Malicious.Vendor String For malicious email addresses, the vendor that made the decision.
Email.Malicious.Description String For malicious email addresses, the reason that the vendor made the decision.
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

!email email="michaljordan@gmail.com"

Context Example
{
    "DBotScore": {
        "Vendor": "Have I Been Pwned? V2", 
        "Indicator": "michaljordan@gmail.com", 
        "Score": 2, 
        "Type": "email"
    }, 
    "Account.Email": {
        "Pwned-V2": {
            "Compromised": {
                "Vendor": "Have I Been Pwned? V2", 
                "Reporters": "Canva, Dubsmash, Modern Business Solutions, Straffic, TestGame"
            }
        }, 
        "Address": "michaljordan@gmail.com"
    }
}
Human Readable Output

Have I Been Pwned query for email: michaljordan@gmail.com

Canva (canva.com): 137272116 records breached [Verified breach]

Date: 2019-05-24

In May 2019, the graphic design tool website Canva suffered a data breach that impacted 137 million subscribers. The exposed data included email addresses, usernames, names, cities of residence and passwords stored as bcrypt hashes for users not using social logins. The data was provided to HIBP by a source who requested it be attributed to “JimScott.Sec@protonmail.com”.
Data breached: Email addresses,Geographic locations,Names,Passwords,Usernames

Dubsmash (dubsmash.com): 161749950 records breached [Verified breach]

Date: 2018-12-01

In December 2018, the video messaging service Dubsmash suffered a data breach. The incident exposed 162 million unique email addresses alongside usernames and PBKDF2 password hashes. In 2019, the data appeared listed for sale on a dark web marketplace (along with several other large breaches) and subsequently began circulating more broadly. The data was provided to HIBP by a source who requested it to be attributed to "BenjaminBlue@exploit.im".
Data breached: Email addresses,Geographic locations,Names,Passwords,Phone numbers,Spoken languages,Usernames

Modern Business Solutions (modbsolutions.com): 58843488 records breached [Verified breach]

Date: 2016-10-08

In October 2016, a large Mongo DB file containing tens of millions of accounts was shared publicly on Twitter (the file has since been removed). The database contained over 58M unique email addresses along with IP addresses, names, home addresses, genders, job titles, dates of birth and phone numbers. The data was subsequently attributed to "Modern Business Solutions", a company that provides data storage and database hosting solutions. They’ve yet to acknowledge the incident or explain how they came to be in possession of the data.
Data breached: Dates of birth,Email addresses,Genders,IP addresses,Job titles,Names,Phone numbers,Physical addresses

Straffic (straffic.io): 48580249 records breached [Verified breach]

Date: 2020-02-14

In February 2020, Israeli marketing company Straffic exposed a database with 140GB of personal data. The publicly accessible Elasticsearch database contained over 300M rows with 49M unique email addresses. Exposed data also included names, phone numbers, physical addresses and genders. In their breach disclosure message, Straffic stated that "it is impossible to create a totally immune system, and these things can occur".
Data breached: Email addresses,Genders,Names,Phone numbers,Physical addresses

TestGame (zynga.com): 172869660 records breached [Verified breach]

Date: 2019-09-01

In September 2019, game developer TestGame (the creator of Words with Friends) suffered a data breach. The incident exposed 173M unique email addresses alongside usernames and passwords stored as salted SHA-1 hashes. The data was provided to HIBP by dehashed.com.
Data breached: Email addresses,Passwords,Phone numbers,Usernames

4. domain


Checks if a domain was compromised.

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 Comma-separated list of domains to check. Required
Context Output
Path Type Description
Domain.Pwned-V2.Compromised.Vendor String For compromised domains, the vendor that made the decision.
Domain.Pwned-V2.Compromised.Reporters String For compromised domains, the reporters for the vendor to make the compromised decision.
Domain.Name String The domain name.
Domain.Malicious.Vendor String For malicious domains, the vendor that made the decision.
Domain.Malicious.Description String For malicious domains, the reason that the vendor made the decision.
DBotScore.Indicator String The indicator that was tested.
DBotScore.Type String The indicator type.
DBotScore.Vendor String Vendor used to calculate the score.
DBotScore.Score Number The actual score.
Command Example

!domain domain="adobe.com"

Context Example
{
    "Domain": {
        "Pwned-V2": {
            "Compromised": {
                "Vendor": "Have I Been Pwned? V2", 
                "Reporters": "Adobe"
            }
        }, 
        "Name": "adobe.com"
    }, 
    "DBotScore": {
        "Vendor": "Have I Been Pwned? V2", 
        "Indicator": "adobe.com", 
        "Score": 2, 
        "Type": "domain"
    }
}
Human Readable Output

Have I Been Pwned query for domain: adobe.com

Adobe (adobe.com): 152445165 records breached [Verified breach]

Date: 2013-10-04

In October 2013, 153 million Adobe accounts were breached with each containing an internal ID, username, email, encrypted password and a password hint in plain text. The password cryptography was poorly done and many were quickly resolved back to plain text. The unencrypted hints also disclosed much about the passwords adding further to the risk that hundreds of millions of Adobe customers already faced.
Data breached: Email addresses,Password hints,Passwords,Usernames

5. pwned-username


Checks if a username was compromised.

Base Command

pwned-username

Input
Argument Name Description Required
username Comma-separated list of usernames to check. Required
Context Output
Path Type Description
Username.Pwned-V2.Compromised.Vendor String For compromised usernames, the vendor that made the decision.
Username.Pwned-V2.Compromised.Reporters String For compromised usernames, the reporters for the vendor to make the compromised decision.
Username.Name String The username name.
Username.Malicious.Vendor String For malicious usernames, the vendor that made the decision.
Username.Malicious.Description String For malicious usernames, the reason that the vendor made the decision.
Command Example

!pwned-username username="jondon"

Context Example
{
    "Domain": {
        "Pwned-V2": {
            "Compromised": {
                "Vendor": "Have I Been Pwned? V2", 
                "Reporters": "Gawker, hackforums.net"
            }
        }, 
        "Name": "jondon"
    }, 
    "DBotScore": {
        "Vendor": "Have I Been Pwned? V2", 
        "Indicator": "jondon", 
        "Score": 2, 
        "Type": "domain"
    }
}
Human Readable Output

Have I Been Pwned query for username: jondon

Gawker (gawker.com): 1247574 records breached [Verified breach]

Date: 2010-12-11

In December 2010, Gawker was attacked by the hacker collective "Gnosis" in retaliation for what was reported to be a feud between Gawker and 4Chan. Information about Gawkers 1.3M users was published along with the data from Gawker’s other web presences including Gizmodo and Lifehacker. Due to the prevalence of password reuse, many victims of the breach then had their Twitter accounts compromised to send Acai berry spam.
Data breached: Email addresses,Passwords,Usernames

hackforums.net (hackforums.net): 191540 records breached [Verified breach]

Date: 2011-06-25

In June 2011, the hacktivist group known as “LulzSec” leaked one final large data breach they titled “50 days of lulz”. The compromised data came from sources such as AT&T, Battlefield Heroes and the hackforums.net website. The leaked Hack Forums data included credentials and personal information of nearly 200,000 registered forum users.
Data breached: Dates of birth,Email addresses,Instant messenger identities,IP addresses,Passwords,Social connections,Spoken languages,Time zones,User website URLs,Usernames,Website activity

Configuration parameters

  • api_key — API Key
  • credentials_api_key
  • max_retry_time — Maximum time per request (in seconds)
  • default_dbot_score_email — Email Severity: The DBot reputation for compromised emails (SUSPICIOUS or MALICIOUS)
  • default_dbot_score_domain — Domain Severity: The DBot reputation for compromised domains (SUSPICIOUS or MALICIOUS)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • integrationReliability — Source Reliability
  • feedExpirationPolicy
  • feedExpirationInterval

Commands (5)

  • domain

    Checks if a domain was compromised.

  • email

    Checks if an email address was compromised.

  • pwned-domain

    Checks if a domain was compromised.

  • pwned-email

    Checks if an email address was compromised.

  • pwned-username

    Checks if a username was compromised.

from CommonServerPython import *

""" IMPORTS """

import re

import urllib3

# Disable insecure warnings
urllib3.disable_warnings()

params = demisto.params()
VENDOR = "Have I Been Pwned? V2"
MAX_RETRY_ALLOWED = params.get("max_retry_time", -1)
API_KEY = params.get("credentials_api_key", {}).get("password") or params.get("api_key")
USE_SSL = not params.get("insecure", False)
BASE_URL = "https://haveibeenpwned.com/api/v3"
DEFAULT_DBOT_SCORE_EMAIL = 2 if params.get("default_dbot_score_email") == "SUSPICIOUS" else 3
DEFAULT_DBOT_SCORE_DOMAIN = 2 if params.get("default_dbot_score_domain") == "SUSPICIOUS" else 3

""" GLOBALS/PARAMS """

VENDOR = "Have I Been Pwned? V2"
SUFFIXES = {
    "email": "/breachedaccount/",
    "domain": "/breaches?domain=",
    "username": "/breachedaccount/",
    "paste": "/pasteaccount/",
    "email_truncate_verified": "?truncateResponse=false&includeUnverified=true",
    "domain_truncate_verified": "&truncateResponse=false&includeUnverified=true",
    "username_truncate_verified": "?truncateResponse=false&includeUnverified=true",
}

RETRIES_END_TIME = datetime.min

""" HELPER FUNCTIONS """


def error_handler(res):
    if res.status_code == 404:
        raise DemistoException("No result found.")
    else:
        demisto.error(f"Error in API call to Pwned Integration [{res.status_code}]. Full text: {res.text}")
    return_error(f"Error in API call to Pwned Integration [{res.status_code}] - {res.reason}")


def http_request(method, url_suffix, params=None, data=None):
    headers = {
        "hibp-api-key": API_KEY,
        "user-agent": "DBOT-API",
        "Content-Type": "application/json",
        "Accept": "application/json",
    }
    try:
        return generic_http_request(
            method=method,
            server_url=BASE_URL,
            verify=USE_SSL,
            client_headers=headers,
            url_suffix=url_suffix,
            data=data,
            params=params,
            error_handler=error_handler,
            status_list_to_retry=[429],
            retries=5,
            ok_codes=(200,),
        )
    except DemistoException:
        return None


def html_description_to_human_readable(breach_description):
    """
    Converting from html description to hr
    :param breach_description: Description of breach from API response
    :return: Description string that altered HTML urls to clickable urls
    for better readability in war-room
    """
    html_link_pattern = re.compile('<a href="(.+?)"(.+?)>(.+?)</a>')
    patterns_found = html_link_pattern.findall(breach_description)
    for link in patterns_found:
        html_actual_address = link[0]
        html_readable_name = link[2]
        link_from_desc = "[" + html_readable_name + "]" + "(" + html_actual_address + ")"
        breach_description = re.sub(html_link_pattern, link_from_desc, breach_description, count=1)
    return breach_description


def data_to_markdown(query_type, query_arg, api_res, api_paste_res=None):
    records_found = False

    md = "### Have I Been Pwned query for " + query_type.lower() + ": *" + query_arg + "*\n"

    if api_res:
        records_found = True
        for breach in api_res:
            verified_breach = "Verified" if breach["IsVerified"] else "Unverified"
            md += (
                "#### "
                + breach["Title"]
                + " ("
                + breach["Domain"]
                + "): "
                + str(breach["PwnCount"])
                + " records breached ["
                + verified_breach
                + " breach]\n"
            )
            md += "Date: **" + breach["BreachDate"] + "**\n\n"
            md += html_description_to_human_readable(breach["Description"]) + "\n"
            md += "Data breached: **" + ",".join(breach["DataClasses"]) + "**\n"

    if api_paste_res:
        records_found = True
        pastes_list = []
        for paste_breach in api_paste_res:
            paste_entry = {
                "Source": paste_breach["Source"],
                "Title": paste_breach["Title"],
                "ID": paste_breach["Id"],
                "Date": "",
                "Amount of emails in paste": str(paste_breach["EmailCount"]),
            }

            if paste_breach["Date"]:
                paste_entry["Date"] = paste_breach["Date"].split("T")[0]

            pastes_list.append(paste_entry)

        md += tableToMarkdown(
            'The email address was found in the following "Pastes":',
            pastes_list,
            ["ID", "Title", "Date", "Source", "Amount of emails in paste"],
        )

    if not records_found:
        md += "No records found"

    return md


def create_dbot_score_dictionary(indicator_value, indicator_type, dbot_score):
    return {
        "Indicator": indicator_value,
        "Type": indicator_type,
        "Vendor": VENDOR,
        "Score": dbot_score,
        "Reliability": demisto.params().get("integrationReliability"),
    }


def create_context_entry(context_type, context_main_value, comp_sites, comp_pastes, malicious_score):
    context_dict = {}  # dict

    if context_type == "email":
        context_dict["Address"] = context_main_value
    else:
        context_dict["Name"] = context_main_value

    context_dict["Pwned-V2"] = {"Compromised": {"Vendor": VENDOR, "Reporters": ", ".join(comp_sites + comp_pastes)}}

    if malicious_score == 3:
        context_dict["Malicious"] = add_malicious_to_context(context_type)

    return context_dict


def add_malicious_to_context(malicious_type):
    return {"Vendor": VENDOR, "Description": "The " + malicious_type + " has been compromised"}


def email_to_entry_context(email, api_email_res, api_paste_res):
    dbot_score = 0
    comp_email = {}  # type: dict
    comp_sites = sorted([item["Title"] for item in api_email_res])
    comp_pastes = sorted({item["Source"] for item in api_paste_res})

    if len(comp_sites) > 0:
        dbot_score = DEFAULT_DBOT_SCORE_EMAIL
        email_context = create_context_entry("email", email, comp_sites, comp_pastes, DEFAULT_DBOT_SCORE_EMAIL)
        comp_email[outputPaths["email"]] = email_context

    comp_email["DBotScore"] = create_dbot_score_dictionary(email, "email", dbot_score)

    return comp_email


def domain_to_entry_context(domain, api_res):
    comp_sites = [item["Title"] for item in api_res]
    comp_sites = sorted(comp_sites)
    comp_domain = {}  # type: dict
    dbot_score = 0

    if len(comp_sites) > 0:
        dbot_score = DEFAULT_DBOT_SCORE_DOMAIN
        domain_context = create_context_entry("domain", domain, comp_sites, [], DEFAULT_DBOT_SCORE_DOMAIN)
        comp_domain[outputPaths["domain"]] = domain_context

    comp_domain["DBotScore"] = create_dbot_score_dictionary(domain, "domain", dbot_score)

    return comp_domain


def set_retry_end_time():
    global RETRIES_END_TIME
    if MAX_RETRY_ALLOWED != -1:
        RETRIES_END_TIME = datetime.now() + timedelta(seconds=int(MAX_RETRY_ALLOWED))


""" COMMANDS + REQUESTS FUNCTIONS """


def test_module(args_dict):
    """
    If the http request was successful the test will return OK
    :return: 3 arrays of outputs
    """
    http_request("GET", SUFFIXES.get("username", "") + "test")
    return ["ok"], [None], [None]


def pwned_email_command(args_dict):
    """
     Executing the pwned request for emails list, in order to support list input, the function returns 3 lists of outputs
    :param args_dict: the demisto argument - in this case the email list is needed
    :return: 3 arrays of outputs
    """
    email_list = argToList(args_dict.get("email", ""))
    api_email_res_list, api_paste_res_list = pwned_email(email_list)

    md_list = []
    ec_list = []

    for email, api_email_res, api_paste_res in zip(email_list, api_email_res_list, api_paste_res_list):
        md_list.append(data_to_markdown("Email", email, api_email_res, api_paste_res))
        ec_list.append(email_to_entry_context(email, api_email_res or [], api_paste_res or []))
    return md_list, ec_list, api_email_res_list


def pwned_email(email_list):
    """
    Executing the http requests
    :param email_list: the email list that needed for the http requests
    :return: 2 arrays of http requests outputs
    """
    api_email_res_list = []
    api_paste_res_list = []

    for email in email_list:
        email_suffix = SUFFIXES.get("email") + email + SUFFIXES.get("email_truncate_verified")
        paste_suffix = SUFFIXES.get("paste") + email
        api_email_res_list.append(http_request("GET", url_suffix=email_suffix))
        api_paste_res_list.append(http_request("GET", url_suffix=paste_suffix))

    return api_email_res_list, api_paste_res_list


def pwned_domain_command(args_dict):
    """
     Executing the pwned request for domains list, in order to support list input, the function returns 3 lists of
     outputs
    :param args_dict: the demisto argument - in this case the domain list is needed
    :return: 3 arrays of outputs
    """
    domain_list = argToList(args_dict.get("domain", ""))
    api_res_list = pwned_domain(domain_list)

    md_list = []
    ec_list = []

    for domain, api_res in zip(domain_list, api_res_list):
        md_list.append(data_to_markdown("Domain", domain, api_res))
        ec_list.append(domain_to_entry_context(domain, api_res or []))
    return md_list, ec_list, api_res_list


def pwned_domain(domain_list):
    """
    Executing the http request
    :param domain_list: the domains list that needed for the http requests
    :return: an array of http requests outputs
    """
    api_res_list = []
    for domain in domain_list:
        suffix = SUFFIXES.get("domain") + domain + SUFFIXES.get("domain_truncate_verified")
        api_res_list.append(http_request("GET", url_suffix=suffix))
    return api_res_list


def pwned_username_command(args_dict):
    """
    Executing the pwned request for usernames list, in order to support list input, the function returns 3 lists of
    outputs
    :param args_dict: the demisto argument - in this case the username list is needed
    :return: 3 arrays of outputs
    """
    username_list = argToList(args_dict.get("username", ""))
    api_res_list = pwned_username(username_list)

    md_list = []
    ec_list = []

    for username, api_res in zip(username_list, api_res_list):
        md_list.append(data_to_markdown("Username", username, api_res))
        ec_list.append(domain_to_entry_context(username, api_res or []))
    return md_list, ec_list, api_res_list


def pwned_username(username_list):
    """
    Executing the http request
    :param username_list: the username list that needed for the http requests
    :return: an array of http requests outputs
    """
    api_res_list = []
    for username in username_list:
        suffix = SUFFIXES.get("username") + username + SUFFIXES.get("username_truncate_verified")
        api_res_list.append(http_request("GET", url_suffix=suffix))
    return api_res_list


def main():  # pragma: no cover
    if not API_KEY:
        raise DemistoException("API key must be provided.")
    command = demisto.command()
    LOG(f"Command being called is: {command}")
    try:
        handle_proxy()
        set_retry_end_time()
        commands = {
            "test-module": test_module,
            "email": pwned_email_command,
            "pwned-email": pwned_email_command,
            "domain": pwned_domain_command,
            "pwned-domain": pwned_domain_command,
            "pwned-username": pwned_username_command,
        }
        if command in commands:
            md_list, ec_list, api_email_res_list = commands[command](demisto.args())
            for md, ec, api_paste_res in zip(md_list, ec_list, api_email_res_list):
                return_outputs(md, ec, api_paste_res)

    # Log exceptions
    except Exception as e:
        return_error(str(e))


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