# quarkslab/qazpt architecture

> Architecture diagram of the GitHub repository quarkslab/qazpt, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-05-13). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/quarkslab/qazpt
- Repository: https://github.com/quarkslab/qazpt

## Overview

1. **What this repository is**
   QAZPT is a Python CLI tool for analyzing Entra ID permission inheritance. Its architecture is centered on building an in-memory identity/permission graph, resolving control relationships, then computing effective permissions and exporting results in several formats.

2. **Primary entrypoints**
   The runtime entrypoints are `qazpt/__main__.py` and `qazpt/main.py`, which expose the `qazpt` command and its `inheritance` subcommand. The CLI is the orchestration layer: it parses flags, loads data, selects analysis mode, and routes output to tree/CSV/JSON/Neo4j exporters.

3. **Boundary between data acquisition and analysis**
   The code is split into a “fetch/auth” side and an “analysis” side. `qazpt/az/auth.py` and the `qazpt/az/ad/*` package model Entra ID entities and permissions, while `qazpt/core/*` contains the permission-inheritance logic that operates on those models. This is an important architectural boundary: Microsoft Graph access and identity modeling are kept separate from graph algorithms.

4. **Azure/Entra ID domain model**
   `qazpt/az/ad/user.py`, `application/*`, `credentials.py`, and `permissions/*` define the domain objects for users, app registrations, service principals, roles, and permission grants. These classes represent the raw security inventory that the analyzer consumes, including app roles, delegated permissions, and Entra ID role membership.

5. **Core graph representation**
   `qazpt/core/graph/` is the central internal data structure. `graph.py`, `node.py`, and `condensation.py` suggest a graph abstraction with explicit support for strongly connected components and condensation into a DAG. This graph is where ownership, service-principal linkage, FIC relationships, and role-based control edges are materialized.

6. **Inheritance resolvers as graph builders**
   The `qazpt/core/graph/resolvers/` package is the main “relationship discovery” layer. Each resolver contributes one inheritance path:
   direct ownership, service principal of an app registration, federated identity credentials, Entra ID roles, and Microsoft Graph application roles. These resolvers are what turn raw directory objects into control edges in the graph.

7. **Permission computation pipeline**
   `qazpt/core/analysis.py` and `qazpt/core/linker.py` likely orchestrate the end-to-end analysis: resolve links, build the graph, then compute transitive permission propagation. The README shows two analysis strategies: a legacy DFS approach and a Tarjan-based condensation approach that collapses cycles before bottom-up propagation.

8. **Important algorithmic boundary: cyclic vs condensed traversal**
   The repository explicitly supports two traversal modes because inheritance graphs can contain cycles. The condensed DAG path is the default and is the more complete approach; the legacy DFS path exists for compatibility or troubleshooting. This affects diagramming because graph construction and graph evaluation are not the same phase.

9. **Permission sources and effective permissions**
   `qazpt/core/permissions/sets.py` and `sources.py` appear to define the vocabulary of permission sets and their origins. The output distinguishes direct permissions from inherited ones, and also tracks “highly privileged” entities that stop transitive expansion because they already control the directory.

10. **Output and visualization layer**
    `qazpt/ui/visualization.py` and `qazpt/ui/colors.py` handle human-readable rendering of the inheritance tree and gained permissions. `qazpt/ui/neo4j_export.py` is a separate export path that writes the analyzed graph into Neo4j, including nodes, labels, and relationships described in the README. This is a distinct sink from the CLI text/CSV/JSON outputs.

11. **Caching and repeated runs**
    `qazpt/utils/cache.py` plus the `--local-cache-dir` CLI option indicate a local persistence layer for fetched directory data. Cache use is an architectural optimization boundary: it reduces repeated Microsoft Graph calls and lets analysis run on previously collected snapshots.

12. **External dependencies and runtime shape**
    The project is a Python package built with `pyproject.toml`, tested with `pytest`, and distributed with container support via `Dockerfile` and `docker-compose.yml`. Neo4j is the only explicitly managed external service in the repo; Microsoft Graph/Entra ID is the data source implied by the auth and permission model.

13. **Test architecture**
    The `tests/data/*` fixtures are structured as captured directory snapshots for different entity types and permissions scenarios. The tests appear to validate inheritance output against those fixtures, which means the test suite is focused on graph correctness and output stability rather than live integration with Microsoft Graph.

14. **Diagramming summary**
    A good architecture diagram should show: CLI entrypoint → auth/data retrieval/cache → domain entity modeling → resolver-driven graph construction → condensation/transitive permission analysis → output/export paths (terminal tree, CSV/JSON, Neo4j). The key conceptual boundaries are source data vs analysis graph, and direct relationships vs computed effective permissions.

