fix(enrichers): dnsx stdin input + configurable Scamalytics API host

Two enrichers silently return empty results instead of failing loudly.

1. domain_to_dns
   dnsx >= 1.2 treats -d as "bruteforce this domain" and exits with
   "missing wordlist(w) flag required with domain(d) input" unless -w is
   also given, so `dnsx -d example.com -a -aaaa -json -silent` never
   produces output. The enricher catches nothing (the tool exits 0) and
   returns [], while Celery still reports the task as succeeded.
   Feed the single target on stdin instead, which is the supported
   single-target invocation. DockerTool.launch() gains an optional
   entrypoint override so a shell can be put in front of the image
   entrypoint; the domain is passed through shlex.quote().

2. ip_to_fraudscore
   Scamalytics assigns each account a numbered API node (api11, api12,
   ...) and the host was hardcoded to api12, which returns 404 for every
   account provisioned on a different node. The host is now read from a
   SCAMLYTICS_API_HOST secret/env var, defaulting to the previous api12
   so existing deployments are unaffected.

Verified against dnsx v1.2.2 and a live Scamalytics v3 account.
This commit is contained in:
Topman3579
2026-07-27 14:29:02 +07:00
parent 0d092c44f0
commit ebda0f4c49
3 changed files with 23 additions and 7 deletions
@@ -74,11 +74,16 @@ class IpToFraudScore(Enricher):
self.ip_risk_mapping = []
api_username = self.get_secret("SCAMLYTICS_USERNAME", os.getenv("SCAMLYTICS_USERNAME"))
api_key = self.get_secret("SCAMLYTICS_API_KEY", os.getenv("SCAMLYTICS_API_KEY"))
api_key = self.get_secret("SCAMLYTICS_API_KEY", os.getenv("SCAMLYTICS_API_KEY"))
# Scamalytics assigns each account a numbered API node (api11, api12,
# ...); a hardcoded host 404s for every account that is not on it.
api_host = self.get_secret(
"SCAMLYTICS_API_HOST", os.getenv("SCAMLYTICS_API_HOST", "api12")
)
for ip in data:
try:
api_request = requests.get(f'https://api12.scamalytics.com/v3/{api_username}/?key={api_key}&ip={ip.address}', timeout=30)
api_request = requests.get(f'https://{api_host}.scamalytics.com/v3/{api_username}/?key={api_key}&ip={ip.address}', timeout=30)
if api_request.status_code != 200:
Logger.error(
+7 -3
View File
@@ -46,7 +46,7 @@ class DockerTool(Tool):
except ImageNotFound:
return False
def launch(self, command: str, volumes: dict = None, timeout: int = 30, environment: dict = None):
def launch(self, command, volumes: dict = None, timeout: int = 30, environment: dict = None, entrypoint=None):
self.install()
# Merge default environment with custom environment
env = {"TERM": "dumb"} # Set terminal type to avoid TTY issues
@@ -54,8 +54,7 @@ class DockerTool(Tool):
env.update(environment)
try:
result = self.client.containers.run(
self.image,
run_kwargs = dict(
command=command,
remove=True,
stdout=True,
@@ -67,6 +66,11 @@ class DockerTool(Tool):
stdin_open=False, # Ensure stdin is not open
environment=env,
)
# Some tools only accept a single target on stdin, which needs a
# shell in front of the image's default entrypoint.
if entrypoint is not None:
run_kwargs["entrypoint"] = entrypoint
result = self.client.containers.run(self.image, **run_kwargs)
return result.decode()
except ImageNotFound:
raise RuntimeError(f"Image {self.image} not found. Did you run install()?")
+9 -2
View File
@@ -1,4 +1,5 @@
import json
import shlex
from typing import List
from ..dockertool import DockerTool
@@ -109,14 +110,20 @@ class DnsxTool(DockerTool):
flags.append("-aaaa")
flags += ["-json", "-silent"]
command = f"-d {domain} {' '.join(flags)}"
# dnsx >= 1.2 treats `-d` as "bruteforce this domain" and refuses to
# run without a `-w` wordlist, so a plain `-d example.com` exits with
# "missing wordlist(w) flag required with domain(d) input" and the
# enricher silently returns nothing. Feeding the single target on
# stdin is the supported invocation.
inner = f"dnsx {' '.join(flags)}"
command = ["-c", f"echo {shlex.quote(domain)} | {inner}"]
env = {}
if api_key:
env["PDCP_API_KEY"] = api_key
try:
result = super().launch(command, environment=env)
result = super().launch(command, environment=env, entrypoint="sh")
except Exception as e:
raise RuntimeError(
f"Error running dnsx: {str(e)}. Output: {getattr(e, 'output', 'No output')}"