VAITP Dataset

← Back to the dataset

CVE-2026-22041

Improper type conversion in Logging Redactor can cause log formatting errors.

  • CVSS 2.0
  • CWE-704
  • Input Validation and Sanitization
  • Remote

Logging Redactor is a Python library designed to redact sensitive data in logs based on regex patterns and / or dictionary keys. Prior to version 0.0.6, non-string types are converted into string types, leading to type errors in %d conversions. The problem has been patched in version 0.0.6. No known workarounds are available.

CVSS base score
2.0
Published
2026-01-08
OWASP
A09 Security Logging and Monitoring Failures
Orthogonal defect classification
Checking
Code defect classification
Incorrect Functionality
Category
Input Validation and Sanitization
Subcategory
Arithmetic Errors
Accessibility scope
Remote
Impact
Denial of Service (DoS)
Affected component
Logging Reda
Fixed by upgrading
Yes

Solution

Upgrade logging-redactor to version 0.0.6 or later.

Vulnerable code sample

import logging
import sys

# This class represents the vulnerable component in a hypothetical logging library
# before the fix. The vulnerability is that it unconditionally converts all log
# arguments to strings, even when the format string expects other types.
class VulnerableRedactingFilter(logging.Filter):
    """
    A filter that simulates the CVE by converting all log arguments
    to strings, regardless of their original type.
    """
    def filter(self, record):
        # Check if there are arguments to process.
        if isinstance(record.args, tuple) and record.args:
            
            # THE VULNERABLE BEHAVIOR:
            # Naively convert all arguments to strings. In a real redactor,
            # this would happen as part of the redaction logic.
            # This causes a TypeError when the original format string
            # expects a number (e.g., %d or %f).
            processed_args = []
            for arg in record.args:
                # The library converts the argument to a string before checking
                # or redacting it.
                processed_args.append(str(arg))
            
            record.args = tuple(processed_args)
            
        # Return True to allow the log record to be processed further.
        return True

# --- Demonstration of the vulnerability ---

# 1. Get a logger instance
logger = logging.getLogger('VulnerableAppLogger')
logger.setLevel(logging.INFO)

# 2. Create a handler to output logs to the console
#    (We use a basic formatter that just shows the message)
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('%(message)s')
handler.setFormatter(formatter)

# 3. Instantiate and add the vulnerable filter to the handler
vulnerable_filter = VulnerableRedactingFilter()
handler.addFilter(vulnerable_filter)

# 4. Add the handler to the logger
logger.addHandler(handler)

# --- Triggering the vulnerability ---

user_id = 404
status_code = 200

# This log call will FAIL and raise a TypeError because of the vulnerability.
# The filter converts the integer `user_id` (404) to the string "404".
# The logger then tries to format "Processing request for user_id: %d"
# with the string "404", which is a type mismatch.
print("Attempting to log a message with an integer format specifier (%d)...")
logger.info("Processing request for user_id: %d", user_id)

# The program will crash on the line above and this line will not be reached.
print("This message will not be displayed.")

Patched code sample

import logging
import sys

# The vulnerability described (for a fictional CVE) is that a logging redactor
# would unconditionally convert all log arguments to strings before formatting.
# This causes a TypeError when a format string like "%d" expects a number
# but receives a string.
#
# The code below demonstrates the FIX for such a vulnerability. The key is that
# the filter now inspects arguments and only modifies them if they need redaction,
# preserving the original type of all other arguments.

SENSITIVE_KEY = "secret-api-key-12345"

class FixedRedactingFilter(logging.Filter):
    """
    A logging filter that correctly redacts sensitive data while preserving
    the original type of non-sensitive arguments, thus preventing TypeErrors.
    """

    def filter(self, record):
        # The vulnerable version would have unconditionally converted all args:
        #
        #   if isinstance(record.args, tuple):
        #       record.args = tuple(str(arg) for arg in record.args)
        #
        # This would cause `logger.info("ID: %d", 100)` to fail because 100
        # would become "100", which is incompatible with the %d format specifier.

        # THE FIX:
        # Process arguments selectively. If an argument does not need redaction,
        # its original type is preserved.
        if isinstance(record.args, tuple) and record.args:
            processed_args = []
            for arg in record.args:
                # Check if the argument needs redaction.
                if str(arg) == SENSITIVE_KEY:
                    processed_args.append("***REDACTED***")
                else:
                    # CRITICAL: Pass the argument through with its original type.
                    processed_args.append(arg)
            record.args = tuple(processed_args)

        # Also redact the formatted message itself, in case sensitive data
        # was not passed as a separate argument.
        if isinstance(record.msg, str):
            record.msg = record.msg.replace(SENSITIVE_KEY, "***REDACTED***")

        return True


# --- Demonstration of the fixed code ---

# 1. Configure a logger
logger = logging.getLogger('FixedRedactorDemo')
logger.setLevel(logging.INFO)

# 2. Create a stream handler and apply the fixed filter
handler = logging.StreamHandler(sys.stdout)
handler.addFilter(FixedRedactingFilter())
formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

# 3. Clear any existing handlers and add the new configured handler
if logger.hasHandlers():
    logger.handlers.clear()
logger.addHandler(handler)


print("--- Demonstrating the fix ---")

# This call would have raised a TypeError in the vulnerable version.
# With the fix, the integer `404` is preserved and formatting succeeds.
print("\n[1] Logging an integer with a %d specifier:")
logger.info("Request for user_id %d failed.", 404)

# This call demonstrates that redaction of sensitive arguments still works.
print("\n[2] Logging a sensitive string argument:")
logger.info("Connection failed with API key: %s", SENSITIVE_KEY)

# This call demonstrates a mix of argument types.
# The integer is preserved, and the sensitive string is redacted.
print("\n[3] Logging a mix of integer and sensitive string arguments:")
logger.info("User %d attempted to use key %s.", 123, SENSITIVE_KEY)

# This call demonstrates redaction when sensitive data is part of the message string.
print("\n[4] Logging sensitive data embedded directly in the message string:")
logger.info(f"Invalid operation with key {SENSITIVE_KEY} for account 987.")

Payload

import logging
from logging_redactor import RedactingFilter # Requires a vulnerable version < 0.0.6

# 1. Set up a logger and handler
logger = logging.getLogger('vulnerable_app')
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()

# 2. Add the vulnerable filter to the handler
# The patterns list can be empty; the vulnerability is in type handling.
vulnerable_filter = RedactingFilter(patterns=[])
handler.addFilter(vulnerable_filter)
logger.addHandler(handler)

# 3. Trigger the vulnerability with a log message containing a numeric
#    format specifier (%d) and an integer argument. This will raise a
#    TypeError, causing a denial of service if unhandled.
user_id = 12345
logger.info("Attempting to process user ID: %d", user_id)

Cite this entry

@misc{vaitp:cve202622041,
  title        = {{Improper type conversion in Logging Redactor can cause log formatting errors.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-22041},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-22041/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::