Plugin System

pySigma uses a plugin system to discover and manage backends, processing pipelines, and validators. This guide explains how to use existing plugins and how to create your own.

Overview

pySigma supports three types of plugins:

  • Backends: Convert Sigma rules into target query languages.

  • Processing Pipelines: Transform rules for specific environments.

  • Validators: Check rules for correctness and best practices.

Plugins are distributed as separate Python packages and discovered through Python namespace packages.

Using Plugins

Discovering Installed Plugins

from sigma.plugins import InstalledSigmaPlugins

plugins = InstalledSigmaPlugins.autodiscover()

# Access backends
print(plugins.backends)  # dict of name -> backend class

# Access pipelines
print(plugins.pipelines)  # dict of name -> pipeline function

# Access validators
print(plugins.validators)  # dict of name -> validator class

Using the Plugin Directory

pySigma maintains a public directory of available plugins:

from sigma.plugins import SigmaPluginDirectory

directory = SigmaPluginDirectory.default_plugin_directory()

# List all available plugins
for plugin in directory.plugins:
    print(f"{plugin.id}: {plugin.description}")

# Install a plugin
directory.get_plugin("splunk").install()

Using Pipeline Resolvers

The ProcessingPipelineResolver combines installed pipelines and resolves them by name, automatically ordering them by their priority values (lowest first). This ensures pipelines are applied in the correct sequence:

from sigma.processing.resolver import ProcessingPipelineResolver
from sigma.plugins import InstalledSigmaPlugins

plugins = InstalledSigmaPlugins.autodiscover()
resolver = ProcessingPipelineResolver.from_pipeline_list(plugins.pipelines.values())

# Resolve and merge pipelines by name, respecting their priority values
pipeline = resolver.resolve(["sysmon", "windows"])
# If sysmon has priority 10 and windows has priority 20,
# they will be merged with sysmon applied first

Creating a Backend Plugin

Package Structure

A backend plugin follows this structure:

pySigma-backend-mybackend/
├── pyproject.toml
├── sigma/
│   └── backends/
│       └── mybackend/
│           ├── __init__.py
│           └── backend.py
└── tests/
    └── test_backend.py

Namespace Package Setup

The sigma/ and sigma/backends/ directories must be namespace packages (no __init__.py in sigma/ and sigma/backends/). Only the leaf package has an __init__.py:

# sigma/backends/mybackend/__init__.py
from .backend import MyBackend

backends = {
    "mybackend": MyBackend,
}

The backends dictionary maps identifiers to backend classes. This is how the plugin system discovers your backend.

Package Configuration

In pyproject.toml:

[project]
name = "pySigma-backend-mybackend"
description = "pySigma backend for MySystem"
dependencies = ["pySigma>=1.0.0"]

[tool.poetry.packages]
include = "sigma"

Creating a Pipeline Plugin

Pipeline plugins provide reusable processing pipelines:

pySigma-pipeline-mypipeline/
├── pyproject.toml
├── sigma/
│   └── pipelines/
│       └── mypipeline/
│           ├── __init__.py
│           └── pipeline.py
└── tests/
    └── test_pipeline.py
# sigma/pipelines/mypipeline/__init__.py
from .pipeline import my_pipeline

pipelines = {
    "my_pipeline": my_pipeline,
}
# sigma/pipelines/mypipeline/pipeline.py
from sigma.processing.pipeline import ProcessingPipeline, ProcessingItem
from sigma.processing.transformations import FieldMappingTransformation
from sigma.processing.conditions import LogsourceCondition

def my_pipeline() -> ProcessingPipeline:
    return ProcessingPipeline(
        name="My Pipeline",
        priority=20,
        items=[
            ProcessingItem(
                identifier="my_field_mapping",
                transformation=FieldMappingTransformation({
                    "CommandLine": "process.command_line",
                }),
                rule_conditions=[
                    LogsourceCondition(
                        category="process_creation",
                        product="windows",
                    )
                ],
            )
        ],
    )

The pipelines dictionary maps names to callable functions that return a ProcessingPipeline instance.

Creating a Validator Plugin

Validator plugins provide custom rule checks:

pySigma-validators-myvalidators/
├── pyproject.toml
├── sigma/
│   └── validators/
│       └── myvalidators/
│           ├── __init__.py
│           └── validators.py
└── tests/
    └── test_validators.py
# sigma/validators/myvalidators/__init__.py
from .validators import MyValidator, AnotherValidator

validators = {
    "my_check": MyValidator,
    "another_check": AnotherValidator,
}

The validators dictionary maps identifiers to validator classes.

Plugin Naming Conventions

Follow these naming conventions for plugins:

  • Backends: pySigma-backend-<name> (e.g., pySigma-backend-splunk)

  • Pipelines: pySigma-pipeline-<name> (e.g., pySigma-pipeline-sysmon)

  • Validators: pySigma-validators-<name>

Note

It is a common practice to package processing pipelines in backend plugins. In such cases the backend naming scheme is used.

Testing Plugins

Test your plugins against the pySigma test infrastructure:

import pytest
from sigma.rule import SigmaRule
from sigma.collection import SigmaCollection

def test_plugin_discovery():
    from sigma.plugins import InstalledSigmaPlugins
    plugins = InstalledSigmaPlugins.autodiscover()
    assert "mybackend" in plugins.backends

def test_basic_conversion():
    from sigma.backends.mybackend import MyBackend
    backend = MyBackend()
    rule = SigmaRule.from_yaml(\"\"\"
        title: Test
        status: test
        logsource:
            category: test
        detection:
            sel:
                field: value
            condition: sel
    \"\"\")
    result = backend.convert(SigmaCollection(rules=[rule]))
    assert len(result) > 0