Skip to content

SecRuleUpdateActionById does not actually replace actions #1414

@blotus

Description

@blotus

Description

When using SecRuleUpdateActionById, the new actions are simply appended to the list of existing actions for the rule, which prevents it from being used to disable specific rules.

Steps to reproduce

SecRule ARGS:id "@eq 0" "id:1, phase:1,deny,status:403,msg:'Invalid id',log,auditlog"
SecRuleUpdateActionById 1 "log,pass"

The first rule uses a deny action to block a request if the id parameter is equals to 0.
But there's a SecRuleUpdateActionById to change the action to pass which should allow the request.

Expected result

Request blocked.

I've tested those rules with modsecurity, and the behaviour is the expected one: the request is allowed.

Actual result

The request is not blocked:

~/git/coraza/examples/http-server (main ✘)✹ ᐅ go run main.go
2025/08/28 14:47:44 [DEBUG] Parsing directive line="SecRule ARGS:id \"@eq 0\" \"id:1, phase:1,deny,status:403,msg:'Invalid id',log,auditlog\""
2025/08/28 14:47:44 [DEBUG] Parsing directive line="SecRuleUpdateActionById 1 \"log,pass\""
Server is running. Listening port: 8090
2025/08/28 14:47:56 [DEBUG] Transaction started tx_id="QyVhEtdOAzffqKwkdpM"
2025/08/28 14:47:56 [DEBUG] Evaluating phase tx_id="QyVhEtdOAzffqKwkdpM" phase=1
2025/08/28 14:47:56 [DEBUG] Evaluating rule tx_id="QyVhEtdOAzffqKwkdpM" rule_id=1
2025/08/28 14:47:56 [DEBUG] Expanding arguments for rule tx_id="QyVhEtdOAzffqKwkdpM" rule_id=1 variable="ARGS"
2025/08/28 14:47:56 [DEBUG] Matching rule tx_id="QyVhEtdOAzffqKwkdpM" rule_id=1 variable_name="ARGS" key="id"
2025/08/28 14:47:56 [DEBUG] Evaluating action tx_id="QyVhEtdOAzffqKwkdpM" action="log"
2025/08/28 14:47:56 [DEBUG] Evaluating action tx_id="QyVhEtdOAzffqKwkdpM" action="auditlog"
2025/08/28 14:47:56 [DEBUG] Evaluating action tx_id="QyVhEtdOAzffqKwkdpM" action="log"
2025/08/28 14:47:56 [DEBUG] Evaluating operator: MATCH tx_id="QyVhEtdOAzffqKwkdpM" rule_id=1 variable="ARGS" operator_function="@eq" operator_data="0" arg="0"
2025/08/28 14:47:56 [DEBUG] Executing disruptive action for rule tx_id="QyVhEtdOAzffqKwkdpM" rule_id=1 action="deny"
2025/08/28 14:47:56 [DEBUG] Executing disruptive action for rule tx_id="QyVhEtdOAzffqKwkdpM" rule_id=1 action="pass"
2025/08/28 14:47:56 [DEBUG] Rule matched tx_id="QyVhEtdOAzffqKwkdpM" rule_id=1
[logError][emergency] [client "[::1]"] Coraza: Access denied (phase 1). Invalid id [file "default.conf"] [line "4"] [id "1"] [rev ""] [msg "Invalid id"] [data ""] [severity "emergency"] [ver ""] [maturity "0"] [accuracy "0"] [hostname ""] [uri "/?id=0"] [unique_id "QyVhEtdOAzffqKwkdpM"]
2025/08/28 14:47:56 [DEBUG] Finished rule evaluation tx_id="QyVhEtdOAzffqKwkdpM" rule_id=1
2025/08/28 14:47:56 [DEBUG] Finished phase tx_id="QyVhEtdOAzffqKwkdpM" phase=1
2025/08/28 14:47:56 [DEBUG] Evaluating phase tx_id="QyVhEtdOAzffqKwkdpM" phase=5
2025/08/28 14:47:56 [DEBUG] Finished phase tx_id="QyVhEtdOAzffqKwkdpM" phase=5
2025/08/28 14:47:56 [DEBUG] Transaction marked for audit logging tx_id="QyVhEtdOAzffqKwkdpM"
2025/08/28 14:47:56 [DEBUG] Transaction finished tx_id="QyVhEtdOAzffqKwkdpM" is_interrupted=true status=403 rule_id=1

As we can see in the logs, both deny and pass actions are specified for the rule (and log twice), and because pass is a noop, the interruption set by deny is returned, which blocks the request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions