Skip to content

Add DarkEye engine (Have I Been Ransomed? and Breach House) - #249

Open
juanmill4 wants to merge 1 commit into
stanfrbd:mainfrom
juanmill4:add-darkeye-engine
Open

juanmill4 wants to merge 1 commit into
stanfrbd:mainfrom
juanmill4:add-darkeye-engine

Conversation

@juanmill4

@juanmill4 juanmill4 commented Oct 26, 2026 •

Copy link
Copy Markdown

Summary

  • Adds a darkeye engine for FQDN and URL observables. It lists the ransomware victims, data breaches, leads and stealer logs that match a domain, using DarkEye's two APIs: Have I Been Ransomed? and Breach House.
  • Each product has its own optional key (DARKEYE_HIBR_API_KEY, DARKEYE_BREACH_HOUSE_API_KEY) and is only queried when its key is set. If neither key is set, the engine logs a warning and returns nothing, as ransomware_live does.
  • The engine returns a single entry, and each record in it carries its product and category. It keeps only listing metadata (product, category, id, title, website, group, country, discovery date) and never reads leaked content, proof files or leak-site or download URLs from the responses.

Scope

  • New feature
  • New engine
  • Documentation

Validation (required)

  • I ran relevant tests locally and they pass.
  • I validated real behavior (not only code style or static checks).
  • I checked edge cases and failure paths.

Test evidence

pytest -q
# before (main e4aee8b): 1372 passed
# after: 1473 passed (101 new, tests/engines/test_darkeye.py)

pytest -q --cov=engines.darkeye tests/engines/test_darkeye.py
# engines/darkeye.py: 100% (185 statements)

ruff check engines/darkeye.py tests/engines/test_darkeye.py utils
# All checks passed!
ruff format --check engines/darkeye.py tests/engines/test_darkeye.py
# 2 files already formatted
node --check static/format_results.js
# OK

The new card and table layouts were also rendered with a <script> payload in the title, website, group and error fields; the output shows only the escaped text.

Besides the mocked tests, the engine was run through analyze against the live APIs:

observable keys outcome
example.com (max_pages=2) both 144 records: HIBR 140 (135 breaches, 5 ransomware), Breach House 4 (3 ransomware, 1 breaches); truncated: true, no errors
example.com HIBR key invalid, Breach House valid HIBR answers 401; the result still has the 4 Breach House records, products: ["Breach House"], and one HIBR authentication error in errors
https://example.com/some/path?x=1 (max_pages=1) HIBR only the domain is taken from the URL; 20 records (19 breaches, 1 ransomware), truncated: true

403, 429, 500 and timeouts are only covered by the mocked tests.

With both keys set, each product is queried on its own, so a failure in one doesn't hide the other's results. If one fails (auth, quota, HTTP error, bad JSON, timeout), the other's records are still returned and the failure goes into errors as {"product", "error"}, which the card, the table cell and the copied text all show. If every configured product fails, analyze returns None, because a found: false result with errors would read as "clean" in a reputation check.

Other choices worth reviewing:

  • Only plain domain names are sent (both APIs treat * and ? as wildcards), and IP addresses are rejected.
  • Redirects are not followed and the timeout is 15 s per request.
  • HIBR is paginated: the engine reads at most 5 pages (max_pages), drops duplicates across pages, and sets truncated when it stops early. Breach House is a single call with limit=25 per category (200 at most).
  • There is no tenacity retry. A failed HIBR lookup can already involve several sequential calls, and retrying would use up more of the key's daily quota.
  • Request exceptions are never logged or re-raised as is, because they can contain the request headers; the logs only get a generic message per product. A test checks that the keys never appear in logs or results.

Maintainer merge policy acknowledgement

  • I understand uncertain changes may be merged to dev first for additional validation.
  • I understand main is kept clean/stable for releases and tags.

New engine checklist

Engine Implementation

  • Added engines/darkeye.py (class engine, crtsh and ransomware_live as templates).
  • Registered in utils/load_engines.py (engines/__init__.py doesn't build the registry, as noted in Add ScanMalware engine #220).
  • Added darkeye_hibr_api_key and darkeye_breach_house_api_key to Secrets in utils/config.py.

Configuration & Secrets

  • Added both keys to .env.sample.
  • Added both variables to docker-compose.yml.

UI & Frontend

  • Copyable via GUI in static/format_results.js.
  • Added templates/engine_layouts/darkeye_card.html and darkeye_table.html.
  • Added to display_cards.html and display_table.html.
  • Added to index.html with a description, in alphabetic order.
  • graph.html: one node per ransomware group found, like ransomware_live.

Documentation

  • Added docs/api-keys/Get-DarkEye-API-keys.md and its link in docs/index.md.
  • Added the engine to docs/quick-start/API-usage-and-engine-names.md.
  • Added the page to the mkdocs.yml sidebar.
  • Quick-start-&-Installation.md: not changed, it has no list of engine variables.
  • Added the variables to docs/quick-start/Advanced-options-for-deployment.md.
  • Updated README.md (env block and services list).

Risk and rollback

  • Risk level: Low
  • Potential impact: one more opt-in engine, unchecked by default, with no new dependency. Without a key it does nothing.
  • Rollback strategy: revert the commit.

Adds a `darkeye` engine for FQDN and URL observables that returns ransomware
victims, data breaches, leads and stealer logs tied to a domain from the two
DarkEye APIs, Have I Been Ransomed? and Breach House.

Each product has its own optional key (DARKEYE_HIBR_API_KEY and
DARKEYE_BREACH_HOUSE_API_KEY) and is queried only when its key is set. The
result is a single "DarkEye" entry whose records carry their product and
category. If one product fails, the records of the other one are still
returned and the failure is listed in `errors`; the engine returns nothing
only when every configured product failed.

Only plain domain names are queried (the APIs treat * and ? as wildcards),
redirects are not followed, and only listing metadata is kept for each record
(product, category, id, title, website, group, country, discovery date).
@stanfrbd

Copy link
Copy Markdown
Owner

Hi @juanmill4 this sounds like an nice addition!
Thank you for the PR.
I will check it out soon!

@stanfrbd

stanfrbd commented Oct 26, 2026 •

Copy link
Copy Markdown
Owner

It seems there is no free plan, so I won't be able to test the product :(
Do you know if I can request a trial?

@juanmill4

Copy link
Copy Markdown
Author

Sure, can you give your email?, I will send you

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants