Skip to content
06. Operators

06. Operators

An operator tests one value of the variable list (02-grammar.md#operator). The index lists every operator name known to any surveyed engine with its status (00-conventions.md); aliases are listed under their canonical name (ADR-0004).

Unit-tier cases for operators live in tests/unit/operators/, imported from the SecRules Test Set by tools/import_sts.py (tests/README.md).

Index

OperatorStatusv2v3CorazaPurpose
beginsWithCoreyesyesyesValue starts with the parameter
containsCoreyesyesyesValue contains the parameter
containsWordExtendedyesyes-Value contains the parameter as a whole word
detectSQLiCoreyesyesyeslibinjection SQL injection detector
detectXSSCoreyesyesyeslibinjection XSS detector
endsWithCoreyesyesyesValue ends with the parameter
eqCoreyesyesyesNumeric equality
fuzzyHashExtendedyesyes-ssdeep fuzzy hash comparison
geCoreyesyesyesNumeric greater-or-equal
geoLookupExtendedyesyesyesGeoIP lookup into GEO
gsbLookupDeprecatedyesyes-Google Safe Browsing lookup
gtCoreyesyesyesNumeric greater-than
inspectFileExtendedyesyesyesRun an external program or Lua script on an uploaded file
ipMatchCoreyesyesyesIP address or CIDR list match
ipMatchFalias of ipMatchFromFileyesyesyesAlias of ipMatchFromFile
ipMatchFromDatasetEngine-specific--yesCoraza: ipMatch against a SecDataset
ipMatchFromFileExtendedyesyesyesipMatch against addresses in a file
leCoreyesyesyesNumeric less-or-equal
ltCoreyesyesyesNumeric less-than
noMatchExtendedyesyesyesNever matches
pmCoreyesyesyesCase-insensitive multi-phrase match
pmfalias of pmFromFileyesyesyesAlias of pmFromFile
pmFromDatasetEngine-specific--yesCoraza: pm against a SecDataset
pmFromFileCoreyesyesyespm with phrases from a file
rblExtendedyesyesyesDNS blocklist lookup
restpathEngine-specific--yesCoraza: match a REST path template and extract ARGS_PATH
rsubDeprecatedyesyes-Regex substitution in STREAM_* variables
rxCoreyesyesyesRegular expression match
rxGlobalEngine-specific-yes-v3: rx matching every occurrence
streqCoreyesyesyesString equality
strmatchExtendedyesyesyesFixed-string match
unconditionalMatchCoreyesyesyesAlways matches
validateByteRangeCoreyesyesyesAny byte outside the permitted ranges
validateDTDExtendedyesyes-Validate XML against a DTD
validateHashExtendedyesyes-Hash engine link check
validateNidEngine-specific--yesCoraza: validate a national id number
validateSchemaExtendedyesyesyesValidate XML against an XML Schema
validateUrlEncodingCoreyesyesyesInvalid URL encoding present
validateUtf8EncodingCoreyesyesyesInvalid UTF-8 present
verifyCCExtendedyesyes-Credit card number check (Luhn)
verifyCPFExtendedyesyes-Brazilian CPF check
verifySSNExtendedyesyes-US SSN check
verifySVNREngine-specific-yes-v3: Austrian SVNR check
withinCoreyesyesyesValue is a substring of the parameter

Regular expressions

@rx PCRE extensions

Status: Extended

Semantics. Regular-expression syntax beyond the Core subset defined under rx (lookahead and lookbehind, backreferences, possessive quantifiers, \K, recursion, Unicode properties beyond \p{L}-style classes that RE2 lacks). Available on ModSecurity v2 (PCRE) and libmodsecurity v3 (PCRE2); not on Coraza (Go regexp). Rules that need them are not portable. See ADR-0018.

Tests. tests/unit/operators/rx-pcre-extra.json

Core operators

beginsWith

Status: Core

Syntax. @beginsWith STRING (macros expand).

Semantics. Matches when the value starts with STRING, byte-wise and case-sensitively. An empty STRING matches every value.

Divergence notes. None known.

Tests. tests/unit/operators/beginsWith.json

contains

Status: Core

Syntax. @contains STRING (macros expand).

Semantics. Matches when STRING occurs anywhere in the value, byte-wise and case-sensitively. An empty STRING matches every value.

Divergence notes. Coraza 3.8.1 rejects an operator with an empty parameter at load time (“empty data”); this affects @beginsWith, @contains, @endsWith, @streq and @within alike (compat/known-gaps.md). ModSecurity accepts it, as the corpus cases inherited from its unit tests show.

Tests. tests/unit/operators/contains.json

detectSQLi

Status: Core

Syntax. @detectSQLi

Semantics. Runs the libinjection SQL injection detector on the value; matches when it reports an injection fingerprint. With capture, TX:0 receives the fingerprint. The detector version is not pinned: the engine tests use inputs every libinjection release classifies the same way.

Divergence notes. ModSecurity v2 and v3 embed the C library; Coraza uses its Go port (internal/operators/detect_sqli.go). Fingerprint tables may lag between ports.

Tests. tests/unit/operators/detectSQLi.json, tests/engine/operators/detect-sqli-xss.yaml

detectXSS

Status: Core

Syntax. @detectXSS

Semantics. Runs the libinjection XSS detector on the value; matches when it reports cross-site scripting.

Divergence notes. As detectSQLi.

Tests. tests/unit/operators/detectXSS.json, tests/engine/operators/detect-sqli-xss.yaml

endsWith

Status: Core

Syntax. @endsWith STRING (macros expand).

Semantics. Matches when the value ends with STRING, byte-wise and case-sensitively.

Divergence notes. None known.

Tests. tests/unit/operators/endsWith.json

eq

Status: Core

Syntax. @eq NUMBER (macros expand).

Semantics. Converts the parameter and the value to integers and matches when they are equal. A string that is not an integer, including the empty string, converts to 0 (ModSecurity v2 atoi, libmodsecurity v3 std::stoi with a catch, Coraza strconv.Atoi ignoring the error). Leading digits followed by text convert to the digits in v2 and v3 and to 0 in Coraza: not specified, and no test uses such a value.

Divergence notes. The partial-number case above.

Tests. tests/unit/operators/eq.json, tests/engine/operators/numeric.yaml

ge

Status: Core

Syntax. @ge NUMBER

Semantics. Numeric greater-than-or-equal, conversions as eq.

Divergence notes. As eq.

Tests. tests/unit/operators/ge.json, tests/engine/operators/numeric.yaml

gt

Status: Core

Syntax. @gt NUMBER

Semantics. Numeric greater-than, conversions as eq.

Divergence notes. As eq.

Tests. tests/unit/operators/gt.json, tests/engine/operators/numeric.yaml

ipMatch

Status: Core

Syntax. @ipMatch LIST where LIST is comma-separated IPv4 or IPv6 addresses, each optionally with a /prefix CIDR suffix.

Semantics. Matches when the value, parsed as an IP address, equals one of the addresses or falls inside one of the networks. A value that is not an address does not match. Mixed IPv4 and IPv6 entries are allowed in one list. Every entry of LIST MUST be a valid address or prefix; an entry that cannot be parsed (for example 10.0.0.0/100) is a configuration error (ADR-0024).

Divergence notes. One corpus case differs: the IPv4-mapped address ::ffff:ffff:ffff against 0:0::/80 matches in ModSecurity and not in Coraza (compat/known-gaps.md). The imported case keeps the ModSecurity expectation. Coraza 3.8.1 skips an unparsable entry (internal/operators/ip_match.go, continue on a ParseCIDR error) where ModSecurity v2 (apache2/re_operators.c msre_op_ipmatch_param_init) and v3 reject the rule; the corpus case asserting a non-match for 10.0.0.0/100 is excluded on import (ADR-0024).

Tests. tests/unit/operators/ipMatch.json, tests/engine/operators/ipmatch.yaml, tests/engine/operators/ipmatch-invalid-entry.yaml

le

Status: Core

Syntax. @le NUMBER

Semantics. Numeric less-than-or-equal, conversions as eq.

Divergence notes. As eq.

Tests. tests/unit/operators/le.json, tests/engine/operators/numeric.yaml

lt

Status: Core

Syntax. @lt NUMBER

Semantics. Numeric less-than, conversions as eq. CRS uses it 190 times to compare anomaly scores against thresholds.

Divergence notes. As eq.

Tests. tests/unit/operators/lt.json, tests/engine/operators/numeric.yaml

pm

Status: Core

Syntax. @pm PHRASE [PHRASE ...] (space-separated).

Semantics. Matches when any phrase occurs in the value. Matching is case-insensitive. A phrase may contain bytes written as |hex|, e.g. |0a|. Engines implement it with an Aho-Corasick automaton, so the number of phrases does not affect rule cost.

Divergence notes. With capture, ModSecurity v2 and libmodsecurity v3 store the matched phrase in TX:0; Coraza does not. Capturing with @pm is therefore not portable.

Tests. tests/unit/operators/pm.json, tests/unit/operators/pm-extra.json

pmFromFile

Status: Core

Syntax. @pmFromFile PATH [PATH ...]; alias @pmf.

Semantics. As pm, with phrases read one per line from each file; empty lines and lines starting with # are ignored; a relative PATH resolves against the directory of the configuration file containing the rule. A missing file MUST be a configuration error.

Divergence notes. None known. Aliases: ADR-0004.

Tests. tests/engine/operators/pmfromfile.yaml

rx

Status: Core

Syntax. @rx REGEX, or simply REGEX (implicit operator, 02-grammar.md#operator).

Semantics. Matches when REGEX matches anywhere in the value (unanchored). The Core syntax is the subset shared by PCRE, PCRE2 and RE2: literals, ., character classes and \d \w \s with negations, \b, byte escapes \xhh and \x{hh}, the anchors ^ $ \A \z (not \Z, which RE2 lacks), alternation, grouping with (...) and (?:...), greedy and lazy quantifiers * + ? {m,n}, inline flags (?i), (?s), (?m) (not (?x), which RE2 rejects). Named groups MUST use (?P<name>...), which all three accept. Anything in #rx-pcre-extensions is not Core (ADR-0018). The pattern is matched in dot-all mode: . matches every byte including the newline. Whether ^ and $ match at inner line boundaries without (?m) is not specified (ADR-0027): rules that need line anchors MUST write (?m). With capture, TX:0 receives the whole match and TX:1…TX:9 the groups; unit cases carry them as re_groups.

Divergence notes. ModSecurity v2 uses PCRE, libmodsecurity v3 PCRE2, Coraza Go regexp (RE2). OWASP CRS v4 is written within the Core subset and checked by its toolchain. Match limits (SecPcreMatchLimit) apply to PCRE engines only. Compile options differ: v2 PCRE_DOTALL | PCRE_DOLLAR_ENDONLY (apache2/re_operators.c), v3.0.16 PCRE2_DOTALL | PCRE2_MULTILINE (src/utils/regex.cc), Coraza v3.8.1 (?sm) by default (internal/operators/rx.go); see ADR-0027.

Tests. tests/unit/operators/rx.json, tests/engine/operators/rx-capture.yaml, tests/engine/operators/rx-dotall.yaml

streq

Status: Core

Syntax. @streq STRING (macros expand).

Semantics. Matches when the value equals STRING byte for byte.

Divergence notes. None known.

Tests. tests/unit/operators/streq.json

unconditionalMatch

Status: Core

Syntax. @unconditionalMatch

Semantics. Always matches, once per selected value. A rule whose variable list selects no values does not match, whatever the operator (all three engines skip evaluation: v2 apache2/re.c iterates zero targets and returns RULE_NO_MATCH when nothing matched, v3 rule_with_operator.cc, Coraza rule.go len(matchedValues) == 0); use SecAction for an unconditional rule.

Divergence notes. The operator takes no parameter. libmodsecurity v3’s scanner recognises it only bare: @unconditionalMatch X is lexed as an @rx pattern (seclang-scanner.ll). The corpus case carrying a stray parameter is excluded on import.

Tests. tests/unit/operators/unconditionalMatch.json

validateByteRange

Status: Core

Syntax. @validateByteRange RANGES, comma-separated single bytes or LOW-HIGH ranges in decimal, e.g. 9,10,13,32-126.

Semantics. Matches when any byte of the value is outside every listed range. An empty parameter, or one containing anything but decimal numbers, - and ,, MUST be a configuration error.

Divergence notes. ModSecurity v2 rejects an empty parameter but parses xxx as the single value 0 (re_operators.c, atoi); libmodsecurity v3 and Coraza reject both. The corpus cases with those parameters are excluded on import (tools/import_sts.py, EXCLUDED_CASES).

Tests. tests/unit/operators/validateByteRange.json

validateUrlEncoding

Status: Core

Syntax. @validateUrlEncoding

Semantics. Matches when the value contains a % not followed by two hexadecimal digits. Intended for values that are still URL-encoded (REQUEST_URI_RAW, raw bodies).

Divergence notes. None known.

Tests. tests/unit/operators/validateUrlEncoding.json

validateUtf8Encoding

Status: Core

Syntax. @validateUtf8Encoding

Semantics. Matches when the value is not well-formed UTF-8: truncated sequences, invalid continuation bytes, overlong encodings, or code points above U+10FFFF.

Divergence notes. None known. (The Phase 1 matrix wrongly listed Coraza as lacking it; internal/operators/validate_utf8_encoding.go registers it.)

Tests. tests/unit/operators/validateUtf8Encoding.json

within

Status: Core

Syntax. @within STRING (macros expand).

Semantics. Matches when the value occurs as a substring of STRING, the reverse of contains. CRS uses it to test a method against an allow-list (REQUEST_METHOD "!@within %{tx.allowed_methods}"). An empty value matches.

Divergence notes. None known.

Tests. tests/unit/operators/within.json

Extended operators

Specified in outline; SHOULD be implemented.

containsWord

Status: Extended

Semantics. Matches when the parameter occurs in the value at a position preceded by the start of the value or a non-word byte and followed by the end of the value or a non-word byte; the empty parameter matches every value. Word bytes are the ASCII letters (libmodsecurity v3 src/operators/contains_word.cc, acceptableChar); ModSecurity v2 (apache2/re_operators.c, msre_op_containsWord_execute) also counts digits and _, so def is a word in 0def for v3 and not for v2. The corpus follows v3.

Implemented by. v2, v3.

fuzzyHash

Status: Extended

Semantics. Compares the value’s ssdeep hash against hashes in a file with a threshold.

Implemented by. v2, v3.

geoLookup

Status: Extended

Semantics. Looks the value up as an IP address in the SecGeoLookupDb database and fills the GEO collection; matches on success.

Implemented by. v2, v3, Coraza.

inspectFile

Status: Extended

Semantics. Runs an external program or Lua script against an uploaded file and matches on its verdict. Filesystem and process access make it Extended.

Implemented by. v2, v3, Coraza.

ipMatchFromFile

Status: Extended

Semantics. As ipMatch, with addresses read one per line from a file; alias @ipMatchF (ADR-0004).

Implemented by. v2, v3, Coraza.

noMatch

Status: Extended

Semantics. Never matches. Used to disable a rule without removing it.

Implemented by. v2, v3, Coraza.

rbl

Status: Extended

Semantics. Looks the value up as an address in a DNS blocklist named by the parameter; SecHttpBlKey enables Project Honey Pot queries.

Implemented by. v2, v3, Coraza.

strmatch

Status: Extended

Semantics. Matches when the parameter occurs anywhere in the value, byte-wise and case-sensitively, like contains; engines implement it with Boyer-Moore-Horspool. contains is preferred.

Implemented by. v2, v3, Coraza.

validateDTD

Status: Extended

Semantics. Validates the XML body against a DTD and matches on failure.

Implemented by. v2, v3.

validateHash

Status: Extended

Semantics. Hash-engine check of a URL; part of the Deprecated hash engine.

Implemented by. v2, v3.

validateSchema

Status: Extended

Semantics. Validates the XML body against an XML Schema file and matches on failure.

Implemented by. v2, v3, Coraza.

verifyCC

Status: Extended

Semantics. The parameter is a regular expression compiled dot-all and multiline (. matches a newline, ^ and $ match at line boundaries). Matches are searched from each offset of the value in turn, anchors relative to the whole value; an empty match is skipped (ModSecurity v2 passes PCRE NOTEMPTY, which backtracks to a non-empty match at the same offset, so a pattern with an empty alternative such as (?:|\d{16}) matches in v2 only; libmodsecurity v3 skips it); the digits of each whole match are checked with the Luhn algorithm, and the first Luhn-valid match matches (ModSecurity v2 apache2/re_operators.c, msre_op_verifyCC_execute and luhn_verify).

Implemented by. v2, v3.

verifyCPF

Status: Extended

Semantics. Searched like verifyCC; a match is valid when its first eleven digits are exactly eleven, are not 00000000000, 11111111111 … 99999999999 or 01234567890, and the last two agree with the CPF check-digit sums (weights 10…2 and 11…2, remainder below 2 giving 0, otherwise 11 minus the remainder) (ModSecurity v2 cpf_verify). libmodsecurity v3.0.16 searches each suffix of the value as a separate subject (src/operators/verify_cpf.cc, VerifyCPF::evaluate), so ^ and look-behind context are relative to the suffix, not the whole value.

Implemented by. v2, v3.

verifySSN

Status: Extended

Semantics. Searched like verifyCC; a match is valid when it holds exactly nine digits that are neither all ascending by one nor all equal, with area (first three), group (next two) and serial (last four) non-zero, area not 666 and below 740 (ModSecurity v2 ssn_verify). libmodsecurity v3.0.16 searches each suffix of the value as a separate subject (src/operators/verify_ssn.cc, VerifySSN::evaluate), so ^ and look-behind context are relative to the suffix, not the whole value.

Implemented by. v2, v3.

Deprecated operators

MUST be accepted by the parser and MAY never match (ADR-0011).

rsub

Status: Deprecated

Semantics. Regular-expression substitution inside STREAM_INPUT_BODY/STREAM_OUTPUT_BODY; requires stream inspection (Deprecated directives).

Implemented by. v2, v3.

gsbLookup

Status: Deprecated

Semantics. Google Safe Browsing lookup; the API it used was retired (ADR-0011).

Implemented by. v2, v3.

Engine-specific operators

Reserved names; not specified (ADR-0010).

ipMatchFromDataset

Status: Engine-specific

Semantics. Coraza: ipMatch against a SecDataset.

Implemented by. Coraza.

pmFromDataset

Status: Engine-specific

Semantics. Coraza: pm against a SecDataset.

Implemented by. Coraza.

restpath

Status: Engine-specific

Semantics. Coraza: matches a REST path template like /users/{id} and stores the placeholders in ARGS_PATH.

Implemented by. Coraza.

rxGlobal

Status: Engine-specific

Semantics. libmodsecurity v3: like rx but matches every occurrence and captures all of them.

Implemented by. v3.

validateNid

Status: Engine-specific

Semantics. Coraza: validates national identification numbers by country code.

Implemented by. Coraza.

verifySVNR

Status: Engine-specific

Semantics. libmodsecurity v3: Austrian social insurance number check.

Implemented by. v3.