critical

CVE

CVE-2026-119145, CVE-2026-119144, CVE-2026-118769, CVE-2026-118765, CVE-2026-118766, CVE-2026-118767, CVE-2026-118768, CVE-2026-119286, CVE-2026-119283, CVE-2026-119284, CVE-2026-119197, CVE-2026-119196, CVE-2026-119195, CVE-2026-119194, CVE-2026-119203, CVE-2026-119204, CVE-2026-119205, CVE-2026-119206, CVE-2026-119207, CVE-2026-119208, CVE-2026-119209

CWE

CWE-22, CWE-1336, CWE-197, CWE-400, CWE-407, CWE-338

Affected Surface

  • Python / Bench `frappe` (`frappe/frappe`) versions `< 15.121.0` and `>= 16.0.0, < 16.34.0` (`CVE-2026-119145` / `GHSA-9pp9-wgp9-3p3j`, CVSS 9.9 Critical; and `CVE-2026-119144` / `GHSA-6pmg-vc6m-6wrr`, CVSS 8.8 High, fixed in `15.111.0` and `16.21.0`)
  • C++ `botan` (`randombit/botan`) versions `>= 2.1.0, < 3.13.0` (`CVE-2026-118769` / `GHSA-c68p-822g-jfvr`, CVSS v4.0 9.2 Critical) and `< 3.13.0` (`CVE-2026-118765` through `CVE-2026-118768`)
  • PyPI `graphql-core` versions `>= 3.3.0a3, < 3.3.1` (`CVE-2026-119286` / `GHSA-mpqx-q82w-9x46`, CVSS 7.5 High)
  • PyPI `joserfc` versions `< 1.7.5` (`CVE-2026-119283` / `GHSA-jpxq-7h8p-2q76`, CVSS 7.5 High) and `< 1.7.6` (`CVE-2026-119284` / `GHSA-9pg5-8c7x-5hfx`, CVSS 6.9 Medium)
  • PyPI `pyasn1` versions `< 0.6.5` (`CVE-2026-119194`, `CVE-2026-119195`, `CVE-2026-119196`, and `CVE-2026-119197`, all CVSS 7.5 High)
  • PyPI `pypdf` versions `< 6.21.4` (`CVE-2026-119203`, `CVE-2026-119204`, `CVE-2026-119205` fixed in `6.21.4`; `CVE-2026-119206` through `CVE-2026-119209` fixed in `6.21.3`, all CVSS 7.1 High)

TL;DR

Between 16:18 UTC and 22:17 UTC on 29 October 2026, NVD and the GitHub Advisory Database published 21 vulnerabilities across six Python and C++ packages:

  1. Frappe Framework < 15.121.0 and 16.x < 16.34.0 (CVE-2026-119145 / GHSA-9pp9-wgp9-3p3j, CVSS 9.9 Critical; and CVE-2026-119144 / GHSA-6pmg-vc6m-6wrr, CVSS 8.8 High): Frappe (the Python full-stack web framework backing ERPNext) patched an authenticated file-upload path traversal flaw (CVE-2026-119145) that wrote uploaded files outside the storage directory to achieve remote code execution, alongside a server-side template injection (SSTI) flaw (CVE-2026-119144, fixed in 15.111.0 and 16.21.0) where user-controlled message fields were rendered through Frappe’s Jinja template context without neutralization.
  2. Botan C++ cryptography library < 3.13.0 (CVE-2026-118769 / GHSA-c68p-822g-jfvr, CVSS v4.0 9.2 Critical, plus CVE-2026-118765 through CVE-2026-118768): In Botan’s C FFI (src/lib/ffi/ffi_block.cpp), botan_block_cipher_block_size() cast b.block_size() (size_t) directly to a signed 32-bit int without bounds checking (static_cast<int>(b.block_size())). If an application accepted an external cipher specification string in raw FFI ECB mode, an attacker could specify the variable-block-size Lion cipher with a huge block size that truncated to a small or negative int, causing the caller to under-allocate its output buffer and corrupt heap memory during botan_block_cipher_encrypt_blocks(). Botan 3.13.0 also fixes AutoSeeded_RNG::randomize() marking an unseeded RNG as seeded on a zero-length read (CVE-2026-118765, CVSS 7.3 High), OCSP redirect SSRF (CVE-2026-118766 and 118768), and Python FFI NUL-byte string truncation (CVE-2026-118767).
  3. graphql-core 3.3.0a3 to < 3.3.1 (CVE-2026-119286 / GHSA-mpqx-q82w-9x46, CVSS 7.5 High): When validate() aborted after reaching max_errors (default 100), it raised a module-level singleton validation_aborted_error = ValidationAbortedError(...). In Python, raising an existing exception instance appends the new call frame (and all local variables referenced by that frame, including the parsed AST DocumentNode, ValidationContext, and errors list) to the exception’s __traceback__ chain, retaining every aborted GraphQL request in memory for the lifetime of the worker process.
  4. joserfc < 1.7.6 (CVE-2026-119283, CVSS 7.5 High; CVE-2026-119284, CVSS 6.9 Medium): In joserfc < 1.7.5, jwe.decrypt_json() accepted a JWE General JSON Serialization recipients array without a count limit and invoked private-key decrypt_cek() for every recipient entry before consulting verify_all_recipients, allowing a single request to trigger thousands of RSA private-key decryptions (max_recipients = 16 added in 1.7.5). In < 1.7.6, compact JWS/JWE deserialization called value.split(b".") before checking validate_compact_value_size(value).
  5. pyasn1 < 0.6.5 (CVE-2026-119194 to CVE-2026-119197, all CVSS 7.5 High) and pypdf < 6.21.4 (CVE-2026-119203 to CVE-2026-119209, all CVSS 7.1 High): pyasn1 0.6.5 eliminates four quadratic BER/CER/DER decoding loops across binary REAL mantissas (119197), constructed BIT STRING fragments (119196), constructed OCTET STRING / ANY chunks (119195), and colliding TagSet hashes in schemaless containers (119194). pypdf 6.21.3 and 6.21.4 fix seven infinite loops and memory exhaustion paths in malformed PDF streams, page trees, and clone_document_from_reader.

