Error Scripts
A few transforms: domain lock, date lock, and self-defending protection are runtime checks that can execute code in response to the check. These transforms all have an optional errorScript
configuration property, where you can provide a script to run when the runtime check triggers. In this section, you learn how to add error scripts to your protected code.
How JSDefender Runs without the errorScript
Configuration Option
When you do not use the errorScript
configuration property, JSDefender applies its default protection:
Transform | Default protection |
---|---|
Domain lock | Raises a ReferenceError: data_value is not defined error message. |
Date lock | Raises a ReferenceError: data_value is not defined error message. |
Self-defending protection | Raises a SyntaxError message. |
SyntaxError
. Sometimes the random-like transformation of the code may result in other error messages.
How JSDefender Uses Your Error Scripts
The protection engine injects the string value of the errorScript
configuration property into the code as it is. Nonetheless, the protection takes care that each error script runs in its JavaScript block statement. Let's assume you have this error handling snippet:
const myMessage = "I got you!";
alert(myMessage);
It goes into the code as if you wrote this, providing a separate scope for the myMessage
constant variable:
{
const myMessage = "I got you!";
alert(myMessage);
}
Where Error Scripts Are Injected
The engine injects the error scripts of domain lock and date lock transformations only once, into a well-defended part of the protected code, called the JSDefender Runtime. If you are protecting multiple files, this code always goes into the very first protected file. The Runtime goes through some unique transformations — you cannot influence or configure them — and so does your script.
In contrast, the self-defending transformation injects the error script into every protected function. As JSDefender supports partial configuration, you can even assign separate error scripts to different protected functions. This script goes through some transformations (again, you cannot influence it) together with the code that provides the self-defending protection.
What You Can Do with an Error Script
With the errorScript
protection option used in domain lock and date lock protections, you can carry out these actions:
- Provide some additional action and then let JSDefender use the default action.
- Provide some additional action and allow the code to run as if no violation occurred.
With the self-defending protection, you can execute your additional action, but there is no way to avoid JSDefender's default action and behave as if code tampering did not happen.
Allow JSDefender to Apply the Default Action
By default, after your error handler runs, JSDefender executes its default action. Let's assume you add this errorScript
to the date lock transform:
const myMessage =
"Sorry, the evaluation period expired. " +
"You cannot use this product anymore";
alert(myMessage);
This code will pop up the alert dialog with the message, and after that, it will stop with a ReferenceError: data_value is not defined
error message.
Make JSDefender Continue as If No Violation
If your code executes a return
statement, it tells JSDefender that you intend to ignore the violation. Add this errorScript
to the date lock transform:
const myMessage = "Please note: the evaluation period expired.";
alert(myMessage);
return;
Though this script displays the message, after the user closes the alert dialog, it allows the code to run normally.
Writing Error Scripts
When creating your error scripts, you can use the entire toolset of JavaScript, including:
- variable, function, and class declarations
- statements
- invoking global functions
- exception handling (
try..catch..finally
)
Nonetheless, there are a few things you should be aware of:
- If you can, avoid using the
with
statement or theeval
function in your code. Those may break the code after running protection. - Handle
var
declarations carefully, as those may collide with identifiers in other lexical scopes of your code. - Do not import modules in the error script code. If you need to invoke functions or class methods, access them through global variables or objects.
- Though you can use async code with
async
andawait
, the error handler runs synchronously.
Using var
declarations
It is a good practice to use only ES 2015 let
and const
variables. If for some reason, you need to use var
declarations, those may (accidentally) collide with other variable names, and that may break your error handler. In these cases, use the JavaScript module pattern with an IIFE (Immediately-Invoked-Function-Expression). For example, do not use this script:
var myMessage = "I got you!";
alert(myMessage);
Instead, inject this:
!(function () {
var myMessage = "I got you!";
alert(myMessage);
})();
Invoking Global Functions
You can access variables in the global JavaScript scope, and call functions declared there, including the global JavaScript functions. So if you have a global function named myDateViolationHandler
, you can apply this errorScript
:
myDateViolationHandler("violated");
myDateViolationHandler
and its reference in the error handler script.
Using async
Code
The error handler is executed synchronously. Though you can add async
code into it, you cannot await
the result of those functions to decide whether to allow running the application code despite the violation, or to stop.
Take a look at this code:
function timeout(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
(async () => {
await timeout(2000);
alert("Invalid date");
return;
})();
At first sight, you may think this code allows your application to continue running after displaying the "Invalid date" message, as there's a return
. However, that returns from the async IIFE. By that time, the message is displayed and confirmed (showing it has a 2-second delay). The protection engine executes its default action and stops with a ReferenceError
message.
To allow the code to run despite the violation, you should add a closing return to your code:
function timeout(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
(async () => {
await timeout(2000);
alert("Invalid date");
return;
})();
return; // --- Go on with the violation
An Error Handler Pattern to Follow
There's a simple pattern you can follow to write excellent error-handling scripts. Here you can learn how.
JSDefender takes care that the domain and date checking protection runs before the very first line of your original code. Using this pattern allows you to store the result of the date violation. Of course, you can use a similar pattern to manage domain violation.
Add this code to your program, right at the beginning:
let dateViolated;
function signDateViolation() {
dateViolated = true;
}
if (dateViolated) {
// Add your date violation logic here
// including aborting the app, logging out the user, etc.
}
// This code runs if you let the control flow reach this point
doMyJob();
Add this errorScript
to the dateLock
configuration:
"dateLock": {
"startDate": "<your start date>",
"endDate": "<your end date>",
"errorScript": "signDateViolation(); return;"
}
As a return
statement closes the error handler, JSDefender lets the flow of the program continue even if a date violation occurred. Since JSDefender runs the date check logic before reaching the let dateViolated
declaration, you can be sure that by the time the if
statement evaluates its condition, the dateViolated
variable will be set correctly.