CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-128084

CVE-2026-128084: Integer Truncation and SQL Query Truncation in sqlite3-ruby

Alon Barad
Alon Barad
Software Engineer

Nov 13, 2026·11 min read·4 visits

Executive Summary (TL;DR)

An integer truncation flaw (CWE-681) in sqlite3-ruby prior to 2.10.2 causes strings of 4 GiB or larger to wrap their length when passed to the SQLite C API on 64-bit systems. This truncation can drop trailing SQL clauses, such as authorization filters, or truncate bound parameters. Upgrading to sqlite3 version 2.10.2 resolves the issue by replacing raw C casts with CRuby's RSTRING_LENINT macro, which raises a RangeError when string lengths exceed INT_MAX.

Prior to version 2.10.2, the sqlite3-ruby gem explicitly cast 64-bit Ruby string lengths (long) to 32-bit signed integers (int) when invoking the underlying SQLite C API. On 64-bit platforms, providing a string of 4 GiB (2^32 bytes) or larger causes integer truncation, instructing SQLite to process only a prefix of the string. This flaw affects SQL query preparation, bound parameter values, and return values from custom Ruby-defined SQL functions and aggregates, allowing trailing SQL clauses such as WHERE restrictions to be silently dropped under specific conditions.

Vulnerability Overview

The sqlite3 gem (sqlite3-ruby) provides native C bindings that allow Ruby applications to interface with the SQLite3 embedded relational database engine. It serves as the primary database adapter for SQLite within the Ruby on Rails ecosystem and standalone Ruby programs. The C extension layer translates Ruby objects—specifically String objects representing SQL statements, query parameters, and custom function outputs—into native C buffers passed directly to SQLite library functions.

In versions prior to 2.10.2, sqlite3-ruby contains an incorrect numeric type conversion vulnerability classified under CWE-681 (Incorrect Conversion between Numeric Types). When passing string lengths from the Ruby runtime to the SQLite C API, the extension performs an explicit narrowing type cast from a 64-bit signed long to a 32-bit signed int. This flaw exists across three distinct operational paths: preparing SQL statements via Database#prepare, binding string or binary blob parameters via Statement#bind_param, and returning string or blob results from custom SQL functions and aggregates via Database#create_function and Database#create_aggregate.

On 64-bit platforms (LP64 architecture), a Ruby string can exceed $2^{32}$ bytes (4 GiB) in length. When a string of 4 GiB or larger is passed through the affected methods, the narrowing cast truncates the 64-bit length to its lower 32 bits. Consequently, the SQLite C API receives a wrapped length value and reads only a small prefix of the buffer.

When this truncation occurs during SQL statement preparation, SQLite parses and executes only the prefix of the SQL query, silently discarding any trailing syntax. If an attacker can inject or control a multi-gigabyte string within a dynamically constructed query, trailing security controls—such as WHERE clauses enforcing tenant isolation or record ownership—can be dropped from the executed statement. Similarly, bound parameters or function return values validated by the Ruby application prior to database interaction will be stored or evaluated in a truncated state.

Root Cause Analysis

The root cause of CVE-2026-128084 lies in the architectural mismatch between how CRuby (MRI) and the legacy SQLite3 C API represent buffer lengths on 64-bit operating systems. In CRuby, the internal structure RString tracks the byte length of a string using a signed long integer, accessed via the RSTRING_LEN(str) macro. On 64-bit Linux, macOS, and BSD systems following the LP64 data model, long is 64 bits wide, permitting string lengths up to $2^{63} - 1$ bytes.

Conversely, the standard SQLite3 C API functions used by sqlite3-ruby accept buffer lengths as a standard C int. On LP64 platforms, int remains a 32-bit signed integer with a maximum positive value of INT_MAX ($2^{31} - 1$, or 2,147,483,647 bytes). The relevant SQLite C API signatures include:

  • sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail)
  • sqlite3_bind_text(sqlite3_stmt*, int, const char*, int nByte, void(*)(void*))
  • sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int nByte, void(*)(void*))
  • sqlite3_result_text(sqlite3_context*, const char*, int nByte, void(*)(void*))
  • sqlite3_result_blob(sqlite3_context*, const void*, int nByte, void(*)(void*))

