8000 Add rule-ctl.py script by theMiddleBlue · Pull Request #2068 · coreruleset/coreruleset · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add rule-ctl.py script #2068

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

Closed

Conversation

theMiddleBlue
Copy link
Contributor

This is the first draft of rule-ctl.py. This is the first README:

draft

OWASP CRS Rule Control Script

This script aims to help when a bulk change on configuration files is needed. rule-ctl.py can, for example, change the value of an action on all rules, or can add/remove/rename a tag on each rule in a file, or can add/remove a transformation function only in rules that match range 942100-942190, etc...

Example Usage

There're only two mandatory parameters: --config and --filter-rule-id.

--config set the target config file

--filter-rule-id a regex that matches only rule ids to change

For example, if you want to add a new tag on each rule in file REQUEST-933-APPLICATION-ATTACK-PHP.conf you can do:

python3 util/rule-ctl/rule-ctl.py \
    --config rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf \
    --filter-rule-id ^933.+ \
    --append-tag foo
    --dryrun

--dryrun sends to stdout the result of changes and prevent writing changes on file. It's a good idea to always check all commands with dryrun before overwrite the target configuration file.

You can even alphabetically sort tag list while adding new tags:

python3 util/rule-ctl/rule-ctl.py \
    --config rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf \
    --filter-rule-id ^933.+ \
    --append-tag foo
    --sort-tag
    --dryrun

Variables

  • --append-variable: Append a variable on the variable list of selected rules
  • --remove-variable: Remove exact matching variable from selected rules
  • --replace-variable: Replace variable on selected rules
  • --replace-variable-name: Replace just variable name part on selected rules

Examples

Replace all ARGS variable with ARGS_GET even if ARGS:foo

python3 rule-ctl.py --config tests/rules.conf \
    --filter-rule-id ^.* \
    --replace-variable-name ARGS,ARGS_GET \
    --dryrun

Tags

  • --append-tag: Append a new tag to the tag list on selected rules
  • --remove-tag: Remove tag from tag list on selected rules
  • --rename-tag: Rename tag on selected rules
  • --sort-tags: Alphabetically sort tag list on selected rules

Examples

Append a new tag foo and sort tag list

python3 rule-ctl.py --config tests/rules.conf \
    --filter-rule-id ^.* \
    --append-tag foo \
    --sort-tags \
    --dryrun

Transformation Functions

  • --append-tfunc: Append a new transformation function on selected rules
  • --remove-tfunc: Remove a transformation function on selected rules

Examples

