diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 59492bd..5662e19 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -67,6 +67,10 @@ jobs: run: | python -m pip install --upgrade pip pip install -r src/requirements.txt + - name: Run pip-audit + run: | + pip install pip-audit + pip-audit -r requirements.lock - name: Determine stress args run: | if [ "${{ github.event_name }}" = "schedule" ]; then diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..47f4453 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,15 @@ +repos: + - repo: https://github.com/psf/black + rev: 23.7.0 + hooks: + - id: black + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.11 + hooks: + - id: ruff + args: ["--select", "RUF100,B"] + - repo: https://github.com/PyCQA/bandit + rev: 1.7.5 + hooks: + - id: bandit + name: bandit diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..daed3ac --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,4 @@ +[tool.mypy] +python_version = "3.11" +strict = true +mypy_path = "src" diff --git a/requirements.lock b/requirements.lock new file mode 100644 index 0000000..fe4ebd0 --- /dev/null +++ b/requirements.lock @@ -0,0 +1,62 @@ +aiohappyeyeballs==2.6.1 +aiohttp==3.12.13 +aiosignal==1.3.2 +attrs==25.3.0 +base58==2.1.1 +bcrypt==4.3.0 +bech32==1.2.0 +bip-utils==2.9.3 +bip85==0.2.0 +cbor2==5.6.5 +certifi==2025.6.15 +cffi==1.17.1 +charset-normalizer==3.4.2 +click==8.2.1 +coincurve==21.0.0 +colorama==0.4.6 +coverage==7.9.1 +crcmod==1.7 +cryptography==45.0.4 +ecdsa==0.19.1 +ed25519-blake2b==1.4.1 +execnet==2.1.1 +frozenlist==1.7.0 +glob2==0.7 +hypothesis==6.135.20 +idna==3.10 +iniconfig==2.1.0 +ipaddress==1.0.23 +junit-xml==1.9 +mnemonic==0.21 +monero==1.1.1 +multidict==6.6.3 +mutmut==2.4.4 +nostr-sdk==0.42.1 +packaging==25.0 +parso==0.8.4 +pluggy==1.6.0 +pony==0.7.19 +portalocker==3.2.0 +propcache==0.3.2 +py-sr25519-bindings==0.2.2 +pycoin==0.92.20241201 +pycparser==2.22 +pycryptodome==3.23.0 +pycryptodomex==3.23.0 +Pygments==2.19.2 +PyNaCl==1.5.0 +PySocks==1.7.1 +pytest==8.4.1 +pytest-cov==6.2.1 +pytest-xdist==3.8.0 +requests==2.32.4 +six==1.17.0 +sortedcontainers==2.4.0 +termcolor==3.1.0 +toml==0.10.2 +tomli==2.2.1 +urllib3==2.5.0 +varint==1.0.2 +websocket-client==1.7.0 +websockets==15.0.1 +yarl==1.20.1 diff --git a/src/tests/test_password_length_constraints.py b/src/tests/test_password_length_constraints.py new file mode 100644 index 0000000..db38702 --- /dev/null +++ b/src/tests/test_password_length_constraints.py @@ -0,0 +1,31 @@ +import pytest +from pathlib import Path +import sys + +sys.path.append(str(Path(__file__).resolve().parents[1])) + +from password_manager.password_generation import PasswordGenerator +from constants import MIN_PASSWORD_LENGTH + + +class DummyEnc: + def derive_seed_from_mnemonic(self, mnemonic): + return b"\x00" * 32 + + +class DummyBIP85: + def derive_entropy(self, index: int, bytes_len: int, app_no: int = 32) -> bytes: + return bytes((index + i) % 256 for i in range(bytes_len)) + + +def make_generator(): + pg = PasswordGenerator.__new__(PasswordGenerator) + pg.encryption_manager = DummyEnc() + pg.bip85 = DummyBIP85() + return pg + + +def test_generate_password_too_short_raises(): + pg = make_generator() + with pytest.raises(ValueError): + pg.generate_password(length=MIN_PASSWORD_LENGTH - 1)