Every CVE Search/ Get API key

CVE-2026-60090

PraisonAI before 4.6.78 SQL/CQL Injection via vector dimension

9.3 CriticalFix available

PraisonAI before 4.6.78 fails to validate the caller-controlled dimension argument in the PGVector and Cassandra knowledge-store create_collection() backends. Although schema, keyspace, and collection-name identifiers are validated, the dimension value (declared as int but not enforced at runtime) is interpolated directly into the vector column of the generated CREATE TABLE DDL. A caller able to influence collection-creation dimensions can pass a string such as '3); DROP TABLE tenant_secrets; --' to inject SQL/CQL tokens into the statement executed by the database driver.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

0.70%Up 0.3 points since Jul 12, 2026
90%50%10%1%

What each source reports

CVE List Score used 9.3

PraisonAI before 4.6.78 SQL/CQL Injection via vector dimension

PraisonAI before 4.6.78 fails to validate the caller-controlled dimension argument in the PGVector and Cassandra knowledge-store create_collection() backends. Although schema, keyspace, and collection-name identifiers are validated, the dimension value (declared as int but not enforced at runtime) is interpolated directly into the vector column of the generated CREATE TABLE DDL. A caller able to influence collection-creation dimensions can pass a string such as '3); DROP TABLE tenant_secrets; --' to inject SQL/CQL tokens into the statement executed by the database driver.

Record
CVE-2026-60090
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-89
Updated
2026-11-02
EUVDEUVD-2026-43175 9.3

PraisonAI before 4.6.78 fails to validate the caller-controlled dimension argument in the PGVector and Cassandra knowledge-store create_collection() backends. Although schema, keyspace, and collection-name identifiers are validated, the dimension value (declared as int but not enforced at runtime) is interpolated directly into the vector column of the generated CREATE TABLE DDL. A caller able to influence collection-creation dimensions can pass a string such as '3); DROP TABLE tenant_secrets; --' to inject SQL/CQL tokens into the statement executed by the database driver.

Record
EUVD-2026-43175
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-q65p-7p84-495c 9.3

Duplicate Advisory: PraisonAI: PGVector and Cassandra knowledge stores interpolate vector dimensions into DDL

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-wf65-4jjx-q444. This link is maintained to preserve external references.

Original Description

PraisonAI before 4.6.78 fails to validate the caller-controlled dimension argument in the PGVector and Cassandra knowledge-store create_collection() backends. Although schema, keyspace, and collection-name identifiers are validated, the dimension value (declared as int but not enforced at runtime) is interpolated directly into the vector column of the generated CREATE TABLE DDL. A caller able to influence collection-creation dimensions can pass a string such as '3); DROP TABLE tenant_secrets; --' to inject SQL/CQL tokens into the statement executed by the database driver.

Record
GHSA-q65p-7p84-495c
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-89
Updated
2026-11-02
GitHub AdvisoriesGHSA-wf65-4jjx-q444 No score

PraisonAI: PGVector and Cassandra knowledge stores interpolate vector dimensions into DDL

PGVector and Cassandra knowledge stores interpolate vector dimensions into DDL

Summary

The PGVector and Cassandra knowledge-store backends validate SQL/CQL identifiers such as schema, keyspace, and collection names, but still insert the caller-controlled dimension argument directly into CREATE TABLE vector column declarations. A caller that can influence collection creation dimensions can append SQL/CQL tokens to the generated DDL executed by the database driver.

Technical Details

The affected boundary is the vector-store collection creation API. The shared KnowledgeStore.create_collection() contract declares dimension: int, but Python type hints are not enforced at runtime. Backends that interpolate that value into DDL must validate the runtime value before constructing SQL/CQL.

src/praisonai/praisonai/persistence/knowledge/pgvector.py already treats DDL identifier interpolation as security-sensitive: __init__() calls validate_identifier(schema, name="schema"), and _table_name() calls validate_identifier(collection, name="collection name") before returning f"{self.schema}.praison_vec_{collection}". However, PGVectorKnowledgeStore.create_collection() then executes:

cur.execute(f"""
    CREATE TABLE IF NOT EXISTS {table} (
        id VARCHAR(255) PRIMARY KEY,
        content TEXT,
        content_hash VARCHAR(64),
        created_at DOUBLE PRECISION,
        metadata JSONB,
        embedding vector({dimension})
    )
""")

