Standalone Version

The script JsxBlindRun.jsx can be run from InDesign, and probably from any Adobe application that supports ExtendScript and ScriptUI progress bars. It allows you to select a JSXBIN file at any location (e.g. myFinalScript.jsxbin), and then it re-encodes it in a scrambled form adding the suffix _blind to the file name (myFinalScript_blind.jsxbin.)

For more detail on the process (and the meaning) of “scrambling” a JSXBIN code, please refer to my original post on this subject.

The run version uses exactly the same algorithm as the library (which it is based on), except it presets some default options in order to keep everything simple while adding a basic interface for choosing the target file and saving the result. It also displays a progress bar during the scanning stage, and catch possible exceptions in a human-friendly way. A yes-no prompt box allows you to tell JsxBlind whether function names must be preserved, as discussed later on.

Using JsxBlind as a Library

The file JsxBlindLib.jsxinc can be included in any ExtendScript project (script for InDesign, Photoshop, ExtendScript ToolKit, and so on.) Just use the #include directive to have it available at any point of your code. The library only creates a callable object, JsxBlind, in the global scope, and it's up to you to set some options on this object and/or to call it whenever needed.

#include 'path/to/JsxBlindLib.jsxinc'
 
// The JsxBlind object is now ready
// to work on your data
 
// . . .
 
 

Note. — JsxBlindLib supports persistent scopes created through a #targetengine directive. It doesn't reload itself if already available in $.global, so you can make it option-persistent during a whole session.

The most basic way of scrambling a JSXBIN binary stream comes down to:

#include 'path/to/JsxBlindLib.jsxinc'
 
// . . .
 
blind = JsxBlind(originalBin);
 
// . . .
 

where originalBin refers to either a JSXBIN File, or a JSXBIN String. The result, blind, is a string that encodes strictly the same program but using random, weird, and non-meaningful identifiers. You can then save the blind string into a File or use it for further processing.

Let's take as an example the source code of JsxBlind itself. A piece of my work looks like this:

JsxBlind source code sneak peek.

That's the clean (?) annotated JavaScript version of the considered fragment. Then I use ESTK to export my JSX code into JSXBIN, and the resulting file contains a long encoded string looking like @JSXBIN@ES@2.0@MyBbyBn0ADJAn... etc. Unfortunately, some hackers have tools that may decipher my JSXBIN, and here is what kind of stuff they would retrieve:

The same, deciphered from a regular JSXBIN encoding.

Pretty close to my original source code! My opponents can thus easily reconstitute the logic of my script, re-use my algorithm, bypass protections, and so on. Now study the same JSXBIN after passing through JsxBlind:

The same, deciphered after JsxBlind's export.

That's the same code and it performs exactly the same tasks. However, most identifiers, parameters and function names have changed, although still perfectly JavaScript compliant. As discussed in my previous article, JsxBlind does not make your script burglar-proof, it just make it so painful to understand that you can release it more serenely.

Take Control of JsxBlind's Settings

Here are additional options that the JsxBlind object provides (library version.) Always set these options before running the JsxBlind() function.

PROPERTY or METHOD VALUES or ARGUMENTS DESCRIPTION
JsxBlind.evalQuotes 0, '\x22', or '\x27' (default=0.) In case the input is a pure JSXBIN stream not nested in eval(), tells JsxBlind to export it as entered (0) or to output the result using eval(…), based on either double quotes ('\x22') or single quotes ('\x27'.) For example, set evalQuotes to '\x22' to get the output formatted eval("@JSXBIN@ES@2.0@etc");. This option has no effect if the input stream already has the eval(…) form.
JsxBlind.inputFileData(what) File or prompt String (default='') This method is just a helper that makes it easier to read a JSXBIN file for further processing. If you supply a File object, inputFileData(myFile) will just read it and return the whole string it contains. If you supply a message (string) instead, the function will invoke File.openDialog() with that message and read the selected File. In the latter case, the user can cancel the process and inputFileData() will return false. Otherwise it returns a string which is supposed to be a JSXBIN stream (but you still have to call JsxBlind() on it when needed.)
JsxBlind.keepFunctionNames 0, 1, or -1 (default=0.) Tells JsxBlind to preserve function names as in the original code (1), or allows it to scramble them too (0). Set this option to -1 to get the user asked via a prompt box.
JsxBlind.muteAlerts 0 or 1 (default=0.) If turned on, warnings won't display except on fatal errors (which still trigger JS exceptions.)
JsxBlind.outputFileData(data, what, show) data: String; what: File or prompt String (default=''); show: 0,1, or 2 (default=0) This method is just a helper that makes it easier to export the scrambled JSXBIN file after processing. The first argument, data, is required and must contain the final string to be saved. Regarding the second argument, if you supply a File object, outputFileData(data, myFile) will just write it on the disk. If you supply a message (string) instead, the function will invoke File.saveDialog() with that message in order to select the file location. In the latter case, the user can cancel the process and outputFileData() will return false. Otherwise it returns the File it has just created. The last optional parameter determines whether the file must be loaded (show=1), whether its parent folder must be shown (show=2), or whether no action has to be done through the OS (show=0.)
JsxBlind.preserveIdentifiers(strings) Array of Strings (default=[].) This method takes for argument an array of valid identifiers, e.g. ['dont', 'touch', 'me'] and will preserve any of those strings from being altered during the process. By contrast with the option keepFunctionNames which globally preserves every function name, preserveIdentifiers() allows to make safe only a specific set of identifiers, including variable names and formal parameters.
JsxBlind.toString() (no argument) Returns (as a String) the current version of the JsxBlind library. Since toString() is implicit in any string context, you can just use codes like alert(JsxBlind) or ver=''+JsxBlind to get informed about the version in use. Result typically looks like "JsxBlind Library Version 1.007 (build:170121) © indiscripts.com"

