From 86378c95fb4940b6c3b0365b2ffb91fa7c9f1d1a Mon Sep 17 00:00:00 2001 From: Simon Fayer Date: Tue, 2 Jun 2026 10:25:42 +0100 Subject: [PATCH] fix: Check all rand module usage --- src/DIRAC/AccountingSystem/Client/DataStoreClient.py | 2 +- src/DIRAC/AccountingSystem/DB/AccountingDB.py | 4 +++- src/DIRAC/Core/DISET/MessageClient.py | 2 +- src/DIRAC/Core/Security/m2crypto/X509Certificate.py | 11 ++++++----- src/DIRAC/Core/Utilities/File.py | 4 ++-- src/DIRAC/Core/Utilities/LockRing.py | 4 ++-- src/DIRAC/Core/Utilities/ThreadPool.py | 8 ++++---- .../private/FTS3Plugins/DefaultFTS3Plugin.py | 4 ++-- .../private/HttpStorageAccessHandler.py | 2 +- src/DIRAC/RequestManagementSystem/Client/ReqClient.py | 2 +- .../Utilities/RSSCacheNoThread.py | 2 +- src/DIRAC/Resources/Computing/BatchSystems/SLURM.py | 2 +- .../Resources/ProxyProvider/DIRACCAProxyProvider.py | 4 ++-- src/DIRAC/Resources/Storage/EchoStorage.py | 2 +- .../Client/StorageManagerClient.py | 2 +- src/DIRAC/WorkloadManagementSystem/DB/TaskQueueDB.py | 2 +- 16 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/DIRAC/AccountingSystem/Client/DataStoreClient.py b/src/DIRAC/AccountingSystem/Client/DataStoreClient.py index 573daa76e5e..7a5d1a63fec 100644 --- a/src/DIRAC/AccountingSystem/Client/DataStoreClient.py +++ b/src/DIRAC/AccountingSystem/Client/DataStoreClient.py @@ -127,7 +127,7 @@ def _sendToFailover(rpcStub): """Create a ForwardDISET operation for failover""" try: request = Request() - request.RequestName = f"Accounting.DataStore.{time.time()}.{random.random()}" + request.RequestName = f"Accounting.DataStore.{time.time()}.{random.random()}" # nosec B311 forwardDISETOp = Operation() forwardDISETOp.Type = "ForwardDISET" forwardDISETOp.Arguments = DEncode.encode(rpcStub) diff --git a/src/DIRAC/AccountingSystem/DB/AccountingDB.py b/src/DIRAC/AccountingSystem/DB/AccountingDB.py index 1662de1177d..7586725b434 100644 --- a/src/DIRAC/AccountingSystem/DB/AccountingDB.py +++ b/src/DIRAC/AccountingSystem/DB/AccountingDB.py @@ -45,7 +45,9 @@ def __init__(self, name="Accounting/AccountingDB", readOnly=False, parentLogger= ) self.__loadCatalogFromDB() - self.__compactTime = datetime.time(hour=2, minute=random.randint(0, 59), second=random.randint(0, 59)) + self.__compactTime = datetime.time( + hour=2, minute=random.randint(0, 59), second=random.randint(0, 59) # nosec B311 + ) lcd = datetime.datetime.utcnow() lcd.replace(hour=self.__compactTime.hour + 1, minute=0, second=0) self.__lastCompactionEpoch = TimeUtilities.toEpoch(lcd) diff --git a/src/DIRAC/Core/DISET/MessageClient.py b/src/DIRAC/Core/DISET/MessageClient.py index 03a249de512..d439bca1599 100755 --- a/src/DIRAC/Core/DISET/MessageClient.py +++ b/src/DIRAC/Core/DISET/MessageClient.py @@ -29,7 +29,7 @@ def _initialize(self): def __generateUniqueClientName(self): hashStr = ":".join( - (str(datetime.datetime.utcnow()), str(random.random()), Network.getFQDN(), gLogger.getName()) + (str(datetime.datetime.utcnow()), str(random.random()), Network.getFQDN(), gLogger.getName()) # nosec B311 ) hexHash = md5(hashStr.encode(), usedforsecurity=False).hexdigest() return hexHash diff --git a/src/DIRAC/Core/Security/m2crypto/X509Certificate.py b/src/DIRAC/Core/Security/m2crypto/X509Certificate.py index 0e47bdb8d8e..6e9f10d3b05 100644 --- a/src/DIRAC/Core/Security/m2crypto/X509Certificate.py +++ b/src/DIRAC/Core/Security/m2crypto/X509Certificate.py @@ -7,7 +7,7 @@ """ import datetime import os -import random +import secrets import time import M2Crypto.m2 @@ -85,10 +85,11 @@ def generateProxyCertFromIssuer(cls, x509Issuer, x509ExtensionStack, proxyKey, l proxyCert.__certObj = M2Crypto.X509.X509() - # According to the proxy RFC, the serial - # number just need to be uniqu among the proxy generated by the issuer. - # The random module of python will be good enough for that - serial = int(random.random() * 10**10) + # According to the proxy RFC, the serial number just needs to be unique + # among the proxy generated by the issuer. + # We need to avoid birthday-style collisions for a given cert (such as + # the pilot) which may issue thousands of proxies per year. + serial = secrets.randbits(64) proxyCert.__certObj.set_serial_number(serial) # No easy way to deep-copy certificate subject, since they are swig object diff --git a/src/DIRAC/Core/Utilities/File.py b/src/DIRAC/Core/Utilities/File.py index 0df45a5765f..e7a49405212 100755 --- a/src/DIRAC/Core/Utilities/File.py +++ b/src/DIRAC/Core/Utilities/File.py @@ -78,7 +78,7 @@ def makeGuid(fileName=None): except Exception: return None else: - myMd5.update(str(random.getrandbits(128)).encode()) + myMd5.update(str(random.getrandbits(128)).encode()) # nosec B311 md5HexString = myMd5.hexdigest().upper() return generateGuid(md5HexString, "MD5") @@ -107,7 +107,7 @@ def generateGuid(checksum, checksumtype): # Failed to use the check sum, generate a new guid myMd5 = hashlib.md5(usedforsecurity=False) - myMd5.update(str(random.getrandbits(128)).encode()) + myMd5.update(str(random.getrandbits(128)).encode()) # nosec B311 md5HexString = myMd5.hexdigest() guid = "{}-{}-{}-{}-{}".format( md5HexString[0:8], diff --git a/src/DIRAC/Core/Utilities/LockRing.py b/src/DIRAC/Core/Utilities/LockRing.py index def75d675fd..e8d1f8a7fcb 100644 --- a/src/DIRAC/Core/Utilities/LockRing.py +++ b/src/DIRAC/Core/Utilities/LockRing.py @@ -15,10 +15,10 @@ def __init__(self): def __genName(self, container): # TODO: Shouldn't this be a UUID? - name = md5(str(time.time() + random.random()).encode(), usedforsecurity=False).hexdigest() + name = md5(str(time.time() + random.random()).encode(), usedforsecurity=False).hexdigest() # nosec B311 retries = 10 while name in container and retries: - name = md5(str(time.time() + random.random()).encode(), usedforsecurity=False).hexdigest() + name = md5(str(time.time() + random.random()).encode(), usedforsecurity=False).hexdigest() # nosec B311 retries -= 1 return name diff --git a/src/DIRAC/Core/Utilities/ThreadPool.py b/src/DIRAC/Core/Utilities/ThreadPool.py index 35044b4db7b..5f904e046b3 100755 --- a/src/DIRAC/Core/Utilities/ThreadPool.py +++ b/src/DIRAC/Core/Utilities/ThreadPool.py @@ -298,8 +298,8 @@ def getGlobalThreadPool(): import random def doSomething(iNumber): - time.sleep(random.randint(1, 5)) - fResult = random.random() * iNumber + time.sleep(random.randint(1, 5)) # nosec B311 + fResult = random.random() * iNumber # nosec B311 if fResult > 3: raise Exception("TEST EXCEPTION") return fResult @@ -313,7 +313,7 @@ def showException(oTJ, exc_info): OTP = ThreadPool(5, 10) def generateWork(iWorkUnits): - for iNumber in [random.randint(1, 20) for _ in range(iWorkUnits)]: + for iNumber in [random.randint(1, 20) for _ in range(iWorkUnits)]: # nosec B311 oTJ = ThreadedJob(doSomething, args=(iNumber,), oCallback=showResult, oExceptionCallback=showException) OTP.queueJob(oTJ) @@ -324,7 +324,7 @@ def generateWork(iWorkUnits): while True: time.sleep(1) gIResult = OTP.processResults() - gINew = gIResult + random.randint(-3, 2) + gINew = gIResult + random.randint(-3, 2) # nosec B311 print(f"Processed {gIResult}, generating {gINew}..") generateWork(gINew) print(f"Threads {OTP.numWorkingThreads()}", OTP.pendingJobs()) diff --git a/src/DIRAC/DataManagementSystem/private/FTS3Plugins/DefaultFTS3Plugin.py b/src/DIRAC/DataManagementSystem/private/FTS3Plugins/DefaultFTS3Plugin.py index c058b7897f1..0e58b23cb94 100644 --- a/src/DIRAC/DataManagementSystem/private/FTS3Plugins/DefaultFTS3Plugin.py +++ b/src/DIRAC/DataManagementSystem/private/FTS3Plugins/DefaultFTS3Plugin.py @@ -103,8 +103,8 @@ def selectSourceSE(self, ftsFile, replicaDict, allowedSources): raise ValueError("No valid replicas") # pick a random source - - randSource = random.choice(list(allowedReplicaSource)) # one has to convert to list + # (choice requires a list) + randSource = random.choice(list(allowedReplicaSource)) # nosec B311 return randSource def inferFTSActivity(self, ftsOperation, rmsRequest, rmsOperation): diff --git a/src/DIRAC/DataManagementSystem/private/HttpStorageAccessHandler.py b/src/DIRAC/DataManagementSystem/private/HttpStorageAccessHandler.py index 104e306a172..a1b6c6d76f6 100644 --- a/src/DIRAC/DataManagementSystem/private/HttpStorageAccessHandler.py +++ b/src/DIRAC/DataManagementSystem/private/HttpStorageAccessHandler.py @@ -36,7 +36,7 @@ def do_GET(self): path = os.path.join(cache_path, fileList[0]) else: # multiple files, make archive - unique = str(random.getrandbits(24)) + unique = str(random.getrandbits(24)) # nosec B311 fileString = " ".join(fileList) tar_path = os.path.join(cache_path, f"dirac_data_{unique}.tar") with tarfile.open(tar_path, "w") as tar: diff --git a/src/DIRAC/RequestManagementSystem/Client/ReqClient.py b/src/DIRAC/RequestManagementSystem/Client/ReqClient.py index 9a9e9c6af9b..d7796da1363 100755 --- a/src/DIRAC/RequestManagementSystem/Client/ReqClient.py +++ b/src/DIRAC/RequestManagementSystem/Client/ReqClient.py @@ -106,7 +106,7 @@ def putRequest(self, request, useFailoverProxy=True, retryMainService=0): return setRequestMgr errorsDict["RequestManager"] = setRequestMgr["Message"] # sleep a bit - time.sleep(random.randint(1, 5)) + time.sleep(random.randint(1, 5)) # nosec B311 self.log.warn( f"putRequest: unable to set request '{request.RequestName}' at RequestManager", setRequestMgr["Message"] diff --git a/src/DIRAC/ResourceStatusSystem/Utilities/RSSCacheNoThread.py b/src/DIRAC/ResourceStatusSystem/Utilities/RSSCacheNoThread.py index 0c0478151e6..2fdbfa2c839 100644 --- a/src/DIRAC/ResourceStatusSystem/Utilities/RSSCacheNoThread.py +++ b/src/DIRAC/ResourceStatusSystem/Utilities/RSSCacheNoThread.py @@ -40,7 +40,7 @@ def __init__(self, lifeTime, updateFunc): # We set a 20% of the lifetime randomly, so that if we have thousands of jobs # starting at the same time, all the caches will not end at the same time. - randomLifeTimeBias = 0.2 * random.random() + randomLifeTimeBias = 0.2 * random.random() # nosec B311 self.log = gLogger.getSubLogger(self.__class__.__name__) diff --git a/src/DIRAC/Resources/Computing/BatchSystems/SLURM.py b/src/DIRAC/Resources/Computing/BatchSystems/SLURM.py index 7016f2f63ae..f7f4fc7507a 100644 --- a/src/DIRAC/Resources/Computing/BatchSystems/SLURM.py +++ b/src/DIRAC/Resources/Computing/BatchSystems/SLURM.py @@ -122,7 +122,7 @@ def _generateSrunWrapper(self, executableFile): :param str executableFile: name of the executable file to wrap :return str: name of the wrapper that runs the executable via srun """ - suffix = random.randrange(1, 99999) + suffix = random.randrange(1, 99999) # nosec B311 wrapper = os.path.join(os.path.dirname(executableFile), "srunExec_%s.sh" % suffix) with open(executableFile, "r") as f: diff --git a/src/DIRAC/Resources/ProxyProvider/DIRACCAProxyProvider.py b/src/DIRAC/Resources/ProxyProvider/DIRACCAProxyProvider.py index b6d23dfd136..37f75140487 100644 --- a/src/DIRAC/Resources/ProxyProvider/DIRACCAProxyProvider.py +++ b/src/DIRAC/Resources/ProxyProvider/DIRACCAProxyProvider.py @@ -32,7 +32,7 @@ """ import re import time -import random +import secrets import datetime import collections @@ -373,7 +373,7 @@ def __createCertM2Crypto(self): userCert.set_pubkey(userPubKey) userCert.set_version(2) userCert.set_subject(self.__X509Name) - userCert.set_serial_number(int(random.random() * 10**10)) + userCert.set_serial_number(secrets.randbits(64)) # Add extentionals userCert.add_ext(X509.new_extension("basicConstraints", "CA:" + str(False).upper())) userCert.add_ext(X509.new_extension("extendedKeyUsage", "clientAuth", critical=1)) diff --git a/src/DIRAC/Resources/Storage/EchoStorage.py b/src/DIRAC/Resources/Storage/EchoStorage.py index b25b894b39c..772402db91b 100644 --- a/src/DIRAC/Resources/Storage/EchoStorage.py +++ b/src/DIRAC/Resources/Storage/EchoStorage.py @@ -175,7 +175,7 @@ def _removeSingleFile(self, path): # If it took too long, we sleep for a bit if duration > REMOVAL_DURATION_THROTTLE_LIMIT: - time.sleep(random.uniform(0, 3)) + time.sleep(random.uniform(0, 3)) # nosec B311 def __addDoubleSlash(self, res): """Utilities to add the double slash between the host(:port) and the path for xroot only diff --git a/src/DIRAC/StorageManagementSystem/Client/StorageManagerClient.py b/src/DIRAC/StorageManagementSystem/Client/StorageManagerClient.py index 33480f07c1b..0424b069ea8 100644 --- a/src/DIRAC/StorageManagementSystem/Client/StorageManagerClient.py +++ b/src/DIRAC/StorageManagementSystem/Client/StorageManagerClient.py @@ -115,7 +115,7 @@ def getFilesToStage(lfnList, jobState=None, checkOnlyTapeSEs=None, jobLog=None): break # No online site found in common, select randomly if not found: - offlineLFNsDict.setdefault(random.choice(ses), list()).append(lfn) + offlineLFNsDict.setdefault(random.choice(ses), list()).append(lfn) # nosec B311 return S_OK( { diff --git a/src/DIRAC/WorkloadManagementSystem/DB/TaskQueueDB.py b/src/DIRAC/WorkloadManagementSystem/DB/TaskQueueDB.py index 18282c0a962..98c2bc3bcc5 100755 --- a/src/DIRAC/WorkloadManagementSystem/DB/TaskQueueDB.py +++ b/src/DIRAC/WorkloadManagementSystem/DB/TaskQueueDB.py @@ -655,7 +655,7 @@ def matchAndGetJob(self, tqMatchDict, numJobsPerTry=50, numQueuesPerTry=10, nega self.log.info("Task queue seems to be empty, triggering a cleaning of", tqId) self.__deleteTQWithDelay.add(tqId, 300, (tqId, tqOwner, tqOwnerGroup)) while jobTQList: - jobId, tqId = jobTQList.pop(random.randint(0, len(jobTQList) - 1)) + jobId, tqId = jobTQList.pop(random.randint(0, len(jobTQList) - 1)) # nosec B311 self.log.verbose("Trying to extract job from TQ", f"{jobId} : {tqId}") retVal = self.deleteJob(jobId, connObj=connObj) if not retVal["OK"]: