@@ -63,24 +63,38 @@ const compileWhitelist = (
6363const isDownloadMessageAllowed = ( {
6464 data,
6565 url,
66- compiledDownloadWhitelist ,
66+ downloadWhitelist ,
6767} : {
68- data : string ,
69- url : string ,
70- compiledDownloadWhitelist : readonly RegExp [ ] ,
68+ data : string ;
69+ url : string ;
70+ downloadWhitelist : { origin : string ; allowedFileExtensions : string [ ] } [ ] ;
7171} ) : boolean => {
72+ let parsedData ;
73+
7274 try {
73- const parsedData = JSON . parse ( data ) ;
74-
75- if ( parsedData ?. method === 'download' ) {
76- return _passesWhitelist ( compiledDownloadWhitelist , url ) ;
77- }
75+ parsedData = JSON . parse ( data ) ;
7876 } catch {
79- // Ignore invalid JSON — treat as non-download message
77+ return true ; // Invalid JSON — treat as non-download message
78+ }
79+
80+ if ( parsedData . method !== 'download' ) {
81+ return true ;
82+ }
83+
84+ const { origin } = new URL ( url ) ;
85+ const fileExtension = parsedData . params ?. fileName ?. split ( '.' ) . pop ( ) ?. toLowerCase ( ) ;
86+
87+ if ( ! fileExtension || ! origin ) {
88+ return false ;
8089 }
8190
82- // Non-download messages are allowed by default
83- return true ;
91+ const matchingRule = downloadWhitelist . find ( rule => {
92+ const ruleOriginRegex = stringWhitelistToRegex ( rule . origin ) ;
93+
94+ return ruleOriginRegex . test ( origin ) && rule . allowedFileExtensions . includes ( fileExtension ) ;
95+ } ) ;
96+
97+ return Boolean ( matchingRule ) ;
8498} ;
8599
86100const urlToProtocolScheme = ( url : string ) : string | null => {
@@ -175,7 +189,7 @@ export {
175189} ;
176190
177191export const useWebWiewLogic = ( {
178- downloadOriginWhitelist ,
192+ downloadWhitelist ,
179193 startInLoadingState,
180194 onLoadStart,
181195 onLoad,
@@ -190,7 +204,7 @@ export const useWebWiewLogic = ({
190204 validateMeta,
191205 validateData,
192206} : {
193- downloadOriginWhitelist : readonly string [ ] ;
207+ downloadWhitelist : { origin : string ; allowedFileExtensions : string [ ] } [ ] ;
194208 startInLoadingState ?: boolean
195209 onLoadStart ?: ( event : WebViewNavigationEvent ) => void ;
196210 onLoad ?: ( event : WebViewNavigationEvent ) => void ;
@@ -263,7 +277,9 @@ export const useWebWiewLogic = ({
263277
264278 const onMessage = useCallback ( ( event : WebViewMessageEvent ) => {
265279 const { nativeEvent } = event ;
266- if ( ! passesWhitelistUse ( nativeEvent . url ) ) return ;
280+ const { url } = nativeEvent ;
281+
282+ if ( ! passesWhitelistUse ( url ) ) return ;
267283
268284 // TODO: can/should we perform any other validation?
269285 try {
@@ -272,10 +288,10 @@ export const useWebWiewLogic = ({
272288
273289 if ( ! isDownloadMessageAllowed ( {
274290 data : parsedData . data ,
275- url : nativeEvent . url ,
276- compiledDownloadWhitelist : compileWhitelist ( downloadOriginWhitelist , [ ] ) ,
291+ downloadWhitelist ,
292+ url ,
277293 } ) ) {
278- console . warn ( 'Download request rejected: origin not in download whitelist' ) ;
294+ console . warn ( 'Download request rejected: origin not in download whitelist or file extension not allowed ' ) ;
279295 return ;
280296 }
281297
@@ -286,7 +302,7 @@ export const useWebWiewLogic = ({
286302 } catch ( err ) {
287303 console . error ( 'Error parsing WebView message' , err ) ;
288304 }
289- } , [ onMessageProp , passesWhitelistUse , validateData , validateMeta , downloadOriginWhitelist ] ) ;
305+ } , [ onMessageProp , passesWhitelistUse , validateData , validateMeta , downloadWhitelist ] ) ;
290306
291307 const onLoadingProgress = useCallback ( ( event : WebViewProgressEvent ) => {
292308 const { nativeEvent : { progress } } = event ;
0 commit comments