HTML5 Call to global function from Worker - javascript

I have a global function in Utils.js called "sendAndWaitCommand".
When I'm try to call this function form the Worker (name 'uploadToDevice.js'), the Worker crashes.
Worker Initialization
var worker = new Worker('uploadToDevice.js');
worker.postMessage(SplitedFile);
The Worker (uploadToDevice.js)
self.addEventListener('message', function (e)
{
var SplitedFile = e.data;
sendAndWaitCommand(SplitedFile[0].substring(1));//crash here
}, false);
Utils.js
function sendAndWaitCommand(commandToSend)
{
...//Some heavy stuff to do.
}
Is there any way to call a global function without the worker will crash?
If not, is there a solution how I can call objects from outside the worker?

The JavaScript model isn't, naturally, thread based but event based. There's no facility to lock data and ensure their integrity in a multi-thread context.
That's why multi-threading schemes (among them the webworkers) don't allow data sharing (a "global" function is some data and usually points to data through the closure). You can't call a "global" function from your webworker. You communicate with messages.
Instead of a global function, you set an event listener :
var myWorker = new Worker("my_task.js");
myWorker.onmessage = function (oEvent) {
// the implementation of your "global function", for example :
sendAndWaitCommand(oEvent.data.commandToSend);
};

If you want to use the code in util.js you would have to import it using the importScripts command:
Example from MDF:
importScripts(); /* imports nothing */
importScripts('foo.js'); /* imports just "foo.js" */
importScripts('foo.js', 'bar.js'); /* imports two scripts */
Note that if util.js interacts with the global scope this won't work, since after being imported it will still be in the same scope as your worker. If util.js contains all the code needed to perform the sendAndWaitCommand then that might be a good choice.
If sendAndWaitCommand interacts with your other code, (jquery.ajax, your error handling code, etc.) it would probably be better to setup a message and event system, like so:
var worker = new Worker('uploadToDevice.js');
worker.onMessage = function (message) {
if (// Test here to see if message is result
message && message[0] == "!")
myApp.onDone(message.substr(1)) // Removes the ! and calls a function to handel the worker being done
else // assume the message is a sendAndWaitCommand request
sendAndWaitCommand(message).then(function (a) {
worker.postMessage(a); // Send the worker the result
});
worker.postMessage(SplitedFile); // Start the worker
The second approach will probably involve more refactoring of your code but may be necessary to use workers correctly.

Related

Angular/Javascript - Custom User Scripts (Eval)

Hi we are building a web app platform where users can make their own smart forms using drag and drop features. We are looking for a way for admin users to create their own custom scripts to run some logic using pre-defined functions for the app. Currently the solution we have come up is using eval().
Knowing Eval is 'evil' we have implemented a function to check if the script is safe before it is executed. Essentially it breaks up the code into tokens and then runs those tokens against a blacklist. Stuff like new, eval, window, require, fetch,browser will show an error. Also the function is executed via a angular service so we try to limit what is injected.
Below is the basic high-level code. We have custom async functions so the solution needs to handle this.
My question is there a better(ie faster) and safer way to run custom scripts?
async runScript(script,callback) {
var updateForm=(id,value)=>{
return this.updateForm(id,value);
}
var getForm=(id)=>{
return this.getForm(id);
}
if (this.checkScriptSafe(script)) {
try {
return eval("(async () => {" + script + "})()");
} catch (e) {
if (e instanceof SyntaxError) {
alert(e.message);
} else {
console.log('Error',e);
alert("Error in script");
}
}
} else {
alert("Script not safe")
}
}
Example script:
"var value = 1 +4; await updateForm("11",value);alert("Success!");"
Function constructor would be a better approach. Function constructor creates a new function that will execute in the global scope. Your eval script (because of the arrow function) will run in the same context as your runScript method. They would access/modify your internals, or override your class methods. They can even override the runScript method itself and remove the checkScriptSafe check.
Using the function constructor is similar to typing in the dev tools console. If your application is not vulnerable to the dev tools console, then you wouldn't have any issues using the function constructor.
Here is an example:
const script = `
var value = 1 +4;\n
await updateForm("11",value);\n
alert("Success!");
`;
// we have to get a handle of the async function constructor
// in order to create an async function
const dummyFunction = async function() {}
const AsyncFunction = dummyFunction.constructor;
// create an async function which will run in the global scope
// the function will have an `updateForm` parameter
const userFunction = new AsyncFunction('updateForm', script);
// now userFunction is equavalent of:
// const userFunction = async function(updateForm) {
// var value = 1 +4;
// await updateForm("11",value);
// alert("Success!");
// }
// bind the current context 'this' to update form and pass it
// to user's function as parameter. The user's function
// will be able to execute it.
userFunction(this.updateForm.bind(this));
I'm not an expert in browser internals. But I assume tokenizing and interpreting the function on your own would be much slower than the Function constructor method. Even if you do everything in the most efficient way, you would still be in the JavaScript domain; v8 (or any other JS engine) would perform the actual interpretation after you. Why not directly give the script to the JS engine then? If the same custom script is going to run frequently, then with the right design, v8 will optimize the custom functions by compiling them into machine code. This wouldn't be the case with eval.

Web Worker - Is it possible to subscribe / catch the init function from inside the Worker js?

When creating new Worker instance with a name, for example:
// index.js
const myWorker = new Worker('./worker.js', { name: 'myWorker' });
Is it possible to subscribe/catch the initialization from inside worker.js and then, for example, grab its name?
// worker.js
const oninit = options => {
console.log(options.name); // ==> logs "myWorker"
};
I can't find such option... Am I getting the concept wrong or something?
I guess "init" happens when the worker is loaded / executed, so whatever you invoke directly in it runs immediately.
The name (DedicatedWorkerGlobalScope.name) from the options should be readable as self.name, so simply calling
// worker.js
console.log(self.name);
should do the trick (provided your worker's environment has working console).

node.js vm and external methods

I have a few problems working on an app that uses vm:
External methods calls inside an infinite while (true) {} loop are not timed out:
var context = {
externalMethod: function(str) {console.log(str)}
};
vm.runInNewContext("while (true) {externalMethod('test')}", context, {}, 100);
Code above runs infinitely, even after 100ms timeout is finished;
vm.runInNewContext("for (;;) {}", {}, {}, 100)
Also this.. Even without running external methods, it isn't timed out.
And one last question: how safe is it to have external method calls inside vm2 that will run untrusted code:
var context = {
method: (str) => {
createMessage("id", str) // some external (non-sandbox) method that sends an authenticated POST request to a chat app...
}
}
So, is it possible to retrieve external global or this, using that method?
Code above runs infinitely, even after 100ms timeout is finished
timeout should be a property of the options object, not a separate argument:
vm.runInNewContext("while (true) {externalMethod('test')}", context, { timeout : 100 })
And one last question: how safe is it to have external method calls inside vm2 that will run untrusted code.
I assume you mean vm (the built-in Node module) and not vm2 here. In that case, when you call code outside of the VM, it can potentially access the globals, locals and this of the "outside" code:
const vm = require('vm');
let SECRET = 'this is a secret';
let context = {
console, // to allow `console.log()` calls inside the sandboxed code
externalMethod() {
console.log('Secret outside:', SECRET)
}
};
vm.runInNewContext(`
console.log('Secret inside: ', typeof SECRET);
externalMethod();
`, context);
You can't access SECRET directly from the 'inside' code, but the external method can access it. So if externalMethod has the potential of running untrusted code, it'll be unsafe.

How to execute method just one time for application

I have a NodeJS application and I want to execute some method for file validations but just one time (this method will validate some files under the node application).
Is there an elegant way to do this? Events?
The NodeJS documentation on modules states that:
Modules are cached after the first time they are loaded.
which you can take advantage of by adding static code to the module. Regardless of how many times the module is loaded, the static code will retain its state(/value).
You can use that state from a method to implement a method that can be called whenever you like -- at the best time during initialization -- but only ever be called once. This is pretty simple:
var called = false;
function checkFiles() {
if (called) return;
// Perform your validation
called = true;
}
module.exports = {
checkFiles: checkFiles
};
Because of the module caching, you can require this file in as many places as you need and it will still only execute once.
To invoke this function, you have a few options:
For a simple application, you can call the function from your main module (or function) and it will be invoked at that time. If the validation should be asynchronous, you can wrap your main method in a function and pass that to the validator as a callback.
//#! /bin/env node
var express = require('express');
var validator = require('./validator');
validator.checkFiles();
var app = express();
var server = app.listen(3000, function () {
...
});
For a more complicated application, you should call this function during your existing initialization routine (again, using callbacks as necessary).
If you have a nice modern promise-based initializer, you could simply add the validator as the first step of the chain.

Updating parameters to JavaScript callback before it returns, without using globals

I'm working on making a modification to a node.js module to improve error handling for one of my uses cases. The specifics of the module aren't really relevant, but one of the things I want to do is trigger a delayed retry when receiving a certain status code from the response to an API request. To do this I'm using the timeOut function to trigger a new call to the function after a period of time.
The structure of the code looks like this:
Outer function (parameters specified by client application)
——API request (using parameters)
——Callback from API request (response with status code)
——If error, set timeout to call outer function after delay
However, I also want to handle the case that the outer function is called again while waiting for the timeout. I don't want any calls to trigger a new API request while a timeout is pending, but I do want the parameters from the most recent call to be used when the timeout finishes.
I've been able to get this working using variables that are global to the module. Each time a new call comes in to the outer function it updates a global object with the new parameters then, if a timeout is pending, returns without calling the API request. The timeout function uses the parameters from the global object to call the outer function, rather than the parameters from when it was set. This way it always uses the most recent values that were passed into the outer function, but doesn't make duplicate API calls.
Here's a simplified example of how I've achieved this behavior with global variables: JSFiddle. Hit run a few times until you get a "failure response" which then triggers the timeout.
This works, but I would prefer not add these global variables into the module if there's a better way.
Is there any way to get this same behavior but have all of the state encapsulated in the outer function without using globals? I'm also open to completely rethinking the way I'm handling this if anyone has ideas.
You're not going to be able to do this without creating variables outside of your outer function, however it's still possible to create those variables without polluting your global scope.
To do so, wrap your outer function in another function that gets executed immediately, sometimes known as an IIFE:
mainFunction: (function() {
var savedParam1;
var savedParam2;
var savedParam3;
var pendingTimeout = false;
return function(param1, param2, param3) {
savedParam1 = param1;
savedParam2 = param2;
savedParam3 = param3;
if (pendingTimeout) {
log('pending timeout -- ignoring new call');
return;
}
/* and so on */
}
})(); // the () causes the outer function to run immediately,
// which creates a scope for the savedParam / pendingTimeout variables,
// and then returns the inner function (your old outer function)
// to be used for mainFunction

Categories