What to do now: Upgrade frappe to >= 15.121.0 or >= 16.34.0, botan to >= 3.13.0, graphql-core to >= 3.3.1, joserfc to >= 1.7.6 (1.7.7 current), pyasn1 to >= 0.6.5, and pypdf to >= 6.21.4.

Affected packages and projects

EcosystemPackage / ProjectCVE / GHSASeverityAffected versionsFixed versionVulnerable component
Python / Benchfrappe (frappe/frappe)CVE-2026-119145 (GHSA-9pp9-wgp9-3p3j), CVE-2026-119144 (GHSA-6pmg-vc6m-6wrr)CVSS 9.9 Critical / 8.8 High< 15.121.0, >= 16.0.0, < 16.34.0 (119144 fixed in 15.111.0 / 16.21.0)15.121.0, 16.34.0File-upload destination validation (119145); message-field Jinja rendering (119144)
C++botan (randombit/botan)CVE-2026-118769 (GHSA-c68p-822g-jfvr), CVE-2026-118765..118768CVSS v4.0 9.2 Critical / 7.3 High>= 2.1.0, < 3.13.0 (118765: >= 3.7.0, < 3.13.0)3.13.0src/lib/ffi/ffi_block.cpp (botan_block_cipher_block_size), auto_rng.cpp, ocsp.cpp, botan3.py
PyPIgraphql-coreCVE-2026-119286 (GHSA-mpqx-q82w-9x46)CVSS 7.5 High>= 3.3.0a3, < 3.3.13.3.1src/graphql/validation/validate.py (validation_aborted_error singleton)
PyPIjoserfcCVE-2026-119283 (GHSA-jpxq-7h8p-2q76), CVE-2026-119284 (GHSA-9pg5-8c7x-5hfx)CVSS 7.5 High / 6.9 Medium< 1.7.5 (119283), < 1.7.6 (119284)1.7.6 (1.7.7 current)src/joserfc/_rfc7516/json.py, registry.py, compact.py
PyPIpyasn1CVE-2026-119194, CVE-2026-119195, CVE-2026-119196, CVE-2026-119197CVSS 7.5 High (all 4)< 0.6.5 (119194: >= 0.3.3, < 0.6.5)0.6.5pyasn1/codec/ber/decoder.py (RealPayloadDecoder, BitStringPayloadDecoder, OctetStringPayloadDecoder, _decodeComponentsSchemaless)
PyPIpypdfCVE-2026-119203 through CVE-2026-119209 (7 advisories)CVSS 7.1 High (all 7)< 6.21.4 (119206..119209: < 6.21.3)6.21.4PDF stream decoding, page label tree traversal, strict mode object loading, and clone_document_from_reader

Technical analysis

1. graphql-core 3.3.1 (CVE-2026-119286): how a module-level exception singleton leaked every aborted GraphQL AST in Python

