Fix code review issues: remove duplicate dependency, improve error handling, fix hardcoded credentials, improve encryption salt

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-01 12:55:26 +00:00
parent 78731566c9
commit bb535028cc
4 changed files with 34 additions and 13 deletions
+15 -2
View File
@@ -68,19 +68,32 @@ def generate_random_token(length: int = 32) -> str:
class CredentialEncryption: class CredentialEncryption:
"""Handles encryption/decryption of sensitive credentials (POP3/IMAP passwords)""" """Handles encryption/decryption of sensitive credentials (POP3/IMAP passwords)"""
def __init__(self, key: Optional[str] = None): def __init__(self, key: Optional[str] = None, user_id: Optional[int] = None):
""" """
Initialize encryption with a key. Initialize encryption with a key.
If no key provided, uses the one from settings. If no key provided, uses the one from settings.
In production, use a unique salt per user for enhanced security.
Args:
key: Encryption key (defaults to settings.ENCRYPTION_KEY)
user_id: Optional user ID for per-user salt generation
""" """
if key is None: if key is None:
key = settings.ENCRYPTION_KEY key = settings.ENCRYPTION_KEY
# Generate salt - in production, this should be unique per user
if user_id is not None:
# Per-user salt for production
salt = f'pop3_forwarder_user_{user_id}'.encode('utf-8')[:16].ljust(16, b'0')
else:
# Default salt for system-wide operations (use with caution)
salt = b'pop3_forwarder_0'
# Derive a proper Fernet key from the provided key # Derive a proper Fernet key from the provided key
kdf = PBKDF2( kdf = PBKDF2(
algorithm=hashes.SHA256(), algorithm=hashes.SHA256(),
length=32, length=32,
salt=b'pop3_forwarder_salt', # In production, use unique salt per user salt=salt,
iterations=100000, iterations=100000,
) )
key_bytes = key.encode('utf-8') key_bytes = key.encode('utf-8')
+2 -2
View File
@@ -375,8 +375,8 @@ class MailProcessor:
finally: finally:
try: try:
server.quit() server.quit()
except: except Exception as e:
pass logger.warning(f"Error closing SMTP connection: {e}")
return await loop.run_in_executor(None, send_email) return await loop.run_in_executor(None, send_email)
+17 -8
View File
@@ -2,6 +2,7 @@
Celery tasks for background email processing. Celery tasks for background email processing.
""" """
import asyncio import asyncio
import os
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import List from typing import List
from celery import Task from celery import Task
@@ -23,8 +24,8 @@ class AsyncTask(Task):
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
"""Run async task in event loop""" """Run async task in event loop"""
loop = asyncio.get_event_loop() # Use asyncio.run() for better event loop management
return loop.run_until_complete(self.run(*args, **kwargs)) return asyncio.run(self.run(*args, **kwargs))
@celery_app.task(base=AsyncTask, name="app.workers.tasks.process_mail_account") @celery_app.task(base=AsyncTask, name="app.workers.tasks.process_mail_account")
@@ -72,15 +73,23 @@ async def process_mail_account(account_id: int):
emails_forwarded = 0 emails_forwarded = 0
emails_failed = 0 emails_failed = 0
# TODO: Get SMTP config from user settings or environment # Get SMTP config from environment or user settings
# TODO: Make this configurable per user in the database
smtp_config = { smtp_config = {
"host": "smtp.gmail.com", "host": os.getenv("SMTP_HOST", "smtp.gmail.com"),
"port": 587, "port": int(os.getenv("SMTP_PORT", "587")),
"username": "smtp_user@gmail.com", # Should come from config "username": os.getenv("SMTP_USER", ""),
"password": "smtp_password", # Should come from config "password": os.getenv("SMTP_PASSWORD", ""),
"use_tls": True "use_tls": os.getenv("SMTP_USE_TLS", "true").lower() == "true"
} }
if not smtp_config["username"] or not smtp_config["password"]:
logger.error(f"SMTP credentials not configured for account {account.id}")
run.status = "failed"
run.error_message = "SMTP credentials not configured"
await db.commit()
return
for email_data in emails: for email_data in emails:
try: try:
success = await MailProcessor.forward_email( success = await MailProcessor.forward_email(
-1
View File
@@ -44,7 +44,6 @@ python-json-logger==2.0.7
pytest==7.4.4 pytest==7.4.4
pytest-asyncio==0.23.3 pytest-asyncio==0.23.3
pytest-cov==4.1.0 pytest-cov==4.1.0
httpx==0.26.0
faker==22.6.0 faker==22.6.0
# Utilities # Utilities