Writing plugins

With Canopy 3, we’ve added the ability to use plugins in order to more easily extend functionality within Canopy. We’ve helped users previously write plugins, such as:

  • Custom tool format parsers, for use in importers (plugin_type = 'parser').

  • Customised finding rating systems (plugin_type = 'custom-rating').

  • XML post-processors for adding custom functions to XML generation for Word reports and/or SoWs (plugin_type = 'xml-postprocessor').

The addition of the plugin system provides a standard interface for adding custom code to your Canopy instance.

Plugins are currently supported using the Python programming language.

Anatomy of a plugin

A plugin requires the following fields to ensure it is set up and registered by the plugin loader:

plugin_type = 'custom-rating'
name = 'Example Custom Rating'

The plugin_type is used to register the type of the plugin. This is arbitrary, although guidelines (e.g. custom-rating) will be provided soon. The name registers the plugins name with the plugin system.

Caveats

The database cannot be assumed to be available during plugin load time. Therefore no module-level code in the plugin file should perform any action that requires database connectivity, directly or indirectly.

Example: Custom rating plugin

The following example shows the implementation of a custom findings rating system for use within Canopy:

from canopy.modules.common.models import Rating

__version__ = '0.0.1'
plugin_type = 'custom-rating'
name = 'Example Custom Rating'

RATINGS_EDITABLE = False

RATINGS = [
    ['Info', 'Info', 'Info', 'Info', 'Low', 'Low', 'Low', 'Low', 'Medium',
    'Medium'],
    ['Info', 'Info', 'Info', 'Info', 'Low', 'Low', 'Low', 'Medium',
    'Medium', 'Medium'],
    ['Info', 'Info', 'Info', 'Low', 'Low', 'Low', 'Low', 'Medium',
    'Medium', 'High'],
    ['Info', 'Info', 'Info', 'Low', 'Low', 'Low', 'Medium', 'Medium',
    'High', 'High'],
    ['Info', 'Info', 'Low', 'Low', 'Low', 'Medium', 'Medium', 'Medium',
    'High', 'High'],
    ['Info', 'Low', 'Low', 'Low', 'Low', 'Medium', 'Medium', 'Medium',
    'High', 'High'],
    ['Info', 'Low', 'Low', 'Low', 'Medium', 'Medium', 'Medium', 'High',
    'High', 'Critical'],
    ['Low', 'Low', 'Low', 'Medium', 'Medium', 'Medium', 'High', 'High',
    'Critical', 'Critical'],
    ['Low', 'Low', 'Medium', 'Medium', 'Medium', 'Medium', 'High', 'High',
    'Critical', 'Critical'],
    ['Low', 'Low', 'Medium', 'Medium', 'Medium', 'High', 'High',
    'Critical', 'Critical', 'Critical'],
]

OVERALL_RATINGS = {
    'Info': 1,
    'Low': 2,
    'Medium': 3,
    'High': 4,
    'Critical': 5,
}


def calc_rating(finding):
    score = 0
    rating = finding.rating
    custom_fields = {
        fv.field.name: fv.value
        for fv in finding.custom_fields.prefetch_related('field')
    }
    impact = int(custom_fields.get('impact', 0))
    ease = int(custom_fields.get('ease', 0))

    if impact and ease:
        rating = Rating.objects.get(type=RATINGS[impact-1][ease-1])
        score = OVERALL_RATINGS[RATINGS[impact-1][ease-1]]

    return score, rating, None

Installing plugins

Once you’re ready to run your plugin, it needs to be installed. For further information on this, see Installing plugins and management commands.

Further improvements

The plugin framework will be improved over the coming releases. Identified areas of improvement include:

  • Making testing and validation of plugins easier.

  • Define a number of helper functions for certain types of plugins (e.g. markup formatting for tool importers).

  • User-interface plugins to support customisation of the UI.