157 Commits
Author SHA1 Message Date
dextmorgn 5ea93339a9 feat(app,api): flat searchable type picker in add-item dialog
Replace category drilldown with a search bar over a flat type list.
GET /types gains a flat=true param that flattens categories server-side
(custom types included); useActionItems(true) opts into it while every
other caller keeps the nested default.
2026-09-20 11:39:03 +02:00
dextmorgn 80ecb58bbb fix(api): require sketch permission before launching an enricher
launch_enricher took payload.sketch_id on trust: any authenticated caller
could dispatch an enricher against a sketch belonging to someone else, and the
worker would write its findings into that sketch.

EnricherService gains get_sketch_for_launch, the same lookup launch_flow has
used since ac485e2c: resolve the sketch, then _check_permission for "update"
on its investigation. No role row means no access, so an unknown sketch 404s
and a viewer or a stranger gets 403, matching the flow route's responses.

The check runs before the graph read, so a denied caller never reaches Neo4j
and never queues a task.

Unlike FlowService's copy, the sketch id is parsed to a UUID first.
SketchRepository.get_by_id binds it as one, so a non-UUID string reached
SQLAlchemy and raised, which would have turned a bad id into a 500 instead of
a denial.
2026-09-18 16:53:35 +02:00
dextmorgn 9b69a8b73f chore(api,core): remove dead enricher schemas, unreachable wobbly_type branch, dead set_params()
EnricherCreate/EnricherRead/EnricherUpdate had no importer anywhere in the
repo and were the whole content of app/api/schemas/enricher.py, so the module
goes with them.

get_enrichers' custom-type branch returns [] before reaching the
wobbly_type=True registry listing. The early return stays (why a published
custom type lists no enrichers is a separate question); the line it strands
does not.

