Skip to content

Commit b40dc5d

Browse files
committed
fix: log but don't leak error details
1 parent c30c919 commit b40dc5d

28 files changed

+390
-66
lines changed

lib/src/alerts/alerts_namespace.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:meta/meta.dart';
44
import 'package:shelf/shelf.dart';
55

66
import '../common/cloud_event.dart';
7+
import '../common/utilities.dart';
78
import '../firebase.dart';
89
import 'alert_event.dart';
910
import 'alert_type.dart';
@@ -87,8 +88,8 @@ class AlertsNamespace extends FunctionsNamespace {
8788
return Response.ok('');
8889
} on FormatException catch (e) {
8990
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
90-
} catch (e) {
91-
return Response(500, body: 'Error processing alert: $e');
91+
} catch (e, stackTrace) {
92+
return logEventHandlerError(e, stackTrace);
9293
}
9394
});
9495
}

lib/src/alerts/app_distribution_namespace.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:meta/meta.dart';
44
import 'package:shelf/shelf.dart';
55

66
import '../common/cloud_event.dart';
7+
import '../common/utilities.dart';
78
import '../firebase.dart';
89
import 'alert_event.dart';
910
import 'alert_type.dart';
@@ -69,8 +70,8 @@ class AppDistributionNamespace {
6970
return Response.ok('');
7071
} on FormatException catch (e) {
7172
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
72-
} catch (e) {
73-
return Response(500, body: 'Error processing alert: $e');
73+
} catch (e, stackTrace) {
74+
return logEventHandlerError(e, stackTrace);
7475
}
7576
});
7677
}

lib/src/alerts/billing_namespace.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:meta/meta.dart';
44
import 'package:shelf/shelf.dart';
55

66
import '../common/cloud_event.dart';
7+
import '../common/utilities.dart';
78
import '../firebase.dart';
89
import 'alert_event.dart';
910
import 'alert_type.dart';
@@ -70,8 +71,8 @@ class BillingNamespace {
7071
return Response.ok('');
7172
} on FormatException catch (e) {
7273
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
73-
} catch (e) {
74-
return Response(500, body: 'Error processing alert: $e');
74+
} catch (e, stackTrace) {
75+
return logEventHandlerError(e, stackTrace);
7576
}
7677
});
7778
}

lib/src/alerts/crashlytics_namespace.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:meta/meta.dart';
44
import 'package:shelf/shelf.dart';
55

66
import '../common/cloud_event.dart';
7+
import '../common/utilities.dart';
78
import '../firebase.dart';
89
import 'alert_event.dart';
910
import 'alert_type.dart';
@@ -126,8 +127,8 @@ class CrashlyticsNamespace {
126127
return Response.ok('');
127128
} on FormatException catch (e) {
128129
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
129-
} catch (e) {
130-
return Response(500, body: 'Error processing alert: $e');
130+
} catch (e, stackTrace) {
131+
return logEventHandlerError(e, stackTrace);
131132
}
132133
});
133134
}

lib/src/alerts/performance_namespace.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:meta/meta.dart';
44
import 'package:shelf/shelf.dart';
55

66
import '../common/cloud_event.dart';
7+
import '../common/utilities.dart';
78
import '../firebase.dart';
89
import 'alert_event.dart';
910
import 'alert_type.dart';
@@ -55,8 +56,8 @@ class PerformanceNamespace {
5556
return Response.ok('');
5657
} on FormatException catch (e) {
5758
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
58-
} catch (e) {
59-
return Response(500, body: 'Error processing alert: $e');
59+
} catch (e, stackTrace) {
60+
return logEventHandlerError(e, stackTrace);
6061
}
6162
});
6263
}

lib/src/common/utilities.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import 'dart:convert';
22

33
import 'package:shelf/shelf.dart' show Request, Response;
4+
import 'package:stack_trace/stack_trace.dart' show Trace;
45

56
import '../https/error.dart';
7+
import '../logger/logger.dart';
68

79
Future<Map<String, dynamic>> readAsJsonMap(Request request) async {
810
final decoded = await _converter.bind(request.read()).first;
@@ -21,3 +23,30 @@ extension HttpErrorExtension on HttpsError {
2123
body: jsonEncode(toErrorResponse()),
2224
);
2325
}
26+
27+
/// Logs an unexpected error with its stack trace and returns an [InternalError].
28+
///
29+
/// Use in HTTPS and callable function handlers where the response must be
30+
/// a structured JSON error. The actual error details are only logged
31+
/// server-side and never exposed to the client.
32+
InternalError logInternalError(Object error, StackTrace stackTrace) {
33+
_logError(error, stackTrace);
34+
return InternalError();
35+
}
36+
37+
/// Logs an unexpected error with its stack trace and returns a generic 500
38+
/// response.
39+
///
40+
/// Use in event-triggered function handlers (Firestore, PubSub, Storage, etc.)
41+
/// where the caller is the Cloud Functions infrastructure rather than an
42+
/// end-user client.
43+
Response logEventHandlerError(Object error, StackTrace stackTrace) {
44+
_logError(error, stackTrace);
45+
return Response.internalServerError();
46+
}
47+
48+
/// Formats and logs an error with a terse, readable stack trace.
49+
void _logError(Object error, StackTrace stackTrace) {
50+
final terse = Trace.from(stackTrace).terse;
51+
logger.error('$error\n$terse');
52+
}

lib/src/database/database_namespace.dart

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:meta/meta.dart';
55
import 'package:shelf/shelf.dart';
66