In GraphQL.js (JavaScript), creating a single module-level validationAbortedError object and throwing it whenever a query exceeds maxErrors (100) is harmless because JavaScript Error objects capture their stack trace once at construction time (new Error(...)).

When graphql-core (3.3.0a3) ported that pattern verbatim to Python in src/graphql/validation/validate.py, it defined a module-level instance validation_aborted_error = ValidationAbortedError(...) and ran raise validation_aborted_error inside on_error():

--- a/src/graphql/validation/validate.py
+++ b/src/graphql/validation/validate.py
@@ -30,11 +30,6 @@ class ValidationAbortedError(GraphQLError):
     """Error when a validation has been aborted (error limit reached)."""
-
-
-validation_aborted_error = ValidationAbortedError(
-    "Too many validation errors, error limit reached. Validation aborted."
-)
-
@@ -113,7 +108,10 @@ def validate(
     def on_error(error: GraphQLError) -> None:
         if len(errors) >= max_errors:
-            raise validation_aborted_error
+            # Unlike GraphQL.js, create a new error each time, because raising a
+            # shared error would add the frames of every validation to its traceback.
+            msg = "Too many validation errors, error limit reached. Validation aborted."
+            raise ValidationAbortedError(msg)

In CPython, raise existing_exc attaches a brand-new traceback frame (tb_next) onto existing_exc.__traceback__ without discarding the traceback frames from earlier raise statements. Every frame in that growing linked list holds strong references to its f_locals, including the ValidationContext, the full parsed DocumentNode AST, and the errors list for that request. Sending repeated unauthenticated GraphQL queries with 101 unknown fields pinned every aborted request’s AST and context in memory forever until the Python worker process was killed by the OOM killer. Commit 3c319fd9f4 (v3.3.1) instantiates a fresh ValidationAbortedError(msg) on each abort.

2. joserfc 1.7.5 / 1.7.6 (CVE-2026-119283 and CVE-2026-119284) and pyasn1 0.6.5 (CVE-2026-119194 to CVE-2026-119197)

In joserfc (< 1.7.5, CVE-2026-119283 / GHSA-jpxq-7h8p-2q76), extract_general_json() and _perform_decrypt() in src/joserfc/_rfc7516/ accepted a JWE General JSON Serialization object ({"protected": "...", "recipients": [...]}) with an arbitrary number of entries in recipients and ran decrypt_recipient(alg, enc, recipient, tag) on every single entry before checking verify_all_recipients. An attacker could submit one JWE containing thousands of recipient blocks targeting an RSA-OAEP or RSA1_5 endpoint and force the server to execute thousands of expensive RSA private-key operations in a single HTTP request. Commit d6dc8df6c7 (1.7.5) enforces JWERegistry.max_recipients = 16 via registry.validate_recipient_count(recipients), and commit 397e04f4bb (1.7.6, CVE-2026-119284) validates the total byte length of compact JWS and JWE strings before calling value.split(b".").

In pyasn1 (< 0.6.5, PR #132), four separate ASN.1 BER/CER/DER decoders accumulated multi-kilobyte inputs quadratically:

  • Binary REAL mantissa slicing (CVE-2026-119197, commit ce3ec33907): RealPayloadDecoder.valueDecoder built the integer mantissa p one byte at a time inside while chunk: p <<= 8; p |= chunk[0]; chunk = chunk[1:], copying the remaining bytes slice and shifting a growing Python bignum on every byte. 0.6.5 replaces the loop with a single C-level call: p = int.from_bytes(bytes(chunk), 'big').
  • Constructed BIT STRING and OCTET STRING / ANY concatenation (CVE-2026-119196 and CVE-2026-119195): BitStringPayloadDecoder (73ba7b043c) and OctetStringPayloadDecoder / AnyPayloadDecoder (4ce00797c5) repeatedly prepended or concatenated growing bit/byte objects across thousands of 1-byte constructed fragments instead of collecting chunks into a list and joining once.
  • Schemaless TagSet hash collisions (CVE-2026-119194, commit a36e3a3d06): _decodeComponentsSchemaless inserted every component’s TagSet into a set even though it only needed to know whether more than one distinct TagSet appeared, allowing colliding long tag IDs to trigger $O(n^2)$ set insertions.

3. Botan 3.13.0 (CVE-2026-118769 and CVE-2026-118765) and Frappe 15.121.0 / 16.34.0 (CVE-2026-119145 and CVE-2026-119144)

In Botan 2.1.0 through 3.12.x, the C FFI function botan_block_cipher_block_size(botan_block_cipher_t bc) in src/lib/ffi/ffi_block.cpp returned a signed int so negative values could represent FFI error codes, but cast the cipher’s size_t block size directly without checking std::in_range<int>(bs):

--- a/src/lib/ffi/ffi_block.cpp
+++ b/src/lib/ffi/ffi_block.cpp
@@ -58,7 +59,18 @@ int botan_block_cipher_set_key(botan_block_cipher_t bc, const uint8_t key[], siz
 int botan_block_cipher_block_size(botan_block_cipher_t bc) {
-   return BOTAN_FFI_VISIT(bc, [](const auto& b) { return static_cast<int>(b.block_size()); });
+   return BOTAN_FFI_VISIT(bc, [](const auto& b) -> int {
+      const size_t bs = b.block_size();
+      if(bs == 0) {
+         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
+      }
+
+      if(std::in_range<int>(bs)) {
+         return static_cast<int>(bs);
+      } else {
+         return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
+      }
+   });
 }

Because Botan supports the parameterized Lion(hash, stream_cipher, block_size) block cipher constructor when initializing a cipher by name string, an attacker who controlled the cipher spec string passed to botan_block_cipher_init(&bc, cipher_name) could request a size_t block size that wrapped int on truncation (CVE-2026-118769, CVSS v4.0 9.2 Critical). Separately, in AutoSeeded_RNG (CVE-2026-118765, commit a7389222ac), calling clear() followed by randomize(out, 0) set the internal seeded flag to true before checking whether any entropy had been polled.

In Frappe Framework, CVE-2026-119145 (GHSA-9pp9-wgp9-3p3j, CVSS 9.9 Critical) allowed authenticated users to traverse out of the site’s public/files or private/files upload directory when saving uploaded files to disk, writing executable files into the application tree. CVE-2026-119144 (GHSA-6pmg-vc6m-6wrr, CVSS 8.8 High) allowed authenticated users with standard operational roles to inject server-side template expressions into message fields that Frappe evaluated in an unsandboxed server context.

Detection and remediation

# 1. Check Python environments and lockfiles for graphql-core, joserfc, pyasn1, pypdf, and frappe
pip show graphql-core joserfc pyasn1 pypdf frappe 2>/dev/null || true

rg -n '"?(graphql-core|joserfc|pyasn1|pypdf|frappe)"?\s*([~=!<>]=|:)' \
  requirements*.txt pyproject.toml uv.lock poetry.lock Pipfile.lock apps.txt 2>/dev/null

# 2. Check installed Botan shared library and CLI version on Linux / macOS build hosts
botan version 2>/dev/null || pkg-config --modversion botan-3 2>/dev/null || true
  1. Upgrade Frappe Framework deployments (bench update --patch) to >= 15.121.0 on the v15 LTS branch or >= 16.34.0 on v16.
  2. Upgrade Botan to >= 3.13.0, especially in any C, Python, Rust, or Go FFI consumer that passes caller-supplied cipher strings to botan_block_cipher_init or calls botan_ocsp_verify.
  3. Upgrade Python libraries:
    • graphql-core (and downstream Strawberry, Ariadne, or Graphene servers) to >= 3.3.1.
    • joserfc to >= 1.7.6 (1.7.7 current).
    • pyasn1 to >= 0.6.5.
    • pypdf to >= 6.21.4.

Timeline

  • 2026-08-06 to 2026-09-24: Botan merges commit c6089ab7fa (CVE-2026-118769), joserfc releases 1.7.5 (d6dc8df6c7) and 1.7.6 (397e04f4bb), and pyasn1 merges PR #132 (v0.6.5).
  • 2026-10-15: Frappe publishes GHSA-9pp9-wgp9-3p3j (15.121.0 / 16.34.0) and GHSA-6pmg-vc6m-6wrr (15.111.0 / 16.21.0).
  • 2026-10-25 to 2026-10-28: graphql-core releases v3.3.1 (3c319fd9f4) and pypdf releases v6.21.3 and v6.21.4 (06593f9937).
  • 2026-10-29 (16:18 to 22:17 UTC): NVD and the GitHub Advisory Database publish CVE-2026-119144, CVE-2026-119145, CVE-2026-118765 through CVE-2026-118769, CVE-2026-119194 through CVE-2026-119197, CVE-2026-119203 through CVE-2026-119209, CVE-2026-119283, CVE-2026-119284, and CVE-2026-119286.

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