EnricherBase.set_params() had no caller and no dynamic-dispatch site;
resolve_params/async_init assign self.params directly.
2026-09-18 16:53:35 +02:00
dextmorgn 7ce7560400 chore: satisfy the mypy ratchet on the files this branch touches 2026-09-18 16:39:59 +02:00
dextmorgn 8280d1c6ff feat(api): accept params on enricher launch payload 2026-09-18 16:39:58 +02:00
dextmorgnandGitHub ac485e2c1c Merge pull request #213 from reconurge/fix/flow-object-level-authz
fix(api): enforce object-level authorization on Flow update/delete/read
2026-09-06 17:42:30 +02:00
dextmorgn 7228e8a210 refactor: remove dead code from the utils.py triplication
flowsint-api/app/utils.py — deleted entirely. Nothing imports it (its
one plausibly-used function, extract_input_schema_flow, is actually
imported from flowsint_core.utils everywhere it's called); it was a
never-referenced full duplicate of the core module.

flowsint_core/utils.py and flowsint_enrichers/utils.py — dropped
get_domain_from_ssl (identical in both, zero callers anywhere in the
repo) and its now-unused ssl/socket/Tuple/cast imports.

flowsint_enrichers/utils.py further trimmed to just get_root_domain,
its only function actually imported anywhere (domain/to_root_domain.py).
The rest (is_valid_ip, resolve_type, extract_enricher, flatten, ...)
had zero callers and zero test coverage — dead since the file was
apparently copy-pasted from flowsint_core/utils.py rather than reusing
it directly.
2026-08-26 00:44:35 +02:00
dextmorgn 441de48264 fix(mypy): finish the ratchet backlog — permissions, dockertool, alembic
Last batch of real fixes plus one more scoped override:

- DockerTool.get_image() read cls.image as a class attribute, but
  __init__ only ever sets it on the instance (self.image = ...) — real
  bug (would AttributeError if get_image() were ever called before an
  instance existed with a subclass not also declaring image at class
  level). Declared `image: str` on the base to document what's
  actually relied on: subclasses set it as a class attribute, __init__
  shadows it per-instance with the tagged form.
- ReconCrawlTool.version() was declared -> str but its body was a bare
  `pass` (returns None) — no callers exist, so raises NotImplementedError
  with a comment instead of guessing a fake version string.
- ReconCrawlTool.launch()'s args param defaulted to None but the body
  calls args.get(...) unconditionally — real crash risk if ever called
  without args (never observed since the one caller always passes a
  dict), guarded with `args = args or {}`.
- Extended flowsint-core's tests.* override (assignment, override) for
  the same InputType/OutputType-pattern reason as the existing entries:
  MockEnricher/ResolveEnricher fixtures reassign InputType/OutputType
  and vary scan()/postprocess() signatures per the documented pattern,
  same as production enrichers.
- permissions.py, alembic/env.py, and one alembic migration's untyped
  params/standard-boilerplate arg-type gaps.

Ratchet is now clean except the accepted import-untyped debt (86
errors — cross-package imports needing py.typed markers project-wide,
deliberately out of scope: adding them earlier in this branch
ballooned the count from 478 to 1423 by unmasking the full ~2200
pre-existing backlog instead of shrinking it).

Verified: all 4 packages' test suites pass (397 + 32 + 6 + 54).
2026-08-25 09:33:36 +02:00
dextmorgn cf69a7e088 fix(ci): make isort's first-party grouping invocation-independent
Without known_first_party, isort's own-package detection depends on
which files it's given: sorted alone, a file inside flowsint_enrichers
gets flowsint_enrichers imports grouped separately from flowsint_core/
flowsint_types; sorted together with the rest of the workspace (as
`make lint` and CI do), they collapse into one group. lint-staged only
ever passes the staged subset, so a commit could pass the pre-commit
hook locally and still fail `make lint` in CI — silently, since both
are "isort says it's fine," just about different file sets.

Pin the four packages as known_first_party in all four pyproject.toml
so grouping is the same regardless of invocation scope. The rest of
the diff is isort re-normalizing ~100 files to the now-consistent
order — import statements only, verified against the diff.
2026-08-25 09:33:33 +02:00
dextmorgn 24e1e276c8 fix(python): correctness fixes surfaced by turning flake8 on
Each is a real defect, not style — flake8 just never ran before to
catch them:

- logger.py: bare `except:` around a queue drain swallowed everything
  including KeyboardInterrupt/SystemExit instead of just queue.Empty
- dockertool.py: bare `except:` narrowed to Exception
- permissions.py: duplicate `HTTPException` import, dead code
- test_registry.py: `test_get_by_lower_key` was defined twice — the
  second definition silently shadowed the first, so only one ever ran
- to_ports.py / test_investigation_repository.py: assigned-but-unused
  local variables, dead code
- to_whois.py / to_infos.py: documented pyflakes false positive
  (OutputType is a class attr, resolves fine at def-time) — noqa'd
  with an explanation instead of restructuring working code
- E712 (`== True`/`== False` → `is True`/`is False`) across 7 test
  files, F541 (f-strings with no placeholders) across 5 enrichers,
  E402 (import order) noqa'd in 2 files where the order is deliberate
  (env vars set before import; documented circular-import workaround)
2026-08-25 09:33:33 +02:00
dextmorgn eb30268c56 style(python): apply black/isort formatting workspace-wide
Mechanical only — black + isort, no logic changes. Enforced going
forward by the CI gate and pre-commit hook added in the previous
commit.
2026-08-25 09:33:33 +02:00
dextmorgn dd9caf2cb1 fix(api): enforce object-level authorization on Flow update/delete/read
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.
2026-08-23 13:10:11 +02:00
Eric HowardandEric 86b254bbf9 Custom types replaced with proper custom_types_category value 2026-06-06 18:48:05 -04:00
Eric f0b0b9bf82 Added category to custom types to reorginize into desired categories 2026-06-06 18:38:35 -04:00
Melih Daskiran 25eb3a0400 Fix zip alignment mismatch, phone validation return value, and vault owner_id type matching 2026-06-04 21:46:44 +03:00
dextmorgn b317e08e57 feat(api): authenticate SSE via header, drop query-param auth and dead route 2026-06-03 11:28:41 +02:00
dextmorgn 1d409d7929 fix(scan): persist results to existing details column, errors to error column
The Scan model exposes details (JSON) and error (Text) columns, but the
enricher/flow tasks assigned to scan.results — a non-mapped attribute.
SQLAlchemy accepted the assignment silently, so commit() never persisted
it and the data was lost. Success paths now write scan.details; failure
paths write scan.error. ScanCreate.results renamed to details to match.

Scan repository tests pass.
2026-05-30 19:02:14 +02:00
dextmorgn ee06d6cf3e fix(api): restrict CORS origins via ALLOWED_ORIGINS env
allow_origins was ["*"] together with allow_credentials=True, which is
both insecure and rejected by browsers for credentialed requests. Read a
comma-separated ALLOWED_ORIGINS env var instead, defaulting to the Vite
dev origin http://localhost:5173. Documented in .env.example.
2026-05-30 18:49:44 +02:00
dextmorgn c9d594a8e7 fix(api): load AUTH_SECRET from validated core.auth source
deps.py read AUTH_SECRET via os.getenv() before load_dotenv() ran,
yielding None when the secret was only present in .env. Import the
already-validated AUTH_SECRET from core.auth (loads dotenv first and
raises if unset) instead of re-reading it. Removes the dup + ordering bug.
2026-05-30 18:44:07 +02:00
dextmorgn bb502379bd feat(api): add collaborator endpoints, profile update, user search and backfill migration
- Add GET/POST/PUT/DELETE /investigations/{id}/collaborators endpoints
- Inject current_user_role in all investigation responses
- Add GET/PUT /auth/me for profile management
- Add GET /auth/users/search for share dialog autocomplete
- Add ProfileUpdate schema with extra=forbid
- Add email to ProfileRead
- Add backfill migration for existing investigations missing owner role entries
2026-04-11 14:44:28 +02:00
dextmorgn 972763343e feat(api): add type detection route and service
Add POST /api/types/detect endpoint that detects the type of a text input
by iterating registered types and calling their detect() classmethod.
Returns detected type with fields and primary field pre-filled.
Falls back to Phrase when no type matches.
2026-03-15 12:35:46 +01:00
dextmorgn 2ea40c4eae feat(api): add input and output type to enricher template 2026-03-09 19:38:42 +01:00
gustavorps b2cfc67117 fix(app): removing slashes 2026-02-17 04:49:46 -03:00
gustavorps 5ee7bf24e0 fix(api): added redirect_slashes=False to face 307 redirect reverse proxy issue 2026-02-17 04:27:13 -03:00
gustavorps da0280506e fix(api): added ignore_trailing_slash=True to face 307 redirect reverse proxy issue 2026-02-17 04:21:01 -03:00
dextmorgn 6494cee2d0 feat(api): update key service 2026-02-16 09:16:05 +01:00
dextmorgn eef214bdfa feat(api): custom types updates 2026-02-14 15:34:28 +01:00
dextmorgn 599fc8353d feat(api): increate prompt chat length 2026-02-12 23:40:12 +01:00
dextmorgn a764704fbe feat(api): progress on templates 2026-02-12 22:04:28 +01:00
dextmorgn 19ee40c46d feat(app): chat context 2026-02-12 22:04:28 +01:00
dextmorgn 07f32c983a feat: use ai sdk useChat 2026-02-12 22:04:21 +01:00
dextmorgn a461f9997d feat: progress on enricher templates 2026-02-12 22:03:28 +01:00
dextmorgn 53a03575cd feat(api): enrichers from templates 2026-02-12 22:03:04 +01:00
dextmorgn 177953b216 feat(api): use repositories in services 2026-02-07 17:23:16 +01:00
dextmorgn 2372678bda feat(api): use dedicated services for business logic 2026-02-05 22:37:31 +01:00
dextmorgn 5977832a1a feat(api): refactor to use nodeLabel, and proper usage of graphService 2026-01-21 17:19:24 +01:00
dextmorgn be0880e77d feat(api) update export json schema 2026-01-11 19:30:38 +01:00
dextmorgn 27c9e91c92 feat(api): add json import support 2026-01-10 18:07:14 +01:00
dextmorgn 1d184bae3b feat(api): update export schema 2026-01-07 22:03:31 +01:00
dextmorgn 6cc8bf25c0 feat(api): export route 2026-01-07 21:44:02 +01:00
dextmorgn c4408c2f3e feat(api): update edge label 2026-01-05 21:45:53 +01:00
dextmorgn e812f3c855 feat(app): get_current_user_sse 2025-12-17 10:17:42 +01:00
dextmorgn a15ce33df4 fix(api): patch missing permissions on api routes 2025-12-17 10:16:54 +01:00
dextmorgn 0577080b4e feat(api): add DNSRecord to raw_material 2025-12-15 16:07:59 +01:00
dextmorgn 6633cc5f4a fix(api): returned parsed node after insert 2025-12-10 15:15:30 +01:00
dextmorgn 5bb7fae4bd refactor(api): better batch import 2025-12-05 15:47:43 +01:00
dextmorgn 152a30c92a feat(api): update flowsint_enrichers registry 2025-11-30 12:50:01 +01:00
dextmorgn f039d92234 fix(api): typo in icon for social account 2025-11-29 14:08:58 +01:00
dextmorgn 7692fc76f6 feat(api): type registry + better import 2025-11-28 20:08:09 +01:00
dextmorgn 6412b1afe8 feat(api): dedicated sketch event route for refresh 2025-11-27 22:20:28 +01:00