Skip to main content

ConcurrencyGuard (cguard)

PyPI version Python Version License: MIT GitHub

v2.0 — universal static concurrency analysis, semantic taint tracking, runtime race verification, and auto-patching for Python codebases.

ConcurrencyGuard Demo


The Problem vs. The cguard Solution

Vulnerability Vector Linters (Ruff, Flake8) AI / LLM Code Reviewers cguard
Unlocked Read-Modify-Write (CG-001) ❌ Blind to DB semantics ⚠️ Hallucinates, misses context ✅ Deterministic AST detection
Missing Idempotency Headers (CG-002) ❌ Ignored ⚠️ High latency, non-deterministic ✅ Sub-millisecond AST verification
Async Loop N+1 Explode (CG-003) ❌ Unchecked ⚠️ Inconsistent pattern matching ✅ Local control-flow AST traversal
Check-Then-Act Uniqueness Races (CG-004) ❌ Ignored ⚠️ Frequently missed ✅ Catch-integrity validation
Celery Retry Duplication (CG-005) ❌ Ignored ⚠️ Misses task retry mechanics ✅ Decorator & kwarg audit
Empirical Exploit Verification ❌ None ❌ Cannot execute code ✅ asyncio.Barrier stress engine
Deterministic Code Remediation ⚠️ Style fixes only ⚠️ Introduces syntax regressions ✅ Zero-regression LibCST patcher

Modern AI agents and developers write fast asynchronous endpoints, but routinely introduce critical race conditions: forgetting .with_for_update() row-level locks on balance deductions, omitting Idempotency-Key headers on mutating POST routes, or executing queries inside loops.

Standard linters only validate syntax, while LLM-based reviewers are slow, non-deterministic, cost money per token, and miss non-local control flow races. cguard runs 100% locally with zero external API calls, using pure Python AST analysis to catch race conditions in milliseconds and providing runtime proof via synchronized barrier stress testing.


Installation

pip install concurrency-guard

Quick Start

1. Scan your codebase for concurrency vulnerabilities

Analyze files or entire directories in milliseconds with deterministic, source-only checks:

# Scan a single route or module
cguard scan app/routers/wallet.py

# Scan an entire repository (auto-excludes virtualenvs, migrations, and build caches)
cguard scan src/

2. Verify race conditions empirically at runtime

Launch synchronized concurrent workers via asyncio.Barrier to mathematically prove whether an endpoint preserves or violates state invariants under high concurrency:

cguard verify examples/vulnerable_wallet.py --workers 20
❌ CONCURRENCY INVARIANT VIOLATED (RACE PROVEN)
Target Endpoint: POST /wallet/withdraw
Concurrent Barrier Workers: 20
Initial Balance: $50
Successful Withdrawals ($50 each): 20
Final Resulting Balance: $-950 (Double-spend exploit confirmed!)

Headless function verification

Stress a plain Python callable without FastAPI, an ORM, or HTTP infrastructure. The verifier imports the selected source file, synchronizes workers with an asyncio.Barrier or threading.Barrier, and compares the observed numeric shared-dictionary value with the value implied by completed workers.

cguard verify worker.py --headless --function update_shared_state --workers 20

3. Automatically patch vulnerabilities in-place

Safely rewrite source code using LibCST syntax-tree transformations:

# Preview patch diff (dry-run)
cguard fix app/routers/wallet.py

# Write deterministic fixes directly to disk
cguard fix app/routers/wallet.py --write

Supported Vulnerability Rules

