• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

esnet-security / SCRAM / 15620444624

12 Jun 2025 08:45PM UTC coverage: 95.004% (-0.7%) from 95.666%
15620444624

Pull #158

github

crankynetman
fix(secrets): and this time I mean it (calling the right function)
Pull Request #158: fix(user model): Fix default password generation.

58 of 61 branches covered (95.08%)

Branch coverage included in aggregate %.

15 of 25 new or added lines in 3 files covered. (60.0%)

1197 of 1260 relevant lines covered (95.0%)

0.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.29
/scram/shared/shared_code.py
1
"""A Module for defining shared code."""
2

3
import secrets
1✔
4
import string
1✔
5

6

7
def make_random_password(length: int = 20, min_digits: int = 5, max_attempts: int = 10000) -> str:
1✔
8
    """make_random_password replaces the deprecated django make_random_password function.
9

10
    Generates a random password of a specified length containing at least a specified number of digits using the
11
    official python best practices and some additional sanity checks. The python docs for this can be found at
12
    https://docs.python.org/3/library/secrets.html#recipes-and-best-practices. Note that generating long passwords
13
    with a high number of digits (>100) is inefficient and should be avoided. This password should only be used for
14
    temporary purposes, such as for a user to log in to the web interface and change their password.
15

16
    Args:
17
        length (int, optional): The total length of the password to generate. Defaults to 20.
18
        min_digits (int, optional): The minimum number of digits the password needs. Defaults to 5.
19
        max_attempts (int, optional): The maximum number of attempts to generate a valid password. Defaults to 10000.
20

21
    Raises:
22
        ValueError: Password length must be at least 1
23
        ValueError: min_digits cannot be negative
24
        ValueError: min_digits cannot exceed password length
25
        ValueError: For performance reasons, min_digits cannot exceed 30% of the password length
26
        RuntimeError: Failed to generate a valid password after max_attempts attempts
27

28
    Returns:
29
        password (str): The generated password.
30
    """
31
    if length < 1:
1✔
NEW
32
        message = "Password length must be at least 1"
×
NEW
33
        raise ValueError(message)
×
34
    if min_digits < 0:
1✔
NEW
35
        message = "min_digits cannot be negative"
×
NEW
36
        raise ValueError(message)
×
37
    if min_digits > length:
1✔
NEW
38
        message = "min_digits cannot exceed password length"
×
NEW
39
        raise ValueError(message)
×
40
    # Only allow a somewhat arbitrary threshold of 30% of the password length for min_digits, for performance reasons.
41
    if min_digits > length * 0.3:
1✔
NEW
42
        message = "For performance reasons, min_digits cannot exceed 30% of the password length"
×
NEW
43
        raise ValueError(message)
×
44

45
    alphabet = string.ascii_letters + string.digits
1✔
46

47
    for _attempt in range(max_attempts):
1✔
48
        password = "".join(secrets.choice(alphabet) for _i in range(length))
1✔
49
        if (
1✔
50
            any(c.islower() for c in password)
51
            and any(c.isupper() for c in password)
52
            and sum(c.isdigit() for c in password) >= min_digits
53
        ):
54
            return password
1✔
55

56
    # If we reached this point, we failed to generate a valid password after max_attempts attempts, likely due to the
57
    # required password length being very long and the min_digits being high.
NEW
58
    message = f"Failed to generate a valid password after {max_attempts} attempts"
×
NEW
59
    raise RuntimeError(message)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc