Skip to content
ADR-0014: `SecDefaultAction` constraints

ADR-0014: `SecDefaultAction` constraints

  • Status: proposed
  • Date: 2026-10-06
  • Deciders: @fzipi
  • Category: Divergence

Context

Checks each engine applies to a SecDefaultAction action list (all verified against source on 2026-10-06):

CheckModSecurity v2 (cmd_default_action)libmodsecurity v3 (seclang-parser.yy)Coraza 3.8.1 (rule_parser.go ParseDefaultActions)
missing disruptive actionerrorerrorerror
missing phaseerrordefaults to phase 1error
metadata actions (id, rev, msg, severity, ver, accuracy, maturity, logdata)errorerror (not run-time actions)error
tagnot checked (ENH comment)errorerror
chain, skip, skipAftererrorerrorerror
t:none / transformationswarningerror on t:noneerror on any t:
second SecDefaultAction for the same phasereplaces the firsterrorerror
when the check runsat the directiveat the directiveat the next rule parsed

Coraza also installs a built-in phase:2,log,auditlog,pass when none is configured. ModSecurity v2 keeps a single current default action set rather than one per phase, which is what let a phase-less rule inherit its phase (ADR-0017). ModSecurity v2 keeps a single current default action set whose phase a phase-less rule inherits; the other two apply default actions by the rule’s own phase (see ADR-0017).

Decision

A SecDefaultAction MUST contain a phase and exactly one disruptive action, and MUST NOT contain chain, skip, skipAfter, any transformation (including t:none) or the metadata actions id, rev, msg, tag, severity, ver, accuracy, maturity, logdata. At most one SecDefaultAction per phase MAY appear in one configuration context; a second one for the same phase MUST be a configuration error. Violations MUST be reported when the directive is parsed, whether or not a rule follows it. Normative text: spec/03-processing-model.md#default-actions.

Options considered

  • v2 replacement semantics for a repeated phase: one engine against two, and silently changing the default for later rules is a frequent misconfiguration; rejected.
  • Error on a repeated phase (chosen): v3 and Coraza behaviour; CRS complies (one per phase in crs-setup.conf).
  • Allow tag as v2 does: a tag inherited by every rule defeats ctl:ruleRemoveByTag; rejected.

Consequences

  • For ModSecurity v2: reject tag; reject a repeated phase (today it replaces, so default-action-redefined.yaml fails on v2).
  • For libmodsecurity v3: require phase instead of defaulting to 1 (today default-action-no-phase.yaml fails on v3).
  • For Coraza: validate at the directive, not at the next rule (today the two negative tests pass only because a rule follows the directive).
  • For rule authors: SecDefaultAction "phase:N,log,auditlog,pass" as CRS ships it is valid everywhere.

Tests

References