No equivalent type or range check runs on dimension. Passing a string such as 3); DROP TABLE tenant_secrets; -- reaches the SQL sent to cur.execute().

src/praisonai/praisonai/persistence/knowledge/cassandra.py has the same pattern. The constructor validates keyspace, and create_collection() validates the collection name, but the vector column DDL uses:

self._session.execute(f"""
    CREATE TABLE IF NOT EXISTS {name} (
        id text PRIMARY KEY,
        content text,
        content_hash text,
        created_at double,
        embedding vector<float, {dimension}>
    )
""")

Passing a string such as 3>; DROP TABLE tenant_secrets; -- reaches the CQL sent to session.execute().

PoV

This minimal PoV imports the real backend classes with fake database drivers, records the statements sent to the drivers, and compares a safe integer dimension with a malicious string dimension. It also attempts a malicious collection name as a negative control; current code rejects that name, proving the identifier hardening is active while the vector dimension remains unguarded.

#!/usr/bin/env python3
"""Local PoV for vector-store dimension DDL interpolation.

The script imports PraisonAI's current source with fake PostgreSQL/Cassandra
drivers, then records the SQL/CQL sent to the driver cursors. No database server
is required; the assertion is that the real classes build executable DDL with an
attacker-controlled dimension string.
"""

from __future__ import annotations

import argparse
import importlib
import json
import subprocess
import sys
import types
from pathlib import Path
from typing import Any


class SqlRecorder:
    def __init__(self) -> None:
        self.statements: list[dict[str, Any]] = []

    def execute(self, statement: str, params: Any = None) -> None:
        normalized = "\n".join(line.rstrip() for line in statement.strip().splitlines())
        self.statements.append({"statement": normalized, "params": params})

    def __enter__(self) -> "SqlRecorder":
        return self

    def __exit__(self, *_exc: object) -> None:
        return None


class FakeConnection:
    def __init__(self, recorder: SqlRecorder) -> None:
        self.recorder = recorder

    def cursor(self, *args: Any, **kwargs: Any) -> SqlRecorder:
        return self.recorder

    def commit(self) -> None:
        return None


class FakePool:
    def __init__(self, recorder: SqlRecorder) -> None:
        self.conn = FakeConnection(recorder)

 …
Record
GHSA-wf65-4jjx-q444
Weakness
CWE-89, CWE-943
Updated
2026-11-02
NVD 9.3

PraisonAI before 4.6.78 fails to validate the caller-controlled dimension argument in the PGVector and Cassandra knowledge-store create_collection() backends. Although schema, keyspace, and collection-name identifiers are validated, the dimension value (declared as int but not enforced at runtime) is interpolated directly into the vector column of the generated CREATE TABLE DDL. A caller able to influence collection-creation dimensions can pass a string such as '3); DROP TABLE tenant_secrets; --' to inject SQL/CQL tokens into the statement executed by the database driver.

Record
CVE-2026-60090
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-89
Updated
2026-11-02
OSV 9.3

PraisonAI before 4.6.78 SQL/CQL Injection via vector dimension

PraisonAI before 4.6.78 fails to validate the caller-controlled dimension argument in the PGVector and Cassandra knowledge-store create_collection() backends. Although schema, keyspace, and collection-name identifiers are validated, the dimension value (declared as int but not enforced at runtime) is interpolated directly into the vector column of the generated CREATE TABLE DDL. A caller able to influence collection-creation dimensions can pass a string such as '3); DROP TABLE tenant_secrets; --' to inject SQL/CQL tokens into the statement executed by the database driver.

Record
CVE-2026-60090
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-89
Updated
2026-11-02
OSVGHSA-q65p-7p84-495c 9.3

Duplicate Advisory: PraisonAI: PGVector and Cassandra knowledge stores interpolate vector dimensions into DDL

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-wf65-4jjx-q444. This link is maintained to preserve external references.

Original Description

PraisonAI before 4.6.78 fails to validate the caller-controlled dimension argument in the PGVector and Cassandra knowledge-store create_collection() backends. Although schema, keyspace, and collection-name identifiers are validated, the dimension value (declared as int but not enforced at runtime) is interpolated directly into the vector column of the generated CREATE TABLE DDL. A caller able to influence collection-creation dimensions can pass a string such as '3); DROP TABLE tenant_secrets; --' to inject SQL/CQL tokens into the statement executed by the database driver.