To suppress compiler warnings regarding implicit conversion from long to int, the sqlite3-ruby C extension historically applied an explicit C type cast: (int)RSTRING_LEN(val). In C, casting a 64-bit integer to a 32-bit integer discards the high-order 32 bits, effectively computing the length modulo $2^{32}$ interpreted as a two's complement signed 32-bit integer.

The behavior of SQLite depends on the resulting 32-bit value after truncation:

  1. Lengths between $2^{31}$ (2 GiB) and $2^{32} - 1$ (4 GiB - 1 byte): The 32nd bit (bit 31) is set, causing the cast (int) value to become negative. In sqlite3_prepare_v2 and sqlite3_bind_text, a negative nByte parameter instructs SQLite to read the input buffer up to the first NUL (\0) terminator byte. In sqlite3_bind_blob, a negative length results in an error or undefined behavior.
  2. Lengths of $2^{32}$ (4 GiB) or greater: When the length reaches $2^{32} + K$ bytes (where $0 \le K < 2^{31}$), the lower 32 bits represent the positive integer $K$. SQLite receives $nByte = K$ and processes exclusively the first $K$ bytes of the buffer, ignoring the remaining 4 GiB of data.

Code Analysis

The vulnerability resided in two C source files within the native extension: ext/sqlite3/statement.c and ext/sqlite3/database.c. In ext/sqlite3/statement.c, both statement preparation and parameter binding performed raw casts on user-supplied Ruby strings.

During statement preparation in prepare(), the SQL string was passed to sqlite3_prepare_v2 with (int)RSTRING_LEN(sql). Similarly, bind_param() handled Encoding::UTF_16LE strings, SQLite3::Blob objects, and standard UTF-8/ASCII strings across three conditional branches, each casting RSTRING_LEN(value) to int.

// Vulnerable code in ext/sqlite3/statement.c (Prior to 2.10.2)
static VALUE
prepare(VALUE self, VALUE db, VALUE sql)
{
    // ...
    int status = sqlite3_prepare_v2(
        db_ctx->db,
        (const char *)StringValuePtr(sql),
        (int)RSTRING_LEN(sql), // VULNERABLE: 64-bit long truncated to 32-bit int
        &ctx->st,
        &tail
    );
    // ...
}

In ext/sqlite3/database.c, the helper function set_sqlite3_func_result() converts the Ruby return value of a custom SQL function or aggregate finalize method into a SQLite context result. Both the blob and text branches exhibited the same narrowing cast.

--- a/ext/sqlite3/database.c
+++ b/ext/sqlite3/database.c
@@ -529,14 +529,14 @@ set_sqlite3_func_result(sqlite3_context *ctx, VALUE result)
                 sqlite3_result_blob(
                     ctx,
                     (const void *)StringValuePtr(result),
-                    (int)RSTRING_LEN(result),
+                    RSTRING_LENINT(result),
                     SQLITE_TRANSIENT
                 );
             } else {
                 sqlite3_result_text(
                     ctx,
                     (const char *)StringValuePtr(result),
-                    (int)RSTRING_LEN(result),
+                    RSTRING_LENINT(result),
                     SQLITE_TRANSIENT
                 );
             }
--- a/ext/sqlite3/statement.c
+++ b/ext/sqlite3/statement.c
@@ -75,7 +75,7 @@ prepare(VALUE self, VALUE db, VALUE sql)
 #endif
                  db_ctx->db,
                  (const char *)StringValuePtr(sql),
-                 (int)RSTRING_LEN(sql),
+                 RSTRING_LENINT(sql),
                  &ctx->st,
                  &tail
              );
@@ -279,7 +279,7 @@ bind_param(VALUE self, VALUE key, VALUE value)
                              ctx->st,
                              index,
                              (const char *)StringValuePtr(value),
