Skip to content

Commit

Permalink
- Various security related changes and code cleanup based on the disc…
Browse files Browse the repository at this point in the history
…ussion here: #1 (comment)

- Added test page for testing cross-domain functionality.
  • Loading branch information
pdille committed Apr 24, 2014
1 parent e4d0e5f commit b0b8835
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 52 deletions.
56 changes: 34 additions & 22 deletions js/org/gigapan/timelapse/crossdomain_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,38 +63,50 @@ function setupPostMessageHandlers() {
});

// Handles the cross-domain iframe request to seek a time machine to the specified time.
pm.bind("timemachine-seek", function(data) {
if (timelapse)
timelapse.seek(data);
pm.bind("timemachine-seek", function(unsafe_data) {
if (unsafe_data && typeof(unsafe_data) !== 'undefined' && timelapse)
timelapse.seek(unsafe_data);

This comment has been minimized.

Copy link
@enguyen

enguyen May 13, 2014

var time = parseFloat(unsafe_data);

... so that one doesn't need to leave this file to know that unsafe_data is being handled properly.

});

// Handles the cross-domain iframe request to change the view of a time machine.
pm.bind("timemachine-set-view", function(data) {
if (timelapse) {
timelapse.setNewView(data.view, data.doWarp, data.doPlay);
pm.bind("timemachine-set-view", function(unsafe_data) {
if (unsafe_data && typeof(unsafe_data) !== 'undefined' && timelapse) {
// Sanitize data
var safe_data = {};

This comment has been minimized.

Copy link
@enguyen

enguyen May 13, 2014

I don't think a new object is necessary. This can simply be:

var view = timelapse.unsafeViewToView(unsafe_data.view);
var doWarp = !!unsafe_data.doWarp;
var doPlay = !!unsafe_data.doPlay;
timelapse.setNewView(view, doWarp, doPlay);
safe_data.view = timelapse.unsafeViewToView(unsafe_data.view);
safe_data.doWarp = !!unsafe_data.doWarp;
safe_data.doPlay = !!unsafe_data.doPlay;
timelapse.setNewView(safe_data.view, safe_data.doWarp, safe_data.doPlay);
}
});

// Handles the cross-domain iframe request of changing the view of a time machine based
// on a share URL.
pm.bind("timemachine-set-share-view", function(data) {
if (timelapse) {
var viewArray = data.v.split(",");
var view;
var doWarp = true;
if (viewArray)
view = timelapse.unsafeViewToView(viewArray);
if (view)
timelapse.setNewView(view, doWarp);
var time = data.t;
if (time)
timelapse.seek(time);
// Handles the cross-domain iframe request of changing the view of a time machine based on a share URL.
pm.bind("timemachine-set-share-view", function(unsafe_data) {
if (unsafe_data && typeof(unsafe_data) !== 'undefined' && timelapse) {
// If a share URL (e.g. #v=44.96185,59.06233,4.5,latLng&t=0.10) is passed in
// as a string, then unpack it based on the hash vars.
// Otherwise we are dealing with an object of unpacked hash vars, so move on.
if (typeof(unsafe_data) === "string") {
if (unsafe_data.substr(0,1) == "#")
unsafe_data = unsafe_data.slice(1);
unsafe_data = org.gigapan.Util.unpackVars(unsafe_data);
}

if (unsafe_data.v) {
var newView = timelapse.unsafeViewToView(unsafe_data.v.split(","));
// Always warp to the new view (i.e. pass in true for the second value)
timelapse.setNewView(newView, true);
}
if (unsafe_data.t) {
var newTime = parseFloat(unsafe_data.t);
timelapse.seek(newTime);
}

This comment has been minimized.

Copy link
@enguyen

enguyen May 13, 2014

Nice.

}
});

// Handles the cross-domain iframe request of going to a location on the presentation slider
pm.bind("timemachine-goto-presentation-slide", function(slideTitle) {
var slideId = slideTitle.split(' ').join('_');
pm.bind("timemachine-goto-presentation-slide", function(unsafe_slideTitle) {
var slideId = unsafe_slideTitle.split(' ').join('_');
var $slideContainer = $("#" + slideId).parent();
var keyframeId = $slideContainer.attr("id").split("_")[3];
timelapse.getPresentationSlider().getPresentationSliderViewer().selectAndGo($slideContainer, keyframeId, undefined, undefined, true);
Expand Down
8 changes: 4 additions & 4 deletions js/org/gigapan/timelapse/snaplapseViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1254,12 +1254,12 @@ function playCachedSnaplapse(snaplapseId) {
snaplapse.resetKeyframe();
if (usePresentationSlider) {
$("#" + composerDivId + " .snaplapse_keyframe_container").scrollLeft(0);
var unsafeHashVars = UTIL.getUnsafeHashVars();
var unsafeHashObj = UTIL.getUnsafeHashVars();
// Go to the desired keyframe if there is no shared view and no tour
if ( typeof unsafeHashVars.v == "undefined" && typeof unsafeHashVars.tour == "undefined") {
if ( typeof unsafeHashObj.v == "undefined" && typeof unsafeHashObj.tour == "undefined") {
var $desiredSlide;
if ( typeof unsafeHashVars.slide != "undefined")
$desiredSlide = $("#" + unsafeHashVars.slide);
if ( typeof unsafeHashObj.slide != "undefined")
$desiredSlide = $("#" + unsafeHashObj.slide);
if ($desiredSlide && $desiredSlide.length > 0)
$desiredSlide[0].click();
else {
Expand Down
91 changes: 65 additions & 26 deletions js/org/gigapan/timelapse/timelapse.js
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,42 @@ if (!window['$']) {
// Extract a safe view object from an unsafe view string.
var unsafeViewToView = function(viewParam) {
var view = null;

if (!viewParam)
return view;

This comment has been minimized.

Copy link
@enguyen

enguyen May 13, 2014

Isn't this just return null?


// If the view is not a string (i.e an object) then we need to break it up into one
// so that we can sanitize it below.

This comment has been minimized.

Copy link
@enguyen

enguyen May 13, 2014

I'm pretty confused by all of this. What's viewParam? A string/object, right? I don't understand the phrase "break an object up into a string" and the end of the block below, viewParam has been converted into an array of strings. This is probably straightforward but all the ambiguous index references and use of .length below really confuse the question of whether this thing is supposed to be a string or an array. Mind clarifying?

Also, let's change the name to unsafe_viewParam or add a comment up top indicating that all elements of this property are parseFloat'd below for safety.

if (viewParam.center || viewParam.bbox) {
var tmpViewParam = [];
if (viewParam.center) {
var isLatLng = false;
var centerView = viewParam.center;
for (var key in centerView) {
tmpViewParam.push(centerView[key]);
if (key == "lat")
isLatLng = true;
}
tmpViewParam.push(viewParam.zoom);
isLatLng ? tmpViewParam.push("latLng") : tmpViewParam.push("pts");
viewParam = tmpViewParam;
} else if (viewParam.bbox) {
var isLatLng = false;
var bboxView = viewParam.bbox;
for (var key in bboxView) {
if (key == "ne" || key == "sw") {
isLatLng = true;
for (var innerKey in bboxView[key])
tmpViewParam.push(bboxView[key][innerKey]);
} else {
tmpViewParam.push(bboxView[key]);
}
}
isLatLng ? tmpViewParam.push("latLng") : tmpViewParam.push("pts");
viewParam = tmpViewParam;
}
}

if (viewParam.indexOf("latLng") != -1) {
if (viewParam.length == 4)
view = {
Expand Down Expand Up @@ -1392,12 +1428,12 @@ if (!window['$']) {

// Handle any hash variables related to time machines
var handleHashChange = function() {
var unsafeHashVars = UTIL.getUnsafeHashVars();
var newView = getViewFromHash(unsafeHashVars);
var newTime = getTimeFromHash(unsafeHashVars);
var tourJSON = getTourFromHash(unsafeHashVars);
var presentationJSON = getPresentationFromHash(unsafeHashVars);
var modisLock = getModisLockFromHash(unsafeHashVars);
var unsafeHashObj = UTIL.getUnsafeHashVars();
var newView = getViewFromHash(unsafeHashObj);
var newTime = getTimeFromHash(unsafeHashObj);
var tourJSON = getTourFromHash(unsafeHashObj);
var presentationJSON = getPresentationFromHash(unsafeHashObj);
var modisLock = getModisLockFromHash(unsafeHashObj);
if (newView || newTime || tourJSON || presentationJSON || modisLock) {
if (newView)
_setNewView(newView, true);
Expand Down Expand Up @@ -1428,49 +1464,52 @@ if (!window['$']) {
return false;
};

// Gets safe view values from an unsafe hash string.
var getViewFromHash = function(unsafeHashVars) {
if (unsafeHashVars && unsafeHashVars.v) {
var newView = unsafeViewToView(unsafeHashVars.v.split(","));
// Gets safe view values (Object) from an unsafe object containing key-value pairs from the URL hash.
var getViewFromHash = function(unsafeHashObj) {
if (unsafeHashObj && unsafeHashObj.v) {
var newView = unsafeViewToView(unsafeHashObj.v.split(","));
return newView;
}
return null;
};

// Gets a safe time value from an unsafe hash string.
var getTimeFromHash = function(unsafeHashVars) {
if (unsafeHashVars && unsafeHashVars.t) {
var newTime = parseFloat(unsafeHashVars.t);
// Gets a safe time value (Float) from an unsafe object containing key-value pairs from the URL hash.
// TODO: what if time is 0?

This comment has been minimized.

Copy link
@enguyen

enguyen May 13, 2014

How about if (unsafeHashObj && $.type(unsafeHashObj.t) == "string") {?

var getTimeFromHash = function(unsafeHashObj) {
if (unsafeHashObj && unsafeHashObj.t) {
var newTime = parseFloat(unsafeHashObj.t);
return newTime;
}
return null;
};

// Gets a safe MODIS month lock value from an unsafe hash string.
var getModisLockFromHash = function(unsafeHashVars) {
if (unsafeHashVars && unsafeHashVars.l) {
var newMonthLock = unsafeHashVars.l;
// Gets a safe MODIS month lock value (String) from an unsafe object containing key-value pairs from the URL hash.
var getModisLockFromHash = function(unsafeHashObj) {
if (unsafeHashObj && unsafeHashObj.l) {
var newMonthLock = String(unsafeHashObj.l);
return newMonthLock;
}
return null;
};

// Gets safe tour JSON from an unsafe hash string.
var getTourFromHash = function(unsafeHashVars) {
if (unsafeHashVars && unsafeHashVars.tour) {
// Gets safe tour JSON from an unsafe object containing key-value pairs from the URL hash.
// The JSON returned is safe because calls to urlStringToJSON go to carefully-designed methods that use strict encoders (and naming conventions to mark strings not strictly sanitized) to ensure the input is safe.
var getTourFromHash = function(unsafeHashObj) {
if (unsafeHashObj && unsafeHashObj.tour) {
if (snaplapse) {
var tourJSON = snaplapse.urlStringToJSON(unsafeHashVars.tour);
var tourJSON = snaplapse.urlStringToJSON(unsafeHashObj.tour);
return tourJSON;
}
}
return null;
};

// Gets safe presentation JSON from an unsafe hash string.
var getPresentationFromHash = function(unsafeHashVars) {
if (unsafeHashVars && unsafeHashVars.presentation) {
// Gets safe presentation JSON from an unsafe object containing key-value pairs from the URL hash.
// The JSON returned is safe because calls to urlStringToJSON go to carefully-designed methods that use strict encoders (and naming conventions to mark strings not strictly sanitized) to ensure the input is safe.
var getPresentationFromHash = function(unsafeHashObj) {
if (unsafeHashObj && unsafeHashObj.presentation) {
if (presentationSlider) {
var presentationJSON = presentationSlider.urlStringToJSON(unsafeHashVars.presentation);
var presentationJSON = presentationSlider.urlStringToJSON(unsafeHashObj.presentation);
return presentationJSON;
}
}
Expand Down
52 changes: 52 additions & 0 deletions tests/cross_domain_test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-16">
<script src="../js/jquery/jquery.min.js" type="text/javascript"></script>
<script src="../js/org/gigapan/postmessage.js" type="text/javascript"></script>
<script>
// Handles the sending of cross-domain iframe requests.
function post(type, data) {
pm({
target: window.frames["time_machine_iframe"],
type: type,
data: data,
url: "http://timemachine1.gc.cs.cmu.edu/timemachines/crossdomaintest/tests/landsat.html",
origin: "http://timemachine1.gc.cs.cmu.edu"
});
}
// Receives the result of the request to the iframe (post("timemachine-is-supported"))
// in the form of true/false
pm.bind("timemachine-is-supported", function(unsafe_data) {
console.log("Browser supported:", !!unsafe_data);
});

// Receives the result of the request to the iframe (post("timemachine-get-share-view"))
// in the form of #v=lat,lng,zoom,latLng&t=timeInSeconds
pm.bind("timemachine-get-share-view", function(unsafe_data) {
console.log("Share View", unsafe_data);
});

// Receives the result of the request to the iframe (post("timemachine-get-current-view"))
// in the form of "lat,lng,zoom,latLng"
pm.bind("timemachine-get-current-view", function(unsafe_data) {
console.log("Current View", unsafe_data);
});
</script>
</head>
<body>
<iframe name="time_machine_iframe" id="time_machine_iframe" width="1068" height="703" src="http://timemachine1.gc.cs.cmu.edu/timemachines/crossdomaintest/tests/landsat.html#presentation=ENDkDPWPyXs9wR6gBAmazon%20Deforestation%20in%20Rondonia_Rondonia_DkDT37Hawc4T8VA%20large-scale%20hydroelectric%20project%20in%20the%20Brazilian%20Amazon%20rainforest_Tucurui%20Dam_BkDTiTRWWTmR6gBRiver%20meandering%20in%20the%20Amazon_Meander_DkDK9ySWib2R2dThe%20abandonment%20of%20a%20river%20channel%20and%20the%20formation%20of%20a%20new%20river%20channel_Bolivia%20Avulsion_DkDU28jbt4xTkcErosion%20in%20the%20Amazon%27s%20mouth_Mouth%20of%20Amazon_DkDcOeLoWayQ6gBThe%20Cape%20and%20islands%20are%20subject%20to%20massive%20coastal%20erosion_South%20Cape%20Cod_BkDPt3KmUZ3P6gBThe%20Outer%20Banks%20often%20suffers%20significant%20beach%20erosion%20during%20storms_Outer%20Banks%20NC_DkDFGq9mKcYi8VThe%20shrinking%20and%20drying%20up%20of%20the%20Lake%20Urmia_Lake%20Urmia_DkDOcGQprwpkmRThe%20shrinking%20of%20the%20Aral%20Sea%20destroys%20fishing%20industry%20and%20brings%20unemployment%20and%20public%20health%20problems_Aral%20Sea_DkDSq1joU5sk6gBThe%20expansion%20of%20irrigation%20systems%20near%20the%20Aral%20Sea_Aral%20Expansion_BkDcM7nkdxUhgZCenter-pivot%20irrigation%20irrigates%20crops%20with%20sprinklers%20centered%20on%20the%20pivot%2C%20creating%20a%20green%20circular%20pattern_Saudi%20Irrigation_CkDWmblQtbBxuXBushfires%20in%20Australia%20are%20frequent%20during%20the%20hotter%20months%20of%20the%20year_Australia%20Bushfire_DkDWZuFgTJ1tycThe%20eruption%20of%20Mount%20Pinatubo%20on%20June%2015%2C%201991_Pinatubo_Untitled_B" frameborder="0"></iframe>
<br/>
<b>Available cross domain functions</b>
<br/>
<button type="button" onclick="post('timemachine-is-supported')">Is Time Machine Supported?</button>
<button type="button" onclick="post('timemachine-get-current-view')">Get Current View</button>
<button type="button" onclick="post('timemachine-get-share-view')">Get Share View</button>
<button type="button" onclick="post('timemachine-play')">Play</button>
<button type="button" onclick="post('timemachine-pause')">Pause</button>
<button type="button" onclick="post('timemachine-seek', 1)">Seek to 1994 (1 sec into video)</button>
<button type="button" onclick="post('timemachine-set-view', {view: {center: {'lat': '44.96185', 'lng': '59.06233'}, 'zoom': '4.5'}, doWarp: true, doPlay: false})">Set View to Aral Sea</button>
<button type="button" onclick="post('timemachine-set-share-view', 'v=44.96185,59.06233,4.5,latLng&t=0.10')">Set View to Aral Sea from Share View</button>
<button type="button" onclick="post('timemachine-goto-presentation-slide', 'Pinatubo')">Change Slide to Lake Urmia</button>
</body>
</html>

0 comments on commit b0b8835

Please sign in to comment.