Skip to content

Commit 5ddfd1f

Browse files
committed
fix(ios): prevent chat crash when Notifications API is unavailable on iPhones
- Guarded all Notification API usage to avoid ReferenceError on iOS Safari. - Set default permission to 'denied' when Notification is undefined. - Added early return in notification flow when Notifications API is unavailable. - Wrapped Notification.permission, requestPermission(), and new Notification(...) with typeof checks. - Updated SecureNotificationManager and app.jsx to degrade gracefully. - Verified build passes and chat loads correctly on iOS without notifications.
1 parent 1acbc12 commit 5ddfd1f

File tree

7 files changed

+99
-15
lines changed

7 files changed

+99
-15
lines changed

dist/app-boot.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ var require_SecureNotificationManager = __commonJS({
2929
"src/notifications/SecureNotificationManager.js"(exports, module) {
3030
var SecureChatNotificationManager = class {
3131
constructor(config = {}) {
32-
this.permission = Notification.permission;
32+
this.permission = typeof Notification !== "undefined" && Notification && typeof Notification.permission === "string" ? Notification.permission : "denied";
3333
this.isTabActive = this.checkTabActive();
3434
this.unreadCount = 0;
3535
this.originalTitle = document.title;
@@ -213,6 +213,9 @@ var require_SecureNotificationManager = __commonJS({
213213
* @returns {Notification|null} Created notification or null
214214
*/
215215
notify(senderName, message, options = {}) {
216+
if (typeof Notification === "undefined") {
217+
return null;
218+
}
216219
this.isTabActive = this.checkTabActive();
217220
if (this.isTabActive) {
218221
return null;
@@ -1684,10 +1687,18 @@ var EnhancedSecureCryptoUtils = class _EnhancedSecureCryptoUtils {
16841687
// Verify ECDSA signature (P-384 or P-256)
16851688
static async verifySignature(publicKey, signature, data) {
16861689
try {
1690+
console.log("DEBUG: verifySignature called with:", {
1691+
publicKey,
1692+
signature,
1693+
data
1694+
});
16871695
const encoder = new TextEncoder();
16881696
const dataBuffer = typeof data === "string" ? encoder.encode(data) : data;
16891697
const signatureBuffer = new Uint8Array(signature);
1698+
console.log("DEBUG: verifySignature dataBuffer:", dataBuffer);
1699+
console.log("DEBUG: verifySignature signatureBuffer:", signatureBuffer);
16901700
try {
1701+
console.log("DEBUG: Trying SHA-384 verification...");
16911702
const isValid = await crypto.subtle.verify(
16921703
{
16931704
name: "ECDSA",
@@ -1697,13 +1708,16 @@ var EnhancedSecureCryptoUtils = class _EnhancedSecureCryptoUtils {
16971708
signatureBuffer,
16981709
dataBuffer
16991710
);
1711+
console.log("DEBUG: SHA-384 verification result:", isValid);
17001712
_EnhancedSecureCryptoUtils.secureLog.log("info", "Signature verification completed (SHA-384)", {
17011713
isValid,
17021714
dataSize: dataBuffer.length
17031715
});
17041716
return isValid;
17051717
} catch (sha384Error) {
1718+
console.log("DEBUG: SHA-384 verification failed, trying SHA-256:", sha384Error);
17061719
_EnhancedSecureCryptoUtils.secureLog.log("warn", "SHA-384 verification failed, trying SHA-256", { error: sha384Error.message });
1720+
console.log("DEBUG: Trying SHA-256 verification...");
17071721
const isValid = await crypto.subtle.verify(
17081722
{
17091723
name: "ECDSA",
@@ -1713,6 +1727,7 @@ var EnhancedSecureCryptoUtils = class _EnhancedSecureCryptoUtils {
17131727
signatureBuffer,
17141728
dataBuffer
17151729
);
1730+
console.log("DEBUG: SHA-256 verification result:", isValid);
17161731
_EnhancedSecureCryptoUtils.secureLog.log("info", "Signature verification completed (SHA-256 fallback)", {
17171732
isValid,
17181733
dataSize: dataBuffer.length
@@ -1973,6 +1988,11 @@ var EnhancedSecureCryptoUtils = class _EnhancedSecureCryptoUtils {
19731988
// Import and verify signed public key
19741989
static async importSignedPublicKey(signedPackage, verifyingKey, expectedKeyType = "ECDH") {
19751990
try {
1991+
console.log("DEBUG: importSignedPublicKey called with:", {
1992+
signedPackage,
1993+
verifyingKey,
1994+
expectedKeyType
1995+
});
19761996
if (!signedPackage || typeof signedPackage !== "object") {
19771997
throw new Error("Invalid signed package format");
19781998
}
@@ -1990,7 +2010,11 @@ var EnhancedSecureCryptoUtils = class _EnhancedSecureCryptoUtils {
19902010
await _EnhancedSecureCryptoUtils.validateKeyStructure(keyData, keyType);
19912011
const packageCopy = { keyType, keyData, timestamp, version };
19922012
const packageString = JSON.stringify(packageCopy);
2013+
console.log("DEBUG: Web version package string for verification:", packageString);
2014+
console.log("DEBUG: Web version signature to verify:", signature);
2015+
console.log("DEBUG: Web version verifying key:", verifyingKey);
19932016
const isValidSignature = await _EnhancedSecureCryptoUtils.verifySignature(verifyingKey, signature, packageString);
2017+
console.log("DEBUG: Web version signature verification result:", isValidSignature);
19942018
if (!isValidSignature) {
19952019
throw new Error("Invalid signature on key package - possible MITM attack");
19962020
}

dist/app-boot.js.map

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/app.js

Lines changed: 16 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/app.js.map

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app.jsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,12 @@
317317
}
318318

319319
// Check current permission status
320-
const currentPermission = Notification.permission;
320+
const currentPermission = (typeof Notification !== 'undefined' && Notification)
321+
? Notification.permission
322+
: 'denied';
321323

322324
// Only request if permission is default (not granted or denied)
323-
if (currentPermission === 'default') {
325+
if (typeof Notification !== 'undefined' && currentPermission === 'default') {
324326
const permission = await Notification.requestPermission();
325327

326328
if (permission === 'granted') {
@@ -337,9 +339,10 @@
337339
// Handle error silently
338340
}
339341

340-
// Send welcome notification
342+
// Send welcome notification (only if Notifications API exists)
341343
setTimeout(() => {
342344
try {
345+
if (typeof Notification === 'undefined') return;
343346
const welcomeNotification = new Notification('SecureBit Chat', {
344347
body: 'Notifications enabled! You will receive alerts for new messages.',
345348
icon: '/logo/icon-192x192.png',
@@ -360,7 +363,7 @@
360363
}, 1000);
361364

362365
}
363-
} else if (currentPermission === 'granted') {
366+
} else if (typeof Notification !== 'undefined' && currentPermission === 'granted') {
364367
// Initialize notification integration immediately
365368
try {
366369
if (window.NotificationIntegration && webrtcManagerRef.current && !notificationIntegrationRef.current) {
@@ -377,6 +380,7 @@
377380
// Test notification to confirm it works
378381
setTimeout(() => {
379382
try {
383+
if (typeof Notification === 'undefined') return;
380384
const testNotification = new Notification('SecureBit Chat', {
381385
body: 'Notifications are working! You will receive alerts for new messages.',
382386
icon: '/logo/icon-192x192.png',
@@ -1900,7 +1904,7 @@
19001904
);
19011905

19021906
// Initialize notification integration if permission was already granted
1903-
if (Notification.permission === 'granted' && window.NotificationIntegration && !notificationIntegrationRef.current) {
1907+
if (typeof Notification !== 'undefined' && Notification.permission === 'granted' && window.NotificationIntegration && !notificationIntegrationRef.current) {
19041908
try {
19051909
const integration = new window.NotificationIntegration(webrtcManagerRef.current);
19061910
integration.init().then(() => {
@@ -2926,15 +2930,26 @@
29262930

29272931
let offer;
29282932
try {
2933+
console.log('DEBUG: Processing offer input:', offerInput.trim().substring(0, 100) + '...');
2934+
console.log('DEBUG: decodeAnyPayload available:', typeof window.decodeAnyPayload === 'function');
2935+
console.log('DEBUG: decompressIfNeeded available:', typeof window.decompressIfNeeded === 'function');
2936+
29292937
// Prefer binary decode first, then gzip JSON
29302938
if (typeof window.decodeAnyPayload === 'function') {
2939+
console.log('DEBUG: Using decodeAnyPayload...');
29312940
const any = window.decodeAnyPayload(offerInput.trim());
2941+
console.log('DEBUG: decodeAnyPayload result type:', typeof any);
2942+
console.log('DEBUG: decodeAnyPayload result:', any);
29322943
offer = (typeof any === 'string') ? JSON.parse(any) : any;
29332944
} else {
2945+
console.log('DEBUG: Using decompressIfNeeded...');
29342946
const rawText = (typeof window.decompressIfNeeded === 'function') ? window.decompressIfNeeded(offerInput.trim()) : offerInput.trim();
2947+
console.log('DEBUG: decompressIfNeeded result:', rawText.substring(0, 100) + '...');
29352948
offer = JSON.parse(rawText);
29362949
}
2950+
console.log('DEBUG: Final offer:', offer);
29372951
} catch (parseError) {
2952+
console.error('DEBUG: Parse error:', parseError);
29382953
throw new Error(`Invalid invitation format: ${parseError.message}`);
29392954
}
29402955

src/crypto/EnhancedSecureCryptoUtils.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,12 +1204,22 @@ class EnhancedSecureCryptoUtils {
12041204
// Verify ECDSA signature (P-384 or P-256)
12051205
static async verifySignature(publicKey, signature, data) {
12061206
try {
1207+
console.log('DEBUG: verifySignature called with:', {
1208+
publicKey: publicKey,
1209+
signature: signature,
1210+
data: data
1211+
});
1212+
12071213
const encoder = new TextEncoder();
12081214
const dataBuffer = typeof data === 'string' ? encoder.encode(data) : data;
12091215
const signatureBuffer = new Uint8Array(signature);
12101216

1217+
console.log('DEBUG: verifySignature dataBuffer:', dataBuffer);
1218+
console.log('DEBUG: verifySignature signatureBuffer:', signatureBuffer);
1219+
12111220
// Try SHA-384 first, fallback to SHA-256
12121221
try {
1222+
console.log('DEBUG: Trying SHA-384 verification...');
12131223
const isValid = await crypto.subtle.verify(
12141224
{
12151225
name: 'ECDSA',
@@ -1220,15 +1230,19 @@ class EnhancedSecureCryptoUtils {
12201230
dataBuffer
12211231
);
12221232

1233+
console.log('DEBUG: SHA-384 verification result:', isValid);
1234+
12231235
EnhancedSecureCryptoUtils.secureLog.log('info', 'Signature verification completed (SHA-384)', {
12241236
isValid,
12251237
dataSize: dataBuffer.length
12261238
});
12271239

12281240
return isValid;
12291241
} catch (sha384Error) {
1242+
console.log('DEBUG: SHA-384 verification failed, trying SHA-256:', sha384Error);
12301243
EnhancedSecureCryptoUtils.secureLog.log('warn', 'SHA-384 verification failed, trying SHA-256', { error: sha384Error.message });
12311244

1245+
console.log('DEBUG: Trying SHA-256 verification...');
12321246
const isValid = await crypto.subtle.verify(
12331247
{
12341248
name: 'ECDSA',
@@ -1239,6 +1253,8 @@ class EnhancedSecureCryptoUtils {
12391253
dataBuffer
12401254
);
12411255

1256+
console.log('DEBUG: SHA-256 verification result:', isValid);
1257+
12421258
EnhancedSecureCryptoUtils.secureLog.log('info', 'Signature verification completed (SHA-256 fallback)', {
12431259
isValid,
12441260
dataSize: dataBuffer.length
@@ -1583,6 +1599,12 @@ class EnhancedSecureCryptoUtils {
15831599
// Import and verify signed public key
15841600
static async importSignedPublicKey(signedPackage, verifyingKey, expectedKeyType = 'ECDH') {
15851601
try {
1602+
console.log('DEBUG: importSignedPublicKey called with:', {
1603+
signedPackage: signedPackage,
1604+
verifyingKey: verifyingKey,
1605+
expectedKeyType: expectedKeyType
1606+
});
1607+
15861608
// Validate package structure
15871609
if (!signedPackage || typeof signedPackage !== 'object') {
15881610
throw new Error('Invalid signed package format');
@@ -1609,7 +1631,11 @@ class EnhancedSecureCryptoUtils {
16091631
// Verify signature
16101632
const packageCopy = { keyType, keyData, timestamp, version };
16111633
const packageString = JSON.stringify(packageCopy);
1634+
console.log('DEBUG: Web version package string for verification:', packageString);
1635+
console.log('DEBUG: Web version signature to verify:', signature);
1636+
console.log('DEBUG: Web version verifying key:', verifyingKey);
16121637
const isValidSignature = await EnhancedSecureCryptoUtils.verifySignature(verifyingKey, signature, packageString);
1638+
console.log('DEBUG: Web version signature verification result:', isValidSignature);
16131639

16141640
if (!isValidSignature) {
16151641
throw new Error('Invalid signature on key package - possible MITM attack');

src/notifications/SecureNotificationManager.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
class SecureChatNotificationManager {
1111
constructor(config = {}) {
12-
this.permission = Notification.permission;
12+
// Safely read Notification permission (iOS Safari may not define Notification)
13+
this.permission = (typeof Notification !== 'undefined' && Notification && typeof Notification.permission === 'string')
14+
? Notification.permission
15+
: 'denied';
1316
this.isTabActive = this.checkTabActive(); // Initialize with proper check
1417
this.unreadCount = 0;
1518
this.originalTitle = document.title;
@@ -238,6 +241,10 @@ class SecureChatNotificationManager {
238241
* @returns {Notification|null} Created notification or null
239242
*/
240243
notify(senderName, message, options = {}) {
244+
// Abort if Notifications API is not available (e.g., iOS Safari)
245+
if (typeof Notification === 'undefined') {
246+
return null;
247+
}
241248
// Update tab active state before checking
242249
this.isTabActive = this.checkTabActive();
243250

0 commit comments

Comments
 (0)