-                             (int)RSTRING_LEN(value),
+                             RSTRING_LENINT(value),
                              SQLITE_TRANSIENT
                          );
             } else {
@@ -290,7 +290,7 @@ bind_param(VALUE self, VALUE key, VALUE value)
                                  ctx->st,
                                  index,
                                  (const char *)StringValuePtr(value),
-                                 (int)RSTRING_LEN(value),
+                                 RSTRING_LENINT(value),
                                  SQLITE_TRANSIENT
                              );
                 } else {
@@ -301,7 +301,7 @@ bind_param(VALUE self, VALUE key, VALUE value)
                                  ctx->st,
                                  index,
                                  (const char *)StringValuePtr(value),
-                                 (int)RSTRING_LEN(value),
+                                 RSTRING_LENINT(value),
                                  SQLITE_TRANSIENT
                              );
                 }

In commit 41059aa3ccc2c0f910d6df4fccfe36b3c3c6c649, the maintainer replaced all six occurrences of (int)RSTRING_LEN(...) with CRuby's internal macro RSTRING_LENINT(...). In the Ruby C API, RSTRING_LENINT(str) calls rb_long2int(RSTRING_LEN(str)), which checks if the long value fits within the bounds of a standard signed int (INT_MIN to INT_MAX). If the string length exceeds INT_MAX ($2^{31} - 1$ bytes), rb_long2int immediately raises a Ruby RangeError exception and aborts execution before the SQLite C API is invoked.

> [!NOTE] > Fix Completeness Assessment: The patch completely resolves the vulnerability across all string and blob entry points in sqlite3-ruby. Because RSTRING_LENINT enforces an upper bound of INT_MAX ($2^{31} - 1$ bytes), it prevents both the 4 GiB positive wraparound ($\ge 2^{32}$) and the 2 GiB negative length interpretation ($2^{31}$ to $2^{32} - 1$). The regression test suite in test/test_statement.rb, test/test_database.rb, and test/test_integration_aggregate.rb verifies this boundary by allocating a $2^{31}$-byte string ("\0" * (2**31)) and confirming that RangeError is raised across all affected methods.

Exploitation

Exploiting CVE-2026-128084 to alter query logic requires overcoming significant architectural constraints, reflected in its High Attack Complexity (AC:H) CVSS metric. First, the target environment must run on a 64-bit platform with enough available virtual memory to allocate a minimum 4 GiB contiguous buffer in the Ruby process. Second, if the attack vector is over the network, all intermediary proxies (such as Nginx or Cloudflare) and the Ruby web server (such as Puma or Unicorn) must be configured without standard request body size limits, which typically cap payloads between 1 MiB and 100 MiB.

For SQL query truncation via Database#prepare, the application must construct a SQL statement by interpolating or concatenating an attacker-controlled string directly into the query text, rather than solely binding it as a parameter. Consider an application that places user-controlled data into a SQL comment, identifier, or literal prior to a restrictive WHERE clause:

# Example vulnerable query construction
# Attacker controls `comment_padding` with a length of ~4 GiB
sql = "SELECT * FROM documents WHERE title = 'report' /* #{comment_padding} */ AND tenant_id = #{current_tenant_id}"
db.execute(sql)

To drop the trailing AND tenant_id = ... clause, the attacker crafts comment_padding such that the total byte length of sql equals $2^{32} + K$, where $K$ is the exact byte offset immediately preceding /* #{comment_padding} */. Specifically, the prefix SELECT * FROM documents WHERE title = 'report' is 47 bytes long. If the total string length is $4,294,967,296 + 47 = 4,294,967,343$ bytes, (int)RSTRING_LEN(sql) evaluates to 47.

When sqlite3_prepare_v2 executes with nByte = 47, it reads only SELECT * FROM documents WHERE title = 'report' . Because this 47-byte prefix is syntactically valid SQL and does not contain an unclosed string literal or comment, SQLite compiles and executes the query without the AND tenant_id restriction, returning records across all tenants.

