Skip to main content

Documentation Index

Fetch the complete documentation index at: https://quintsecurity.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Security Graph: Attack Path Visualization for AI Agents

The Precedent

Wiz built a $32B company partly because their Security Graph made invisible risk visible. The UX thesis: show a CISO “this misconfigured S3 bucket is reachable from this compromised EC2 via this role” as a traversable graph, and they immediately understand blast radius. Every major cloud security vendor copied the pattern:
VendorGraph BrandKey UX Move
WizSecurity GraphLeft-to-right attack path flow. Colored icons per resource type (green=network, orange=compute, blue=identity, teal=storage). Badge counts on nodes. “Click to expand” clusters. “View on Security Graph” link from every alert. Crown jewel terminus nodes highlighted.
Orca SecurityUnified Data ModelDark-themed horizontal flow with diamond-shaped risk nodes between resources. Split pane: “Attack Flow” graph on top, “Attack Story” narrative below explaining each hop in plain English. Business Impact Score per path. MITRE ATT&CK tags on each vector.
Microsoft DefenderCloud Security GraphSimple Attacker -> Internet -> Vulnerability -> Sensitive Data linear path as conceptual intro. Full product has “Cloud Security Explorer” with graph-based query builder (KQL-style). Blast radius view from any choke point. Now covers AI agents explicitly (2026 update).
Palo Alto Prisma CloudEvidence Graph”Intelligent Network Graph” combining network + config data. DSPM findings injected into attack path nodes so you see data classification at the terminus. Natural language queries via Prisma Copilot.
Cisco (ex-Lightspin)Attack Path EngineGraph-theory core from Lightspin acquisition (2023). Prioritized paths by severity. Agentless scanning. Integrated into Panoptica/ET&I.

What They All Share

Every implementation follows the same pattern:
  1. Left-to-right directed graph from entry point (Internet/Attacker) to crown jewel (database/model/PII)
  2. Typed nodes with distinct icons and colors per category
  3. Typed edges showing relationship semantics (permissions, network, data flow)
  4. Click-to-expand on clustered nodes (e.g., “8 Application Endpoints”)
  5. Narrative companion alongside the graph explaining the path in English
  6. Severity scoring on the path as a whole, not just individual nodes

Screenshot Reference (Wiz AI-SPM Attack Path)

Wiz’s AI-specific attack path visualization (2025) shows: Internet -> Application Endpoints (8, expandable) -> EC2 Instance (redhat-customer-related-v1, with CVE-2022-21724 finding) -> EC2IAMFullAccessRole -> s3-for-ai-training-test (S3 Bucket) -> test-bedrock-model (Bedrock Custom Model) This is the exact pattern we need, but for AI agent sessions instead of cloud infrastructure.

Quint’s Version: The Agent Security Graph

Nobody is doing this for AI agents yet. Wiz shows “Internet -> VM -> IAM Role -> S3 -> Bedrock Model.” We show “Claude Code session on Amer’s laptop -> Bash tool -> MCP postgres server -> production RDS -> PII table.” The insight: Wiz maps infrastructure attack paths. Quint maps agent capability paths — what an autonomous agent can reach given its tools, credentials, and MCP connections, cross-referenced against what it actually did.

Node Taxonomy

type NodeKind =
  // Physical layer
  | 'machine'           // Amer's MacBook Pro (hostname, OS, IP)
  | 'user'              // amer@quintai.dev (identity, role)

  // Agent layer
  | 'agent_session'     // Claude Code session abc-123 (pid, start_time, model)
  | 'agent_platform'    // Claude Code | Cursor | Copilot | Aider
  | 'model'             // claude-opus-4-7 | gpt-4o | claude-sonnet-4

  // Capability layer
  | 'mcp_server'        // github | postgres | slack | filesystem
  | 'tool'              // Bash | Read | Write | list_repos | query
  | 'permission'        // "allow Bash(*)" | "allow Read(/Users/**)"

  // Resource layer
  | 'cloud_resource'    // RDS instance | S3 bucket | ECS service | Lambda
  | 'local_resource'    // ~/.ssh/id_rsa | .env | ~/.aws/credentials
  | 'api_endpoint'      // api.quintai.dev/v1/events | github.com/api/v3

  // Data layer
  | 'data_class'        // PII | credentials | source_code | financial
  | 'secret'            // AWS_SECRET_ACCESS_KEY | GITHUB_TOKEN | DB password

Edge Taxonomy

