Skip to main content
added 6 characters in body
Source Link
Quill
  • 12.1k
  • 5
  • 41
  • 94

Usage ($$.globalVar$$.globalVar inside the execexec should return undefinedundefined):

Usage ($$.globalVar inside the exec should return undefined):

Usage ($$.globalVar inside the exec should return undefined):

Reversed answer invalidation
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238

We are developing a desktop application, which uses HTML and JS as frontend. So it's almost a browser.

The wrapping with (function($$) { ... }).call({}, $$) is to get our code in its own local scope. that way, we can not expose function, which shouldn't be outside of the file they were used. ($$ is used to expose functions for our code)

The functions which are usable for 3rd party developers, are exposed through API(). So every "Plugin" (if you like to call it that) has its own local copy of API functions and cannot mess with global objects.

We are developing a desktop application, which uses HTML and JS as frontend. So it's almost a browser.

The wrapping with (function($$) { ... }).call({}, $$) is to get our code in its own local scope. that way, we can not expose function, which shouldn't be outside of the file they were used. ($$ is used to expose functions for our code)

The functions which are usable for 3rd party developers, are exposed through API(). So every "Plugin" (if you like to call it that) has its own local copy of API functions and cannot mess with global objects.

Rollback to Revision 1
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238
// scope.js
// $$ holds all global variables, and is not accessible
// inside an plugin (unless there is a helper method in the API, which exposes the variable)

$$ = {};
    
    // (function($$) { ... }).call({}, $$) is used to set this to an empty object.
    // This prevents getting values from this, which should be in another scope
    // "use strict"; is used to prevent the creation of global variables
    
    // initialize the global variables
    (function($$) {
        "use strict";
    
        // read-only property
        $$.DEBUG = {
            get: function() {
                return true;
            }
        };
        Object.defineProperty($$, 'DEBUG', $$.DEBUG);
    
        // execute the plugin code
        $$.exec = function (code) {
            return (function($$) {
                /**/
                // get the exposed API Methods
                var API = new $$.API(),
                $$ = {}; // Hide the global $$ object
    
                // prepare code for execution
                code = code.replace('eval', 'evil').trim();
                if (code[code.length - 1] !== ';') {
                    code += ';';
                }
                var _code = "(function() { var document = {}, window = {}; " + code + " return this; }).call({})";
    
                // call with eval
                try {
                    // everything set with this will be returned after the eval
                    return eval(_code);
                } catch(e) {
                    console.error(e);
                }
                /**/
            }).call({}, $$);
        };
    }).call({}, $$);
    
    
    // $$.API() returns an object, which holds all exposed API methods
// because $$.API() is an class, every plugin gets an local API oject.
    // If the elements of this object are overwritten, it does not interfere with other API objects
    (function($$) {
        $$.API = function() {
            // Default API values
            this.ApiVar = 3;
    
            // Expose global variables to the API
            this.setGlobalVar = function(value) {
                $$.globalVar = value;
            };
            this.getGlobalVar = function() {
                return $$.globalVar;
            };
            return this;
        };
    }).call({}, $$);
    
    // setting a global variable
    "use strict";
    $$.globalVar = 15;

I used Node.js for testing but it shouldn't matter where it runs.

Edit: I changed the code to replace every occurrence of the word "eval" with the word evil.

We are developing a desktop application, which uses html and js as frontend. So it's almost a browser.

In the future, we want to provide a method to integrate 3rd party code in our content, which must be properly secured (not all functions we use, should be available to everyone) and also dynamically loaded. The eval is necessary for loading the js code.

I know that the eval statement is considered evil. But thats one of the reasons i wrote this code. Now with the changed exec function, no eval call is allowed in the dynamically loaded code.

Edit2: The wrapping with (function($$) { ... }).call({}, $$) is to get our code in its own local scope. that way, we can not expose function, which shouldn't be outside of the file they were used. ($$ is used to expose functions for our code)

The functions which are usable for 3rd party developers, are exposed through API(). So every "Plugin" (if you like to call it that) has its own local copy of API functions and cannot mess with global objects.

// scope.js
// $$ holds all global variables, and is not accessible
// inside an plugin (unless there is a helper method in the API, which exposes the variable)

