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-22041

Type-Casting Disasters: How loggingredactor Broke Python's Lazy Logging

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 24, 2026·6 min read·17 visits

Executive Summary (TL;DR)

loggingredactor < 0.0.6 converts ALL log arguments to strings to check for secrets. This breaks `logger.info('%d', 123)` because `%d` expects an int, not the string '123'. Result: App crashes and lost logs.

In the world of Python logging, laziness is a virtue—until a library like loggingredactor comes along and forces everyone to work too hard. CVE-2026-22041 (GHSA-rvjx-cfjh-5mc9) exposes a fundamental misunderstanding of Python's logging architecture within the loggingredactor library (versions < 0.0.6). By aggressively casting all non-collection types to strings in an attempt to sanitize data, the library broke the contract of lazy string formatting. This resulted in application crashes via `TypeError` whenever a developer tried to log a number using numeric format specifiers. While not a remote code execution flaw, it represents a significant 'Denial of Observability' and stability risk, effectively blinding operations teams when they need logs the most.

The Hook: When Cleanliness Becomes Destructive

We all know the drill. You're building a Python application, and your security team (or your paranoia) whispers in your ear: "Don't log PII. Don't log API keys." So, you grab a library like loggingredactor. Its job is simple: intercept log records, scrub the nasty bits using regex or dictionary keys, and pass the clean data along. It's the janitor of your application logs.

But here is the problem with overzealous janitors: sometimes they throw out the furniture while trying to sweep the floor. loggingredactor made a critical architectural error. It assumed that everything passed to a logger is destined to be a string immediately. It forgot that Python logging is designed to be lazy.

When you call logger.info("User ID: %d", user_id), Python doesn't format that string right away. It passes the format string and the integer user_id to the logging handlers. This optimization saves CPU cycles if the log level is set to WARNING, meaning the INFO log never actually gets formatted. loggingredactor inserted itself into this pipeline and decided to forcefully convert user_id to a string ("123") before the formatter got to it. When the formatter finally woke up and saw %d paired with "123", it threw a tantrum—specifically, a TypeError.

The Flaw: A Case of Aggressive Typing

The root cause here is a classic case of "Hammer, meet Nail." The developers of loggingredactor needed to run regex substitutions on log data. Regex works on strings. Therefore, the logic went: "Make everything a string."

In versions prior to 0.0.6, the RedactingFilter.redact method contained a recursive loop. If it encountered a dictionary, it dove in. If it encountered a list, it iterated. But for everything else—integers, floats, booleans, custom objects—it executed this ruthless line of code:

content_copy = isinstance(content_copy, str) and content_copy or str(content_copy)

This looks like a clever one-liner, but it's a landmine. It forces a type coercion. If you passed 123 (int), it became '123' (str). If you passed True (bool), it became 'True' (str).

The crash happens downstream in the standard library's logging module. When the LogRecord is processed, the formatting operator % is applied. If the message template is "Value: %d" and the argument has been mutated into a string, Python raises TypeError: %d format: a number is required, not str.

This essentially turns your logging infrastructure into a minefield. Any developer using type-specific formatters (%d, %f, %x) will inadvertently crash the application thread just by trying to write a log message.

The Code: Autopsy of a Bad Decision

Let's look at the "before" and "after" code to understand exactly how this was remediated. This diff is from the critical patch in version 0.0.6.

The Vulnerable Code (v0.0.5):

# Inside RedactingFilter.redact(self, msg)
if isinstance(content_copy, dict):
    # ... handle dicts ...
else:
    # THE BUG: Force everything to string so we can regex it
    content_copy = isinstance(content_copy, str) and content_copy or str(content_copy)
    for pattern in self._mask_patterns:
        content_copy = re.sub(pattern, self._mask, content_copy)

It's that else block that causes the pain. It assumes that if it's not a dict, it must be coercible to a string for regex processing.

The Fixed Code (v0.0.6):

from collections.abc import Mapping
 
# Inside RedactingFilter.redact(self, msg)
if isinstance(content_copy, Mapping):
    # ... handle dicts safely ...
