mirror of
https://github.com/reconurge/flowsint.git
synced 2026-11-08 14:35:41 -06:00
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:
@@ -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(
|
||||
|
||||
@@ -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()?")
|
||||
|
||||
@@ -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')}"
|
||||
|
||||
Reference in New Issue
Block a user