From 16001eab7cdeec8c0c5c6c8c463b05518d40cbf4 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Sun, 15 Mar 2020 20:15:07 +0100 Subject: [PATCH] passExtensions.pass-audit: 0.1 -> 1.0.1, refactor Updates to v1.0.1[1] which supports subdirs and zxcvbn[2]-based complexity checks. Also, the following things changed: * Add separate output for man-pages * Enable test-suite (after adding a patch which mocks the `pwnedpasswords.com`). * Added myself as maintainer. [1] https://github.com/roddhjav/pass-audit/releases/tag/v1.0.1 / https://github.com/roddhjav/pass-audit/releases/tag/v1.0 [2] https://pypi.org/project/zxcvbn-python/ --- pkgs/tools/security/pass/extensions/audit.nix | 42 ----- ...it-possible-to-run-the-tests-offline.patch | 175 ++++++++++++++++++ .../audit/0002-Fix-audit.bash-setup.patch | 28 +++ .../pass/extensions/audit/default.nix | 51 +++++ .../security/pass/extensions/default.nix | 2 +- 5 files changed, 255 insertions(+), 43 deletions(-) delete mode 100644 pkgs/tools/security/pass/extensions/audit.nix create mode 100644 pkgs/tools/security/pass/extensions/audit/0001-Make-it-possible-to-run-the-tests-offline.patch create mode 100644 pkgs/tools/security/pass/extensions/audit/0002-Fix-audit.bash-setup.patch create mode 100644 pkgs/tools/security/pass/extensions/audit/default.nix diff --git a/pkgs/tools/security/pass/extensions/audit.nix b/pkgs/tools/security/pass/extensions/audit.nix deleted file mode 100644 index ca5ca855337..00000000000 --- a/pkgs/tools/security/pass/extensions/audit.nix +++ /dev/null @@ -1,42 +0,0 @@ -{ stdenv, pass, fetchFromGitHub, pythonPackages, makeWrapper }: - -let - pythonEnv = pythonPackages.python.withPackages (p: [ p.requests ]); - -in stdenv.mkDerivation rec { - pname = "pass-audit"; - version = "0.1"; - - src = fetchFromGitHub { - owner = "roddhjav"; - repo = "pass-audit"; - rev = "v${version}"; - sha256 = "0v0db8bzpcaa7zqz17syn3c78mgvw4mpg8qg1gh5rmbjsjfxw6sm"; - }; - - nativeBuildInputs = [ makeWrapper ]; - - buildInputs = [ pythonEnv ]; - - patchPhase = '' - sed -i -e "s|/usr/lib|$out/lib|" audit.bash - sed -i -e 's|$0|${pass}/bin/pass|' audit.bash - ''; - - dontBuild = true; - - installFlags = [ "PREFIX=$(out)" ]; - - postFixup = '' - wrapProgram $out/lib/password-store/extensions/audit.bash \ - --prefix PATH : "${pythonEnv}/bin" \ - --run "export PREFIX" - ''; - - meta = with stdenv.lib; { - description = "Pass extension for auditing your password repository."; - homepage = https://github.com/roddhjav/pass-audit; - license = licenses.gpl3Plus; - platforms = platforms.unix; - }; -} diff --git a/pkgs/tools/security/pass/extensions/audit/0001-Make-it-possible-to-run-the-tests-offline.patch b/pkgs/tools/security/pass/extensions/audit/0001-Make-it-possible-to-run-the-tests-offline.patch new file mode 100644 index 00000000000..36faf3140cc --- /dev/null +++ b/pkgs/tools/security/pass/extensions/audit/0001-Make-it-possible-to-run-the-tests-offline.patch @@ -0,0 +1,175 @@ +From 37c2b4d2940476555aeec20fe1e5e3fa0492a94e Mon Sep 17 00:00:00 2001 +From: Maximilian Bosch +Date: Sun, 15 Mar 2020 19:58:53 +0100 +Subject: [PATCH] Make it possible to run the tests offline + +Helpful when developing without network access, also makes sure that +the test actually depend on the API's data like number of breaches +(which will change in time). +--- + tests/commons.py | 25 +++++++++++++++++++++++++ + tests/test_audit.py | 8 +++++--- + tests/test_pass_audit.py | 10 +++++++++- + tests/test_pwned.py | 8 +++++--- + 4 files changed, 44 insertions(+), 7 deletions(-) + +diff --git a/tests/commons.py b/tests/commons.py +index 13c4cb1..4f1ecd8 100644 +--- a/tests/commons.py ++++ b/tests/commons.py +@@ -56,3 +56,28 @@ class TestPass(TestBase): + for path in self.store.list(root): + data[path] = self.store.show(path) + return data ++ ++ ++def mock_request(*args, **kwargs): ++ class MockResponse: ++ def __init__(self): ++ data = [ ++ "D5EE0CB1A41071812CCED2F1930E6E1A5D2:2", ++ "2DC183F740EE76F27B78EB39C8AD972A757:52579", ++ "CF164D7A51A1FD864B1BF9E1CE8A3EC171B:4", ++ "D0B910E7A3028703C0B30039795E908CEB2:7", ++ "AD6438836DBE526AA231ABDE2D0EEF74D42:3", ++ "EBAB0A7CE978E0194608B572E4F9404AA21:3", ++ "17727EAB0E800E62A776C76381DEFBC4145:120", ++ "5370372AC65308F03F6ED75EC6068C8E1BE:1386", ++ "1E4C9B93F3F0682250B6CF8331B7EE68FD8:3730471", ++ "437FAA5A7FCE15D1DDCB9EAEAEA377667B8:123422", ++ "944C22589AC652B0F47918D58CA0CDCCB63:411" ++ ] ++ ++ self.text = "\r\n".join(data) ++ ++ def raise_for_status(self): ++ pass ++ ++ return MockResponse() +diff --git a/tests/test_audit.py b/tests/test_audit.py +index d8c7a9a..5e0a9cf 100644 +--- a/tests/test_audit.py ++++ b/tests/test_audit.py +@@ -17,12 +17,13 @@ + # + + from .. import pass_audit +-from tests.commons import TestPass +- ++from tests.commons import TestPass, mock_request ++from unittest import mock + + class TestPassAudit(TestPass): + passwords_nb = 7 + ++ @mock.patch('requests.get', mock_request) + def test_password_notpwned(self): + """Testing: pass audit for password not breached with K-anonymity method.""" + data = self._getdata("Password/notpwned") +@@ -30,9 +31,10 @@ class TestPassAudit(TestPass): + breached = audit.password() + self.assertTrue(len(breached) == 0) + ++ @mock.patch('requests.get', mock_request) + def test_password_pwned(self): + """Testing: pass audit for password breached with K-anonymity method.""" +- ref_counts = [51259, 3, 114, 1352, 3645804, 78773, 396] ++ ref_counts = [52579, 3, 120, 1386, 3730471, 123422, 411] + data = self._getdata("Password/pwned") + audit = pass_audit.PassAudit(data) + breached = audit.password() +diff --git a/tests/test_pass_audit.py b/tests/test_pass_audit.py +index 4c10f87..2c949f7 100644 +--- a/tests/test_pass_audit.py ++++ b/tests/test_pass_audit.py +@@ -19,7 +19,8 @@ + import os + + from .. import pass_audit +-from tests.commons import TestPass ++from tests.commons import TestPass, mock_request ++from unittest import mock + + + class TestPassAuditCMD(TestPass): +@@ -47,6 +48,7 @@ class TestPassAuditCMD(TestPass): + cmd = ['--not-an-option', '-q'] + self._passaudit(cmd, 2) + ++ @mock.patch('requests.get', mock_request) + def test_pass_audit_StoreNotInitialized(self): + """Testing: store not initialized.""" + cmd = ['Password/', '-v'] +@@ -56,6 +58,7 @@ class TestPassAuditCMD(TestPass): + os.rename(os.path.join(self.store.prefix, 'backup.gpg-id'), + os.path.join(self.store.prefix, '.gpg-id')) + ++ @mock.patch('requests.get', mock_request) + def test_pass_audit_InvalidID(self): + """Testing: invalid user ID.""" + os.rename(os.path.join(self.store.prefix, '.gpg-id'), +@@ -66,26 +69,31 @@ class TestPassAuditCMD(TestPass): + os.rename(os.path.join(self.store.prefix, 'backup.gpg-id'), + os.path.join(self.store.prefix, '.gpg-id')) + ++ @mock.patch('requests.get', mock_request) + def test_pass_audit_NotAFile(self): + """Testing: pass audit not_a_file.""" + cmd = ['not_a_file'] + self._passaudit(cmd, 1) + ++ @mock.patch('requests.get', mock_request) + def test_pass_audit_passwords_notpwned(self): + """Testing: pass audit Password/notpwned.""" + cmd = ['Password/notpwned'] + self._passaudit(cmd) + ++ @mock.patch('requests.get', mock_request) + def test_pass_audit_passwords_pwned(self): + """Testing: pass audit Password/pwned.""" + cmd = ['Password/pwned'] + self._passaudit(cmd) + ++ @mock.patch('requests.get', mock_request) + def test_pass_audit_passwords_good(self): + """Testing: pass audit Password/good.""" + cmd = ['Password/good'] + self._passaudit(cmd) + ++ @mock.patch('requests.get', mock_request) + def test_pass_audit_passwords_all(self): + """Testing: pass audit .""" + cmd = [''] +diff --git a/tests/test_pwned.py b/tests/test_pwned.py +index 5ce6bc6..c28939a 100644 +--- a/tests/test_pwned.py ++++ b/tests/test_pwned.py +@@ -17,7 +17,8 @@ + # + + from .. import pass_audit +-from tests.commons import TestPass ++from tests.commons import TestPass, mock_request ++from unittest import mock + + + class TestPwnedAPI(TestPass): +@@ -25,12 +26,13 @@ class TestPwnedAPI(TestPass): + def setUp(self): + self.api = pass_audit.PwnedAPI() + ++ @mock.patch('requests.get', mock_request) + def test_password_range(self): + """Testing: https://api.haveibeenpwned.com/range API.""" + prefix = '21BD1' + Hash = '21BD12DC183F740EE76F27B78EB39C8AD972A757' + hashes, counts = self.api.password_range(prefix) + self.assertIn(Hash, hashes) +- self.assertTrue(counts[hashes.index(Hash)] == 51259) ++ self.assertTrue(counts[hashes.index(Hash)] == 52579) + self.assertTrue(len(hashes) == len(counts)) +- self.assertTrue(len(hashes) == 527) ++ self.assertTrue(len(hashes) == 11) +-- +2.25.0 + diff --git a/pkgs/tools/security/pass/extensions/audit/0002-Fix-audit.bash-setup.patch b/pkgs/tools/security/pass/extensions/audit/0002-Fix-audit.bash-setup.patch new file mode 100644 index 00000000000..5703f3c1f65 --- /dev/null +++ b/pkgs/tools/security/pass/extensions/audit/0002-Fix-audit.bash-setup.patch @@ -0,0 +1,28 @@ +From 8f76b32946430737f97f2702afd828b09536afd2 Mon Sep 17 00:00:00 2001 +From: Maximilian Bosch +Date: Sun, 15 Mar 2020 20:10:11 +0100 +Subject: [PATCH 2/2] Fix audit.bash setup + +This sets PASSWORD_STORE_DIR (needed by the python-code) to +PASSWORD_STORE_DIR and properly falls back to `~/.password-store` if +it's not set. +--- + audit.bash | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/audit.bash b/audit.bash +index 7a973dc..c40ff76 100755 +--- a/audit.bash ++++ b/audit.bash +@@ -17,7 +17,7 @@ + # + + cmd_audit() { +- export PASSWORD_STORE_DIR=$PREFIX GIT_DIR PASSWORD_STORE_GPG_OPTS ++ export PASSWORD_STORE_DIR=${PASSWORD_STORE_DIR:-$HOME/.password-store} GIT_DIR PASSWORD_STORE_GPG_OPTS + export X_SELECTION CLIP_TIME PASSWORD_STORE_UMASK GENERATED_LENGTH + export CHARACTER_SET CHARACTER_SET_NO_SYMBOLS EXTENSIONS PASSWORD_STORE_KEY + export PASSWORD_STORE_ENABLE_EXTENSIONS PASSWORD_STORE_SIGNING_KEY +-- +2.25.0 + diff --git a/pkgs/tools/security/pass/extensions/audit/default.nix b/pkgs/tools/security/pass/extensions/audit/default.nix new file mode 100644 index 00000000000..bae6b9bbafd --- /dev/null +++ b/pkgs/tools/security/pass/extensions/audit/default.nix @@ -0,0 +1,51 @@ +{ stdenv, pass, fetchFromGitHub, pythonPackages, makeWrapper, gnupg }: + +let + pythonEnv = pythonPackages.python.withPackages (p: [ p.requests p.setuptools p.zxcvbn ]); + +in stdenv.mkDerivation rec { + pname = "pass-audit"; + version = "1.0.1"; + + src = fetchFromGitHub { + owner = "roddhjav"; + repo = "pass-audit"; + rev = "v${version}"; + sha256 = "1mdckw0dwcnv8smp1za96y0zmdnykbkw2606v7mzfnzbz4zjdlwl"; + }; + + patches = [ + ./0001-Make-it-possible-to-run-the-tests-offline.patch + ./0002-Fix-audit.bash-setup.patch + ]; + + postPatch = '' + substituteInPlace audit.bash \ + --replace '/usr/bin/env python3' "${pythonEnv}/bin/python3" + ''; + + outputs = [ "out" "man" ]; + + buildInputs = [ pythonEnv ]; + nativeBuildInputs = [ makeWrapper ]; + + doCheck = true; + checkInputs = [ pythonPackages.green pass gnupg ]; + checkPhase = '' + ${pythonEnv}/bin/python3 setup.py green -q + ''; + + installFlags = [ "DESTDIR=${placeholder "out"}" "PREFIX=" ]; + postInstall = '' + wrapProgram $out/lib/password-store/extensions/audit.bash \ + --prefix PYTHONPATH : "$out/lib/${pythonEnv.libPrefix}/site-packages" + ''; + + meta = with stdenv.lib; { + description = "Pass extension for auditing your password repository."; + homepage = https://github.com/roddhjav/pass-audit; + license = licenses.gpl3Plus; + platforms = platforms.unix; + maintainers = with maintainers; [ ma27 ]; + }; +} diff --git a/pkgs/tools/security/pass/extensions/default.nix b/pkgs/tools/security/pass/extensions/default.nix index 6eb321229e0..1f41a692482 100644 --- a/pkgs/tools/security/pass/extensions/default.nix +++ b/pkgs/tools/security/pass/extensions/default.nix @@ -3,7 +3,7 @@ with pkgs; { - pass-audit = callPackage ./audit.nix { + pass-audit = callPackage ./audit { pythonPackages = python3Packages; }; pass-checkup = callPackage ./checkup.nix {};