Procházet zdrojové kódy

fix: embed.js doesn't expands iframes height (#18301)

also including some refactoring:
- add `// @ts-check`
- use Map to completely avoid prototype pollution
- assign random id to each iframe for reduce chance to brute-force attack, and leak of iframe counts
- check iframe.contentWindow and MessageEvent.source to validate message is coming from correct iframe (it works on latest Chrome/Firefox/Safari but I'm not sure this is allowed by spec)

follow-up of #17420
fix #18299
rinsuki před 2 roky
rodič
revize
6e736f2452
1 změnil soubory, kde provedl 29 přidání a 7 odebrání
  1. 29 7
      public/embed.js

+ 29 - 7
public/embed.js

@@ -1,6 +1,11 @@
+// @ts-check
+
 (function() {
   'use strict';
 
+  /**
+   * @param {() => void} loaded
+   */
   var ready = function(loaded) {
     if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {
       loaded();
@@ -10,25 +15,42 @@
   };
 
   ready(function() {
-    var iframes = [];
+    /** @type {Map<number, HTMLIFrameElement>} */
+    var iframes = new Map();
 
     window.addEventListener('message', function(e) {
       var data = e.data || {};
 
-      if (data.type !== 'setHeight' || !iframes[data.id] || window.location.origin !== e.origin || data.id.toString() === '__proto__') {
+      if (typeof data !== 'object' || data.type !== 'setHeight' || !iframes.has(data.id)) {
+        return;
+      }
+
+      var iframe = iframes.get(data.id);
+
+      if ('source' in e && iframe.contentWindow !== e.source) {
         return;
       }
 
-      iframes[data.id].height = data.height;
+      iframe.height = data.height;
     });
 
     [].forEach.call(document.querySelectorAll('iframe.mastodon-embed'), function(iframe) {
-      iframe.scrolling      = 'no';
-      iframe.style.overflow = 'hidden';
+      // select unique id for each iframe
+      var id = 0, failCount = 0, idBuffer = new Uint32Array(1);
+      while (id === 0 || iframes.has(id)) {
+        id = crypto.getRandomValues(idBuffer)[0];
+        failCount++;
+        if (failCount > 100) {
+          // give up and assign (easily guessable) unique number if getRandomValues is broken or no luck
+          id = -(iframes.size + 1);
+          break;
+        }
+      }
 
-      iframes.push(iframe);
+      iframes.set(id, iframe);
 
-      var id = iframes.length - 1;
+      iframe.scrolling      = 'no';
+      iframe.style.overflow = 'hidden';
 
       iframe.onload = function() {
         iframe.contentWindow.postMessage({