All notable changes to SpiderFoot are documented in this file.
Format based on Keep a Changelog.
A hardening and test-maturity release. A multi-pass audit (see AUDIT.md) raised
the safety net and used it to surface and fix latent defects across the API,
core engine, and persistence layers. No breaking changes — see
UPGRADING.md for the in-place adoption guide and the
behavioural corrections (IP/scope classification, URL dedup, fixed endpoints) to
expect.
- Core IP scope/SSRF classification (security): the netaddr 1.x upgrade
silently broke the IP classifiers.
isValidLocalOrLoopbackIpreturnedFalsefor every RFC1918 private address (probed a removedis_privateattribute), sosfp_dnsresolvemislabelled private IPs as publicIP_ADDRESS(scope creep);isPublicIpAddressclassified link-local (incl. the cloud-metadata address169.254.169.254) as public. The proxy helpers had the same misuse (one crashed withAttributeError). All rewritten on the netaddr 1.x API with adversarial tests. - Scan scope matching (security):
SpiderFootTarget.matches()compared the raw value against lowercased aliases, so mixed-case in-scope hosts (Sub.Example.COM) were treated as out-of-scope (under-scanning). The leading-dot suffix-bypass guard is preserved. - Config persistence: boolean options flipped
True→Falseon an in-memory serialize/unserialize round trip (int 1vsstr "1"); normalised for both the in-memory and DB paths. - Event dedup:
ContentNormalizer._normalize_urlmangled non-default ports (:8080→80) and collided distinct URLs, dropping real findings as duplicates in the live scan path; rewritten port-aware. - Correlation engine: an invalid regex in any YAML correlation rule aborted the entire correlation run; bad patterns are now skipped with a warning.
- API routing: unreachable endpoints caused by parameterized routes
shadowing literal siblings — fixed across
notification_rules,report_templates,scan_comparison,tag_group,data,webhooks,health,scan, andstorage(/snapshots/all);POST /data/modules/bulk-disablenow accepts a JSON body (was always 400); and six handlers that turned intended 404/422/400 responses into 500s now preserve the status code. - Crashes:
GET /data/modules/dependencies(undefined fallback →NameError),POST /asm/ingest(ternary-precedence bug setrisk=None), and a maintenance task’s undefined logger. - Deprecation cleanup: Pydantic v1
.dict()→model_dump(),datetime.utcnow()→datetime.now(timezone.utc).
- Test coverage raised from 66.6% to ~71% with a CI gate
(
--cov-fail-under=70). New suites cover all 28 API routers, all 7 Celery task modules, the LLM agents, and a parametrized contract over the 20 untestedsfp_tool_*wrappers. - Real-PostgreSQL DB integration gate in CI exercising the full scan/event/config persistence lifecycle (the DB layer is unit-untestable).
- App-wide route-shadowing guard test (all HTTP methods) to prevent regressions of the routing bug class.
- Reusable in-memory
FakeRedis/FakeScanServicetest helpers.
- Coverage gate ratcheted 66 → 70; Codacy/Codecov coverage upload wired.
- Fixed the integration job (wrong DB env var; un-failable
|| echo) so the new DB gate actually runs and gates. - Worked around a coverage C-tracer + threads
SystemErrorflake viaconcurrency = threadin.coveragerc.
- Containerized microservices architecture: Docker Compose split into domain service files with service-integration support for running the scanner, API, agents, and workers as independent services.
- Helm chart: Kubernetes deployment chart (
helm/spiderfoot) plus expanded per-module documentation. - Modular LLM analysis agents: refactored AI analysis agents with an IaC advisor agent (
spiderfoot/agents/iac_advisor.py) and structured-output integration. - E2E testing framework: end-to-end test scaffolding, Docker Compose core-services profile, and supporting automation scripts.
- CI: Codecov coverage upload on every test run.
- Codebase audit & alignment pass (see
AUDIT.md): corrected broken internal imports (spiderfoot.correlations→spiderfoot.correlation,sflibscanner import), removed a tautological SQL clause, made non-securityhashlib.md5calls FIPS-safe, switched async handlers toasyncio.get_running_loop(), fixed a case-inverted GTM analytics filter, added a missing HTTP timeout insfp_zoomeye, closed a leaked file handle insfp_tool_cmseek, and removed an unusedpickleimport. - Documentation alignment: removed stale
sfwebui.py/ port:5001references, corrected repository URLs and version strings, and fixed the malformed README header image.
- Docker Compose modularization (Batch 40): Split monolithic
docker-compose.ymlinto domain-specific include files using the Composeincludedirective for cleaner service management - OpenAPI TypeScript SDK (Batch 41): Automated SDK generation pipeline via
@hey-api/openapi-ts v0.92.4— produces fetch-based client with full type safety, JWT interceptor, and clean operation names from post-processed OpenAPI spec - JSONL streaming export (Batch 42): Newline-delimited JSON export endpoint (
/api/scans/{id}/export/jsonl) for large scan results; pipeline-friendly format for downstream tooling - SSE live event stream (Batch 42): Server-Sent Events endpoint (
/events/stream) for real-time scan event delivery to frontends and integrations - Native async I/O engine (Batch 43):
SpiderFootAsyncPluginbase class with nativeaiohttpHTTP client andaiodnsDNS resolver — opt-in async path for scanner modules without affecting sync modules - AI structured outputs (Batch 44): 12 Pydantic models in
spiderfoot/ai/schemas.pyfor typed LLM responses;chat_structured()method using OpenAIresponse_format: json_schemamode with automatic validation - PEP 561 strict typing (Batch 45):
py.typedmarker for downstream type checking;mypyconfiguration insetup.cfg; strict type annotations across DB layer, core models, and network utilities
- Go CLI: Full-featured cross-platform CLI (
spiderfoot-cli) built with Cobra/Viper — scan management, module listing, STIX/JSON/CSV export, schedule CRUD, health check, config management - Command Palette: Global
Ctrl+Kquick-navigate with fuzzy search over pages and recent scans, ARIA-compliantcomboboxpattern - Schedules CRUD page: Create, edit, enable/disable, delete scan schedules from the frontend with PATCH support
- STIX export: Full STIX 2.1 bundle export from scan detail page
- Emotional design system: Tooltip component, risk pills, SSE-driven progress bars, scan completion celebrations, toast notifications
- Comprehensive test coverage: 282 tests across 27 test files — pages, components, utilities, auth, API client, scan tabs, CommandPalette
- SQL injection fix (P0): Parameterized
INclauses indb_event.py(scanElementSourcesDirect,scanElementChildrenDirect) - Jinja2 SSTI sandbox: Replaced
jinja2.EnvironmentwithSandboxedEnvironmentin report templates - SSO tokens → hash fragment: OAuth2/SAML callbacks redirect to
/#access_token=…instead of query params - XSS in PDF export: Escaped
scan.name,scan.target,scanIdindocument.write() - Stored XSS in email notifications: Escaped HTML in
_send_emailtitle/message/data interpolation - Login URL error sanitization: Whitelisted SSO error codes via
SSO_ERROR_MAP; unknown codes get generic message - API auth on all routers: Added
Depends(get_api_key)to ~170 previously unprotected endpoints - WebSocket authentication: JWT/API-key verification on WS connect (code 4003 on failure)
- Path parameter validation:
SafeIdregex applied to 59+ route handlers across scan, workspace, reports, export routers - SSRF webhook URL validation: Blocks private/loopback/link-local IPs and dangerous hostnames
- Content-Disposition injection:
safe_filename()applied to all 16 download headers - SSE token leak fix: Replaced EventSource with fetch+ReadableStream to avoid token in URL
- Docker hardening:
no-new-privileges,read_only,tmpfson 13/23 services - Content-Security-Policy and optional HSTS headers
- Error detail leak sweep: 80+
detail=str(e)patterns replaced with generic messages across all routers - Hardcoded credentials removed: MinIO
changeme123and PostgreSQLchangemefallbacks eliminated - Export SafeID validation: Added
validateSafeIDcall in CLI export command
- Architecture: microservice-only (Batches 34–36): Removed monolith entry points (
sf.py,sfcli.py,sfwebui.py,sf_orchestrator.py) and the entire Python CLI package (spiderfoot/cli/, 30 files). SpiderFoot is now strictly: Python API (FastAPI), Node.js Frontend (React), Go CLI (Cobra), PostgreSQL - PostgreSQL-only database layer: Removed all SQLite support — merged dual schema into single PostgreSQL
createSchemaQueries, removedcreatePostgreSQLSchemaQueries, eliminated allimport sqlite3statements, changed every(sqlite3.Error, psycopg2.Error)catch topsycopg2.Error, removedSQLiteBackendclass fromreport_storage.py(~165 lines), madebuild_config_from_env()raiseEnvironmentErrorinstead of SQLite fallback - Renamed SQLite-named symbols:
SpiderFootSqliteLogHandler→SpiderFootDbLogHandler,enable_sqlite→enable_db_handler,_store_sqlite→_store_default,_bulk_store_sqlite→_bulk_store_default db_migrate.py: ReplacedSqliteAdapterwithPostgresAdapter, removedSQLITEfromDbDialectenumauth/service.py: PostgreSQL-only withRuntimeErroron missing DSN- Dockerfile: API-only (port 8001), removed monolith/webui entry points
- Go CLI User-Agent: Dynamic
SpiderFoot-CLI/<version>header viaclient.Versionpropagated fromroot.go - Go CLI input validation: Added
validateSafeIDto schedule update, delete, and trigger commands - GrpcDataService import: Wrapped in
try/except ImportErrorfor graceful degradation when gRPC stubs not generated - Debian packaging: Removed stale
sf.py,sfcli.py,sfwebui.pyreferences frompackaging/debian/install - Version bump to 6.0.0: VERSION, package.json, CLI root.go/client.go, Layout.tsx, README badge
- STIX API path corrected:
/scans/→/api/scans/prefix - Health CLI path corrected:
/api/health→/health(root-mounted router) - GeoMapTab: Migrated 4 individual
useQuerycalls to singleuseQueries()for parallel fetching - Scans search pagination: Search mode now uses server-side
limit/offsetpagination instead of fetching 200 results; pagination controls shown in search mode - Search/filter reset: Page resets to 1 when search query or status filter changes
- Event types type safety:
unknown[]→Array<{ name: string; description?: string }> - Empty catch blocks: GraphTab and LogTab now log errors instead of silently swallowing
@types/dompurifymoved from dependencies to devDependencies- Node engines field: Added
"engines": { "node": ">=18" }to package.json - Layout version constant: Extracted
APP_VERSIONconstant, replacing 2 hardcodedv5.9.2references - Route-level code splitting: 10 of 12 pages lazy-loaded via
React.lazy() - AbortSignal support: All 84 API methods accept
signal; all 38queryFncall sites forward TanStack Query's signal - Admin pages → React Query: SSOSettings, Users, ApiKeys migrated from useState/useEffect to useQuery/useMutation
- Concurrent token refresh deduplication: Shared
refreshPromiseprevents race conditions useDocumentTitleon all 13 pages- Workspaces.tsx refactor: Extracted
WorkspaceReportCardcomponent anduseWorkspaceMutationshook — reduced from 1003 to 714 lines - Workspace scans cache isolation: Separated
['workspace-scans', workspaceId]query key from global scans list - TypeScript strict compliance: Zero errors on
tsc --noEmit— fixed mutation type mismatches, unused imports/parameters across all source and test files - Accessibility: ARIA labels on scan tabs, forms, filters, checkboxes, GeoMap SVG, skip-to-content link
- Monolith entry points:
sf.py,sf_orchestrator.py,sfcli.py— all replaced bysfapi.py+ Go CLI - Python CLI package: Entire
spiderfoot/cli/directory (30 files, ~3,500 lines) — replaced by Go CLI - SQLite support: All SQLite connection paths, schema definitions, adapters, and
import sqlite3removed from production code SQLiteBackend: Removed fromreport_storage.py(~165 lines) — PostgreSQL and in-memory backends remain- gRPC proto-generated stubs:
spiderfoot_pb2.pyandspiderfoot_pb2_grpc.pyremoved from repo (regenerate withscripts/generate_proto.py) - Legacy test files:
test_spiderfootdb.py,test_spiderfootdb_enhanced.py,test_spiderfootdb_extended.py,test_report_storage.py(SQLite-dependent) - Obsolete test infrastructure:
test_harness.py,benchmark.py— monolith-era utilities
- GraphQL subscription DB leak: Connection created once before polling loop instead of per-iteration
- Canvas animation memory leak: Proper
cancelAnimationFramecleanup in GraphTab - localStorage QuotaExceededError:
safeStorage.tswith LRU eviction for report cache - Background polling:
refetchIntervalInBackground: falseas QueryClient default - Token refresh → centralized
saveTokens() - Clipboard unhandled rejections:
.catch()on allnavigator.clipboard.writeText()calls - useEffect dependency arrays: Fixed stale closures in App.tsx
- Report store thread-safety: Added
threading.Lockto in-memory fallback dict - Unbounded multi-scan batches: Capped at 50 IDs per request
__version__.pyVERSION tuple crash: Pre-release suffix (e.g.6.0.0-rc.1) causedValueErrorintuple(map(int, ...))— now strips suffix before parsing- Go CLI Makefile stale version: Changed from hardcoded
5.9.2to$(shell cat ../VERSION) - Go CLI
GetRawUser-Agent: Fixed stale5.9.2in export request header sfcli.pyentry point: Addedmain()function forconsole_scriptscompatibility; guarded unconditional debug print behind-dflag- CI branch references: Fixed 4 workflows targeting
masterinstead ofmain(build-artifacts, wiki-sync, semgrep, codeql-analysis) - CodeQL
actions/checkout@v2: Updated to@v4 - Build-artifacts packaging gates: Changed from Python 3.9 (not in matrix) to 3.10 so .deb/.rpm/snap/Homebrew steps actually run
- Acceptance tests: Fixed port
5001→8001and endpoint/ping→/healthfor v6 API - Dockerfile HEALTHCHECK: Standardized to
/healthendpoint docker/build.sh: Added active-worker build step (5th image); added configurableREGISTRYprefix
- Go CLI in CI: New
go-clijob inci.yml—go vet,go test -race, smoke build - Go CLI cross-compilation in releases: 6-platform matrix build in
release.ymlwith artifacts attached to GitHub Releases - Go vulnerability scanning:
govulncheckstep insecurity-scan.ymlforcli/go.mod - Full-stack deployment test: New
deploy-test.ymlworkflow — Docker build, API smoke test, Go CLI integration against live API, frontend production build validation, summary gate - Semgrep action: Updated from pinned SHA to tagged
@v1 - Python 3.13 classifier: Added to
setup.py
- Dual-CLI strategy: README now documents both Go CLI and Python REPL CLI with comparison table and recommended use cases
- Go CLI README: Fixed
--cron→--intervalin schedule examples; addedupdatesubcommand - Pipfile: Added deprecation notice — canonical deps are
requirements.txt
- SSO callback error sanitization: Replaced
str(e)in OAuth2 callback and SAML ACS error redirects with generic "SSO authentication failed" message; full exception logged server-side vialog.exception() - Docker hardening documentation: Added comprehensive comment block documenting which 13 services are hardened and why each of the 10 remaining services is excluded (writable filesystem requirements)
- Route-level code splitting expanded: Moved ScanDetail, NewScan, and Settings from eager imports to
React.lazy()with<Suspense>boundaries — now 10 of 12 pages are lazy-loaded for smaller initial bundle
- 99 additional tests across 3 new test files:
auth.test.ts(36): Zustand auth store — saveTokens, clearTokens, setTokensFromUrl, hasPermission (all roles), login/LDAP errors, token refresh, user fetchapi.test.ts(45): API utilities — formatEpoch/formatDuration edge cases, statusColor/statusBadgeClass all variants, getErrorMessage for all error shapesLayout.test.tsx(18): Layout component — nav items, dropdowns, admin visibility, user menu, sign out, about modal, mobile header, theme toggle
- Total: 230 tests across 11 files, all passing
- Auth route info leak closure: Sanitized
detail=str(e)in token refresh (catch-allException) and LDAP login (ImportErrorcould expose filesystem paths); generic messages returned, full details logged server-side - Gateway error sanitization: Removed internal exception message from
GatewayErrorin api_gateway.py; prevents service internals from reaching clients - Docker hardening expansion: Added
no-new-privileges,read_only, andtmpfsto 10 additional services (redis, frontend/nginx, qdrant, vector, tika, agents, celery-beat, flower, litellm, pg-backup) — now 13/23 services hardened
- Workspaces.tsx deduplication: Replaced 115-line inline
renderSimpleMdwith sharedMarkdownRenderercomponent; removed unusedsanitizeHTMLandinlineFormatimports
- 92 component/page tests: UI components (47 tests: StatusBadge, Toast, Tabs, ConfirmDialog, ModalShell, Expandable, EmptyState, PageHeader, ProgressBar), ErrorBoundary (8 tests), MarkdownRenderer component (16 tests), Login page (21 tests with mocked auth/API)
- Frontend CI job: Added to
.github/workflows/ci.yml— Node 20, TypeScript type-check, ESLint, Vitest (131 tests), coverage reporting. Integration tests now gate on both backend and frontend
- WebSocket authentication: Added
_verify_ws_token()that validates?token=<jwt_or_api_key>query parameters; unauthenticated connections are rejected with code 4003 beforewebsocket_manager.connect()(BaseHTTPMiddleware does not intercept WS) - Scan progress endpoint auth: Added
Depends(optional_auth)to all 6 scan_progress REST/SSE endpoints; sanitized 5 error messages that leaked scan IDs - Auth token localStorage safety: Wrapped
saveTokens/clearTokensand the refresh interceptor in try/catch to prevent QuotaExceededError from breaking auth flows
- AbortSignal for request cancellation: Added optional
signal?: AbortSignalto all 84 API methods in api.ts; updated all 38queryFncall sites across 17 files to forward TanStack Query's signal — enables automatic cancellation on component unmount - Admin pages → React Query: Migrated SSOSettings.tsx (1 query + 3 mutations), Users.tsx (1 query + 4 mutations), ApiKeys.tsx (1 query + 3 mutations) from manual useState + useEffect to useQuery/useMutation with signal support
- Shared MarkdownRenderer: Extracted 110-line
renderMarkdownToHTML()andinlineFormat()into a shared component; replaced duplicate code in ReportTab.tsx and Workspaces.tsx - Settings.tsx DOM leak: Replaced
document.createElement('input')with a ref-based hidden<input>in JSX; added proper append/remove for the download anchor
- Vitest frontend test foundation: Added vitest + @testing-library/react + jsdom; 39 tests across 4 suites: sanitize (7 XSS tests), MarkdownRenderer (14 render tests), safeStorage (4 quota tests), API helpers (14 utility tests)
- Error detail leak sweep (25 more instances): Sanitized 5 GraphQL resolver
message=str(e), 13 health endpointstr(e)(leaking DSNs/URLs), and 7 scan bulk-op / reports / enginesstr(e)with generic messages; all server-side logging preserved - Path parameter validation: Added
SafeId(^[a-zA-Z0-9_\\-]{1,64}$) andSafeNametype aliases to dependencies.py; applied to 59 route handlers across scan.py (38), workspace.py (17), and reports.py (4) — FastAPI returns 422 for non-matching input - Content-Security-Policy header: Added
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'toSecurityHeaders.DEFAULT_HEADERS - HSTS opt-in: Added
get_headers()classmethod that includesStrict-Transport-Security: max-age=63072000; includeSubDomainswhenSF_HSTS_ENABLEDenv var is set - Docker container hardening: Added
no-new-privileges:true,read_only: true, and tmpfs mounts to api, celery-worker, and celery-worker-active containers
- localStorage QuotaExceededError: Added
safeStorage.tswithsafeSetItem()— truncates values to 100 KB, catches quota errors with LRU eviction of report cache keys; applied to 6 report-caching writes in Workspaces.tsx and ReportTab.tsx
- Path traversal in MinIO / Qdrant storage: Added
_validate_safe_name()in minio_manager.py (rejects..,/,\, non-alphanumeric-dot-hyphen); validated bucket names (must start withsf-, S3-compliant) and collection/name params in the storage router - SQL identifier injection in db_utils.py: Added
_quote_ident()helper that double-quotes SQL identifiers and rejects embedded quotes; applied to allDROP TABLEstatements and parameterized theinformation_schema.columnsWHERE clause - 55 error detail leaks sanitized: Replaced all
detail=str(e)anddetail=f"...{e}"patterns across 14 router files (data, rbac, rbac_enhanced, schedules, storage, tenants, workspace, visualization, scan, rag_correlation, keys, engines, export, correlations) with static generic messages; added server-sidelog.warning/log.exception
_report_storethread-safety: Addedthreading.Lockto the in-memory fallback report dict in reports.py; all reads, writes, deletes, and clears wrapped inwith _report_lock:- Unbounded multi-scan batch endpoints: Added
MAX_MULTI_SCAN_IDS = 50cap toexport-multi,viz-multi, andrerun-multi; returns 400 when exceeded - Config untyped dict bodies: Replaced 6 raw
dict = Body(...)params with Pydantic models:ApiKeyCreateRequest(name/scopes/expires with bounds),CredentialCreateRequest,ConfigImportRequest, plusextra="allow"models for freeform option endpoints
- About modal → ModalShell: Replaced ad-hoc About dialog in Layout.tsx with the existing
ModalShellcomponent, gainingrole="dialog",aria-modal,aria-labelledby, focus trap, and Escape key handling; removed unused X icon import
- XSS in PDF export:
scan.name,scan.targetandscanIdwere interpolated raw intodocument.write()in ReportTab PDF export; addedescapeHTML()helper and wrapped all dynamic values - Concurrent token refresh race: Multiple 401s triggered independent
/api/auth/refreshcalls, invalidating single-use rotation tokens and logging users out; added sharedrefreshPromisededuplication - Route-level RBAC: Added
RequirePermissioncomponent;/usersrequiresuser:read,/sso-settingsand/api-keysrequiresystem:admin; falls through when auth is disabled (dev mode) - Hardcoded credentials removed: Removed
changeme123MinIO fallbacks from 5 Python files; removedchangemePostgreSQL DSN from maintenance.py; MinIOConfig now logs CRITICAL on insecure defaults; stripped credential hints from Layout.tsx service links - SSRF webhook URL validation: Added
_validate_webhook_url()— requires http(s) scheme, blocks private/loopback/link-local/reserved IPs and known dangerous hostnames; applied to all webhook Pydantic models
- GraphQL subscription DB leak:
scan_progressandscan_events_livecalled_get_db()inside polling loops, leaking ~30 connections/min per subscriber; DB handle now created once before the loop and closed infinally - scan-profiles missing auth:
list_scan_profiles()andget_scan_profile()were the only unprotected endpoints on the scan router; addedapi_key_dep - Error detail leaks: Replaced 4
detail=str(e)/detail=f"...{e}"patterns in scan.py and export.py with generic messages; addedexc_info=Trueserver-side logging - Clipboard unhandled rejections: Added
.catch()tonavigator.clipboard.writeText()in CopyButton, Scans Copy ID, and ApiKeys Copy Key - Background polling waste: Set
refetchIntervalInBackground: falseas QueryClient default — polling pauses when tab is hidden - Token refresh bypasses saveTokens:
refreshAccessToken()now uses centralizedsaveTokens()instead of rawlocalStorage.setItem
- SQL injection in db_event.py: Replaced string-interpolated
INclauses inscanElementSourcesDirectandscanElementChildrenDirectwith parameterized placeholders; added early-return on empty input - Jinja2 SSTI sandbox: Replaced
jinja2.Environmentwithjinja2.sandbox.SandboxedEnvironmentin report_templates.py; replaced dangerousstr.format()fallback withstring.Template.safe_substitute() - SSO tokens moved from URL to hash fragment: OAuth2 callback and SAML ACS now redirect to
/#access_token=…instead of/?access_token=…; frontend reads fromwindow.location.hash— tokens no longer appear in server logs or Referer headers
- API auth on 20 unprotected routers: Added
Depends(get_api_key)to all remaining unprotected router groups (~170 endpoints); only health, SSO, auth, and WebSocket routers remain intentionally public - Stored XSS in email notifications: Wrapped title, message, and data key/value interpolations in
_send_emailwithhtml.escape()to prevent stored XSS via crafted scan names or event data - Content-Disposition header injection: Added
safe_filename()utility to sanitise download filenames (strips CR/LF, quotes, backslashes, path separators, non-ASCII); applied across all 16 Content-Disposition headers in export, scan, visualization, workspace, and reports routers
- useEffect dependency arrays: Fixed missing deps in App.tsx (
setTokensFromUrl,fetchAuthStatus,fetchCurrentUser) to prevent stale closures - Canvas animation memory leak: GraphTab now stores
requestAnimationFrameID and callscancelAnimationFramein cleanup; uses stablenodes.length/edges.lengthdeps - SearchInput debounce: Added
useDebounce<T>hook; SearchInput accepts optionaldebounceMsprop; enabled on LogTab and BrowseTab to reduce re-renders during typing - React.memo on scan tabs: Wrapped all 8 scan tab components (
SummaryTab,SettingsTab,LogTab,BrowseTab,CorrelationsTab,ReportTab,GeoMapTab,GraphTab) withReact.memoto skip unnecessary re-renders
- API auth on 5 unprotected router groups: Added
Depends(get_api_key)to ASM, Marketplace, RBAC Enhanced, Data Retention, and Distributed Scan routers — 60+ endpoints were previously accessible without authentication - React ErrorBoundary: Added top-level
ErrorBoundarycomponent wrapping<App />; catches render crashes and shows a recoverable fallback UI instead of a white screen - Unsafe JSON.parse in ApiKeys.tsx: Wrapped
JSON.parse(k.allowed_modules)in render path with try/catch to prevent runtime crash on malformed data - Dead file upload UI removed: Removed non-functional drag-and-drop file upload from NewScan page (files were never sent to the API, misleading users)
- Shell injection in sfp_tool_gobuster: Replaced
shell=Truewithsubprocess.run(cmd_list)(no shell) andshutil.which()for path resolution; also fixed calls to non-existentself.sf.execute()method - Mutation error feedback: Added
onErrorhandlers to 14 React Query mutations that silently swallowed failures (ScanDetail, Scans, Modules, Workspaces, CorrelationsTab, ReportTab, BrowseTab) - GraphQL error logging: Added
exc_info=Trueto all 20_log.error()calls in resolvers.py for proper stack traces in logs
- ModalShell a11y overhaul: Added
role="dialog",aria-modal="true",aria-labelledby, Escape key handler, focus trap (Tab/Shift+Tab cycle), auto-focus on mount,aria-labelon close button - Deduplicated ModalShell: Removed duplicate definition from SSOSettings.tsx; now uses shared component from
components/ui
- Ethereum address detection: Fixed
detectTargetType()in Workspaces.tsx — Ethereum addresses (0x...) were incorrectly labeled asBITCOIN_ADDRESS; added properETHEREUM_ADDRESStype - Resource management: Wrapped 3
urllib.request.urlopen()calls innotifications.pywith context managers to properly close HTTP responses
- JWT hardening: Replaced hand-rolled HMAC-SHA256 JWT with PyJWT library; auto-detects insecure default secrets (
changeme,secret, etc.) and generates cryptographic random secret at startup; logs CRITICAL warning for insecure configurations - Auth bypass lockdown: API key dev-mode bypass now requires explicit
SF_AUTH_DISABLED=trueenvironment variable (previously any misconfiguration could skip auth) - CORS lockdown: Default allowed origins changed from
*tohttp://localhost:3000,https://localhost; logs WARNING when wildcard CORS is active - XSS prevention: Wrapped all 7
dangerouslySetInnerHTMLusages inScanDetail.tsxandWorkspaces.tsxwith DOMPurify sanitization (strict tag/attribute allow-list) - Removed
python-josedependency in favor of solepyjwtJWT library
- ScanDetail.tsx split: Decomposed 1,979-line monolith into 10 focused tab components (
SummaryTab,BrowseTab,CorrelationsTab,GraphTab,GeoMapTab,ReportTab,SettingsTab,LogTab,MiniStat,ExportDropdown) + sharedgeo.tsutility; page shell reduced to ~130 lines - ESLint flat config: Added
eslint.config.js(ESLint v9) withtypescript-eslint,react-hooksplugin, and@typescript-eslint/no-explicit-anywarning rule - Eliminated all 18 explicit
anytypes: Created sharedgetErrorMessage()utility usingaxios.isAxiosError()for type-safe error extraction; replaced 15catch(err: any)patterns, fixed 1onError: (err: any), fixed 2 untyped.map()callbacks - Fixed ESLint errors: unnecessary escape characters, ternary-as-statement expressions
- Added
self.checkForStop()guards to 35 critical module loops (15 critical ≥40 body lines, 20 high-priority 24-39 body lines) enabling graceful scan cancellation - Modules patched:
sfp_leakcheck,sfp_tool_tlsx,sfp_tool_sslyze,sfp_tool_testsslsh,sfp_keybase,sfp_leakix,sfp_greynoise,sfp_arbitrum,sfp_grep_app,sfp_tool_dnsx,sfp_builtwith,sfp_names,sfp_certspotter,sfp_dehashed,sfp_mnemonic,sfp_circllu,sfp_tool_onesixtyone,sfp_tool_gitleaks,sfp_alienvault,sfp_aparat,sfp_tool_linkfinder,sfp_tool_sslscan,sfp_apileak,sfp_tool_gospider,sfp_company,sfp_discord,sfp_wechat,sfp_douyin,sfp_rocketreach,sfp_xiaohongshu,sfp_emailcrawlr,sfp_tool_nikto,sfp_tool_dalfox,sfp_apple_itunes,sfp_hackertarget
- Removed dead
import urllib.errorandimport urllib.requestfrom 30 modules (onlyurllib.parsewas used) - Fixed
sfp_zoomeyelatentNameErrorbug: was catchingurllib.error.HTTPError/URLErrorwithout importingurllib— dead except blocks removed since module usesself.fetch_url() - Removed 3,445 lines of dead test code: 120 never-implemented integration test stubs (
@unittest.skip("todo")with dummy data), 4 broken unit test stubs (hadselfdepth=0instead ofself, depth=0) - Fixed unconditional
skipIf(True)intest_sfcli_enhanced.py→ properos.name == 'nt'platform guard
- Removed unused
werkzeugfrom requirements (never imported) - Moved
openaito optional comment (LLM client uses raw HTTP, never imports the package) - Added note that
weasyprintis optional (PDF export only)
- Modules page: Enabled/Disabled stat cards now compute counts client-side from per-module status map; previously relied on server aggregate fields that could lag after toggling a module
- GeoMap tab: Fixed latitude projection using correct simplemaps SVG bounds (83.65°N – 56°S) instead of ±90° pole-to-pole; southern-hemisphere markers were shifted up to 141 px northward
- Version bump to 5.9.2 across all files (VERSION, package.json, Layout.tsx, README badge, Homebrew formula, ARCHITECTURE.md, overview.md, sfp_aprsfi User-Agent, test fixtures)
- Deleted 8 modules for offline/shutdown services:
sfp_crobat_api(sonar.omnisint.io shut down 2022),sfp_crxcavator(CRXcavator shut down 2023),sfp_dnsgrep(bufferover.run shut down),sfp_fsecure_riddler(riddler.io discontinued 2021),sfp_phishstats(phishstats.info offline),sfp_psbdmp(psbdmp.cc offline),sfp_punkspider(punkspider.org shut down),sfp_robtex(free API deprecated) - Deleted corresponding unit and integration tests (16 test files)
- sfp_flickr: Replaced broken
retrieveApiKey()(scrapedYUI_config.flickr.api.site_key) with user-provided API key; changed model fromFREE_NOAUTH_UNLIMITEDtoFREE_AUTH_UNLIMITED - sfp_keybase: Added null-safety checks and maintenance-mode note (Zoom acquisition 2020); removed unused imports
- sfp_virustotal: Full v2 → v3 API migration (endpoints, auth header
x-apikey, relationships API, response parsing) - sfp_greynoise: Full v2 → v3 API migration (IP lookup, GNQL queries,
cve→cvesfield, response normalization) - sfp_nameapi: Fixed HTTP → HTTPS for API endpoint
- sfp_subdomainradar: Fixed 3 critical API structure mismatches (auth header, response parsing, endpoint paths)
- Consolidated
docker-compose.ymlanddocker-compose-simple.ymlinto a single compose file using Docker Compose profiles - 5 core services (postgres, redis, api, celery-worker, frontend) always start without any profile
- 7 opt-in profiles:
scan,proxy,storage,monitor,ai,scheduler,sso fullmeta-profile activates all profiles exceptsso- Core services use
${VAR:-fallback}env var patterns for graceful degradation without optional services (embedding/reranker default tomock, qdrant tomemory, minio/tika/OTEL to empty)
- Deleted
docker-compose-simple.yml(replaced by core-only profile of unified compose file) - Deleted
docker/env.simple.example(replaced by.env.exampleprofile sections)
- Updated README.md with profile-based Quick Start, Deployment Modes, and Services tables
- Updated
docker_deployment.md,quickstart.md,getting_started.md,user_guide.md,installation.md,active-scan-worker.mdwith profile commands - Restructured
.env.examplewith profile-organized sections (core active, profile vars commented)
PostgreSQLBackendfor report storage — replaces SQLite in microservices deployments- Auto-detection of
SF_POSTGRES_DSNfrom environment — zero-config upgrade - psycopg2-based backend with
ON CONFLICTupsert, thread-local connections, same API asSQLiteBackend StoreConfigauto-selects PostgreSQL when DSN is available, falls back to SQLite otherwiseStorageBackendenum expanded withPOSTGRESQLvariant- Health dashboard now reports
"backend": "postgresql"when running in Docker
spiderfoot.tasks.scan— Celery-based scan execution replacingBackgroundTasks/mp.Processrun_scantask with 24h hard / 23h soft time limits,acks_late=True, deduplication guard- Crash recovery via dedup guard (checks terminal states, stale
RUNNINGwith progress age) - Scan progress stored in Redis hashes (
sf:scan:progress:{scan_id}) and published via pub/sub abort_scan,run_batch_scans,update_scan_progresssupporting taskscelery_app.py— central Celery configuration with 6 task queues (default,scan,report,export,agents,monitor), auto-routing, JSON+msgpack serialization, beat schedule (hourly cleanup, 5-min health checks)
ScanProfiledataclass with module selection by flags, use cases, categories, explicit include/excludeProfileManagersingleton with 10 built-in profiles:quick-recon,full-footprint,passive-only,vuln-assessment,social-media,dark-web,infrastructure,api-powered,minimal,tools-onlyProfileCategoryenum (reconnaissance, vulnerability, social, infrastructure, dark_web, custom)- JSON import/export, directory loading, auto-exclude deprecated modules
- API endpoints:
GET /scan-profiles,GET /scan-profiles/{name} - Frontend profile picker in New Scan page with category badges and module counts
- Full
[data-theme="light"]CSS with reversed semantic color scale - Theme-aware badge classes:
badge-critical,badge-high,badge-medium,badge-low,badge-info,badge-success - Status dot, risk pill, health badge, and correlation card classes with proper light-background contrast
StatusBadgeandRiskPillscomponents with dual-theme CSS class system- Three-way theme toggle (Light / Dark / System) in sidebar
- New Scan page: 4-tab module selection (By Use Case, By Profile, By Required Data, By Module), target type auto-detection (domain/IP/email/phone/ASN/BTC/ETH/username/name), document upload with drag-and-drop
- Workspaces page: multi-target workspace grouping with 6 tabs (overview, targets, scans, correlations, geomap, report)
- Correlations tab: first-class tab in scan detail and workspace views, on-demand correlation runs, risk breakdown summary
- Dashboard: health panel with
healthApi.dashboard, stat cards from search/facets API, 15s auto-refresh - Scans page: server-side search with facets, bulk stop/delete operations
- Services dropdown in sidebar: AI Agents, Grafana, Jaeger, Prometheus, Traefik, MinIO, Flower
- 9 tool modules migrated to
SpiderFootModernPluginbase class:sfp_tool_whatweb,sfp_tool_trufflehog,sfp_tool_testsslsh,sfp_tool_snallygaster,sfp_tool_retirejs,sfp_tool_onesixtyone,sfp_tool_nbtscan,sfp_tool_dnstwist,sfp_tool_cmseek - All tool modules now have
from __future__ import annotations, typed return annotations, andtoolDetailsinmetadict setup()signatures standardized withsuper().setup(sfc, userOpts or {})
- API health endpoint on port 8687 for container healthcheck
- Loki sink fixes for 400-error prevention
- Traefik access log source added
- Jaeger datasource added to Grafana provisioning
- Vector container healthcheck in docker-compose
- Docker CI workflow (
.github/workflows/docker.yml) with two-stage build (base + 4-service matrix), GHCR push, semver tagging fromVERSIONfile - MinIO now creates 7 buckets (
sf-logs,sf-reports,sf-pg-backups,sf-qdrant-snapshots,sf-data,sf-loki-data,sf-loki-ruler) Dockerfile.active-workerfor active scan tools (external binaries)- Flower monitoring dashboard for Celery added to compose
modules.md: corrected to 36 external tool integrations, removed duplicatesfp_tool_wappalyzertable entrydocker_deployment.md: corrected bucket list to 7 (was 5), fixedsf-qdrant→sf-qdrant-snapshotsnameREADME.md: version badge updated to 5.9.0
- Scan restart loop: scans no longer restart endlessly when Celery workers reconnect
- Scan profiles API resolution: profile-based module resolution now works correctly
- Light theme contrast: badges, status dots, risk pills, correlation cards now readable on white backgrounds
- Health tab cleanup: removed 6 broken health checks, kept 9 meaningful subsystem probes
- Loki
expand-envflag: added-config.expand-env=trueto Loki command for envvar interpolation - Report storage backend indicator: health dashboard now correctly shows
postgresqlinstead ofsqlite - Orphaned
fetchUrlmethod removed fromspiderfoot/__init__.py(module-level function withselfparameter) __version__.pyfallback corrected from nonsensical5.245.0to5.9.0PostgreSQLBackendmissing from__all__inspiderfoot/reporting/__init__.py
- Grafana 11.4.0 dashboard service with auto-provisioned SpiderFoot Overview (12 panels)
- Loki 3.3.2 log aggregation with MinIO S3 backend, TSDB indexing, 30-day retention
- Prometheus 2.54.1 metrics collection with 10 scrape targets (api, scanner, agents, enrichment, vector, qdrant, minio, jaeger, litellm, self)
- Pre-built Grafana datasources: Loki, Prometheus, PostgreSQL
- Activated Vector.dev → Loki log sink with service/job/level labels
- Activated Vector.dev → Prometheus exporter on :9598 with
spiderfootnamespace
- Jaeger 2.4.0 all-in-one tracing service
- Vector.dev OTLP source (gRPC :4317, HTTP :4318) for trace ingestion
- Vector.dev → Jaeger OTLP sink for trace forwarding
spiderfoot/observability/tracing.py— OpenTelemetry instrumentation withget_tracer(),trace_span()context manager, graceful no-op fallback
- LiteLLM v1.74.0 unified LLM proxy with OpenAI-compatible API
- Multi-provider support: OpenAI (gpt-4o, gpt-4o-mini, gpt-3.5-turbo), Anthropic (claude-sonnet, claude-haiku), Ollama (llama3, mistral, codellama)
- Embedding models: text-embedding-3-small, text-embedding-3-large
- Redis-backed response caching (db:2), Prometheus callbacks for cost tracking
- Router aliases: default→gpt-4o-mini, fast→gpt-3.5-turbo, smart→gpt-4o, local→ollama/llama3
- API service now routes LLM calls through
SF_LLM_API_BASE=http://litellm:4000
spiderfoot/agents/package with 6 analysis agents:- FindingValidator — validates MALICIOUS_/VULNERABILITY_/LEAKED_* findings, produces verdict/confidence/remediation
- CredentialAnalyzer — assesses LEAKED_CREDENTIALS/API_KEY_* exposure risk
- TextSummarizer — summarizes RAW_/TARGET_WEB_CONTENT/PASTE_ content with entity/sentiment extraction
- ReportGenerator — generates executive summaries on SCAN_COMPLETE with threat assessment
- DocumentAnalyzer — analyzes DOCUMENT_UPLOAD/USER_DOCUMENT for entities/IOCs, supports large document chunking
- ThreatIntelAnalyzer — maps MALICIOUS_/CVE_/DARKNET_* to MITRE ATT&CK techniques
BaseAgentABC with concurrency semaphore, timeout handling, LLM calling (aiohttp → LiteLLM), Prometheus metrics (processed_total, errors_total, avg_processing_time_ms)- FastAPI agents service (:8100) with /agents/process, /agents/analyze, /agents/report, /agents/status, /metrics, /health endpoints
- Redis pub/sub event listener for automatic agent dispatch with wildcard pattern matching
spiderfoot/enrichment/package:- DocumentConverter — PDF (pypdf), DOCX (python-docx), XLSX (openpyxl), HTML, RTF (striprtf), text; optional Tika fallback
- EntityExtractor — pre-compiled regex for IPv4/IPv6, emails, URLs, domains, MD5/SHA1/SHA256, phone numbers, CVEs, Bitcoin/Ethereum, AWS keys, credit cards; smart dedup and private IP filtering
- EnrichmentPipeline — orchestrates convert → extract → store (MinIO sf-enrichment bucket) with SHA256-based document IDs
- FastAPI enrichment service (:8200) with /enrichment/upload (100MB limit), /enrichment/process-text, /enrichment/batch, /enrichment/results/{id}, /metrics, /health
spiderfoot/user_input/package:- POST /input/document — upload → enrichment → agent analysis chain
- POST /input/iocs — IOC list submission with deduplication
- POST /input/report — structured report → entity extraction → agent analysis → MinIO
- POST /input/context — scope/exclusions/known_assets/threat_model per scan
- POST /input/targets — batch target list for multi-scan
- Automatic forwarding to enrichment and agents services via HTTP
- Submission tracking with GET /input/submissions and /input/submissions/{id}
- Docker Compose expanded from 10 → 17 containers
- MinIO init now creates 8 buckets (added sf-loki-data, sf-loki-ruler, sf-enrichment)
- Nginx config expanded with upstream blocks and location routing for Grafana (with WebSocket), Prometheus, Jaeger, LiteLLM, agents, enrichment, user-input
docker/env.exampleexpanded with monitoring, tracing, LLM, and resource limit variablesconfig/vector.toml— Loki sink activated, Prometheus exporter activated, OTLP trace source + Jaeger sink added- New
infra/directory with configs:loki/local-config.yaml,grafana/provisioning/,grafana/dashboards/,prometheus/prometheus.yml,litellm/config.yaml - Docker networks: sf-frontend (bridge), sf-backend (internal) — all new services on sf-backend
- New volumes: grafana-data, prometheus-data
- README.md: updated Mermaid architecture diagram (17 containers), version badge (5.3.3), services table, Quick Start URLs, project structure; added Monitoring, AI Agents, Document Enrichment, User-Defined Input, LLM Gateway sections
- ARCHITECTURE.md: updated topology diagram, service table, package listing; added AI Agents, Enrichment, User Input, LLM Gateway, Observability Stack sections
docker/env.example: comprehensive example with all new service configuration