DeHashed
This integration allows you to check if your personal information such as your email, username, or password is being compromised.
Utilities · DeHashed
Details
| ID | DeHashed |
|---|---|
| Provider | DeHashed |
| Category | Utilities |
| From Version | 5.5.0 |
| Docker Image | demisto/fastapi:0.125.0.10158186 |
| Supported Modules | Agentix XSIAM |
README
This integration allows you to check if your personal information such as your email, username, or password is being compromised.
Configure DeHashed in Cortex
| Parameter | Description | Required |
|---|---|---|
| API Key | For generating an API Key, see https://www.dehashed.com. | True |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False | |
| Email Severity: The DBot reputation for compromised emails (SUSPICIOUS or MALICIOUS) | False | |
| Source Reliability | Reliability of the source providing the intelligence data. | 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.
dehashed-search
Performs a search to check if information is compromised.
Base Command
dehashed-search
Input
| Argument Name | Description | Required |
|---|---|---|
| asset_type | If you select the “all fields” option, the search is performed on all fields with the specified value entered in the “value” argument, and you don’t have to pass the “operation” argument. Possible values are: email, ip_address, username, hashed_password, name, vin, address, phone, all_fields. | Required |
| value | The searched value. | Required |
| operation | The search operator. Possible values are: is, regex. | Required |
| page | The number of page to return. Each page contains up to 5,000 results. | Optional |
| results_from | Starting result number to display. Default is 1. | Optional |
| results_to | Ending result number to display. Default is 50. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| DeHashed.Search.Id | String | ID of the object. |
| DeHashed.Search.Email | String | Email address of the object. |
| DeHashed.Search.Username | String | Username of the object. |
| DeHashed.Search.Password | String | Password of the object. |
| DeHashed.Search.HashedPassword | String | Hashed password of the object. |
| DeHashed.Search.Name | String | Name of the object. |
| DeHashed.Search.Address | String | Address of the object. |
| DeHashed.Search.IpAddress | String | IP address of the object. |
| DeHashed.Search.Phone | Number | Phone number of the object. |
| DeHashed.Search.Dob | String | Date of birth. |
| DeHashed.Search.LicensePlate | String | License plate. |
| DeHashed.Search.Company | String | Company name. |
| DeHashed.Search.Url | String | Associated URL. |
| DeHashed.Search.Social | String | Social media handle. |
| DeHashed.Search.CryptocurrencyAddress | String | Cryptocurrency address. |
| DeHashed.Search.DatabaseName | String | Source database/breach name (drives DBot score). |
| Dehashed.LastQuery.ResultsFrom | Number | The value of the “results_from” argument that was passed in the last query. |
| Dehashed.LastQuery.ResultsTo | Unknown | The value of the “results_to” argument that was passed in the last query. |
| Dehashed.LastQuery.TotalResults | Number | The total number of entries returned from the last query. |
| Dehashed.LastQuery.DisplayedResults | Number | The number of entries that were displayed in Cortex XSOAR from the last query. |
Command Example
!dehashed-search asset_type=all_fields operation=contains value=or-gal@gmail.com results_to=4 results_from=1
!dehashed-search asset_type=email operation=is value=or-gal@gmail.com page=1
!dehashed-search asset_type=name operation=contains value=gal,gil,test1 results_from=2 results_to=30 page=3
!dehashed-search asset_type=name operation=regex value=joh?n(ath[oa]n)
Human Readable Output
Checks if an email address was compromised.
Base Command
email
Input
| Argument Name | Description | Required |
|---|---|---|
| The email address to check. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| DeHashed.Search.Id | String | ID of the object. |
| DeHashed.Search.Email | String | Email address of the object. |
| DeHashed.Search.Username | String | Username of the object. |
| DeHashed.Search.Password | String | Password of the object. |
| DeHashed.Search.HashedPassword | String | Hashed password of the object. |
| DeHashed.Search.Name | String | Name of the object. |
| DeHashed.Search.Address | String | Address of the object. |
| DeHashed.Search.IpAddress | String | IP address of the object. |
| DeHashed.Search.Phone | Number | Phone number of the object. |
| DeHashed.Search.Dob | String | Date of birth. |
| DeHashed.Search.LicensePlate | String | License plate. |
| DeHashed.Search.Company | String | Company name. |
| DeHashed.Search.Url | String | Associated URL. |
| DeHashed.Search.Social | String | Social media handle. |
| DeHashed.Search.CryptocurrencyAddress | String | Cryptocurrency address. |
| DeHashed.Search.DatabaseName | String | Source database/breach name (drives DBot score). |
| 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. |
| DBotScore.Reliability | String | Reliability of the source providing the intelligence data. |
Command Example
!email email=or-gal@gmail.com
Human Readable Output
Configuration parameters
credentials— (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settingsemail_dbot_score— Email Severity: The DBot reputation for compromised emails (SUSPICIOUS or MALICIOUS)integration_reliability— Source Reliability
Commands (2)
-
dehashed-searchPerforms a search to check if information is compromised.
-
emailChecks if an email address was compromised.
# ruff: noqa: F403, F405 import demistomock as demisto from CommonServerPython import * from CommonServerUserPython import * from typing import Any, Literal from pydantic import Field, SecretStr, root_validator, validator # pylint: disable=no-name-in-module from ContentClientApiModule import * from BaseContentApiModule import * # region Constants BASE_CONTEXT_OUTPUT_PREFIX = "DeHashed" BASE_URL = "https://api.dehashed.com/v2/" REQUEST_PAGE_SIZE = 5000 MAX_REQUEST_PAGE_SIZE = 10_000 # DBotScore severity labels (instance config values). SCORE_SUSPICIOUS_LABEL = "SUSPICIOUS" SCORE_MALICIOUS_LABEL = "MALICIOUS" AssetType = Literal[ "email", "ip_address", "username", "hashed_password", "name", "vin", "address", "phone", "all_fields", ] Operation = Literal["is", "regex"] # endregion # region Helpers def _build_search_query(asset_type: str, value: str, operation: str) -> str: """ Builds the DeHashed query string from the user-provided ``asset_type``, ``value``, and ``operation``. Args: asset_type (str): The asset type (e.g. "email", "all_fields"). values (str): The value to search for. operation (str): The operation - "is" or "regex". Returns: str: The constructed query string. """ if not value: raise DemistoException('This command must get "value" as an argument.') query_value = f'"{value}"' if operation == "is" else value return query_value if asset_type == "all_fields" else f"{asset_type}:{query_value}" def _filter_results( entries: list[dict[str, Any]], results_from: int, results_to: int, ) -> tuple[list[dict[str, Any]], int, int]: """ Performs the client-side slicing of search entries given a 1-based inclusive range. Args: entries (list[dict[str, Any]]): The full list of entries from the API. results_from (int): 1-based start index (inclusive). results_to (int): 1-based end index (inclusive). Returns: tuple[list[dict[str, Any]], int, int]: A tuple of the sliced entries, the resolved ``results_from``, and the resolved ``results_to``. """ total_entries = len(entries) if results_to > total_entries: results_to = total_entries return entries[results_from - 1 : results_to], results_from, results_to def _transform_entry(entry: dict[str, Any]) -> dict[str, Any]: """ Converts a single raw v2 DeHashed entry into the CamelCase-keyed dict matching the v1 context paths. The v2 API returns most identity fields as JSON arrays (e.g. ``email: ["example@example.com"]``, ``phone: ["1", "2"]``), but the v1 context paths consumed by existing playbooks expect scalars — so list-typed fields are flattened into comma-separated strings while non-list fields are passed through unchanged. Empty / missing fields are omitted so playbook conditionals behave the same as in v1. Rules: - List-typed fields (e.g. ``email``, ``ip_address``, ``phone``, ``hashed_password``) are flattened into a comma-separated string (``", "`` separator). - Plain string / scalar fields (e.g. ``id``, ``database_name``) are passed through unchanged. - Dict / nested fields (e.g. ``raw_record``) are passed through. - Empty list / ``None`` / empty string fields are omitted from the output. - Keys are converted from ``snake_case`` to ``CamelCase``. Args: entry (dict[str, Any]): A single raw entry from ``response.entries``. Returns: dict[str, Any]: The flattened, CamelCase-keyed entry. """ out: dict[str, Any] = {} for raw_key, value in entry.items(): if isinstance(value, list): non_empty = [str(v) for v in value if v not in ("", None)] if not non_empty: continue value = ", ".join(non_empty) elif value in ("", None): continue out[underscoreToCamelCase(raw_key)] = value return out def compute_score(entries: list[dict[str, Any]], dbot_score_config: str) -> int: """ Computes the DBotScore for an email lookup based on the v2 ``database_name`` field. Args: entries (list[dict[str, Any]]): Raw v2 entries from ``response.entries``. dbot_score_config (str): The configured severity label — ``"SUSPICIOUS"`` or ``"MALICIOUS"``. Returns: int: ``Common.DBotScore.NONE`` (0) when no breach sources are found, ``Common.DBotScore.SUSPICIOUS`` (2) when configured as ``"SUSPICIOUS"``, otherwise ``Common.DBotScore.BAD`` (3). """ sources = [e.get("database_name") for e in entries if e.get("database_name")] if not sources: return Common.DBotScore.NONE if dbot_score_config == SCORE_SUSPICIOUS_LABEL: return Common.DBotScore.SUSPICIOUS # numeric 2 return Common.DBotScore.BAD # numeric 3 — labeled "MALICIOUS" in UI # endregion # region Parameters class Credentials(ContentBaseModel): """Credentials model for API authentication.""" # username field omitted because `hiddenusername: true` in YML password: SecretStr class DehashedParams(BaseParams): """Integration parameters for DeHashed.""" credentials: Credentials email_dbot_score: str = Field("SUSPICIOUS", alias="email_dbot_score") integration_reliability: str = Field("B - Usually reliable", alias="integration_reliability") @property def api_key(self): return self.credentials.password # endregion # region Auth & Client class DehashedAuthHandler(APIKeyAuthHandler): """Custom authentication handler for DeHashed.""" def __init__(self, api_key: SecretStr): super().__init__( key=api_key.get_secret_value(), header_name="Dehashed-Api-Key", ) class DehashedClient(ContentClient): """Client for DeHashed API.""" def __init__(self, params: DehashedParams): auth_handler = DehashedAuthHandler(params.api_key) super().__init__( base_url=BASE_URL, verify=params.verify, proxy=params.proxy, auth_handler=auth_handler, client_name="DehashedClient", ) def general_search( self, query: str, page: int | None = None, size: int | None = None, wildcard: bool | None = None, regex: bool | None = None, de_dupe: bool | None = None, ) -> dict[str, Any]: """ Searches across the DeHashed database with various filtering options. Args: query (str): The search query string. page (int | None): Page number for results pagination. size (int | None): Number of results per page. wildcard (bool | None): Whether to use wildcard matching. regex (bool | None): Whether to use regex matching. de_dupe (bool | None): Whether to remove duplicate results. Returns: dict[str, Any]: The search results. """ url_suffix = "/search" json_data: dict[str, Any] = assign_params( query=query, page=page, size=size, wildcard=wildcard, regex=regex, de_dupe=de_dupe, ) demisto.debug(f"Sending a POST Request to {url_suffix}.") return self.post( url_suffix=url_suffix, json_data=json_data, resp_type="json", ) # endregion # region test-module def test_module(client: DehashedClient) -> str: """ Verifies connectivity with the DeHashed API by issuing a sample search. Args: client (DehashedClient): The DeHashed API client. Returns: str: "ok" if the connection is successful, otherwise an error message. """ try: demisto.debug("[Testing] Testing API connectivity") client.general_search(query="email:example@example.com", page=1, size=1) demisto.debug("[Testing] API connectivity test passed") except Exception as e: demisto.error(traceback.format_exc()) return f"AuthenticationError: Connection failed. Make sure credentials are correctly set. {str(e)}" demisto.debug("[Testing] All tests passed.") return "ok" # endregion # region dehashed-search class DehashedSearchArgs(ContentBaseModel): asset_type: AssetType = Field(alias="asset_type") value: str = Field(alias="value") operation: Operation = Field(alias="operation") page: int = Field(1, alias="page") results_from: int = Field(1, alias="results_from") results_to: int = Field(50, alias="results_to") @validator("page", pre=True, allow_reuse=True) @classmethod def validate_page(cls, v): result = arg_to_number(v) if result is None or result <= 0: raise ValueError('"page" expected to be greater than zero.') return result @validator("results_from", pre=True, allow_reuse=True) @classmethod def validate_results_from(cls, v): result = arg_to_number(v) if result is None or result <= 0: raise ValueError('"results_from" expected to be greater than zero.') return result @validator("results_to", pre=True, allow_reuse=True) @classmethod def validate_results_to(cls, v): result = arg_to_number(v) if result is None or result <= 0: raise ValueError('"results_to" expected to be greater than zero.') return result @root_validator(allow_reuse=True) @classmethod def validate_range(cls, values): results_from = values.get("results_from") results_to = values.get("results_to") if results_to < results_from: raise DemistoException('"results_from" expected to be less than or equal to "results_to"') return values def dehashed_search_command(client: DehashedClient, args: DehashedSearchArgs) -> list[CommandResults] | CommandResults: """ Executes the dehashed-search command. Performs a search against the DeHashed v2 API while preserving the user-facing arguments and outputs of the original DeHashed integration. Args: client (DehashedClient): The DeHashed API client. args (DehashedSearchArgs): The command arguments. Returns: list[CommandResults] | CommandResults: The results of the command execution. """ query_string = _build_search_query(args.asset_type, args.value, args.operation) demisto.debug(f"[dehashed-search] Built query string: {query_string!r}") result = client.general_search( query=query_string, page=args.page, size=REQUEST_PAGE_SIZE, wildcard=None, regex=True if args.operation == "regex" else None, de_dupe=None, ) if not isinstance(result, dict): raise DemistoException(f"Got unexpected output from api: {result}") query_data = result.get("entries") if not query_data: return CommandResults(readable_output="No matching results found") filtered_results, results_from, results_to = _filter_results(query_data, args.results_from, args.results_to) demisto.debug(f"[dehashed-search] Transforming {len(filtered_results)} entries to v1-compat shape.") transformed_entries = [_transform_entry(entry) for entry in filtered_results] last_query = { "ResultsFrom": results_from, "ResultsTo": results_to, "DisplayedResults": len(filtered_results), "TotalResults": result.get("total"), "PageNumber": args.page, } returned_page_size = min(REQUEST_PAGE_SIZE, len(query_data)) readable_output = tableToMarkdown( f"DeHashed Search - got total results: {result.get('total')}, page number: {args.page}" f", page size is: {returned_page_size}. returning results from {results_from} to {results_to}.", transformed_entries, headerTransform=pascalToSpace, ) return [ CommandResults( # NOTE: `(true)` forces a full overwrite of the LastQuery context on each run. outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.LastQuery(true)", outputs=last_query, readable_output=readable_output, raw_response=result, ), CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.Search", outputs_key_field="Id", outputs=transformed_entries, raw_response=result, ), ] # endregion # region email class EmailArgs(ContentBaseModel): email: list[str] = Field(alias="email") @validator("email", pre=True, allow_reuse=True) @classmethod def validate_email(cls, v): return argToList(v) def email_command( client: DehashedClient, args: EmailArgs, email_dbot_score: str, reliability: str | None, ) -> list[CommandResults]: """ Executes the email command. Iterates over each email address in ``args.email`` and performs a separate DeHashed search per email. For each email a dedicated ``CommandResults`` is returned, including a ``Common.EMAIL`` indicator with its own ``Common.DBotScore`` scoped to that email's search results. Args: client (DehashedClient): The DeHashed API client. args (EmailArgs): The command arguments. email_dbot_score (str): The configured DBotScore severity ("SUSPICIOUS" or "MALICIOUS"). reliability (str | None): The configured reliability of the source. Returns: list[CommandResults]: A list of ``CommandResults``, one per email address in ``args.email``. Each entry contains its own indicator, DBotScore, and per-email outputs/readable output. """ command_results: list[CommandResults] = [] for indicator_value in args.email: query_string = _build_search_query("email", indicator_value, "is") demisto.debug(f"[email] Built query string for {indicator_value!r}: {query_string!r}") result = client.general_search( query=query_string, page=None, size=MAX_REQUEST_PAGE_SIZE, wildcard=None, regex=None, de_dupe=None, ) if not isinstance(result, dict): raise DemistoException(f"Got unexpected output from api: {result}") query_data: list[dict[str, Any]] = result.get("entries") or [] transformed_entries = [_transform_entry(entry) for entry in query_data] score = compute_score(query_data, email_dbot_score) sources = [entry.get("database_name") for entry in query_data if entry.get("database_name")] description: str | None = None if score >= Common.DBotScore.SUSPICIOUS: unique_sources = sorted({s for s in sources if s}) description = f"Found in {len(unique_sources)} breach(es): {', '.join(unique_sources)}" dbot_kwargs: dict[str, Any] = { "indicator": indicator_value, "indicator_type": DBotScoreType.EMAIL, "integration_name": BASE_CONTEXT_OUTPUT_PREFIX, "score": score, } if reliability: dbot_kwargs["reliability"] = DBotScoreReliability.get_dbot_score_reliability_from_str(reliability) if description: dbot_kwargs["malicious_description"] = description dbot_score_obj = Common.DBotScore(**dbot_kwargs) common_email = Common.EMAIL( address=indicator_value, domain=indicator_value.split("@")[1] if "@" in indicator_value else None, description=description, dbot_score=dbot_score_obj, ) if not transformed_entries: command_results.append( CommandResults( indicator=common_email, readable_output=f"No matching results found for {indicator_value}", raw_response=result, ) ) continue readable_output = tableToMarkdown( f"DeHashed Search for {indicator_value} - got total results: {result.get('total')}", transformed_entries, headerTransform=pascalToSpace, ) command_results.append( CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.Search", outputs_key_field="Id", outputs=transformed_entries, indicator=common_email, readable_output=readable_output, raw_response=result, ) ) return command_results # endregion # region ExecutionConfig class DehashedExecutionConfig(BaseExecutionConfig): """Execution configuration for DeHashed.""" @property def params(self) -> DehashedParams: return DehashedParams(**self._raw_params) @property def dehashed_search_args(self) -> DehashedSearchArgs: return DehashedSearchArgs(**self._raw_args) @property def email_args(self) -> EmailArgs: return EmailArgs(**self._raw_args) # endregion # region Main def main() -> None: """ Main entry point for the DeHashed integration. Initializes the execution configuration, client, and dispatches the command to the appropriate command function. """ execution = DehashedExecutionConfig() command = execution.command demisto.debug(f"[Main] Starting to execute {command=}.") try: params = execution.params client = DehashedClient(params) match command: case "test-module": return_results(test_module(client)) case "dehashed-search": return_results(dehashed_search_command(client, execution.dehashed_search_args)) case "email": return_results( email_command( client, execution.email_args, email_dbot_score=params.email_dbot_score, reliability=params.integration_reliability, ) ) case _: raise NotImplementedError(f"Command {command} is not implemented.") except Exception as e: demisto.error(traceback.format_exc()) return_error(f"Failed to execute {command} command.\nError:\n{str(e)}") if __name__ in ("__main__", "__builtin__", "builtins"): main() # endregion