Root cause
Injection & unsafe execution introduced
feat: add Linear integration — agentic control plane with tools + LinearBot + AgentSession webhooks
How AI contributed: Direct introductionPublished Jun 18, 2026 in
mervinpraison/praisonaiAll 19 findings in this repository
LinearBot webhook signature fail-open: LinearBot in src/praisonai/praisonai/bots/linear.py started a public webhook listener on 0.0.0.0 and silently skipped Linear-Signature HMAC verification when LINEAR_WEBHOOK_SECRET was unset, allowing unauthenticated network callers to forge AgentSession webhooks and trigger arbitrary agent executions and Linear comment posting.
Root cause
feat: add Linear integration — agentic control plane with tools + LinearBot + AgentSession webhooks
Fix
refactor: harden input validation and access controls
Case facts
@@ -0,0 +1,512 @@+"""+Linear Bot implementation for PraisonAI.++Supports Linear agent integration via AgentSession webhooks.++Usage:+ bot = LinearBot(token="linear-oauth-token", agent=agent, + signing_secret="webhook-secret")+ await bot.start()+"""++from __future__ import annotations++import asyncio+import hashlib+import hmac+import json+import logging+import os+import time+from typing import Any, Callable, Dict, List, Optional, TYPE_CHECKING, Union++if TYPE_CHECKING:+ from praisonaiagents import Agent++from praisonai.bots._protocol_mixin import ChatCommandMixin, MessageHookMixin+from praisonaiagents.bots import (+ BotConfig,+ BotMessage,+ BotUser,+ BotChannel,+ MessageType,+)++from ._commands import format_status, format_help+from ._session import BotSessionManager+from ._rate_limit import RateLimiter+from ._ack import AckReactor++logger = logging.getLogger(__name__)++# Linear GraphQL endpoint+LINEAR_API_BASE = "https://api.linear.app/graphql"+++class LinearBot(ChatCommandMixin, MessageHookMixin):+ """Linear bot runtime for PraisonAI agents.++ Connects an agent to Linear via AgentSession webhooks, handling+ mentions, assignments, and providing full bot functionality.++ Example:+ from praisonai.bots import LinearBot+ from praisonaiagents import Agent++ agent = Agent(name="assistant")+ bot = LinearBot(+ token="YOUR_OAUTH_TOKEN",+ signing_secret="YOUR_WEBHOOK_SECRET",+ agent=agent,+ )++ await bot.start()+ """++ def __init__(+ self,+ token: str = "",+ agent: Optional["Agent"] = None,+ config: Optional[BotConfig] = None,+ signing_secret: str = "",+ webhook_port: int = 8080,+ webhook_path: str = "/webhook",+ **kwargs,+ ):+ # Store extra kwargs for forward compatibility+ self._extra_kwargs = kwargs+ + self._token = token or os.environ.get("LINEAR_OAUTH_TOKEN", "") or os.environ.get("LINEAR_API_KEY", "")+ self._agent = agent+ self.config = config or BotConfig(token=self._token, mode="webhook")+ self._signing_secret = signing_secret or os.environ.get("LINEAR_WEBHOOK_SECRET", "")+ self._webhook_port = webhook_port+ self._webhook_path = webhook_path++ self._is_running = False+ self._started_at: Optional[float] = None+ self._bot_user: Optional[BotUser] = None+ @@ -240,12 +240,16 @@ async def _handle_webhook(self, request) -> Any: # Read raw body for signature verification raw_body = await request.read() - # Verify signature if secret is configured+ from praisonai.bots.webhook_security import webhooks_require_verification+ if self._signing_secret: signature = request.headers.get("Linear-Signature", "") if not self._verify_signature(raw_body, signature): logger.warning("Invalid webhook signature") return web.Response(status=401, text="Invalid signature")+ elif webhooks_require_verification():+ logger.warning("Webhook rejected: signing secret not configured")+ return web.Response(status=401, text="Webhook verification not configured") # Parse JSON body try:AI-assisted change f4d5ba77a21b919b343f2a3596739aebf834313f1fa65ce83d8d377d13ddaadf · Fix ad79593fa8a84fbb37aaa3592c21985552bdb47e174da6d6641496d48465b9f0