Append t:lowercase to all selected rules (you don't need the t: prefix)

python3 rule-ctl.py --config tests/rules.conf \
    --filter-rule-id ^.* \
    --append-tfunc lowercase \
    --dryrun

Actions

  • --replace-action: Replace action on selected rules
  • --uncond-replace-action: Unconditional replace action on selected rules
  • --remove-action: remove action from selected rules

Examples

Replace action severity:CRITICAL with severity:INFO and set a new message on rule id 125

python3 rule-ctl.py --config tests/rules.conf \
    --filter-rule-id ^125 \
    --replace-action severity:CRITICAL,severity:INFO \
    --uncond-replace-action 'msg:this is a new message for rule 125' \
    --dryrun

CTL

  • --append-ctl: Append a new ctl action on selected rules

Examples

Remove rule id 1337 on rule 125 by adding ctl:ruleRemoveById=1337. Do it on main rule (skipping chained rules if present)

python3 rule-ctl.py --config tests/rules.conf \
    --filter-rule-id ^125 \
    --append-ctl ruleRemoveById=1337 \
    --skip-chain \
    --dryrun

Others

  • --target-file: Set the target file where changes will be saved (default: use file set by --config)
  • --skip-chain: Skip chained rules
  • --dryrun: Do not write any changes, just output the results
  • --debug: Show debug messages
  • --silent: Used with --dryrun and --debug doesn't write and shows only debug messages
  • --json: Used with --dryrun return the msc_pyparser JSON output instead of ModSecurity file

@theMiddleBlue theMiddleBlue marked this pull request as draft May 7, 2021 14:14
@dune73
Copy link
Member
dune73 commented Jun 6, 2021

This is a very, very useful script that makes msc_pyparser much more accessible. I'll try to get @airween to review this PR and provide some feedback, so this can be finished and merged asap.

@airween airween self-requested a review June 7, 2021 12:29
@fzipi fzipi self-requested a review June 7, 2021 18:45
@franbuehler
Copy link
Contributor
franbuehler commented Jun 7, 2021

Meeting decision June (#2074 (comment)):
@airween and @fzipi test this one (already self-assigned as reviewer). And Paul Beckett volunteers too to test it.

@53cur3M3
Copy link
Contributor

Very minor point... some lines in multi-line command examples in README.md are missing trailing \

@53cur3M3
Copy link
Contributor

Running command (as non-root user) results in warnings:
WARNING: Couldn't open 'parser.out'. [Errno 13] Permission denied: '/usr/local/lib/python3.8/dist-packages/parser.out'
Generating LALR tables
WARNING: Couldn't create 'parsetab'. [Errno 13] Permission denied: '/usr/local/lib/python3.8/dist-packages/parsetab.py'

@airween
Copy link
Contributor
airween commented Jun 21, 2021

Running command (as non-root user) results in warnings:
WARNING: Couldn't open 'parser.out'. [Errno 13] Permission denied: '/usr/local/lib/python3.8/dist-packages/parser.out'
Generating LALR tables
WARNING: Couldn't create 'parsetab'. [Errno 13] Permission denied: '/usr/local/lib/python3.8/dist-packages/parsetab.py'

How did you install the msc_pyparser package? (From PyPI, or downloaded the package?)

@53cur3M3
Copy link
Contributor

on ubuntu 20.04 LTS
sudo apt install python3-pip
sudo pip3 install -r util/rule-ctl/requirements.txt

@airween
Copy link
Contributor
airween commented Jun 21, 2021

Hmm... it's interesting. What do you get when you type this command?

>>> print(msc_pyparser.__version__)

You should get 1.1. If you get something different, try to add the version in the requirements.txt:

msc_pyparser==1.1

I have cloned theMiddle's repository, and have tried to repdoduce your issue, but I couldn't.

@53cur3M3
Copy link
Contributor

import msc_pyparser
print(msc_pyparser.version)
1.1

@airween
Copy link
Contributor
airween commented Jun 22, 2021

Which Python version do you have? I could reproduce your issue on Python3.5 - looks like this version does not install the parsetab.py and parser.out files, or the interpreter looks then under the module's directory, not under the /usr/local.

I have to review it - workaround: try to run as root the script at first time, that will generate the necessary files.

@53cur3M3
Copy link
Contributor

import sys
sys.version_info
sys.version_info(major=3, minor=8, micro=5, releaselevel='final', serial=0)

Looked like warnings were not fatal, script seemed to be producing expected results.

As per your workaround, I can confirm running script as root, has no warnings, and results in the creation of: /usr/local/lib/python3.8/dist-packages/parser.out and /usr/local/lib/python3.8/dist-packages/parsetab.py . Subsequently running as non-root then has no warnings.

@53cur3M3
Copy link
Contributor

I've tried adding/removing transformation functions and tags.

Might be expected behaviour, but attempts to remove multiple tags seems to silently fail to remove all the tags:
python3 util/rule-ctl/rule-ctl.py --config rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf --filter-rule-id ^933.+ --remove-tag platform-multi --remove-tag application-multi --dryrun
above doesn't remove platform-multi tag

whereas it does seem possible to remove multiple transformation functions:
python3 util/rule-ctl/rule-ctl.py --config rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf --filter-rule-id ^933.+ --remove-tfunc lowercase --remove-tfunc urlDecodeUni --dryrun

also I kind of expected (possibly wrongly/unreasonably) that transformation function removal would occur before appending so that it would be possible to strip transformation functions and re-add them in a specific order:
python3 util/rule-ctl/rule-ctl.py --config rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf --filter-rule-id ^933.+ --remove-tfunc lowercase --remove-tfunc urlDecodeUni --append-tfunc urlDecodeUni --append-tfunc lowercase --dryrun
No hardship to have to do this as two commands; but maybe worth a note in the documentation (assuming it's not already there and I haven't failed to spot it)?

@dune73
Copy link
Member
dune73 commented Jul 2, 2021

@theMiddleBlue : We've had a few comments for this welcome script. Can you incorporate these and finish the PR? Would be nice to merge this in the coming days.

@airween
Copy link
Contributor
airween commented Jul 5, 2021

I have found a strange behavior in the rule-ctl.py script: comparing the output with the original file. It seems the line (re)numbering is wrong:

132,133c134
<     SecRule MATCHED_VARS "@pm =" \
<         "capture,\
---
>     SecRule MATCHED_VARS "@pm =" "capture,\

(the first action after the operator and its argument goes into same line, not the next one)

There are few lines where the line increment statements are commented, eg. here. May be this caused the behavior above.

I can take a look it later, if you need.

@franbuehler
Copy link
Contributor

Meeting decision July:
#2141 (comment)
@airween will review it and send a fix - @theseion wants to test it

@theseion
Copy link
Contributor
theseion commented Jul 6, 2021

@airween How did you produce that issue? The simple things I've tried all work.

@airween
Copy link
Contributor
airween commented Jul 6, 2021

Hi @theseion,

here is how I did it:

$ git branch
* add-script-rule-ctl
  v3.4/dev

$ python3 rule-ctl.py --config ../../rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf --filter-rule-id ^933.+ --append-tag foo

$ git diff ../../rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
diff --git a/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf b/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
index 49974f1..271b171 100644
--- a/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
+++ b/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
@@ -14,6 +14,7 @@
 
 
 SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:933011,phase:1,pass,nolog,skipAfter:END-REQUEST-933-APPLICATION-ATTACK-PHP"
+
 SecRule TX:EXECUTING_PARANOIA_LEVEL "@lt 1" "id:933012,phase:2,pass,nolog,skipAfter:END-REQUEST-933-APPLICATION-ATTACK-PHP"
 #
 # -= Paranoia Level 1 (default) =- (apply only when tx.executing_paranoia_level is sufficiently high: 1 or higher)
@@ -58,12 +59,12 @@ SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAME
     tag:'paranoia-level/1',\
     tag:'OWASP_CRS',\
     tag:'capec/1000/152/242',\
+    tag:'foo',\
...
...
@@ -125,12 +126,12 @@ SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAME
     tag:'paranoia-level/1',\
     tag:'OWASP_CRS',\
     tag:'capec/1000/152/242',\
+    tag:'foo',\
     ctl:auditLogParts=+E,\
     ver:'OWASP_CRS/3.3.0',\
     severity:'CRITICAL',\
     chain"
-    SecRule MATCHED_VARS "@pm =" \
-        "capture,\
+    SecRule MATCHED_VARS "@pm =" "capture,\
         setvar:'tx.php_injection_score=+%{tx.critical_anomaly_score}',\

See the last few lines. The new lines with tag:'foo' are expected, but the SecRule MATCHED_VARS "@pm =" "capture,\ isn't.

@airween
Copy link
Contributor
airween commented Jul 14, 2021

@theseion can you confirm (or refute :)) this behavior? I mean after the transformation (eg. append a new tag) the firs action moves into same line as variable and operator are.

@airween
Copy link
Contributor
airween commented Jul 14, 2021

Another strange behavior: if there is no any tag at the rule, append-tag has no effect. (I know there no such CRS rule, but if anybody wants to use its own set, it would be a good feature.)

@theseion
Copy link
Contributor

Sorry for the delay.

@theseion can you confirm (or refute :)) this behavior? I mean after the transformation (eg. append a new tag) the firs action moves into same line as variable and operator are.

