Skip to content

SecLang YAML Structure Reference

This page documents the structured representation used in SecRule.spec.secLangRules[].

Top-Level Rule

- metadata: SecRuleMetadata
  conditions: []Condition
  actions: SecRuleActions
  chainedRule: bool
  secMarker: string

Metadata

metadata:
  id: 942100
  phase: "2"
  message: "SQL Injection Attack Detected"
  severity: CRITICAL
  tags:
    - attack-sqli
    - OWASP_CRS
    - paranoia-level/2

Supported severities: EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.

Conditions

A condition describes when the rule should trigger.

Variable Form

conditions:
- variables:
  - name: REQUEST_URI
  - name: ARGS_GET
    collection: id           # optional member of collection
  operator:
    name: rx
    value: (?:union|select|--)
    negate: false

Collection / TX Form (common in CRS)

conditions:
- collections:
  - name: TX
    arguments:
    - DETECTION_PARANOIA_LEVEL
  operator:
    name: lt
    value: "1"

Always-Match (initialization rules)

conditions:
- always-match: true

Operators

Name Example Value Notes
rx (?i)select.*from Most common
streq, contains, startswith, endsWith admin String ops
eq, gt, ge, lt, le 5 Numeric
ipMatch 192.168.0.0/16,10.0.0.0/8
detectSQLi (no value) Uses libinjection
detectXSS (no value)
geoLookup (no value) Requires GeoIP data in Wasm image

Actions

Disruptive Actions

These decide whether the request continues:

disruptive:
  disruptiveActionType: deny | block | allow | pass | drop | redirect
  status: "403"                    # for deny/block
  redirectUrl: "https://..."

Flow Control

flow:
- flowActionType: skip
  value: "950000"           # skip next N rules
- flowActionType: skipAfter
  value: END-REQUEST-920    # skip to marker

Non-Disruptive Actions

These modify state or logging without stopping evaluation:

nonDisruptive:
- nonDisruptiveActionType: setvar
  value: TX.inbound_anomaly_score_pl1=+5
- nonDisruptiveActionType: msg
  value: "Attack detected on %{REQUEST_URI}"
- nonDisruptiveActionType: logdata
  value: "%{MATCHED_VAR}"
- nonDisruptiveActionType: ctl
  value: ruleRemoveById=942100
- nonDisruptiveActionType: nolog

Chaining

To express "condition A AND condition B":

- metadata: { id: 942100, ... }
  conditions: [ condition A ]
  actions:
    disruptive: { disruptiveActionType: pass }
  chainedRule: true
- metadata: { ... same id ... }
  conditions: [ condition B ]
  actions:
    disruptive: { disruptiveActionType: block }

The converter automatically emits the chain action on the first part.

SecMarker

A marker rule looks like this:

- secMarker: END-REQUEST-920-PROTOCOL-ENFORCEMENT
  metadata:
    id: 0
    phase: "1"
  conditions:
  - always-match: true
  actions:
    nonDisruptive:
    - nonDisruptiveActionType: nolog

Later rules can skipAfter: END-REQUEST-920-PROTOCOL-ENFORCEMENT.

Complete Realistic Example

See the CRS samples in the repository (config/samples/crs/) — they are the best reference for complex real-world rules including heavy use of TX variables, ctl actions, and chaining.

Conversion Guarantees

The controller uses the same crslang library that the CRS converter uses. The generated .status.secRuleString is what Coraza actually compiles.

If you ever want to debug "why isn't my rule matching?", copy the secRuleString value and test it directly with the coraza CLI or a minimal Coraza-Go program.

Full CRD Schema

For every possible field and validation rule, see the generated CRD:

Next: read the Troubleshooting page.