Overview

This guide should give you a clear understanding of what Sensei is and how to configure it. The overview will explain some key concepts and guide you to create your first recipe.

How does Sensei work?

Sensei is an IDE plugin that performs on the fly code analysis. This behavior is configured in what we call 'recipes'. A recipe consists of three sections.

General Settings

The General Settings contain the metadata of the recipe, e.g. name, description, category, etc. Most of the information in this section is used to describe a recipe rather than to configure its behavior.

Recipe Settings

This section describes the behavior of the recipe, i.e. what code to look for.

QuickFix Settings

This section contains all the possible quickfixes (suggestions) for a single recipe. This behavior is invoked when a user places their cursor at a piece of marked code, and clicks on the light bulb or presses the quickfix menu shortcut.

How are recipes stored?

Recipes are stored in what we call 'cookbooks'. Technically, a cookbook is simply a directory, that contains a configuration file, with an optional subdirectory, containing the descriptions. Cookbooks can be either in the form of a directory or that of a ZIP file depending on where these are loaded from.

This leads directly to our second topic: recipe distribution. Cookbooks can be stored in numerous places.

  • Local Path

  • HTTP(S) server

  • Git repository

  • Inside the project

Note

In the tutorials, recipes will always be stored inside the project.

Configuring which cookbooks Sensei should load, can be done locally or remotely (if you have company administrator rights at https://portal.securecodewarrior.com). During IntelliJ startup both of these configurations are read. Afterwards, Sensei itself will load the recipes from these locations. This implies that you can remotely configure to load local recipes.

Note

Free authenticated Sensei users will have the "Basic Protection Cookbook" configured as a default remote cookbook named basic-protection-set.

Non-authenticated users will have the "Basic Protection Cookbook" configured as a default remote cookbook named basic-protection-set.

Local settings

Open your IDE and navigate to Tools | Sensei | Cookbook Manager.

Remote settings

As mentioned before, this requires company administration access to find and tweak these settings. If you have administrator rights, these settings can be found by logging in at the Secure Code Warrior portal and navigating to Administration | Preferences | Rule Settings.

See also

For more information, see How to distribute recipes

What to search for?

To understand how the search engine works, we'll start by explaining some key concepts.

search:             # start of our recipe
  methodcall:       # example of a target
    name: "equals"  # example of an option

The above example is a very simple recipe that you can write in Sensei. The first keyword of every recipe should be search. This tells the engine that we will be searching for a specific target. In this example, we are looking for a methodcall. Each target can also be configured to match only specific elements. In this case, only method calls that have the name equals will match the configured recipe.

See also

The list of available targets can be found at reference > targets.

Each option has its own way of being configured. Some of these will require you to simply specify a string or a boolean value. Others will expect another target configuration. For example:

search:
  methodcall:
    name: "equals"
    on:                      # example of an option that expects a target configuration
      methodcall:            # second configured target
        name: "toLowerCase"  # option of our second target

See also

All these options are described in the reference section for each of the available targets. To find the list of available options for the target used, visit reference > targets > methodcall.

A quick recap of what we've learned.

  1. Recipes start with the keyword search

  2. Next, specify what you are looking for by choosing one of the available targets

  3. Finally, targets can be configured using the available options. Some of these options expect primitives values, while others expect other target configurations.

How to make suggestions?

Suggestions can be configured in the quickfix settings tab. Any recipe has the ability to configure multiple suggestions. Each of these consist of the name that will be shown to the user and a list of actions that will be performed when invoking the quickfix.

availableFixes:             # List of all suggestions
- name: "Remove statement"  # Name that will be shown to the user
  actions:
  - remove:                 # currently only a single action will be performed

See also

The list of available actions can be found at reference > actions.

These actions will by default operate on the element that matches the target specification. In the majority of cases, you'll want to perform multiple actions that modify other parts of the file.

availableFixes:
- name: "Use instance variable"    # Name that will be shown to the user
  actions:
  - rewrite:                       # First action that will be performed
      to: "this.heavyObject"
  - addField:                      # Second action that will be performed
      field: "Heavy heavyObject;"
      target: "parentClass"        # Tells the action to operate on the class

To recap, each recipe can have multiple suggestions. Each suggestion has a name that is shown to the user, and a list of actions. These actions operate by default on the matched element, but this behavior can be changed by specifying the target.

Let's get practical

Now that the basic concepts have been covered, we can go through a few examples that explain certain details a bit more in-depth. We will address the following topics:

  1. What element should I look for?

  2. How the search influences the quickfix actions

  3. Making recipes more generic by using regular expressions and logical operators.

  4. Narrowing down when a recipe should trigger by scoping