> [!IMPORTANT] > Bind Parameter Placeholder Constraint: If the truncated trailing portion of the SQL statement contains any bind parameter placeholders (such as ? or :tenant_id), exploitation fails safely. When sqlite3_prepare_v2 compiles the truncated prefix, the resulting sqlite3_stmt structure has fewer parameter slots than the original query string. When the Ruby wrapper subsequently iterates over the bind arguments and calls Statement#bind_param for the dropped placeholder index, SQLite returns SQLITE_RANGE, causing sqlite3-ruby to raise SQLite3::RangeException and abort execution before the statement runs.

A secondary exploitation path involves Statement#bind_param. If an application validates a 4 GiB string in Ruby (for example, verifying a cryptographic signature over a multi-gigabyte payload or checking a suffix) and then binds the string into an INSERT or UPDATE statement, SQLite will store only the $K$-byte prefix where $K = \text{length} \pmod{2^{32}}$. No public proof-of-concept exploits or active exploitation campaigns exist for this vulnerability.

Impact Assessment

CVE-2026-128084 is assigned a CVSS v3.1 base score of 4.8 (Medium) with the vector CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N. The Attack Vector is Network (AV:N) and Privileges Required is None (PR:N), accounting for scenarios where unauthenticated endpoints accept input passed to the database layer. However, the Attack Complexity is High (AC:H) because the target system must accept, buffer, and interpolate a payload of at least 4 GiB without triggering web server body limits or Out-Of-Memory (OOM) process termination.

The Confidentiality Impact is rated Low (C:L). In vulnerable query constructions where trailing WHERE filters are truncated and no bind placeholders exist in the dropped segment, an attacker can read unauthorized rows from the target table. Because the attacker cannot inject arbitrary new SQL commands (unless a separate SQL injection vulnerability already exists), data disclosure is restricted to the tables and columns already referenced in the prefix of the truncated SELECT query.

The Integrity Impact is also rated Low (I:L). If query truncation occurs on an UPDATE or DELETE statement—such as truncating DELETE FROM sessions /* 4GiB */ WHERE user_id = 5 to DELETE FROM sessions—the statement executes across all rows in the table rather than the intended subset. Furthermore, if an application relies on bind_param or custom SQL functions to store validated objects $\ge 4\text{ GiB}$, data integrity is compromised because only the wrapped prefix length is written to the database.

Availability Impact is scored as None (A:N) within the context of the integer truncation mechanic itself, as SQLite cleanly parses the truncated buffer without crashing or corrupting memory. While transmitting 4 GiB strings can cause memory exhaustion, that resource consumption is orthogonal to the CWE-681 type conversion flaw. According to CISA and FIRST.org data, this vulnerability is not listed in the Known Exploited Vulnerabilities (KEV) catalog.

Remediation

The definitive remediation for CVE-2026-128084 is to upgrade the sqlite3 gem to version 2.10.2 or later. Version 2.10.2 replaces the unsafe (int)RSTRING_LEN casts with RSTRING_LENINT, ensuring that any string exceeding INT_MAX (2,147,483,647 bytes) immediately raises a RangeError exception before interacting with the SQLite C API.

To update the dependency in a Ruby or Ruby on Rails project using Bundler, modify the Gemfile to require at least version 2.10.2:

gem "sqlite3", ">= 2.10.2"

Execute Bundler to update the locked gem version and verify the installed release:

bundle update sqlite3
bundle info sqlite3

For environments where immediate gem upgrades cannot be deployed, security teams should apply defense-in-depth controls to prevent 4 GiB strings from reaching the database adapter:

  1. Enforce Strict HTTP Request Size Limits: Configure reverse proxies and ingress controllers (such as Nginx client_max_body_size, Apache LimitRequestBody, or AWS ALB limits) as well as Rack middleware to reject unusually large HTTP request bodies.
  2. Eliminate Dynamic SQL Concatenation: Ensure all SQL queries use parameterized statements with bind placeholders (? or :name) rather than string interpolation. When queries are static, user input cannot alter the byte length of the SQL statement passed to Database#prepare.
  3. Application-Level Length Validation: Implement strict length validations on model attributes and input parameters (e.g., ActiveRecord validates :content, length: { maximum: 65535 }) prior to executing database queries.

