Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions agent-framework/prometheus_swarm/clients/web_client_nonce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import random
import string
from typing import Optional

class WebClientNonceManager:
"""
A utility class for generating and managing web client nonces.

Nonces (number used once) are random, unique tokens used to prevent
replay attacks and ensure request uniqueness.
"""

@staticmethod
def generate_nonce(length: int = 32) -> str:
"""
Generate a cryptographically secure random nonce.

Args:
length (int, optional): Length of the nonce. Defaults to 32.

Returns:
str: A randomly generated nonce string.

Raises:
ValueError: If length is less than 16.
"""
if length < 16:
raise ValueError("Nonce length must be at least 16 characters")

# Use a combination of uppercase, lowercase, and digits
characters = string.ascii_letters + string.digits

# Use SystemRandom for cryptographically secure random generation
secure_random = random.SystemRandom()

nonce = ''.join(secure_random.choice(characters) for _ in range(length))
return nonce

@staticmethod
def validate_nonce(nonce: Optional[str], min_length: int = 16, max_length: int = 64) -> bool:
"""
Validate a nonce's format and characteristics.

Args:
nonce (Optional[str]): The nonce to validate.
min_length (int, optional): Minimum acceptable nonce length. Defaults to 16.
max_length (int, optional): Maximum acceptable nonce length. Defaults to 64.

Returns:
bool: True if nonce is valid, False otherwise.
"""
if nonce is None:
return False

# Check length constraints
if not (min_length <= len(nonce) <= max_length):
return False

# Ensure nonce contains only valid characters
return all(char in string.ascii_letters + string.digits for char in nonce)
51 changes: 51 additions & 0 deletions agent-framework/tests/unit/test_web_client_nonce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import pytest
from prometheus_swarm.clients.web_client_nonce import WebClientNonceManager

def test_generate_nonce_default_length():
"""Test nonce generation with default length."""
nonce = WebClientNonceManager.generate_nonce()
assert len(nonce) == 32
assert WebClientNonceManager.validate_nonce(nonce)

def test_generate_nonce_custom_length():
"""Test nonce generation with custom length."""
for length in [16, 24, 48, 64]:
nonce = WebClientNonceManager.generate_nonce(length)
assert len(nonce) == length
assert WebClientNonceManager.validate_nonce(nonce)

def test_generate_nonce_invalid_length():
"""Test nonce generation fails for too short nonces."""
with pytest.raises(ValueError):
WebClientNonceManager.generate_nonce(15)

def test_nonce_uniqueness():
"""Verify generated nonces are unique."""
nonces = set(WebClientNonceManager.generate_nonce() for _ in range(100))
assert len(nonces) == 100

def test_nonce_validation():
"""Test nonce validation with various inputs."""
# Valid nonces
valid_cases = [
WebClientNonceManager.generate_nonce(),
WebClientNonceManager.generate_nonce(16),
WebClientNonceManager.generate_nonce(64)
]

# Invalid nonces
invalid_cases = [
None,
"", # Empty string
"short", # Too short
"x" * 65, # Too long
"invalid-nonce!", # Special characters
"UPPERCASE_ONLY", # Case sensitivity
" spaces "
]

for nonce in valid_cases:
assert WebClientNonceManager.validate_nonce(nonce), f"Failed to validate: {nonce}"

for nonce in invalid_cases:
assert not WebClientNonceManager.validate_nonce(nonce), f"Incorrectly validated: {nonce}"