VYPR
Moderate severityNVD Advisory· Published Oct 13, 2022· Updated May 15, 2025

Allocation of Resources Without Limits or Throttling in ikus060/rdiffweb

CVE-2022-3456

Description

Allocation of Resources Without Limits or Throttling in GitHub repository ikus060/rdiffweb prior to 2.5.0.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Rdiffweb prior to 2.5.0 lacks rate limiting on sensitive endpoints, enabling brute-force attacks and potential denial of service.

Vulnerability

Description CVE-2022-3456 describes an allocation of resources without limits or throttling in rdiffweb, a web-based backup management tool. Prior to version 2.5.0, the application did not enforce rate limits on critical endpoints such as login, multi-factor authentication (MFA), password change, and the RESTful API [1][2]. This missing control allows an attacker to send an unrestricted number of requests, consuming server resources and bypassing protections against brute-force attempts.

Exploitation

An unauthenticated attacker can exploit this vulnerability by sending a high volume of requests to the unprotected endpoints. Without rate limiting, the attacker can perform brute-force password guessing or attempt to bypass MFA without being throttled [2][3]. Additionally, the lack of resource limits can lead to resource exhaustion, potentially causing a denial of service (DoS) condition for legitimate users.

Impact

Successful exploitation could result in account compromise through brute-force attacks, as well as service degradation or unavailability due to resource exhaustion. The vulnerability is classified under CWE-770 (Allocation of Resources Without Limits or Throttling) and has a CVSS score reflecting medium severity [3].

Mitigation

The issue is fixed in rdiffweb version 2.5.0, which introduces a configurable rate limit per hour on sensitive endpoints. When the limit is exceeded, the server returns an HTTP 429 status or logs out the user [2][4]. Users are strongly advised to upgrade to version 2.5.0 or later to protect against this vulnerability.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
rdiffwebPyPI
< 2.5.02.5.0

Affected products

2
  • ghsa-coords
    Range: < 2.5.0
  • ikus060/ikus060/rdiffwebv5
    Range: unspecified

Patches

1
b78ec09f4582

Improve ratelimit implementation

