flowsint

repository·main·Indexed 27 days ago

https://github.com/reconurge/flowsint

An open-source OSINT graph analysis and intelligence platform (version 1.0.0) designed for ethical investigation, reconnaissance, and verification. It features a visual graph interface, automated data enrichers, and a modular architecture consisting of flowsint-api, flowsint-app, flowsint-core, flowsint-enrichers, and flowsint-types.

Tokens
34.2K
Snippets
74
Records
137
Agent score
93%

What's inside flowsint

  1. Overview of Flowsint capabilities

    main

    Flowsint is a modular OSINT (Open Source Intelligence) investigation and reconnaissance platform. It is designed to provide a stable infrastructure for intelligence gathering, allowing tools to be integrated as pluggable extensions rather than siloed scripts.

    Key features include:

    • Graph-based visualization: View entity relationships visually.
    • Automated Enrichers: Access to 30+ automated enrichers for intelligence gathering.
    • Modular Architecture: Clean separation of concerns for extensibility.
    • Privacy-first Design: Local data storage and local execution of enrichers.
    • Automated Search Flows: Orchestrated investigation workflows.
  2. Overview of Flowsint core modules

    main

    Flowsint is organized into several autonomous modules:

    • flowsint-core: Core utilities, orchestrator, vault, celery tasks, and base classes (handles DB connections, Auth, Logging, and Config).
    • flowsint-types: Pydantic models and type definitions (Domain, IP, ASN, Individual, etc.).
    • flowsint-enrichers: Enricher modules and scanning logic (Domain, IP, Social, Crypto, etc.).
    • flowsint-api: FastAPI server providing REST API endpoints, Auth, Graph integration, and real-time streaming.
    • flowsint-app: Frontend application (UI).

    Dependency Flow: flowsint-app $\rightarrow$ flowsint-api $\rightarrow$ flowsint-core $\rightarrow$ flowsint-enrichers $\rightarrow$ flowsint-types

  3. Understand Enrichers and Pivots

    main

    In Flowsint, an Enricher is an operation that transforms a source entity (Input A) into one or more target entities (Output B) using a specific method called a Pivot.

    The Pivot is the technical process or method used to derive the result (e.g., a DNS resolution, a WHOIS lookup, or an API query).

    Conceptual Flow: Source Entity (A) -> Pivot (Method) -> Target Entity (B)

    Examples:

    • domain name $\rightarrow$ DNS resolution $\rightarrow$ IP address
    • IP address $\rightarrow$ WHOIS Lookup $\rightarrow$ owner
    • image $\rightarrow$ Reverse Image Search $\rightarrow$ web pages containing that image
  4. Use the Query Key Factory for TanStack Query

    main

    The project uses @lukemorales/query-key-factory to manage TanStack Query keys. This provides type-safe query keys with auto-completion, centralized management, and easy invalidation without hardcoded strings.

    Key files:

    • src/api/query-keys.ts: Main query key definitions.
    • src/api/query-keys-examples.ts: Usage examples and custom hooks.
  5. Understand Flowsint core terminology

    main

    To use the Flowsint platform effectively, it is important to understand the following domain-specific concepts:

    • Investigation: A structured process of collecting and analyzing information. It can be exploratory (discovering new elements) or targeted (validating hypotheses). An investigation consists of multiple sketches and one or more analyses.
    • Sketch: A visual representation of the current state of the investigation graph, produced by executing enrichers on entities.
    • Analysis: The processing and interpretation of collected data to identify trends or confirm/refute hypotheses. These can be quantitative (statistical) or qualitative (contextual).
    • Flow: The process of chaining multiple enrichers together, where the output of one enricher serves as the input for the next to expand an investigation.
  6. Understand the Flowsint type system

    main

    The Flowsint type system is built on Pydantic models located in the flowsint-types package. Every data type must be a Python class that inherits from FlowsintType and must be decorated with @flowsint_type to be registered in the global type registry. This registration enables automatic validation, serialization, JSON schema generation, auto-discovery, and graph-specific features like automatic label generation.

    Types are typically organized in flowsint-types/src/flowsint_types/, where each type (or group of closely related types) resides in its own Python file.

  7. Important usage warnings and legal disclaimer

    main

    Flowsint is a powerful tool that requires responsible use. Users must adhere to the following guidelines:

    • You are solely responsible for your use of the software.
    • Use the tool in compliance with all applicable laws and regulations.
    • Obtain proper authorization before conducting any security testing or reconnaissance activities.

    Infrastructure and Risk

    • Local Execution: All enrichers run locally on your machine. This ensures confidentiality but places the burden of risk on the user.
    • Service Bans: Rapid, large-scale requests (e.g., performing DNS resolution for 10,000 IPs) can lead to being banned from services or flagged by infrastructure providers.
    • Scale Awareness: Be aware of the scale at which gathering can occur and understand the underlying mechanics of the tools used in the enrichers.
  8. Understand the Enricher architecture

    main

    Enrichers are the high-level business logic layer in Flowsint. They orchestrate intelligence gathering workflows by taking input data, processing it through tools or APIs, and creating Neo4j graph nodes and relationships.

    Unlike Tools (which are low-level wrappers for raw data), Enrichers are high-level workflows that understand types, handle parameters, and manage the graph database structure.

    Every enricher follows a two-phase execution model:

    1. Scanning Phase: An async method where core logic (API calls, tool execution, data gathering) occurs. Input is automatically validated via Pydantic.
    2. Postprocessing Phase: A method where graph nodes and relationships are created in Neo4j based on the scan results and original input.
  9. Handle multiple output types using Pydantic

    main

    If an enricher produces multiple different types of entities (e.g., a crawler finding both emails and phone numbers), define a custom BaseModel using Pydantic to act as the OutputType. This model should aggregate the various discovered entities. In the postprocess phase, you can then iterate through the fields of this model to create nodes and relationships for each entity type.

    from pydantic import BaseModel
    from flowsint_types import Website, Email, Phone
    
    class CrawlerResults(BaseModel):
        website: Website
        emails: List[Email] = []
        phones: List[Phone] = []
    
    @flowsint_enricher
    class WebsiteToCrawlerEnricher(Enricher):
        InputType = Website
        OutputType = CrawlerResults
    
        async def scan(self, data: List[InputType]) -> List[OutputType]:
            # ... logic to populate CrawlerResults
            return [CrawlerResults(website=w, emails=e, phones=p)]
    
        def postprocess(self, results: List[OutputType], input_data: List[InputType]) -> List[OutputType]:
            for result in results:
                self.create_node(result.website)
                for email in result.emails:
                    self.create_node(email)
                    self.create_relationship(result.website, email, "HAS_EMAIL")
            return results
  10. Test Flowsint types with pytest

    main

    Create test files in flowsint-types/tests/ matching your type filename.

    Recommended Test Coverage:

    • Happy Path: Basic creation with valid data.
    • Validation: Testing both valid and invalid inputs (expecting ValueError).
    • Label Computation: Testing nodeLabel with and without optional fields.
    • Serialization: Verifying model_dump() (dict) and model_dump_json() (JSON) output.
    • Nested Objects: Testing relationships when a type contains other Flowsint types.
    # Example test structure
    from flowsint_types import Vehicle
    import pytest
    
    def test_vehicle_creation():
        """Test creating a vehicle with required fields."""
        vehicle = Vehicle(license_plate="ABC123")
        assert vehicle.license_plate == "ABC123"
    
    def test_vehicle_missing_required_field():
        """Test that validation fails without required fields."""
        with pytest.raises(ValueError):
            Vehicle()
  11. Implement Basic and Dynamic Queries with `queryKeys`

    main

    Replace hardcoded array keys in useQuery with the queryKeys object. For queries requiring parameters, call the corresponding key function with the required arguments.

    import { useQuery } from '@tanstack/react-query'
    import { queryKeys } from '@/api/query-keys'
    import { investigationService } from '@/api/investigation-service'
    
    // Basic query usage
    const { data } = useQuery({
      queryKey: queryKeys.investigations.list,
      queryFn: investigationService.get,
    })
    
    // Dynamic Keys with Parameters
    const { data: detailData } = useQuery({
      queryKey: queryKeys.investigations.detail(investigationId),
      queryFn: () => investigationService.getById(investigationId),
    })
  12. Test Docker-based tools with pytest

    main

    To test tools that require Docker, use the @pytest.mark.docker decorator. This allows you to separate tests that require a running Docker daemon from standard unit tests. Place your test files in flowsint-enrichers/tests/tools/ mirroring your tool's directory structure.

    # tests/tools/network/test_my_subdomain_tool.py
    from tools.network.my_subdomain_tool import MySubdomainTool
    import pytest
    
    @pytest.mark.docker
    def test_tool_install():
        """Test that the Docker image can be pulled."""
        tool = MySubdomainTool()
        tool.install()
        assert tool.is_installed()
    
    @pytest.mark.docker
    def test_tool_launch():
        """Test running the tool against a domain."""
        tool = MySubdomainTool()
        results = tool.launch("example.com")
        assert isinstance(results, list)