-
Notifications
You must be signed in to change notification settings - Fork 10
feat: add plugin schema #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
fzipi
wants to merge
7
commits into
main
Choose a base branch
from
feat/add-plugin-schema
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
25def86
feat: add plugin schema
fzipi bc82781
fix: address schema review comments - constraints, types, and naming
Copilot e9ba83f
feat: remove version field from plugin schema
fzipi 83f5046
Apply suggestions from code review
fzipi 67cec35
fix: address unresolved review comments from theseion
fzipi b96f392
Apply suggestions from code review
fzipi eae1216
fix: table formatting, add engines taxonomy, restrict repository to G…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| # CRS Plugin Descriptor Schema | ||
|
|
||
| ## Overview | ||
|
|
||
| The plugin descriptor schema defines a `plugin.yaml` file that lives in the root of each CRS plugin repository. It provides machine-readable metadata about the plugin, its configuration variables, and compatibility requirements. | ||
|
|
||
| The plugin registry aggregates these descriptors to generate the registry table, and downstream tooling (such as a CRS configurator) can parse them to build preconfigured CRS deployments based on plugin selection. | ||
|
|
||
| ## Goals | ||
|
|
||
| - Allow each plugin repository to be the single source of truth for its own metadata. | ||
| - Enable automated tooling to discover, validate, and configure plugins. | ||
| - Replace manual registry table maintenance with generated output. | ||
| - Provide enough information for a configurator to present a UI for plugin selection and variable tuning. | ||
|
|
||
| ## Schema Structure | ||
|
|
||
| The schema is defined as [JSON Schema (2020-12)](https://json-schema.org/draft/2020-12/json-schema-core) in [`plugin-schema.json`](../plugin-schema.json). A `plugin.yaml` file has the following top-level sections: | ||
|
|
||
| ### `schema_version` | ||
|
|
||
| **Required.** Integer, currently fixed at `1`. | ||
|
|
||
| Allows future evolution of the schema without breaking existing parsers. Tooling should check this field and handle unknown versions gracefully. | ||
|
|
||
| ### `plugin` | ||
|
|
||
| **Required.** Plugin identity and metadata. | ||
|
|
||
| | Field | Required | Description | | ||
| |--------------------|----------|-------------| | ||
| | `name` | yes | Plugin name following the CRS convention: `<name>-plugin`. Validated by regex. | | ||
| | `description` | yes | One-line summary of the plugin's purpose. | | ||
| | `long_description` | no | Multi-line extended description for documentation and UI display. | | ||
| | `type` | yes | `official` (coreruleset-maintained) or `3rd-party`. | | ||
| | `category` | no | Functional category: `rule-exclusion`, `detection`, `protection`, `utility`, `logging`, or `performance`. | | ||
| | `status` | yes | Maturity level: `tested`, `being-tested`, `untested`, or `draft`. | | ||
| | `license` | yes | SPDX license identifier (e.g., `Apache-2.0`, `GPL-2.0-only`). | | ||
| | `authors` | yes | List of author objects with `name` (required), `email` and `url` (optional). | | ||
| | `repository` | yes | URL of the plugin GitHub repository (only GitHub repositories are currently supported). | | ||
| | `homepage` | no | URL to documentation or project site. | | ||
| | `keywords` | no | Tags for discovery and categorization. | | ||
|
|
||
| ### `rule_id_range` | ||
|
|
||
| **Required.** The registered rule ID block from the plugin registry. | ||
|
|
||
| | Field | Description | | ||
| |---------|-------------| | ||
| | `start` | First rule ID in the allocated range (integer, 9500000-9999999). | | ||
| | `end` | Last rule ID in the allocated range (integer, 9500000-9999999). | | ||
|
|
||
| Plugins typically receive a 1,000-ID range. The schema validates that values fall within the CRS plugin namespace (9,500,000 - 9,999,999). | ||
|
|
||
| ### `compatibility` | ||
|
|
||
| **Optional.** WAF engine and CRS version requirements. | ||
|
|
||
| | Field | Description | | ||
| |---------------|-------------| | ||
| | `crs_version` | Version constraint string (e.g., `>=4.0.0`). | | ||
| | `engines` | List of compatible WAF engines, one of: `modsecurity2`, `modsecurity3`, `coraza`, `all`. | | ||
|
|
||
| When omitted, no compatibility constraints are assumed. Tooling should treat missing `engines` field as if `engines` were set tp `all`. | ||
|
|
||
| ### `dependencies` | ||
|
|
||
| **Optional.** List of other CRS plugins this plugin depends on. | ||
|
|
||
| Each entry has: | ||
| | Field | Required | Description | | ||
| |-----------|----------|-------------| | ||
| | `name` | yes | Name of the required plugin. | | ||
| | `version` | no | Version constraint for the dependency. | | ||
|
|
||
| Most plugins have no dependencies. This field exists for plugins that build on top of other plugins. | ||
|
|
||
| ### `configuration` | ||
|
|
||
| **Required.** The key section for automated tooling. Describes the config file and all user-tunable variables. | ||
|
|
||
| | Field | Description | | ||
| |-------------|-------------| | ||
| | `file` | Path to the `-config.conf` file relative to the plugin root. | | ||
| | `variables` | List of transaction variable definitions (see below). | | ||
|
|
||
| #### Variable Definition | ||
|
|
||
| Each entry in `variables` describes a single `tx.*` variable from the config file: | ||
|
|
||
| | Field | Required | Description | | ||
| |------------------|----------|-------------| | ||
| | `name` | yes | Full ModSecurity variable name (e.g., `tx.myplugin_enabled`). | | ||
| | `type` | yes | Data type: `boolean`, `integer`, `string`, `list`, or `enum`. | | ||
| | `default` | no | Default value if not set by the user. | | ||
| | `description` | yes | Human-readable explanation of the variable. | | ||
| | `required` | no | Whether the user must explicitly set this variable (default: `false`). | | ||
| | `allowed_values` | no | List of valid values when type is `enum`. | | ||
|
fzipi marked this conversation as resolved.
|
||
| | `separator` | no | String used to separate multiple entries when type is `list`. | | ||
| | `prefix` | no | String marking the beginning of a list entry when type is `list`. | | ||
| | `suffix` | no | String marking the end of a list entry when type is `list`. | | ||
| | `example` | no | Example value for documentation. | | ||
| | `min` | no | Minimum value when type is `integer`. | | ||
| | `max` | no | Maximum value when type is `integer`. | | ||
|
|
||
| #### Variable Types | ||
|
|
||
| The four types cover all patterns found across existing CRS plugins: | ||
|
|
||
| - **`boolean`** — Enable/disable flags. Every plugin has at least `tx.<name>_enabled`. Values are integers: `0` (disabled) or `1` (enabled), following the ModSecurity convention for boolean flags. | ||
| - **`integer`** — Numeric thresholds and limits (e.g., `tx.body-decompress-plugin_max_data_size_bytes`). Supports `min`/`max` constraints. | ||
| - **`string`** — Freeform text values. The `example` field helps users understand the expected format. | ||
| - **`list`** — Structured list of strings (e.g., `tx.google-oauth2-plugin_whitelisted_parameters`). The fields `separator`, `prefix`, `suffix` define the format. For example, the list `|a/ |b/ |c/` would be defined as follows: | ||
| - `separator`: `" "` (space character) | ||
| - `prefix`: `"|"` | ||
| - `suffix`: `"/"` | ||
| - **`enum`** — Constrained choices (e.g., `tx.phpmyadmin-rule-exclusions-plugin_url_format` with values `v51`, `v52`). Must include `allowed_values`. | ||
|
|
||
| ## Design Decisions | ||
|
|
||
| ### Why YAML over JSON or TOML? | ||
|
|
||
| YAML supports comments, multi-line strings, and is widely used in the CRS ecosystem (test files use FTW YAML format). Plugin authors already work with YAML for regression tests, so it is familiar. | ||
|
|
||
| ### Why `schema_version` as a top-level field? | ||
|
|
||
| Embedding versioning in the schema itself allows parsers to detect incompatible changes before attempting to parse. This is simpler than relying on file naming or external version tracking. | ||
|
|
||
| ### Why typed variables with constraints? | ||
|
|
||
| A configurator project needs to know more than just variable names and defaults. By declaring types, allowed values, and numeric bounds, tooling can: | ||
|
|
||
| 1. Render appropriate input widgets (toggle for boolean, slider for bounded integer, dropdown for enum). | ||
| 2. Validate user input before generating configuration. | ||
| 3. Generate documentation automatically. | ||
|
|
||
| ### Why is `configuration` required? | ||
|
|
||
| Every CRS plugin has at least one configuration variable (`tx.<name>_enabled`). Making this section required ensures consistency and guarantees that tooling always has something to work with, even for minimal rule-exclusion plugins. | ||
|
|
||
| ### Why no `version` field? | ||
|
|
||
| The plugin version is derived from GitHub release tags at query time. Embedding a version in `plugin.yaml` would inevitably drift because developers forget to bump it. Tooling should fetch the latest release tag from the repository URL instead. This keeps `plugin.yaml` as a static descriptor that rarely needs updating. | ||
|
|
||
| ### Why separate `rule_id_range` from `plugin`? | ||
|
|
||
| The rule ID range is a registry-level concern (namespace coordination), not a plugin identity attribute. Keeping it separate makes it clear that this range is allocated by the registry and should not be changed unilaterally. | ||
|
|
||
| ### Category taxonomy | ||
|
theseion marked this conversation as resolved.
|
||
|
|
||
| The six categories cover the existing plugin landscape: | ||
|
|
||
| | Category | Examples | | ||
| |------------------|----------| | ||
| | `rule-exclusion` | wordpress, drupal, nextcloud, phpbb, cpanel | | ||
| | `detection` | fake-bot | | ||
| | `protection` | dos-protection | | ||
| | `utility` | template, body-decompress, auto-decoding | | ||
| | `logging` | database-logging, false-positive-report | | ||
| | `performance` | performance-plugin | | ||
|
|
||
| New categories can be added to the schema's enum as the ecosystem grows. | ||
|
|
||
| ### Engine taxonomy | ||
|
|
||
| The supported WAF engines are: | ||
|
|
||
| | Engine | Description | | ||
| |------------------|-------------| | ||
| | `modsecurity2` | ModSecurity 2.x (Apache module) | | ||
| | `modsecurity3` | ModSecurity 3.x (connector-based, e.g. nginx) | | ||
| | `coraza` | Coraza WAF | | ||
| | `all` | Compatible with all supported engines | | ||
|
|
||
| New engines can be added to the schema's enum as the ecosystem grows. | ||
|
|
||
| ## Configurator Integration | ||
|
|
||
| The primary consumer of `plugin.yaml` files is a configurator tool that: | ||
|
|
||
| 1. **Fetches** `plugin.yaml` from each registered plugin repository. | ||
| 2. **Presents** available plugins grouped by category, with descriptions and compatibility info. | ||
| 3. **Collects** user choices: which plugins to enable and how to tune their variables. | ||
| 4. **Validates** input against variable constraints (type, min/max, allowed_values). | ||
| 5. **Resolves** dependencies between plugins. | ||
| 6. **Generates** a complete CRS configuration with all selected plugins and their tuned `tx.*` variables. | ||
|
|
||
| ## Rollout Plan | ||
|
|
||
| 1. Add `plugin.yaml` to `coreruleset/template-plugin` as the reference implementation. | ||
| 2. Get feedback from the CRS team and iterate on the schema. | ||
| 3. Once the schema is accepted, create PRs adding `plugin.yaml` to all registered plugins. | ||
| 4. Update the registry to validate incoming plugin registrations against the schema. | ||
| 5. Build the configurator project. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # OWASP CRS Plugin Descriptor | ||
| # https://github.com/coreruleset/plugin-registry | ||
|
|
||
| schema_version: 1 | ||
|
|
||
| plugin: | ||
| name: "body-decompress-plugin" | ||
| description: "On-the-fly decompression of response bodies for CRS inspection" | ||
| long_description: | | ||
| Decompresses gzip/deflate compressed HTTP response bodies so that CRS | ||
| response rules can inspect the actual content. Without this plugin, | ||
| compressed responses bypass response body inspection rules. | ||
| type: "official" | ||
| category: "utility" | ||
| status: "being-tested" | ||
| license: "Apache-2.0" | ||
| authors: | ||
| - name: "OWASP CRS Team" | ||
| url: "https://coreruleset.org" | ||
| repository: "https://github.com/coreruleset/body-decompress-plugin" | ||
| keywords: | ||
| - "decompression" | ||
| - "response-body" | ||
| - "gzip" | ||
|
|
||
| rule_id_range: | ||
| start: 9503000 | ||
| end: 9503999 | ||
|
|
||
| compatibility: | ||
| crs_version: ">=4.0.0" | ||
| engines: | ||
| - "modsecurity2" | ||
| - "modsecurity3" | ||
|
|
||
| configuration: | ||
| file: "plugins/body-decompress-config-before.conf" | ||
| variables: | ||
| - name: "tx.body-decompress-plugin_enabled" | ||
| type: "boolean" | ||
| default: 1 | ||
| description: "Enable or disable response body decompression" | ||
|
|
||
| - name: "tx.body-decompress-plugin_max_data_size_bytes" | ||
| type: "integer" | ||
| default: 102400 | ||
| description: "Maximum decompressed data size in bytes" | ||
| min: 1024 | ||
| max: 10485760 | ||
| example: 102400 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # OWASP CRS Plugin Descriptor | ||
| # https://github.com/coreruleset/plugin-registry | ||
|
|
||
| schema_version: 1 | ||
|
|
||
| plugin: | ||
| name: "fake-bot-plugin" | ||
| description: "Detects fake search engine bots by verifying crawler IP addresses" | ||
| long_description: | | ||
| Identifies HTTP requests that claim to be from known search engine crawlers | ||
| (Googlebot, Bingbot, etc.) but originate from IP addresses not belonging to | ||
| those search engines. Helps protect against scraping and abuse from clients | ||
| impersonating legitimate bots. | ||
| type: "official" | ||
| category: "detection" | ||
| status: "tested" | ||
| license: "Apache-2.0" | ||
| authors: | ||
| - name: "OWASP CRS Team" | ||
| url: "https://coreruleset.org" | ||
| repository: "https://github.com/coreruleset/fake-bot-plugin" | ||
| homepage: "https://coreruleset.org/docs/plugins/" | ||
| keywords: | ||
| - "bot-detection" | ||
| - "fake-bot" | ||
| - "crawler" | ||
| - "security" | ||
|
|
||
| rule_id_range: | ||
| start: 9504000 | ||
| end: 9504999 | ||
|
|
||
| compatibility: | ||
| crs_version: ">=4.0.0" | ||
| engines: | ||
| - "modsecurity2" | ||
| - "modsecurity3" | ||
| - "coraza" | ||
|
|
||
| configuration: | ||
| file: "plugins/fake-bot-config.conf" | ||
| variables: | ||
| - name: "tx.fake-bot-plugin_enabled" | ||
| type: "boolean" | ||
| default: 1 | ||
| description: "Enable or disable the fake bot detection plugin (0 to disable)" | ||
|
|
||
| - name: "tx.fake-bot-plugin_whitelist_broken_apple_devices" | ||
| type: "boolean" | ||
| default: 0 | ||
| description: "Whitelist broken Apple devices that incorrectly identify as Googlebot" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| # OWASP CRS Plugin Descriptor | ||
| # https://github.com/coreruleset/plugin-registry | ||
|
|
||
| schema_version: 1 | ||
|
|
||
| plugin: | ||
| name: "wordpress-rule-exclusions-plugin" | ||
| description: "CRS rule exclusions for WordPress applications" | ||
| long_description: | | ||
| Provides targeted rule exclusions to eliminate false positives when | ||
| running OWASP CRS in front of WordPress sites. Covers the WordPress | ||
| admin panel, REST API, and common plugin/theme patterns. | ||
| type: "official" | ||
| category: "rule-exclusion" | ||
| status: "tested" | ||
| license: "Apache-2.0" | ||
| authors: | ||
| - name: "OWASP CRS Team" | ||
| url: "https://coreruleset.org" | ||
| repository: "https://github.com/coreruleset/wordpress-rule-exclusions-plugin" | ||
| keywords: | ||
| - "wordpress" | ||
| - "cms" | ||
| - "rule-exclusion" | ||
| - "false-positive" | ||
|
|
||
| rule_id_range: | ||
| start: 9507000 | ||
| end: 9507999 | ||
|
|
||
| compatibility: | ||
| crs_version: ">=4.0.0" | ||
| engines: | ||
| - "modsecurity2" | ||
| - "modsecurity3" | ||
| - "coraza" | ||
|
|
||
| configuration: | ||
| file: "plugins/wordpress-rule-exclusions-config.conf" | ||
| variables: | ||
| - name: "tx.wordpress-rule-exclusions-plugin_enabled" | ||
| type: "boolean" | ||
| default: 1 | ||
| description: "Enable or disable WordPress rule exclusions (0 to disable)" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.