Official Patches

sparklemotionGitHub Security Advisory GHSA-4fqr-96q8-xrfx
sparklemotionOfficial Fix Commit in sqlite3-ruby
sparklemotionsqlite3-ruby v2.10.2 Release Notes

Fix Analysis (1)

Technical Appendix

CVSS Score
4.8/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N

Affected Systems

sqlite3 RubyGem (sqlite3-ruby) on 64-bit platforms

Affected Versions Detail

Product
Affected Versions
Fixed Version
sqlite3 (sqlite3-ruby)
sparklemotion
< 2.10.22.10.2
AttributeDetail
Vulnerability IDCVE-2026-128084 / GHSA-4fqr-96q8-xrfx
Weakness ClassCWE-681: Incorrect Conversion between Numeric Types
CVSS v3.1 Score4.8 Medium (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N)
Affected Componentsqlite3 RubyGem (< 2.10.2) on 64-bit platforms
Patched Versionsqlite3 >= 2.10.2
ImpactSQL query truncation (dropping trailing clauses) and bound parameter truncation
Exploit StatusNo known public exploits; Not in CISA KEV

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
CWE-681
Incorrect Conversion between Numeric Types

The software converts a numeric value of a larger representation type (64-bit signed long) into a smaller representation type (32-bit signed int), causing the upper 32 bits to be discarded and resulting in integer truncation and length wraparound.

Vulnerability Timeline

Fix commit 41059aa3ccc2c0f910d6df4fccfe36b3c3c6c649 authored and merged via Pull Request #762
2026-10-26
sqlite3-ruby version 2.10.2 released to RubyGems
2026-10-26
GitHub Security Advisory GHSA-4fqr-96q8-xrfx published
2026-11-13
CVE-2026-128084 published by GitHub_M CNA
2026-11-13

References & Sources

  • [1]GitHub Security Advisory: GHSA-4fqr-96q8-xrfx
  • [2]GitHub Pull Request #762
  • [3]GitHub Fix Commit 41059aa3ccc2c0f910d6df4fccfe36b3c3c6c649
  • [4]Fix Commit Patch File
  • [5]sqlite3-ruby Release v2.10.2
  • [6]CVE-2026-128084 Record at CVE.org
  • [7]CISA Known Exploited Vulnerabilities Catalog

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•39 minutes ago•CVE-2026-128077
6.5

CVE-2026-128077: Authorization Bypass via Request Path Desynchronization in Nuxt and @nuxt/nitro-server

