praisonai-platform: Comment endpoints accept any issue_id without workspace ownership check, cross-workspace comment read and post IDOR
Description
Comment endpoints in PraisonAI platform fail to verify issue ownership, allowing cross-workspace reading and posting of comments.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Comment endpoints in PraisonAI platform fail to verify issue ownership, allowing cross-workspace reading and posting of comments.
Vulnerability
The comment endpoints POST /workspaces/{workspace_id}/issues/{issue_id}/comments and GET .../comments only check workspace membership via require_workspace_member(workspace_id) but then pass the user-supplied issue_id directly to CommentService without verifying that the issue belongs to the workspace. This affects versions in the affected code [1][2].
Exploitation
An attacker who is a legitimate member of any workspace can craft requests targeting issues in other workspaces by supplying a different issue_id. They can list all comments on any issue and post new comments, bypassing workspace boundaries [1][2].
Impact
The attacker gains unauthorized read access to all comments on any issue across all workspaces, and can inject arbitrary comments into any issue. This compromises confidentiality and integrity of issue discussions [1][2].
Mitigation
The fix requires validating that the issue_id belongs to the workspace before processing the comment. As per the advisory, the vendor was notified. No specific patch version is mentioned; users should apply the fix from the repository or wait for a patched release. If no patch is available, consider disabling the comment endpoints or adding manual validation [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
1Patches
28c4ab718aeebPraisonAI Call API Release
7 files changed · +43 −14
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("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')
poetry.lock+20 −2 modified@@ -6258,6 +6258,24 @@ cffi = ">=1.4.1" docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] +[[package]] +name = "pyngrok" +version = "7.2.0" +description = "A Python wrapper for ngrok." +optional = true +python-versions = ">=3.8" +files = [ + {file = "pyngrok-7.2.0-py3-none-any.whl", hash = "sha256:1e96ab1229736e2e030fa8975805ab1fa9e463178f83337fc07fdd2b4e8dbed6"}, + {file = "pyngrok-7.2.0.tar.gz", hash = "sha256:4e43af9b2f21ceed8d213797028fe8823003f185b49792e4d383302365c81515"}, +] + +[package.dependencies] +PyYAML = ">=5.1" + +[package.extras] +dev = ["coverage[toml]", "flake8", "flake8-pyproject", "pep8-naming", "psutil"] +docs = ["Sphinx", "mypy", "sphinx-autodoc-typehints (==1.25.2)", "sphinx-notfound-page", "sphinx-substitution-extensions", "types-PyYAML"] + [[package]] name = "pyparsing" version = "3.2.0" @@ -8857,7 +8875,7 @@ type = ["pytest-mypy"] agentops = ["agentops"] anthropic = ["langchain-anthropic"] api = ["flask"] -call = ["fastapi", "flaml", "python-dotenv", "twilio", "typer", "uvicorn", "websockets"] +call = ["fastapi", "flaml", "pyngrok", "python-dotenv", "rich", "twilio", "typer", "uvicorn", "websockets"] chat = ["aiosqlite", "chainlit", "crawl4ai", "greenlet", "litellm", "tavily-python"] code = ["aiosqlite", "chainlit", "crawl4ai", "greenlet", "litellm", "tavily-python"] cohere = ["langchain-cohere"] @@ -8871,4 +8889,4 @@ ui = ["chainlit"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "74602750ef14d7040dad02842980b2eebbf0ac8c1f4324627602764578814a5a" +content-hash = "d246ccacd08ba3599a7b22e222e784396df2385dc76532b56e86bfccaf7522c0"
praisonai/api/call.py+16 −6 modified@@ -10,6 +10,8 @@ from dotenv import load_dotenv import typer import uvicorn +from pyngrok import ngrok +from rich import print load_dotenv() @@ -151,17 +153,26 @@ async def send_session_update(openai_ws): print('Sending session update:', json.dumps(session_update)) await openai_ws.send(json.dumps(session_update)) -def run_server(port: int): +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) + 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")): +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"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): @@ -170,9 +181,8 @@ def main(port: int = typer.Option(8090, help="Port to run the server on")): port_value = port port_int = int(port_value) - print(f"Using port: {port_int}") # Debug print - run_server(port=port_int) + run_server(port=port_int, use_ngrok=ngrok) if __name__ == "__main__": app_cli()
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+3 −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 = "" @@ -44,6 +44,7 @@ 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 = "*" @@ -118,7 +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"] +call = ["twilio", "fastapi", "uvicorn", "websockets", "python-dotenv", "typer", "flaml", "pyngrok", "rich"] [tool.poetry-dynamic-versioning] enable = true
b707fd0b27fbMerge pull request #171 from MervinPraison/develop
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("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')
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  ### Manual Model Output - \ No newline at end of file + + +## 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 addedpoetry.lock+1180 −1156 modifiedpraisonai/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 route layer gates access on workspace membership but never verifies that the URL-supplied issue_id belongs to that workspace, allowing cross-workspace comment read and write."
Attack vector
An attacker who is a member of any workspace can read every comment on, and post new comments to, any issue in any other workspace. The attacker harvests a target issue UUID from side channels (activity feed, webhooks, cross-workspace links) and sends `GET /workspaces/{attacker_workspace}/issues/{target_issue}/comments` or `POST` with a comment body. The membership check passes because the attacker controls the workspace in the URL path, and `CommentService` never confirms the issue belongs to that workspace [CWE-639].
Affected code
The vulnerability is in `src/praisonai-platform/praisonai_platform/api/routes/issues.py`, lines 143-171, and `src/praisonai-platform/praisonai_platform/services/comment_service.py`, lines 19-53. The `add_comment` and `list_comments` route handlers gate access via `require_workspace_member(workspace_id)` but then pass the URL-supplied `issue_id` directly to `CommentService.create()` and `CommentService.list_for_issue()` without verifying that the issue belongs to the workspace. The service methods query or write comments using only `issue_id` with no workspace join.
What the fix does
The suggested fix adds a `_require_issue_in_workspace` helper that resolves the issue scoped to `workspace_id` before dispatching to `CommentService`. This ensures that if the issue does not belong to the workspace, a 404 is returned, preventing both unauthorized reads and writes. The fix is applied at the route layer in `issues.py` and does not change the `CommentService` signature.
Preconditions
- configpraisonai-platform is deployed multi-tenant
- authAttacker has any workspace membership token
- inputTarget issue UUID is known or guessable
Generated on Jun 1, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.