77
import '../common/cloud_event.dart';
8+
import '../common/utilities.dart';
89
import '../firebase.dart';
910
import 'data_snapshot.dart';
1011
import 'event.dart';
@@ -125,8 +126,8 @@ class DatabaseNamespace extends FunctionsNamespace {
125126
);
126127

127128
await handler(event);
128-
} catch (e) {
129-
return Response(500, body: 'Handler error: $e');
129+
} catch (e, stackTrace) {
130+
return logEventHandlerError(e, stackTrace);
130131
}
131132

132133
return Response.ok('');
@@ -177,10 +178,7 @@ class DatabaseNamespace extends FunctionsNamespace {
177178
} on FormatException catch (e) {
178179
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
179180
} catch (e, stackTrace) {
180-
return Response(
181-
500,
182-
body: 'Error processing Database event: $e\n$stackTrace',
183-
);
181+
return logEventHandlerError(e, stackTrace);
184182
}
185183
}, refPattern: _normalizeRefPattern(ref));
186184
}
@@ -293,8 +291,8 @@ class DatabaseNamespace extends FunctionsNamespace {
293291
);
294292

295293
await handler(event);
296-
} catch (e) {
297-
return Response(500, body: 'Handler error: $e');
294+
} catch (e, stackTrace) {
295+
return logEventHandlerError(e, stackTrace);
298296
}
299297

300298
return Response.ok('');
@@ -454,8 +452,8 @@ class DatabaseNamespace extends FunctionsNamespace {
454452
);
455453

456454
await handler(event);
457-
} catch (e) {
458-
return Response(500, body: 'Handler error: $e');
455+
} catch (e, stackTrace) {
456+
return logEventHandlerError(e, stackTrace);
459457
}
460458

461459
return Response.ok('');
@@ -629,8 +627,8 @@ class DatabaseNamespace extends FunctionsNamespace {
629627
);
630628

631629
await handler(event);
632-
} catch (e) {
633-
return Response(500, body: 'Handler error: $e');
630+
} catch (e, stackTrace) {
631+
return logEventHandlerError(e, stackTrace);
634632
}
635633

636634
return Response.ok('');

lib/src/eventarc/eventarc_namespace.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:meta/meta.dart';
44
import 'package:shelf/shelf.dart';
55

66
import '../common/cloud_event.dart';
7+
import '../common/utilities.dart';
78
import '../firebase.dart';
89
import 'options.dart';
910

@@ -57,8 +58,8 @@ class EventarcNamespace extends FunctionsNamespace {
5758
return Response.ok('');
5859
} on FormatException catch (e) {
5960
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
60-
} catch (e) {
61-
return Response(500, body: 'Error processing Eventarc event: $e');
61+
} catch (e, stackTrace) {
62+
return logEventHandlerError(e, stackTrace);
6263
}
6364
});
6465
}

lib/src/firebase.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'eventarc/eventarc_namespace.dart';
1212
import 'firestore/firestore_namespace.dart';
1313
import 'https/https_namespace.dart';
1414
import 'identity/identity_namespace.dart';
15+
import 'logger/logger.dart';
1516
import 'pubsub/pubsub_namespace.dart';
1617
import 'remote_config/remote_config_namespace.dart';
1718
import 'scheduler/scheduler_namespace.dart';
@@ -62,7 +63,7 @@ class Firebase {
6263
// Create Firestore instance
6364
_firestoreInstance = _adminApp!.firestore();
6465
} catch (e) {
65-
print('Warning: Failed to initialize Firebase Admin SDK: $e');
66+
logger.warn('Failed to initialize Firebase Admin SDK: $e');
6667
}
6768
}
6869

lib/src/firestore/firestore_namespace.dart

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:meta/meta.dart';
55
import 'package:shelf/shelf.dart';
66

77
import '../common/cloud_event.dart';
8+
import '../common/utilities.dart';
89
import '../firebase.dart';
910
import 'document_snapshot.dart';
1011
import 'event.dart';
@@ -500,8 +501,8 @@ class FirestoreNamespace extends FunctionsNamespace {
500501
FirestoreEvent<EmulatorDocumentSnapshot?>,
501502
))(event);
502503
}
503-
} catch (e) {
504-
return Response(500, body: 'Handler error: $e');
504+
} catch (e, stackTrace) {
505+
return logEventHandlerError(e, stackTrace);
505506
}
506507

507508
return Response.ok('');
@@ -571,10 +572,7 @@ class FirestoreNamespace extends FunctionsNamespace {
571572
} on FormatException catch (e) {
572573
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
573574
} catch (e, stackTrace) {
574-
return Response(
575-
500,
576-
body: 'Error processing Firestore event: $e\n$stackTrace',
577-
);
575+
return logEventHandlerError(e, stackTrace);
578576
}
579577
}, documentPattern: document);
580578
}
@@ -673,8 +671,8 @@ class FirestoreNamespace extends FunctionsNamespace {
673671
FirestoreEvent<Change<EmulatorDocumentSnapshot>?>,
674672
))(event);
675673
}
676-
} catch (e) {
677-
return Response(500, body: 'Handler error: $e');
674+
} catch (e, stackTrace) {
675+
return logEventHandlerError(e, stackTrace);
678676
}
679677

680678
return Response.ok('');
@@ -686,10 +684,7 @@ class FirestoreNamespace extends FunctionsNamespace {
686684
);
687685
}
688686
} catch (e, stackTrace) {
689-
return Response(
690-
500,
691-
body: 'Error processing Firestore event: $e\n$stackTrace',
692-
);
687+
return logEventHandlerError(e, stackTrace);
693688
}
694689
}, documentPattern: document);
695690
}

0 commit comments

Comments
 (0)