high

CVE

CVE-2026-61438, CVE-2026-119120, CVE-2026-119119, CVE-2026-119118, CVE-2026-119121, CVE-2026-119122, CVE-2026-62264, CVE-2026-119052

CWE

CWE-94, CWE-502, CWE-306, CWE-295, CWE-918, CWE-770, CWE-755

Affected Surface

  • PyPI `praisonai` (`< 4.6.78`) and `praisonaiagents` (`< 1.6.78`) (`CVE-2026-61438` / `GHSA-26mh-57q7-jfvr`, CVSS 7.3 High)
  • PyPI and Docker `crawl4ai` (`unclecode/crawl4ai`) versions `< 0.9.5` (`CVE-2026-119118`, `CVE-2026-119119`, `CVE-2026-119120`, `CVE-2026-119121`, `CVE-2026-119122`, and `GHSA-j843-8p47-rhxp`)
  • npm `@sylphx/pdf-reader-mcp` versions `>= 2.2.0, < 3.0.1` when run with `MCP_TRANSPORT=http` (`CVE-2026-62264` / `GHSA-q344-5v34-gm84`, CVSS 7.5 High)
  • C++ `ggml-org/llama.cpp` (`ggml-rpc` server) releases `b2877` before `b11935` (`CVE-2026-119052`, CVSS 7.5 High / v4.0 8.7 High)

TL;DR

On 29 October 2026, NVD and the GitHub Advisory Database published nine security advisories across four AI agent, web-crawling, MCP server, and LLM inference tools:

  1. PraisonAI < 4.6.78 (CVE-2026-61438 / GHSA-26mh-57q7-jfvr, CVSS 7.3 High): JobWorkflowExecutor._exec_inline_python() attempted to sandbox inline script: steps in YAML workflows by walking the parsed Python AST and stripping __import__, eval, exec, and open from __builtins__. However, the AST node allowlist permitted ast.Import and ast.ImportFrom (which compile to bytecode IMPORT_NAME instructions), and the module allowlist explicitly included 'os'. Any workflow script: step containing import os; os.system('...') passed every AST check and executed arbitrary OS commands. Fixed in praisonai 4.6.78 and praisonaiagents 1.6.78 (commit 393de39408), which also closes path traversal in read_file / run_skill_script, unauthenticated 0.0.0.0 binding in AgentOS and the Jobs/MCP servers, and find -exec sandbox bypasses.
  2. Crawl4AI < 0.9.5 (6 coordinated-disclosure advisories in the Docker API server and library):
    • CVE-2026-119120 (GHSA-f856-7xp9-w3cc, CVSS 7.5 High): The untrusted Docker API request filter (UNTRUSTED_FORBIDDEN_FIELDS) did not block CosineStrategy.model_name, allowing an API caller to point from_pretrained() at an attacker-controlled Hugging Face repository (triggering pickle RCE on PyTorch < 2.6 or disk/memory exhaustion).
    • CVE-2026-119119 (GHSA-rhjm-7hj7-75fx, CVSS 7.5 High / v4.0 8.7 High) and GHSA-j843-8p47-rhxp: _clamp_untrusted() did not cap delay_before_return_html, mean_delay, or max_range (allowing a caller to hold Chromium pages open indefinitely), and allowed caller-supplied regexes in RegexExtractionStrategy.custom and RegexChunking.patterns that froze the Python GIL.
    • CVE-2026-119118 (GHSA-j2qh-f73r-mg3q, CVSS v4.0 8.3 High), CVE-2026-119121 (GHSA-qj46-wp7m-rgj8), and CVE-2026-119122 (GHSA-w4xc-px5p-c32w): Chromium was always launched with --ignore-certificate-errors and --no-sandbox; css_selector and wait_for allowed page JavaScript execution even when js_code was blocked; and CacheValidator.validate() (check_cache_freshness) bypassed the Docker egress proxy.
  3. @sylphx/pdf-reader-mcp 2.2.0 to < 3.0.1 (CVE-2026-62264 / GHSA-q344-5v34-gm84, CVSS 7.5 High): When started with MCP_TRANSPORT=http, the server bound 0.0.0.0:8080 by default, read MCP_API_KEY, and logged API key authentication enabled (X-API-Key header), but never checked the X-API-Key header on incoming /mcp requests. Any network client could call every PDF tool and read local PDF files on the host without authentication. Fixed in 3.0.1 (8.5.5 current).
  4. llama.cpp b2877 to < b11935 (CVE-2026-119052, CVSS 7.5 High / v4.0 8.7 High): In ggml/src/ggml-rpc/ggml-rpc.cpp, recv_msg() read a 64-bit size from the socket after the HELLO handshake and called input.resize(size) inside a try block that caught std::bad_alloc but not std::length_error. Sending a size larger than std::vector::max_size() threw an uncaught std::length_error and aborted the rpc-server process.