https://github.com/ikus060/rdiffwebPatrik DufresneOct 11, 2022via ghsa
15 files changed · +167 124
  • doc/configuration.md+1 1 modified
    @@ -273,7 +273,7 @@ attacks and authenticated users to avoid Denial Of Service attack.
     
     | Option | Description | Example |
     | --- | --- | --- |
    -| rate-limit | maximum number of requests per minute that can be made by an IP address for an unauthenticated connection. When this limit is reached, an HTTP 429 message is returned to the user. This security measure is used to limit brute force attacks on the login page and the RESTful API. | 10 |
    +| rate-limit | maximum number of requests per hour that can be made on sensitive endpoints. When this limit is reached, an HTTP 429 message is returned to the user or the user is logged out. This security measure is used to limit brute force attacks on the login page and the RESTful API. | 20 |
     | rate-limit-dir | location where to store rate-limit information. When undefined, data is kept in memory. | /var/lib/rdiffweb/session |
     
     ## Custom user's password length limits
    
  • rdiffweb/controller/api.py+8 3 modified
    @@ -64,9 +64,14 @@ def _checkpassword(realm, username, password):
                 return True
             # Disable password authentication for MFA
             if userobj.mfa == UserObject.ENABLED_MFA:
    +            cherrypy.tools.ratelimit.hit()
                 return False
         # Otherwise validate username password
    -    return any(cherrypy.engine.publish('login', username, password))
    +    valid = any(cherrypy.engine.publish('login', username, password))
    +    if not valid:
    +        # When invalid, we need to increase the rate limit.
    +        cherrypy.tools.ratelimit.hit()
    +    return valid
     
     
     class ApiCurrentUser(Controller):
    @@ -99,9 +104,9 @@ def default(self):
     @cherrypy.tools.auth_basic(realm='rdiffweb', checkpassword=_checkpassword, priority=70)
     @cherrypy.tools.auth_form(on=False)
     @cherrypy.tools.auth_mfa(on=False)
    -@cherrypy.tools.sessions(on=False)
     @cherrypy.tools.i18n(on=False)
    -@cherrypy.tools.ratelimit()
    +@cherrypy.tools.ratelimit(scope='rdiffweb-api', hit=0, priority=69)
    +@cherrypy.tools.sessions(on=False)
     class ApiPage(Controller):
         """
         This class provide a restful API to access some of the rdiffweb resources.
    
  • rdiffweb/controller/form.py+1 3 modified
    @@ -19,8 +19,6 @@
     from markupsafe import Markup
    
     from wtforms.form import Form
    
     
    
    -SUBMIT_METHODS = {'POST', 'PUT', 'PATCH', 'DELETE'}
    
    -
    
     
    
     class _ProxyFormdata:
    
         """
    
    @@ -65,7 +63,7 @@ def is_submitted(self):
             Consider the form submitted if there is an active request and
    
             the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
    
             """
    
    -        return cherrypy.request.method in SUBMIT_METHODS
    
    +        return cherrypy.request.method == 'POST'
    
     
    
         def validate_on_submit(self):
    
             """
    
    
  • rdiffweb/controller/page_admin_session.py+0 4 modified
    @@ -30,10 +30,6 @@ class RevokeSessionForm(CherryForm):
         action = StringField(validators=[validators.regexp('delete')])
         number = IntegerField(validators=[validators.data_required()])
     
    -    @property
    -    def app(self):
    -        return cherrypy.request.app
    -
     
     @cherrypy.tools.is_admin()
     class AdminSessionPage(Controller):
    
  • rdiffweb/controller/page_login.py+1 1 modified
    @@ -62,7 +62,7 @@ class LoginPage(Controller):
     
         @cherrypy.expose()
         @cherrypy.tools.auth_mfa(on=False)
    -    @cherrypy.tools.ratelimit()
    +    @cherrypy.tools.ratelimit(methods=['POST'])
         def index(self, **kwargs):
             """
             Called by auth_form to generate the /login/ page.
    
  • rdiffweb/controller/page_mfa.py+1 1 modified
    @@ -70,7 +70,7 @@ def validate(self, extra_validators=None):
     
     class MfaPage(Controller):
         @cherrypy.expose()
    -    @cherrypy.tools.ratelimit()
    +    @cherrypy.tools.ratelimit(methods=['POST'])
         def index(self, **kwargs):
             form = MfaForm()
     
    
  • rdiffweb/controller/page_pref_general.py+1 19 modified
    @@ -29,10 +29,6 @@
     from rdiffweb.core.model import UserObject
     from rdiffweb.tools.i18n import gettext_lazy as _
     
    -# Maximum number of password change attempt before logout
    -CHANGE_PASSWORD_MAX_ATTEMPT = 5
    -CHANGE_PASSWORD_ATTEMPTS = 'change_password_attempts'
    -
     
     class UserProfileForm(CherryForm):
         action = HiddenField(default='set_profile_info')
    @@ -99,23 +95,8 @@ def populate_obj(self, user):
             # Check if current password is "valid" if Not, rate limit the
             # number of attempts and logout user after too many invalid attempts.
             if not user.validate_password(self.current.data):
    -            cherrypy.session[CHANGE_PASSWORD_ATTEMPTS] = cherrypy.session.get(CHANGE_PASSWORD_ATTEMPTS, 0) + 1
    -            attempts = cherrypy.session[CHANGE_PASSWORD_ATTEMPTS]
    -            if attempts >= CHANGE_PASSWORD_MAX_ATTEMPT:
    -                cherrypy.session.clear()
    -                cherrypy.session.regenerate()
    -                flash(
    -                    _("You were logged out because you entered the wrong password too many times."),
    -                    level='warning',
    -                )
    -                raise cherrypy.HTTPRedirect('/login/')
                 self.current.errors = [_("Wrong current password.")]
                 return False
    -
    -        # Clear number of attempts
    -        if CHANGE_PASSWORD_ATTEMPTS in cherrypy.session:
    -            del cherrypy.session[CHANGE_PASSWORD_ATTEMPTS]
    -
             try:
                 user.set_password(self.new.data)
                 return True
    @@ -151,6 +132,7 @@ class PagePrefsGeneral(Controller):
         """
     
         @cherrypy.expose
    +    @cherrypy.tools.ratelimit(methods=['POST'], logout=True)
         def default(self, **kwargs):
             # Process the parameters.
             profile_form = UserProfileForm(obj=self.app.currentuser)
    
  • rdiffweb/controller/page_pref_session.py+0 4 modified
    @@ -30,10 +30,6 @@ class RevokeSessionForm(CherryForm):
         action = StringField(validators=[validators.regexp('delete')])
         number = IntegerField(validators=[validators.data_required()])
     
    -    @property
    -    def app(self):
    -        return cherrypy.request.app
    -
     
     class PagePrefSession(Controller):
         @cherrypy.expose
    
  • rdiffweb/controller/tests/test_api.py+10 2 modified
    @@ -124,6 +124,14 @@ class APIRatelimitTest(rdiffweb.test.WebCase):
         }
     
         def test_login_ratelimit(self):
    -        for i in range(0, 6):
    -            self.getPage('/api/')
    +        # Given invalid credentials sent to API
    +        headers = [("Authorization", "Basic " + b64encode(b"admin:invalid").decode('ascii'))]
    +        for i in range(1, 5):
    +            self.getPage('/api/', headers=headers)
    +            self.assertStatus(401)
    +        # Then the 6th request is refused
    +        self.getPage('/api/', headers=headers)
    +        self.assertStatus(429)
    +        # Next request is also refused event if credentials are valid.
    +        self.getPage('/api/', headers=[("Authorization", "Basic " + b64encode(b"admin:admin123").decode('ascii'))])
             self.assertStatus(429)
    
  • rdiffweb/controller/tests/test_page_login.py+57 44 modified
    @@ -19,9 +19,9 @@
     
     @author: Patrik Dufresne
     """
    +import os
     
    -
    -from parameterized import parameterized
    +from parameterized import parameterized, parameterized_class
     
     import rdiffweb.test
     from rdiffweb.core.model import DbSession, SessionObject, UserObject
    @@ -212,67 +212,80 @@ def test_getpage_default(self):
             self.assertInBody('HEADER-NAME')
     
     
    +@parameterized_class(
    +    [
    +        {"default_config": {'rate-limit': 5}},
    +        {"default_config": {'rate-limit': 5, 'rate-limit-dir': '/tmp'}},
    +    ]
    +)
     class LoginPageRateLimitTest(rdiffweb.test.WebCase):
    -
    -    default_config = {
    -        'rate-limit': 5,
    -    }
    +    def setUp(self):
    +        if os.path.isfile('/tmp/ratelimit-127.0.0.1'):
    +            os.unlink('/tmp/ratelimit-127.0.0.1')
    +        if os.path.isfile('/tmp/ratelimit-127.0.0.1.-login'):
    +            os.unlink('/tmp/ratelimit-127.0.0.1.-login')
    +        return super().setUp()
     
         def test_login_ratelimit(self):
             # Given an unauthenticate
    -        # When requesting multple time the login page
    -        for i in range(0, 6):
    -            self.getPage('/login/')
    +        # When requesting multiple time the login page
    +        for i in range(1, 5):
    +            self.getPage('/login/', method='POST', body={'login': 'invalid', 'password': 'invalid'})
    +            self.assertStatus(200)
             # Then a 429 error (too many request) is return
    +        self.getPage('/login/', method='POST', body={'login': 'invalid', 'password': 'invalid'})
             self.assertStatus(429)
     
     
    -class LoginPageRateLimitWithSessionDirTest(rdiffweb.test.WebCase):
    +class LoginPageRateLimitTest2(rdiffweb.test.WebCase):
     
    -    default_config = {
    -        'rate-limit-dir': '/tmp',
    -        'rate-limit': 5,
    -    }
    +    default_config = {'rate-limit': 5}
     
    -    def test_login_ratelimit(self):
    +    def test_login_ratelimit_forwarded_for(self):
             # Given an unauthenticate
    -        # When requesting multple time the login page
    -        for i in range(0, 6):
    -            self.getPage('/login/')
    -        # Then a 429 error (too many request) is return
    +        # When requesting multiple time the login page with different `X-Forwarded-For`
    +        for i in range(1, 5):
    +            self.getPage(
    +                '/login/',
    +                headers=[('X-Forwarded-For', '127.0.0.%s' % i)],
    +                method='POST',
    +                body={'login': 'invalid', 'password': 'invalid'},
    +            )
    +            self.assertStatus(200)
    +        # Then original IP get blocked
    +        self.getPage(
    +            '/login/',
    +            headers=[('X-Forwarded-For', '127.0.0.%s' % i)],
    +            method='POST',
    +            body={'login': 'invalid', 'password': 'invalid'},
    +        )
             self.assertStatus(429)
     
     
    -class LoginPageRateLimitTestWithXForwardedFor(rdiffweb.test.WebCase):
    +class LoginPageRateLimitTest3(rdiffweb.test.WebCase):
    +    default_config = {'rate-limit': 5}
     
    -    default_config = {
    -        'rate-limit': 5,
    -    }
    -
    -    def test_login_ratelimit(self):
    +    def test_login_ratelimit_real_ip(self):
             # Given an unauthenticate
    -        # When requesting multple time the login page
    -        for i in range(0, 6):
    -            self.getPage('/login/', headers=[('X-Forwarded-For', '127.0.0.%s' % i)])
    -        # Then a 429 error (too many request) is return
    +        # When requesting multiple time the login page with different `X-Real-IP`
    +        for i in range(1, 5):
    +            self.getPage(
    +                '/login/',
    +                headers=[('X-Real-IP', '127.0.0.128')],
    +                method='POST',
    +                body={'login': 'invalid', 'password': 'invalid'},
    +            )
    +            self.assertStatus(200)
    +        # Then the X-Real-IP get blocked
    +        self.getPage(
    +            '/login/',
    +            headers=[('X-Real-IP', '127.0.0.128')],
    +            method='POST',
    +            body={'login': 'invalid', 'password': 'invalid'},
    +        )
             self.assertStatus(429)
     
     
    -class LoginPageRateLimitTestWithXRealIP(rdiffweb.test.WebCase):
    -
    -    default_config = {
    -        'rate-limit': 5,
    -    }
    -
    -    def test_login_ratelimit(self):
    -        # Given an unauthenticate
    -        # When requesting multple time the login page
    -        for i in range(0, 6):
    -            self.getPage('/login/', headers=[('X-Real-IP', '127.0.0.%s' % i)])
    -        # Then a 200 is return.
    -        self.assertStatus(200)
    -
    -
     class LogoutPageTest(rdiffweb.test.WebCase):
         def test_getpage_without_login(self):
             # Given an unauthenticated user
    
  • rdiffweb/controller/tests/test_page_prefs_general.py+28 15 modified
    @@ -203,21 +203,6 @@ def test_change_password_with_too_long(self):
             self._set_password(self.PASSWORD, new_password, new_password)
             self.assertInBody("Password must have between 8 and 128 characters.")
     
    -    def test_change_password_too_many_attemps(self):
    -        # When udating user's password with wrong current password 5 times
    -        for _i in range(1, 5):
    -            self._set_password('wrong', "pr3j5Dwi", "pr3j5Dwi")
    -            self.assertStatus(200)
    -            self.assertInBody("Wrong current password.")
    -        # Then user session is cleared and user is redirect to login page
    -        self._set_password('wrong', "pr3j5Dwi", "pr3j5Dwi")
    -        self.assertStatus(303)
    -        self.assertHeaderItemValue('Location', self.baseurl + '/login/')
    -        # Then a warning message is displayed on login page
    -        self.getPage('/login/')
    -        self.assertStatus(200)
    -        self.assertInBody('You were logged out because you entered the wrong password too many times.')
    -
         def test_change_password_with_same_value(self):
             # Given a user with a password
             self._set_password(self.PASSWORD, "pr3j5Dwi", "pr3j5Dwi")
    @@ -256,3 +241,31 @@ def test_update_repos(self):
             # Then the list is free of inexisting repos.
             userobj.expire()
             self.assertEqual(['broker-repo', 'testcases'], sorted([r.name for r in userobj.repo_objs]))
    +
    +
    +class PagePrefGeneralRateLimitTest(rdiffweb.test.WebCase):
    +    login = True
    +
    +    default_config = {'rate-limit': 5}
    +
    +    def test_change_password_too_many_attemps(self):
    +        # When udating user's password with wrong current password 5 times
    +        for _i in range(1, 5):
    +            self.getPage(
    +                '/prefs/general',
    +                method='POST',
    +                body={'action': 'set_password', 'current': 'wrong', 'new': 'pr3j5Dwi', 'confirm': 'pr3j5Dwi'},
    +            )
    +            self.assertStatus(200)
    +            self.assertInBody("Wrong current password.")
    +        # Then user session is cleared and user is redirect to login page
    +        self.getPage(
    +            '/prefs/general',
    +            method='POST',
    +            body={'action': 'set_password', 'current': 'wrong', 'new': 'pr3j5Dwi', 'confirm': 'pr3j5Dwi'},
    +        )
    +        self.assertStatus(303)
    +        self.assertHeaderItemValue('Location', self.baseurl + '/')
    +        # Then a warning message is displayed on login page
    +        self.getPage('/login/')
    +        self.assertStatus(200)
    
  • rdiffweb/core/config.py+2 2 modified
    @@ -404,8 +404,8 @@ def get_parser():
             '--rate-limit',
             metavar='LIMIT',
             type=int,
    -        default=30,
    -        help='maximum number of requests per minute that can be made by an IP address for an unauthenticated connection. When this limit is reached, an HTTP 429 message is returned to the user. This security measure is used to limit brute force attacks on the login page and the RESTful API.',
    +        default=20,
    +        help='maximum number of requests per hour that can be made on sensitive endpoints. When this limit is reached, an HTTP 429 message is returned to the user or the user is logged out. This security measure is used to limit brute force attacks on the login page and the RESTful API.',
         )
     
         parser.add(
    
  • rdiffweb/rdw_app.py+2 2 modified
    @@ -209,8 +209,8 @@ def __init__(self, cfg):
                     'tools.sessions.persistent': False,  # auth_form should update this.
                     'tools.auth_form.timeout': cfg.session_persistent_timeout,  # minutes
                     'tools.ratelimit.debug': cfg.debug,
    -                'tools.ratelimit.delay': 60,
    -                'tools.ratelimit.anonymous_limit': cfg.rate_limit,
    +                'tools.ratelimit.delay': 3600,
    +                'tools.ratelimit.limit': cfg.rate_limit,
                     'tools.ratelimit.storage_class': rate_limit_storage_class,
                     'tools.ratelimit.storage_path': cfg.rate_limit_dir,
                 },
    
  • rdiffweb/tools/ratelimit.py+54 23 modified
    @@ -1,6 +1,6 @@
     # -*- coding: utf-8 -*-
    -# rdiffweb, A web interface to rdiff-backup repositories
    -# Copyright (C) 2012-2021 rdiffweb contributors
    +# udb, A web interface to manage IT network
    +# Copyright (C) 2022 IKUS Software inc.
     #
     # This program is free software: you can redistribute it and/or modify
     # it under the terms of the GNU General Public License as published by
    @@ -33,13 +33,13 @@ class _DataStore:
         def __init__(self, **kwargs):
             self._locks = {}
     
    -    def get_and_increment(self, token, delay):
    +    def get_and_increment(self, token, delay, hit=1):
             lock = self._locks.setdefault(token, threading.RLock())
             with lock:
                 tracker = self._load(token)
                 if tracker is None or tracker.timeout < time.time():
                     tracker = Tracker(token=token, hits=0, timeout=int(time.time() + delay))
    -            tracker = tracker._replace(hits=tracker.hits + 1)
    +            tracker = tracker._replace(hits=tracker.hits + hit)
                 self._save(tracker)
             return tracker.hits
     
    @@ -97,7 +97,7 @@ def _load(self, token):
                     return pickle.load(f)
                 finally:
                     f.close()
    -        except (IOError, EOFError):
    +        except Exception:
                 # Drop session data if invalid
                 pass
             return None
    @@ -111,43 +111,74 @@ def _save(self, tracker):
                 f.close()
     
     
    -def check_ratelimit(delay=60, anonymous_limit=0, registered_limit=0, rate_exceed_status=429, debug=False, **conf):
    +def check_ratelimit(
    +    delay=3600, limit=25, return_status=429, logout=False, scope=None, methods=None, debug=False, hit=1, **conf
    +):
         """
    -    Verify the ratelimit. By default return a 429 HTTP error code (Too Many Request).
    +    Verify the ratelimit. By default return a 429 HTTP error code (Too Many Request). After 25 request within the same hour.
    +
    +    Arguments:
    +        delay:         Time window for analysis in seconds. Default per hour (3600 seconds)
    +        limit:         Number of request allowed for an entry point. Default 25
    +        return_status: HTTP Error code to return.
    +        logout:        True to logout user when limit is reached
    +        scope:         if specify, define the scope of rate limit. Default to path_info.
    +        methods:       if specify, only the methods in the list will be rate limited.
    +    """
    +    assert delay > 0, 'invalid delay'
     
    -    Usage:
    +    # Check if limit is enabled
    +    if limit <= 0:
    +        return
     
    -    @cherrypy.tools.ratelimit(on=True, anonymous_limit=5, registered_limit=50, storage_class=FileRateLimit, storage_path='/tmp')
    -    def index(self):
    -        pass
    -    """
    +    # Check if this 'method' should be rate limited
    +    request = cherrypy.request
    +    if methods is not None and request.method not in methods:
    +        if debug:
    +            cherrypy.log(
    +                'skip rate limit for HTTP method %s' % (request.method,),
    +                'TOOLS.RATELIMIT',
    +            )
    +        return
     
         # If datastore is not pass as configuration, create it for the first time.
    -    datastore = getattr(cherrypy, '_ratelimit_datastore', None)
    +    datastore = getattr(cherrypy.request.app, '_ratelimit_datastore', None)
         if datastore is None:
             # Create storage using storage class
             storage_class = conf.get('storage_class', RamRateLimit)
             datastore = storage_class(**conf)
    -        cherrypy._ratelimit_datastore = datastore
    +        cherrypy.request.app._ratelimit_datastore = datastore
     
         # If user is authenticated, use the username else use the ip address
    -    token = cherrypy.request.login or cherrypy.request.remote.ip
    -
    -    # Get the real limit depending of user login.
    -    limit = registered_limit if cherrypy.request.login else anonymous_limit
    -    if limit is None or limit <= 0:
    -        return
    +    token = (request.login or request.remote.ip) + '.' + (scope or request.path_info)
     
         # Get hits count using datastore.
    -    hits = datastore.get_and_increment(token, delay)
    +    hits = datastore.get_and_increment(token, delay, hit)
         if debug:
             cherrypy.log(
    -            'check and increase rate limit for token %s, limit %s, hits %s' % (token, limit, hits), 'TOOLS.RATELIMIT'
    +            'check and increase rate limit for scope %s, limit %s, hits %s' % (token, limit, hits), 'TOOLS.RATELIMIT'
             )
     
         # Verify user has not exceeded rate limit
         if limit <= hits:
    -        raise cherrypy.HTTPError(rate_exceed_status)
    +        if logout:
    +            if hasattr(cherrypy, 'session'):
    +                cherrypy.session.clear()
    +            raise cherrypy.HTTPRedirect("/")
    +
    +        raise cherrypy.HTTPError(return_status)
    +
    +
    +def hit(hit=1):
    +    """
    +    May be called directly by handlers to add a hit for the given request.
    +    """
    +    conf = cherrypy.tools.ratelimit._merged_args()
    +    conf['hit'] = hit
    +    cherrypy.tools.ratelimit.callable(**conf)
     
     
     cherrypy.tools.ratelimit = cherrypy.Tool('before_handler', check_ratelimit, priority=60)
    +
    +
    +cherrypy.tools.ratelimit.hit = hit
    
  • README.md+1 0 modified
    @@ -134,6 +134,7 @@ This next release focus on two-factor-authentication as a measure to increase se
     * Enforce validation on fullname, username and email
     * Limit incorrect attempts to change the user's password to prevent brute force attacks #225 [CVE-2022-3273](https://nvd.nist.gov/vuln/detail/CVE-2022-3273)
     * Enforce password policy new password cannot be set as new password [CVE-2022-3376](https://nvd.nist.gov/vuln/detail/CVE-2022-3376)
    +* Enforce better rate limit on login, mfa, password change and API [CVE-2022-3439](https://nvd.nist.gov/vuln/detail/CVE-2022-3439) [CVE-2022-3456](https://nvd.nist.gov/vuln/detail/CVE-2022-3456)
     
     Breaking changes:
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.