Repository: tox-dev/tox

Issue:
tox4: Bugs in the validation of --hash argument
The regular expression that checks the content of the `--hash` command line argument:

https://github.com/tox-dev/tox/blob/rewrite/src/tox/tox_env/python/pip/req/args.py#L52

```
_HASH = re.compile(r"sha(256:[a-z0-9]{64}|384:[a-z0-9]{96}|521:[a-z0-9]{128})")
```

does not accept `sha512:` as prefix but accepts non-hexadecimal characters beyond `a-f`. This should be fixed with a help of appropriate tests.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/2024?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev) Report
> Merging [#2024](https://codecov.io/gh/tox-dev/tox/pull/2024?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev) (dc80d19) into [rewrite](https://codecov.io/gh/tox-dev/tox/commit/5118401e6beb9f9b6dbe9bffa847dc99d678b1d7?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev) (5118401) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/2024/graphs/tree.svg?width=650&height=150&src=pr&token=DYodAwDCZ5&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev)](https://codecov.io/gh/tox-dev/tox/pull/2024?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev)

```diff
@@           Coverage Diff            @@
##           rewrite    #2024   +/-   ##
========================================
  Coverage    99.83%   99.83%           
========================================
  Files          151      151           
  Lines         8450     8451    +1     
  Branches       871      871           
========================================
+ Hits          8436     8437    +1     
  Misses           3        3           
  Partials        11       11           
```

| Flag | Coverage Δ | |
|---|---|---|
| tests | `99.83% <100.00%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/2024?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev) | Coverage Δ | |
|---|---|---|
| [src/tox/tox\_env/python/pip/req/args.py](https://codecov.io/gh/tox-dev/tox/pull/2024/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev#diff-c3JjL3RveC90b3hfZW52L3B5dGhvbi9waXAvcmVxL2FyZ3MucHk=) | `100.00% <100.00%> (ø)` | |
| [tests/tox\_env/python/pip/req/test\_file.py](https://codecov.io/gh/tox-dev/tox/pull/2024/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev#diff-dGVzdHMvdG94X2Vudi9weXRob24vcGlwL3JlcS90ZXN0X2ZpbGUucHk=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/2024?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/2024?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev). Last update [5118401...dc80d19](https://codecov.io/gh/tox-dev/tox/pull/2024?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tox-dev).

jugmac00: There is a test failure on Ubuntu and Python 3.8... which seems unrelated.. hm...
gaborbernat: > There is a test failure on Ubuntu and Python 3.8... which seems unrelated.. hm...

I don't see anything 🤔 
eumiro: > > There is a test failure on Ubuntu and Python 3.8... which seems unrelated.. hm...
> 
> I don't see anything thinking

It's because I re-run the tests without changing anything in my PR. I also had an unrelated error while in my fork, only this time Ubuntu and Python 3.9:

https://github.com/eumiro/tox/runs/2325831238?check_suite_focus=true

```
_________________________ test_journal_enabled_default _________________________
[gw1] linux -- Python 3.9.4 /home/runner/work/tox/tox/.tox/4/py39/bin/python

base_info = {'host': 'fv-az231-641', 'platform': 'linux', 'reportversion': '1', 'toxversion': '4.0.0a2.dev199+gdc0f73c'}

    def test_journal_enabled_default(base_info: Dict[str, Any]) -> None:
        journal = Journal(enabled=True)
        assert bool(journal) is True
>       assert journal.content == base_info
E       AssertionError: assert {'host': 'fv-az231-641.internal.cloudapp.net',\n 'platform': 'linux',\n 'reportversion': '1',\n 'toxversion': '4.0.0a2.dev199+gdc0f73c'} == {'host': 'fv-az231-641',\n 'platform': 'linux',\n 'reportversion': '1',\n 'toxversion': '4.0.0a2.dev199+gdc0f73c'}
E         Common items:
E         {'platform': 'linux',
E          'reportversion': '1',
E          'toxversion': '4.0.0a2.dev199+gdc0f73c'}
E         Differing items:
E         {'host': 'fv-az231-641.internal.cloudapp.net'} != {'host': 'fv-az231-641'}
E         Full diff:
E           {
E         -  'host': 'fv-az231-641',
E         +  'host': 'fv-az231-641.internal.cloudapp.net',
E            'platform': 'linux',
E            'reportversion': '1',
E            'toxversion': '4.0.0a2.dev199+gdc0f73c',
E           }

base_info  = {'host': 'fv-az231-641',
 'platform': 'linux',
 'reportversion': '1',
 'toxversion': '4.0.0a2.dev199+gdc0f73c'}
journal    = <tox.journal.main.Journal object at 0x7f902048dc70>

tests/journal/test_main_journal.py:24: AssertionError
```

Patch:
diff --git a/src/tox/tox_env/python/pip/req/args.py b/src/tox/tox_env/python/pip/req/args.py
index 798c4223..e1dbd61e 100644
--- a/src/tox/tox_env/python/pip/req/args.py
+++ b/src/tox/tox_env/python/pip/req/args.py
@@ -49,7 +49,7 @@ def _req_options(parser: ArgumentParser, cli_only: bool) -> None:
         parser.add_argument("--hash", action=AddSortedUniqueAction, type=_validate_hash)
 
 
-_HASH = re.compile(r"sha(256:[a-z0-9]{64}|384:[a-z0-9]{96}|512:[a-z0-9]{128})")
+_HASH = re.compile(r"sha(256:[a-f0-9]{64}|384:[a-f0-9]{96}|512:[a-f0-9]{128})")
 
 
 def _validate_hash(value: str) -> str:


Test patch:
diff --git a/tests/tox_env/python/pip/req/test_file.py b/tests/tox_env/python/pip/req/test_file.py
index 51452793..16f1c1dd 100644
--- a/tests/tox_env/python/pip/req/test_file.py
+++ b/tests/tox_env/python/pip/req/test_file.py
@@ -372,11 +372,20 @@ def test_req_path_with_space_escape(tmp_path: Path) -> None:
     assert found == ["c"]
 
 
-def test_bad_hash(tmp_path: Path) -> None:
+@pytest.mark.parametrize(
+    "hash_value",
+    [
+        "sha256:a",
+        "sha256:xxxxxxxxxx123456789012345678901234567890123456789012345678901234",
+        "sha512:thisshouldfail8525e477385ef5ee6cecdc8f8fcc2a79d1b35a9f57ad15c814"
+        "dada670026f41fdd62e5e10b3fd75d6112704a9521c3df105f0b6f3bb11b128a",
+    ],
+)
+def test_bad_hash(hash_value: str, tmp_path: Path) -> None:
     requirements_txt = tmp_path / "req.txt"
-    requirements_txt.write_text("attrs --hash sha256:a")
+    requirements_txt.write_text(f"attrs --hash {hash_value}")
     req_file = RequirementsFile(requirements_txt, constraint=False)
-    with pytest.raises(ValueError, match="^argument --hash: sha256:a$"):
+    with pytest.raises(ValueError, match=f"^argument --hash: {hash_value}$"):
         assert req_file.requirements