What to do now: Upgrade praisonai to >= 4.6.78 (4.7.21 current), crawl4ai (and the unclecode/crawl4ai Docker image) to >= 0.9.5, @sylphx/pdf-reader-mcp to >= 3.0.1 (8.5.5 current), and llama.cpp to >= b11935.

Affected packages and projects

EcosystemPackage / ProjectCVE / GHSASeverityAffected versionsFixed versionVulnerable component
PyPIpraisonai, praisonaiagentsCVE-2026-61438 (GHSA-26mh-57q7-jfvr)CVSS 7.3 Highpraisonai < 4.6.78, praisonaiagents < 1.6.784.6.78 / 1.6.78 (4.7.21 / 1.7.19 current)JobWorkflowExecutor._exec_inline_python(), sandbox_executor.py, path_safety.py, http_stream.py
PyPI / Dockercrawl4ai (unclecode/crawl4ai)CVE-2026-119120 (GHSA-f856-7xp9-w3cc), CVE-2026-119119 (GHSA-rhjm-7hj7-75fx), CVE-2026-119118 (GHSA-j2qh-f73r-mg3q), CVE-2026-119121 (GHSA-qj46-wp7m-rgj8), CVE-2026-119122 (GHSA-w4xc-px5p-c32w), GHSA-j843-8p47-rhxpCVSS 7.5 High (v4.0 up to 8.7 High)< 0.9.5 (119120 and 119121 affect >= 0.9.0, < 0.9.5)0.9.5crawl4ai/async_configs.py, crawl4ai/cache_validator.py, deploy/docker/utils.py
npm@sylphx/pdf-reader-mcpCVE-2026-62264 (GHSA-q344-5v34-gm84)CVSS 7.5 High>= 2.2.0, < 3.0.1 (MCP_TRANSPORT=http)3.0.1 (8.5.5 current)src/index.ts, src/mcp.ts (isApiKeyValid)
C++ / GitHubggml-org/llama.cppCVE-2026-119052CVSS 7.5 High (v4.0 8.7 High)b2877 to < b11935b11935ggml/src/ggml-rpc/ggml-rpc.cpp (recv_msg)

Technical analysis

1. PraisonAI (CVE-2026-61438 / GHSA-26mh-57q7-jfvr): import os inside the AST workflow sandbox

When PraisonAI executes a job workflow (praisonai workflow run <file>.yaml with PRAISONAI_ALLOW_JOB_WORKFLOWS=true), JobWorkflowExecutor._exec_inline_python() parses inline script: steps with ast.parse(code, mode="exec") and walks the tree to block unsafe nodes before calling exec() with a restricted __builtins__ dictionary (which omits __import__, eval, exec, and open).

The sandbox failed for two reasons occurring together:

# Vulnerable AST validation in JobWorkflowExecutor._exec_inline_python (< 4.6.78)
allowed_modules = {'os', 'datetime', 'time', 'json', 'math', 're', 'collections', 'itertools'}
if isinstance(node, ast.Import):
    for alias in node.names:
        if alias.name.split('.')[0] not in allowed_modules:
            return {"ok": False, "error": f"Import of '{alias.name}' not allowed..."}

First, allowed_modules included 'os' so workflow scripts could access os.path or os.getcwd(), without restricting attribute access on the imported os module (os.system, os.popen, os.execvp). Second, in Python, when ast.Import nodes are allowed and compiled with compile(tree, ..., "exec"), the CPython VM executes the IMPORT_NAME bytecode instruction, which resolves __import__ from the interpreter’s builtins fallback or sys.modules['os'] if os is already loaded. Consequently, a two-line workflow step:

steps:
  - script: |
      import os
      result = os.system("id")

passed every AST check and ran arbitrary OS commands.