type EdgeKind =
  // Structural
  | 'runs_on'           // agent_session -> machine
  | 'authored_by'       // agent_session -> user
  | 'powered_by'        // agent_session -> model
  | 'platform_is'       // agent_session -> agent_platform
  | 'spawned_from'      // child_session -> parent_session (subagents)

  // Capability
  | 'has_tool'          // agent_session -> tool
  | 'connects_to'       // mcp_server -> cloud_resource | api_endpoint
  | 'serves_tool'       // mcp_server -> tool
  | 'grants'            // permission -> tool (with scope)

  // Data flow (observed)
  | 'read'              // tool -> local_resource | cloud_resource
  | 'wrote'             // tool -> local_resource | cloud_resource
  | 'invoked'           // agent_session -> tool (with timestamp, args hash)
  | 'exfiltrated_to'    // tool -> api_endpoint (outbound data)

  // Credential chain
  | 'inherits_creds'    // mcp_server -> machine (ambient creds)
  | 'has_access_to'     // secret -> cloud_resource
  | 'exposed_in'        // secret -> local_resource (.env file)

  // Classification
  | 'contains'          // cloud_resource -> data_class
  | 'classified_as'     // local_resource -> data_class

The Headline Query

“Show me the blast radius of compromising this agent session.” Traverse outward from an agent_session node:
  1. What tools does it have access to?
  2. For each tool, what mcp_servers provide it?
  3. For each MCP server, what cloud_resources and api_endpoints does it connect to?
  4. For each cloud resource, what data_class does it contain?
  5. For each tool, what local_resources has it actually read/written (observed edges)?
  6. What secrets are reachable via ambient credential inheritance?
The result is a subgraph. Highlight it. That is the blast radius. The inverse query is equally powerful: “What agent sessions can reach this production database?” Traverse inward from the cloud_resource node.

UX Flow

Entry Point: Session Dashboard

User clicks a session row in the existing sessions list. Currently opens session detail view. New: add a “Graph” tab alongside “Timeline” and “Events.”

Graph View

+------------------------------------------------------------------+
|  Session: abc-123                    [Timeline] [Events] [Graph]  |
+------------------------------------------------------------------+
|                                                                    |
|  [Amer]--authored_by-->[Claude Code]--runs_on-->[MacBook Pro]     |
|       \                     |                                      |
|        \                    |--has_tool-->[Bash]                   |
|         \                   |--has_tool-->[Read]                   |
|          \                  |--has_tool-->[Write]                  |
|           powered_by        |--has_tool-->[mcp_postgres.query]     |
|            \                |                    |                 |
|             v               |              connects_to             |
|        [claude-opus-4-7]    |                    |                 |
|                             |                    v                 |
|                      spawned_from         [prod RDS]               |
|                             |                    |                 |
|                             v              contains                |
|                    [subagent-1]                   |                 |
|                                                  v                 |
|                                            [PII: emails]          |
|                                                                    |
+------------------------------------------------------------------+
|  Blast Radius: 3 cloud resources, 2 data classes, 14 local files  |
|  [Expand All]  [Show Only Observed]  [Export]                     |
+------------------------------------------------------------------+

Interactions

  • Click any node -> side panel shows detail (for agent_session: pid, duration, event count; for cloud_resource: ARN, region, data classes)
  • Click “Blast Radius” on any node -> highlights all reachable nodes with pulsing animation
  • Click “Can Reach” on any resource -> highlights all sessions/tools that can reach it (inverse traversal)
  • Toggle “Show Only Observed” -> dims capability edges, highlights only edges with actual observed events (tool invocations, file reads, network calls)
  • Drag to rearrange -> force-directed layout snaps back; manual positions persist per session

Divergence Overlay

This is the Quint-specific innovation no cloud security vendor has. Our three-tier architecture (Proxy intent / ES truth / Cloud storage) gives us two parallel views of reality:
  • Intent edges (green): what the agent said it would do (from proxy/MCP interception)
  • Truth edges (red): what actually happened (from Endpoint Security framework)
  • Divergence: where intent and truth differ. Visually: red glow on edges where ES observed activity the proxy didn’t predict, or vice versa.
Example: Agent says “I’ll read config.yaml” (intent edge to config.yaml). ES observes reads to config.yaml AND ~/.ssh/id_rsa (truth edge to both). The ~/.ssh/id_rsa edge glows red — divergence detected. That is the signal.

Graph Backend

Option Analysis

