From the issue: during assessments people often run nmap manually with specific flags, and naabu can also export in nmap XML. Letting Flowsint ingest that XML enriches a sketch with the hosts/ports already discovered. This reuses the existing two-phase import pipeline (analyze → execute) — no new infrastructure.
How it plugs in
The import pipeline dispatches parsers by file extension (flowsint_core/imports/file_parser.py) and each parser is a function returning a FileParseResult of typed entities + edges, which the pipeline persists. The new parser follows that exact contract (same shape as the existing JSON parser).
flowsint_core/imports/nmap/parse_nmap.py (new) — parse_nmap_xml() turns an nmap report into:
Ip entities (one per up host; down hosts skipped; IPv4 preferred, IPv6 supported, MAC ignored)
Port entities (number, protocol, state, service, and a banner composed from service product/version/extrainfo)
IP -[HAS_PORT]-> Port edges (the same relationship label the ip_to_ports enricher uses)
Wiring: .xml added to ALLOWED_EXTENSIONS (core dispatcher) + the analyze route's filename validation (flowsint-api/.../sketches.py) + the import sheet's accepted extensions (flowsint-app/.../import-sheet.tsx). Ip and Port already exist, so no type changes were needed.
Security
Import files are untrusted, so the XML is parsed with defusedxml (new flowsint-core dependency) to prevent XXE / billion-laughs attacks. Element type hints still come from the stdlib (identical types, not a parsing path).
Testing
uv run --package flowsint-core pytest flowsint-core/tests/import/ -q
56 passed # 10 new nmap tests + existing import suite, no regressions
New tests (pure-parse, no DB): IP/port extraction, port fields + banner composition, HAS_PORT edges, down-host skipping, IPv6, invalid-port skipping, empty/invalid XML handling, and dispatch through parse_import_file.
Scope note: this maps the core of an nmap scan (hosts + ports + service info). NSE script output (which could map to the existing Script type) is intentionally left out to keep the PR focused; happy to add it as a follow-up if wanted.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
## 📋 Pull Request Information
**Original PR:** https://github.com/reconurge/flowsint/pull/184
**Author:** [@rachit367](https://github.com/rachit367)
**Created:** 6/9/2026
**Status:** 🔄 Open
**Base:** `main` ← **Head:** `feat/nmap-xml-importer`
---
### 📝 Commits (1)
- [`f9100ba`](https://github.com/reconurge/flowsint/commit/f9100ba15ee69ea35db912b553877fb0da089f4a) feat(imports): add nmap XML importer
### 📊 Changes
**8 files changed** (+314 additions, -4 deletions)
<details>
<summary>View changed files</summary>
📝 `flowsint-api/app/api/routes/sketches.py` (+4 -2)
📝 `flowsint-app/src/components/sketches/import-sheet.tsx` (+1 -1)
📝 `flowsint-core/pyproject.toml` (+1 -0)
📝 `flowsint-core/src/flowsint_core/imports/file_parser.py` (+4 -1)
➕ `flowsint-core/src/flowsint_core/imports/nmap/__init__.py` (+3 -0)
➕ `flowsint-core/src/flowsint_core/imports/nmap/parse_nmap.py` (+150 -0)
➕ `flowsint-core/tests/import/nmap/test_nmap_import.py` (+140 -0)
📝 `uv.lock` (+11 -0)
</details>
### 📄 Description
## What
Adds an **nmap XML importer** so an existing scan can be imported into a sketch, instead of re-scanning a target or adding ports by hand.
Closes #107
## Why
From the issue: during assessments people often run nmap manually with specific flags, and naabu can also export in nmap XML. Letting Flowsint ingest that XML enriches a sketch with the hosts/ports already discovered. This reuses the existing two-phase import pipeline (analyze → execute) — no new infrastructure.
## How it plugs in
The import pipeline dispatches parsers by file extension (`flowsint_core/imports/file_parser.py`) and each parser is a function returning a `FileParseResult` of typed entities + edges, which the pipeline persists. The new parser follows that exact contract (same shape as the existing JSON parser).
- `flowsint_core/imports/nmap/parse_nmap.py` (new) — `parse_nmap_xml()` turns an nmap report into:
- `Ip` entities (one per *up* host; down hosts skipped; IPv4 preferred, IPv6 supported, MAC ignored)
- `Port` entities (`number`, `protocol`, `state`, `service`, and a `banner` composed from service `product`/`version`/`extrainfo`)
- `IP -[HAS_PORT]-> Port` edges (the same relationship label the `ip_to_ports` enricher uses)
- Wiring: `.xml` added to `ALLOWED_EXTENSIONS` (core dispatcher) + the analyze route's filename validation (`flowsint-api/.../sketches.py`) + the import sheet's accepted extensions (`flowsint-app/.../import-sheet.tsx`). `Ip` and `Port` already exist, so no type changes were needed.
## Security
Import files are untrusted, so the XML is parsed with **`defusedxml`** (new `flowsint-core` dependency) to prevent XXE / billion-laughs attacks. Element type hints still come from the stdlib (identical types, not a parsing path).
## Testing
```
uv run --package flowsint-core pytest flowsint-core/tests/import/ -q
56 passed # 10 new nmap tests + existing import suite, no regressions
```
New tests (pure-parse, no DB): IP/port extraction, port fields + banner composition, `HAS_PORT` edges, down-host skipping, IPv6, invalid-port skipping, empty/invalid XML handling, and dispatch through `parse_import_file`.
> Scope note: this maps the core of an nmap scan (hosts + ports + service info). NSE script output (which could map to the existing `Script` type) is intentionally left out to keep the PR focused; happy to add it as a follow-up if wanted.
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/reconurge/flowsint/pull/184
Author: @rachit367
Created: 6/9/2026
Status: 🔄 Open
Base:
main← Head:feat/nmap-xml-importer📝 Commits (1)
f9100bafeat(imports): add nmap XML importer📊 Changes
8 files changed (+314 additions, -4 deletions)
View changed files
📝
flowsint-api/app/api/routes/sketches.py(+4 -2)📝
flowsint-app/src/components/sketches/import-sheet.tsx(+1 -1)📝
flowsint-core/pyproject.toml(+1 -0)📝
flowsint-core/src/flowsint_core/imports/file_parser.py(+4 -1)➕
flowsint-core/src/flowsint_core/imports/nmap/__init__.py(+3 -0)➕
flowsint-core/src/flowsint_core/imports/nmap/parse_nmap.py(+150 -0)➕
flowsint-core/tests/import/nmap/test_nmap_import.py(+140 -0)📝
uv.lock(+11 -0)📄 Description
What
Adds an nmap XML importer so an existing scan can be imported into a sketch, instead of re-scanning a target or adding ports by hand.
Closes #107
Why
From the issue: during assessments people often run nmap manually with specific flags, and naabu can also export in nmap XML. Letting Flowsint ingest that XML enriches a sketch with the hosts/ports already discovered. This reuses the existing two-phase import pipeline (analyze → execute) — no new infrastructure.
How it plugs in
The import pipeline dispatches parsers by file extension (
flowsint_core/imports/file_parser.py) and each parser is a function returning aFileParseResultof typed entities + edges, which the pipeline persists. The new parser follows that exact contract (same shape as the existing JSON parser).flowsint_core/imports/nmap/parse_nmap.py(new) —parse_nmap_xml()turns an nmap report into:Ipentities (one per up host; down hosts skipped; IPv4 preferred, IPv6 supported, MAC ignored)Portentities (number,protocol,state,service, and abannercomposed from serviceproduct/version/extrainfo)IP -[HAS_PORT]-> Portedges (the same relationship label theip_to_portsenricher uses).xmladded toALLOWED_EXTENSIONS(core dispatcher) + the analyze route's filename validation (flowsint-api/.../sketches.py) + the import sheet's accepted extensions (flowsint-app/.../import-sheet.tsx).IpandPortalready exist, so no type changes were needed.Security
Import files are untrusted, so the XML is parsed with
defusedxml(newflowsint-coredependency) to prevent XXE / billion-laughs attacks. Element type hints still come from the stdlib (identical types, not a parsing path).Testing
New tests (pure-parse, no DB): IP/port extraction, port fields + banner composition,
HAS_PORTedges, down-host skipping, IPv6, invalid-port skipping, empty/invalid XML handling, and dispatch throughparse_import_file.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.