The vulnerability is an improper authentication issue in PraisonAI's LinearBot. The root cause is a 'fail-open' design in the webhook handling logic. The LinearBot._handle_webhook function would only verify the cryptographic signature of an incoming webhook if a LINEAR_WEBHOOK_SECRET was configured. If the secret was missing, the bot would skip the verification and process the request, allowing an unauthenticated attacker to forge webhook events and invoke the bot's agent. This was compounded by the LinearBot.start function, which would bind the listener to a public network interface (0.0.0.0) even in this insecure state. The provided patch corrects this by introducing a 'fail-closed' mechanism. It modifies _handle_webhook to reject requests with a 401 Unauthorized status if the signing secret is not configured, unless a specific environment variable (PRAISONAI_INSECURE_WEBHOOKS) is set to explicitly allow this behavior for development purposes.
Vulnerable functions
praisonai.bots.linear.LinearBot._handle_webhook
src/praisonai/praisonai/bots/linear.py
This function is the entry point for incoming webhooks. The vulnerability lies in its conditional logic. It only performs signature verification if a `_signing_secret` is configured. If the secret is missing, it proceeds to process the webhook without any authentication, allowing an attacker to send forged requests. The patch adds a check to reject requests if the secret is missing and insecure webhooks are not explicitly enabled.
praisonai.bots.linear.LinearBot.__init__
src/praisonai/praisonai/bots/linear.py
The constructor contributes to the vulnerability by allowing the `_signing_secret` to be initialized as an empty string if the `LINEAR_WEBHOOK_SECRET` environment variable is not set. This empty state is what the `_handle_webhook` function uses as the condition to insecurely skip signature verification.
praisonai.bots.linear.LinearBot.start
src/praisonai/praisonai/bots/linear.py
This function starts the webhook listener. It contributes to the vulnerability's impact by binding to a public interface (`0.0.0.0`) even when no signing secret is provided, thus exposing the unauthenticated webhook endpoint to the network. It only logs a warning instead of refusing to start in an insecure configuration.
praisonai.cli.features.bots_cli.start_linear
src/praisonai/praisonai/cli/features/bots_cli.py
As stated in the vulnerability description, this CLI function, which is the command-line entry point for running the bot, facilitates the vulnerability. It allows the bot to be started without the required `LINEAR_WEBHOOK_SECRET`, leading to the insecure, unauthenticated webhook listener being exposed.
CVE-2026-56837: PraisonAI LinearBot processes unsigned webhooks when LINEAR_WEBHOOK_SECRET is missing
Basic Information
Basic Information
2f9677abb2ea68eab864ee8b6a828fd0141612e1
v4.6.57
v4.6.56
v4.5.50
Sampled tags where the LinearBot component was not present:
v4.5.49
v4.5.51
v4.6.9
v4.6.10
Suggested affected range: LinearBot-bearing releases with the fail-open
signature behavior, at least 4.5.50 and >= 4.6.56, <= 4.6.58. The
component appears non-contiguously in sampled tags, so maintainers should
confirm the exact packaged version history before publishing a final range.
Root Cause
LinearBot.__init__() accepts an empty signing secret and falls back to an
empty environment value:
self._signing_secret = signing_secret or os.environ.get("LINEAR_WEBHOOK_SECRET", "")
start() treats the missing secret as a warning instead of refusing to expose
the webhook listener:
if not self._signing_secret:
logger.warning("LINEAR_WEBHOOK_SECRET not set - webhook signatures will not be verified")
self._site = web.TCPSite(self._runner, "0.0.0.0", self._webhook_port)
_handle_webhook() only verifies the request if the secret is truthy:
if self._signing_secret:
signature = request.headers.get("Linear-Signature", "")
if not self._verify_signature(raw_body, signature):
return web.Response(status=401, text="Invalid signature")
With no secret configured, the code continues to JSON parsing, accepts a caller
supplied webhookTimestamp, reads the caller supplied Linear-Event header,
and schedules processing:
The CLI has the same fail-open posture: start_linear() loads
LINEAR_WEBHOOK_SECRET, prints a warning when it is missing, then reports a
public http://0.0.0.0:<port>/webhook endpoint with verification disabled.
Why This Is Not Intended Behavior
PraisonAI's Linear Bot documentation tells operators to set
LINEAR_WEBHOOK_SECRET, pass it to praisonai bot linear, copy the Linear
webhook signing secret, and use it for HMAC-SHA256 verification. The same page
says missing secrets disable signature verification, while its best-practices
section says webhook secrets ensure authenticity.
Linear's webhook documentation says receivers should ensure requests were sent
by Linear by verifying the Linear-Signature HMAC over the raw body, then
checking that webhookTimestamp is recent. The timestamp check alone is not an
authentication boundary because an attacker can supply a current timestamp in a
forged body.
The implementation itself also confirms the intended boundary: when a secret is
configured, missing and bad signatures are rejected before agent dispatch. The
bug is the missing-secret fail-open mode on a public webhook server, not the
signature algorithm.
If a PraisonAI operator starts LinearBot with a Linear token but omits
LINEAR_WEBHOOK_SECRET, any network caller that can reach the webhook endpoint
can spoof Linear webhook events and invoke the configured agent through the
Linear integration.
For the AgentSession event path, this lets the attacker supply issue title and
description content that becomes the agent input. Depending on the configured
agent and tools, this can cause unauthorized LLM/tool execution, consume paid
model quota, create or update Linear comments under the bot identity, and drive
the bot into workflows intended only for authenticated Linear events.
This report does not claim arbitrary code execution by default. The concrete
boundary crossed is unauthenticated remote agent invocation through a forged
Linear webhook.
Suggested Fix
Fail closed for public webhook listeners:
Refuse to start LinearBot when LINEAR_WEBHOOK_SECRET is missing, unless an
explicit development-only option such as
--insecure-skip-webhook-signature-verification is provided.
In _handle_webhook(), reject requests when no signing secret is configured
instead of silently skipping verification.
Preserve raw-body HMAC verification and constant-time comparison for the
configured-secret path.
Treat timestamp freshness as replay protection after signature validation,
not as a replacement for authentication.
Prefer loopback binding by default, or require an explicit host flag for
public binding.
Add regression tests:
no signing secret rejects startup or rejects webhook requests;
missing signature with a configured secret returns 401;
invalid signature with a configured secret returns 401;
valid HMAC with a configured secret returns success;
stale timestamp after valid HMAC returns 401;
the CLI does not advertise a public unauthenticated webhook by default.