security: sanitize all error messages shown to users
Created centralized userFriendlyError() utility that converts raw
exceptions into clean, user-friendly messages. Strips hostnames,
ports, OS error codes, HTTP status codes, stack traces, and Django
field names. Maps network/timeout/auth/server errors to plain
English messages.
Fixed 16 locations across 10 files:
- home_screen, calendar_screen, learn_more_screen (SnackBar/Text)
- login_screen, desktop_login_screen (SnackBar)
- profile_screen, contribute_screen, search_screen (SnackBar)
- review_form, review_section (inline error text)
- gamification_provider (error field)
Also removed double-wrapped exceptions in ReviewService (rethrow
instead of throw Exception('Failed to...: $e')).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
// lib/features/gamification/providers/gamification_provider.dart
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../../../core/utils/error_utils.dart';
|
||||
import '../models/gamification_models.dart';
|
||||
import '../services/gamification_service.dart';
|
||||
|
||||
@@ -41,7 +42,7 @@ class GamificationProvider extends ChangeNotifier {
|
||||
shopItems = results[2] as List<ShopItem>;
|
||||
achievements = results[3] as List<AchievementBadge>;
|
||||
} catch (e) {
|
||||
error = e.toString();
|
||||
error = userFriendlyError(e);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
@@ -58,7 +59,7 @@ class GamificationProvider extends ChangeNotifier {
|
||||
try {
|
||||
leaderboard = await _service.getLeaderboard(district: district, timePeriod: leaderboardTimePeriod);
|
||||
} catch (e) {
|
||||
error = e.toString();
|
||||
error = userFriendlyError(e);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -73,7 +74,7 @@ class GamificationProvider extends ChangeNotifier {
|
||||
try {
|
||||
leaderboard = await _service.getLeaderboard(district: leaderboardDistrict, timePeriod: period);
|
||||
} catch (e) {
|
||||
error = e.toString();
|
||||
error = userFriendlyError(e);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ class ReviewService {
|
||||
page: (res['page'] as num?)?.toInt() ?? page,
|
||||
pageSize: (res['page_size'] as num?)?.toInt() ?? pageSize,
|
||||
);
|
||||
} catch (e) {
|
||||
throw Exception('Failed to load reviews: $e');
|
||||
} catch (_) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ class ReviewService {
|
||||
},
|
||||
requiresAuth: true,
|
||||
);
|
||||
} catch (e) {
|
||||
throw Exception('Failed to submit review: $e');
|
||||
} catch (_) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,8 +84,8 @@ class ReviewService {
|
||||
requiresAuth: true,
|
||||
);
|
||||
return (res['helpful_count'] as num?)?.toInt() ?? 0;
|
||||
} catch (e) {
|
||||
throw Exception('Failed to mark review as helpful: $e');
|
||||
} catch (_) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +97,8 @@ class ReviewService {
|
||||
body: {'review_id': reviewId},
|
||||
requiresAuth: true,
|
||||
);
|
||||
} catch (e) {
|
||||
throw Exception('Failed to flag review: $e');
|
||||
} catch (_) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// lib/features/reviews/widgets/review_form.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../core/storage/token_storage.dart';
|
||||
import '../../../core/utils/error_utils.dart';
|
||||
import '../models/review_models.dart';
|
||||
import 'star_rating_input.dart';
|
||||
|
||||
@@ -60,7 +61,7 @@ class _ReviewFormState extends State<ReviewForm> {
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) setState(() { _state = _FormState.idle; _error = e.toString(); });
|
||||
if (mounted) setState(() { _state = _FormState.idle; _error = userFriendlyError(e); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// lib/features/reviews/widgets/review_section.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../core/storage/token_storage.dart';
|
||||
import '../../../core/utils/error_utils.dart';
|
||||
import '../models/review_models.dart';
|
||||
import '../services/review_service.dart';
|
||||
import 'review_summary.dart';
|
||||
@@ -55,7 +56,7 @@ class _ReviewSectionState extends State<ReviewSection> {
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) setState(() { _loading = false; _error = e.toString(); });
|
||||
if (mounted) setState(() { _loading = false; _error = userFriendlyError(e); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user