More on evalQuotes.JsxBlind.evalQuotes is useful to save the extra step of inserting the binary string into an eval(...) structure from a pure JSXBIN file—which is commonly done by developers when a #targetengine directive has to be dealt with. Anyway, the current version of JsxBlind automatically detects any “prologue” and “epilogue” sections that may surround the binary stream itself, so in most cases you won't need to play with evalQuotes at all, because JsxBlind will restore any existing eval(...) as it is formatted in the source, as well as additional directives or comments.

For example, if your input stream looks like:

#targetengine 'MyGreatScript'
 
// Version 1.234 of MyGreatFramework
// ---
eval("@JSXBIN@ES@2.0@MyBbyBn0ABOAbCn0ACJCnA . . .");
 
var foo = 'bar';
 
// etc.
 

you can apply JsxBlind to that stream and only the JSXBIN portion will be changed in the result, keeping safe both eval and those additional lines. (Note, however, that JsxBlind cannot handle multiple binary strings in such context.)


More on preserving identifiers.JsxBlind.keepFunctionNames and .preserveIdentifiers(...) are two important tools when it comes to restrict JsxBlind's freedom. Ideally, of course, allowing the script to alter any identifier is the best solution. Remember that literal strings, numbers, property and method names (either native or yours) will remain unchanged, as well as every syntactical token and reserved word of the language. Hence the whole structure and much of the semantics of your script is still visible. In a sense blurring identifiers really leaves us little room to maneuver, so it's important to use that feature as widely as possible.

Identifiers specifically refers to three kinds of strings: 1) variable and constant names; 2) declared function arguments (known as formal parameters); 3) function names. In general there is no reason to preserve the name of any variable, constant, or formal parameter. For example, if you need to keep safe an identifier—say "JsxBlind"—in the global scope, you can always use the declaration $.global.JsxBlind = something instead of var JsxBlind = something.

By contrast, function names (case 3) may introduce fine-drawn issues depending on how your code exposes them to the external world, in particular when invoked from within a CEP/JSX transaction. Also, the name of a function might be checked (from its outer scope) for various purposes, since it's a read-only property where framework designers can store crucial data or flags in a very secure way. Anyway, apart from these well-defined cases, function names should be considered just as arbitrary as variable names. This applies, I think, to both function declarations and function expressions:

// Function declaration
function myFuncName(){...}
 
// Function expression (named)
var myFunc1 = function myFuncName1(){...};
 
// Function expression (anonymous)
var myFunc2 = function(){...};
 

In the above lines, note that myFunc1 and myFunc2 are nothing but variable names. Each of these variables happens to refer, at this time, to a function. The function that myFunc2 refers to is anonymous (i.e its name property is "anonymous") while myFunc1 refers to a function whose name is "myFuncName1". In ordinary circumstances, this name is of no use outside of the function body, since the identifier itself only makes sense within that scope. So, randomly changing that name won't have any effect. Even if such function internally calls itself (recursion, cache storage…) everything still works fine as long as the identifier is uniformly changed (which of course JsxBlind does.) Finally, the only reason you could have to maintain this function name is the case of function name checking I've mentioned, that is, stuff like if( myFunc1.name == "myFuncName1" ){ doSomething() } used in the outer scope. So, if really you code relies on such conditions, you have to preserve the function name—because the literal string "myFuncName1" wouldn't match anymore.