elif isinstance(content_copy, str):
    # ONLY apply regex if it is ALREADY a string
    for pattern in self._mask_patterns:
        content_copy = re.sub(pattern, self._mask, content_copy)
# Note: If it's not a Mapping or a str, it implicitly does nothing and returns the original object.

The fix is subtle but vital. It stops the implicit casting. If content_copy is an integer, it hits the end of the if/elif chain and remains an integer. The logging formatter downstream stays happy because %d receives an integer. However, as we'll discuss later, this fix introduces a new, interesting behavior regarding what actually gets redacted.

The Exploit: Denial of Observability

Exploiting this isn't about popping a shell; it's about causing chaos in the application's stability and observability. Imagine a scenario where a financial application processes transactions. It logs the transaction ID and the amount using standard logging practices.

import logging
from loggingredactor import RedactingFilter
 
# Vulnerable setup
logger = logging.getLogger('banking_app')
logger.addFilter(RedactingFilter())
 
# The harmless developer writes this:
tx_id = 94821
amount = 500.00
 
# The crash:
# The filter turns tx_id into "94821". 
# The formatter expects an int for %d.
logger.info("Processing TX ID: %d", tx_id)

When this code runs, the application raises an unhandled TypeError. If this logging call is inside the main transaction loop and not wrapped in a specific try/except block for logging errors (which nobody writes), the entire transaction fails.

> [!NOTE] > The Irony: The crash often happens inside the error handling routine.

Consider an app that catches an exception, tries to log it, and then crashes while logging the error. You lose the original error trace and are left with a TypeError from the logging library. This effectively blinds the ops team to the real root cause of production issues.

The Fix & The Bypass: Security vs. Stability

The patch in version 0.0.6 solves the crash by removing the forced string conversion. However, this introduces a fascinating side-effect that I like to call the "Redaction Bypass via Type confusion."

In the old version (v0.0.5), the library was paranoid. It converted everything to a string and checked it against regex patterns. If you had a credit card number stored as an integer (bad practice, but it happens) or a float, the old version would stringify it and redact it.

The New Behavior (v0.0.6):

# Since we only check `isinstance(x, str)`:
secret_pin = 1234
# Regex pattern is r'\d{4}'
 
# This integer bypasses the redaction logic entirely because it is not a string!
logger.info("User PIN: %s", secret_pin)

Because secret_pin is an integer, the new redact method ignores it. The logging formatter eventually converts it to a string for output, after the redaction filter has already finished its job. The result? Cleartext secrets in logs.

This is a classic trade-off. The maintainers chose application stability (stop crashing on %d) over aggressive, paranoid redaction. It is now the developer's responsibility to ensure that sensitive fields are cast to strings before logging if they rely on regex-based redaction.

Official Patches

PyPIFixed version release on PyPI
GitHubRelease notes for version 0.0.6

Fix Analysis (2)

Technical Appendix

CVSS Score
5.3/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
EPSS Probability
0.04%
Top 90% most exploited

Affected Systems

Python applications using loggingredactor < 0.0.6Systems using standard Python logging with %d, %f, or %x formatters

Affected Versions Detail

Product
Affected Versions
Fixed Version
loggingredactor
armurox
< 0.0.60.0.6
AttributeDetail
CWE IDCWE-704 (Incorrect Type Conversion)
CVSS v3.15.3 (Medium)
Attack VectorLocal (via Log Arguments)
ImpactDenial of Service (App Crash) / Data Loss
EPSS Score0.00042
Exploit MaturityPoC Available

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
CWE-704
Incorrect Type Conversion or Cast

Incorrect Type Conversion or Cast

Known Exploits & Detection

GitHub IssueOriginal issue report containing the reproduction script

Vulnerability Timeline

Vulnerability reported via GitHub Issue
2025-02-27
Initial patch attempt committed
2025-02-28
Version 0.0.6 released with complete fix
2025-03-01
CVE-2026-22041 Assigned
2026-01-08

References & Sources

  • [1]GitHub Security Advisory GHSA-rvjx-cfjh-5mc9

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

•about 6 hours ago•CVE-2026-48861
2.1

