VYPR
High severity8.1GHSA Advisory· Published Jun 1, 2026· Updated Jun 1, 2026

praisonai-platform: Project endpoints accept any project_id without workspace ownership check, cross-workspace read/update/delete IDOR

CVE-2026-47418

Description

Insecure Direct Object Reference in PraisonAI platform project endpoints allows cross-workspace read/update/delete of projects.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Insecure Direct Object Reference in PraisonAI platform project endpoints allows cross-workspace read/update/delete of projects.

Vulnerability

The PraisonAI platform project CRUD endpoints (GET / PATCH / DELETE /workspaces/{workspace_id}/projects/{project_id} and GET .../{project_id}/stats) enforce workspace membership only via require_workspace_member(workspace_id), but then resolve project_id using ProjectService.get(project_id) which performs a primary-key lookup without a workspace_id predicate. The update, delete, and get_stats methods inherit this gap. This allows a user who is a member of any workspace to access projects belonging to any other workspace. The vulnerable code is in src/praisonai-platform/praisonai_platform/services/project_service.py (lines 47-108) and route handlers in src/praisonai-platform/praisonai_platform/api/routes/projects.py (lines 51-108) [1][2].

Exploitation

An attacker must be an authenticated user who is a member of at least one workspace. No special privileges are required beyond that. The attacker can craft requests to the project endpoints using a valid workspace_id (from their own workspace) and an arbitrary project_id that belongs to a different workspace. Because the backend does not verify that the project belongs to the specified workspace, the attacker can read, modify, delete, or retrieve statistics for any project in the system [1][2].

Impact

Successful exploitation results in unauthorized access to project data across workspaces. An attacker can read sensitive project information, modify project details, delete projects, or view project statistics. This constitutes a breach of confidentiality, integrity, and availability for projects in all workspaces [1][2].

Mitigation

As of the advisory publication date (2026-06-01), no official patch has been released. The vendor has been notified and a fix is expected. In the interim, users should restrict workspace membership to trusted users and consider implementing additional authorization checks in the project service methods to ensure that project_id is scoped to the provided workspace_id [1][2].

AI Insight generated on Jun 1, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

1
b707fd0b27fb

Merge pull request #171 from MervinPraison/develop

