JEB Plugin Development Tutorial part 7/8

Unit Interactivity

The source code for part 7 of this sample plugin is located on GitHub:

Syntax Highlighting

Let's dive deeper and implement the first thing we want to get from an editor: syntax highlighting. This will allow us to uncover more corners of the JEB API.

We will need to add item highlights on some parts of the text. Remember our sample, we will use only a single part:

function a() {
  alert("a called");
}

A standard editor highlights keywords: they are specific words (reserved, most of the time) that are related to a language. In JavaScript, they are "function", "var", "if", "else"... Other interesting artifacts that could be highlighted are strings. In this tutorial, we will highlight functions, variables and strings surrounded by double-quotes.

We want to modify only the display, not interact with the model (eg, renaming, formatting), so we will only look at ITextDocument. It has two important features:

Let's concentrate on the ITextDocumentPart. The interface defines two methods:

Remember that a part represents a part of the document (for buffered document) or can include the whole document. So the list of lines can be all the lines or just the lines of a part. We won't treat buffered document in this tutorial to keep things simple.

Once again, let's check only the most important object, ILine. It contains:

We will need to add ITextItem to our current model. The TextItem default implementation can be used. It takes as argument:

Assignment 1: Clone the sample code and checkout the tutorial7 branch. It already displays functions using a special ItemClassIdentifiers. Try to do the same for the var keyword and string constants.

Notifications

JEB is a security-oriented product: its aim is to help you analyze malicious code and pin-point potential areas of interest. Notifications can be used to show areas that are noteworthy; styles can be attached to notifications to highlight their corresponding elements on rendering. Notifications are global to a unit, they should be generated whe the processor plugin process()-es the input.

For the sake of this tutorial, we want to highlight the usage of function "alert" because it can block the UI unreasonably.

The AbstractUnit interface exposes the following method:

public addtNotifications(IUnitNotification unit);

We will use the default implementation: UnitNotification.

In the process() method of your JavaScript plugin, add the following snippet:

root.visit(new NodeVisitor() {
    @Override
    public boolean visit(AstNode node) {
        switch(node.getType()) {
        case Token.EXPR_VOID:
        case Token.EXPR_RESULT:
            break;
        case Token.CALL:
            visitTarget(((FunctionCall)node).getTarget());
            break;
        default:
            break;
        }
    return true;
    }

    private void visitTarget(AstNode target) {
        if(target.getType() == Token.NAME) {
            if(((Name)target).getIdentifier().equals("alert")) {
                // Add notification
                addNotification(new UnitNotification(NotificationType.POTENTIALLY_HARMFUL,
                        String.format("alert is detected at position %d", target.getAbsolutePosition())));
            }
        }
    }
});

The visit() method recursively process all the elements. When we have a JavaScript function call, we check the name and add a notification if the function is "alert".

Now, you should see something new in the UI:

If you want to see all notifications as soon as the file is opened in JEB, you will need to slightly modify your code. Since the process() method is only called on opening an IUnit, the best is to call it directly after IUnit creation:

public IUnit prepare(String name, IInput input, IUnitProcessor unitProcessor, IUnitCreator parent) {
    IUnit sampleUnit = new SampleUnit(name, input, unitProcessor, parent, pdm);
    sampleUnit.process(); // forces children calculation
    return sampleUnit;
}

Always be careful to add this line somewhere in the process() method to indicate that the processing should not be done once again:

setProcessed(true);

Now, we see something interesting on the screenshot: there is an address column that is blank. IUnitNotification defines an address binding which can be used to jump to the related section. Let's see how to use it.

Addressing

To be able to jump to a correct Address, we need to define a binding from Unit addressing (address) to a position in a Document (coordinates).

ITextDocument uses ICoordinates: it contains the anchor id, a line number relative to the anchor, and the position (column offset) in the line.

Addresses, however, are user-defined (in this context, the user is the plugin developer): it is up to user to define them and their granularity. For instance, you can choose to support addresses:

The important aspect of addresses is that an address must not be directly tied to a Document (your implementation of Document can change: if you add a beautifier, if you decide to format your document on a single line, if you use a tree/table for your representation, etc.). Of course, there should still be a way to make the link between the two addressing systems:

Refer to the technical draft "Positioning within inputs, units, and documents" for additional details on addressing.

At last, an address must identify an object/position in a unique way. Otherwise, you wouldn't know where to jump.

For our sample JavaScript plugin, we will use the addressing using AstNode.getAbsolutePosition(). When jumping from Notification Panel, we have the current address (Absolute position), and we will jump to an ICoordinates in the ITextDocument. So the main method to implement is:

public ICoordinates addressToCoordinates(String address);

Assignment 2: Try to implement this method.

There is also another column in the Notification Panel called "Label": it represents the label of the address. You can indicate here a human readable label: for example, the name of the function.

To display label, we need to inherit from IInteractiveUnit and implement String getAddressLabel(String address) to see changes in the Notification Panel.

Assignment 3 (hard): Try to implement the getAddressLabel() method.

A solution to the assignments can be found by checking out the branch tutorial7_solution of the sample code.

You may have noticed that IInteractiveUnit provides a lot of methods. We will focus on it in the next part!

Next: Part 8