Skip to content

Part 7: Interactivity

Warning

This tutorial is deprecated, and will be rewritten to account for JEB 4 API changes.

JEB Plugin Development Tutorial part 7/8

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

  • Clone this repo: git clone https://github.com/pnfsoftware/jeb2-plugindemo-js.git
  • Switch to the tutorial7 branch: git checkout tutorial7

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:

  • it builds ITextDocumentPart
  • it has a coordinate to/from address conversion system. We will see this later.

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

  • one returns a list of ILine objects: the part is split into lines.
  • the other one returns a list of IAnchor objects. Anchors act the same as chapters for a book: an anchor indicates the beginning of a new section. If you don't need it, you can use a single anchor for the entire document.

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:

  • a CharSequence: the text line
  • a List of ITextItem. As said in the API: A text item is used to identify a particular sequence of characters. This looks like what we were looking for.

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

  • the start offset of the keyword
  • the length of the keyword
  • an ItemClassIdentifiers identifier to apply a specific semantic and eventually, style, to your element. You can configure the display of these styles in the Edit, Fonts and Style... panel.

Assignment

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 when 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:

  • at function level/ element level. For example: "function a", "function a / statement 1"
  • at byte level. For example, using absolute byte address in file: "0x1A", "0x89"
  • at any other level. For example, at each line, each character for that line, etc.

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:

Note

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

Implement the addressToCoordinates 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

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!