fix(platform-tools): stop silently disabling TLS verification via CREWAI_FACTORY

Both platform API requests (fetch actions and execute action) set
`verify=os.environ.get("CREWAI_FACTORY","false").lower() != "true"`, so
CREWAI_FACTORY=true silently turned off TLS verification for requests that carry
the platform integration Bearer token — a MITM/token-leak footgun behind a
vaguely-named flag.

Centralize into `platform_tls_verify()`: verification is on by default and can
be disabled only via an explicit opt-out (CREWAI_PLATFORM_INSECURE_SKIP_TLS_VERIFY;
legacy CREWAI_FACTORY still honored for internal builds), and disabling now emits
a loud UserWarning pointing at REQUESTS_CA_BUNDLE for trusting a self-signed
endpoint securely. Only the exact value "true" disables (unchanged), so a stray
value can't drop verification.

Tests: default on; explicit + legacy flags disable with a loud warning; non-"true"
values stay verified. Existing factory-true tests still pass (behavior preserved).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBYGqJHC2TMC6fonFziuuh
This commit is contained in:
Joao Moura
2026-07-07 00:35:07 -07:00
parent 799ab0f548
commit 8afc97d1f5
4 changed files with 79 additions and 4 deletions

View File

@@ -1,7 +1,6 @@
"""Crewai Enterprise Tools."""
import json
import os
from typing import Any
from crewai.tools import BaseTool
@@ -12,6 +11,7 @@ import requests
from crewai_tools.tools.crewai_platform_tools.misc import (
get_platform_api_base_url,
get_platform_integration_token,
platform_tls_verify,
)
@@ -72,7 +72,7 @@ class CrewAIPlatformActionTool(BaseTool):
headers=headers,
json=payload,
timeout=60,
verify=os.environ.get("CREWAI_FACTORY", "false").lower() != "true",
verify=platform_tls_verify(),
)
data = response.json()

View File

@@ -1,7 +1,6 @@
"""CrewAI platform tool builder for fetching and creating action tools."""
import logging
import os
from types import TracebackType
from typing import Any
@@ -14,6 +13,7 @@ from crewai_tools.tools.crewai_platform_tools.crewai_platform_action_tool import
from crewai_tools.tools.crewai_platform_tools.misc import (
get_platform_api_base_url,
get_platform_integration_token,
platform_tls_verify,
)
@@ -49,7 +49,7 @@ class CrewaiPlatformToolBuilder:
headers=headers,
timeout=30,
params={"apps": ",".join(self._apps)},
verify=os.environ.get("CREWAI_FACTORY", "false").lower() != "true",
verify=platform_tls_verify(),
)
response.raise_for_status()
except Exception as e:

View File

@@ -1,4 +1,35 @@
import os
import warnings
def platform_tls_verify() -> bool:
"""TLS verification setting for CrewAI platform API requests.
Verification is ON by default and should stay on: these requests carry the
platform integration token, so an unverified connection exposes it to
man-in-the-middle attacks. It can be disabled only via an explicit opt-out
(``CREWAI_PLATFORM_INSECURE_SKIP_TLS_VERIFY``; the legacy ``CREWAI_FACTORY``
is still honored for internal builds), and doing so warns loudly. To trust a
self-signed endpoint *securely*, leave verification on and point requests at
the CA bundle via the standard ``REQUESTS_CA_BUNDLE`` environment variable.
"""
if (
os.getenv("CREWAI_PLATFORM_INSECURE_SKIP_TLS_VERIFY", "false").strip().lower()
== "true"
or os.getenv("CREWAI_FACTORY", "false").strip().lower() == "true"
):
warnings.warn(
"TLS certificate verification is DISABLED for CrewAI platform API "
"requests; the integration token is sent over an unverified connection "
"and is exposed to man-in-the-middle attacks. Unset "
"CREWAI_PLATFORM_INSECURE_SKIP_TLS_VERIFY (and the legacy CREWAI_FACTORY). "
"To trust a self-signed endpoint securely, keep verification on and set "
"REQUESTS_CA_BUNDLE to your CA instead.",
UserWarning,
stacklevel=2,
)
return False
return True
def get_platform_api_base_url() -> str:

View File

@@ -0,0 +1,44 @@
"""Tests for platform-API TLS verification behavior."""
import warnings
import pytest
from crewai_tools.tools.crewai_platform_tools.misc import platform_tls_verify
@pytest.fixture(autouse=True)
def _clear_tls_env(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("CREWAI_FACTORY", raising=False)
monkeypatch.delenv("CREWAI_PLATFORM_INSECURE_SKIP_TLS_VERIFY", raising=False)
def test_tls_verify_on_by_default() -> None:
with warnings.catch_warnings():
warnings.simplefilter("error") # any warning would fail the test
assert platform_tls_verify() is True
def test_tls_verify_disabled_by_explicit_flag_warns_loudly(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("CREWAI_PLATFORM_INSECURE_SKIP_TLS_VERIFY", "true")
with pytest.warns(UserWarning, match="TLS certificate verification is DISABLED"):
assert platform_tls_verify() is False
def test_tls_verify_disabled_by_legacy_factory_flag_warns_loudly(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Back-compat: CREWAI_FACTORY still disables, but now loudly instead of silently.
monkeypatch.setenv("CREWAI_FACTORY", "true")
with pytest.warns(UserWarning, match="TLS certificate verification is DISABLED"):
assert platform_tls_verify() is False
def test_tls_verify_only_exact_true_disables(monkeypatch: pytest.MonkeyPatch) -> None:
# A stray non-"true" value must not silently drop TLS verification.
monkeypatch.setenv("CREWAI_FACTORY", "1")
with warnings.catch_warnings():
warnings.simplefilter("error")
assert platform_tls_verify() is True