CVE-2022-3273 (Python, published 2022-10-06T18:16Z)
Description: value: Allocation of Resources Without Limits or Throttling in GitHub repository ikus060/rdiffweb prior to 2.5.0a4.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: CWE-326 \u2014 Inadequate Encryption Strength
The product stores or transmits sensitive data using an encryption scheme that is theoretically sound, but is not strong enough for the level of protection required.
Repository: https://github.com/ikus060/rdiffweb
Files changed:
README.md
rdiffweb/controller/__init__.py
rdiffweb/controller/page_admin_users.py
rdiffweb/controller/page_pref_general.py
rdiffweb/controller/tests/test_page_prefs_general.py
rdiffweb/core/login.py
rdiffweb/core/model/_user.py
rdiffweb/core/model/tests/test_user.py
Commit message: Limit incorrect attempts to change the user's password to prevent brute force attacks #225


Vulnerable code:
    flash_message = FlashMessage(message, level)
            userobj.set_password(self.password.data, old_password=None)
        try:
            user.set_password(self.new.data, old_password=self.current.data)
            flash(_("Password updated successfully."), level='success')
        except ValueError as e:
            flash(str(e), level='warning')
        self.assertInBody("Wrong password")
from rdiffweb.core.passwd import check_password
        if user and check_password(password, user.hash_password):
    def set_password(self, password, old_password=None):
        assert old_password is None or isinstance(old_password, str)
        if old_password and not check_password(old_password, self.hash_password):
            raise ValueError(_("Wrong password"))

    def test_set_password_with_old_password(self):
        # Given a user in drabase with a password
        userobj = UserObject.add_user('john', 'password')
        self.listener.user_password_changed.reset_mock()
        # When updating the user's password with old_password
        userobj.set_password('new_password', old_password='password')
        # Then password is SSHA
        self.assertTrue(check_password('new_password', userobj.hash_password))
        # Check if listener called
        self.listener.user_password_changed.assert_called_once_with(userobj)

    def test_set_password_with_invalid_old_password(self):
        # Given a user in drabase with a password
        userobj = UserObject.add_user('foo', 'password')
        self.listener.user_password_changed.reset_mock()
        # When updating the user's password with wrong old_password
        # Then an exception is raised
        with self.assertRaises(ValueError):
            userobj.set_password('new_password', old_password='invalid')
        # Check if listener called
        self.listener.user_password_changed.assert_not_called()


Fixed code:
* 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)
    flash_message = FlashMessage(str(message), level)
            userobj.set_password(self.password.data)
# Maximum number of password change attempt before logout
CHANGE_PASSWORD_MAX_ATTEMPT = 5
CHANGE_PASSWORD_ATTEMPTS = 'change_password_attempts'

        # 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/')
            flash(_("Wrong current password."), level='warning')
        else:
            # Clear number of attempts
            if CHANGE_PASSWORD_ATTEMPTS in cherrypy.session:
                del cherrypy.session[CHANGE_PASSWORD_ATTEMPTS]
            # If Valid, update password
            try:
                user.set_password(self.new.data)
                flash(_("Password updated successfully."), level='success')
            except ValueError as e:
                flash(str(e), level='warning')
        self.assertInBody("Wrong current password")
    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.')

        if user and user.validate_password(password):
    def set_password(self, password):
    def validate_password(self, password):
        return check_password(password, self.hash_password)