Record
GHSA-q65p-7p84-495c
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-89
Updated
2026-11-02
OSVGHSA-wf65-4jjx-q444 No score

PraisonAI: PGVector and Cassandra knowledge stores interpolate vector dimensions into DDL

PGVector and Cassandra knowledge stores interpolate vector dimensions into DDL

Summary

The PGVector and Cassandra knowledge-store backends validate SQL/CQL identifiers such as schema, keyspace, and collection names, but still insert the caller-controlled dimension argument directly into CREATE TABLE vector column declarations. A caller that can influence collection creation dimensions can append SQL/CQL tokens to the generated DDL executed by the database driver.

Technical Details

The affected boundary is the vector-store collection creation API. The shared KnowledgeStore.create_collection() contract declares dimension: int, but Python type hints are not enforced at runtime. Backends that interpolate that value into DDL must validate the runtime value before constructing SQL/CQL.

src/praisonai/praisonai/persistence/knowledge/pgvector.py already treats DDL identifier interpolation as security-sensitive: __init__() calls validate_identifier(schema, name="schema"), and _table_name() calls validate_identifier(collection, name="collection name") before returning f"{self.schema}.praison_vec_{collection}". However, PGVectorKnowledgeStore.create_collection() then executes:

cur.execute(f"""
    CREATE TABLE IF NOT EXISTS {table} (
        id VARCHAR(255) PRIMARY KEY,
        content TEXT,
        content_hash VARCHAR(64),
        created_at DOUBLE PRECISION,
        metadata JSONB,
        embedding vector({dimension})
    )
""")

No equivalent type or range check runs on dimension. Passing a string such as 3); DROP TABLE tenant_secrets; -- reaches the SQL sent to cur.execute().

src/praisonai/praisonai/persistence/knowledge/cassandra.py has the same pattern. The constructor validates keyspace, and create_collection() validates the collection name, but the vector column DDL uses:

self._session.execute(f"""
    CREATE TABLE IF NOT EXISTS {name} (
        id text PRIMARY KEY,
        content text,
        content_hash text,
        created_at double,
        embedding vector<float, {dimension}>
    )
""")

Passing a string such as 3>; DROP TABLE tenant_secrets; -- reaches the CQL sent to session.execute().

PoV

This minimal PoV imports the real backend classes with fake database drivers, records the statements sent to the drivers, and compares a safe integer dimension with a malicious string dimension. It also attempts a malicious collection name as a negative control; current code rejects that name, proving the identifier hardening is active while the vector dimension remains unguarded.

#!/usr/bin/env python3
"""Local PoV for vector-store dimension DDL interpolation.

The script imports PraisonAI's current source with fake PostgreSQL/Cassandra
drivers, then records the SQL/CQL sent to the driver cursors. No database server
is required; the assertion is that the real classes build executable DDL with an
attacker-controlled dimension string.
"""

from __future__ import annotations

import argparse
import importlib
import json
import subprocess
import sys
import types
from pathlib import Path
from typing import Any


class SqlRecorder:
    def __init__(self) -> None:
        self.statements: list[dict[str, Any]] = []

    def execute(self, statement: str, params: Any = None) -> None:
        normalized = "\n".join(line.rstrip() for line in statement.strip().splitlines())
        self.statements.append({"statement": normalized, "params": params})

    def __enter__(self) -> "SqlRecorder":
        return self

    def __exit__(self, *_exc: object) -> None:
        return None


class FakeConnection:
    def __init__(self, recorder: SqlRecorder) -> None:
        self.recorder = recorder

    def cursor(self, *args: Any, **kwargs: Any) -> SqlRecorder:
        return self.recorder

    def commit(self) -> None:
        return None


class FakePool:
    def __init__(self, recorder: SqlRecorder) -> None:
        self.conn = FakeConnection(recorder)

 …
Record
GHSA-wf65-4jjx-q444
Weakness
CWE-89, CWE-943
Updated
2026-11-02
OSVPYSEC-2026-4260 No score

PraisonAI: PGVector and Cassandra knowledge stores interpolate vector dimensions into DDL

PGVector and Cassandra knowledge stores interpolate vector dimensions into DDL

Summary

