Fix Critical Security Issue: Replace print() with proper logging

Replaced all debug print() statements with proper logging framework:
- tasks/views.py: 5 print statements → logger.debug()
- sync/views.py: 15 print statements → logger.debug()
- config/celery.py: 1 print statement → logger.debug()
- notifications/tasks.py: 2 print statements → logger.error()

Retained print() in settings files (development.py, selfhosted.py) as they are appropriate warnings to stderr for configuration issues.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Keith Smith
2025-12-22 22:34:57 -07:00
co-authored by Claude Sonnet 4.5
parent a22c3eed50
commit b57321e1e4
4 changed files with 34 additions and 22 deletions
+4 -1
View File
@@ -2,10 +2,13 @@
Celery configuration for KeepItGoing. Celery configuration for KeepItGoing.
""" """
import logging
import os import os
from celery import Celery from celery import Celery
from celery.schedules import crontab from celery.schedules import crontab
logger = logging.getLogger(__name__)
# Set the default Django settings module # Set the default Django settings module
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
@@ -32,4 +35,4 @@ app.conf.beat_schedule = {
@app.task(bind=True) @app.task(bind=True)
def debug_task(self): def debug_task(self):
print(f'Request: {self.request!r}') logger.debug(f'Request: {self.request!r}')
+5 -2
View File
@@ -2,10 +2,13 @@
Celery tasks for notifications. Celery tasks for notifications.
""" """
import logging
from celery import shared_task from celery import shared_task
from django.utils import timezone from django.utils import timezone
from datetime import timedelta from datetime import timedelta
logger = logging.getLogger(__name__)
@shared_task @shared_task
def send_due_reminders(): def send_due_reminders():
@@ -157,7 +160,7 @@ def send_fcm_notification(token, title, body, data=None):
except Exception as e: except Exception as e:
# Log error but don't fail # Log error but don't fail
print(f"FCM notification failed: {e}") logger.error(f"FCM notification failed: {e}")
@shared_task @shared_task
@@ -187,7 +190,7 @@ def send_web_push_notification(subscription_info, title, body, data=None):
) )
except Exception as e: except Exception as e:
print(f"Web push notification failed: {e}") logger.error(f"Web push notification failed: {e}")
@shared_task @shared_task
+17 -14
View File
@@ -4,6 +4,7 @@ Sync views for KeepItGoing.
Handles data synchronization between clients and server. Handles data synchronization between clients and server.
""" """
import logging
import uuid import uuid
from datetime import datetime from datetime import datetime
from django.utils import timezone from django.utils import timezone
@@ -25,6 +26,8 @@ from tasks.serializers import (
from .models import SyncLog, SyncConflict from .models import SyncLog, SyncConflict
from .serializers import SyncConflictSerializer from .serializers import SyncConflictSerializer
logger = logging.getLogger(__name__)
@api_view(['POST']) @api_view(['POST'])
@permission_classes([IsAuthenticated]) @permission_classes([IsAuthenticated])
@@ -64,7 +67,7 @@ def sync(request):
# Debug logging # Debug logging
task_changes = changes.get('tasks', []) task_changes = changes.get('tasks', [])
deleted_task_count = sum(1 for t in task_changes if t.get('is_deleted', False)) deleted_task_count = sum(1 for t in task_changes if t.get('is_deleted', False))
print(f"[SERVER SYNC DEBUG] Received sync request with {len(task_changes)} tasks, {deleted_task_count} marked as deleted") logger.debug(f"Received sync request with {len(task_changes)} tasks, {deleted_task_count} marked as deleted")
if not device_id: if not device_id:
return Response( return Response(
@@ -122,17 +125,17 @@ def process_task_changes(user, task_changes, last_sync_at):
# Debug logging # Debug logging
if is_deleted: if is_deleted:
print(f"[SERVER SYNC DEBUG] Received deletion for task sync_id: {sync_id}") logger.debug(f"Received deletion for task sync_id: {sync_id}")
try: try:
existing = Task.all_objects.get(sync_id=sync_id, user=user) existing = Task.all_objects.get(sync_id=sync_id, user=user)
if is_deleted: if is_deleted:
# Soft delete the task # Soft delete the task
print(f"[SERVER SYNC DEBUG] Soft deleting task: {existing.title} (id: {existing.id})") logger.debug(f"Soft deleting task: {existing.title} (id: {existing.id})")
existing.is_deleted = True existing.is_deleted = True
existing.save() existing.save()
print(f"[SERVER SYNC DEBUG] Task soft deleted successfully") logger.debug(f"Task soft deleted successfully")
continue continue
# Check for conflict # Check for conflict
@@ -212,12 +215,12 @@ def process_time_entry_changes(user, entry_changes, last_sync_at):
"""Process time entry changes from client.""" """Process time entry changes from client."""
conflicts = [] conflicts = []
print(f"[SERVER SYNC DEBUG] Processing {len(entry_changes)} time entry changes") logger.debug(f"Processing {len(entry_changes)} time entry changes")
for entry_data in entry_changes: for entry_data in entry_changes:
sync_id = entry_data.get('sync_id') sync_id = entry_data.get('sync_id')
is_deleted = entry_data.get('is_deleted', False) is_deleted = entry_data.get('is_deleted', False)
print(f"[SERVER SYNC DEBUG] Time entry sync_id: {sync_id}, is_deleted: {is_deleted}") logger.debug(f"Time entry sync_id: {sync_id}, is_deleted: {is_deleted}")
try: try:
existing = TimeEntry.all_objects.get(sync_id=sync_id, user=user) existing = TimeEntry.all_objects.get(sync_id=sync_id, user=user)
@@ -254,8 +257,8 @@ def process_time_entry_changes(user, entry_changes, last_sync_at):
except TimeEntry.DoesNotExist: except TimeEntry.DoesNotExist:
if not is_deleted: if not is_deleted:
task_sync_id = entry_data.get('task_sync_id') task_sync_id = entry_data.get('task_sync_id')
print(f"[SERVER SYNC DEBUG] Creating new time entry for task_sync_id: {task_sync_id}") logger.debug(f"Creating new time entry for task_sync_id: {task_sync_id}")
print(f"[SERVER SYNC DEBUG] started_at: {entry_data.get('started_at')}, ended_at: {entry_data.get('ended_at')}") logger.debug(f" started_at: {entry_data.get('started_at')}, ended_at: {entry_data.get('ended_at')}")
try: try:
task = Task.objects.get(sync_id=task_sync_id, user=user) task = Task.objects.get(sync_id=task_sync_id, user=user)
@@ -276,9 +279,9 @@ def process_time_entry_changes(user, entry_changes, last_sync_at):
ended_at=ended_at, ended_at=ended_at,
notes=entry_data.get('notes', ''), notes=entry_data.get('notes', ''),
) )
print(f"[SERVER SYNC DEBUG] Created time entry: {new_entry.id}, duration_seconds: {new_entry.duration_seconds}") logger.debug(f"Created time entry: {new_entry.id}, duration_seconds: {new_entry.duration_seconds}")
except Task.DoesNotExist: except Task.DoesNotExist:
print(f"[SERVER SYNC DEBUG] Task not found for sync_id: {task_sync_id}") logger.debug(f"Task not found for sync_id: {task_sync_id}")
pass # Skip if task doesn't exist pass # Skip if task doesn't exist
return conflicts return conflicts
@@ -348,7 +351,7 @@ def create_task_from_data(user, data):
def get_server_changes(user, last_sync_at): def get_server_changes(user, last_sync_at):
"""Get all changes on server since last sync.""" """Get all changes on server since last sync."""
print(f"[SERVER SYNC DEBUG] get_server_changes: last_sync_at={last_sync_at}") logger.debug(f"get_server_changes: last_sync_at={last_sync_at}")
filter_kwargs = {'user': user} filter_kwargs = {'user': user}
# For incremental sync, filter by updated_at # For incremental sync, filter by updated_at
@@ -364,11 +367,11 @@ def get_server_changes(user, last_sync_at):
tags = Tag.all_objects.filter(user=user) tags = Tag.all_objects.filter(user=user)
time_entries = TimeEntry.all_objects.filter(user=user) time_entries = TimeEntry.all_objects.filter(user=user)
print(f"[SERVER SYNC DEBUG] Returning {len(tasks)} tasks, {len(tags)} tags, {len(time_entries)} time entries") logger.debug(f"Returning {len(tasks)} tasks, {len(tags)} tags, {len(time_entries)} time entries")
if time_entries: if time_entries:
print(f"[SERVER SYNC DEBUG] Time entries being sent to client:") logger.debug(f"Time entries being sent to client:")
for entry in time_entries: for entry in time_entries:
print(f"[SERVER SYNC DEBUG] sync_id: {entry.sync_id}, task: {entry.task.title}, duration: {entry.duration_seconds}s, updated_at: {entry.updated_at}") logger.debug(f" sync_id: {entry.sync_id}, task: {entry.task.title}, duration: {entry.duration_seconds}s, updated_at: {entry.updated_at}")
return { return {
'tasks': TaskSyncSerializer(tasks, many=True).data, 'tasks': TaskSyncSerializer(tasks, many=True).data,
+8 -5
View File
@@ -2,6 +2,7 @@
Task views for KeepItGoing. Task views for KeepItGoing.
""" """
import logging
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import Q from django.db.models import Q
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
@@ -22,6 +23,8 @@ from .serializers import (
TaskShareSerializer, TaskShareSerializer,
) )
logger = logging.getLogger(__name__)
# ============================================================================= # =============================================================================
# API Views # API Views
@@ -553,7 +556,7 @@ def task_detail_partial(request, task_id):
def web_timer_start(request, task_id): def web_timer_start(request, task_id):
"""Start a timer for a task (web view).""" """Start a timer for a task (web view)."""
task = get_object_or_404(Task, id=task_id, user=request.user) task = get_object_or_404(Task, id=task_id, user=request.user)
print(f"[WEB TIMER DEBUG] Starting timer for task: {task.title} (id: {task_id})") logger.debug(f"Starting timer for task: {task.title} (id: {task_id})")
# Stop any existing running timer first (properly to calculate duration) # Stop any existing running timer first (properly to calculate duration)
running_entries = TimeEntry.objects.filter( running_entries = TimeEntry.objects.filter(
@@ -567,7 +570,7 @@ def web_timer_start(request, task_id):
entry.save() # This will calculate duration_seconds entry.save() # This will calculate duration_seconds
stopped_count += 1 stopped_count += 1
print(f"[WEB TIMER DEBUG] Stopped {stopped_count} existing timers") logger.debug(f"Stopped {stopped_count} existing timers")
# Create new timer # Create new timer
entry = TimeEntry.objects.create( entry = TimeEntry.objects.create(
@@ -575,7 +578,7 @@ def web_timer_start(request, task_id):
user=request.user, user=request.user,
started_at=timezone.now() started_at=timezone.now()
) )
print(f"[WEB TIMER DEBUG] Created new timer entry: {entry.id}") logger.debug(f"Created new timer entry: {entry.id}")
next_url = request.POST.get('next', 'dashboard') next_url = request.POST.get('next', 'dashboard')
return redirect(next_url) return redirect(next_url)
@@ -585,7 +588,7 @@ def web_timer_start(request, task_id):
@require_POST @require_POST
def web_timer_stop(request, task_id): def web_timer_stop(request, task_id):
"""Stop a timer for a task (web view).""" """Stop a timer for a task (web view)."""
print(f"[WEB TIMER DEBUG] Stopping timer for task_id: {task_id}") logger.debug(f"Stopping timer for task_id: {task_id}")
# Get the running timer and stop it properly (to trigger save() and calculate duration) # Get the running timer and stop it properly (to trigger save() and calculate duration)
entries = TimeEntry.objects.filter( entries = TimeEntry.objects.filter(
@@ -600,7 +603,7 @@ def web_timer_stop(request, task_id):
entry.save() # This will calculate duration_seconds entry.save() # This will calculate duration_seconds
stopped_count += 1 stopped_count += 1
print(f"[WEB TIMER DEBUG] Stopped {stopped_count} timer entries") logger.debug(f"Stopped {stopped_count} timer entries")
next_url = request.POST.get('next', 'dashboard') next_url = request.POST.get('next', 'dashboard')
return redirect(next_url) return redirect(next_url)