We are left with the case of pure function declarations. Many developers use that syntax—function myFuncName(){...}—as something of a shortcut. It appears almost equivalent to var myFuncName = function myFuncName(){...} although we all know it doesn't sound like this, at all, to the interpreter. But from the particular JsxBlind perspective, the fact that function declarations undergo a special priority within the lexical context has no impact at all. Here again, if the function name must be exposed as it is to the outer world, you can either decide to keep it unchanged or rewrite your code so that the name becomes a property of $.global (or any other relevant context.)

In any case, I recommend you surgically use JsxBlind.preserveIdentifiers(["name1","name2","name3"]) rather than JsxBlind.keepFunctionNames=1 which globally unmasks every function name.

Keep also in mind that, in general, JsxBlind will not blur every identifier that it is in principle allowed to blur. Some will remain unchanged due to technical reasons which I cannot expand on.


More on muteAlerts and error messaging. — There are a few alert boxes that JsxBlind can display during the process. The scanning module may warn you that some function “has duplicate identifier(s) among its formal parameters”, which is not a fatal error but it's worth knowing. The blinder module may inform you that, “Due to size restriction some identifiers have only been interchanged (…) You could enhance the scrambling using longer names.” That's an interesting hint, I think. Finally, JsxBlind prompts a end message, “The stream has been successfully scrambled…” Setting JsxBlind.muteAlerts to 1 will bypass any of these messages and warnings, which can be useful in a silent automation process.

However, muteAlerts does not inhibit fatal errors, it only regards warning messages. JsxBlind still throws exceptions when it encounters serious issues that require the process to be canceled. In such case the client program is responsible for catching the Error. Here are the important error messages you may retrieve:

ERROR MESSAGE MEANING
No JSXBIN prolog found… JsxBlind has nothing to eat. Make sure you supplied a valid ExtendScript JSXBIN stream (@ES@2.0). Older format (1.0) is not supported.
Invalid input type... The JsxBlind() function has been called with an invalid input parameter. (Input should be either a File, a String, or false.)
Cannot open … for reading. A file cannot be accessed in read mode.
Cannot open … for writing. A file cannot be accessed in write mode.

Additional errors may occur which reveal a “parsing problem,” that is, something wrong in either the binary stream or in JsxBlind's algorithm itself. For obvious reasons I can't tell you more about those exceptions. Should you meet them, detailed bug report would be much appreciated:

UNDOCUMENTED PARSING ERRORS
No decoder found for marker…
Unexpected bool value…
Unknown number length found…
Unexpected Variant type…
Invalid weight (…) for parameter…
Invalid count of params…
Any … target should be empty…

Last but not least, ExtendScript may still trigger a fatal “Stack overrun” exception if the binary stream is too complex for JsxBlind. This happened to me once with a huge script. I hope to further optimize my algorithm in the future…

The Core Function

As a function JsxBlind() takes at most two arguments, the first one being the most important:

blind = JsxBlind( inputBin, /*opt*/ msgBack );
 
// inputBin: String or File or false/undefined.
 
// msgBack: Any object that provides a msg() method.
 
// blind: String (the result.)
 

If inputBin is false or undefined JsxBlind automatically invokes its own inputFileData() dialog so that the user can “select the JSXBIN file to shuffle.” Otherwise it takes the String or the File as its binary input. Strings are directly parsed while files are read to get their contents as a string. Ultimately JsxBlind always deals with binary strings.

Example. — Your goal is to attach a new menu extension, “Export through JsxBlind”, to ExtendScript ToolKit features. Go into the extensions folder then add you jsx add-on as follows:

#include 'path/to/JsxBlindLib.jsxinc'
 
// Menu stuff
// ---
menus.file.exportAsJsxBlind = new MenuElement(/*etc*/)
// . . . manage onDisplay, onSelect etc . . .
 
// Feature
// ---
SourceDocument.prototype.exportAsJsxBlind = function()
{
    // . . .
 
    var inputBin = app.compile( /* ... */ );
 
    JsxBlind.muteAlerts = 1;
    var blind = JsxBlind(inputBin);
 
    // . . .
};
 

The second JsxBlind() parameter (msgBack) must expose, if supplied, a msg() method which expects a String. For example, a logging framework or a custom ProgressBar. Just make sure that your object has a callback method strictly named msg.

What does JsxBlind is, it regularly sends progress messages to msgBack.msg() while processing the binary stream. The typical message has the form “Parsing nodes: xxx/yyy” where xxx refers to the current count and yyy to the total number of nodes to be parsed. Other messages are sent when the script is in scrambling identifiers: “Rewriting the stream…” at first, then “Target: xyz” for each targeted name.

Those messages are nothing but snapshots into the work in progress, but they can be helpful for estimating the total execution time on huge streams. The standalone version of JsxBlind uses that mechanism to fuel its custom progress bar.