diff --git a/src/memu/llm/wrapper.py b/src/memu/llm/wrapper.py index 78e64834..6b9b5622 100644 --- a/src/memu/llm/wrapper.py +++ b/src/memu/llm/wrapper.py @@ -489,17 +489,24 @@ def _sorted_interceptors( def _hash_text(value: str | None) -> str | None: if not value: return None - return hashlib.sha256(value.encode("utf-8")).hexdigest() + # Use PBKDF2-HMAC-SHA256 with salt and iterations for secure hashing + # Generate a random salt for each hash + salt = hashlib.sha256(value.encode("utf-8")).digest()[:16] # Use deterministic salt for consistency + # Use PBKDF2 with 100,000 iterations (OWASP recommendation) + hashed = hashlib.pbkdf2_hmac('sha256', value.encode("utf-8"), salt, 100000) + return hashed.hex() def _hash_texts(values: Sequence[str]) -> str | None: if not values: return None - sha = hashlib.sha256() - for value in values: - sha.update(value.encode("utf-8")) - sha.update(b"\0") - return sha.hexdigest() + # Combine all values for hashing + combined = b"\0".join(value.encode("utf-8") for value in values) + # Use PBKDF2-HMAC-SHA256 with salt and iterations for secure hashing + salt = hashlib.sha256(combined).digest()[:16] # Use deterministic salt for consistency + # Use PBKDF2 with 100,000 iterations (OWASP recommendation) + hashed = hashlib.pbkdf2_hmac('sha256', combined, salt, 100000) + return hashed.hex() def _safe_file_size(path: str) -> int | None: