From 6dcb579de42ba3d71c687027811d8f11e3f7cb9c Mon Sep 17 00:00:00 2001 From: Alex Schneider Date: Sat, 17 Oct 2015 16:15:42 -0700 Subject: [PATCH 1/3] Fix sanitization by constructing elements in Javascript. This is still technically a hack, the elements should really be defined in a view with templates, but it fixes the issue with sanitize where it can't sanitize if it can't parse HTML. --- js/plugins.js | 108 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/js/plugins.js b/js/plugins.js index 6717fe2..b028069 100644 --- a/js/plugins.js +++ b/js/plugins.js @@ -85,7 +85,7 @@ var UrlPlugin = function(name, urlCallback) { */ var contentForMessage = function(message) { message.metadata = []; - message.text = $sanitize(message.text); + // message.text = $sanitize(message.text); var addPluginContent = function(content, pluginName, num) { if (num) { pluginName += " " + num; @@ -187,7 +187,14 @@ plugins.factory('userPlugins', function() { var addMatch = function(match) { for (var i = 0; match && i < match.length; i++) { var id = match[i].substr(match[i].length - 22, match[i].length); - content.push(''); + // content.push(''); + var element = angular.element('') + .attr('src', '//embed.spotify.com/?uri=spotify:track:' + id) + .attr('width', '300') + .attr('height', '80') + .attr('frameborder', '0') + .attr('allowtransparency', 'true'); + content.push(element.prop('outerHTML')); } }; addMatch(message.match(/spotify:track:([a-zA-Z-0-9]{22})/g)); @@ -206,8 +213,15 @@ plugins.factory('userPlugins', function() { if (match){ var token = match[1], - embedurl = "https://www.youtube.com/embed/" + token + "?html5=1&iv_load_policy=3&modestbranding=1&rel=0&showinfo=0"; - return ''; + embedurl = "https://www.youtube.com/embed/" + token + "?html5=1&iv_load_policy=3&modestbranding=1&rel=0&showinfo=0", + element = angular.element('') + .attr('src', embedurl) + .attr('width', '560') + .attr('height', '315') + .attr('frameborder', '0') + .attr('allowfullscreen', 'true'); + return element.prop('outerHTML'); + // return ''; } }); @@ -225,7 +239,13 @@ plugins.factory('userPlugins', function() { if (match) { var id = match[1]; var embedurl = 'https://www.dailymotion.com/embed/video/' + id + '?html&controls=html&startscreen=html&info=0&logo=0&related=0'; - return ''; + var element = angular.element('') + .attr('src', embedurl) + .attr('width', '480') + .attr('height', '270') + .attr('frameborder', '0'); + return element.prop('outerHTML'); + // return ''; } return null; @@ -242,7 +262,13 @@ plugins.factory('userPlugins', function() { if (match) { var id = match[1]; var embedurl = 'http://www.allocine.fr/_video/iblogvision.aspx?cmedia=' + id; - return ''; + var element = angular.element('') + .attr('src', embedurl) + .attr('width', '480') + .attr('height', '270') + .attr('frameborder', '0'); + return element.prop('outerHTML'); + // return ''; } return null; @@ -264,8 +290,14 @@ plugins.factory('userPlugins', function() { // TODO strip an existing dl=0 parameter url = url + "?dl=1"; } - - return ''; + var element = angular.element('') + .attr('target', '_blank') + .attr('href', url) + .append(angular.element('') + .addClass('embed') + .attr('src', url)); + return element.prop('outerHTML'); + // return ''; } }); @@ -274,7 +306,13 @@ plugins.factory('userPlugins', function() { */ var videoPlugin = new UrlPlugin('video', function(url) { if (url.match(/\.(mp4|webm|ogv)\b/i)) { - return ''; + var element = angular.element('') + .addClass('embed') + .attr('width', '560') + .append(angular.element('') + .attr('src', url)); + return element.prop('outerHTML'); + // return ''; } }); @@ -283,13 +321,27 @@ plugins.factory('userPlugins', function() { */ var cloudmusicPlugin = new UrlPlugin('cloud music', function(url) { /* SoundCloud http://help.soundcloud.com/customer/portal/articles/247785-what-widgets-can-i-use-from-soundcloud- */ + var element; if (url.match(/^https?:\/\/soundcloud.com\//)) { - return ''; + element = angular.element('') + .attr('width', '100%') + .attr('height', '120') + .attr('scrolling', 'no') + .attr('frameborder', 'no') + .attr('src', 'https://w.soundcloud.com/player/?url=' + url + '&color=ff6600&auto_play=false&show_artwork=true'); + return element.prop('outerHTML'); + // return ''; } /* MixCloud */ if (url.match(/^https?:\/\/([a-z]+\.)?mixcloud.com\//)) { - return ''; + element = angular.element('') + .attr('width', '480') + .attr('height', '60') + .attr('frameborder', '0') + .attr('src', '//www.mixcloud.com/widget/iframe/?feed=' + url + '&mini=1&stylecolor=&hide_artwork=&embed_type=widget_standard&hide_tracklist=1&hide_cover='); + return element.prop('outerHTML'); + // return ''; } }); @@ -298,7 +350,15 @@ plugins.factory('userPlugins', function() { */ var googlemapPlugin = new UrlPlugin('Google Map', function(url) { if (url.match(/^https?:\/\/maps\.google\./i) || url.match(/^https?:\/\/(?:[\w]+\.)?google\.[\w]+\/maps/i)) { - return ''; + var element = angular.element('') + .attr('width', '450') + .attr('height', '350') + .attr('frameborder', '0') + .attr('scrolling', 'no') + .attr('marginheight', '0') + .attr('src', url + '&output=embed'); + return element.prop('outerHTML'); + // return ''; } }); @@ -329,7 +389,11 @@ plugins.factory('userPlugins', function() { var location = match[2]; var city = match[match.length - 1].slice(0, -1); url = "http://www.yr.no/" + language + "/" + location + "avansert_meteogram.png"; - return "Meteogram for " + city + ""; + var element = angular.element('') + .attr('src', url) + .attr('alt', 'Meteogram for ' + city); + return element.prop('outerHTML'); + // return "Meteogram for " + city + ""; } }); @@ -365,7 +429,14 @@ plugins.factory('userPlugins', function() { var id = url.match(regex); if (id) { var src = "https://media.giphy.com/media/" + id[1] + "/giphy.gif"; - return ''; + var element = angular.element('') + .attr('target', '_blank') + .attr('href', url) + .append(angular.element('') + .addClass('embed') + .attr('src', src)); + return element.prop('outerHTML'); + // return ''; } }); @@ -402,7 +473,14 @@ plugins.factory('userPlugins', function() { match = url.match(regexp); if (match) { var id = match[2], embedurl = "https://vine.co/v/" + id + "/embed/simple?audio=1"; - return ''; + var element = angular.element('') + .addClass('vine-embed') + .attr('src', embedurl) + .attr('width', '600') + .attr('height', '600') + .attr('frameborder', '0'); + return element.prop('outerHTML') + ''; + // return '; } }); From 1d628ed8493f1a7ee1db48b02ce962489b8a8af3 Mon Sep 17 00:00:00 2001 From: Alex Schneider Date: Sat, 17 Oct 2015 16:23:48 -0700 Subject: [PATCH 2/3] Remove comments that we don't really need --- js/plugins.js | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/js/plugins.js b/js/plugins.js index b028069..3bff6ab 100644 --- a/js/plugins.js +++ b/js/plugins.js @@ -57,7 +57,7 @@ var UrlPlugin = function(name, urlCallback) { * to display when messages are received. * */ - plugins.service('plugins', ['userPlugins', '$sce', '$sanitize', function(userPlugins, $sce, $sanitize) { + plugins.service('plugins', ['userPlugins', '$sce', function(userPlugins, $sce) { /* * Defines the plugin manager object @@ -85,7 +85,6 @@ var UrlPlugin = function(name, urlCallback) { */ var contentForMessage = function(message) { message.metadata = []; - // message.text = $sanitize(message.text); var addPluginContent = function(content, pluginName, num) { if (num) { pluginName += " " + num; @@ -187,7 +186,6 @@ plugins.factory('userPlugins', function() { var addMatch = function(match) { for (var i = 0; match && i < match.length; i++) { var id = match[i].substr(match[i].length - 22, match[i].length); - // content.push(''); var element = angular.element('') .attr('src', '//embed.spotify.com/?uri=spotify:track:' + id) .attr('width', '300') @@ -221,7 +219,6 @@ plugins.factory('userPlugins', function() { .attr('frameborder', '0') .attr('allowfullscreen', 'true'); return element.prop('outerHTML'); - // return ''; } }); @@ -245,7 +242,6 @@ plugins.factory('userPlugins', function() { .attr('height', '270') .attr('frameborder', '0'); return element.prop('outerHTML'); - // return ''; } return null; @@ -268,7 +264,6 @@ plugins.factory('userPlugins', function() { .attr('height', '270') .attr('frameborder', '0'); return element.prop('outerHTML'); - // return ''; } return null; @@ -297,7 +292,6 @@ plugins.factory('userPlugins', function() { .addClass('embed') .attr('src', url)); return element.prop('outerHTML'); - // return ''; } }); @@ -312,7 +306,6 @@ plugins.factory('userPlugins', function() { .append(angular.element('') .attr('src', url)); return element.prop('outerHTML'); - // return ''; } }); @@ -330,7 +323,6 @@ plugins.factory('userPlugins', function() { .attr('frameborder', 'no') .attr('src', 'https://w.soundcloud.com/player/?url=' + url + '&color=ff6600&auto_play=false&show_artwork=true'); return element.prop('outerHTML'); - // return ''; } /* MixCloud */ @@ -341,7 +333,6 @@ plugins.factory('userPlugins', function() { .attr('frameborder', '0') .attr('src', '//www.mixcloud.com/widget/iframe/?feed=' + url + '&mini=1&stylecolor=&hide_artwork=&embed_type=widget_standard&hide_tracklist=1&hide_cover='); return element.prop('outerHTML'); - // return ''; } }); @@ -358,7 +349,6 @@ plugins.factory('userPlugins', function() { .attr('marginheight', '0') .attr('src', url + '&output=embed'); return element.prop('outerHTML'); - // return ''; } }); @@ -393,7 +383,6 @@ plugins.factory('userPlugins', function() { .attr('src', url) .attr('alt', 'Meteogram for ' + city); return element.prop('outerHTML'); - // return "Meteogram for " + city + ""; } }); @@ -436,7 +425,6 @@ plugins.factory('userPlugins', function() { .addClass('embed') .attr('src', src)); return element.prop('outerHTML'); - // return ''; } }); @@ -480,7 +468,6 @@ plugins.factory('userPlugins', function() { .attr('height', '600') .attr('frameborder', '0'); return element.prop('outerHTML') + ''; - // return '; } }); From a02573a9c2ac025f7543df32f0f67cec12472951 Mon Sep 17 00:00:00 2001 From: Tor Hveem Date: Mon, 19 Oct 2015 14:51:14 +0200 Subject: [PATCH 3/3] Construct plugin elements async. Fixes #660 This commit changes the plugins that uses angular.element to load resources directly to instead return a function that runs angular.element. When the browser runs angular.element('') and gets a src attr it will fetch the content in the src. This makes the browser running gb automatically fetch all plugin content that gets linked even if the user didn't request it. Maybe we could change our plugin architecture to split the match function and the generator functions so this could be solved more elegantly. --- js/plugins.js | 68 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/js/plugins.js b/js/plugins.js index 3bff6ab..f659012 100644 --- a/js/plugins.js +++ b/js/plugins.js @@ -285,13 +285,16 @@ plugins.factory('userPlugins', function() { // TODO strip an existing dl=0 parameter url = url + "?dl=1"; } - var element = angular.element('') - .attr('target', '_blank') - .attr('href', url) - .append(angular.element('') - .addClass('embed') - .attr('src', url)); - return element.prop('outerHTML'); + return function() { + var element = this.getElement(); + var imgElem = angular.element('') + .attr('target', '_blank') + .attr('href', url) + .append(angular.element('') + .addClass('embed') + .attr('src', url)); + element.innerHTML = imgElem.prop('outerHTML'); + }; } }); @@ -300,12 +303,15 @@ plugins.factory('userPlugins', function() { */ var videoPlugin = new UrlPlugin('video', function(url) { if (url.match(/\.(mp4|webm|ogv)\b/i)) { - var element = angular.element('') - .addClass('embed') - .attr('width', '560') - .append(angular.element('') - .attr('src', url)); - return element.prop('outerHTML'); + return function() { + var element = this.getElement(); + var velement = angular.element('') + .addClass('embed') + .attr('width', '560') + .append(angular.element('') + .attr('src', url)); + element.innerHTML = velement.prop('outerHTML'); + }; } }); @@ -375,14 +381,17 @@ plugins.factory('userPlugins', function() { var regexp = /^https?:\/\/(?:www\.)?yr\.no\/(place|stad|sted|sadji|paikka)\/(([^\s.;,(){}<>\/]+\/){3,})/; var match = url.match(regexp); if (match) { - var language = match[1]; - var location = match[2]; - var city = match[match.length - 1].slice(0, -1); - url = "http://www.yr.no/" + language + "/" + location + "avansert_meteogram.png"; - var element = angular.element('') - .attr('src', url) - .attr('alt', 'Meteogram for ' + city); - return element.prop('outerHTML'); + return function() { + var element = this.getElement(); + var language = match[1]; + var location = match[2]; + var city = match[match.length - 1].slice(0, -1); + url = "http://www.yr.no/" + language + "/" + location + "avansert_meteogram.png"; + var ielement = angular.element('') + .attr('src', url) + .attr('alt', 'Meteogram for ' + city); + element.innerHTML = ielement.prop('outerHTML'); + }; } }); @@ -418,13 +427,16 @@ plugins.factory('userPlugins', function() { var id = url.match(regex); if (id) { var src = "https://media.giphy.com/media/" + id[1] + "/giphy.gif"; - var element = angular.element('') - .attr('target', '_blank') - .attr('href', url) - .append(angular.element('') - .addClass('embed') - .attr('src', src)); - return element.prop('outerHTML'); + return function() { + var element = this.getElement(); + var gelement = angular.element('') + .attr('target', '_blank') + .attr('href', url) + .append(angular.element('') + .addClass('embed') + .attr('src', src)); + element.innerHTML = gelement.prop('outerHTML'); + }; } });