@@ -79,7 +79,7 @@ var _VirtualDom_nodeNS = F2(function(namespace, tag)
7979 return {
8080 $ : __2_NODE ,
8181 __tag : tag ,
82- __facts : _VirtualDom_organizeFacts ( factList ) ,
82+ __facts : _VirtualDom_organizeFacts ( factList , tag ) ,
8383 __kids : kids ,
8484 __namespace : namespace ,
8585 __descendantsCount : descendantsCount
@@ -110,7 +110,7 @@ var _VirtualDom_keyedNodeNS = F2(function(namespace, tag)
110110 return {
111111 $ : __2_KEYED_NODE ,
112112 __tag : tag ,
113- __facts : _VirtualDom_organizeFacts ( factList ) ,
113+ __facts : _VirtualDom_organizeFacts ( factList , tag ) ,
114114 __kids : kids ,
115115 __namespace : namespace ,
116116 __descendantsCount : descendantsCount
@@ -130,7 +130,7 @@ function _VirtualDom_custom(factList, model, render, diff)
130130{
131131 return {
132132 $ : __2_CUSTOM ,
133- __facts : _VirtualDom_organizeFacts ( factList ) ,
133+ __facts : _VirtualDom_organizeFacts ( factList , '' ) ,
134134 __model : model ,
135135 __render : render ,
136136 __diff : diff
@@ -285,10 +285,12 @@ var _VirtualDom_attributeNS = F3(function(namespace, key, value)
285285// js_html ones are so weird that I prefer to see them near each other.
286286
287287
288+ var _VirtualDom_RE_iframe = / ^ i f r a m e $ / i;
288289var _VirtualDom_RE_script = / ^ s c r i p t $ / i;
289290var _VirtualDom_RE_on_formAction = / ^ ( o n | f o r m A c t i o n $ ) / i;
290291var _VirtualDom_RE_js = / ^ \s * j \s * a \s * v \s * a \s * s \s * c \s * r \s * i \s * p \s * t \s * : / i;
291292var _VirtualDom_RE_js_html = / ^ \s * ( j \s * a \s * v \s * a \s * s \s * c \s * r \s * i \s * p \s * t \s * : | d \s * a \s * t \s * a \s * : \s * t \s * e \s * x \s * t \s * \/ \s * h \s * t \s * m \s * l \s * ( , | ; ) ) / i;
293+ var _VirtualDom_RE_sandboxAllowScripts = / ( ^ | [ \t \n \f \r ] ) a l l o w - s c r i p t s (? = $ | [ \t \n \f \r ] ) / gi;
292294
293295
294296function _VirtualDom_noScript ( tag )
@@ -384,9 +386,12 @@ var _VirtualDom_mapEventRecord = F2(function(func, record)
384386// ORGANIZE FACTS
385387
386388
387- function _VirtualDom_organizeFacts ( factList )
389+ function _VirtualDom_organizeFacts ( factList , elementTag )
388390{
389- for ( var facts = { } ; factList . b ; factList = factList . b ) // WHILE_CONS
391+ var isIframe = _VirtualDom_RE_iframe . test ( elementTag ) ;
392+
393+ // For iframe, make sure attributes come first – see _VirtualDom_iframeSandbox.
394+ for ( var facts = isIframe ? { a__1_ATTR : { } } : { } ; factList . b ; factList = factList . b ) // WHILE_CONS
390395 {
391396 var entry = factList . a ;
392397
@@ -409,6 +414,11 @@ function _VirtualDom_organizeFacts(factList)
409414 : subFacts [ key ] = value ;
410415 }
411416
417+ if ( isIframe )
418+ {
419+ _VirtualDom_iframeSandbox ( facts ) ;
420+ }
421+
412422 return facts ;
413423}
414424
@@ -418,6 +428,104 @@ function _VirtualDom_addClass(object, key, newClass)
418428 object [ key ] = classes ? classes + ' ' + newClass : newClass ;
419429}
420430
431+ function _VirtualDom_iframeSandbox ( facts )
432+ {
433+ var hasSrcdocProperty = false ;
434+ var hasSandboxProperty = false ;
435+ var srcAttrs = [ ] ;
436+ var srcdocAttrs = [ ] ;
437+ var sandboxAttrs = [ ] ;
438+
439+ for ( var key in facts )
440+ {
441+ switch ( key )
442+ {
443+ // This handles attributes, but what about `a__1_ATTR_NS`, by the way?
444+ // It is only possible to set the sandbox via `.setAttributeNS(null, 'sandbox', 'allow-x')`
445+ // but the Elm API always passes a string as the first argument (the namespace).
446+ case 'a__1_ATTR' :
447+ for ( var attr in facts [ key ] )
448+ {
449+ switch ( attr . toLowerCase ( ) )
450+ {
451+ case 'src' :
452+ srcAttrs . push ( attr ) ;
453+ break ;
454+
455+ case 'srcdoc' :
456+ srcdocAttrs . push ( attr ) ;
457+ break ;
458+
459+ case 'sandbox' :
460+ sandboxAttrs . push ( attr ) ;
461+ break ;
462+ }
463+ }
464+ break ;
465+
466+ case 'srcdoc' :
467+ hasSrcdocProperty = true ;
468+ break ;
469+
470+ case 'sandbox' :
471+ hasSandboxProperty = true ;
472+ break ;
473+ }
474+ }
475+
476+ if ( hasSrcdocProperty || srcdocAttrs . length > 0 )
477+ {
478+ // If srcdoc is set, but not sandbox, use a default sandbox that protects against scripts.
479+ if ( ! hasSandboxProperty && sandboxAttrs . length === 0 )
480+ {
481+ // Setting the sandbox attribute disallows everything not mentioned.
482+ // We’re especially interested in NOT having allow-scripts.
483+ // The below permissions are the only ones that don’t require JavaScript
484+ // and might be expected to be allowed by Elm developers, since Elm used
485+ // to not set the sandbox attribute at all (allowing everything).
486+ facts . a__1_ATTR . sandbox = 'allow-downloads allow-forms allow-top-navigation' ;
487+ }
488+ // Otherwise, use the user provided sandbox, but make sure it does not use allow-scripts.
489+ else
490+ {
491+ if ( hasSandboxProperty )
492+ {
493+ facts . sandbox = String ( facts . sandbox ) . replace ( _VirtualDom_RE_sandboxAllowScripts , '' ) ;
494+ }
495+
496+ for ( var i = 0 ; i < sandboxAttrs . length ; i ++ )
497+ {
498+ var attr = sandboxAttrs [ i ] ;
499+ facts . a__1_ATTR [ attr ] = facts . a__1_ATTR [ attr ] . replace ( _VirtualDom_RE_sandboxAllowScripts , '' ) ;
500+ }
501+ }
502+ }
503+
504+ // Move src and srcdoc attributes last (in object iteration order), so that sandbox is set first.
505+ // That’s important when the iframe is already inserted into the document.
506+ // Otherwise, at least srcdoc can execute (without restrictions) before the sandbox is set.
507+ // Attributes are already guaranteed to be before properties – see `_VirtualDom_organizeFacts`.
508+ // Note that srcdoc overrides src according to the spec. If you however set first `src` and then
509+ // immediately `srcdoc` with JavaScript, you can still see the request for `src` show up in the
510+ // browser network panel, being cancelled right away.
511+ // Here, we choose to apply consistently apply `srcdoc` first.
512+ // Note that this is needed both when we add a default srcdoc, and when the user added srcdoc themselves.
513+ for ( var i = 0 ; i < srcdocAttrs . length ; i ++ )
514+ {
515+ var attr = srcdocAttrs [ i ] ;
516+ var value = facts . a__1_ATTR [ attr ] ;
517+ delete facts . a__1_ATTR [ attr ] ;
518+ facts . a__1_ATTR [ attr ] = value ;
519+ }
520+ for ( var i = 0 ; i < srcAttrs . length ; i ++ )
521+ {
522+ var attr = srcAttrs [ i ] ;
523+ var value = facts . a__1_ATTR [ attr ] ;
524+ delete facts . a__1_ATTR [ attr ] ;
525+ facts . a__1_ATTR [ attr ] = value ;
526+ }
527+ }
528+
421529
422530
423531// RENDER
0 commit comments