CVE-2022-3179 (Python, published 2022-09-13T17:15Z)
Description: value: Weak Password Requirements in GitHub repository ikus060/rdiffweb prior to 2.4.2.
Severity: nan (CVSS v3 base score: 8.8)
Weakness: CWE-521 \u2014 Weak Password Requirements
The product does not require that users should have strong passwords, which makes it easier for attackers to compromise user accounts.
Repository: https://github.com/ikus060/rdiffweb
Files changed:
README.md
doc/configuration.md
rdiffweb/controller/page_admin.py
rdiffweb/controller/pref_general.py
rdiffweb/controller/tests/test_page_admin.py
rdiffweb/controller/tests/test_page_prefs.py
rdiffweb/core/config.py
rdiffweb/core/store.py
Commit message: Enforce minimum and maximum password length #211


Vulnerable code:
    password = PasswordField(_('Password'))
from wtforms.validators import DataRequired, EqualTo, InputRequired, Regexp
        self._add_user("admin_role", "admin_role@test.com", "test2", "/home/", ADMIN_ROLE)
        self._add_user("maintainer_role", "maintainer_role@test.com", "test2", "/home/", MAINTAINER_ROLE)
        self._add_user("user_role", "user_role@test.com", "test2", "/home/", USER_ROLE)
        self._add_user("invalid", "invalid@test.com", "test2", "/home/", 'admin')
        self._add_user("test2", "test2@test.com", "test2", "/home/", USER_ROLE)
        self._add_user("Éric", "éric@test.com", "Éric", "/home/", USER_ROLE)
        self._add_user("test1", "test1@test.com", "test1", "/tmp/", USER_ROLE)
        self._add_user("test1", "test1@test.com", "test1", "/tmp/", USER_ROLE)
        self._add_user("test5", "test1@test.com", "test5", "/var/invalid/", USER_ROLE)
        self._add_user("test2", None, "test2", "/tmp/", USER_ROLE)
        self._add_user("test6", None, "test6", None, USER_ROLE)
        self._edit_user("test1", "test1@test.com", "test", "/var/invalid/", USER_ROLE)
        self._set_password(self.PASSWORD, "newpass", "newpass")
        self._set_password("newpass", self.PASSWORD, self.PASSWORD)
        self._set_password("oups", "t", "t")

Fixed code:
* Enforce minimum and maximum password length #211
## Custom user's password length limits

By default, Rdiffweb supports passwords with the following lengths:

* Minimum: 8 characters
* Maximum: 128 characters

Changing the minimum or maximum length does not affect existing users' passwords. Existing users are not prompted to reset their passwords to meet the new limits. The new limit only applies when an existing user changes their password.

| Option | Description | Example |
| --- | --- | --- |
| password-min-length | Minimum length of the user's password | 8 |
| password-max-length | Maximum length of the user's password | 128 |

    password = PasswordField(_('Password'), validators=[validators.optional()])
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.password.validators += [
            validators.length(
                min=self.app.cfg.password_min_length,
                max=self.app.cfg.password_max_length,
                message=_('Password must have between %(min)d and %(max)d characters.'),
            )
        ]

    @property
    def app(self):
        return cherrypy.request.app

from wtforms.validators import DataRequired, EqualTo, InputRequired, Length, Regexp
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.new.validators += [
            Length(
                min=self.app.cfg.password_min_length,
                max=self.app.cfg.password_max_length,
                message=_('Password must have between %(min)d and %(max)d characters.'),
            )
        ]

    @property
    def app(self):
        return cherrypy.request.app

        self._add_user("admin_role", "admin_role@test.com", "password", "/home/", ADMIN_ROLE)
        self._add_user("maintainer_role", "maintainer_role@test.com", "password", "/home/", MAINTAINER_ROLE)
        self._add_user("user_role", "user_role@test.com", "password", "/home/", USER_ROLE)
        self._add_user("invalid", "invalid@test.com", "test1234", "/home/", 'admin')
        self._add_user("test2", "test2@test.com", "test1234", "/home/", USER_ROLE)
        self._add_user("Éric", "éric@test.com", "password", "/home/", USER_ROLE)
        self._add_user("test1", "test1@test.com", "password", "/tmp/", USER_ROLE)
        self._add_user("test1", "test1@test.com", "password", "/tmp/", USER_ROLE)
        self._add_user("test5", "test1@test.com", "password", "/var/invalid/", USER_ROLE)
        self._add_user("test2", None, "password", "/tmp/", USER_ROLE)
        self._add_user("test6", None, "password", None, USER_ROLE)
    def test_change_password_with_too_short(self):
        self._edit_user(self.USERNAME, password='short')
        self.assertInBody("Password must have between 8 and 128 characters.")

    def test_change_password_with_too_long(self):
        new_password = 'a' * 129
        self._edit_user(self.USERNAME, password=new_password)
        self.assertInBody("Password must have between 8 and 128 characters.")

        self._edit_user("test1", "test1@test.com", "password", "/var/invalid/", USER_ROLE)
        self._set_password(self.PASSWORD, "newpassword", "newpassword")
        self._set_password("newpassword", self.PASSWORD, self.PASSWORD)
        self._set_password("oups", "newpassword", "newpassword")
    def test_change_password_with_too_short(self):
        self._set_password(self.PASSWORD, "short", "short")
        self.assertInBody("Password must have between 8 and 128 characters.")

    def test_change_password_with_too_long(self):
        new_password = 'a' * 129
        self._set_password(self.PASSWORD, new_password, new_password)
        self.assertInBody("Password must have between 8 and 128 characters.")

    parser.add(
        '--password-min-length',
        type=int,
        help="Minimum length of the user's password",
        default=8,
    )

    parser.add(
        '--password-max-length',
        type=int,
        help="Maximum length of the user's password",
        default=128,
    )

        cfg = self._store.app.cfg
        if cfg.password_min_length > len(password) > cfg.password_max_length:
            raise ValueError("invalid password length")