CVE-2026-48861: HTTP Request Splitting and Smuggling via Method Parameter CRLF Injection in Elixir Mint

CVE-2026-48861 is a client-side HTTP request-line CRLF (Carriage Return Line Feed) injection vulnerability in the popular Elixir HTTP client library, Mint. The vulnerability permits HTTP Request Splitting and HTTP Request Smuggling when an application forwards untrusted, attacker-controlled inputs to Mint's HTTP client requests as either the HTTP request method or target. By embedding CRLF characters within these parameters, an attacker can terminate the request line prematurely, inject malicious headers, or pipeline entirely independent requests. These smuggled requests are then processed by upstream or downstream proxy servers as separate HTTP queries on the same TCP connection. While Mint version 1.7.0 introduced target validation to secure the request target, the HTTP request method parameter remained completely unvalidated. This flaw allows attackers to bypass routing filters, access restricted internal APIs, or poison HTTP caches under default configurations.

Amit Schendel
Amit Schendel
4 views•7 min read
•about 7 hours ago•CVE-2026-49753
6.3

CVE-2026-49753: HTTP Request/Response Smuggling via Inconsistent Content-Length Parsing in Elixir Mint Client

An Inconsistent Interpretation of HTTP Requests (HTTP Request/Response Smuggling) vulnerability in the Elixir Mint HTTP client allows attacker-controlled HTTP/1 servers to desynchronize response framing on shared connections due to over-lenient parsing of sign-prefixed Content-Length headers.

Amit Schendel
Amit Schendel
6 views•6 min read
•about 7 hours ago•CVE-2026-49754
8.2

CVE-2026-49754: Denial of Service via Unbounded HTTP/2 CONTINUATION Frame Accumulation in Elixir Mint

An allocation of resources without limits or throttling vulnerability in Elixir Mint allows an attacker-controlled HTTP/2 server to exhaust memory in a Mint client. The vulnerability is exploited by sending a HEADERS frame without the END_HEADERS flag followed by an infinite stream of CONTINUATION frames. Because the client lacks limits on the incoming header-block accumulator, the client continuously consumes memory until an out-of-memory crash occurs.

Amit Schendel
Amit Schendel
7 views•6 min read
•about 8 hours ago•CVE-2026-48596
2.1

CVE-2026-48596: Improper Neutralization of CRLF Sequences in Elixir Tesla Multipart HTTP Client

CVE-2026-48596 is an Improper Neutralization of CRLF Sequences in HTTP Headers (HTTP Request/Response Splitting, CWE-113) in the Elixir Tesla HTTP client. The flaw resides in how multipart content-type parameters are joined and serialized, enabling attackers to inject arbitrary headers or split HTTP requests when applications pass untrusted inputs to the parameters of multipart uploads.

Alon Barad
Alon Barad
5 views•6 min read
•about 8 hours ago•CVE-2026-48594
8.2

CVE-2026-48594: Decompression Bomb Denial of Service in Elixir Tesla HTTP Client

An improper handling of highly compressed data (decompression bomb) vulnerability exists in the Elixir Tesla HTTP client when utilizing response decompression middlewares. By serving highly compressed responses or stacked content-encoding headers, a malicious server can cause arbitrary heap exhaustion, leading to a denial of service (DoS) crash in the BEAM virtual machine.

Amit Schendel
Amit Schendel
6 views•6 min read
•about 9 hours ago•CVE-2026-48595
8.2

CVE-2026-48595: Cross-Origin Credential Leakage in Elixir Tesla Client via Case-Sensitive Redirect Filter Bypass

A high-severity security vulnerability in Elixir's Tesla HTTP client library (CVE-2026-48595) allows unauthenticated remote attackers to harvest sensitive credentials, including Authorization headers and cookies. The flaw resides in the 'Tesla.Middleware.FollowRedirects' component, which performs case-sensitive lookups when stripping credentials during cross-origin redirects. Because HTTP headers are case-insensitive by RFC specifications, standard canonical casing (e.g., 'Authorization') bypasses the lowercase-only blocklist, leaking tokens to untrusted external redirect destinations.

Alon Barad
Alon Barad
6 views•5 min read