Skip to content

Commit 20c6d38

Browse files
committed
try catch for security flag, unit tests added
1 parent ab37ba5 commit 20c6d38

File tree

2 files changed

+64
-6
lines changed

2 files changed

+64
-6
lines changed

pyartifactory/models/artifact.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import hashlib
77
from datetime import datetime
88
from pathlib import Path
9-
from typing import Any, Callable, Dict, List, Literal, Optional, Union
9+
from typing import Callable, Dict, List, Literal, Optional, Union
1010

1111
from pydantic import BaseModel
1212

@@ -18,18 +18,28 @@ class Checksums(BaseModel):
1818
md5: str
1919
sha256: str
2020

21+
@staticmethod
22+
def get_hasher(func: Callable[..., hashlib._Hash]) -> hashlib._Hash:
23+
# In Python 3.9+, some hash algorithms (like md5, sha1, sha256) may be disabled in FIPS-compliant systems
24+
# unless 'usedforsecurity=False' is specified. This flag allows the hash function to be used for non-security
25+
# purposes such as checksums, even in FIPS environments.
26+
try:
27+
return func(usedforsecurity=False)
28+
except TypeError:
29+
return func()
30+
2131
@classmethod
2232
def generate(cls, file_: Path) -> Checksums:
2333
block_size: int = 65536
24-
mapping: dict[str, Callable[[], Any]] = {
25-
"md5": lambda: hashlib.md5(usedforsecurity=False),
26-
"sha1": lambda: hashlib.sha1(usedforsecurity=False),
27-
"sha256": lambda: hashlib.sha256(usedforsecurity=False),
34+
mapping = {
35+
"md5": hashlib.md5,
36+
"sha1": hashlib.sha1,
37+
"sha256": hashlib.sha256,
2838
}
2939
results = {}
3040

3141
for algorithm, hashing_function in mapping.items():
32-
hasher = hashing_function()
42+
hasher = cls.get_hasher(hashing_function)
3343
with file_.absolute().open("rb") as fd:
3444
buf = fd.read(block_size)
3545
while len(buf) > 0:

tests/test_artifacts.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,54 @@ def test_checksum_defined_file(file_path: Path, expected_sha1: str, expected_md5
612612
assert result == expected
613613

614614

615+
def test_get_hasher_security_flag():
616+
calls = {"kwargs": None}
617+
618+
class Dummy:
619+
def __init__(self):
620+
self._buf = b""
621+
622+
def update(self, b):
623+
self._buf += b
624+
625+
def hexdigest(self):
626+
return "ok"
627+
628+
def func(**kwargs):
629+
calls["kwargs"] = kwargs
630+
return Dummy()
631+
632+
hasher = Checksums.get_hasher(func)
633+
assert isinstance(hasher, Dummy)
634+
assert calls["kwargs"] == {"usedforsecurity": False}
635+
636+
637+
def test_get_hasher_type_error_thrown():
638+
calls = {"with_kwargs": 0, "without_kwargs": 0}
639+
640+
class Dummy:
641+
def __init__(self):
642+
self._buf = b""
643+
644+
def update(self, b):
645+
self._buf += b
646+
647+
def hexdigest(self):
648+
return "ok"
649+
650+
def func(**kwargs):
651+
if kwargs:
652+
calls["with_kwargs"] += 1
653+
raise TypeError("unexpected kwarg")
654+
calls["without_kwargs"] += 1
655+
return Dummy()
656+
657+
hasher = Checksums.get_hasher(func)
658+
assert isinstance(hasher, Dummy)
659+
assert calls["with_kwargs"] == 1
660+
assert calls["without_kwargs"] == 1
661+
662+
615663
@responses.activate
616664
def test_deploy_artifact_with_checksum_success(mocker):
617665
responses.add(responses.PUT, f"{URL}/{ARTIFACT_PATH}", status=200)

0 commit comments

Comments
 (0)