https://github.com/MervinPraison/PraisonAIMervin PraisonOct 17, 2024Fixed in 0.1.4via release-tag
20 files changed · +8540 1214
  • Dockerfile+1 1 modified
    @@ -1,6 +1,6 @@
     FROM python:3.11-slim
     WORKDIR /app
     COPY . .
    -RUN pip install flask praisonai==0.1.3 gunicorn markdown
    +RUN pip install flask praisonai==0.1.4 gunicorn markdown
     EXPOSE 8080
     CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
    
  • docs/api/praisonai/deploy.html+1 1 modified
    @@ -110,7 +110,7 @@ <h2 id="raises">Raises</h2>
                 file.write(&#34;FROM python:3.11-slim\n&#34;)
                 file.write(&#34;WORKDIR /app\n&#34;)
                 file.write(&#34;COPY . .\n&#34;)
    -            file.write(&#34;RUN pip install flask praisonai==0.1.3 gunicorn markdown\n&#34;)
    +            file.write(&#34;RUN pip install flask praisonai==0.1.4 gunicorn markdown\n&#34;)
                 file.write(&#34;EXPOSE 8080\n&#34;)
                 file.write(&#39;CMD [&#34;gunicorn&#34;, &#34;-b&#34;, &#34;0.0.0.0:8080&#34;, &#34;api:app&#34;]\n&#39;)
                 
    
  • docs/api-reference/index.mdx+22 0 added
    @@ -0,0 +1,22 @@
    +---
    +title: 'API Reference'
    +description: 'PraisonAI API documentation'
    +---
    +
    +{/* Replace any comments with this style */}
    +
    +# API Reference
    +
    +{/* Rest of the content remains the same */}
    +
    +This section provides detailed information about the PraisonAI API.
    +
    +## Modules
    +
    +- [praisonai](../api/praisonai/index)
    +- [praisonai.auto](../api/praisonai/auto)
    +- [praisonai.agents_generator](../api/praisonai/agents_generator)
    +- [praisonai.cli](../api/praisonai/cli)
    +- [praisonai.deploy](../api/praisonai/deploy)
    +
    +For more detailed API documentation, please refer to the generated files in the `api` folder.
    
  • docs/home.md+12 16 modified
    @@ -2,31 +2,27 @@
     
     <p align="center">
       <picture>
    -    <source media="(prefers-color-scheme: dark)" srcset="images/praisonai-logo-large.png">
    -    <source media="(prefers-color-scheme: light)" srcset="images/praisonai-logo-large.png">
    -    <img alt="PraisonAI Logo" src="images/praisonai-logo-large.png">
    +    <source media="(prefers-color-scheme: dark)" srcset="/logo/dark.png" />
    +    <source media="(prefers-color-scheme: light)" srcset="/logo/light.png" />
    +    <img alt="PraisonAI Logo" src="/logo/light.png" style={{display: "block", margin: "auto"}} />
       </picture>
     </p>
     
     <p align="center">
    -<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads"></a>
    -<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version"></a>
    -<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License"></a>
    +<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads" /></a>
    +<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version" /></a>
    +<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" /></a>
     </p>
     
    -<div align="center">
    -
     # Praison AI
     
    -</div>
    -
     Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, represents a low-code, centralised framework designed to simplify the creation and orchestration of multi-agent systems for various LLM applications, emphasizing ease of use, customization, and human-agent interaction.
     
     <div align="center">
       <picture>
    -    <source media="(prefers-color-scheme: dark)" srcset="images/architecture-dark.png">
    -    <source media="(prefers-color-scheme: light)" srcset="images/architecture-light.png">
    -    <img alt="PraisonAI Architecture" src="images/architecture-light.png">
    +    <source media="(prefers-color-scheme: dark)" srcset="images/architecture-dark.png" />
    +    <source media="(prefers-color-scheme: light)" srcset="images/architecture-light.png" />
    +    <img alt="PraisonAI Architecture" src="images/architecture-light.png" />
       </picture>
     </div>
     
    @@ -42,8 +38,8 @@ Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, rep
     
     |               | Cookbook        | Open in Colab                                                                                                                                                                                                                                  |
     | ------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    -| Basic         | PraisonAI       | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>       |
    -| Include Tools | PraisonAI Tools | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-tools-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a> |
    +| Basic         | PraisonAI       | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" /></a>       |
    +| Include Tools | PraisonAI Tools | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-tools-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" /></a> |
     
     ## Install
     
    @@ -109,4 +105,4 @@ praisonai --framework autogen
     
     ```bash
     praisonai --auto create a movie script about Dog in Moon
    -```
    \ No newline at end of file
    +```
    
  • docs/index.md+10 20 modified
    @@ -1,30 +1,20 @@
     # Praison AI
     
    -<p align="center">
    -  <picture>
    -    <source media="(prefers-color-scheme: dark)" srcset="overrides/images/praisonai-logo-dark.png">
    -    <source media="(prefers-color-scheme: light)" srcset="overrides/images/praisonai-logo-light.png">
    -    <img alt="PraisonAI Logo" src="overrides/images/praisonai-logo-light.png">
    -  </picture>
    -</p>
    +<img alt="PraisonAI Logo" src="overrides/images/praisonai-logo-light.png" style="display: block; margin: auto;" />
     
     <p align="center">
    -<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads"></a>
    -<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version"></a>
    -<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License"></a>
    +<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads" /></a>
    +<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version" /></a>
    +<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" /></a>
     </p>
     
    -<div align="center">
    -
    -</div>
    -
     Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, represents a low-code, centralised framework designed to simplify the creation and orchestration of multi-agent systems for various LLM applications, emphasizing ease of use, customization, and human-agent interaction.
     
     <div align="center">
       <picture>
    -    <source media="(prefers-color-scheme: dark)" srcset="images/architecture-dark.png">
    -    <source media="(prefers-color-scheme: light)" srcset="images/architecture-light.png">
    -    <img alt="PraisonAI Architecture" src="images/architecture-dark.png">
    +    <source media="(prefers-color-scheme: dark)" srcset="images/architecture-dark.png" />
    +    <source media="(prefers-color-scheme: light)" srcset="images/architecture-light.png" />
    +    <img alt="PraisonAI Architecture" src="images/architecture-light.png" />
       </picture>
     </div>
     
    @@ -40,8 +30,8 @@ Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, rep
     
     |               | Cookbook        | Open in Colab                                                                                                                                                                                                                                  |
     | ------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    -| Basic         | PraisonAI       | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>       |
    -| Include Tools | PraisonAI Tools | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-tools-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a> |
    +| Basic         | PraisonAI       | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" /></a>       |
    +| Include Tools | PraisonAI Tools | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-tools-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" /></a> |
     
     ## Install
     
    @@ -79,4 +69,4 @@ praisonai --init create a movie script about dog in moon
     ## Run
     ```bash
     praisonai
    -```
    \ No newline at end of file
    +```
    
  • docs/introduction.md+28 0 added
    @@ -0,0 +1,28 @@
    +---
    +title: 'Introduction to PraisonAI'
    +description: 'Get started with PraisonAI, an intelligent AI-powered tool'
    +---
    +
    +# Introduction to PraisonAI
    +
    +Welcome to PraisonAI documentation! PraisonAI is a low-code, centralized framework designed to simplify the creation and orchestration of multi-agent systems for various LLM applications, emphasizing ease of use, customization, and human-agent interaction.
    +
    +## Key Features
    +
    +- Leverages both AutoGen and CrewAI
    +- Simplifies multi-agent system creation
    +- Emphasizes ease of use and customization
    +- Supports human-agent interaction
    +
    +## Getting Started
    +
    +To get started with PraisonAI, check out our [Installation](./installation) guide.
    +
    +For a quick overview, see our [TL;DR](./tldr) page.
    +
    +## Explore Further
    +
    +- [User Interface](./ui/ui)
    +- [Models](./models)
    +- [Tools](./tools)
    +- [API Reference](./api-reference)
    
  • docs/ui/ui.md+6 3 modified
    @@ -8,8 +8,7 @@
     | **Chat** | Chat with 100+ LLMs, single AI Agent | [https://docs.praison.ai/ui/chat](https://docs.praison.ai/ui/chat) |
     | **Code** | Chat with entire Codebase, single AI Agent | [https://docs.praison.ai/ui/code](https://docs.praison.ai/ui/code) |
     
    -## Chainlit
    -```bash
    +## Chainlit```bash
     pip install -U "praisonai[ui]"
     export OPENAI_API_KEY="Enter your API key"
     chainlit create-secret
    @@ -88,4 +87,8 @@ streamlit run app.py
     ![Run Agents](../images/ui-step-8.png)
     
     ### Manual Model Output
    -![Manual Model Output](../images/ui-step-11.png)
    \ No newline at end of file
    +![Manual Model Output](../images/ui-step-11.png)
    +
    +## PraisonAI Call
    +
    +To use the PraisonAI Call feature:
    
  • eslint.config.mjs+18 0 added
    @@ -0,0 +1,18 @@
    +import globals from "globals";
    +import * as mdx from 'eslint-plugin-mdx';
    +
    +export default [
    +  {
    +    files: ["**/*.{md,mdx}"],
    +    processor: mdx.createRemarkProcessor({
    +      lintCodeBlocks: true,
    +    }),
    +    languageOptions: {
    +      globals: globals.browser,
    +    },
    +    rules: {
    +      'no-var': 'error',
    +      'prefer-const': 'error',
    +    },
    +  },
    +];
    
  • .eslintrc+10 0 added
    @@ -0,0 +1,10 @@
    +{
    +    "extends": ["plugin:mdx/recommended"],
    +    // optional, if you want to lint code blocks at the same time
    +    "settings": {
    +      "mdx/code-blocks": true,
    +      // optional, if you want to disable language mapper, set it to `false`
    +      // if you want to override the default language mapper inside, you can provide your own
    +      "mdx/language-mapper": {}
    +    }
    +  }
    \ No newline at end of file
    
  • .gitignore+4 1 modified
    @@ -54,4 +54,7 @@ crewAI
     .venv
     
     # Credentials
    -credentials.json
    \ No newline at end of file
    +credentials.json
    +
    +# node_modules
    +node_modules
    
  • mint.json+109 0 added
    @@ -0,0 +1,109 @@
    +{
    +  "name": "PraisonAI Documentation",
    +  "logo": {
    +    "light": "/docs/images/praisonai-logo-black-large.png",
    +    "dark": "/docs/images/praisonai-logo-large.png"
    +  },
    +  "favicon": "/docs/images/favicon.png",
    +  "colors": {
    +    "primary": "#0069ED",
    +    "light": "#4D9CFF",
    +    "dark": "#0050B4"
    +  },
    +  "topbarLinks": [
    +    {
    +      "name": "GitHub",
    +      "url": "https://github.com/MervinPraison/PraisonAI"
    +    }
    +  ],
    +  "topbarCtaButton": {
    +    "name": "Get Started",
    +    "url": "https://docs.praison.ai/installation"
    +  },
    +  "navigation": [
    +    {
    +      "group": "Getting Started",
    +      "pages": [
    +        "introduction",
    +        "tldr",
    +        "installation",
    +        "initialise",
    +        "run",
    +        "auto"
    +      ]
    +    },
    +    {
    +      "group": "User Interface",
    +      "pages": [
    +        "ui/ui",
    +        "ui/chat",
    +        "ui/code",
    +        "ui/realtime"
    +      ]
    +    },
    +    {
    +      "group": "Core Concepts",
    +      "pages": [
    +        "train",
    +        "framework/crewai",
    +        "framework/autogen"
    +      ]
    +    },
    +    {
    +      "group": "Models",
    +      "pages": [
    +        "models",
    +        "models/openai",
    +        "models/groq",
    +        "models/google",
    +        "models/anthropic",
    +        "models/cohere",
    +        "models/mistral",
    +        "models/openrouter",
    +        "models/ollama",
    +        "models/other"
    +      ]
    +    },
    +    {
    +      "group": "Tools",
    +      "pages": [
    +        "tools",
    +        "tools/custom",
    +        "tools/gpt",
    +        "tools/duckduckgo",
    +        "tools/langchain",
    +        "firecrawl",
    +        "tools/wikipedia",
    +        "tools/youtube",
    +        "tools/tavily",
    +        "tools/reddit",
    +        "tools/you.com",
    +        "tools/crawl4ai",
    +        "tools/mem0"
    +      ]
    +    },
    +    {
    +      "group": "Monitoring",
    +      "pages": ["monitoring/agentops"]
    +    },
    +    {
    +      "group": "Developers",
    +      "pages": [
    +        "developers/test",
    +        "developers/agents-playbook",
    +        "developers/wrapper",
    +        "developers/wrapper-tools",
    +        "developers/googlecolab",
    +        "developers/googlecolab-tools",
    +        "deploy"
    +      ]
    +    },
    +    {
    +      "group": "API Reference",
    +      "pages": ["api-reference/index"]
    +    }
    +  ],
    +  "footerSocials": {
    +    "github": "https://github.com/MervinPraison/PraisonAI"
    +  }
    +}
    
  • package.json+9 0 added
    @@ -0,0 +1,9 @@
    +{
    +  "devDependencies": {
    +    "eslint": "^8.57.1",
    +    "eslint-plugin-mdx": "^3.1.5",
    +    "eslint-plugin-react": "^7.37.1",
    +    "globals": "^15.11.0",
    +    "typescript-eslint": "^8.8.1"
    +  }
    +}
    
  • package-lock.json+6906 0 added
  • poetry.lock+1180 1156 modified
  • praisonai/api/call.py+188 0 added
    @@ -0,0 +1,188 @@
    +import os
    +import json
    +import base64
    +import asyncio
    +import websockets
    +from fastapi import FastAPI, WebSocket, Request
    +from fastapi.responses import HTMLResponse
    +from fastapi.websockets import WebSocketDisconnect
    +from twilio.twiml.voice_response import VoiceResponse, Connect, Say, Stream
    +from dotenv import load_dotenv
    +import typer
    +import uvicorn
    +from pyngrok import ngrok
    +from rich import print
    +
    +load_dotenv()
    +
    +# Configuration
    +OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') # requires OpenAI Realtime API Access
    +PORT = int(os.getenv('PORT', 8090))
    +SYSTEM_MESSAGE = (
    +    "You are a helpful and bubbly AI assistant who loves to chat about "
    +    "anything the user is interested in and is prepared to offer them facts. "
    +    "You have a penchant for dad jokes, owl jokes, and rickrolling – subtly. "
    +    "Always stay positive, but work in a joke when appropriate."
    +)
    +VOICE = 'alloy'
    +LOG_EVENT_TYPES = [
    +    'response.content.done', 'rate_limits.updated', 'response.done',
    +    'input_audio_buffer.committed', 'input_audio_buffer.speech_stopped',
    +    'input_audio_buffer.speech_started', 'session.created'
    +]
    +
    +app = FastAPI()
    +
    +if not OPENAI_API_KEY:
    +    raise ValueError('Missing the OpenAI API key. Please set it in the .env file.')
    +
    +@app.get("/", response_class=HTMLResponse)
    +async def index_page():
    +    return """
    +    <html>
    +        <head>
    +            <title>Praison AI Call Server</title>
    +        </head>
    +        <body>
    +            <h1>Praison AI Call Server is running!</h1>
    +        </body>
    +    </html>
    +    """
    +
    +@app.api_route("/call", methods=["GET", "POST"])
    +async def handle_incoming_call(request: Request):
    +    """Handle incoming call and return TwiML response to connect to Media Stream."""
    +    response = VoiceResponse()
    +    response.say("")
    +    response.pause(length=1)
    +    response.say("O.K. you can start talking!")
    +    host = request.url.hostname
    +    connect = Connect()
    +    connect.stream(url=f'wss://{host}/media-stream')
    +    response.append(connect)
    +    return HTMLResponse(content=str(response), media_type="application/xml")
    +
    +@app.websocket("/media-stream")
    +async def handle_media_stream(websocket: WebSocket):
    +    """Handle WebSocket connections between Twilio and OpenAI."""
    +    print("Client connected")
    +    await websocket.accept()
    +
    +    async with websockets.connect(
    +        'wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01',
    +        extra_headers={
    +            "Authorization": f"Bearer {OPENAI_API_KEY}",
    +            "OpenAI-Beta": "realtime=v1"
    +        }
    +    ) as openai_ws:
    +        await send_session_update(openai_ws)
    +        stream_sid = None
    +
    +        async def receive_from_twilio():
    +            """Receive audio data from Twilio and send it to the OpenAI Realtime API."""
    +            nonlocal stream_sid
    +            try:
    +                async for message in websocket.iter_text():
    +                    data = json.loads(message)
    +                    if data['event'] == 'media' and openai_ws.open:
    +                        audio_append = {
    +                            "type": "input_audio_buffer.append",
    +                            "audio": data['media']['payload']
    +                        }
    +                        await openai_ws.send(json.dumps(audio_append))
    +                    elif data['event'] == 'start':
    +                        stream_sid = data['start']['streamSid']
    +                        print(f"Incoming stream has started {stream_sid}")
    +            except WebSocketDisconnect:
    +                print("Client disconnected.")
    +                if openai_ws.open:
    +                    await openai_ws.close()
    +
    +        async def send_to_twilio():
    +            """Receive events from the OpenAI Realtime API, send audio back to Twilio."""
    +            nonlocal stream_sid
    +            try:
    +                async for openai_message in openai_ws:
    +                    response = json.loads(openai_message)
    +                    if response['type'] in LOG_EVENT_TYPES:
    +                        print(f"Received event: {response['type']}", response)
    +                    if response['type'] == 'session.updated':
    +                        print("Session updated successfully:", response)
    +                    if response['type'] == 'response.audio.delta' and response.get('delta'):
    +                        # Audio from OpenAI
    +                        try:
    +                            audio_payload = base64.b64encode(base64.b64decode(response['delta'])).decode('utf-8')
    +                            audio_delta = {
    +                                "event": "media",
    +                                "streamSid": stream_sid,
    +                                "media": {
    +                                    "payload": audio_payload
    +                                }
    +                            }
    +                            await websocket.send_json(audio_delta)
    +                        except Exception as e:
    +                            print(f"Error processing audio data: {e}")
    +            except Exception as e:
    +                print(f"Error in send_to_twilio: {e}")
    +
    +        await asyncio.gather(receive_from_twilio(), send_to_twilio())
    +
    +async def send_session_update(openai_ws):
    +    """Send session update to OpenAI WebSocket."""
    +    session_update = {
    +        "type": "session.update",
    +        "session": {
    +            "turn_detection": {
    +                "type": "server_vad",
    +                "threshold": 0.5,
    +                "prefix_padding_ms": 300,
    +                "silence_duration_ms": 200
    +            },
    +            "input_audio_format": "g711_ulaw",
    +            "output_audio_format": "g711_ulaw",
    +            # "input_audio_transcription": { "model": 'whisper-1' },
    +            # "transcription_models": [{"model": "whisper-1"}],
    +            "voice": VOICE,
    +            "tools": [],
    +            "tool_choice": "auto",
    +            "instructions": SYSTEM_MESSAGE,
    +            "modalities": ["text", "audio"],
    +            "temperature": 0.8
    +        }
    +    }
    +    print('Sending session update:', json.dumps(session_update))
    +    await openai_ws.send(json.dumps(session_update))
    +
    +def run_server(port: int, use_ngrok: bool = False):
    +    """Run the FastAPI server using uvicorn."""
    +    if use_ngrok:
    +        public_url = ngrok.connect(port).public_url
    +        # print(f"Ngrok tunnel established: {public_url}")
    +        print(f"Praison AI Voice URL: {public_url}/call")
    +    
    +    print(f"Starting Praison AI Call Server on port {port}...")
    +    uvicorn.run(app, host="0.0.0.0", port=port, log_level="warning")
    +
    +app_cli = typer.Typer()
    +
    +@app_cli.command()
    +def main(
    +    port: int = typer.Option(8090, help="Port to run the server on"),
    +    ngrok: bool = typer.Option(False, help="Use ngrok to expose the server")
    +):
    +    """Run the Praison AI Call Server."""
    +    # print(f"Received port value: {port}")  # Debug print
    +    # print(f"Use ngrok: {ngrok}")  # Debug print
    +    
    +    # Extract the actual port value from the OptionInfo object
    +    if isinstance(port, typer.models.OptionInfo):
    +        port_value = port.default
    +    else:
    +        port_value = port
    +    
    +    port_int = int(port_value)
    +    
    +    run_server(port=port_int, use_ngrok=ngrok)
    +
    +if __name__ == "__main__":
    +    app_cli()
    
  • praisonai/cli.py+10 1 modified
    @@ -16,6 +16,8 @@
     import shutil
     import subprocess
     import logging
    +import importlib
    +import praisonai.api.call as call_module
     logging.basicConfig(level=os.environ.get('LOGLEVEL', 'INFO'), format='%(asctime)s - %(levelname)s - %(message)s')
     
     try:
    @@ -134,6 +136,10 @@ def main(self):
                 self.create_realtime_interface()
                 return
             
    +        if getattr(args, 'call', False):
    +            call_module.main()
    +            return
    +        
             if args.agent_file == 'train':
                 package_root = os.path.dirname(os.path.abspath(__file__))
                 config_yaml_destination = os.path.join(os.getcwd(), 'config.yaml')
    @@ -261,6 +267,7 @@ def parse_args(self):
             parser.add_argument("--ollama", type=str, help="Ollama model name")
             parser.add_argument("--dataset", type=str, help="Dataset name for training", default="yahma/alpaca-cleaned")
             parser.add_argument("--realtime", action="store_true", help="Start the realtime voice interaction interface")
    +        parser.add_argument("--call", action="store_true", help="Start the PraisonAI Call server")
             args, unknown_args = parser.parse_known_args()
     
             if unknown_args and unknown_args[0] == '-b' and unknown_args[1] == 'api:app':
    @@ -277,6 +284,8 @@ def parse_args(self):
                 args.code = True
             if args.agent_file == 'realtime':
                 args.realtime = True
    +        if args.agent_file == 'call':
    +            args.call = True
     
             return args
         
    @@ -448,4 +457,4 @@ def create_realtime_interface(self):
     
     if __name__ == "__main__":
         praison_ai = PraisonAI()
    -    praison_ai.main()
    \ No newline at end of file
    +    praison_ai.main()
    
  • praisonai/deploy.py+1 1 modified
    @@ -56,7 +56,7 @@ def create_dockerfile(self):
                 file.write("FROM python:3.11-slim\n")
                 file.write("WORKDIR /app\n")
                 file.write("COPY . .\n")
    -            file.write("RUN pip install flask praisonai==0.1.3 gunicorn markdown\n")
    +            file.write("RUN pip install flask praisonai==0.1.4 gunicorn markdown\n")
                 file.write("EXPOSE 8080\n")
                 file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
                 
    
  • praisonai.rb+1 1 modified
    @@ -3,7 +3,7 @@ class Praisonai < Formula
       
         desc "AI tools for various AI applications"
         homepage "https://github.com/MervinPraison/PraisonAI"
    -    url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/0.1.3.tar.gz"
    +    url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/0.1.4.tar.gz"
         sha256 "1828fb9227d10f991522c3f24f061943a254b667196b40b1a3e4a54a8d30ce32"  # Replace with actual SHA256 checksum
         license "MIT"
       
    
  • pyproject.toml+11 2 modified
    @@ -1,6 +1,6 @@
     [tool.poetry]
     name = "PraisonAI"
    -version = "0.1.3"
    +version = "0.1.4"
     description = "PraisonAI application combines AutoGen and CrewAI or similar frameworks into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customization, and efficient human-agent collaboration."
     authors = ["Mervin Praison"]
     license = ""
    @@ -38,6 +38,13 @@ websockets = {version = ">=12.0", optional = true}
     plotly = {version = ">=5.24.0", optional = true}
     yfinance = {version = ">=0.2.44", optional = true}
     duckduckgo_search = {version = ">=6.3.0", optional = true}
    +twilio = {version = ">=7.0.0", optional = true}
    +fastapi = {version = ">=0.95.0", optional = true}
    +uvicorn = {version = ">=0.20.0", optional = true}
    +python-dotenv = {version = ">=0.19.0", optional = true}
    +typer = {version = ">=0.9.0", optional = true}
    +flaml = {version = ">=2.3.1", extras = ["automl"], optional = true}
    +pyngrok = {version = ">=1.4.0", optional = true}
     
     [tool.poetry.group.docs.dependencies]
     mkdocs = "*"
    @@ -97,6 +104,7 @@ build-backend = "poetry.core.masonry.api"
     praisonai = "praisonai.__main__:main"
     setup-conda-env = "setup.setup_conda_env:main"
     post-install = "setup.post_install:main"
    +praisonai-call = "praisonai.api.call:main"
     
     [tool.poetry.extras]
     ui = ["chainlit"]
    @@ -111,6 +119,7 @@ chat = ["chainlit", "litellm", "aiosqlite", "greenlet", "tavily-python", "crawl4
     code = ["chainlit", "litellm", "aiosqlite", "greenlet", "tavily-python", "crawl4ai"]
     train = ["setup-conda-env"]
     realtime = ["chainlit", "litellm", "aiosqlite", "greenlet", "tavily-python", "crawl4ai", "websockets", "plotly", "yfinance", "duckduckgo_search"]
    +call = ["twilio", "fastapi", "uvicorn", "websockets", "python-dotenv", "typer", "flaml", "pyngrok", "rich"]
     
     [tool.poetry-dynamic-versioning]
     enable = true
    @@ -119,4 +128,4 @@ style = "semver"
     
     [tool.poetry.build]
     generate-setup-file = false
    -script = "praisonai/setup/post_install.py"
    \ No newline at end of file
    +script = "praisonai/setup/post_install.py"
    
  • README.md+13 11 modified
    @@ -1,15 +1,15 @@
     <p align="center">
       <picture>
    -    <source media="(prefers-color-scheme: dark)" srcset="docs/images/praisonai-logo-large.png">
    -    <source media="(prefers-color-scheme: light)" srcset="docs/images/praisonai-logo-black-large.png">
    -    <img alt="PraisonAI Logo" src="docs/images/praisonai-logo-black-large.png">
    +    <source media="(prefers-color-scheme: dark)" srcset="docs/logo/dark.png" />
    +    <source media="(prefers-color-scheme: light)" srcset="docs/logo/light.png" />
    +    <img alt="PraisonAI Logo" src="docs/logo/light.png" />
       </picture>
     </p>
     
     <p align="center">
    -<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads"></a>
    -<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version"></a>
    -<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License"></a>
    +<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads" /></a>
    +<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version" /></a>
    +<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" /></a>
     </p>
     
     <div align="center">
    @@ -22,9 +22,9 @@ Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, rep
     
     <div align="center">
       <picture>
    -    <source media="(prefers-color-scheme: dark)" srcset="docs/images/architecture-dark.png">
    -    <source media="(prefers-color-scheme: light)" srcset="docs/images/architecture-light.png">
    -    <img alt="PraisonAI Architecture" src="docs/images/architecture-light.png">
    +    <source media="(prefers-color-scheme: dark)" srcset="docs/images/architecture-dark.png" />
    +    <source media="(prefers-color-scheme: light)" srcset="docs/images/architecture-light.png" />
    +    <img alt="PraisonAI Architecture" src="docs/images/architecture-light.png" />
       </picture>
     </div>
     
    @@ -46,8 +46,8 @@ Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, rep
     
     |               | Cookbook        | Open in Colab                                                                                                                                                                                                                                  |
     | ------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    -| Basic         | PraisonAI       | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>       |
    -| Include Tools | PraisonAI Tools | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-tools-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a> |
    +| Basic         | PraisonAI       | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" /></a>       |
    +| Include Tools | PraisonAI Tools | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-tools-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" /></a> |
     
     ## Install
     
    @@ -58,6 +58,7 @@ Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, rep
     | **PraisonAI Chat** | `pip install "praisonai[chat]"` |
     | **PraisonAI Train** | `pip install "praisonai[train]"` |
     | **PraisonAI Realtime** | `pip install "praisonai[realtime]"` |
    +| **PraisonAI Call** | `pip install "praisonai[call]"` |
     
     ## Key Features
     
    @@ -324,3 +325,4 @@ This configuration ensures that your development dependencies are correctly cate
     ## License
     
     Praison AI is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
    +
    

Vulnerability mechanics

Root cause

"ProjectService methods perform primary-key-only lookups with no workspace_id predicate, so a user who is a member of any workspace can access projects belonging to any other workspace."

Attack vector

An attacker who is a member of any workspace can read, modify, delete, or read stats for projects belonging to a different workspace. The attacker first obtains a target project UUID (e.g. from activity feeds, issue records, webhook payloads, or error messages). They then send authenticated requests to endpoints like `GET /workspaces/{attacker_workspace}/projects/{target_uuid}`; the `require_workspace_member` check passes because the attacker is a member of their own workspace, but `ProjectService.get(project_id)` performs a primary-key-only SQL lookup with no `workspace_id` filter, returning the foreign project. The same pattern applies to PATCH, DELETE, and stats endpoints. [ref_id=1][ref_id=2]

Affected code

The bug is in `src/praisonai-platform/praisonai_platform/services/project_service.py`, lines 47–108, where `ProjectService.get(project_id)`, `update(project_id, ...)`, `delete(project_id)`, and `get_stats(project_id)` all perform primary-key-only lookups with no `workspace_id` predicate. The route handlers in `src/praisonai-platform/praisonai_platform/api/routes/projects.py`, lines 51–108, accept `workspace_id` from the URL but never thread it into the service calls, using it only for the membership gate. [ref_id=1][ref_id=2]

What the fix does

The patch modifies `ProjectService.get()` to accept a `workspace_id` parameter and adds a `WHERE` clause filtering on both `Project.id` and `Project.workspace_id`, so that a project is only returned if it belongs to the workspace specified in the URL. The `update()`, `delete()`, and `get_stats()` methods are updated to pass `workspace_id` through to `get()`, and the route handlers in `routes/projects.py` are updated to thread `workspace_id` into every service call. This ensures that a project from a different workspace is treated as not found (404), closing the IDOR. [ref_id=1][ref_id=2]

Preconditions

  • configpraisonai-platform is deployed in a multi-tenant configuration
  • authAttacker has a valid membership token for any workspace
  • inputAttacker knows or can guess the target project's UUID

Generated on Jun 1, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.