AngularJS Exception Handler: Using service with unknown provider injection - javascript

I'm using AngularJS and I've created a custom exception handler.
angular.module('moduleName')
.factory('$exceptionHandler, () => {
return (exception, cause) => {
try {
var someService = $injector.get('serviceName');
someService.alert(exception);
}
catch (ex) {
window.alert(ex);
}
};
})
I caught up a situation when i forgot to run a script (in the html page) of one of the injections services that the service (named 'serviceName') are using. This has caused an exception when there is a use of the 'alert' function of the service.
I added the try/catch code in order to use regular 'alert' function whenever the service is malfunction (wrong injection or couldn't initialized) so the flow of the application won't hurt and in case of an exception handler exception catch, the code will run with or without the service.
However, even with the try zone, when there is use of the alert function of the service (when the service is malfunction), the $exceptionHandler function is invoked again instead of reaching to the catch zone...
Is there a way to get to the catch zone instead of invoking again the $exceptionHandler function? or is there a different way i could use in order to see if the service is good to use and all of his injections were loaded correctly?
Thanks in advanced

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.

Intercepting browser errors even if wrapped in try catch

So as the title states I would like to be able to intercept all errors that occur on the page. So starting off with the simplest way:
Add an error event listener to window and store the errors as they occur
The problem with this approach is that if the code that is causing the error is wrapped in a try catch block, the error listener never gets triggered.
A partial solution to this issue is to override the Error constructor so that any time code such as throw new Error() is called we can intercept it using our override. This approach works very nicely for user generated errors, this doesn't work for errors that originate in the browser. For example:
const a = ()=> {
const b = {};
console.log(b.c.d) // Uncaught TypeError: Cannot read property 'c' of undefined
}
try {
a()
} catch(err) {
console.log(err)
}
I would like to be able to detect that a TypeError has been thrown. Overriding the TypeError constructor does not work in this case.
Any ideas?
EDIT: The point is to be able to intercept errors that 3rd party scripts wrap with a try catch

How does Sentry track errors?

Do anyone have the Idea of how Sentry intercepts exceptions?
I am only starting to use Sentry. Here is what it suggested to me:
// add this
const Sentry = require('#sentry/node');
Sentry.init({ dsn: 'https://eed343acxxxxxxxxxxx3bb7e3f4bc10#sentry.io/1511416' });
// and call some undefined function
myUndefinedFunction();
So if there is no try .. catch thing, then how does Sentry tracks errors? The source code is open but is pretty hard to grasp.
P.S. I am interested in the internal mechanism of work not in the user instructions.
You simply need to add captureExcepton :
First, init the app
Sentry.init({ dsn: 'https://8ad77012ba2c436aba8a12e0b6cfd46b#sentry.io/1382991' });
Then you need to capture error by doing like this :
function captureError(err){
Sentry.withScope(scope => {
Object.keys(extra).forEach(key => { //Here you can add extra message(optional)
scope.setExtra(key, extra[key]);
});
Sentry.captureException(error); //important
});
}
and you can call this function from anywhere just pass err object
Note : You need to capture this error somewhere either in function or either in try catch. You need to tell sentry to capture this exception
When an error is fired but not caught, the browser will trigger an error event on the window object (see MDN docs for `onerror). Sentry listens for this - here's where in the source code:
https://github.com/getsentry/sentry-javascript/blob/2f365ab86418b7d0bb624bf68fc4084c9bf4691b/packages/utils/src/instrument.ts#L585-L604
In this file you can see (among other things) how Sentry listens for uncaught Promise rejections, using the same approach:
https://github.com/getsentry/sentry-javascript/blob/2f365ab86418b7d0bb624bf68fc4084c9bf4691b/packages/utils/src/instrument.ts#L606-L621
if you have a error, you should send error to sentry, like this:
import * as Sentry from "#sentry/browser";
handleError(error: any): void {
Sentry.captureException(error);
}

Scoping issue - calling an internal dojo function from external function

I have a function that is outside of the require portion of the dojo functions.
This function needs to call a function that resides within the dojo require block.
How do i call a function that is within the require code block from a function that resides outside the dojo require block?
Perhaps a little more application flow will demonstrate the need
Main window application spawns a child window
Main window sends a message to the child window that has a global function that will receive the message
Child window receives the message
The external function parses the message and determines that the map
needs to be updated (The child window that is spawned is the mapping window and loads a lot of ESRI modules in the require section)
the child window function needs to call a function that is
within the require code block of dojo to do the actual ESRI related tasks
It's a hacky solution and you should really think of a way to rearrange your modules if possible, but this should at least work:
var inside = null;
function outside () {
try { inside(); }
catch (err) { /* log error or throw away, whatever */ }
}
require(['dojo/_base/declare', ..., 'your/module/function'], function (declare, ..., myModuleFunction) {
inside = myModuleFunction;
outside();
});
Just require the module which contains the function (named "your/module/function" and myModuleFunction in the example), store it in a variable outside of the require and call it in a function which has been defined outside already. I added a try-catch block because it is good measure and prevents your code from blowing up if you call outside too early.
In case the function inside the dojo require block isn't a module, it's almost the same:
var inside = null;
function outside () {
try { inside(); }
catch (err) { /* log error or throw away, whatever */ }
}
require(['dojo/_base/declare'], function (declare) {
inside = function () { console.log('Inside the require block'); };
outside();
});
Except that you don't have to require it.
Talk about a hack... here is what i did to get the results i needed.
I created a hidden button on the form, bound the click event to fire off the function.
When the message was received and processed, I fired off the button click event - and viola!!
thanks everyone for the help.

Expose javascript errors in angular.js

in some cases, the angular engine doesn't output the raw javascript error. For example
myapp.directive('helloWorld', function() {
return {
scope: '#',
restrict: 'AE',
template: '<p ng-click="clearMessage2()">Hello, World! {{message}}</p>',
link: function(scope, elem, attrs) {
scope.clearMessage = function() {
scope.message = '';
}
}
}});
When I click on p element produced with directive I'm expecting the error in console to say clearMessage2() is not defined, but this doesn't happen and the only way to check things is to use console.log inside clearMessage definition.
Is it possible to change that behavior of angular.js and do not hide errors happening inside JS code?
It is possible, but not recommended. The problem is that angular does not execute the method in the ng-click directive as is (like in regular onclick), but instead evaluates expressions using the $parse service. From the angular doc:
Angular does not use JavaScript's eval() to evaluate expressions.
Instead Angular's $parse service processes these expressions.
The implementation of expression evaluation in Angular is deliberately forgiving.
In JavaScript, trying to evaluate undefined properties generates ReferenceError or TypeError. In Angular, expression evaluation is forgiving to undefined and null. It makes more sense to show nothing than to throw an exception if a is undefined (perhaps we are waiting for the server response, and it will become defined soon). If expression evaluation wasn't forgiving we'd have to write bindings that clutter the code
So the $parseProvider will not execute an undefined function at all, instead it will execute a noop function (which is an implementation of a null object pattern). Here is an excerpt from the $parseFunctionCall method:
var fn = fnGetter(scope, locals, context) || noop;
The execution of a null object will do nothing, and that is what's happening. You could probably achieve what you want by modifying the $parseFunctionCall to execute any function, instead of executing a noop function.
Changing code looks like the only option since configuration of these services is not sufficient for your use-case. However, i don't think it's a recommended approach unless you know the Angular API very well.
For additional reading:
Angular expressions
$parse service

Categories