diff --git a/src/js/libpannellum.js b/src/js/libpannellum.js index 312467a..5a5eb8a 100644 --- a/src/js/libpannellum.js +++ b/src/js/libpannellum.js @@ -175,9 +175,9 @@ function Renderer(container, image, imageType, video) { faceImg.side = s; faceImg.onload = onLoad; if (imageType == 'multires') { - faceImg.src = path.replace('%s',sides[s]) + '.' + image.extension; + faceImg.src = encodeURI(path.replace('%s', sides[s]) + '.' + image.extension); } else { - faceImg.src = image[s].src; + faceImg.src = encodeURI(image[s].src); } } @@ -934,7 +934,7 @@ function Renderer(container, image, imageType, video) { node.textureLoaded = true; delete node.image; }; - node.image.src = node.path + '.' + image.extension; + node.image.src = encodeURI(node.path + '.' + image.extension); } } diff --git a/src/js/pannellum.js b/src/js/pannellum.js index 482ffa5..222fdb7 100644 --- a/src/js/pannellum.js +++ b/src/js/pannellum.js @@ -290,7 +290,7 @@ function init() { if (config.basePath && !absoluteURL(p)) { p = config.basePath + p; } - panoImage[i].src = p; + panoImage[i].src = encodeURI(p); } } else if (config.type == 'multires') { onImageLoad(); @@ -308,7 +308,7 @@ function init() { for (i = 0; i < config.panoramas.length; i++) { if (panoImage.canPlayType(config.panoramas[i].type).length > 0) { panoImage.crossOrigin = 'anonymous'; - panoImage.src = absoluteURL(config.panoramas[i].file) ? config.panoramas[i].file : p + config.panoramas[i].file; + panoImage.src = encodeURI(absoluteURL(config.panoramas[i].file) ? config.panoramas[i].file : p + config.panoramas[i].file); break; } } @@ -333,7 +333,7 @@ function init() { if (xhr.status != 200) { // Display error if image can't be loaded var a = document.createElement('a'); - a.href = p; + a.href = encodeURI(p); a.innerHTML = a.href; anError('The file ' + a.outerHTML + ' could not be accessed.'); } @@ -1120,15 +1120,15 @@ function createHotSpots() { }); config.hotSpots.forEach(function(hs) { var div = document.createElement('div'); - div.className = 'pnlm-hotspot pnlm-tooltip pnlm-sprite pnlm-' + hs.type; + div.className = 'pnlm-hotspot pnlm-tooltip pnlm-sprite pnlm-' + escapeHTML(hs.type); var span = document.createElement('span'); - span.innerHTML = hs.text; + span.innerHTML = escapeHTML(hs.text); var a; if (hs.URL) { a = document.createElement('a'); - a.setAttribute('href', hs.URL); + a.setAttribute('href', encodeURI(hs.URL)); a.setAttribute('target', '_blank'); renderContainer.appendChild(a); div.style.cursor = 'pointer'; @@ -1136,19 +1136,19 @@ function createHotSpots() { a.appendChild(div); } else if (hs.video) { var video = document.createElement('video'); - video.setAttribute('src',hs.video); - video.setAttribute('controls',true); - video.setAttribute('style','width:' + hs.width + 'px'); + video.setAttribute('src', encodeURI(hs.video)); + video.setAttribute('controls', true); + video.setAttribute('style', 'width:' + hs.width + 'px'); renderContainer.appendChild(div); span.appendChild(video); } else if (hs.image) { a = document.createElement('a'); - a.setAttribute('href', hs.image); + a.setAttribute('href', encodeURI(hs.image)); a.setAttribute('target', '_blank'); span.appendChild(a); var image = document.createElement('img'); - image.setAttribute('src',hs.image); - image.setAttribute('style','width:' + hs.width + 'px'); + image.setAttribute('src', encodeURI(hs.image)); + image.setAttribute('style', 'width:' + hs.width + 'px'); renderContainer.appendChild(div); a.appendChild(image); @@ -1300,7 +1300,7 @@ function processOptions() { preview = new Image(); preview.crossOrigin = 'anonymous'; - preview.src = p; + preview.src = encodeURI(p); preview.className = 'pnlm-preview-img'; renderContainer.appendChild(preview); } @@ -1310,17 +1310,17 @@ function processOptions() { if (config.hasOwnProperty(key)) { switch(key) { case 'title': - infoDisplay.title.innerHTML = config[key]; + infoDisplay.title.innerHTML = escapeHTML(config[key]); infoDisplay.container.style.display = 'inline'; break; case 'author': - infoDisplay.author.innerHTML = 'by ' + config[key]; + infoDisplay.author.innerHTML = 'by ' + escapeHTML(config[key]); infoDisplay.container.style.display = 'inline'; break; case 'fallback': - infoDisplay.errorMsg.innerHTML = '

Your browser does not support WebGL.
Click here to view this panorama in an alternative viewer.

'; + infoDisplay.errorMsg.innerHTML = '

Your browser does not support WebGL.
Click here to view this panorama in an alternative viewer.

'; break; case 'hfov': @@ -1358,11 +1358,6 @@ function processOptions() { config.autoRotateStopDelay = config[key]; break; - case 'header': - // Add contents to header - document.getElementsByTagName('head')[0].innerHTML += config[key]; - break; - case 'showZoomCtrl': if (config[key]) { // Show zoom controls @@ -1549,6 +1544,21 @@ function loadScene(sceneId, targetPitch, targetYaw) { } /** + * Escapes HTML string (to mitigate possible DOM XSS attacks). + * @private + * @param {string} s - String to escape + * @returns {string} Escaped string + */ +function escapeHTML(s) { + return String(s).replace(/&/g, '&') + .replace('"', '"') + .replace("'", ''') + .replace('<', '<') + .replace('>', '>') + .replace('/', '/'); +} + +/** * Returns the pitch of the center of the view. * @memberof Viewer * @instance