fix: resolve logical errors, bugs, and security issues across codebase

- Fix is_admin() method shadowing is_admin database column in User model
- Fix check_password() crash when password_hash is None (OAuth-only users)
- Fix SystemSetting.all_settings() formatting error (missing newline)
- Fix MAIL_PORT returning string instead of int in config
- Fix AUTOMATION_TOKEN config formatting (missing newline before comment)
- Fix path traversal vulnerability in serve_user_audio using realpath validation
- Fix weak auth in process.py, replace session check with @login_required
- Fix int() crash on non-numeric priority in import_songs.py
- Add timeout to SMTP connection in email_helper.py
- Add timeouts to external API requests in metadata.py and spotify_helper.py
- Fix security tests to properly reload config module
- Fix metadata test mock data key mismatch (preview_url -> spotify_preview_url)
- Add skip decorator to integration test requiring live API credentials

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-12 00:13:30 +00:00
parent 4685e05860
commit cfe000f030
15 changed files with 66 additions and 46 deletions
+1 -1
View File
@@ -82,7 +82,7 @@ def send_email(recipient, subject, body_text, attachments=None):
try:
current_app.logger.info(f"Attempting to send email to {recipient} via {mail_host}:{mail_port}")
with smtplib.SMTP(mail_host, mail_port) as server:
with smtplib.SMTP(mail_host, mail_port, timeout=30) as server:
server.starttls()
current_app.logger.debug("STARTTLS established")
server.login(mail_username, mail_password)
+4 -4
View File
@@ -504,7 +504,7 @@ def get_deezer_data(isrc, app=None):
if not deezer_client:
# If no client in app context, make direct API call
response = requests.get(f"https://api.deezer.com/track/isrc:{isrc}")
response = requests.get(f"https://api.deezer.com/track/isrc:{isrc}", timeout=10)
if response.status_code == 200:
track = response.json()
else:
@@ -539,7 +539,7 @@ def get_deezer_data(isrc, app=None):
if deezer_client:
album = deezer_client.get_album(album_id)
else:
album_response = requests.get(f"https://api.deezer.com/album/{album_id}")
album_response = requests.get(f"https://api.deezer.com/album/{album_id}", timeout=10)
album = album_response.json() if album_response.status_code == 200 else None
if album and not album.get('error'):
@@ -591,7 +591,7 @@ def get_lastfm_data(artist_name, track_title, app=None):
'format': 'json'
}
response = requests.get(url=url, params=params)
response = requests.get(url=url, params=params, timeout=10)
if response.status_code == 200:
data = response.json()
@@ -753,7 +753,7 @@ def get_acrcloud_data(isrc, app=None):
'include_works': 1 # Include additional work metadata
}
response = requests.get(url, headers=headers, params=params)
response = requests.get(url, headers=headers, params=params, timeout=10)
if response.status_code != 200:
app.logger.warning(f"ACRCloud API error: {response.status_code} - {response.text}")
return result
+2 -2
View File
@@ -26,7 +26,7 @@ def refresh_spotify_token(refresh_token):
'client_secret': client_secret
}
response = requests.post('https://accounts.spotify.com/api/token', data=data)
response = requests.post('https://accounts.spotify.com/api/token', data=data, timeout=10)
if response.status_code == 200:
return response.json()
@@ -181,7 +181,7 @@ def get_spotify_user_info(access_token):
}
try:
response = requests.get('https://api.spotify.com/v1/me', headers=headers)
response = requests.get('https://api.spotify.com/v1/me', headers=headers, timeout=10)
if response.status_code == 200:
return response.json()