shrawan_lakhe
shrawan_lakhe

Reputation: 356

Execute js code string as function in Node.js without using eval

I am writing code to generate thumbnails based on user selected image manipulation actions which may be multiple as choosen by user using lwip npm package module.

For multiple actions lwip provides batch function and then chaining other manipulating functions.The problem i faced is that user may select any combination of host of manipulating functions and it is too cumbersome to check for each and every combinations of selected actions.

So, i have generated the code dynamically as js code string which i need to execute as function without using eval that may compromise application security

Below is my code

    'use strict';
(function(uploadhandler){

    var lwip=require('lwip'),
        imageSettingProvider=require('../data/imagesettingprovider'),
        uploadFolder='public/uploads/',
        imageManipulatorHelper=require('./imagemanipulationactions'),
        manipulatedImage='';

    uploadhandler.generateThumbnail=function(filePath,filename,ImageUploadSetting,fs){
        // compound effects
        var thumbnailPath='';

        lwip.open(filePath, function(err, image) {
            if (err) {
                console.log(err);
            }else{
                imageSettingProvider.getImageSetting(ImageUploadSetting,{},function(err,imageSettings){
                    imageSettings.forEach(function(element,index,array){
                        thumbnailPath=uploadFolder + element.folderName + '/' + filename;
                        var imageAction=element.action;
                        if(imageAction.indexOf(',')>-1){
                            var imageManipulationActions=imageAction.split(',');
                            var manipulationHtml='';
                            manipulationHtml += 'image.batch()';
                            var actionHtml='';
                            imageManipulationActions.forEach(function(actionelement,actionindex,actionarray){
                                actionHtml += uploadhandler.PerformMultipleImageManipulation(actionelement,element,actionHtml);
                            });
                            manipulationHtml += actionHtml;
                            console.log('----------------------------------------------------------------------------------------------------------');
                            manipulationHtml += '.writeFile(thumbnailPath, function(err) { if (err) throw err;});';
                            console.log(manipulationHtml);

                        }
                    });
                });
            }
        });
    };


    uploadhandler.PerformMultipleImageManipulation=function(imageAction,imageOpts,actionHtml){
        switch (imageAction){
            case "crop":
                actionHtml = '.crop(' + imageOpts.width + ',' + imageOpts.height + ')';
                break;
            case "cropbycoordinates":
                actionHtml = '.crop(' + imageOpts.cropLeftPos + ',' + imageOpts.cropTopPos + ',' + imageOpts.cropRightPos + ',' + imageOpts.cropBottomPos + ')';
                break;
            case "resize":
                actionHtml = '.resize(' + imageOpts.width + ',' + imageOpts.height + ')';
                break;
            case "resizecrop":
                actionHtml = '.resize(' + imageOpts.width + ',' + imageOpts.height + ')' + '.crop(' + imageOpts.width + ',' + imageOpts.height + ')';
                break;
            case "rotate":
                actionHtml = '.rotate(' + imageOpts.rotateDegree + ',' + imageOpts.backgroundColor + ')';
                break;
            case "blur":
                actionHtml = '.blur(' + imageOpts.blurVal + ')';
                break;
            case "scale":
                actionHtml = '.scale(' + imageOpts.scaleVal + ')';
                break;
            case "mirror":
                actionHtml = '.mirror(' + imageOpts.flipAxes + ')';
                break;
            case "fade":
                actionHtml = '.fade(' + imageOpts.fadeVal + ')';
                break;
        }
        return actionHtml;
    };

})(module.exports);

Now when i log the manipulation variable to the console,it gives:

image.batch()
.resize(480,320)
.crop(480,320)
.rotate(75,white)
.writeFile(thumbnailPath, function(err) { 
if (err) throw err;
});

Now i need to execute this above js code string as function to generate thumbnail image without using javascript eval function.

I have tried using following approach from sitepoint website:

// function we want to run
var fnstring = "runMe";

// find object
var fn = window[fnstring];

// is object a function?
if (typeof fn === "function") fn();

But it gives me with the error " ReferenceError: window is not defined "

Please guide me to solve this problem.

Upvotes: 0

Views: 4816

Answers (2)

SwiftNinjaPro
SwiftNinjaPro

Reputation: 127

If it helps, you could run a regex replace function.

Note: I have not tested this.

// if worried about security with eval, you may want to put functions in an object instead of using global
const myFunctions = {
    runMe: function(){/* do stuff */},
    runMe2: function(){/* do stuff */}
};

const allowedFuncs = ['runMe', 'runMe2'];
// or dynamic to entire object
const allowedFuncs = Object.keys(myFunctions);

str = str.replace(new RegExp('(''+allowedFuncs.join('|')+)\\((.*?)\\)', 'g'), (str, func, attrs) => {
    // check allowed list for added security
    if(allowedFuncs.includes(func)){
        attrs = attrs.split(',');
        myFunctions[func](...attrs); // 3 dots makes js expand array to params separated by camas
    }
    return str; // returning str replaces nothing
});

Upvotes: 0

vorillaz
vorillaz

Reputation: 6276

Fetch the actions into global object and execute each one using each particular function's namespace.

var helper = {};
helper.b = function() {
  console.log("foo");
}
helper.c = function() {
  console.log("bar");
}

//execute them

function execute(key) {
  try {
    helper[key]();
  } catch (e) {
    throw new Error("Function does not exist");
  }
}

execute("b");
execute("c");
execute("d");

Upvotes: 4

Related Questions