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

praisonai-platform: Any workspace member can delete the entire workspace via DELETE /workspaces/{id}

CVE-2026-47412

Description

Any member of a PraisonAI workspace can delete the entire workspace and all its data via the DELETE /workspaces/{id} endpoint due to an authorization bypass.

AI Insight

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

Any member of a PraisonAI workspace can delete the entire workspace and all its data via the DELETE /workspaces/{id} endpoint due to an authorization bypass.

Vulnerability

The DELETE /workspaces/{workspace_id} endpoint in src/praisonai-platform/praisonai_platform/api/routes/workspaces.py (lines 77–86) is protected only by Depends(require_workspace_member), which defaults to min_role="member" and is never overridden to require a higher privilege (e.g., owner). The dependency checks that the user is a member of the workspace but does not verify any role level before calling WorkspaceService.delete(), which performs a cascading delete of the workspace and all associated records (projects, issues, comments, agents, labels, members). No confirmation token, soft-delete, or recovery mechanism exists. This affects all versions up to and including the one disclosed in the advisory. [1][2]

Exploitation

An attacker needs only a valid session as any member of the target workspace (no special role required). The attacker sends a single HTTP DELETE request to /workspaces/{workspace_id} (with the workspace ID known or enumerable). No user interaction beyond the attacker's own action is needed; no race condition or additional privilege is required. The endpoint immediately executes the deletion. [1][2]

Impact

On success, the attacker causes complete and irreversible loss of the entire workspace: every project, issue, comment, agent, label, and member record is permanently deleted. This is a denial-of-service and data-loss event with no recovery path. The affected workspace owner loses all tenant data. [1][2]

Mitigation

The fix requires changing the dependency to require the owner role (e.g., Depends(require_workspace_member(min_role="owner"))) and ideally adding a confirmation mechanism (e.g., requiring the user to type the workspace name) and a soft-delete/recovery window. As of the advisory publication date (2026-06-01), the vendor has been notified but no patched version or workaround has been released in the available references. [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

"The DELETE /workspaces/{workspace_id} endpoint uses require_workspace_member with the default min_role='member' instead of requiring the owner role, allowing any workspace member to delete the entire workspace."

Attack vector

An attacker who is a member of a workspace (holding a JWT with `role="member"`) sends a single `DELETE /workspaces/{workspace_id}` request with their bearer token. The `require_workspace_member` dependency passes because the default minimum role is `"member"`, allowing the request to reach `WorkspaceService.delete()`. SQLAlchemy cascade rules then remove the workspace row and every related row (members, projects, issues, comments, agents, labels). There is no owner-role gate, confirmation token, soft-delete window, or recovery path. [ref_id=1] [ref_id=2]

Affected code

**File:** `src/praisonai-platform/praisonai_platform/api/routes/workspaces.py`, lines 77-86; `services/workspace_service.py`'s `delete()` method. The route uses `Depends(require_workspace_member)` which defaults to `min_role="member"` and is never overridden. The service method `WorkspaceService.delete(workspace_id)` performs the destructive operation without any caller-permission verification. The role hierarchy (`MemberService.has_role`, member_service.py:80-96) is implemented but unused for this endpoint. [ref_id=1]

What the fix does

The patch introduces a new dependency `_require_workspace_owner` that calls `require_workspace_member` with `min_role="owner"`, and replaces the existing `Depends(require_workspace_member)` on the delete endpoint. This ensures only users with the `owner` role can invoke the destructive delete operation. Member-tier tokens now fail at the dependency gate, returning a 403 before the service layer is reached. [ref_id=1] [ref_id=2]

Preconditions

  • configpraisonai-platform is deployed multi-tenant
  • authAttacker has any membership token (role='member') in the target workspace
  • networkAttacker can send HTTP DELETE requests to the API endpoint

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.