The PGVector and Cassandra knowledge-store backends validate SQL/CQL identifiers such as schema, keyspace, and collection names, but still insert the caller-controlled dimension argument directly into CREATE TABLE vector column declarations. A caller that can influence collection creation dimensions can append SQL/CQL tokens to the generated DDL executed by the database driver.

Technical Details

The affected boundary is the vector-store collection creation API. The shared KnowledgeStore.create_collection() contract declares dimension: int, but Python type hints are not enforced at runtime. Backends that interpolate that value into DDL must validate the runtime value before constructing SQL/CQL.

src/praisonai/praisonai/persistence/knowledge/pgvector.py already treats DDL identifier interpolation as security-sensitive: __init__() calls validate_identifier(schema, name="schema"), and _table_name() calls validate_identifier(collection, name="collection name") before returning f"{self.schema}.praison_vec_{collection}". However, PGVectorKnowledgeStore.create_collection() then executes:

cur.execute(f"""
    CREATE TABLE IF NOT EXISTS {table} (
        id VARCHAR(255) PRIMARY KEY,
        content TEXT,
        content_hash VARCHAR(64),
        created_at DOUBLE PRECISION,
        metadata JSONB,
        embedding vector({dimension})
    )
""")

No equivalent type or range check runs on dimension. Passing a string such as 3); DROP TABLE tenant_secrets; -- reaches the SQL sent to cur.execute().

src/praisonai/praisonai/persistence/knowledge/cassandra.py has the same pattern. The constructor validates keyspace, and create_collection() validates the collection name, but the vector column DDL uses:

self._session.execute(f"""
    CREATE TABLE IF NOT EXISTS {name} (
        id text PRIMARY KEY,
        content text,
        content_hash text,
        created_at double,
        embedding vector<float, {dimension}>
    )
""")

Passing a string such as 3>; DROP TABLE tenant_secrets; -- reaches the CQL sent to session.execute().

PoV

This minimal PoV imports the real backend classes with fake database drivers, records the statements sent to the drivers, and compares a safe integer dimension with a malicious string dimension. It also attempts a malicious collection name as a negative control; current code rejects that name, proving the identifier hardening is active while the vector dimension remains unguarded.

#!/usr/bin/env python3
"""Local PoV for vector-store dimension DDL interpolation.

The script imports PraisonAI's current source with fake PostgreSQL/Cassandra
drivers, then records the SQL/CQL sent to the driver cursors. No database server
is required; the assertion is that the real classes build executable DDL with an
attacker-controlled dimension string.
"""

from __future__ import annotations

import argparse
import importlib
import json
import subprocess
import sys
import types
from pathlib import Path
from typing import Any


class SqlRecorder:
    def __init__(self) -> None:
        self.statements: list[dict[str, Any]] = []

    def execute(self, statement: str, params: Any = None) -> None:
        normalized = "\n".join(line.rstrip() for line in statement.strip().splitlines())
        self.statements.append({"statement": normalized, "params": params})

    def __enter__(self) -> "SqlRecorder":
        return self

    def __exit__(self, *_exc: object) -> None:
        return None


class FakeConnection:
    def __init__(self, recorder: SqlRecorder) -> None:
        self.recorder = recorder

    def cursor(self, *args: Any, **kwargs: Any) -> SqlRecorder:
        return self.recorder

    def commit(self) -> None:
        return None


class FakePool:
    def __init__(self, recorder: SqlRecorder) -> None:
        self.conn = FakeConnection(recorder)

 …
Record
PYSEC-2026-4260
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
praisonai
PyPI
through 4.6.77
4.6.78
praisonai
PyPI
before 4.6.78, plus listed versions
0.0.10.0.100.0.110.0.120.0.13
Show 45 moreShow less
0.0.140.0.150.0.160.0.170.0.180.0.190.0.20.0.200.0.210.0.220.0.230.0.240.0.250.0.260.0.270.0.280.0.290.0.30.0.300.0.310.0.320.0.330.0.340.0.350.0.360.0.370.0.380.0.390.0.40.0.400.0.410.0.420.0.430.0.440.0.450.0.460.0.470.0.480.0.490.0.50.0.500.0.520.0.530.0.540.0.55
4.6.78

History

History
RecordedChangedSources
affected
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-60090 is a critical vulnerability (CVSS 9.3) in Mervinpraison Praisonai, published July 11, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.70%, the 53rd percentile. Fixed in praisonai 4.6.78.

References

This page doesn't list everything

Left out here: 713 versions and the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open