about summary refs log tree commit diff
path: root/public
diff options
context:
space:
mode:
authorrinsuki <428rinsuki+git@gmail.com>2022-05-04 10:20:44 +0900
committerGitHub <noreply@github.com>2022-05-04 03:20:44 +0200
commit6e736f2452d2e6fdd4da6d8f6f2f44da9d83fa4f (patch)
treed20669ab3c0f8750e425bf34ebc0a4e7d8c7cdc1 /public
parenta01580f09f33c275fcc0ffe616b5b5b403f46cae (diff)
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
Diffstat (limited to 'public')
-rw-r--r--public/embed.js36
1 files changed, 29 insertions, 7 deletions
diff --git a/public/embed.js b/public/embed.js
index d597fd33c..5607c24d5 100644
--- a/public/embed.js
+++ b/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({