Yes, I can confirm that. Only appears to affect chained rules.

Another strange behavior: if there is no any tag at the rule, append-tag has no effect. (I know there no such CRS rule, but if anybody wants to use its own set, it would be a good feature.)

The rules_tag_append function is very complicated. I'm not surprised that happens.

@airween
Copy link
Contributor
airween commented Jul 15, 2021

Yes, I can confirm that. Only appears to affect chained rules.

only appears in case of chained rules, if there is any non-chained rule before that :)

The rules_tag_append function is very complicated. I'm not surprised that happens.

I think the problem is that the code doesn't follow the number of appended lines (as an offset/shift value), only increments by 1 the lines. I can take a look later.

@airween
Copy link
Contributor
airween commented Jul 19, 2021

@theMiddleBlue, @theseion - would you like to review this patch?

This solves only the append-tag formatting, but I think we can follow this pattern to solve the line shift problem (see above).

@theseion
Copy link
Contributor

I'm a bit busy but I'll try to get look at the patch in a couple of days.

@theMiddleBlue
Copy link
Contributor Author

@theMiddleBlue, @theseion - would you like to review this patch?

This solves only the append-tag formatting, but I think we can follow this pattern to solve the line shift problem (see above).

thanks for fixing it! I'll try it asap

@theseion
Copy link
Contributor

The patch works. I've started refactoring the script heavily because I didn't understand how it works. If something useful comes out of that I'll contribute that.

@airween
Copy link
Contributor
airween commented Jul 28, 2021

The patch works. I've started refactoring the script heavily because I didn't understand how it works. If something useful comes out of that I'll contribute that.

Let me know if I can help you anything.

@dune73
Copy link
Member
dune73 commented Aug 2, 2021

There is still work going on for this PR and reviews are happening. So I suggest we skip it for the agenda of the meeting tonight. Feel free to merge if you guys think you are done.

@theseion
Copy link
Contributor

FYI: I have a working implementation. I'm writing tests at the moment, which will take me a while but we'll have a proper test suite for all the actions at the end.

@dune73
Copy link
Member
dune73 commented Sep 2, 2021

Hey @theseion, the expectation of getting a test suite for this is very nice. Do you have a time line for this?

@theseion
Copy link
Contributor
theseion commented Sep 2, 2021

I'm working on it now. I hope to have it ready by monday.

@theseion
Copy link
Contributor
theseion commented Sep 6, 2021

I've opened a new PR with the refactored version of the script. The PR includes a pytest test suite.

@dune73
Copy link
Member
dune73 commented Oct 4, 2021

Closing this in favour of #2193

@dune73 dune73 closed this Oct 4, 2021
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.

6 participants
0