$$ = {};
    
    // (function($$) { ... }).call({}, $$) is used to set this to an empty object.
    // This prevents getting values from this, which should be in another scope
    // "use strict"; is used to prevent the creation of global variables
    
    // initialize the global variables
    (function($$) {
        "use strict";
    
        // read-only property
        $$.DEBUG = {
            get: function() {
                return true;
            }
        };
        Object.defineProperty($$, 'DEBUG', $$.DEBUG);
    
        // execute the plugin code
        $$.exec = function (code) {
            return (function($$) {
                /**/
                // get the exposed API Methods
                var API = new $$.API(),
                $$ = {}; // Hide the global $$ object
    
                // prepare code for execution
                code = code.replace('eval', 'evil').trim();
                if (code[code.length - 1] !== ';') {
                    code += ';';
                }
                var _code = "(function() { var document = {}, window = {}; " + code + " return this; }).call({})";
    
                // call with eval
                try {
                    // everything set with this will be returned after the eval
                    return eval(_code);
                } catch(e) {
                    console.error(e);
                }
                /**/
            }).call({}, $$);
        };
    }).call({}, $$);
    
    
    // $$.API() returns an object, which holds all exposed API methods
// because $$.API() is an class, every plugin gets an local API oject.
    // If the elements of this object are overwritten, it does not interfere with other API objects
    (function($$) {
        $$.API = function() {
            // Default API values
            this.ApiVar = 3;
    
            // Expose global variables to the API
            this.setGlobalVar = function(value) {
                $$.globalVar = value;
            };
            this.getGlobalVar = function() {
                return $$.globalVar;
            };
            return this;
        };
    }).call({}, $$);
    
    // setting a global variable
    "use strict";
    $$.globalVar = 15;

I used Node.js for testing but it shouldn't matter where it runs.

Edit: I changed the code to replace every occurrence of the word "eval" with the word evil.

We are developing a desktop application, which uses html and js as frontend. So it's almost a browser.

In the future, we want to provide a method to integrate 3rd party code in our content, which must be properly secured (not all functions we use, should be available to everyone) and also dynamically loaded. The eval is necessary for loading the js code.

I know that the eval statement is considered evil. But thats one of the reasons i wrote this code. Now with the changed exec function, no eval call is allowed in the dynamically loaded code.

Edit2: The wrapping with (function($$) { ... }).call({}, $$) is to get our code in its own local scope. that way, we can not expose function, which shouldn't be outside of the file they were used. ($$ is used to expose functions for our code)

The functions which are usable for 3rd party developers, are exposed through API(). So every "Plugin" (if you like to call it that) has its own local copy of API functions and cannot mess with global objects.

// scope.js
// $$ holds all global variables, and is not accessible
// inside an plugin (unless there is a helper method in the API, which exposes the variable)

$$ = {};
    
    // (function($$) { ... }).call({}, $$) is used to set this to an empty object.
    // This prevents getting values from this, which should be in another scope
    // "use strict"; is used to prevent the creation of global variables
    
    // initialize the global variables
    (function($$) {
        "use strict";
    
        // read-only property
        $$.DEBUG = {
            get: function() {
                return true;
            }
        };
        Object.defineProperty($$, 'DEBUG', $$.DEBUG);
    
        // execute the plugin code
        $$.exec = function (code) {
            return (function($$) {
                /**/
                // get the exposed API Methods
                var API = new $$.API(),
                $$ = {}; // Hide the global $$ object
    
                // prepare code for execution
                code = code.trim();
                if (code[code.length - 1] !== ';') {
                    code += ';';
                }
                var _code = "(function() { var document = {}, window = {}; " + code + " return this; }).call({})";
    
                // call with eval
                try {
                    // everything set with this will be returned after the eval
                    return eval(_code);
                } catch(e) {
                    console.error(e);
                }
                /**/
            }).call({}, $$);
        };
    }).call({}, $$);
    
    
    // $$.API() returns an object, which holds all exposed API methods
// because $$.API() is an class, every plugin gets an local API oject.
    // If the elements of this object are overwritten, it does not interfere with other API objects
    (function($$) {
        $$.API = function() {
            // Default API values
            this.ApiVar = 3;
    
            // Expose global variables to the API
            this.setGlobalVar = function(value) {
                $$.globalVar = value;
            };
            this.getGlobalVar = function() {
                return $$.globalVar;
            };
            return this;
        };
    }).call({}, $$);
    
    // setting a global variable
    "use strict";
    $$.globalVar = 15;

I used Node.js for testing but it shouldn't matter where it runs.

added 677 characters in body
Source Link
Loading
added 677 characters in body
Source Link
Loading
Source Link
Loading