Flow update, delete, and ID-based read never checked caller identity
against the target record, so any authenticated user could tamper with
or delete another user's Flow by UUID, and the list endpoint returned
every user's flows unfiltered. Added a nullable owner_id column (NULL
= pre-existing flows, readable by all but not editable via the API),
stamp it on create, and check ownership in FlowService before mutating
or returning a single Flow.
Fixes GHSA-qvx2-6v5v-wwrg.
Container couldn't open /etc/nginx/nginx.conf (permission denied) on
SELinux-enforcing hosts (e.g. Fedora Kinoite) because the bind-mounted
file kept its host label. Add :Z to relabel it for container-only
access; no-op on hosts without SELinux.
Fixes#207
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.
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.