CVE-2026-128077 (GHSA-r99w-jh2x-3mg9) is an authorization bypass and interpretation conflict vulnerability affecting Nuxt and its underlying @nuxt/nitro-server package in versions 4.6.0 through 4.6.3. When an incoming HTTP request path begins with a double forward slash (e.g., //x/admin), the WHATWG URL parser within the Nuxt server-side page renderer interprets the initial segment as a network host/authority and resolves the remaining path (/admin) against the application's page router. Because Nitro server middleware, route-scoped serverHandlers, and Nitro routeRules evaluate the unparsed request path (//x/admin), path-based access controls, redirects, and security header policies configured for /admin fail to match and are bypassed.

Amit Schendel
Amit Schendel
2 views•10 min read
•about 3 hours ago•CVE-2026-128068
5.3

CVE-2026-128068: Regular Expression Denial of Service in rails-html-sanitizer

CVE-2026-128068 (GHSA-cpgf-5mjf-73p7) is a Regular Expression Denial of Service (ReDoS) vulnerability in the rails-html-sanitizer Ruby gem affecting versions 1.0.3 through 1.7.1. The flaw resides in the Rails::HTML::PermitScrubber#scrub_attribute method, which uses the multiline regular expression /^\s*[^#\s].*/m to enforce local-only URI fragment references on SVG elements. On Ruby runtimes prior to version 3.2 (CRuby <= 3.1 and JRuby), evaluating an attribute value composed of repeated newline and whitespace characters without a hash character triggers quadratic backtracking O(n^2). Applications are only vulnerable if their sanitization allowlist is explicitly configured to permit SVG reference elements such as <use> alongside href or xlink:href attributes.

Alon Barad
Alon Barad
4 views•9 min read
•about 4 hours ago•CVE-2026-126360
5.3

CVE-2026-126360: Regular Expression Denial of Service in OpenTelemetry Python opentelemetry-api

CVE-2026-126360 (GHSA-mf5v-w3w3-f6cg) is an algorithmic complexity vulnerability (Regular Expression Denial of Service) in the trace context propagation logic of the opentelemetry-api package within the OpenTelemetry Python ecosystem. In versions prior to 1.45.2, the TraceContextTextMapPropagator.extract() method utilizes a regular expression susceptible to quadratic-time backtracking when parsing malformed W3C traceparent headers containing long sequences of whitespace followed by an embedded newline character. In Python applications—particularly those using single-threaded asynchronous event loops—an unauthenticated remote attacker transmitting payloads over newline-preserving carriers can monopolize CPU execution and induce application-level Denial of Service.

Alon Barad
Alon Barad
4 views•10 min read
•about 5 hours ago•CVE-2026-126359
3.4

CVE-2026-126359: Cross-Site Scripting via Split Closing Style Tag in Hono CSS Helper

CVE-2026-126359 (GHSA-3gg2-335m-rxc6) is an improper input neutralization vulnerability (CWE-79) in the Hono web framework (`honojs/hono`). Versions from 3.12.0 prior to 4.13.16 evaluate and escape interpolated strings in the `hono/css` helper individually before concatenating template chunks and stripping CSS comments. When a `<` character and a `/style>` sequence are split across template boundaries, adjacent interpolations, or separated by a CSS comment, the sanitizer fails to escape the closing tag sequence. During Server-Side Rendering (SSR), the `<Style />` component outputs an unescaped `</style>` delimiter, permitting an attacker to break out of the CSS raw text context and execute arbitrary HTML and JavaScript in the victim's browser.

Alon Barad
Alon Barad
5 views•10 min read
•about 6 hours ago•CVE-2026-126358
5.3

CVE-2026-126358: Authorization Bypass via Wildcard Middleware Omission in Hono RegExpRouter

An incorrect authorization vulnerability (CWE-863) exists in the Hono web application framework (npm package `hono`) from version 4.1.4 prior to 4.13.16. Flaws in the registration-time coverage calculation and regular expression compilation within `RegExpRouter` cause wildcard middleware to be omitted under three specific routing conditions: parameter pattern equivalence mismatches, wildcard boundary collisions, and request paths containing percent-encoded line terminators. Because Hono's default `SmartRouter` selects `RegExpRouter` for most standard web applications, unauthenticated remote clients can bypass authentication, authorization, or security middleware registered on wildcard paths to directly access protected route handlers or the application's not-found handler.

Alon Barad
Alon Barad
5 views•13 min read
•about 7 hours ago•CVE-2026-126357
5.3

CVE-2026-126357: Authentication Bypass via Mount Path Transformation in Hono

CVE-2026-126357 (GHSA-4m76-xfj5-7975) is an authentication bypass vulnerability in the Hono web application framework prior to version 4.13.16. The flaw resides in the prefix-stripping and URL reconstruction logic of `app.mount()` and the `mount()` middleware. Two distinct path transformation errors—static route-pattern length slicing on dynamic prefixes and URL normalization of decoded pathnames—create a routing discrepancy between the outer Hono router and the mounted sub-application. An unauthenticated attacker can exploit these discrepancies to bypass outer authentication or authorization middleware and access protected inner routes directly.

Amit Schendel
Amit Schendel
5 views•9 min read