Main entry points: [`qazpt/az/auth.py`](https://github.com/quarkslab/qazpt/blob/main/qazpt/az/auth.py) (Auth), [`qazpt/main.py`](https://github.com/quarkslab/qazpt/blob/main/qazpt/main.py) (Main CLI), [`qazpt/core/graph/resolvers/base.py`](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/base.py) (Resolvers), [`qazpt/core/analysis.py`](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/analysis.py) (Analysis)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_entry["CLI"]
  node_qazpt__main(("qazpt<br/>entrypoint<br/>[__main__.py]"))
  node_qazpt_main["Main CLI<br/>cli router<br/>[main.py]"]
end

subgraph group_source["Azure data"]
  node_qazpt_auth["Auth<br/>[auth.py]"]
  node_qazpt_requests["Requests<br/>api client<br/>[requests.py]"]
  node_qazpt_cache[("Cache<br/>[cache.py]")]
  node_qazpt_abstraction["Abstraction<br/>domain base<br/>[abstraction.py]"]
  node_qazpt_user["Users<br/>identity model<br/>[user.py]"]
  node_qazpt_app["Applications<br/>app model<br/>[application.py]"]
  node_qazpt_app_perms["App perms<br/>app grants<br/>[permissions.py]"]
  node_qazpt_creds["Credentials<br/>credential model<br/>[credentials.py]"]
  node_qazpt_entra_perms["Dir perms<br/>permission model<br/>[permissions.py]"]
  node_qazpt_broker["Broker<br/>[broker.py]"]
end

subgraph group_engine["Core engine"]
  node_qazpt_analysis["Analysis<br/>pipeline<br/>[analysis.py]"]
  node_qazpt_linker["Linker<br/>[linker.py]"]
  node_qazpt_graph[("Graph<br/>[graph.py]")]
  node_qazpt_node(("Node<br/>[node.py]"))
  node_qazpt_condense["Condense<br/>tarjan pass<br/>[condensation.py]"]
  node_qazpt_resolvers["Resolvers<br/>[base.py]"]
  node_qazpt_ownership["Ownership<br/>resolver<br/>[ownership.py]"]
  node_qazpt_sp["App SP<br/>resolver<br/>[app_sp.py]"]
  node_qazpt_fic["FIC<br/>resolver<br/>[fic.py]"]
  node_qazpt_role_app["App roles<br/>resolver<br/>[admin_app_roles.py]"]
  node_qazpt_role_entra["Entra roles<br/>resolver"]
end

subgraph group_output["Exports"]
  node_qazpt_visual["Tree view<br/>renderer<br/>[visualization.py]"]
  node_qazpt_neo4j["Neo4j export<br/>exporter<br/>[neo4j_export.py]"]
end

subgraph group_support["Permission vocab"]
  node_qazpt_perm_sets["Perm sets<br/>permission sets<br/>[sets.py]"]
  node_qazpt_perm_sources["Sources<br/>[sources.py]"]
end

node_qazpt__main -->|"starts"| node_qazpt_main
node_qazpt_main -->|"loads"| node_qazpt_auth
node_qazpt_main -->|"orchestrates"| node_qazpt_broker
node_qazpt_main -->|"runs"| node_qazpt_analysis
node_qazpt_main -->|"renders"| node_qazpt_visual
node_qazpt_main -->|"exports"| node_qazpt_neo4j
node_qazpt_auth -->|"calls"| node_qazpt_requests
node_qazpt_auth -->|"reads/writes"| node_qazpt_cache
node_qazpt_auth -->|"models"| node_qazpt_abstraction
node_qazpt_auth -->|"models"| node_qazpt_user
node_qazpt_auth -->|"models"| node_qazpt_app
node_qazpt_auth -->|"models"| node_qazpt_creds
node_qazpt_auth -->|"models"| node_qazpt_entra_perms
node_qazpt_broker -->|"uses"| node_qazpt_auth
node_qazpt_analysis -->|"delegates"| node_qazpt_linker
node_qazpt_linker -->|"invokes"| node_qazpt_resolvers
node_qazpt_resolvers -->|"includes"| node_qazpt_ownership
node_qazpt_resolvers -->|"includes"| node_qazpt_sp
node_qazpt_resolvers -->|"includes"| node_qazpt_fic
node_qazpt_resolvers -->|"includes"| node_qazpt_role_app
node_qazpt_resolvers -->|"includes"| node_qazpt_role_entra
node_qazpt_linker -->|"builds"| node_qazpt_graph
node_qazpt_graph -->|"contains"| node_qazpt_node
node_qazpt_graph -->|"condenses"| node_qazpt_condense
node_qazpt_condense -->|"annotates"| node_qazpt_perm_sets
node_qazpt_analysis -->|"tracks"| node_qazpt_perm_sources
node_qazpt_analysis -->|"produces"| node_qazpt_visual
node_qazpt_analysis -->|"produces"| node_qazpt_neo4j
node_qazpt_perm_sets -->|"classifies"| node_qazpt_perm_sources
node_qazpt_visual -->|"shows"| node_qazpt_perm_sets
node_qazpt_neo4j -->|"writes"| node_qazpt_graph

click node_qazpt__main "https://github.com/quarkslab/qazpt/blob/main/qazpt/__main__.py"
click node_qazpt_main "https://github.com/quarkslab/qazpt/blob/main/qazpt/main.py"
click node_qazpt_auth "https://github.com/quarkslab/qazpt/blob/main/qazpt/az/auth.py"
click node_qazpt_requests "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/requests.py"
click node_qazpt_cache "https://github.com/quarkslab/qazpt/blob/main/qazpt/utils/cache.py"
click node_qazpt_abstraction "https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/base/abstraction.py"
click node_qazpt_user "https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/user.py"
click node_qazpt_app "https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/application/application.py"
click node_qazpt_app_perms "https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/application/permissions.py"
click node_qazpt_creds "https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/credentials.py"
click node_qazpt_entra_perms "https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/permissions/permissions.py"
click node_qazpt_broker "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/broker.py"
click node_qazpt_analysis "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/analysis.py"
click node_qazpt_linker "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/linker.py"
click node_qazpt_graph "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/graph.py"
click node_qazpt_node "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/node.py"
click node_qazpt_condense "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/condensation.py"
click node_qazpt_resolvers "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/base.py"
click node_qazpt_ownership "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/ownership.py"
click node_qazpt_sp "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/app_sp.py"
click node_qazpt_fic "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/fic.py"
click node_qazpt_role_app "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/role_based/admin_app_roles.py"
click node_qazpt_role_entra "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/role_based/admin_entra_id_roles.py"
click node_qazpt_perm_sets "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/permissions/sets.py"
click node_qazpt_perm_sources "https://github.com/quarkslab/qazpt/blob/main/qazpt/core/permissions/sources.py"
click node_qazpt_visual "https://github.com/quarkslab/qazpt/blob/main/qazpt/ui/visualization.py"
click node_qazpt_neo4j "https://github.com/quarkslab/qazpt/blob/main/qazpt/ui/neo4j_export.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_qazpt__main,node_qazpt_main toneBlue
class node_qazpt_auth,node_qazpt_requests,node_qazpt_cache,node_qazpt_abstraction,node_qazpt_user,node_qazpt_app,node_qazpt_app_perms,node_qazpt_creds,node_qazpt_entra_perms,node_qazpt_broker toneAmber
class node_qazpt_analysis,node_qazpt_linker,node_qazpt_graph,node_qazpt_node,node_qazpt_condense,node_qazpt_resolvers,node_qazpt_ownership,node_qazpt_sp,node_qazpt_fic,node_qazpt_role_app,node_qazpt_role_entra toneMint
class node_qazpt_visual,node_qazpt_neo4j toneRose
class node_qazpt_perm_sets,node_qazpt_perm_sources toneIndigo
```

## Components

### CLI

Command entrypoints and orchestration

- [qazpt](https://github.com/quarkslab/qazpt/blob/main/qazpt/__main__.py): entrypoint · Package CLI entrypoint · `qazpt/__main__.py`
- [Main CLI](https://github.com/quarkslab/qazpt/blob/main/qazpt/main.py): cli router · Command parsing and subcommand dispatch · `qazpt/main.py`

### Azure data

Authentication, requests, cache, and Entra ID domain models

- [Auth](https://github.com/quarkslab/qazpt/blob/main/qazpt/az/auth.py): graph auth · Microsoft Graph authentication and data loading boundary · `qazpt/az/auth.py`
- [Requests](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/requests.py): api client · Graph request helpers and transport · `qazpt/core/requests.py`
- [Cache](https://github.com/quarkslab/qazpt/blob/main/qazpt/utils/cache.py): local cache · Snapshot persistence for repeated analysis runs · `qazpt/utils/cache.py`
- [Abstraction](https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/base/abstraction.py): domain base · Common AD entity abstractions · `qazpt/az/ad/base/abstraction.py`
- [Users](https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/user.py): identity model · User entity model · `qazpt/az/ad/user.py`
- [Applications](https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/application/application.py): app model · App registration and service principal model · `qazpt/az/ad/application/application.py`
- [App perms](https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/application/permissions.py): app grants · Application permission and grant model · `qazpt/az/ad/application/permissions.py`
- [Credentials](https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/credentials.py): credential model · Secrets, certificates, and related identity credentials · `qazpt/az/ad/credentials.py`
- [Dir perms](https://github.com/quarkslab/qazpt/blob/main/qazpt/az/ad/permissions/permissions.py): permission model · Entra ID permission and role model · `qazpt/az/ad/permissions/permissions.py`
- [Broker](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/broker.py): data broker · Coordinates collected data before analysis · `qazpt/core/broker.py`

### Core engine

Graph construction and permission analysis

- [Analysis](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/analysis.py): pipeline · End-to-end inheritance computation · `qazpt/core/analysis.py`
- [Linker](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/linker.py): relation linker · Connects modeled entities into control relationships · `qazpt/core/linker.py`
- [Graph](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/graph.py): control graph · In-memory identity and permission graph · `qazpt/core/graph/graph.py`
- [Node](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/node.py): graph node · Vertex representation for identities and permissions · `qazpt/core/graph/node.py`
- [Condense](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/condensation.py): tarjan pass · Strongly connected component condensation into DAG · `qazpt/core/graph/condensation.py`
- [Resolvers](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/base.py): edge resolvers · Relationship discovery and graph building · `qazpt/core/graph/resolvers/base.py`
- [Ownership](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/ownership.py): resolver · Direct ownership inheritance edges · `qazpt/core/graph/resolvers/ownership.py`
- [App SP](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/app_sp.py): resolver · Application to service principal linkage · `qazpt/core/graph/resolvers/app_sp.py`
- [FIC](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/fic.py): resolver · Federated identity credential inheritance · `qazpt/core/graph/resolvers/fic.py`
- [App roles](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/role_based/admin_app_roles.py): resolver · Microsoft Graph application role inheritance · `qazpt/core/graph/resolvers/role_based/admin_app_roles.py`
- [Entra roles](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/graph/resolvers/role_based/admin_entra_id_roles.py): resolver · Directory role inheritance · `qazpt/core/graph/resolvers/role_based/admin_entra_id_roles.py`

### Exports

Human-readable and Neo4j output sinks

- [Tree view](https://github.com/quarkslab/qazpt/blob/main/qazpt/ui/visualization.py): renderer · Terminal-friendly inheritance visualization · `qazpt/ui/visualization.py`
- [Neo4j export](https://github.com/quarkslab/qazpt/blob/main/qazpt/ui/neo4j_export.py): exporter · Writes analyzed graph to Neo4j · `qazpt/ui/neo4j_export.py`

### Permission vocab

Permission set and source definitions

- [Perm sets](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/permissions/sets.py): permission sets · Effective permission set vocabulary · `qazpt/core/permissions/sets.py`
- [Sources](https://github.com/quarkslab/qazpt/blob/main/qazpt/core/permissions/sources.py): permission sources · Origin tracking for permission gains · `qazpt/core/permissions/sources.py`

## Connections

- qazpt → Main CLI: starts · Module entrypoint invokes CLI dispatch
- Main CLI → Auth: loads · CLI requests authenticated directory data
- Main CLI → Broker: orchestrates · CLI hands off collection and analysis coordination
- Main CLI → Analysis: runs · CLI triggers inheritance analysis
- Main CLI → Tree view: renders · CLI can print tree-style results
- Main CLI → Neo4j export: exports · CLI can export the analyzed graph
- Auth → Requests: calls · Auth layer uses Graph request helpers
- Auth → Cache: reads/writes · Fetched snapshots can be cached locally
- Auth → Abstraction: models · Auth layer populates common AD abstractions
- Auth → Users: models · User objects are part of the source inventory
- Auth → Applications: models · Application and service principal objects are collected
- Auth → Credentials: models · Credential artifacts are collected
- Auth → Dir perms: models · Directory roles and grants are collected
- Broker → Auth: uses · Broker coordinates authenticated data access
- Analysis → Linker: delegates · Analysis starts by building relationships
- Linker → Resolvers: invokes · Linker applies resolver framework
- Resolvers → Ownership: includes · Ownership resolver contributes control edges
- Resolvers → App SP: includes · Service-principal resolver contributes control edges
- Resolvers → FIC: includes · Federated identity resolver contributes control edges
- Resolvers → App roles: includes · App role resolver contributes control edges
- Resolvers → Entra roles: includes · Entra role resolver contributes control edges
- Linker → Graph: builds · Linker materializes the internal control graph
- Graph → Node: contains · Graph is composed of typed nodes
- Graph → Condense: condenses · Cycle handling collapses SCCs into a DAG
- Condense → Perm sets: annotates · Condensation feeds effective permission computation
- Analysis → Sources: tracks · Analysis records where permissions came from
- Analysis → Tree view: produces · Analysis results can be rendered as text trees
- Analysis → Neo4j export: produces · Analysis results can be exported to Neo4j
- Perm sets → Sources: classifies · Permission sets are tied to their origins
- Tree view → Perm sets: shows · Rendered output highlights direct and inherited permissions
- Neo4j export → Graph: writes · Neo4j export serializes the analyzed graph
