Source: bridgecrewio/checkov @
25821c4
How It Works
Checkov is a static analysis tool for infrastructure-as-code that parses IaC files (Terraform, CloudFormation, Kubernetes manifests, Dockerfiles, and ~20 other formats), builds an in-memory resource graph, then evaluates that graph against a library of policy checks. The core loop is: parse → build graph → run checks → produce report.
Each supported framework has a dedicated runner (e.g., terraform.runner.Runner, cloudformation.runner.Runner). Runners discover files, parse them into a normalized definition structure, hand those definitions to a GraphManager which constructs a directed graph via rustworkx (a Rust-based Python graph library), and then pass the graph to one or more registries that execute checks against it. Results are collected into Report objects that support CLI, JSON, SARIF, JUnit, CycloneDX, and SPDX output formats.
Checkov ships with over 1,000 built-in policies organized across two check systems: legacy Python-based resource checks (subclassing BaseResourceCheck) and newer declarative graph checks written in YAML/JSON. The declarative checks use a solver engine that evaluates logical conditions (attribute checks, connection checks, filter conditions) against the resource graph, enabling cross-resource policy evaluation—e.g., “ensure every ALB has an associated WAF.”
Architecture
checkov/
├── main.py # CLI entry point, Checkov class
├── runner_filter.py # Filters which checks/frameworks run
├── common/
│ ├── checks_infra/ # Check loading, parsing, registry base classes
│ ├── graph/
│ │ ├── checks_infra/ # Graph check base classes, solver engine
│ │ ├── graph_builder/ # LocalGraph, graph manager, connectors
│ │ └── db_connectors/ # rustworkx (default) and networkx backends
│ ├── bridgecrew/ # Prisma Cloud / Bridgecrew platform integration
│ ├── output/ # Report, Record, SARIF, CycloneDX, SPDX
│ ├── runners/
│ │ ├── base_runner.py # Abstract BaseRunner
│ │ └── runner_registry.py # Orchestrates parallel runner execution
│ └── parallelizer/ # Multi-process parallelization
├── terraform/ # Terraform runner, parser, graph builder, checks
├── cloudformation/ # CloudFormation runner + checks
├── kubernetes/ # K8s runner + checks
├── secrets/ # Secret detection runner
├── sast/ # SAST runner
├── sca_package_2/ # SCA (package) runner
├── sca_image/ # SCA (container image) runner
└── [25+ framework dirs] # ARM, Bicep, Helm, Dockerfile, etc.
Each framework directory follows a consistent pattern: a runner.py, a checks/ directory (with resource/ for Python checks and graph_checks/ for YAML/JSON checks), and optionally a graph_builder/ and graph_manager.py.
The Spine
The request lifecycle flows through these key points:
bin/checkov→ callsCheckov(sys.argv[1:])(inmain.py)Checkov.__init__→ parses CLI args viaconfigargparse, normalizes framework/filter configCheckov.run()→ instantiatesRunnerFilter, constructsRunnerRegistrywith all 27+ runnersRunnerRegistry.run()→ filters valid runners, dispatches them viaparallel_runner.run_function()(multi-process by default)- Each runner’s
run()method:- Discovers files by extension/name
- Parses definitions (framework-specific parser)
- Builds a
LocalGraphvia itsGraphManager - Loads checks from its registry (Python checks auto-register at import; YAML/JSON checks loaded from disk)
BaseRegistry.run_checks()executes checks against the graph usingThreadPoolExecutor- Returns a
Report
RunnerRegistrymerges reports, runs post-scan integrations, returns toCheckov.run()Checkovoutputs reports (CLI, file, upload to Prisma Cloud)
CLI args → Checkov → RunnerRegistry → [Runner.run() × N (parallel)]
↓
Parse → Graph → Checks → Report
↓
Merge reports → Output / Upload
Key Patterns
Check Registration via Import Side-Effects
Python-based checks register themselves at import time. BaseResourceCheck.__init__() calls resource_registry.register(self) — simply importing the module is enough. This is why you’ll see import checkov.terraform.checks.resource chains in runner initialization.
Declarative Graph Checks (YAML/JSON)
The newer check format defines policies declaratively:
metadata:
id: "CKV2_AWS_7"
name: "Ensure EMR clusters' security groups are not open"
category: "NETWORKING"
definition:
and:
- resource_types: [aws_emr_cluster]
connected_resource_types: [aws_security_group]
operator: exists
cond_type: connection
- cond_type: attribute
resource_types: [aws_security_group]
attribute: "ingress.*.cidr_blocks"
operator: not_contains
value: "0.0.0.0/0"These are parsed by GraphCheckParser and evaluated by a solver engine (checkov/common/checks_infra/solvers/) supporting and, or, not combinators plus attribute, connection, and filter conditions.
Runner Per Framework
Every framework follows the same contract: subclass BaseRunner, implement run(), declare check_type. The RunnerRegistry orchestrates them in parallel via Python’s multiprocessing.
Graph Backend: rustworkx
Graphs are stored in rustworkx.PyDiGraph (a Rust-based directed graph library) by default. A NetworkxConnector fallback exists. This is a performance-critical choice—graph traversal is on the hot path for every check.
Bridgecrew/Prisma Cloud Integration
bc_integration (a singleton) handles API key validation, policy metadata download, custom policy loading, suppression rules, enforcement rules, and result upload to S3. The integration feature registry (integration_feature_registry) runs pre-scan and post-scan hooks for licensing, custom policies, suppressions, and policy metadata.
Non-Obvious Details
YAML graph checks are compiled to JSON at build time
setup.pyincludes aPreBuildCommandthat transforms all YAML graph checks to JSON duringpython setup.py build. The JSON versions are what actually ship in the package.
The
commonpackage must not import framework-specific modulesEnforced via
import-linterinpyproject.toml. Thecheckov.commonpackage is forbidden from importing any framework module (terraform, cloudformation, etc.). There are a handful of explicit exceptions, most marked “considering what to do”—technical debt from tight coupling.
Graph check IDs follow a convention
CKV_<provider>_<number>for single-resource checks (legacy Python),CKV2_<provider>_<number>for multi-resource graph checks. The numbering is sequential per provider.
Pyston JIT acceleration
On Linux/macOS x86_64 with CPython < 3.11, Checkov installs
pyston==2.3.5as a dependency for JIT acceleration. This is a non-trivial performance optimization that most users won’t notice.
Two-level parallelism
Runners execute in parallel (multi-process), and within each runner, graph checks execute in parallel (multi-thread via
ThreadPoolExecutor). This two-level parallelism is the main reason Checkov can scan large codebases in reasonable time.
Assessment
Strengths:
- The declarative YAML/JSON check format is a major strength—policies are data, not code, making them auditable and easy to write without Python knowledge.
- The graph-based approach enables cross-resource checks that simpler line-by-line scanners can’t express.
- Framework coverage is exceptional (27+ frameworks) while maintaining a consistent runner pattern.
- Strong import dependency boundaries enforced by linting.
Concerns:
- The codebase has significant coupling to the Prisma Cloud platform. The
bc_integrationsingleton permeates the runner registry and main loop, making offline/pure-CLI usage a secondary path. - At ~9,800 files, the monorepo structure creates long import chains and high test overhead.
- The legacy Python check system and the newer graph check system coexist, creating two mental models for contributors. New checks should almost always use the YAML format, but the old system isn’t deprecated.
- The
pyproject.tomlshows several “considering what to do” exceptions to the import rules, indicating architectural debt.
Recommendations:
- For users adopting Checkov: invest in custom YAML graph checks via
--external-checks-dirrather than writing Python checks. - For contributors: understand the graph check solver engine before attempting complex multi-resource policies.
- For the project: consider extracting the
bc_integrationlayer into a pluggable backend to clean up the separation between core scanning and platform upload.