Rule ID Rule Name Trigger Condition Automated Remediation (LibCST)
CG-001 Unlocked Read-Modify-Write Race Route queries state (select, db.scalar, session.get), mutates an attribute, and commits without .with_for_update() locking. Appends .with_for_update() row lock to the query chain.
CG-002 Missing Idempotency Guard on POST Mutating @app.post or @router.post endpoint executes database writes without an Idempotency-Key parameter. Injects idempotency_key: str = Header(..., alias="Idempotency-Key") into endpoint parameters.
CG-003 Async Loop N+1 Query Explode Query operations (.execute(), .scalar(), .scalars(), .get()) called directly inside a for or async for loop body. Flags query explosion; suggests eager loading via joinedload / selectinload.
CG-004 Check-Then-Act Uniqueness Race Route checks existence via select(), executes db.add(), and commits without wrapping in a try...except IntegrityError handler. Wraps commit in try...except IntegrityError with rollback.
CG-005 Non-Idempotent Celery Retry Task Celery task configured with retries (autoretry_for, max_retries) executes mutations without acks_late=True or an idempotency lock. Recommends acks_late=True and distributed deduplication lock before side-effects.
CG-006 Async Shared-State Mutation An async function modifies a module-level list/dict/set without an async lock context. Use asyncio.Lock around the critical section.
CG-007 Threaded Built-in Mutation A thread target mutates module-level built-in state without a threading.Lock/RLock. Protect the collection or use a queue.
CG-008 Generic Read-Modify-Write Race An attribute/subscript is read into a local, transformed, then assigned back without synchronization. Lock the entire RMW operation or use an atomic primitive.
CG-009–CG-010 File-system Races Concurrent write-mode open/aiofiles.open, or a hard-coded temporary filename. Serialize writes and use tempfile for task-local paths.
CG-011–CG-012 Cache Check-Then-Act Redis or Memcached get/set state transitions lack an atomic cache operation. Use Redis SET NX, Lua, or Memcached atomic increment/CAS.
CG-013 Lock-order Deadlock Two locks are acquired in opposite nesting orders in one module. Establish and enforce one global lock order.
CG-014 Inter-procedural Shared-state Race A local or imported function call chain reaches a cross-file function that mutates module shared state. Propagate a synchronization boundary through the chain or make the leaf operation atomic.
CG-015 Tainted Shared-state Mutation A reference to global, class-level, singleton, or Redis shared state flows through locals/callees and is mutated without a lock. Lock the complete operation or use an atomic primitive.
CG-016 Django Unlocked RMW A Django objects.get() or objects.filter().first() result is mutated and saved without select_for_update(). Insert select_for_update() within transaction.atomic().

cguard fix --write can remediate CG-006, CG-007, CG-009, and CG-016 in addition to the database rules: it injects the required standard-library lock import and wraps the flagged mutation, or wraps a write-mode file context in filelock.FileLock.


Architecture

concurrency-guard operates entirely offline and locally with zero external network or LLM dependencies:

  • Static Scanner (ast): Recursively walks Python Abstract Syntax Trees to identify dangerous state-mutation patterns across control flow, function parameters, loop constructs, and decorator metadata.
  • Project Call Graph (ast): Resolves local functions and import aliases across the scanned source set, then propagates shared-state hazards to every transitive caller without importing or executing application code.
  • Semantic Taint Tracking (ast): Propagates shared-memory references through local aliases and resolved call arguments, identifying unprotected mutation sinks in asynchronous or threaded execution contexts.
  • Runtime Verification Engine: Exercises FastAPI endpoints or raw Python functions using synchronized asyncio.Barrier and threading.Barrier workers to empirically demonstrate state corruption.
  • Auto-Patcher (libcst): Performs lossless, Concrete Syntax Tree transformations that preserve comments, formatting, and indentation while injecting locks and error guards.

Custom rules

Teams can add checks for proprietary clients and frameworks without changing cguard source. Pass a YAML file with --rules:

rules:
  - id: ACME-001
    function: acme_cache.get
    modifies: account.balance
    without_context: account_lock
    message: Acme cache balance updates must be guarded.
    fix: Use `async with account_lock:` around the update.
cguard scan src/ --rules concurrency-rules.yml

The DSL is intentionally declarative: function/call matches a call name, modifies/variable matches an assignment target, and without_context/ without_lock suppresses the result when a matching with or async with context exists.

Source and releases

Source, issues, and release history are maintained at AyushkhatiDev/concurrency-guard.


License

Distributed under the MIT License. See LICENSE for details.

Metadata

Release files for concurrency-guard 2.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for concurrency-guard 2.1.0
File Size Uploaded Provenance Actions
concurrency_guard-2.1.0.tar.gz 17.4 MB Without provenance Details for concurrency_guard-2.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for concurrency-guard 2.1.0
File Interpreter ABI Platform Actions
concurrency_guard-2.1.0-py3-none-any.whl Python 3 none any Details for concurrency_guard-2.1.0-py3-none-any.whl

Total release size: 17.4 MB

Release files / concurrency_guard-2.1.0.tar.gz

Download URL concurrency_guard-2.1.0.tar.gz
Size 17.4 MB
Tags Source
SHA-256 checksum
How to use checksums
f3a7f677676e7fddc1a0c900fc9b83085487976955aca7a2f1cde20a42f2c79a
BLAKE2b-256 checksum
How to use checksums
a7796830fbeb8e4602ce58f9b1eb07b0471be795813e19d0ae00e98d5c0830ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.8

Release files / concurrency_guard-2.1.0-py3-none-any.whl

Download URL concurrency_guard-2.1.0-py3-none-any.whl
Size 42.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fb08fb343049faabdb4a7444b31e545e504f273df882c2a7cd2444c9f68bb35e
BLAKE2b-256 checksum
How to use checksums
4a16ab5ef3248ac52c2359df1cafd43e8815127dcc85157e0860aab5e7c536b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.8

Security for concurrency-guard 2.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page