[Utils] Fix privilege bypass, StringCopy function 48/136648/2
authorJakub Skowron <j.skowron@samsung.com>
Fri, 30 Jun 2017 14:02:34 +0000 (16:02 +0200)
committerJakub Skowron <j.skowron@samsung.com>
Fri, 30 Jun 2017 14:02:34 +0000 (16:02 +0200)
User could redefine String and String.indexOf to bypass privilege
check and to go outside of virtual-root by ../ in path

Change-Id: Ia9f7210ba685d1df18c9c443706361f624a38c1e
Signed-off-by: Jakub Skowron <j.skowron@samsung.com>
src/filesystem/js/common.js
src/utils/utils_api.js

index adf52c7e514350805061fec593b0a735ee7c2d8d..e900cb325625e2b410f8ac8abcf74acc6119b132 100644 (file)
@@ -169,6 +169,8 @@ var commonFS_ = (function() {
   }
 
   function checkPathWithoutDots(aPath) {
+    aPath = xwalk.utils.StringCopy(aPath);
+
     if (-1 !== aPath.indexOf('/../')) {
       return false;
     }
index 3c4d46ed843200d71eb3a0fd127df172540c0aba..b9a4a83fd715feb6cb69fd5d8bd78e1eaac01517 100644 (file)
@@ -3,20 +3,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-//Object xwalk.JSON - guaranteed to not being modified by the application programmer
-var JSON_ = {stringify: JSON.stringify, parse: JSON.parse};
-Object.freeze(JSON_);
-exports.JSON = JSON_;
+//We're in a function set up by XWALK which 'use strict' mode,
+//so we use below function to get out of the strict mode to get global 'this'
+var _global = (new Function('return this'))();
+
+var shallow_copy_own_elements = function(orig) {
+  var copy = {};
+  //copy only own properties
+  var names = Object.getOwnPropertyNames(orig);
+  for( var i in names ) {
+    var key = names[i]
+    copy[key] = orig[key];
+  }
+  return copy;
+};
 
-var _enableJsLogs = false;
+//xwalk.JSON: guaranteed to not being modified by the application programmer
+exports.JSON = shallow_copy_own_elements(JSON);
+Object.freeze(exports.JSON);
 
-var _global = {};
-if (typeof window != 'undefined') {
-  _global = window;
-}
-else if (typeof global != 'undefiend') {
-  _global = global;
-}
+var _enableJsLogs = false;
 
 /**
  * @deprecated Used only by validateArguments()
@@ -146,6 +152,21 @@ function Utils() {
   });
 }
 
+var origString = String;
+var StringPrototypeCopy = shallow_copy_own_elements(String.prototype);
+Object.freeze(StringPrototypeCopy);
+
+var StringCopy = function(str) {
+  return Object.setPrototypeOf( new origString(str), StringPrototypeCopy );
+};
+StringCopy.fromCharCode = String.fromCharCode;
+StringCopy.fromCodePoint = String.fromCodePoint;
+StringCopy.raw = String.raw;
+Object.freeze(StringCopy);
+
+//xwalk.utils.StringCopy: returns a sanitized version of String - user cannot modify its prototype
+Utils.prototype.StringCopy = StringCopy;
+
 Utils.prototype.error = console.error.bind(console);
 Utils.prototype.warn = console.warn.bind(console);
 Utils.prototype.log = _enableJsLogs ? console.log.bind(console) : function(){};
@@ -331,8 +352,9 @@ Type.prototype.isUndefined = function(obj) {
 };
 
 Type.prototype.isA = function(obj, type) {
-  var clas = Object.prototype.toString.call(obj).slice(8, -1);
-  return (obj !== undefined) && (obj !== null) && (clas === type);
+  return obj !== undefined && obj !== null &&
+         obj.constructor !== null && obj.constructor !== undefined &&
+         obj.constructor.name === type;
 };
 
 Type.prototype.isEmptyObject = function(obj) {
@@ -475,7 +497,7 @@ Converter.prototype.toDouble = function(val, nullable) {
 };
 
 function _toString(val) {
-  return String(val);
+  return StringCopy(val).toString();
 }
 
 Converter.prototype.toString = function(val, nullable) {
@@ -1018,7 +1040,7 @@ var NativeManager = function(extension) {
   });
 
   extension_.setMessageListener(function(json) {
-    var msg = JSON_.parse(json);
+    var msg = exports.JSON.parse(json);
     var id;
 
     if (msg.hasOwnProperty(this.CALLBACK_ID_KEY)) {
@@ -1082,7 +1104,7 @@ NativeManager.prototype.call = function(cmd, args, callback) {
 };
 
 NativeManager.prototype.callSync = function(cmd, args) {
-  var request = JSON_.stringify({
+  var request = exports.JSON.stringify({
     cmd: cmd,
     args: args || {}
   });
@@ -1092,7 +1114,7 @@ NativeManager.prototype.callSync = function(cmd, args) {
     /* C++ extension didn't set sync response using Instance::SendSyncReply */
     throw new WebAPIException(WebAPIException.ABORT_ERR, "Internal error");
   }
-  return JSON_.parse(response);
+  return exports.JSON.parse(response);
 };
 
 NativeManager.prototype.sendRuntimeMessage = function(msg, body) {
@@ -1346,13 +1368,13 @@ var NativeBridge = (function (extension, debug) {
     var Bridge = function () {};
     Bridge.prototype = {
         sync: function (data) {
-            var json = JSON_.stringify({
+            var json = exports.JSON.stringify({
               cmd: data.cmd,
               args: data
             });
             if (debug) xwalk.utilss.log('bridge.sync, json: ' + json);
             var result = extension.internal.sendSyncMessage(json);
-            var obj = JSON_.parse(result);
+            var obj = exports.JSON.parse(result);
             if (obj.error)
                 throw new WebAPIException(obj.code, obj.name, obj.message);
             return obj.result;
@@ -1360,7 +1382,7 @@ var NativeBridge = (function (extension, debug) {
         async: function (data) {
             var l = new Listener();
             data.cid = Listeners.getInstance().add(l);
-            var json = JSON_.stringify({
+            var json = exports.JSON.stringify({
                 cmd: data.cmd,
                 args: data
             });
@@ -1398,7 +1420,7 @@ var NativeBridge = (function (extension, debug) {
          */
 
         if (debug) xwalk.utils.log('bridge.setMessageListener, json: ' + json);
-        var data = JSON_.parse(json);
+        var data = exports.JSON.parse(json);
         if (data.cid && data.action) {
             setTimeout(function() {
                 Listeners.getInstance().resolve(data.cid, data.action, data.args, data.keep);