ApproachLatencyComplexityAlready in StackVerdict
Postgres recursive CTE~50ms for 3-hop on 10K nodesLowYes (RDS)Start here
Postgres + Apache AGE~20ms, native graph queriesMediumYes (extension)Upgrade path
Memgraph<10ms, Cypher nativeMediumYes (already eval’d)If graph queries dominate
Neo4j<10msHigh (new infra)NoOverkill for now
Recommendation: Postgres recursive CTE, migrate to AGE when query complexity demands it. The graph is small per-org. A design partner with 50 machines, 200 agent sessions/day, 20 MCP servers = ~5K nodes, ~15K edges. Postgres handles this trivially. Recursive CTEs with WITH RECURSIVE give us variable-depth traversal. We already have RDS. Zero new infrastructure.

Schema Sketch

CREATE TABLE graph_nodes (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  org_id      UUID NOT NULL REFERENCES organizations(id),
  kind        TEXT NOT NULL,  -- 'agent_session', 'tool', 'cloud_resource', etc.
  external_id TEXT,           -- session UUID, ARN, hostname, etc.
  label       TEXT NOT NULL,  -- display name
  metadata    JSONB,          -- kind-specific properties
  created_at  TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE graph_edges (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  org_id      UUID NOT NULL REFERENCES organizations(id),
  kind        TEXT NOT NULL,  -- 'has_tool', 'connects_to', 'read', etc.
  source_id   UUID NOT NULL REFERENCES graph_nodes(id),
  target_id   UUID NOT NULL REFERENCES graph_nodes(id),
  observed    BOOLEAN DEFAULT false,  -- capability vs actual
  metadata    JSONB,          -- timestamps, event count, args hash
  created_at  TIMESTAMPTZ DEFAULT now()
);

CREATE INDEX idx_edges_source ON graph_edges(org_id, source_id);
CREATE INDEX idx_edges_target ON graph_edges(org_id, target_id);
CREATE INDEX idx_nodes_kind   ON graph_nodes(org_id, kind);

Blast Radius Query (Recursive CTE)

WITH RECURSIVE reachable AS (
  -- Seed: the selected node
  SELECT target_id AS node_id, 1 AS depth, ARRAY[source_id, target_id] AS path
  FROM graph_edges
  WHERE source_id = $1 AND org_id = $2

  UNION ALL

  -- Traverse outward
  SELECT e.target_id, r.depth + 1, r.path || e.target_id
  FROM graph_edges e
  JOIN reachable r ON e.source_id = r.node_id
  WHERE e.org_id = $2
    AND e.target_id != ALL(r.path)  -- prevent cycles
    AND r.depth < 6                  -- max depth
)
SELECT DISTINCT n.*, r.depth, r.path
FROM reachable r
JOIN graph_nodes n ON n.id = r.node_id
ORDER BY r.depth;

Graph Frontend

XYFlow (@xyflow/react v12) — already in quint-cloud-dashboard/package.json. No new dependency. Layout strategy:
  • Dagre (via @dagrejs/dagre) for the default left-to-right hierarchical layout matching Wiz’s visual language
  • D3-Force as an alternative toggle for exploring dense graphs
  • Custom node components per NodeKind with distinct icons and color families
  • Animated edges for “observed” (data flowing) vs static edges for “capability” (could flow)
  • Edge labels showing relationship type on hover

Engineering Effort

PhaseWorkEstimate
1. Schema + APIgraph_nodes/graph_edges tables, migration, CRUD endpoints, recursive CTE blast-radius query3 days
2. Graph PopulationPipeline worker that builds graph from existing session/event data. On session create: add agent_session + tool nodes. On MCP registration: add mcp_server + connects_to edges. On observed event: mark edge as observed.4 days
3. Frontend: Graph TabNew tab in session detail. XYFlow canvas with Dagre layout. Custom node components (8 kinds). Click-to-select, side panel.5 days
4. Blast Radius”Blast Radius” button triggers CTE query, highlights subgraph with animation. Inverse “Can Reach” query.2 days
5. Divergence OverlayCompare intent vs truth edges. Red glow on divergent edges. Toggle switch.3 days
6. PolishExport (SVG/PNG), keyboard nav, minimap, responsive, loading states2 days
Total: ~19 days for one engineer. Call it 4 weeks with testing and iteration. Phase 1-3 are the MVP. Ship that to design partners. Phases 4-6 are the differentiation.

Why This Wins

Wiz showed CISOs that isolated CVEs are noise but connected attack paths are signal. We do the same thing for AI agents: isolated tool calls are noise, but “this Claude session can reach prod Postgres via MCP, and it read your SSH keys, and the intent didn’t match” — that is the signal no one else surfaces. The divergence overlay is the moat. Cloud security vendors compare config-vs-runtime. We compare intent-vs-truth at the agent behavioral layer. Nobody else has both the proxy (intent) and ES (truth) data to build this view.