feat: allow selectors on *_NAMES collections#1143
Conversation
… non-selectable collection
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1143 +/- ##
==========================================
- Coverage 83.98% 83.84% -0.15%
==========================================
Files 170 170
Lines 9824 9909 +85
==========================================
+ Hits 8251 8308 +57
- Misses 1329 1357 +28
Partials 244 244
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
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 |
| // CanBeSelected returns true if the variable supports selection (ie, `:foobar`) | ||
| func (v RuleVariable) CanBeSelected() bool { | ||
| switch v { |
There was a problem hiding this comment.
I don't think adding everything here makes sense. Just return true on those who can, and use the default otherwise.
There was a problem hiding this comment.
Although for performance it makes sense, I believe this is easier to maintain and more readable
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
I agree that readability does not really matter here.
I've updated the code generation to only generate the cases where true is returned.
Definitely not for this PR. We should create an issue to refactor generation then. |
*_NAMES collections*_NAMES collections
|
LGTM in general, but I believe this lacks negative tests and its decreasing the general project coverage |
|
|
||
| func (c *NamedCollectionNames) FindRegex(key *regexp.Regexp) []types.MatchData { | ||
| panic("selection operator not supported") | ||
| var res []types.MatchData |
There was a problem hiding this comment.
Is there any chance data is empty? if so I would handle the empty case before this allocation.
There was a problem hiding this comment.
Yes, there are probably situations where data can be empty (I haven't tested, but I'd expect a collection like XML to have an empty data on a non-XML request )
AFAIK, declaring a slice like this does not perform any actual allocation (other than the header of the slice, which will be all zero), and the actual allocation will be performed the first time we append to it, but I can add a check on data if you are worried about it.
There was a problem hiding this comment.
Also, from what I see, this check is not performed in the existing code (here for example)
| for k, data := range c.collection.Map.data { | ||
| if key.MatchString(k) { | ||
| for _, d := range data { | ||
| res = append(res, &corazarules.MatchData{ |
There was a problem hiding this comment.
I wonder if MatchData is mutable, if so we probably want to reuse the pointer?
| } else { | ||
| panic("attempted to use regex with non-selectable collection: " + rv.Variable.Name()) | ||
| // This should probably never happen, selectability is checked at parsing time | ||
| tx.debugLogger.Error().Str("collection", rv.Variable.Name()).Msg("attempted to use regex with non-selectable collection") |
There was a problem hiding this comment.
We discussed time ago that panic is ok, as this is a low level issue and coraza should not run here
There was a problem hiding this comment.
I'm not sure I agree with this: coraza is designed as a library, and in my point of view, this means that explicit panics must be avoided at all costs (with very little exceptions, if you can call panic, you can return an error), and not doing anything is almost always better than bringing down a production website.
If a function call can lead to a panic, it should be made very clear to the caller (either with an explicit function name (Must....) or, at the very least, with some documentation): I don't mind wrapping every call to coraza with a recover, but I need to be aware it's required.
For this specific case, it can only (AFAIK) be triggered by a configuration error, so this means it should be detected when parsing the configuration (and is now thanks to this PR), so the panic has become redundant.
|
Thanks a lot @blotus ! |
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 explicitpaniccall.There are 3 main changes:
collections.NamedCollectionNamesimplementscollection.Keyed: this allows the use of a selector for the collections created with.Names()panicscalls: as Coraza is designed to be embedded in other software, callingpanicis never a good idea.Parser changes
I've embedded information about whether a collection can be selected or not in the
internal/variables/variables.gofile, as a comment for each collection that does support it (hopefully, I did not miss any), and added aCanBeSelectedmethod 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.NamedCollectionNamesimplementscollection.KeyedThis one is straightforward,
NamedCollectionNamesnow implementsGet,FindStringandFindRegex.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
panicThe first two were removed as part of making
namedCollectionNamesimplementsKeyed.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).