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')
  • Additional rating systems (plugin_type = 'custom-rating')
  • XML postprocessors 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 rating system for use within Canopy:

from canopy.modules.common.models import Rating

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


TODO: explain the plugin line-by-line.

TODO: provide additional examples.

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