@@ -51,30 +51,79 @@ import { generateAnswersWithMoonshotCompletionApi } from '../services/apis/moons
51
51
import { generateAnswersWithMoonshotWebApi } from '../services/apis/moonshot-web.mjs'
52
52
import { isUsingModelName } from '../utils/model-name-convert.mjs'
53
53
54
+ const RECONNECT_CONFIG = {
55
+ MAX_ATTEMPTS : 5 ,
56
+ BASE_DELAY_MS : 1000 , // Base delay in milliseconds
57
+ BACKOFF_MULTIPLIER : 2 , // Multiplier for exponential backoff
58
+ } ;
59
+
60
+ const SENSITIVE_KEYWORDS = [
61
+ 'apikey' , // Covers apiKey, customApiKey, claudeApiKey, etc.
62
+ 'token' , // Covers accessToken, refreshToken, etc.
63
+ 'secret' ,
64
+ 'password' ,
65
+ 'kimimoonshotrefreshtoken' ,
66
+ ] ;
67
+
68
+ function redactSensitiveFields ( obj , recursionDepth = 0 , maxDepth = 5 ) {
69
+ if ( recursionDepth > maxDepth ) {
70
+ // Prevent infinite recursion on circular objects or excessively deep structures
71
+ return 'REDACTED_TOO_DEEP' ;
72
+ }
73
+ // Handle null, primitives, and functions directly
74
+ if ( obj === null || typeof obj !== 'object' ) {
75
+ return obj ;
76
+ }
77
+
78
+ // Create a new object or array to store redacted fields, ensuring original is not modified
79
+ const redactedObj = Array . isArray ( obj ) ? [ ] : { } ;
80
+
81
+ for ( const key in obj ) {
82
+ // Ensure we're only processing own properties
83
+ if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) {
84
+ const lowerKey = key . toLowerCase ( ) ;
85
+ let isSensitive = false ;
86
+ for ( const keyword of SENSITIVE_KEYWORDS ) {
87
+ if ( lowerKey . includes ( keyword ) ) {
88
+ isSensitive = true ;
89
+ break ;
90
+ }
91
+ }
92
+
93
+ if ( isSensitive ) {
94
+ redactedObj [ key ] = 'REDACTED' ;
95
+ } else if ( typeof obj [ key ] === 'object' ) {
96
+ // If the value is an object (or array), recurse
97
+ redactedObj [ key ] = redactSensitiveFields ( obj [ key ] , recursionDepth + 1 , maxDepth ) ;
98
+ } else {
99
+ // Otherwise, copy the value as is
100
+ redactedObj [ key ] = obj [ key ] ;
101
+ }
102
+ }
103
+ }
104
+ return redactedObj ;
105
+ }
106
+
54
107
function setPortProxy ( port , proxyTabId ) {
55
108
try {
56
109
console . debug ( `[background] Attempting to connect to proxy tab: ${ proxyTabId } ` )
57
- // Define listeners here so they can be referenced for removal
58
- // These will be port-specific if setPortProxy is called for different ports.
59
- // However, a single port object is typically used per connection instance from the other side.
60
- // The issue arises if `setPortProxy` is called multiple times on the *same* port object for reconnections.
61
110
62
- // Ensure old listeners on port.proxy are removed if it exists (e.g. from a previous failed attempt on this same port object)
111
+ // Ensure old listeners on port.proxy are removed if it exists
63
112
if ( port . proxy ) {
64
113
try {
65
114
if ( port . _proxyOnMessage ) port . proxy . onMessage . removeListener ( port . _proxyOnMessage ) ;
66
115
if ( port . _proxyOnDisconnect ) port . proxy . onDisconnect . removeListener ( port . _proxyOnDisconnect ) ;
67
116
} catch ( e ) {
68
- console . warn ( '[background] Error removing old listeners from previous port.proxy:' , e ) ;
117
+ console . warn ( '[background] Error removing old listeners from previous port.proxy instance :' , e ) ;
69
118
}
70
119
}
71
- // Also remove listeners from the main port object that this function might have added
120
+ // Also remove listeners from the main port object that this function might have added in a previous call for this port instance
72
121
if ( port . _portOnMessage ) port . onMessage . removeListener ( port . _portOnMessage ) ;
73
122
if ( port . _portOnDisconnect ) port . onDisconnect . removeListener ( port . _portOnDisconnect ) ;
74
123
75
124
76
125
port . proxy = Browser . tabs . connect ( proxyTabId , { name : 'background-to-content-script-proxy' } )
77
- console . log ( `[background] Successfully connected to proxy tab: ${ proxyTabId } ` )
126
+ console . debug ( `[background] Successfully connected to proxy tab: ${ proxyTabId } ` )
78
127
port . _reconnectAttempts = 0 // Reset retry count on successful connection
79
128
80
129
port . _proxyOnMessage = ( msg ) => {
@@ -90,64 +139,75 @@ function setPortProxy(port, proxyTabId) {
90
139
}
91
140
}
92
141
93
- const MAX_RECONNECT_ATTEMPTS = 5 ;
94
-
95
142
port . _proxyOnDisconnect = ( ) => {
96
143
console . warn ( `[background] Proxy tab ${ proxyTabId } disconnected.` )
97
144
98
- // Cleanup this specific proxy's listeners
99
- if ( port . proxy ) {
100
- port . proxy . onMessage . removeListener ( port . _proxyOnMessage ) ;
101
- port . proxy . onDisconnect . removeListener ( port . _proxyOnDisconnect ) ; // remove self
145
+ // Cleanup this specific proxy's listeners before setting port.proxy to null
146
+ if ( port . proxy ) { // Check if port.proxy is still valid
147
+ if ( port . _proxyOnMessage ) {
148
+ try { port . proxy . onMessage . removeListener ( port . _proxyOnMessage ) ; }
149
+ catch ( e ) { console . warn ( "[background] Error removing _proxyOnMessage from disconnected port.proxy:" , e ) ; }
150
+ }
151
+ if ( port . _proxyOnDisconnect ) { // port._proxyOnDisconnect is this function itself
152
+ try { port . proxy . onDisconnect . removeListener ( port . _proxyOnDisconnect ) ; }
153
+ catch ( e ) { console . warn ( "[background] Error removing _proxyOnDisconnect from disconnected port.proxy:" , e ) ; }
154
+ }
102
155
}
103
156
port . proxy = null // Clear the old proxy
104
157
105
158
port . _reconnectAttempts = ( port . _reconnectAttempts || 0 ) + 1 ;
106
- if ( port . _reconnectAttempts > MAX_RECONNECT_ATTEMPTS ) {
107
- console . error ( `[background] Max reconnect attempts (${ MAX_RECONNECT_ATTEMPTS } ) reached for tab ${ proxyTabId } . Giving up.` ) ;
108
- // Important: also clean up listeners on the main 'port' object associated with this proxy connection
109
- if ( port . _portOnMessage ) port . onMessage . removeListener ( port . _portOnMessage ) ;
110
- // Do not remove port._portOnDisconnect here as it might be the generic one for the *other* end.
111
- // This needs careful thought: is portOnDisconnect for THIS proxy instance or for the overall port?
112
- // Assuming port.onDisconnect is for the connection that initiated this proxy.
113
- // If that disconnects, it should clean up its own _portOnMessage and _proxyOnMessage etc.
159
+ if ( port . _reconnectAttempts > RECONNECT_CONFIG . MAX_ATTEMPTS ) {
160
+ console . error ( `[background] Max reconnect attempts (${ RECONNECT_CONFIG . MAX_ATTEMPTS } ) reached for tab ${ proxyTabId } . Giving up.` ) ;
161
+ if ( port . _portOnMessage ) {
162
+ try { port . onMessage . removeListener ( port . _portOnMessage ) ; }
163
+ catch ( e ) { console . warn ( "[background] Error removing _portOnMessage on max retries:" , e ) ; }
164
+ }
165
+ // Note: _portOnDisconnect on the main port should remain to handle its eventual disconnection.
114
166
return ;
115
167
}
116
168
117
- const delay = Math . pow ( 2 , port . _reconnectAttempts - 1 ) * 1000 ; // Exponential backoff
169
+ const delay = Math . pow ( RECONNECT_CONFIG . BACKOFF_MULTIPLIER , port . _reconnectAttempts - 1 ) * RECONNECT_CONFIG . BASE_DELAY_MS ;
118
170
console . log ( `[background] Attempting reconnect #${ port . _reconnectAttempts } in ${ delay / 1000 } s for tab ${ proxyTabId } .` )
119
171
120
172
setTimeout ( ( ) => {
121
173
console . debug ( `[background] Retrying connection to tab ${ proxyTabId } , attempt ${ port . _reconnectAttempts } .` ) ;
122
- setPortProxy ( port , proxyTabId ) ; // Reconnect (will add new listeners)
174
+ setPortProxy ( port , proxyTabId ) ; // Reconnect
123
175
} , delay ) ;
124
176
}
125
177
126
- // This is the handler for when the *other* end of the 'port' disconnects (e.g. the popup closes)
127
- // It should clean up everything related to this 'port's proxying activity.
128
178
port . _portOnDisconnect = ( ) => {
129
179
console . log ( '[background] Main port disconnected (e.g. popup/sidebar closed). Cleaning up proxy connections and listeners.' ) ;
130
- if ( port . _portOnMessage ) port . onMessage . removeListener ( port . _portOnMessage ) ;
180
+ if ( port . _portOnMessage ) {
181
+ try { port . onMessage . removeListener ( port . _portOnMessage ) ; }
182
+ catch ( e ) { console . warn ( "[background] Error removing _portOnMessage on main port disconnect:" , e ) ; }
183
+ }
131
184
if ( port . proxy ) {
132
- if ( port . _proxyOnMessage ) port . proxy . onMessage . removeListener ( port . _proxyOnMessage ) ;
133
- if ( port . _proxyOnDisconnect ) port . proxy . onDisconnect . removeListener ( port . _proxyOnDisconnect ) ;
185
+ if ( port . _proxyOnMessage ) {
186
+ try { port . proxy . onMessage . removeListener ( port . _proxyOnMessage ) ; }
187
+ catch ( e ) { console . warn ( "[background] Error removing _proxyOnMessage from port.proxy on main port disconnect:" , e ) ; }
188
+ }
189
+ if ( port . _proxyOnDisconnect ) {
190
+ try { port . proxy . onDisconnect . removeListener ( port . _proxyOnDisconnect ) ; }
191
+ catch ( e ) { console . warn ( "[background] Error removing _proxyOnDisconnect from port.proxy on main port disconnect:" , e ) ; }
192
+ }
134
193
try {
135
- port . proxy . disconnect ( ) ; // Disconnect the connection to the content script
194
+ port . proxy . disconnect ( ) ;
136
195
} catch ( e ) {
137
- console . warn ( '[background] Error disconnecting port.proxy:' , e ) ;
196
+ console . warn ( '[background] Error disconnecting port.proxy on main port disconnect :' , e ) ;
138
197
}
139
198
port . proxy = null ;
140
199
}
141
- // Remove self
142
- if ( port . _portOnDisconnect ) port . onDisconnect . removeListener ( port . _portOnDisconnect ) ;
143
- // Reset for potential future use of this port object if it's somehow reused (though typically new port objects are made)
200
+ if ( port . _portOnDisconnect ) { // Remove self from main port
201
+ try { port . onDisconnect . removeListener ( port . _portOnDisconnect ) ; }
202
+ catch ( e ) { console . warn ( "[background] Error removing _portOnDisconnect on main port disconnect:" , e ) ; }
203
+ }
144
204
port . _reconnectAttempts = 0 ;
145
205
}
146
206
147
207
port . proxy . onMessage . addListener ( port . _proxyOnMessage )
148
- port . onMessage . addListener ( port . _portOnMessage ) // For messages from the other end to be proxied
149
- port . proxy . onDisconnect . addListener ( port . _proxyOnDisconnect ) // When content script/tab proxy disconnects
150
- port . onDisconnect . addListener ( port . _portOnDisconnect ) // When the other end (popup/sidebar) disconnects
208
+ port . onMessage . addListener ( port . _portOnMessage )
209
+ port . proxy . onDisconnect . addListener ( port . _proxyOnDisconnect )
210
+ port . onDisconnect . addListener ( port . _portOnDisconnect )
151
211
152
212
} catch ( error ) {
153
213
console . error ( `[background] Error in setPortProxy for tab ${ proxyTabId } :` , error )
@@ -158,23 +218,14 @@ async function executeApi(session, port, config) {
158
218
console . log (
159
219
`[background] executeApi called for model: ${ session . modelName } , apiMode: ${ session . apiMode } ` ,
160
220
)
161
- console . debug ( '[background] Full session details:' , session )
162
- // Redact sensitive config details before logging
163
- const redactedConfig = { ...config } ;
164
- if ( redactedConfig . apiKey ) redactedConfig . apiKey = 'REDACTED' ;
165
- if ( redactedConfig . customApiKey ) redactedConfig . customApiKey = 'REDACTED' ;
166
- if ( redactedConfig . claudeApiKey ) redactedConfig . claudeApiKey = 'REDACTED' ;
167
- if ( redactedConfig . kimiMoonShotRefreshToken ) redactedConfig . kimiMoonShotRefreshToken = 'REDACTED' ;
168
- // Add any other sensitive keys that might exist in 'config' or 'session.apiMode'
169
- if ( session . apiMode && session . apiMode . apiKey ) {
170
- redactedConfig . apiMode = { ...session . apiMode , apiKey : 'REDACTED' } ;
171
- } else if ( session . apiMode ) {
172
- redactedConfig . apiMode = { ...session . apiMode } ;
221
+ // Use the new helper function for session and config details
222
+ console . debug ( '[background] Full session details (redacted):' , redactSensitiveFields ( session ) )
223
+ console . debug ( '[background] Full config details (redacted):' , redactSensitiveFields ( config ) )
224
+ // Specific redaction for session.apiMode if it exists, as it's part of the session object
225
+ if ( session . apiMode ) {
226
+ console . debug ( '[background] Session apiMode details (redacted):' , redactSensitiveFields ( session . apiMode ) )
173
227
}
174
228
175
-
176
- console . debug ( '[background] Redacted config details:' , redactedConfig )
177
-
178
229
try {
179
230
if ( isUsingCustomModel ( session ) ) {
180
231
console . debug ( '[background] Using Custom Model API' )
@@ -489,10 +540,11 @@ try {
489
540
const headers = details . requestHeaders
490
541
let modified = false
491
542
for ( let i = 0 ; i < headers . length ; i ++ ) {
492
- if ( headers [ i ] . name . toLowerCase ( ) === 'origin' ) {
543
+ const headerNameLower = headers [ i ] ?. name ?. toLowerCase ( ) ; // Apply optional chaining
544
+ if ( headerNameLower === 'origin' ) {
493
545
headers [ i ] . value = 'https://www.bing.com'
494
546
modified = true
495
- } else if ( headers [ i ] . name . toLowerCase ( ) === 'referer' ) {
547
+ } else if ( headerNameLower === 'referer' ) {
496
548
headers [ i ] . value = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
497
549
modified = true
498
550
}
@@ -539,21 +591,8 @@ try {
539
591
} )
540
592
console . debug ( `[background] Side panel options set for tab ${ tabId } using Browser.sidePanel` )
541
593
} else {
542
- // Fallback or log if Browser.sidePanel is somehow not available (though polyfill should handle it)
543
- console . debug ( '[background] Browser.sidePanel API not available. Attempting chrome.sidePanel as fallback.' )
544
- // Keeping the original chrome check as a fallback, though ideally Browser.sidePanel should work.
545
- // eslint-disable-next-line no-undef
546
- if ( chrome && chrome . sidePanel ) {
547
- // eslint-disable-next-line no-undef
548
- await chrome . sidePanel . setOptions ( {
549
- tabId,
550
- path : 'IndependentPanel.html' ,
551
- enabled : true ,
552
- } )
553
- console . debug ( `[background] Side panel options set for tab ${ tabId } using chrome.sidePanel` )
554
- } else {
555
- console . debug ( '[background] chrome.sidePanel API also not available.' )
556
- }
594
+ // Log if Browser.sidePanel is somehow not available (polyfill should generally handle this)
595
+ console . warn ( '[background] Browser.sidePanel API not available. Side panel options not set.' )
557
596
}
558
597
} catch ( error ) {
559
598
console . error ( '[background] Error in tabs.onUpdated listener callback:' , error , tabId , info )
0 commit comments