ctx.clip() without save/restore was permanently mutating the clipping
region across all subsequent canvas draw calls, causing uncontrollable
scaling on nodes and background when a node had an imageUrl.
Remove Docker-specific resolver (127.0.0.11) in favor of direct
proxy_pass, which falls back to the system resolver (/etc/resolv.conf).
Mount local nginx.conf into the prod image to override the baked-in config.
TXT imports of file hashes were detected as Username, because File.detect
always returned False and Username.detect matches any 3-80 char
alphanumeric string. Hashes therefore couldn't be used as File entities
to pivot from (VirusTotal, MalwareBazaar, sandboxes), per #45.
- File.detect now recognizes 32/40/64-char hex strings (MD5/SHA1/SHA256),
and File.from_string stores the value in the matching hash_* field
(normalized to lowercase) while keeping it as the filename/label.
- Username.detect now explicitly rejects hash-shaped strings, so hash
detection is correct regardless of type-registration order (File is
already registered before Username, but this removes the implicit
dependency).
Tests cover detection of each hash type (case/whitespace), rejection of
non-hashes, from_string field population, the Username regression, and
end-to-end detect_type() resolution to File.
Closes#45
Adds application-level technology fingerprinting, extending coverage
beyond ip/port/website/ssl_certificate as requested in #59.
- New `Technology` type (name, version, category, source) registered in
the type registry, the package exports, and TYPE_TO_MODEL. Its
from_string accepts both bare names ("nginx") and wappalyzer-style
"name:version" pairs ("PHP:8.1").
- New `tech_detect` enricher: Website -> Technology, using the existing
httpx tool with -td (wappalyzer dataset). Each detected technology
becomes a Technology node linked to the website with a
USES_TECHNOLOGY relationship; results are de-duplicated per probe.
Tests cover the Technology type (label, from_string, registry), the
tech entry parser, enricher registration/metadata, and scan() with the
httpx Docker tool mocked, so no Docker daemon is required.
Closes#59
Adds a `domain_to_dns` enricher that resolves a domain's A (IPv4) and
AAAA (IPv6) records through ProjectDiscovery's dnsx, emitting each
resolved IP as an `Ip` node linked to the domain with a `RESOLVES_TO`
relationship.
This complements the existing `domain_to_ip` enricher (which uses
Python's socket.gethostbyname and is limited to a single IPv4 result)
by leveraging the dnsx toolkit for full A/AAAA resolution.
Implementation:
- Extend DnsxTool with `resolve_domain()` that runs
`dnsx -d <domain> -a [-aaaa] -json -silent` and parses the JSONL
output (a/aaaa fields per retryabledns.DNSData) into a de-duplicated
IP list. The existing CIDR `launch()` path is left untouched.
- New DomainToDnsEnricher (auto-registered via @flowsint_enricher) with
an optional `ipv6` toggle and optional PDCP_API_KEY vault secret.
- Unit tests covering registry wiring, JSONL parsing (A/AAAA, dedup,
blank/malformed lines, empty output) and scan() with the Docker tool
mocked, so no Docker daemon is required to run them.
Closes#79
Detected by Aeon + semgrep.
Severity: medium
CWE-350 (reverse-DNS trust) / CWE-352 (CSRF via DNS rebinding) / CWE-1021 (UI layering / dropped X-Frame-Options).
- Add `map $http_host $is_flowsint_host` allowlist + default-deny `if` at
the top of the server block. Default permits localhost / 127.0.0.1 /
::1 (any port, case-insensitive); two commented-out template lines show
LAN operators how to add their server hostname/IP.
- Re-declare the four `add_header` security headers in the static-assets
and `/index.html` location blocks, which previously dropped them due to
nginx`s replace-all add_header inheritance.
- README: one paragraph under "Deploy on a network" naming the new
operator step alongside the existing `.env` rotation.
Any code path reaching the Neo4jConnection singleton requires NEO4J_*
env vars at construction — tests only passed thanks to developers'
local .env files. Driver creation is lazy, nothing connects.
Verified by running all four suites with every .env removed:
458 passed.
Previous fix covered one occurrence; 17 other tests built
Neo4jGraphRepository(neo4j_connection=None), hitting the singleton
fallback that requires NEO4J_* env vars. Single helper, no env
dependency left.
Neo4jGraphRepository(neo4j_connection=None) falls back to the
Neo4jConnection singleton, which requires NEO4J_* env vars — the test
only passed locally thanks to .env. Pass a mock instead; the test
nulls _connection right after anyway.
flowsint_core imports yaml (template_generator_service, yaml_loader)
but never declared it — present locally only as a leftover in the
shared venv. CI's clean resolve exposed the missing declaration.
Single entrypoint: CI calls `make test`, same as local development,
so the two can't drift. uv handles Python (.python-version) and
dependency sync; cache keyed on uv.lock.
Also add flowsint-api to `make test` — its suite existed but was
never wired in.
Frontend baked VITE_API_URL into the bundle at build time, so
published images pointed at whatever URL CI had — "fail to fetch"
for every other deployment. Bundle now defaults to same-origin
relative URLs and the frontend nginx proxies /api/ to the api
service over the Docker network; CORS becomes irrelevant.
- docker-compose.prod.yml: use ghcr images (FLOWSINT_VERSION,
default latest), drop build sections; bind postgres/redis/neo4j/
api ports to 127.0.0.1 — only 5173 faces the network
- nginx: /api/ reverse proxy, request-time DNS resolution, SSE
support (proxy_buffering off)
- Makefile: prod pulls instead of building; remove dead deploy
targets referencing nonexistent docker-compose.deploy.yml
- README: network/server deployment section (secrets checklist,
version pinning, TLS via reverse proxy)
pip-audit reported 27 vulnerabilities across 12 packages, including
cryptography PYSEC-2026-36 (CVSS 9.8) used by the vault, a starlette
Host-header path bypass, and python-multipart arbitrary file write.
- cryptography 45.0.7 -> 46.0.7 (pin was capped at <46)
- fastapi 0.115 -> 0.136, pulling starlette 0.46 -> 1.2
- python-multipart 0.0.20 -> 0.0.32
- pyjwt, urllib3, aiohttp, lxml, idna, mako and transitive bumps
- dev tools: pytest 8 -> 9 (requires pytest-httpx >=0.36), black 25 -> 26
All four module test suites pass (458 tests). pip-audit now reports
no known vulnerabilities.
Windows checkouts with core.autocrlf=true convert entrypoint.sh to
CRLF, which Docker copies into the image. The shebang then reads
#!/bin/sh\r and exec fails with 'no such file or directory'.
Also covers .env.example (trailing \r in env values) and Makefile.
Windows users hit failures with make-based install. Replicate
check-env + build + up targets as plain PowerShell/docker compose
commands for prod and dev.