From 18ae0d83281731f4447b2e9b1b824c52b4ffb274 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 18:21:23 -0300 Subject: [PATCH 01/18] chore(deps): update actions/setup-go digest to 41dfa10 (#1179) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/fuzz.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/regression.yml | 2 +- .github/workflows/tinygo.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 546d0dd6b..4c266b491 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5 + - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 with: go-version: ">=1.22.0" - run: go run mage.go fuzz diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 52142b694..290e4ff29 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - name: Install Go - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5 + uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 with: go-version: v1.22.x cache: true diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 6f06c35d1..f81e95ab9 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -23,7 +23,7 @@ jobs: - name: Checkout code uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - name: Install Go - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5 + uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 with: go-version: ${{ matrix.go-version }} cache: true diff --git a/.github/workflows/tinygo.yml b/.github/workflows/tinygo.yml index 0782eed2e..c01a2f766 100644 --- a/.github/workflows/tinygo.yml +++ b/.github/workflows/tinygo.yml @@ -27,7 +27,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - name: Install Go - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5 + uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 with: go-version: ${{ matrix.go-version }} cache: true From 0b0ada471a2987ea45c8b3bc1dde621f984afa0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= <3012076+fzipi@users.noreply.github.com> Date: Mon, 28 Oct 2024 10:16:04 -0300 Subject: [PATCH 02/18] fix: apply mage format (#1181) Signed-off-by: Felipe Zipitria --- examples/http-server/go.mod | 2 +- examples/http-server/go.sum | 3 +-- go.sum | 2 -- internal/auditlog/formats_ocsf_test.go | 5 +++-- testing/coreruleset/coreruleset_test.go | 2 +- testing/coreruleset/go.mod | 2 +- testing/coreruleset/go.sum | 3 +-- 7 files changed, 8 insertions(+), 11 deletions(-) diff --git a/examples/http-server/go.mod b/examples/http-server/go.mod index b3698b8ce..b3f8819c6 100644 --- a/examples/http-server/go.mod +++ b/examples/http-server/go.mod @@ -5,7 +5,7 @@ go 1.22 require github.com/corazawaf/coraza/v3 v3.2.1 require ( - github.com/corazawaf/libinjection-go v0.2.1 // indirect + github.com/corazawaf/libinjection-go v0.2.2 // indirect github.com/magefile/mage v1.15.0 // indirect github.com/petar-dambovaliev/aho-corasick v0.0.0-20240411101913-e07a1f0e8eb4 // indirect github.com/tidwall/gjson v1.18.0 // indirect diff --git a/examples/http-server/go.sum b/examples/http-server/go.sum index 9afa47292..4457419b9 100644 --- a/examples/http-server/go.sum +++ b/examples/http-server/go.sum @@ -1,7 +1,6 @@ github.com/corazawaf/coraza/v3 v3.2.1 h1:zBIji4ut9FtFe8lXdqFwXMAkUoDJZ7HsOlEUYWERLI8= github.com/corazawaf/coraza/v3 v3.2.1/go.mod h1:fVndCGdUHJWl9c26VZPcORQRzUYwMPnRkC6TyTkhbUg= -github.com/corazawaf/libinjection-go v0.2.1 h1:vNJ7L6c4xkhRgYU6sIO0Tl54TmeCQv/yfxBma30Dy/Y= -github.com/corazawaf/libinjection-go v0.2.1/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw= +github.com/corazawaf/libinjection-go v0.2.2 h1:Chzodvb6+NXh6wew5/yhD0Ggioif9ACrQGR4qjTCs1g= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= diff --git a/go.sum b/go.sum index 997e6a3fc..29d49f709 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ github.com/anuraaga/go-modsecurity v0.0.0-20220824035035-b9a4099778df h1:YWiVl53v0R8Knj/k+4slO0SXPL67Y4dXWiOIWNzrkew= github.com/anuraaga/go-modsecurity v0.0.0-20220824035035-b9a4099778df/go.mod h1:7jguE759ADzy2EkxGRXigiC0ER1Yq2IFk2qNtwgzc7U= -github.com/corazawaf/libinjection-go v0.2.1 h1:vNJ7L6c4xkhRgYU6sIO0Tl54TmeCQv/yfxBma30Dy/Y= -github.com/corazawaf/libinjection-go v0.2.1/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw= github.com/corazawaf/libinjection-go v0.2.2 h1:Chzodvb6+NXh6wew5/yhD0Ggioif9ACrQGR4qjTCs1g= github.com/corazawaf/libinjection-go v0.2.2/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= diff --git a/internal/auditlog/formats_ocsf_test.go b/internal/auditlog/formats_ocsf_test.go index 2f0409918..930357fc6 100644 --- a/internal/auditlog/formats_ocsf_test.go +++ b/internal/auditlog/formats_ocsf_test.go @@ -9,12 +9,13 @@ import ( "strings" "testing" + "github.com/valllabh/ocsf-schema-golang/ocsf/v1_2_0/events/application" + "github.com/valllabh/ocsf-schema-golang/ocsf/v1_2_0/events/application/enums" + "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" "github.com/corazawaf/coraza/v3/internal/collections" "github.com/corazawaf/coraza/v3/types" "github.com/corazawaf/coraza/v3/types/variables" - "github.com/valllabh/ocsf-schema-golang/ocsf/v1_2_0/events/application" - "github.com/valllabh/ocsf-schema-golang/ocsf/v1_2_0/events/application/enums" ) func TestOCSFFormatter(t *testing.T) { diff --git a/testing/coreruleset/coreruleset_test.go b/testing/coreruleset/coreruleset_test.go index 6fe6ead86..9955ff8b8 100644 --- a/testing/coreruleset/coreruleset_test.go +++ b/testing/coreruleset/coreruleset_test.go @@ -23,6 +23,7 @@ import ( "time" "github.com/bmatcuk/doublestar/v4" + albedo "github.com/coreruleset/albedo/server" "github.com/coreruleset/go-ftw/config" "github.com/coreruleset/go-ftw/output" "github.com/coreruleset/go-ftw/runner" @@ -34,7 +35,6 @@ import ( "github.com/corazawaf/coraza/v3" txhttp "github.com/corazawaf/coraza/v3/http" "github.com/corazawaf/coraza/v3/types" - albedo "github.com/coreruleset/albedo/server" ) func BenchmarkCRSCompilation(b *testing.B) { diff --git a/testing/coreruleset/go.mod b/testing/coreruleset/go.mod index e35277884..1a3cf3e26 100644 --- a/testing/coreruleset/go.mod +++ b/testing/coreruleset/go.mod @@ -15,7 +15,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect - github.com/corazawaf/libinjection-go v0.2.1 // indirect + github.com/corazawaf/libinjection-go v0.2.2 // indirect github.com/coreruleset/ftw-tests-schema/v2 v2.1.0 // indirect github.com/fatih/color v1.17.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect diff --git a/testing/coreruleset/go.sum b/testing/coreruleset/go.sum index 39b19a5c1..4a81d6329 100644 --- a/testing/coreruleset/go.sum +++ b/testing/coreruleset/go.sum @@ -8,8 +8,7 @@ github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwN github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/corazawaf/coraza-coreruleset/v4 v4.6.0 h1:VGlMw3QMuKaV7XgifPgcqCm66K+HRSdM4d9PRh1nD50= github.com/corazawaf/coraza-coreruleset/v4 v4.6.0/go.mod h1:1FQt1p+JSQ6tYrafMqZrEEdDmhq6aVuIJdnk+bM9hMY= -github.com/corazawaf/libinjection-go v0.2.1 h1:vNJ7L6c4xkhRgYU6sIO0Tl54TmeCQv/yfxBma30Dy/Y= -github.com/corazawaf/libinjection-go v0.2.1/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw= +github.com/corazawaf/libinjection-go v0.2.2 h1:Chzodvb6+NXh6wew5/yhD0Ggioif9ACrQGR4qjTCs1g= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreruleset/albedo v0.0.16 h1:YYWEJBSfAwVmZ5tbBgQQhL8uU6IKeA2QwpAkii3UPKY= github.com/coreruleset/albedo v0.0.16/go.mod h1:6mYBASfvvRM2ckXgYO7N5nyKAj8OqLnT4+YLbM0/XWE= From abf359e0c4db8b79a8712fac51c1a6db549765ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= <3012076+fzipi@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:30:17 -0300 Subject: [PATCH 03/18] fix: handle additional broken macro definitions (#1180) Signed-off-by: Felipe Zipitria --- experimental/plugins/macro/macro.go | 29 +++++++++++--------- experimental/plugins/macro/macro_test.go | 34 +++++++++++++++++++++++- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/experimental/plugins/macro/macro.go b/experimental/plugins/macro/macro.go index ec756f0d8..fabd039d3 100644 --- a/experimental/plugins/macro/macro.go +++ b/experimental/plugins/macro/macro.go @@ -99,38 +99,38 @@ func (m *macro) compile(input string) error { return fmt.Errorf("empty macro") } - currentToken := strings.Builder{} m.original = input + var currentToken strings.Builder isMacro := false + for i := 0; i < l; i++ { c := input[i] - if c == '%' && (i <= l && input[i+1] == '{') { - // we have a macro + + if c == '%' && i+1 < l && input[i+1] == '{' { if currentToken.Len() > 0 { - // we add the text token m.tokens = append(m.tokens, macroToken{ text: currentToken.String(), variable: variables.Unknown, key: "", }) + currentToken.Reset() } - currentToken.Reset() isMacro = true - i++ + i++ // Skip '{' continue } if isMacro { if c == '}' { - // we close a macro isMacro = false - // TODO(jcchavezs): key should only be empty in single collections + if input[i-1] == '.' { + return fmt.Errorf("empty variable name") + } varName, key, _ := strings.Cut(currentToken.String(), ".") v, err := variables.Parse(varName) if err != nil { return fmt.Errorf("unknown variable %q", varName) } - // we add the variable token m.tokens = append(m.tokens, macroToken{ text: currentToken.String(), variable: v, @@ -140,8 +140,7 @@ func (m *macro) compile(input string) error { continue } - if !(c == '.' || c == '_' || c == '-' || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { - currentToken.WriteByte(c) + if !isValidMacroChar(c) { return fmt.Errorf("malformed variable starting with %q", "%{"+currentToken.String()) } @@ -152,10 +151,10 @@ func (m *macro) compile(input string) error { } continue } - // we have a normal character + currentToken.WriteByte(c) } - // if there is something left + if currentToken.Len() > 0 { m.tokens = append(m.tokens, macroToken{ text: currentToken.String(), @@ -166,6 +165,10 @@ func (m *macro) compile(input string) error { return nil } +func isValidMacroChar(c byte) bool { + return c == '.' || c == '_' || c == '-' || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') +} + // String returns the original string func (m *macro) String() string { return m.original diff --git a/experimental/plugins/macro/macro_test.go b/experimental/plugins/macro/macro_test.go index 94e9579bb..7ea662df5 100644 --- a/experimental/plugins/macro/macro_test.go +++ b/experimental/plugins/macro/macro_test.go @@ -36,7 +36,39 @@ func TestCompile(t *testing.T) { } }) - t.Run("malformed macro", func(t *testing.T) { + t.Run("single percent sign", func(t *testing.T) { + m := ¯o{} + err := m.compile("%") + if err != nil { + t.Errorf("single percent sign should not error") + } + }) + + t.Run("empty braces", func(t *testing.T) { + m := ¯o{} + err := m.compile("%{}") + if err == nil { + t.Errorf("expected error for empty braces") + } + }) + + t.Run("missing key", func(t *testing.T) { + m := ¯o{} + err := m.compile("%{tx.}") + if err == nil { + t.Errorf("expected error for missing key") + } + }) + + t.Run("missing collection", func(t *testing.T) { + m := ¯o{} + err := m.compile("%{.key}") + if err == nil { + t.Errorf("expected error for missing collection") + } + }) + + t.Run("malformed macros", func(t *testing.T) { for _, test := range []string{"%{tx.count", "%{{tx.count}", "%{{tx.{count}", "something %{tx.count"} { t.Run(test, func(t *testing.T) { m := ¯o{} From 3719dceb712164276e3164ddc544d6f9d0d7b14e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= <3012076+fzipi@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:39:38 -0300 Subject: [PATCH 04/18] fix: redirect action status codes (#1183) * fix: redirect action status codes Signed-off-by: Felipe Zipitria * docs: reword redirect description Signed-off-by: Felipe Zipitria --------- Signed-off-by: Felipe Zipitria --- internal/actions/redirect.go | 13 ++++-- testing/engine/disruptive_actions.go | 70 ++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/internal/actions/redirect.go b/internal/actions/redirect.go index 5ba2740b8..12cd11dd5 100644 --- a/internal/actions/redirect.go +++ b/internal/actions/redirect.go @@ -12,9 +12,9 @@ import ( // // Description: // Intercepts transaction by issuing an external (client-visible) redirection to the given location. -// If the status action is presented on the same rule, -// and its value can be used for a redirection (i.e., one of the following: 301, 302, 303, or 307), -// the value will be used for the redirection status code. Otherwise, status code 302 will be used. +// If the status action is presented on the same rule, and its value can be used for a redirection +// (supported redirection codes: 301, 302, 303, 307) the value will be used for the redirection status code. +// Otherwise, status code 302 will be used. // // Example: // ``` @@ -34,12 +34,17 @@ func (a *redirectFn) Init(_ plugintypes.RuleMetadata, data string) error { } func (a *redirectFn) Evaluate(r plugintypes.RuleMetadata, tx plugintypes.TransactionState) { + status := 302 // default status code for redirection rid := r.ID() if rid == noID { rid = r.ParentID() } + rstatus := r.Status() + if rstatus == 301 || rstatus == 302 || rstatus == 303 || rstatus == 307 { + status = rstatus + } tx.Interrupt(&types.Interruption{ - Status: r.Status(), + Status: status, RuleID: rid, Action: "redirect", Data: a.target, diff --git a/testing/engine/disruptive_actions.go b/testing/engine/disruptive_actions.go index 1efb066d2..7173aee89 100644 --- a/testing/engine/disruptive_actions.go +++ b/testing/engine/disruptive_actions.go @@ -84,6 +84,71 @@ var _ = profile.RegisterProfile(profile.Profile{ }, }, }, + // Phase 2 + { + Stage: profile.SubStage{ + Input: profile.StageInput{ + URI: "/redirect2", + }, + Output: profile.ExpectedOutput{ + TriggeredRules: []int{21}, + Interruption: &profile.ExpectedInterruption{ + Status: 302, + Data: "https://www.example.com", + RuleID: 21, + Action: "redirect", + }, + }, + }, + }, + { + Stage: profile.SubStage{ + Input: profile.StageInput{ + URI: "/redirect6", + }, + Output: profile.ExpectedOutput{ + TriggeredRules: []int{61}, + Interruption: &profile.ExpectedInterruption{ + Status: 302, + Data: "https://www.example.com", + RuleID: 61, + Action: "redirect", + }, + }, + }, + }, + { + Stage: profile.SubStage{ + Input: profile.StageInput{ + URI: "/redirect7", + }, + Output: profile.ExpectedOutput{ + TriggeredRules: []int{62}, + Interruption: &profile.ExpectedInterruption{ + Status: 307, + Data: "https://www.example.com", + RuleID: 62, + Action: "redirect", + }, + }, + }, + }, + { + Stage: profile.SubStage{ + Input: profile.StageInput{ + URI: "/redirect8", + }, + Output: profile.ExpectedOutput{ + TriggeredRules: []int{63}, + Interruption: &profile.ExpectedInterruption{ + Status: 302, + Data: "https://www.example.com", + RuleID: 63, + Action: "redirect", + }, + }, + }, + }, { Stage: profile.SubStage{ Input: profile.StageInput{ @@ -305,6 +370,11 @@ SecRule REQUEST_URI "/redirect5$" "phase:5,id:51,log,status:302,redirect:https:/ SecRule REQUEST_URI "/deny5$" "phase:5,id:52,log,status:500,deny" SecRule REQUEST_URI "/drop5$" "phase:5,id:53,log,drop" +SecRule REQUEST_URI "/redirect6$" "phase:2,id:61,log,redirect:https://www.example.com" +SecRule REQUEST_URI "/redirect7$" "phase:2,id:62,log,status:307,redirect:https://www.example.com" +SecRule REQUEST_URI "/redirect8$" "phase:2,id:63,log,status:401,redirect:https://www.example.com" + + # Rule 103 is missing the phase, therefore phase:2 is implicitly applied with its related default actions # So we will expect a deny with 501 response for the blocking action. SecDefaultAction "phase:2,deny,status:501,log" From 42e97ac847211df0419b0a4bf9e6126b774f71cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= <3012076+fzipi@users.noreply.github.com> Date: Thu, 31 Oct 2024 03:22:14 -0300 Subject: [PATCH 05/18] feat: add SecRuleUpdateActionById directive (#1071) * feat: add SecRuleUpdateActionById directive Signed-off-by: Felipe Zipitria * fix: testing rule e2e Signed-off-by: Felipe Zipitria * fix: apply code review suggestions Signed-off-by: Felipe Zipitria --------- Signed-off-by: Felipe Zipitria --- internal/seclang/directives.go | 84 ++++++++++++++++++- internal/seclang/directives_test.go | 38 +++++++++ internal/seclang/directivesmap.gen.go | 3 +- .../seclang/generator/directivesmap.go.tmpl | 1 - testing/engine/directives_updateactions.go | 64 ++++++++++++++ 5 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 testing/engine/directives_updateactions.go diff --git a/internal/seclang/directives.go b/internal/seclang/directives.go index 2bf34a3e5..b45220c8c 100644 --- a/internal/seclang/directives.go +++ b/internal/seclang/directives.go @@ -357,7 +357,7 @@ func directiveSecRuleRemoveByID(options *DirectiveOptions) error { options.WAF.Rules.DeleteByID(id) } else { if idx == 0 { - return fmt.Errorf("SecRuleUpdateTargetById: invalid negative id: %s", idOrRange) + return fmt.Errorf("SecRuleRemoveById: invalid negative id: %s", idOrRange) } start, err := strconv.Atoi(idOrRange[:idx]) if err != nil { @@ -1046,6 +1046,88 @@ func updateTargetBySingleID(id int, variables string, options *DirectiveOptions) return rp.ParseVariables(strings.Trim(variables, "\"")) } +// Description: Updates the action list of the specified rule(s). +// Syntax: SecRuleUpdateActionById ID ACTIONLIST +// --- +// This directive will overwrite the action list of the specified rule with the actions provided in the second parameter. +// It has two limitations: it cannot be used to change the ID or phase of a rule. +// Only the actions that can appear only once are overwritten. +// The actions that are allowed to appear multiple times in a list, will be appended to the end of the list. +// The following example demonstrates how `SecAuditEngine` is used: +// ```apache +// SecRuleUpdateActionById 12345 "deny,status:403" +// ``` +func directiveSecRuleUpdateActionByID(options *DirectiveOptions) error { + if len(options.Opts) == 0 { + return errEmptyOptions + } + + idsOrRanges := strings.Fields(options.Opts) + idsOrRangesLen := len(idsOrRanges) + if idsOrRangesLen < 2 { + return errors.New("syntax error: SecRuleUpdateActionById id \"ACTION1,ACTION2,...\"") + } + // The last element is expected to be the actions(s) + actions := idsOrRanges[idsOrRangesLen-1] + for _, idOrRange := range idsOrRanges[:idsOrRangesLen-1] { + if idx := strings.Index(idOrRange, "-"); idx == -1 { + id, err := strconv.Atoi(idOrRange) + if err != nil { + return err + } + return updateActionBySingleID(id, actions, options) + } else { + if idx == 0 { + return fmt.Errorf("SecRuleUpdateActionById: invalid negative id: %s", idOrRange) + } + start, err := strconv.Atoi(idOrRange[:idx]) + if err != nil { + return err + } + + end, err := strconv.Atoi(idOrRange[idx+1:]) + if err != nil { + return err + } + if start == end { + return updateActionBySingleID(start, actions, options) + } + if start > end { + return fmt.Errorf("invalid range: %s", idOrRange) + } + + for _, rule := range options.WAF.Rules.GetRules() { + if rule.ID_ < start && rule.ID_ > end { + continue + } + rp := RuleParser{ + rule: &rule, + options: RuleOptions{}, + defaultActions: map[types.RulePhase][]ruleAction{}, + } + if err := rp.ParseActions(strings.Trim(actions, "\"")); err != nil { + return err + } + } + } + } + return nil +} + +func updateActionBySingleID(id int, actions string, options *DirectiveOptions) error { + + rule := options.WAF.Rules.FindByID(id) + if rule == nil { + return fmt.Errorf("SecRuleUpdateActionById: rule \"%d\" not found", id) + } + rp := RuleParser{ + rule: rule, + options: RuleOptions{}, + defaultActions: map[types.RulePhase][]ruleAction{}, + } + return rp.ParseActions(strings.Trim(actions, "\"")) +} + // Description: Updates the target (variable) list of the specified rule(s) by tag. // Syntax: SecRuleUpdateTargetByTag TAG TARGET1[|TARGET2|TARGET3] // --- diff --git a/internal/seclang/directives_test.go b/internal/seclang/directives_test.go index 1d828f4c4..b5b47b777 100644 --- a/internal/seclang/directives_test.go +++ b/internal/seclang/directives_test.go @@ -36,6 +36,31 @@ func Test_NonImplementedDirective(t *testing.T) { } } +func TestSecRuleUpdateActionByID(t *testing.T) { + waf := corazawaf.NewWAF() + rule, err := ParseRule(RuleOptions{ + Data: "REQUEST_URI \"^/test\" \"id:181,log\"", + WAF: waf, + WithOperator: true, + }) + if err != nil { + t.Error(err) + } + if err := waf.Rules.Add(rule); err != nil { + t.Error(err) + } + if waf.Rules.Count() != 1 { + t.Error("Failed to add rule") + } + if err := directiveSecRuleUpdateActionByID(&DirectiveOptions{ + WAF: waf, + Opts: "181 \"nolog\"", + }); err != nil { + t.Error(err) + } + +} + func TestSecRuleUpdateTargetByID(t *testing.T) { waf := corazawaf.NewWAF() rule, err := ParseRule(RuleOptions{ @@ -179,6 +204,19 @@ func TestDirectives(t *testing.T) { {"1 2", expectNoErrorOnDirective}, {"1 2 3-4", expectNoErrorOnDirective}, }, + "SecRuleUpdateActionById": { + {"", expectErrorOnDirective}, + {"a", expectErrorOnDirective}, + {"1-a", expectErrorOnDirective}, + {"a-2", expectErrorOnDirective}, + {"2-1", expectErrorOnDirective}, + {"1-a \"status:403\"", expectErrorOnDirective}, + {"a-2 \"status:403\"", expectErrorOnDirective}, + {"2-1 \"status:403\"", expectErrorOnDirective}, + {"-1 \"status:403\"", expectErrorOnDirective}, + {"1 2 3-4 \"status:403\"", expectNoErrorOnDirective}, + {"1 2 3-4 \"status:403,nolog\"", expectNoErrorOnDirective}, + }, "SecRuleUpdateTargetById": { {"", expectErrorOnDirective}, {"a", expectErrorOnDirective}, diff --git a/internal/seclang/directivesmap.gen.go b/internal/seclang/directivesmap.gen.go index 9e6fc118d..a0b2102d2 100644 --- a/internal/seclang/directivesmap.gen.go +++ b/internal/seclang/directivesmap.gen.go @@ -60,6 +60,7 @@ var ( _ directive = directiveSecDebugLog _ directive = directiveSecDebugLogLevel _ directive = directiveSecRuleUpdateTargetByID + _ directive = directiveSecRuleUpdateActionByID _ directive = directiveSecRuleUpdateTargetByTag _ directive = directiveSecIgnoreRuleCompilationErrors _ directive = directiveSecDataset @@ -121,6 +122,7 @@ var directivesMap = map[string]directive{ "secdebuglog": directiveSecDebugLog, "secdebugloglevel": directiveSecDebugLogLevel, "secruleupdatetargetbyid": directiveSecRuleUpdateTargetByID, + "secruleupdateactionbyid": directiveSecRuleUpdateActionByID, "secruleupdatetargetbytag": directiveSecRuleUpdateTargetByTag, "secignorerulecompilationerrors": directiveSecIgnoreRuleCompilationErrors, "secdataset": directiveSecDataset, @@ -130,7 +132,6 @@ var directivesMap = map[string]directive{ "secargumentseparator": directiveUnsupported, "seccookieformat": directiveUnsupported, "secruleupdatetargetbymsg": directiveUnsupported, - "secruleupdateactionbyid": directiveUnsupported, "secrulescript": directiveUnsupported, "secruleperftime": directiveUnsupported, "secunicodemap": directiveUnsupported, diff --git a/internal/seclang/generator/directivesmap.go.tmpl b/internal/seclang/generator/directivesmap.go.tmpl index 0e2956acf..069de4ef9 100644 --- a/internal/seclang/generator/directivesmap.go.tmpl +++ b/internal/seclang/generator/directivesmap.go.tmpl @@ -17,7 +17,6 @@ var directivesMap = map[string]directive{ "secargumentseparator": directiveUnsupported, "seccookieformat": directiveUnsupported, "secruleupdatetargetbymsg": directiveUnsupported, - "secruleupdateactionbyid": directiveUnsupported, "secrulescript": directiveUnsupported, "secruleperftime": directiveUnsupported, "secunicodemap": directiveUnsupported, diff --git a/testing/engine/directives_updateactions.go b/testing/engine/directives_updateactions.go new file mode 100644 index 000000000..e5fd692d1 --- /dev/null +++ b/testing/engine/directives_updateactions.go @@ -0,0 +1,64 @@ +// Copyright 2024 Juan Pablo Tosso and the OWASP Coraza contributors +// SPDX-License-Identifier: Apache-2.0 + +package engine + +import ( + "github.com/corazawaf/coraza/v3/testing/profile" +) + +var _ = profile.RegisterProfile(profile.Profile{ + Meta: profile.Meta{ + Author: "fzipi", + Description: "Test SecRuleUpdateActionById directives", + Enabled: true, + Name: "SecRuleUpdateActionById.yaml", + }, + Tests: []profile.Test{ + { + Title: "SecRuleUpdateActionById to pass", + Stages: []profile.Stage{ + // Phase 1 + { + Stage: profile.SubStage{ + Input: profile.StageInput{ + URI: "/phase1?param1=value1", + }, + Output: profile.ExpectedOutput{ + TriggeredRules: []int{ + 1004, + }, + }, + }, + }, + // Phase 2 + { + Stage: profile.SubStage{ + Input: profile.StageInput{ + URI: "/phase2?param2=value2", + }, + Output: profile.ExpectedOutput{ + TriggeredRules: []int{ + 1014, + }, + Interruption: &profile.ExpectedInterruption{ + Status: 302, + Data: "https://www.example.com/", + RuleID: 1014, + Action: "redirect", + }, + }, + }, + }, + }, + }, + }, + Rules: ` + SecRule ARGS "@contains value1" "phase:1,id:1004,deny" + SecRule ARGS "@contains value1" "phase:1,id:1005,log" + SecRuleUpdateActionById 1004 "pass" + + SecRule ARGS "@contains value2" "phase:2,id:1014,block,deny" + SecRuleUpdateActionById 1014 "redirect:'https://www.example.com/',status:302" + `, +}) From 673397cfdbc511dec94f043b9b7249b9fac62837 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 07:37:15 +0100 Subject: [PATCH 06/18] chore(deps): update github/codeql-action digest to 6624720 (#1169) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 80cd227ad..7b34e294e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,12 +13,12 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - name: Initialize CodeQL - uses: github/codeql-action/init@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3 + uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3 with: languages: go - name: Autobuild - uses: github/codeql-action/autobuild@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3 + uses: github/codeql-action/autobuild@662472033e021d55d94146f66f6058822b0b39fd # v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3 + uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3 From 1d71fafdcc2015c5c1f3f71c361ebc6d0ec9ea93 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 08:02:39 +0100 Subject: [PATCH 07/18] fix(deps): update module github.com/bmatcuk/doublestar/v4 to v4.7.1 (#1171) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- testing/coreruleset/go.mod | 2 +- testing/coreruleset/go.sum | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/testing/coreruleset/go.mod b/testing/coreruleset/go.mod index 1a3cf3e26..dda3467bc 100644 --- a/testing/coreruleset/go.mod +++ b/testing/coreruleset/go.mod @@ -3,7 +3,7 @@ module github.com/corazawaf/coraza/v3/testing/coreruleset go 1.22.3 require ( - github.com/bmatcuk/doublestar/v4 v4.6.1 + github.com/bmatcuk/doublestar/v4 v4.7.1 github.com/corazawaf/coraza-coreruleset/v4 v4.6.0 github.com/corazawaf/coraza/v3 v3.0.0-00010101000000-000000000000 github.com/coreruleset/albedo v0.0.16 diff --git a/testing/coreruleset/go.sum b/testing/coreruleset/go.sum index 4a81d6329..7a692094c 100644 --- a/testing/coreruleset/go.sum +++ b/testing/coreruleset/go.sum @@ -6,9 +6,12 @@ github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZC github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= +github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/corazawaf/coraza-coreruleset/v4 v4.6.0 h1:VGlMw3QMuKaV7XgifPgcqCm66K+HRSdM4d9PRh1nD50= github.com/corazawaf/coraza-coreruleset/v4 v4.6.0/go.mod h1:1FQt1p+JSQ6tYrafMqZrEEdDmhq6aVuIJdnk+bM9hMY= github.com/corazawaf/libinjection-go v0.2.2 h1:Chzodvb6+NXh6wew5/yhD0Ggioif9ACrQGR4qjTCs1g= +github.com/corazawaf/libinjection-go v0.2.2/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreruleset/albedo v0.0.16 h1:YYWEJBSfAwVmZ5tbBgQQhL8uU6IKeA2QwpAkii3UPKY= github.com/coreruleset/albedo v0.0.16/go.mod h1:6mYBASfvvRM2ckXgYO7N5nyKAj8OqLnT4+YLbM0/XWE= From 72057915f39c5a6d6b8422b6b52b464c9902962d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 08:03:11 +0100 Subject: [PATCH 08/18] chore(deps): update actions/checkout digest to 11bd719 (#1168) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Felipe Zipitría <3012076+fzipi@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/fuzz.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/regression.yml | 2 +- .github/workflows/tinygo.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7b34e294e..f1ae1f5bc 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Initialize CodeQL uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3 diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 4c266b491..4a3a96088 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -11,7 +11,7 @@ jobs: name: Fuzz tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 with: go-version: ">=1.22.0" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 290e4ff29..ca3870900 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -18,7 +18,7 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Install Go uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 with: diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index f81e95ab9..e70625f5e 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -21,7 +21,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Install Go uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 with: diff --git a/.github/workflows/tinygo.yml b/.github/workflows/tinygo.yml index c01a2f766..720c68916 100644 --- a/.github/workflows/tinygo.yml +++ b/.github/workflows/tinygo.yml @@ -24,7 +24,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Install Go uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 From c27bac81a11f876f4598868a9fcdc1d9a7669994 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Thu, 31 Oct 2024 14:35:30 +0100 Subject: [PATCH 09/18] nits: `SecRuleUpdateActionById` doc (#1185) nits: doc --- examples/http-server/go.sum | 1 + internal/seclang/directives.go | 4 ++-- testing/coreruleset/go.sum | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/http-server/go.sum b/examples/http-server/go.sum index 4457419b9..b60148c40 100644 --- a/examples/http-server/go.sum +++ b/examples/http-server/go.sum @@ -1,6 +1,7 @@ github.com/corazawaf/coraza/v3 v3.2.1 h1:zBIji4ut9FtFe8lXdqFwXMAkUoDJZ7HsOlEUYWERLI8= github.com/corazawaf/coraza/v3 v3.2.1/go.mod h1:fVndCGdUHJWl9c26VZPcORQRzUYwMPnRkC6TyTkhbUg= github.com/corazawaf/libinjection-go v0.2.2 h1:Chzodvb6+NXh6wew5/yhD0Ggioif9ACrQGR4qjTCs1g= +github.com/corazawaf/libinjection-go v0.2.2/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= diff --git a/internal/seclang/directives.go b/internal/seclang/directives.go index b45220c8c..7d02b89d8 100644 --- a/internal/seclang/directives.go +++ b/internal/seclang/directives.go @@ -1053,7 +1053,7 @@ func updateTargetBySingleID(id int, variables string, options *DirectiveOptions) // It has two limitations: it cannot be used to change the ID or phase of a rule. // Only the actions that can appear only once are overwritten. // The actions that are allowed to appear multiple times in a list, will be appended to the end of the list. -// The following example demonstrates how `SecAuditEngine` is used: +// The following example demonstrates how `SecRuleUpdateActionById` is used: // ```apache // SecRuleUpdateActionById 12345 "deny,status:403" // ``` @@ -1067,7 +1067,7 @@ func directiveSecRuleUpdateActionByID(options *DirectiveOptions) error { if idsOrRangesLen < 2 { return errors.New("syntax error: SecRuleUpdateActionById id \"ACTION1,ACTION2,...\"") } - // The last element is expected to be the actions(s) + // The last element is expected to be the action(s) actions := idsOrRanges[idsOrRangesLen-1] for _, idOrRange := range idsOrRanges[:idsOrRangesLen-1] { if idx := strings.Index(idOrRange, "-"); idx == -1 { diff --git a/testing/coreruleset/go.sum b/testing/coreruleset/go.sum index 7a692094c..c978a841b 100644 --- a/testing/coreruleset/go.sum +++ b/testing/coreruleset/go.sum @@ -4,8 +4,6 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= -github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/corazawaf/coraza-coreruleset/v4 v4.6.0 h1:VGlMw3QMuKaV7XgifPgcqCm66K+HRSdM4d9PRh1nD50= From 681787ef1267fde8affb0ebd691b38fab140bd7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= <3012076+fzipi@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:35:45 +0000 Subject: [PATCH 10/18] chore: update renovate config to use common (#1184) Co-authored-by: Matteo Pace --- renovate.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/renovate.json b/renovate.json index f1b6d2387..9d89bab52 100644 --- a/renovate.json +++ b/renovate.json @@ -1,9 +1,6 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:best-practices" - ], - "constraints": { - "go": "1.22" - } + "local>corazawaf/renovate-config" + ] } From dc6fd712a5cb3bf6ffd8ab76cb3a9725b401a544 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 01:23:26 +0000 Subject: [PATCH 11/18] fix(deps): update module github.com/coreruleset/go-ftw to v1.1.0 in testing/coreruleset/go.mod (#1188) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- testing/coreruleset/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/coreruleset/go.mod b/testing/coreruleset/go.mod index dda3467bc..930b67238 100644 --- a/testing/coreruleset/go.mod +++ b/testing/coreruleset/go.mod @@ -7,7 +7,7 @@ require ( github.com/corazawaf/coraza-coreruleset/v4 v4.6.0 github.com/corazawaf/coraza/v3 v3.0.0-00010101000000-000000000000 github.com/coreruleset/albedo v0.0.16 - github.com/coreruleset/go-ftw v1.0.4-0.20240923043156-8474a93d514a + github.com/coreruleset/go-ftw v1.1.0 github.com/rs/zerolog v1.33.0 ) From ed8caab73619ace8bc18e9b53837bc628382f22d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 01:23:40 +0000 Subject: [PATCH 12/18] chore(deps): update actions/cache action to v4 in .github/workflows/tinygo.yml (#1189) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/tinygo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tinygo.yml b/.github/workflows/tinygo.yml index 720c68916..4e4a48ac9 100644 --- a/.github/workflows/tinygo.yml +++ b/.github/workflows/tinygo.yml @@ -38,7 +38,7 @@ jobs: tinygo-version: ${{ matrix.tinygo-version }} - name: Cache TinyGo build - uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c # v3 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 with: path: | ~/.cache/tinygo From 4e714c60c317b19667a37894f2210e883ed65f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= <3012076+fzipi@users.noreply.github.com> Date: Sat, 2 Nov 2024 00:50:28 +0000 Subject: [PATCH 13/18] Revert "fix(deps): update module github.com/coreruleset/go-ftw to v1.1.0 in testing/coreruleset/go.mod" (#1190) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "fix(deps): update module github.com/coreruleset/go-ftw to v1.1.0 in t…" This reverts commit dc6fd712a5cb3bf6ffd8ab76cb3a9725b401a544. --- testing/coreruleset/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/coreruleset/go.mod b/testing/coreruleset/go.mod index 930b67238..dda3467bc 100644 --- a/testing/coreruleset/go.mod +++ b/testing/coreruleset/go.mod @@ -7,7 +7,7 @@ require ( github.com/corazawaf/coraza-coreruleset/v4 v4.6.0 github.com/corazawaf/coraza/v3 v3.0.0-00010101000000-000000000000 github.com/coreruleset/albedo v0.0.16 - github.com/coreruleset/go-ftw v1.1.0 + github.com/coreruleset/go-ftw v1.0.4-0.20240923043156-8474a93d514a github.com/rs/zerolog v1.33.0 ) From 2253d0462284c20f0ba359410fc4d27a9b7532c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= <3012076+fzipi@users.noreply.github.com> Date: Sat, 2 Nov 2024 09:17:48 +0000 Subject: [PATCH 14/18] fix: toolchain version in go.mod (#1192) Signed-off-by: Felipe Zipitria --- examples/http-server/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/http-server/go.mod b/examples/http-server/go.mod index b3f8819c6..e0a154946 100644 --- a/examples/http-server/go.mod +++ b/examples/http-server/go.mod @@ -1,6 +1,6 @@ module github.com/corazawaf/coraza/v3/examples/http-server -go 1.22 +go 1.22.0 require github.com/corazawaf/coraza/v3 v3.2.1 From 643c29f18cf8d3d3fdca1799cc186423c21ecd1c Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Sat, 2 Nov 2024 09:56:53 +0000 Subject: [PATCH 15/18] chore: refactor process body related logs and doc (#1187) refactor process body related logs and doc --- http/middleware.go | 21 ++------------ internal/corazawaf/transaction.go | 40 +++++++++++++++----------- internal/corazawaf/transaction_test.go | 4 +-- types/transaction.go | 16 +++++++---- 4 files changed, 38 insertions(+), 43 deletions(-) diff --git a/http/middleware.go b/http/middleware.go index acb72e16d..c1e3f50bb 100644 --- a/http/middleware.go +++ b/http/middleware.go @@ -86,27 +86,10 @@ func processRequest(tx types.Transaction, req *http.Request) (*types.Interruptio // Adds all remaining bytes beyond the coraza limit to its buffer // It happens when the partial body has been processed and it did not trigger an interruption - body := io.MultiReader(rbr, req.Body) + bodyReader := io.MultiReader(rbr, req.Body) // req.Body is transparently reinizialied with a new io.ReadCloser. // The http handler will be able to read it. - // Prior to Go 1.19 NopCloser does not implement WriterTo if the reader implements it. - // - https://github.com/golang/go/issues/51566 - // - https://tip.golang.org/doc/go1.19#minor_library_changes - // This avoid errors like "failed to process request: malformed chunked encoding" when - // using io.Copy. - // In Go 1.19 we just do `req.Body = io.NopCloser(reader)` - if rwt, ok := body.(io.WriterTo); ok { - req.Body = struct { - io.Reader - io.WriterTo - io.Closer - }{body, rwt, req.Body} - } else { - req.Body = struct { - io.Reader - io.Closer - }{body, req.Body} - } + req.Body = io.NopCloser(bodyReader) } } diff --git a/internal/corazawaf/transaction.go b/internal/corazawaf/transaction.go index c05128271..7fce408f3 100644 --- a/internal/corazawaf/transaction.go +++ b/internal/corazawaf/transaction.go @@ -969,9 +969,9 @@ func (tx *Transaction) ReadRequestBodyFrom(r io.Reader) (*types.Interruption, in // ProcessRequestBody Performs the analysis of the request body (if any) // -// This method perform the analysis on the request body. It is optional to -// call that function. If this API consumer already knows that there isn't a -// body for inspect it is recommended to skip this step. +// It is recommended to call this method even if it is not expected to have a body. +// It permits to execute rules belonging to request body phase, but not necesarily +// processing the request body. // // Remember to check for a possible intervention. func (tx *Transaction) ProcessRequestBody() (*types.Interruption, error) { @@ -985,11 +985,15 @@ func (tx *Transaction) ProcessRequestBody() (*types.Interruption, error) { } if tx.lastPhase != types.PhaseRequestHeaders { - if tx.lastPhase >= types.PhaseRequestBody { - // Phase already evaluated or skipped - tx.debugLogger.Warn().Msg("ProcessRequestBody should have already been called") - } else { - tx.debugLogger.Debug().Msg("Skipping request body processing, anomalous call before request headers evaluation") + switch { + case tx.lastPhase == types.PhaseRequestBody: + // This condition can happen quite often when ProcessPartial is used as the write body functions call ProcessRequestBody when + // the limit is reached + tx.debugLogger.Debug().Msg("Request body processing has been already performed") + case tx.lastPhase > types.PhaseRequestBody: + tx.debugLogger.Warn().Msg("Skipping anomalous call to ProcessRequestBody. It should have already been called") + default: + tx.debugLogger.Warn().Msg("Skipping anomalous call to ProcessRequestBody. It has been called before request headers evaluation") } return nil, nil } @@ -1215,9 +1219,9 @@ func (tx *Transaction) ReadResponseBodyFrom(r io.Reader) (*types.Interruption, i // ProcessResponseBody Perform the analysis of the the response body (if any) // -// This method perform the analysis on the response body. It is optional to -// call that method. If this API consumer already knows that there isn't a -// body for inspect it is recommended to skip this step. +// It is recommended to call this method even if it is not expected to have a body. +// It permits to execute rules belonging to request body phase, but not necesarily +// processing the response body. // // note Remember to check for a possible intervention. func (tx *Transaction) ProcessResponseBody() (*types.Interruption, error) { @@ -1231,14 +1235,18 @@ func (tx *Transaction) ProcessResponseBody() (*types.Interruption, error) { } if tx.lastPhase != types.PhaseResponseHeaders { - if tx.lastPhase >= types.PhaseResponseBody { - // Phase already evaluated or skipped - tx.debugLogger.Warn().Msg("ProcessResponseBody should have already been called") - } else { + switch { + case tx.lastPhase == types.PhaseResponseBody: + // This condition can happen quite often when ProcessPartial is used as the write body functions call ProcessResponseBody when + // the limit is reached + tx.debugLogger.Debug().Msg("Response body processing has been already performed") + case tx.lastPhase > types.PhaseResponseBody: + tx.debugLogger.Warn().Msg("Skipping anomalous call to ProcessResponseBody. It should have already been called") + default: // Prevents evaluating response body rules if last phase has not been response headers. It may happen // when a server returns an error prior to evaluating WAF rules, but ResponseBody is still called at // the end of http stream - tx.debugLogger.Debug().Msg("Skipping response body processing, anomalous call before response headers evaluation") + tx.debugLogger.Warn().Msg("Skipping anomalous call to ProcessResponseBody. It has been called before response headers evaluation") } return nil, nil } diff --git a/internal/corazawaf/transaction_test.go b/internal/corazawaf/transaction_test.go index bfff8821a..0bb0c4c65 100644 --- a/internal/corazawaf/transaction_test.go +++ b/internal/corazawaf/transaction_test.go @@ -1018,10 +1018,10 @@ func TestProcessBodiesSkippedIfHeadersPhasesNotReached(t *testing.T) { if want, have := 3, len(logEntries); want != have { t.Fatalf("unexpected number of log entries, want %d, have %d", want, have) } - if want, have := "anomalous call before request headers evaluation", logEntries[1]; !strings.Contains(have, want) { + if want, have := "has been called before request headers evaluation", logEntries[1]; !strings.Contains(have, want) { t.Fatalf("unexpected message, want %q, have %q", want, have) } - if want, have := "anomalous call before response headers evaluation", logEntries[2]; !strings.Contains(have, want) { + if want, have := "has been called before response headers evaluation", logEntries[2]; !strings.Contains(have, want) { t.Fatalf("unexpected message, want %q, have %q", want, have) } if err := tx.Close(); err != nil { diff --git a/types/transaction.go b/types/transaction.go index dfbb3c66e..97c7d7ce0 100644 --- a/types/transaction.go +++ b/types/transaction.go @@ -77,9 +77,9 @@ type Transaction interface { // ProcessRequestBody Performs the analysis of the request body (if any) // - // This method perform the analysis on the request body. It is optional to - // call that function. If this API consumer already knows that there isn't a - // body for inspect it is recommended to skip this step. + // It is recommended to call this method even if it is not expected to have a body. + // It permits to execute rules belonging to request body phase, but not necesarily + // processing the request body. // // Remember to check for a possible intervention. ProcessRequestBody() (*Interruption, error) @@ -88,6 +88,8 @@ type Transaction interface { // returns an interruption if the body is bigger than the limit and the action is to // reject. This is specially convenient to resolve an interruption before copying // the body into the request body buffer. + // ProcessRequestBody is called automatically when the action is to process partially + // the body (up to the limit) if the limit is reached. // // It returns the corresponding interruption, the number of bytes written an error if any. WriteRequestBody(b []byte) (*Interruption, int, error) @@ -96,6 +98,8 @@ type Transaction interface { // returns an interruption if the body is bigger than the limit and the action is to // reject. This is specially convenient to resolve an interruption before copying // the body into the request body buffer. + // ProcessRequestBody is called automatically when the action is to process partially + // the body (up to the limit) if the limit is reached. // // It returns the corresponding interruption, the number of bytes written an error if any. ReadRequestBodyFrom(io.Reader) (*Interruption, int, error) @@ -120,9 +124,9 @@ type Transaction interface { // ProcessResponseBody Perform the analysis of the response body (if any) // - // This method perform the analysis on the response body. It is optional to - // call that method. If this API consumer already knows that there isn't a - // body for inspect it is recommended to skip this step. + // It is recommended to call this method even if it is not expected to have a body. + // It permits to execute rules belonging to request body phase, but not necesarily + // processing the response body. // // note Remember to check for a possible intervention. ProcessResponseBody() (*Interruption, error) From dee06766390fceb88b318cef36726a4be549decf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 15:07:53 +0000 Subject: [PATCH 16/18] fix(deps): update module github.com/coreruleset/go-ftw to v1.1.1 in testing/coreruleset/go.mod (#1191) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- testing/coreruleset/go.mod | 14 +++++++------- testing/coreruleset/go.sum | 12 ++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/testing/coreruleset/go.mod b/testing/coreruleset/go.mod index dda3467bc..06676fc11 100644 --- a/testing/coreruleset/go.mod +++ b/testing/coreruleset/go.mod @@ -5,9 +5,9 @@ go 1.22.3 require ( github.com/bmatcuk/doublestar/v4 v4.7.1 github.com/corazawaf/coraza-coreruleset/v4 v4.6.0 - github.com/corazawaf/coraza/v3 v3.0.0-00010101000000-000000000000 + github.com/corazawaf/coraza/v3 v3.2.1 github.com/coreruleset/albedo v0.0.16 - github.com/coreruleset/go-ftw v1.0.4-0.20240923043156-8474a93d514a + github.com/coreruleset/go-ftw v1.1.1 github.com/rs/zerolog v1.33.0 ) @@ -28,13 +28,13 @@ require ( github.com/imdario/mergo v0.3.16 // indirect github.com/knadh/koanf/maps v0.1.1 // indirect github.com/knadh/koanf/parsers/yaml v0.1.0 // indirect - github.com/knadh/koanf/providers/env v0.1.0 // indirect - github.com/knadh/koanf/providers/file v1.1.0 // indirect + github.com/knadh/koanf/providers/env v1.0.0 // indirect + github.com/knadh/koanf/providers/file v1.1.2 // indirect github.com/knadh/koanf/providers/rawbytes v0.1.0 // indirect github.com/knadh/koanf/v2 v2.1.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/kyokomi/emoji/v2 v2.2.13 // indirect - github.com/magefile/mage v1.15.0 // indirect + github.com/magefile/mage v1.15.1-0.20231118170541-2385abb49a1f // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -50,9 +50,9 @@ require ( golang.org/x/net v0.30.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.26.0 // indirect - golang.org/x/time v0.6.0 // indirect + golang.org/x/time v0.7.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/protobuf v1.34.1 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/binaryregexp v0.2.0 // indirect ) diff --git a/testing/coreruleset/go.sum b/testing/coreruleset/go.sum index c978a841b..2fb7e20c8 100644 --- a/testing/coreruleset/go.sum +++ b/testing/coreruleset/go.sum @@ -17,6 +17,8 @@ github.com/coreruleset/ftw-tests-schema/v2 v2.1.0 h1:2ilKzKRG5UzzxBcrJLXFtPalStd github.com/coreruleset/ftw-tests-schema/v2 v2.1.0/go.mod h1:ZHVFX5ses4+5IxUP0ufCNg/VqRWxziH6ZuUca092Hxo= github.com/coreruleset/go-ftw v1.0.4-0.20240923043156-8474a93d514a h1:ucsngMh75QkedILO3N9JRiVWPbkcelhH055adS51hNs= github.com/coreruleset/go-ftw v1.0.4-0.20240923043156-8474a93d514a/go.mod h1:/HKpRGHn0rOEb+VdkfjLqltb3Jr5DHDeKPAiC/04lR8= +github.com/coreruleset/go-ftw v1.1.1 h1:Ew4LpzjgN59derh7it2dcAdCwQ+24ZY4lYAVonWwmok= +github.com/coreruleset/go-ftw v1.1.1/go.mod h1:Hr/rDC0fzVa9iV8ACLUwgAin13p7L9WtLgwz/gkz+A0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -55,8 +57,12 @@ github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY= github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clwyZozgVRiKg= github.com/knadh/koanf/providers/env v0.1.0/go.mod h1:RE8K9GbACJkeEnkl8L/Qcj8p4ZyPXZIQ191HJi44ZaQ= +github.com/knadh/koanf/providers/env v1.0.0 h1:ufePaI9BnWH+ajuxGGiJ8pdTG0uLEUWC7/HDDPGLah0= +github.com/knadh/koanf/providers/env v1.0.0/go.mod h1:mzFyRZueYhb37oPmC1HAv/oGEEuyvJDA98r3XAa8Gak= github.com/knadh/koanf/providers/file v1.1.0 h1:MTjA+gRrVl1zqgetEAIaXHqYje0XSosxSiMD4/7kz0o= github.com/knadh/koanf/providers/file v1.1.0/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI= +github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w= +github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI= github.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI51k5pe8RYKQ0qME= github.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c= github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= @@ -71,6 +77,8 @@ github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= +github.com/magefile/mage v1.15.1-0.20231118170541-2385abb49a1f h1:iiLWLoibjCL0XND6inF7bs2nc20lU/FYkiR//VIOLUc= +github.com/magefile/mage v1.15.1-0.20231118170541-2385abb49a1f/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -121,12 +129,16 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From de409e378b912233f7a89a1e09b92d67f2b03f8d Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Tue, 5 Nov 2024 15:27:32 +0000 Subject: [PATCH 17/18] perf: GetField reduce allocations (#1195) reuse slice --- internal/corazawaf/transaction.go | 11 ++++++----- internal/corazawaf/transaction_test.go | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/internal/corazawaf/transaction.go b/internal/corazawaf/transaction.go index 7fce408f3..1a4785b90 100644 --- a/internal/corazawaf/transaction.go +++ b/internal/corazawaf/transaction.go @@ -587,9 +587,9 @@ func (tx *Transaction) GetField(rv ruleVariableParams) []types.MatchData { } // in the most common scenario filteredMatches length will be - // the same as matches length, so we avoid allocating per result - filteredMatches := make([]types.MatchData, 0, len(matches)) - + // the same as matches length, so we avoid allocating per result. + // We reuse the matches slice to store filtered results avoiding extra allocation. + filteredCount := 0 for _, c := range matches { isException := false lkey := strings.ToLower(c.Key()) @@ -600,10 +600,11 @@ func (tx *Transaction) GetField(rv ruleVariableParams) []types.MatchData { } } if !isException { - filteredMatches = append(filteredMatches, c) + matches[filteredCount] = c + filteredCount++ } } - matches = filteredMatches + matches = matches[:filteredCount] if rv.Count { count := len(matches) diff --git a/internal/corazawaf/transaction_test.go b/internal/corazawaf/transaction_test.go index 0bb0c4c65..64d548546 100644 --- a/internal/corazawaf/transaction_test.go +++ b/internal/corazawaf/transaction_test.go @@ -1291,6 +1291,20 @@ func TestTxGetField(t *testing.T) { } } +func BenchmarkTxGetField(b *testing.B) { + tx := makeTransaction(b) + rvp := ruleVariableParams{ + Variable: variables.Args, + } + for i := 0; i < b.N; i++ { + tx.GetField(rvp) + } + if err := tx.Close(); err != nil { + b.Fatalf("Failed to close transaction: %s", err.Error()) + } + b.ReportAllocs() +} + func TestTxProcessURI(t *testing.T) { waf := NewWAF() tx := waf.NewTransaction() From 5fbd62a876518b159f5093326aabed5d6d9e3b35 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Thu, 7 Nov 2024 09:50:52 +0000 Subject: [PATCH 18/18] docs: nits and avoids mentioning not existing resources (#1203) nits:doc --- internal/corazawaf/transaction.go | 3 +-- internal/seclang/directives.go | 3 --- types/transaction.go | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/corazawaf/transaction.go b/internal/corazawaf/transaction.go index 1a4785b90..2fe3585c8 100644 --- a/internal/corazawaf/transaction.go +++ b/internal/corazawaf/transaction.go @@ -1292,8 +1292,7 @@ func (tx *Transaction) ProcessResponseBody() (*types.Interruption, error) { return tx.interruption, nil } -// ProcessLogging Logging all information relative to this transaction. -// An error log +// ProcessLogging logs all information relative to this transaction. // At this point there is not need to hold the connection, the response can be // delivered prior to the execution of this method. func (tx *Transaction) ProcessLogging() { diff --git a/internal/seclang/directives.go b/internal/seclang/directives.go index 7d02b89d8..30540da32 100644 --- a/internal/seclang/directives.go +++ b/internal/seclang/directives.go @@ -774,9 +774,6 @@ func directiveSecAuditLogRelevantStatus(options *DirectiveOptions) error { // Syntax: SecAuditLogParts [PARTLETTERS] // Default: ABCFHZ // --- -// The format of the audit log format is documented in detail in the Audit Log Data -// Format Documentation. -// // Example: // ```apache // SecAuditLogParts ABCFHZ diff --git a/types/transaction.go b/types/transaction.go index 97c7d7ce0..b16448a20 100644 --- a/types/transaction.go +++ b/types/transaction.go @@ -148,7 +148,6 @@ type Transaction interface { ReadResponseBodyFrom(io.Reader) (*Interruption, int, error) // ProcessLogging Logging all information relative to this transaction. - // An error log // At this point there is not need to hold the connection, the response can be // delivered prior to the execution of this method. ProcessLogging()