Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow selectors on *_NAMES collections #1143

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

blotus
Copy link
Contributor

@blotus blotus commented Sep 4, 2024

Hello,

This PR aims to allow the use of rules such as SecRule &REQUEST_COOKIES_NAMES:JSESSIONID "@eq 0" "id:45" (supported by ModSecurity and also present as an example in the documentation of Coraza), which currently causes Coraza to crash due to an explicit panic call.

There are 3 main changes:

  • Check if a collection supports selectors during parsing time, instead of throwing an error at runtime.
  • Make collections.NamedCollectionNames implements collection.Keyed: this allows the use of a selector for the collections created with .Names()
  • Remove runtime panics calls: as Coraza is designed to be embedded in other software, calling panic is never a good idea.

Parser changes

I've embedded information about whether a collection can be selected or not in the internal/variables/variables.go file, as a comment for each collection that does support it (hopefully, I did not miss any), and added a CanBeSelected method that is called during parsing to check if the selector is allowed or not.

I don't know if I'm really happy with embedding information in comments, but it was the least intrusive way I found to handle this.

collections.NamedCollectionNames implements collection.Keyed

This one is straightforward, NamedCollectionNames now implements Get, FindString and FindRegex.
Because it's a named collection, the key and the value in the returned results will be the same: the name of the key.

Remove runtime panic

The first two were removed as part of making namedCollectionNames implements Keyed.

The other two (which are the ones that caused the crash mentioned at the beginning of this PR) have been replaced by an error log.
In theory, this log should never occur because selectability is now checked during parsing (in practice, it could happen if a collection is marked as selectable but does not implement Keyed).

@blotus blotus requested a review from a team as a code owner September 4, 2024 12:32
Copy link

codecov bot commented Sep 4, 2024

Codecov Report

Attention: Patch coverage is 21.64502% with 181 lines in your changes missing coverage. Please review.

Project coverage is 82.27%. Comparing base (4ff1f76) to head (8dbefad).
Report is 79 commits behind head on main.

Files with missing lines Patch % Lines
internal/variables/variablesmap.gen.go 13.26% 170 Missing ⚠️
internal/corazawaf/transaction.go 10.00% 9 Missing ⚠️
internal/collections/named.go 91.30% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1143      +/-   ##
==========================================
- Coverage   82.72%   82.27%   -0.45%     
==========================================
  Files         162      166       +4     
  Lines        9080     8109     -971     
==========================================
- Hits         7511     6672     -839     
+ Misses       1319     1187     -132     
  Partials      250      250              
Flag Coverage Δ
default 82.27% <21.64%> (+4.44%) ⬆️
examples 82.27% <21.64%> (+55.84%) ⬆️
ftw 82.27% <21.64%> (+34.91%) ⬆️
ftw-multiphase 82.27% <21.64%> (+32.73%) ⬆️
tinygo 82.27% <21.64%> (+6.87%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jptosso
Copy link
Member

jptosso commented Sep 4, 2024

Interesting, thank you very much for your contribution

Im a bit worried about how the complexity of variables is growing. Maybe not for this PR, but we need to improve generation of code, even for this "selectable" feature

Comment on lines +215 to +217
// CanBeSelected returns true if the variable supports selection (ie, `:foobar`)
func (v RuleVariable) CanBeSelected() bool {
switch v {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think adding everything here makes sense. Just return true on those who can, and use the default otherwise.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although for performance it makes sense, I believe this is easier to maintain and more readable

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is auto generated so I would not be concern about readability. @blotus could you do a quick benchmark on this matter i.e. adding everything or just true and all the rest on a default?

@fzipi
Copy link
Member

fzipi commented Sep 8, 2024

Im a bit worried about how the complexity of variables is growing. Maybe not for this PR, but we need to improve generation of code, even for this "selectable" feature

Definitely not for this PR. We should create an issue to refactor generation then.

@fzipi fzipi changed the title Allow selectors on *_NAMES collections feat: allow selectors on *_NAMES collections Sep 8, 2024
@jptosso
Copy link
Member

jptosso commented Sep 18, 2024

LGTM in general, but I believe this lacks negative tests and its decreasing the general project coverage

@@ -101,11 +101,41 @@ type NamedCollectionNames struct {
}

func (c *NamedCollectionNames) FindRegex(key *regexp.Regexp) []types.MatchData {
panic("selection operator not supported")
var res []types.MatchData
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any chance data is empty? if so I would handle the empty case before this allocation.

var res []types.MatchData

for k, data := range c.collection.Map.data {
if key.MatchString(k) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use early termination to reduce the indentation.

for k, data := range c.collection.Map.data {
if key.MatchString(k) {
for _, d := range data {
res = append(res, &corazarules.MatchData{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if MatchData is mutable, if so we probably want to reuse the pointer?

@@ -22,6 +22,17 @@ func (v RuleVariable) Name() string {
}
}

//CanBeSelected returns true if the variable supports selection (ie, `:foobar`)
Copy link
Member

@jcchavezs jcchavezs Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//CanBeSelected returns true if the variable supports selection (ie, `:foobar`)
// CanBeSelected returns true if the variable supports selection (e.g. `:foobar`)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants