From f54add533564894f4e301376f79850089ce4e36c Mon Sep 17 00:00:00 2001 From: jackbrett Date: Thu, 23 Jan 2025 12:39:01 -0600 Subject: [PATCH 01/16] add max password length, default 72, configurable via ZXCVBN_MAX_LENGTH env var --- README.rst | 13 +++++++++++++ tests/l33t_exploit_test.py | 12 ++++++++++++ tests/zxcvbn_test.py | 5 ++++- zxcvbn/__init__.py | 8 ++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/l33t_exploit_test.py diff --git a/README.rst b/README.rst index fb3d411..3c3d42c 100644 --- a/README.rst +++ b/README.rst @@ -103,6 +103,18 @@ Output: }], } +To override the default maximum password length of 72 characters, set the +``ZXCVBN_MAX_LENGTH`` environment variable: + +.. code-block:: bash + + export ZXCVBN_MAX_LENGTH=128 + +.. warning:: + We strongly advise against setting ``ZXCVBN_MAX_LENGTH`` to a value greater than 72, + as it can lead to long processing times and may leave server-side applications open + to denial-of-service scenarios. + Custom Ranked Dictionaries -------------------------- @@ -121,6 +133,7 @@ In order to support more languages or just add password dictionaries of your own These lists will be added to the current ones, but you can also overwrite the current ones if you wish. The lists you add should be in order of how common the word is used with the most common words appearing first. + CLI ~~~ diff --git a/tests/l33t_exploit_test.py b/tests/l33t_exploit_test.py new file mode 100644 index 0000000..954a249 --- /dev/null +++ b/tests/l33t_exploit_test.py @@ -0,0 +1,12 @@ +import pytest +from zxcvbn import zxcvbn + +# Test ACsploit-generated password targeting zxcvbn's l33t matching algorithm +# (see https://github.com/GoSimpleLLC/nbvcxz/issues/60) +def test_l33t_exploit(): + + password = "4@8({[ MAX_LENGTH: + raise ValueError(f"Password length exceeds {MAX_LENGTH} characters.") + try: # Python 2 string types basestring = (str, unicode) From 68ab293d8a111b9f8bea6f878b37a64ef07babd7 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 09:49:54 -0600 Subject: [PATCH 02/16] add tox.ini, add python versions to test --- .github/workflows/build.yml | 58 +++++++++++++++++++++++-------------- tox.ini | 10 +++++++ 2 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 tox.ini diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 59aaa6b..e24ac45 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,27 +7,43 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.6, 3.7, 3.8, 3.9] + python-version: + [ + "3.0.1", + "3.1.4", + "3.2.5", + "3.4.10", + "3.5.10", + "3.6.15", + "3.7.17", + "3.8.18", + "3.9.21", + "3.10.16", + "3.11.11", + "3.12.8", + "3.13.0", + ] steps: - - uses: actions/checkout@master - - name: set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -U setuptools - pip install -r requirements.txt - pip install . - - name: Run mypy - if: "matrix.python-version != '2.7' && matrix.python-version != 'pypy2'" - run: | + - uses: actions/checkout@master + - name: set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U setuptools + pip install -r requirements.txt + pip install . + pip install tox + - name: Run mypy + if: "matrix.python-version != '2.7' && matrix.python-version != 'pypy2'" + run: | pip install -U mypy mypy -p zxcvbn --ignore-missing-imports - - name: Run tests - run: | - pytest -v - - name: Test Compatibility - run: | - python tests/test_compatibility.py tests/password_expected_value.json + - name: Run tests + run: | + pytest -v + - name: Test Compatibility + run: | + tox diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..3392a84 --- /dev/null +++ b/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py37, py38, py39, py310, py311 +isolated_build = True + +[testenv] +deps = + pytest +commands = + pytest + python tests/test_compatibility.py tests/password_expected_value.json From 699eeda97d81ade3a6c36d1163a950365e327a9d Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 09:58:15 -0600 Subject: [PATCH 03/16] try v5 of setup-python gha --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e24ac45..5e86f41 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From c505a517fd1549ca0eb0f53449a9442068e3d476 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 10:06:07 -0600 Subject: [PATCH 04/16] try dropping python versions older than 3.6 --- .github/workflows/build.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e86f41..b4296d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,11 +9,6 @@ jobs: matrix: python-version: [ - "3.0.1", - "3.1.4", - "3.2.5", - "3.4.10", - "3.5.10", "3.6.15", "3.7.17", "3.8.18", From c88af4444861694324047911a279213dca16314a Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 10:18:59 -0600 Subject: [PATCH 05/16] update build.yml with python versions supported by Ubuntu 24.04 --- .github/workflows/build.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b4296d6..2c7c39d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,17 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: - [ - "3.6.15", - "3.7.17", - "3.8.18", - "3.9.21", - "3.10.16", - "3.11.11", - "3.12.8", - "3.13.0", - ] + python-version: ["3.8.18", "3.9.21", "3.10.16", "3.11.11"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} From 6734d06686f9c3f53e3c9693979e06f06567a4aa Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 10:28:24 -0600 Subject: [PATCH 06/16] try python version 3.8.18 by itself --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c7c39d..8e2b2a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8.18", "3.9.21", "3.10.16", "3.11.11"] + python-version: ["3.8.18"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} From 8b8e53ae0bff68a3d1ac3369ad5ee2d892973425 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 10:57:51 -0600 Subject: [PATCH 07/16] let tox control pytest version --- requirements.txt | 1 - tox.ini | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e0fa86f..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pytest==3.5.0 diff --git a/tox.ini b/tox.ini index 3392a84..f2094e8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,12 @@ [tox] -envlist = py37, py38, py39, py310, py311 +envlist = py38 isolated_build = True [testenv] deps = - pytest + # Vary Pytest by Python version: + pytest==3.5.0: python_version < '3.8' + pytest==7.4.2: python_version >= '3.8' commands = pytest python tests/test_compatibility.py tests/password_expected_value.json From bd63736695b06bbaf663bc8119099d3b8468643e Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 10:59:05 -0600 Subject: [PATCH 08/16] rm reference to requirements.txt --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e2b2a4..3f46639 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,6 @@ jobs: run: | python -m pip install --upgrade pip pip install -U setuptools - pip install -r requirements.txt pip install . pip install tox - name: Run mypy From 909084c1f3dcf9ac9241cd47853a7780f3d5932e Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 11:05:34 -0600 Subject: [PATCH 09/16] tweak tox config --- .github/workflows/build.yml | 3 --- requirements.txt | 5 +++++ tox.ini | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 requirements.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3f46639..787458c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,8 +26,5 @@ jobs: pip install -U mypy mypy -p zxcvbn --ignore-missing-imports - name: Run tests - run: | - pytest -v - - name: Test Compatibility run: | tox diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dfbc44f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +# For older Python versions < 3.6 (example threshold), install an older Pytest: +pytest==3.5.0; python_version < "3.6" + +# For Python 3.6+ or 3.8+, install a more modern Pytest: +pytest==7.4.2; python_version >= "3.6" diff --git a/tox.ini b/tox.ini index f2094e8..42ce9db 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ isolated_build = True [testenv] deps = - # Vary Pytest by Python version: + # Then vary Pytest by Python version: pytest==3.5.0: python_version < '3.8' pytest==7.4.2: python_version >= '3.8' commands = From b27c7ab1a7a0e7625acfa93dea2724881e2b65cc Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 11:08:11 -0600 Subject: [PATCH 10/16] trying another tox config --- .github/workflows/build.yml | 1 + tox.ini | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 787458c..3c0c01d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -U setuptools + pip install -r requirements.txt pip install . pip install tox - name: Run mypy diff --git a/tox.ini b/tox.ini index 42ce9db..8f5c205 100644 --- a/tox.ini +++ b/tox.ini @@ -4,9 +4,7 @@ isolated_build = True [testenv] deps = - # Then vary Pytest by Python version: - pytest==3.5.0: python_version < '3.8' - pytest==7.4.2: python_version >= '3.8' + pytest commands = pytest python tests/test_compatibility.py tests/password_expected_value.json From dffa53bd5763364b0fa121b7c0034fa5d50494ed Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 11:21:58 -0600 Subject: [PATCH 11/16] add py versions 3.9, 3.10, 3.11 --- .github/workflows/build.yml | 2 +- requirements.txt | 4 ++-- tox.ini | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c0c01d..e7adf97 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8.18"] + python-version: ["3.8.18", "3.9.21", "3.10.16", "3.11.11"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} diff --git a/requirements.txt b/requirements.txt index dfbc44f..5ac1407 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -# For older Python versions < 3.6 (example threshold), install an older Pytest: +# For older Python versions < 3.6 install Pytest 3.5.0 pytest==3.5.0; python_version < "3.6" -# For Python 3.6+ or 3.8+, install a more modern Pytest: +# For Python 3.6+, install a more modern Pytest: pytest==7.4.2; python_version >= "3.6" diff --git a/tox.ini b/tox.ini index 8f5c205..2ce0781 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38 +envlist = py38, py39, py310, py311 isolated_build = True [testenv] @@ -7,4 +7,3 @@ deps = pytest commands = pytest - python tests/test_compatibility.py tests/password_expected_value.json From 3c4e3ae2e24965daffb3acc30afa0852825a0461 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 15:02:48 -0600 Subject: [PATCH 12/16] add py versions 3.12 and 3.13 --- .github/workflows/build.yml | 3 ++- tox.ini | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7adf97..b2673ba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8.18", "3.9.21", "3.10.16", "3.11.11"] + python-version: + ["3.8.18", "3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} diff --git a/tox.ini b/tox.ini index 2ce0781..f73fe62 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38, py39, py310, py311 +envlist = py38, py39, py310, py311, py312, py313 isolated_build = True [testenv] @@ -7,3 +7,4 @@ deps = pytest commands = pytest + python tests/test_compatibility.py tests/password_expected_value.json From ba2525d6479fd99a95eb19bc147c1acaacb4a5d9 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Tue, 28 Jan 2025 12:45:59 -0600 Subject: [PATCH 13/16] remove python 2 condition from mypy job --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b2673ba..123eaf8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,6 @@ jobs: pip install . pip install tox - name: Run mypy - if: "matrix.python-version != '2.7' && matrix.python-version != 'pypy2'" run: | pip install -U mypy mypy -p zxcvbn --ignore-missing-imports From d9a05ad1fd96dbf0077cc0e7d119208700bf3d2f Mon Sep 17 00:00:00 2001 From: jackbrett Date: Tue, 18 Feb 2025 10:22:30 -0600 Subject: [PATCH 14/16] update README w/ tested py versions, try 3.8.* as test version --- .github/workflows/build.yml | 2 +- README.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 123eaf8..5758e1e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: python-version: - ["3.8.18", "3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"] + ["3.8.*", "3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} diff --git a/README.rst b/README.rst index fb3d411..b41a3af 100644 --- a/README.rst +++ b/README.rst @@ -16,7 +16,7 @@ time. Features -------- -- **Tested in Python versions 2.7, 3.6-3.9** +- **Tested in Python versions 3.8-3.13** - Accepts user data to be added to the dictionaries that are tested against (name, birthdate, etc) - Gives a score to the password, from 0 (terrible) to 4 (great) - Provides feedback on the password and ways to improve it From 9750eadcbc04929bc336d4142514d96eb6cd84d6 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Tue, 18 Feb 2025 10:24:29 -0600 Subject: [PATCH 15/16] fuzzy match all py test versions --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5758e1e..f9ec440 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: python-version: - ["3.8.*", "3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"] + ["3.8.*", "3.9.*", "3.10.*", "3.11.*", "3.12.*", "3.13.*"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} From 1ed43f525e6c0851216a7b18e2855fe2944efeda Mon Sep 17 00:00:00 2001 From: jackbrett Date: Tue, 18 Feb 2025 12:03:54 -0600 Subject: [PATCH 16/16] use optional third arg instead of env variable for max password length --- README.rst | 16 +++++++++------- tests/l33t_exploit_test.py | 4 ++-- tests/zxcvbn_test.py | 4 +--- zxcvbn/__init__.py | 8 +++----- zxcvbn/__main__.py | 8 +++++++- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/README.rst b/README.rst index bfc358c..6c0a08b 100644 --- a/README.rst +++ b/README.rst @@ -103,19 +103,19 @@ Output: }], } -To override the default maximum password length of 72 characters, set the -``ZXCVBN_MAX_LENGTH`` environment variable: +Another optional argument is ``max_length``, allowing override of the default max password length of 72. +.. code:: python -.. code-block:: bash + from zxcvbn import zxcvbn - export ZXCVBN_MAX_LENGTH=128 + results = zxcvbn('JohnSmith321', user_inputs=['John', 'Smith'], max_length=88) .. warning:: - We strongly advise against setting ``ZXCVBN_MAX_LENGTH`` to a value greater than 72, + + We strongly advise against setting ``max_length`` greater than 72, as it can lead to long processing times and may leave server-side applications open to denial-of-service scenarios. - Custom Ranked Dictionaries -------------------------- @@ -141,11 +141,13 @@ You an also use zxcvbn from the command line:: echo 'password' | zxcvbn --user-input | jq +You can include a ``--max-length`` argument:: + echo '' | zxcvbn --max-length 142 + You can also execute the zxcvbn module:: echo 'password' | python -m zxcvbn --user-input | jq - Contribute ---------- diff --git a/tests/l33t_exploit_test.py b/tests/l33t_exploit_test.py index 954a249..362d204 100644 --- a/tests/l33t_exploit_test.py +++ b/tests/l33t_exploit_test.py @@ -7,6 +7,6 @@ def test_l33t_exploit(): password = "4@8({[ MAX_LENGTH: - raise ValueError(f"Password length exceeds {MAX_LENGTH} characters.") + if len(password) > max_length: + raise ValueError(f"Password exceeds max length of {max_length} characters.") try: # Python 2 string types diff --git a/zxcvbn/__main__.py b/zxcvbn/__main__.py index 8738197..0f4ee91 100644 --- a/zxcvbn/__main__.py +++ b/zxcvbn/__main__.py @@ -16,6 +16,12 @@ help='user data to be added to the dictionaries that are tested against ' '(name, birthdate, etc)', ) +parser.add_argument( + '--max-length', + default=72, + type=int, + help='Override password max length (default: 72)' +) class JSONEncoder(json.JSONEncoder): def default(self, o): @@ -36,7 +42,7 @@ def cli(): else: password = getpass.getpass() - res = zxcvbn(password, user_inputs=args.user_input) + res = zxcvbn(password, user_inputs=args.user_input, max_length=args.max_length) json.dump(res, sys.stdout, indent=2, cls=JSONEncoder) sys.stdout.write('\n')