def test_flag_as_containing_version(self):
        class FakeHasher:
            algo = "CUST"

            def hash(hasher, file_path, chunk_cb):
                if file_path == "/some/path/random1234/readme.txt":
                    chunk_cb(b"Readme for version 1.2.3 test")
                    chunk_cb(b"weird chunk")
                elif file_path == "/some/path/random1234/license.txt":
                    chunk_cb(b"MIT...")
                else:
                    raise FileNotFoundError()
                return "12345"

        with patch('openwebvulndb.common.hash.walk') as walk:
            walk.return_value = [
                ("/some/path/random1234", [],
                 ["readme.txt", "license.txt", "index.php"]),
            ]
            collector = HashCollector(path="/some/path/random1234",
                                      hasher=FakeHasher(),
                                      prefix="wp-content/plugins/my-plugin",
                                      lookup_version="1.2.3")

            signatures = list(collector.collect())

            self.assertIn(
                Signature(path="wp-content/plugins/my-plugin/readme.txt",
                          hash="12345",
                          algo="CUST",
                          contains_version=True), signatures)
            self.assertIn(
                Signature(path="wp-content/plugins/my-plugin/license.txt",
                          hash="12345",
                          algo="CUST"), signatures)
    async def test_collect_for_one_version(self, fake_future):
        with patch("openwebvulndb.common.hash.HashCollector") as HashCollector:
            workspace = MagicMock()
            workspace.workdir = "/my/workspace/path"
            workspace.to_version.return_value = fake_future(None)

            collector = MagicMock()
            collector.collect.return_value = ["Hello"]
            HashCollector.return_value = collector

            hasher = RepositoryHasher(storage=MagicMock(),
                                      hasher=Hasher("SHA256"))
            out = await hasher.collect_for_version(workspace,
                                                   "2.1",
                                                   prefix="test-prefix")

            self.assertEqual(["Hello"], out)

            HashCollector.assert_called_with(path="/my/workspace/path",
                                             hasher=hasher.hasher,
                                             prefix="test-prefix",
                                             lookup_version="2.1")

            collector.collect.assert_called_with()

            workspace.to_version.assert_called_with("2.1")
    def test_exclude_php_files(self):
        with patch('openwebvulndb.common.hash.walk') as walk:
            walk.return_value = [
                ("/some/path/random1234", ["js", "css"],
                 ["readme.txt", "license.txt", "index.php"]),
                ("/some/path/random1234/js", [], ["index.js", "index.php"]),
                ("/some/path/random1234/css", [], ["custom.css", "index.php"]),
            ]
            collector = HashCollector(path="/some/path/random1234",
                                      hasher=MagicMock(),
                                      prefix="wp-content/plugins/my-plugin")
            collector.hasher.algo = "CONST"
            collector.hasher.hash.return_value = "12345"

            signatures = list(collector.collect())

            walk.assert_called_with("/some/path/random1234")

            self.assertNotIn(
                Signature(path="wp-content/plugins/my-plugin/index.php",
                          hash="12345",
                          algo="CONST"), signatures)
            self.assertNotIn(
                Signature(path="wp-content/plugins/my-plugin/js/index.php",
                          hash="12345",
                          algo="CONST"), signatures)
            self.assertNotIn(
                Signature(path="wp-content/plugins/my-plugin/css/index.php",
                          hash="12345",
                          algo="CONST"), signatures)
    def test_exclude_empty_files(self):
        class FakeHasher:

            algo = "sha256"

            def hash(hasher, file_path, chunk_cb):
                if file_path == "/path/empty":
                    raise ValueError("File is empty")
                return "12345"

        with patch('openwebvulndb.common.hash.walk') as walk:
            walk.return_value = [
                ("/path", [], ["readme.txt", "empty", "license.txt"]),
            ]
            collector = HashCollector(path="/path",
                                      hasher=FakeHasher(),
                                      prefix="wp-content/plugins/my-plugin",
                                      lookup_version="1.2.3")

            signatures = list(collector.collect())

            self.assertIn(
                Signature(path="wp-content/plugins/my-plugin/readme.txt",
                          hash="12345",
                          algo="sha256"), signatures)
            self.assertIn(
                Signature(path="wp-content/plugins/my-plugin/license.txt",
                          hash="12345",
                          algo="sha256"), signatures)
    def test_collect_files_has_issues_with_os(self):
        # Ex: OSError: [Errno 40] Too many levels of symbolic links
        with patch('openwebvulndb.common.hash.walk') as walk:
            walk.return_value = [
                ("/some/path/random1234", ["js", "css"],
                 ["readme.txt", "license.txt"]),
                ("/some/path/random1234/js", [], ["index.js"]),
                ("/some/path/random1234/css", [], ["custom.css"]),
            ]
            collector = HashCollector(path="/some/path/random1234",
                                      hasher=MagicMock(),
                                      prefix="wp-content/plugins/my-plugin")
            collector.hasher.algo = "CONST"
            collector.hasher.hash.side_effect = [
                "12345",
                "12345",
                OSError(),
                "12345",
            ]

            signatures = list(collector.collect())

            walk.assert_called_with("/some/path/random1234")

            collector.hasher.hash.assert_has_calls([
                call("/some/path/random1234/readme.txt",
                     chunk_cb=collector.version_checker),
                call("/some/path/random1234/license.txt",
                     chunk_cb=collector.version_checker),
                call("/some/path/random1234/css/custom.css",
                     chunk_cb=collector.version_checker),
                call("/some/path/random1234/js/index.js",
                     chunk_cb=collector.version_checker),
            ],
                                                   any_order=True)

            self.assertIn(
                Signature(path="wp-content/plugins/my-plugin/readme.txt",
                          hash="12345",
                          algo="CONST"), signatures)
            self.assertIn(
                Signature(path="wp-content/plugins/my-plugin/css/custom.css",
                          hash="12345",
                          algo="CONST"), signatures)
            self.assertEqual(3, len(signatures))
            self.assertTrue(signatures[0].dirty)
    def test_collect_files(self):
        with patch('openwebvulndb.common.hash.walk') as walk:
            walk.return_value = [
                ("/some/path/random1234", ["js", "css"],
                 ["readme.txt", "license.txt"]),
                ("/some/path/random1234/js", [], ["index.js"]),
                ("/some/path/random1234/css", [], ["custom.css"]),
            ]
            collector = HashCollector(path="/some/path/random1234",
                                      hasher=MagicMock(),
                                      prefix="wp-content/plugins/my-plugin")
            collector.hasher.algo = "CONST"
            collector.hasher.hash.return_value = "12345"

            signatures = list(collector.collect())

            walk.assert_called_with("/some/path/random1234")

            collector.hasher.hash.assert_has_calls([
                call("/some/path/random1234/readme.txt",
                     chunk_cb=collector.version_checker),
                call("/some/path/random1234/license.txt",
                     chunk_cb=collector.version_checker),
                call("/some/path/random1234/css/custom.css",
                     chunk_cb=collector.version_checker),
                call("/some/path/random1234/js/index.js",
                     chunk_cb=collector.version_checker),
            ],
                                                   any_order=True)

            self.assertIn(
                Signature(path="wp-content/plugins/my-plugin/readme.txt",
                          hash="12345",
                          algo="CONST"), signatures)
            self.assertIn(
                Signature(path="wp-content/plugins/my-plugin/css/custom.css",
                          hash="12345",
                          algo="CONST"), signatures)
            self.assertTrue(signatures[0].dirty)
    def test_strip_filenames(self):
        with patch('openwebvulndb.common.hash.walk') as walk:
            walk.return_value = [("/some/path/random1234", [],
                                  ["readme.txt ", "license.txt "])]
            collector = HashCollector(path="/some/path/random1234",
                                      hasher=MagicMock(),
                                      prefix="wp-content/plugins/my-plugin")
            collector.hasher.algo = "sha256"
            collector.hasher.hash.return_value = "12345"

            signatures = list(collector.collect())

            walk.assert_called_with("/some/path/random1234")

            self.assertIn(
                Signature(path="wp-content/plugins/my-plugin/readme.txt",
                          hash="12345",
                          algo="sha256"), signatures)
            self.assertIn(
                Signature(path="wp-content/plugins/my-plugin/license.txt",
                          hash="12345",
                          algo="sha256"), signatures)
    def test_exclude_version_control_files(self):
        with patch('openwebvulndb.common.hash.walk') as walk:
            walk.return_value = [
                ("/some/path/random1234", ["hidden", ".git"],
                 ["readme.txt", "license.txt", "index.php"]),
                ("/some/path/random1234/.git", [], ["HEAD"]),
                ("/some/path/random1234/hidden", [".svn"], []),
                ("/some/path/random1234/hidden/.svn", ["pristine"], []),
                ("/some/path/random1234/hidden/.svn/pristine", ["da"], []),
                ("/some/path/random1234/hidden/.svn/pristine/da", [],
                 ["da9d42e33e31a89b8e43713fdf6d481a90346b3b.svn-base"
                  ]),  # noqa
            ]
            collector = HashCollector(path="/some/path/random1234",
                                      hasher=MagicMock(),
                                      prefix="wp-content/plugins/my-plugin")
            collector.hasher.algo = "CONST"
            collector.hasher.hash.return_value = "12345"

            signatures = list(collector.collect())

            walk.assert_called_with("/some/path/random1234")

            self.assertEqual(2, len(signatures))