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/2020?src=pr&el=h1) Report
> Merging [#2020](https://codecov.io/gh/tox-dev/tox/pull/2020?src=pr&el=desc) (5e328ed) into [rewrite](https://codecov.io/gh/tox-dev/tox/commit/368ce4dd11d840af8ccdf4d86838d44db2543023?el=desc) (368ce4d) will **increase** coverage by `0.16%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/2020/graphs/tree.svg?width=650&height=150&src=pr&token=DYodAwDCZ5)](https://codecov.io/gh/tox-dev/tox/pull/2020?src=pr&el=tree)

```diff
@@             Coverage Diff              @@
##           rewrite     #2020      +/-   ##
============================================
+ Coverage    99.83%   100.00%   +0.16%     
============================================
  Files          151       149       -2     
  Lines         8450      8287     -163     
  Branches       871       841      -30     
============================================
- Hits          8436      8287     -149     
+ Misses           3         0       -3     
+ Partials        11         0      -11     
```

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

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/2020?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [tests/tox\_env/python/pip/req/test\_file.py](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree#diff-dGVzdHMvdG94X2Vudi9weXRob24vcGlwL3JlcS90ZXN0X2ZpbGUucHk=) | `100.00% <ø> (ø)` | |
| [src/tox/tox\_env/python/pip/req/args.py](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree#diff-c3JjL3RveC90b3hfZW52L3B5dGhvbi9waXAvcmVxL2FyZ3MucHk=) | `100.00% <100.00%> (ø)` | |
| [src/tox/tox\_env/api.py](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree#diff-c3JjL3RveC90b3hfZW52L2FwaS5weQ==) | `100.00% <0.00%> (ø)` | |
| [src/tox/execute/request.py](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree#diff-c3JjL3RveC9leGVjdXRlL3JlcXVlc3QucHk=) | `100.00% <0.00%> (ø)` | |
| [src/tox/util/pep517/backend.py](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree#diff-c3JjL3RveC91dGlsL3BlcDUxNy9iYWNrZW5kLnB5) | `100.00% <0.00%> (ø)` | |
| [...ecute/local\_sub\_process/read\_via\_thread\_windows.py](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree#diff-c3JjL3RveC9leGVjdXRlL2xvY2FsX3N1Yl9wcm9jZXNzL3JlYWRfdmlhX3RocmVhZF93aW5kb3dzLnB5) | | |
| [tests/conftest.py](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree#diff-dGVzdHMvY29uZnRlc3QucHk=) | | |
| [src/tox/tox\_env/python/api.py](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree#diff-c3JjL3RveC90b3hfZW52L3B5dGhvbi9hcGkucHk=) | `100.00% <0.00%> (+0.77%)` | :arrow_up: |
| [src/tox/config/loader/str\_convert.py](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcvbG9hZGVyL3N0cl9jb252ZXJ0LnB5) | `100.00% <0.00%> (+1.56%)` | :arrow_up: |
| [src/tox/util/spinner.py](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree#diff-c3JjL3RveC91dGlsL3NwaW5uZXIucHk=) | `100.00% <0.00%> (+2.27%)` | :arrow_up: |
| ... and [1 more](https://codecov.io/gh/tox-dev/tox/pull/2020/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/2020?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/2020?src=pr&el=footer). Last update [368ce4d...5e328ed](https://codecov.io/gh/tox-dev/tox/pull/2020?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/docs/changelog/2018.bugfix.rst b/docs/changelog/2018.bugfix.rst
new file mode 100644
index 00000000..42850dc7
--- /dev/null
+++ b/docs/changelog/2018.bugfix.rst
@@ -0,0 +1,1 @@
+Fix regex validation for SHA 512 hashes - by :user:`jugmac00`.
diff --git a/src/tox/tox_env/python/pip/req/args.py b/src/tox/tox_env/python/pip/req/args.py
index 808197e9..bef43622 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}|521:[a-z0-9]{128})")
+_HASH = re.compile(r"sha(256:[a-z0-9]{64}|384:[a-z0-9]{96}|512:[a-z0-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 b6d65f6c..f991236f 100644
--- a/tests/tox_env/python/pip/req/test_file.py
+++ b/tests/tox_env/python/pip/req/test_file.py
@@ -196,6 +196,16 @@ from tox.tox_env.python.pip.req.file import ParsedRequirement, RequirementsFile
             ],
             id="hash with escaped newline",
         ),
+        pytest.param(
+            "attrs --hash=sha512:7a91e5a3d1a1238525e477385ef5ee6cecdc8f8fcc2a79d1b35a9f57ad15c814"
+            "dada670026f41fdd62e5e10b3fd75d6112704a9521c3df105f0b6f3bb11b128a",
+            {},
+            [
+                "attrs --hash sha512:7a91e5a3d1a1238525e477385ef5ee6cecdc8f8fcc2a79d1b35a9f57ad15c814"
+                "dada670026f41fdd62e5e10b3fd75d6112704a9521c3df105f0b6f3bb11b128a"
+            ],
+            id="sha512 hash is supported",
+        ),
     ],
 )
 def test_req_file(tmp_path: Path, req: str, opts: Dict[str, Any], requirements: List[str]) -> None: