From 7c07702633c409d4539a045d7b8faccc4dafb43d Mon Sep 17 00:00:00 2001 From: dextermallo Date: Thu, 22 Jun 2023 11:24:59 +0100 Subject: [PATCH 1/9] chore: update doc for matadata actions --- internal/actions/id.go | 10 ++++++++++ internal/actions/msg.go | 10 ++++++++++ internal/actions/tag.go | 15 +++++++++++++++ internal/actions/ver.go | 13 +++++++++++++ 4 files changed, 48 insertions(+) diff --git a/internal/actions/id.go b/internal/actions/id.go index 77f5ac650..4dcd80266 100644 --- a/internal/actions/id.go +++ b/internal/actions/id.go @@ -11,6 +11,16 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Metadata +// +// Description: +// > This action is mandatory for all `SecRule` and `SecAction`, and it must be numeric. +// Assigns a unique ID to the rule or chain in which it appears. +// +// Example: +// ``` +// SecRule &REQUEST_HEADERS:Host "@eq 0" "log,id:60008,severity:2,msg:'Request Missing a Host Header'" +// ``` type idFn struct{} func (a *idFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/msg.go b/internal/actions/msg.go index a3d524019..a8defb773 100644 --- a/internal/actions/msg.go +++ b/internal/actions/msg.go @@ -10,6 +10,16 @@ import ( utils "github.com/corazawaf/coraza/v3/internal/strings" ) +// Action Group: metadata +// +// Description: +// Assigns a custom message to the rule or chain in which it appears, and the message will be logged along with every alert. +// Noted that the msg information appears in the error and/or audit log files and is not sent back to the client in response headers. +// +// Example: +// ``` +// SecRule &REQUEST_HEADERS:Host "@eq 0" "log,id:60008,severity:2,msg:'Request Missing a Host Header'" +// ``` type msgFn struct{} func (a *msgFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/tag.go b/internal/actions/tag.go index f7f16486d..8f911a030 100644 --- a/internal/actions/tag.go +++ b/internal/actions/tag.go @@ -8,6 +8,21 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Metadata +// +// Description: +// Assigns a tag (category) to a rule or a chain. The tag information appears along with other rule metadata. +// Tags allow easy automated categorization of events, and multiple tags can be specified on the same rule. +// You can use forward slashes to create a hierarchy of categories (see example), and it also support Macro Expansions. +// +// Example: +// ``` +// +// SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "\bgetparentfolder\b" \ +// "phase:2,rev:'2.1.3',capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,block,msg:'Cross-site Scripting (XSS) Attack',id:'958016',tag:'WEB_ATTACK/XSS',tag:'WASCTC/WASC-8',tag:'WASCTC/WASC-22',tag:'OWASP_TOP_10/A2',tag:'OWASP_AppSensor/IE1',tag:'PCI/6.5.1',logdata:'% \ +// {TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.xss_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/XSS-%{matched_var_name}=%{tx.0}" +// +// ``` type tagFn struct{} func (a *tagFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/ver.go b/internal/actions/ver.go index 9f97ab055..3123b7158 100644 --- a/internal/actions/ver.go +++ b/internal/actions/ver.go @@ -8,6 +8,19 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Metadata +// +// Description: +// Specifies the rule set version. +// +// Example: +// ``` +// +// SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "\bgetparentfolder\b" \ +// "phase:2,ver:'CRS/2.2.4,capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,block,msg:'Cross-site Scripting (XSS) Attack',id:'958016',tag:'WEB_ATTACK/XSS',tag:'WASCTC/WASC-8',tag:'WASCTC/WASC-22',tag:'OWASP_TOP_10/A2',tag:'OWASP_AppSensor/IE1',tag:'PCI/6.5.1',logdata:'% \ +// {TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.xss_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/XSS-%{matched_var_name}=%{tx.0}" +// +// ``` type verFn struct{} func (a *verFn) Init(r plugintypes.RuleMetadata, data string) error { From 5e2ebaa31a8715b252a4b78c341cef98c65c92a3 Mon Sep 17 00:00:00 2001 From: dextermallo Date: Thu, 22 Jun 2023 11:26:05 +0100 Subject: [PATCH 2/9] chore: update doc for flow action --- internal/actions/chain.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/internal/actions/chain.go b/internal/actions/chain.go index 3d3adea62..3c254a3d7 100644 --- a/internal/actions/chain.go +++ b/internal/actions/chain.go @@ -8,6 +8,41 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Flow +// +// Description: +// Creating a rule chain - chains the current rule with the rule that immediately follows it. +// +// Noted that rule chains simulate **AND condition**. +// The disruptive actions specified in the first portion of the chained rule will be triggered only if all of the variable checks return positive hits. +// If one of the chained rule is negative, the entire rule chain will fail to match. +// +// These action can be specified only by the chain starter rule: +// - disruptive actions +// - execution phases +// - metadata actions (id, rev, msg, tag, severity, logdata) +// - skip +// - skipAfter +// +// The following directives can be used in rule chains: +// - `SecAction` +// - `SecRule` +// - `SecRuleScript` +// +// Special rules control the usage of actions in a chained rule: +// - An action which affects the rule flow (i.e., the disruptive actions, `skip` and `skipAfter`) can be used only in the chain starter. They will be executed only if the entire chain matches. +// - Non-disruptive rules can be used in any rule; they will be executed if the rule that contains them matches and not only when the entire chain matches. +// - The metadata actions (e.g., `id`, `rev`, `msg`) can be used only in the chain starter. +// +// Example: +// ``` +// # Refuse to accept POST requests that do not contain a Content-Length header. +// # Noted that the rule should be preceded by a rule that verifies only valid request methods are used. +// +// SecRule REQUEST_METHOD "^POST$" "phase:1,chain,t:none,id:105" +// SecRule &REQUEST_HEADERS:Content-Length "@eq 0" "t:none" +// +// ``` type chainFn struct{} func (a *chainFn) Init(r plugintypes.RuleMetadata, data string) error { From 3df3941d94ddc4eb244dcf67d90b344989c96683 Mon Sep 17 00:00:00 2001 From: dextermallo Date: Thu, 22 Jun 2023 11:52:21 +0100 Subject: [PATCH 3/9] chore: update doc for remaining flow/metadata actions; wording --- internal/actions/maturity.go | 14 ++++++++++++++ internal/actions/msg.go | 2 +- internal/actions/skip.go | 16 ++++++++++++++++ internal/actions/skipafter.go | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/internal/actions/maturity.go b/internal/actions/maturity.go index 9eab1f311..6375d3932 100644 --- a/internal/actions/maturity.go +++ b/internal/actions/maturity.go @@ -11,6 +11,20 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Metadata +// +// Description: +// Specifies the relative maturity level of the rule related to the length of time a rule has been public and the amount of testing it has received. +// The value is a string based on a numeric scale (1-9 where 9 is extensively tested and 1 is a brand new experimental rule). +// +// Example: +// ``` +// +// SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "\bgetparentfolder\b" \ +// "phase:2,ver:'CRS/2.2.4,accuracy:'9',maturity:'9',capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,block,msg:'Cross-site Scripting (XSS) Attack',id:'958016',tag:'WEB_ATTACK/XSS',tag:'WASCTC/WASC-8',tag:'WASCTC/WASC-22',tag:'OWASP_TOP_10/A2',tag:'OWASP_AppSensor/IE1',tag:'PCI/6.5.1',logdata:'% \ +// {TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.xss_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/XSS-%{matched_var_name}=%{tx.0}" +// +// ``` type maturityFn struct{} func (a *maturityFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/msg.go b/internal/actions/msg.go index a8defb773..23b0e5fdc 100644 --- a/internal/actions/msg.go +++ b/internal/actions/msg.go @@ -10,7 +10,7 @@ import ( utils "github.com/corazawaf/coraza/v3/internal/strings" ) -// Action Group: metadata +// Action Group: Metadata // // Description: // Assigns a custom message to the rule or chain in which it appears, and the message will be logged along with every alert. diff --git a/internal/actions/skip.go b/internal/actions/skip.go index 3ec5c13f0..ca13f8073 100644 --- a/internal/actions/skip.go +++ b/internal/actions/skip.go @@ -11,6 +11,22 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Flow +// +// Description: +// Skips one or more rules (or chained rules) on successful match. +// It only within the current processing phase and not necessarily in the order in which the rules appear in the configuration file. +// If you place a phase 2 rule after a phase 1 rule that uses skip, it will not skip over the phase 2 rule, +// it will skip over the next phase 1 rule that follows it in the phase. +// +// Example: +// ``` +// # Require Accept header, but not from access from the localhost +// SecRule REMOTE_ADDR "^127\.0\.0\.1$" "phase:1,skip:1,id:141" +// +// # This rule will be skipped over when REMOTE_ADDR is 127.0.0.1 +// SecRule &REQUEST_HEADERS:Accept "@eq 0" "phase:1,id:142,deny,msg:'Request Missing an Accept Header'" +// ``` type skipFn struct { data int } diff --git a/internal/actions/skipafter.go b/internal/actions/skipafter.go index dce2e7fb6..ed904cf79 100644 --- a/internal/actions/skipafter.go +++ b/internal/actions/skipafter.go @@ -9,6 +9,38 @@ import ( utils "github.com/corazawaf/coraza/v3/internal/strings" ) +// Action Group: Flow +// +// Description: +// Action `skipAfter` is similar to `skip`, it skip one or more rules (or chained rules) on a successful match, +// **and resuming rule execution with the first rule that follows the rule (or marker created by SecMarker) with the provided ID)). +// The `skipAfter` action works only within the current processing phase and not necessarily the order in which the rules appear in the configuration file. +// +// Example: +// ``` +// # The following rules implement the same logic as the skip example, but using skipAfter: +// # Require Accept header, but not from access from the localhost +// SecRule REMOTE_ADDR "^127\.0\.0\.1$" "phase:1,id:143,skipAfter:IGNORE_LOCALHOST" +// +// # This rule will be skipped over when REMOTE_ADDR is 127.0.0.1 +// SecRule &REQUEST_HEADERS:Accept "@eq 0" "phase:1,deny,id:144,msg:'Request Missing an Accept Header'" +// SecMarker IGNORE_LOCALHOST +// +// # another Example from the OWASP CRS +// SecMarker BEGIN_HOST_CHECK +// +// SecRule &REQUEST_HEADERS:Host "@eq 0" \ +// "skipAfter:END_HOST_CHECK,phase:2,rev:'2.1.3',t:none,block,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER_HOST',tag:'WASCTC/WASC-21', \ +// tag:'OWASP_TOP_10/A7',tag:'PCI/6.5.10',severity:'5',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.notice_anomaly_score}, \ +// setvar:tx.protocol_violation_score=+%{tx.notice_anomaly_score},setvar:tx.%{rule.id}-PROTOCOL_VIOLATION/MISSING_HEADER-%{matched_var_name}=%{matched_var}" +// +// SecRule REQUEST_HEADERS:Host "^$" \ +// "phase:2,rev:'2.1.3',t:none,block,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER_HOST',tag:'WASCTC/WASC-21',tag:'OWASP_TOP_10/A7', \ +// tag:'PCI/6.5.10',severity:'5',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.notice_anomaly_score},setvar:tx.protocol_violation_score=+%{tx.notice_anomaly_score}, \ +// setvar:tx.%{rule.id}-PROTOCOL_VIOLATION/MISSING_HEADER-%{matched_var_name}=%{matched_var}" +// +// SecMarker END_HOST_CHECK +// ``` type skipafterFn struct { data string } From 1c01d688884860ea8cee1d3b177f38547999755c Mon Sep 17 00:00:00 2001 From: dextermallo Date: Thu, 22 Jun 2023 12:04:08 +0100 Subject: [PATCH 4/9] chore: update doc for phase/severity/status actions --- internal/actions/phase.go | 23 +++++++++++++++++++++++ internal/actions/severity.go | 25 +++++++++++++++++++++++++ internal/actions/status.go | 10 ++++++++++ 3 files changed, 58 insertions(+) diff --git a/internal/actions/phase.go b/internal/actions/phase.go index 48238bc3f..e5b0b53d5 100644 --- a/internal/actions/phase.go +++ b/internal/actions/phase.go @@ -9,6 +9,29 @@ import ( "github.com/corazawaf/coraza/v3/types" ) +// Action Group: Metadata +// +// Description: +// Places the rule or chain into one of five available processing phases. +// It can also be used in `SecDefaultAction` to establish the rule defaults. +// +// Besides, There are aliases for some phase numbers: +// - 2 (request) +// - 4 (response) +// - 5 (logging) +// +// > Warning: Keep in mind that the variable used in the rule may not be available if specifying the incorrect phase. +// > This could lead to a false negative situation where your variable and operator may be correct, +// > but it misses malicious data because you specified the wrong phase. +// +// Example: +// ``` +// # Initialize IP address tracking in phase 1 +// SecAction phase:1,nolog,pass,id:126,initcol:IP=%{REMOTE_ADDR} +// +// # Example of using phase alias +// SecRule REQUEST_HEADERS:User-Agent "Test" "phase:request,log,deny,id:127" +// ``` type phaseFn struct{} func (a *phaseFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/severity.go b/internal/actions/severity.go index 718b44781..6dd404cd7 100644 --- a/internal/actions/severity.go +++ b/internal/actions/severity.go @@ -9,6 +9,31 @@ import ( "github.com/corazawaf/coraza/v3/types" ) +// Action Group: Metadata +// +// Description: +// Assigns severity to the rule in which it is used. +// Severity values in Coraza follows the numeric scale of syslog (where 0 is the most severe). +// +// The data below is used by the OWASP Core Rule Set (CRS): +// - **0, EMERGENCY**: is generated from correlation of anomaly scoring data where there is an inbound attack and an outbound leakage. +// - **1, ALERT**: is generated from correlation where there is an inbound attack and an outbound application level error. +// - **2, CRITICAL**: Anomaly Score of 5. Is the highest severity level possible without correlation. It is normally generated by the web attack rules (40 level files). +// - **3, ERROR**: Error - Anomaly Score of 4. Is generated mostly from outbound leakage rules (50 level files). +// - **4, WARNING**: Anomaly Score of 3. Is generated by malicious client rules (35 level files). +// - **5, NOTICE**: Anomaly Score of 2. Is generated by the Protocol policy and anomaly files. +// - **6, INFO** +// - **7, DEBUG** +// +// > It is possible to specify severity levels using either the numerical values or the text values, +// > but you should always specify severity levels using the text values, +// > because it is difficult to remember what a number stands for. +// > The use of the numerical values is deprecated as of version 2.5.0 and may be removed in one of the subsequent major updates. +// +// Example: +// ``` +// SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:CRITICAL,msg:'Restricted HTTP function'" +// ``` type severityFn struct{} func (a *severityFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/status.go b/internal/actions/status.go index 6d10f559a..344100bdc 100644 --- a/internal/actions/status.go +++ b/internal/actions/status.go @@ -11,6 +11,16 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Data +// +// Description: +// Specifies the response status code to use with actions deny and redirect. +// +// Example: +// ``` +// # Deny status 403 +// SecDefaultAction "phase:1,log,deny,id:145,status:403" +// ``` type statusFn struct{} func (a *statusFn) Init(r plugintypes.RuleMetadata, data string) error { From 0c421059aa0a55e5ae296e67cb0c9121929b1cb1 Mon Sep 17 00:00:00 2001 From: dextermallo Date: Thu, 22 Jun 2023 12:16:02 +0100 Subject: [PATCH 5/9] chore: update doc for exec/expirevar/initcol/log/logdata actions --- internal/actions/exec.go | 22 ++++++++++++++++++++++ internal/actions/expirevar.go | 16 ++++++++++++++++ internal/actions/initcol.go | 14 +++++++++++++- internal/actions/log.go | 10 ++++++++++ internal/actions/logdata.go | 12 ++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) diff --git a/internal/actions/exec.go b/internal/actions/exec.go index 9464bff29..487b36b74 100644 --- a/internal/actions/exec.go +++ b/internal/actions/exec.go @@ -7,6 +7,28 @@ import ( "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" ) +// Action Group: Non-disruptive +// +// Description: +// Executes an external script/binary supplied as parameter. +// The `exec` action is executed independently from any disruptive actions specified. +// External scripts will always be called with no parameters. +// Some transaction information will be placed in environment variables. +// All the usual CGI environment variables will be there. +// You should be aware that forking a threaded process results in all threads being replicated in the new process. +// Forking can therefore incur larger overhead in a multithreaded deployment. +// +// > The script you execute must write something (anything) to stdout, +// > if it doesn’t, Coraza will assume that the script failed, and will record the failure. +// +// Example: +// ``` +// # Run external program on rule match +// SecRule REQUEST_URI "^/cgi-bin/script\.pl" "phase:2,id:112,t:none,t:lowercase,t:normalizePath,block,\ exec:/usr/local/apache/bin/test.sh" + +// # Run Lua script on rule match +// SecRule ARGS:p attack "phase:2,id:113,block,exec:/usr/local/apache/conf/exec.lua" +// ``` type execFn struct{} func (a *execFn) Init(_ plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/expirevar.go b/internal/actions/expirevar.go index a05d76623..e09462f62 100644 --- a/internal/actions/expirevar.go +++ b/internal/actions/expirevar.go @@ -7,6 +7,22 @@ import ( "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" ) +// Action Group: Non-disruptive +// +// Description: +// Configures a collection variable to expire after the given time period (in seconds). +// You should use the `expirevar` with `setvar` action to keep the intended expiration time. +// The expire time will be reset if they are used on their own (perhaps in a SecAction directive). +// +// Example: +// ``` +// +// SecRule REQUEST_COOKIES:JSESSIONID "!^$" "nolog,phase:1,id:114,pass,setsid:%{REQUEST_COOKIES:JSESSIONID}" +// +// SecRule REQUEST_URI "^/cgi-bin/script\.pl" "phase:2,id:115,t:none,t:lowercase,t:normalizePath,log,allow,\ +// setvar:session.suspicious=1,expirevar:session.suspicious=3600,phase:1" +// +// ``` type expirevarFn struct{} func (a *expirevarFn) Init(_ plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/initcol.go b/internal/actions/initcol.go index 12163f2d2..4899d7213 100644 --- a/internal/actions/initcol.go +++ b/internal/actions/initcol.go @@ -9,7 +9,19 @@ import ( "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" ) -// Initializes a persistent collection and add the data to the standard collections coraza. +// Action Group: Non-disruptive +// +// Description: +// Initializes a named persistent collection, either by loading data from storage or by creating a new collection in memory. +// Collections are loaded into memory on-demand, when the initcol action is executed. +// A collection will be persisted only if a change was made to it in the course of transaction processing. +// See the `Persistent Storage` section for further details. +// +// Example: +// ``` +// # Initiates IP address tracking, which is best done in phase 1 +// SecAction "phase:1,id:116,nolog,pass,initcol:ip=%{REMOTE_ADDR}" +// ``` type initcolFn struct { collection string variable byte diff --git a/internal/actions/log.go b/internal/actions/log.go index 6b41edd40..ba3297a11 100644 --- a/internal/actions/log.go +++ b/internal/actions/log.go @@ -8,6 +8,16 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Non-disruptive +// +// Description: +// Indicates that a successful match of the rule needs to be logged. +// +// Example: +// ``` +// # log matches from the error log file to the Coraza audit log. +// SecAction "phase:1,id:117,pass,initcol:ip=%{REMOTE_ADDR},log" +// ``` type logFn struct{} func (a *logFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/logdata.go b/internal/actions/logdata.go index 1928d9bf2..03a602d74 100644 --- a/internal/actions/logdata.go +++ b/internal/actions/logdata.go @@ -9,6 +9,18 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Non-disruptive +// +// Description: +// Logs a data fragment as part of the alert message. +// The logdata information appears in the error and/or audit log files. Macro expansion is performed, +// so you may use variable names such as %{TX.0} or %{MATCHED_VAR}. +// The information is properly escaped for use with logging of binary data. +// +// Example: +// ``` +// SecRule ARGS:p "@rx