In v4.6.78 (commit 393de39408 and commit f47a4f97ec), PraisonAI also shipped a broader security hardening pass across praisonai, praisonaiagents, and praisonai-ts:

  • CommandValidator.validate() in sandbox_executor.py now blocks find ... -exec (if cmd_name == "find" and "-exec" in command:).
  • resolve_within_root() in praisonaiagents/tools/path_safety.py confines read_file(), run_skill_script(), _save_output_to_file(), and .praisoninclude paths to the workspace root using os.path.realpath() and os.path.commonpath().
  • PraisonAI MCP http_stream.py, jobs/server.py, and TypeScript AgentOSConfig now default to 127.0.0.1 instead of 0.0.0.0 and refuse to bind non-loopback addresses unless an API key (api_key, PRAISONAI_JOBS_API_KEY, or PRAISONAI_AGENTOS_API_KEY) is configured.

2. Crawl4AI 0.9.5 (CVE-2026-119118 to CVE-2026-119122 and GHSA-j843-8p47-rhxp): untrusted Docker API request deserialization

Crawl4AI’s Docker server (unclecode/crawl4ai, port 11235) accepts serialized CrawlerRunConfig objects on /crawl and /crawl/stream and deserializes them with CrawlerRunConfig.load(raw, provenance=Provenance.UNTRUSTED) in crawl4ai/async_configs.py. Version 0.9.5 (PR #2368, commit 6192d5e5b5) closes six gaps in that untrusted boundary:

--- a/crawl4ai/async_configs.py
+++ b/crawl4ai/async_configs.py
@@ -222,6 +231,21 @@ UNTRUSTED_FORBIDDEN_FIELDS = {
     "PDFContentScrapingStrategy": {
         "image_save_dir", "save_images_locally",
     },
+    # model_name is a HuggingFace repo id handed to from_pretrained: the caller
+    # would pick the weights the server downloads and unpickles.
+    "CosineStrategy": {"model_name"},
+    # Caller-supplied regexes. Python's re holds the GIL while it matches, so
+    # one catastrophic-backtracking pattern freezes the whole server.
+    "RegexExtractionStrategy": {"custom"},
+    "RegexChunking": {"patterns"},
 }
  • Arbitrary Hugging Face model loading (CVE-2026-119120 / GHSA-f856-7xp9-w3cc): Previously, CosineStrategy was on UNTRUSTED_ALLOWED_TYPES without field restrictions, so a caller could pass {"type": "CosineStrategy", "params": {"model_name": "attacker/malicious-repo"}}. The server passed model_name straight to transformers / sentence-transformers from_pretrained(), downloading attacker-chosen weights (and executing pickle payloads on PyTorch < 2.6).
  • Unbounded crawl delays and GIL-blocking regexes (CVE-2026-119119 / GHSA-rhjm-7hj7-75fx and GHSA-j843-8p47-rhxp): _clamp_untrusted() now caps delay_before_return_html, mean_delay, and max_range at 60 seconds and rejects custom regex patterns in RegexExtractionStrategy, RegexChunking, and extraction schemas.
  • Page JS injection (CVE-2026-119121), CacheValidator SSRF (CVE-2026-119122), and Chromium TLS verification (CVE-2026-119118): css_selector is now passed to Playwright’s page.evaluate() as a serialized argument rather than interpolated into JS source; wait_for="js:..." is rejected for Provenance.UNTRUSTED; CacheValidator.validate() routes its HEAD and GET freshness probes through the pinning egress proxy (egress_broker.py, which also now fails closed with EgressBlocked instead of except Exception: pass); and --ignore-certificate-errors / --allow-insecure-localhost were removed from the default Chromium launch flags in deploy/docker/utils.py.

3. @sylphx/pdf-reader-mcp 3.0.1 (CVE-2026-62264) and llama.cpp b11935 (CVE-2026-119052)

In @sylphx/pdf-reader-mcp (>= 2.2.0, < 3.0.1), src/index.ts read const apiKey = process.env['MCP_API_KEY'] and logged API key authentication enabled (X-API-Key header) when starting the HTTP transport (MCP_TRANSPORT=http), but never passed apiKey to startHttpServer() in src/mcp.ts. At the same time, MCP_HTTP_HOST defaulted to '0.0.0.0'. Every /mcp request was accepted without checking X-API-Key. Commit 4c35f72862 (v3.0.1) changes the default MCP_HTTP_HOST to '127.0.0.1' and validates req.headers['x-api-key'] on /mcp using SHA-256 digests and crypto.timingSafeEqual():

// SylphxAI/anymd commit 4c35f72862: src/mcp.ts
const isApiKeyValid = (configuredKey: string, presented: string | string[] | undefined): boolean => {
  const provided = Array.isArray(presented) ? presented[0] : presented;
  if (typeof provided !== 'string' || provided.length === 0) return false;
  const expected = createHash('sha256').update(configuredKey).digest();
  const actual = createHash('sha256').update(provided).digest();
  return timingSafeEqual(expected, actual);
};

In llama.cpp (b2877 before b11935), ggml/src/ggml-rpc/ggml-rpc.cpp implements the distributed inference RPC server. After the initial HELLO packet, recv_msg() reads a 64-bit uint64_t size from the TCP socket and resizes a std::vector<uint8_t>:

--- a/ggml/src/ggml-rpc/ggml-rpc.cpp
+++ b/ggml/src/ggml-rpc/ggml-rpc.cpp
@@ -348,7 +348,10 @@ static bool recv_msg(socket_ptr sock, std::vector<uint8_t> & input) {
     }
     try {
         input.resize(size);
-    } catch (const std::bad_alloc & e) {
+    } catch (const std::length_error &) {
+        LOG_ERROR("Failed to allocate input buffer of size %" PRIu64 "\n", size);
+        return false;
+    } catch (const std::bad_alloc &) {
         LOG_ERROR("Failed to allocate input buffer of size %" PRIu64 "\n", size);
         return false;
     }

In C++, std::vector::resize(size) checks if (size > max_size()) before calling the allocator and throws std::length_error (which inherits from std::logic_error, not std::bad_alloc). Sending an 8-byte length prefix with size = 0xffffffffffffffff bypassed the catch (const std::bad_alloc &) handler and terminated rpc-server via std::terminate(). Commit 7ecb57f7ad (PR #31204, b11935) catches std::length_error alongside std::bad_alloc.

Detection and remediation

# 1. Check Python environments and lockfiles for praisonai and crawl4ai
pip show praisonai praisonaiagents crawl4ai 2>/dev/null || true
rg -n '"?(praisonai|praisonaiagents|crawl4ai)"?\s*([~=!<>]=|:)' \
  requirements*.txt pyproject.toml uv.lock poetry.lock Pipfile.lock 2>/dev/null

# 2. Check Docker Compose / Kubernetes manifests for unclecode/crawl4ai or @sylphx/pdf-reader-mcp
rg -n 'unclecode/crawl4ai|@sylphx/pdf-reader-mcp|MCP_TRANSPORT' \
  docker-compose*.yml Dockerfile* *.yaml *.json 2>/dev/null

# 3. Check running llama.cpp rpc-server processes
ps aux | grep '[r]pc-server' || true
  1. Upgrade praisonai to >= 4.6.78 and praisonaiagents to >= 1.6.78 (4.7.21 and 1.7.19 are current on PyPI). Do not set PRAISONAI_ALLOW_JOB_WORKFLOWS=true or PRAISONAI_ALLOW_LOCAL_TOOLS=true when processing untrusted YAML workflows.
  2. Upgrade crawl4ai to >= 0.9.5 and pull the updated unclecode/crawl4ai:0.9.5 container image. Ensure CRAWL4AI_API_TOKEN is set on every network-reachable Docker deployment.
  3. Upgrade @sylphx/pdf-reader-mcp to >= 3.0.1 (8.5.5 is current), or set MCP_HTTP_HOST=127.0.0.1 if you cannot upgrade immediately.
  4. Upgrade llama.cpp to >= b11935 and never expose ggml-rpc (rpc-server) ports outside a trusted cluster network.

Timeline

  • 2026-06-25: PraisonAI releases v4.6.78 (393de39408) and @sylphx/pdf-reader-mcp releases v3.0.1 (4c35f72862).
  • 2026-09-29 to 2026-10-01: Researchers report the six Crawl4AI Docker server and CacheValidator vulnerabilities to unclecode.
  • 2026-10-14: Crawl4AI tags and publishes v0.9.5 on PyPI and merges PR #2368 (6192d5e5b5) to main on 20 October.
  • 2026-10-29 (14:17 to 16:34 UTC): NVD and the GitHub Advisory Database publish CVE-2026-61438 (GHSA-26mh-57q7-jfvr), CVE-2026-119118 through CVE-2026-119122, CVE-2026-62264 (GHSA-q344-5v34-gm84), and CVE-2026-119052.

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References