@@ -481,11 +481,19 @@ def render_directive_call(
481481 return f"next = engine_bundle::component_signature(next, { rss_string (directive .value )} );"
482482
483483
484- def guard_rule_call ( call : str ) -> str :
484+ def is_detection_paranoia_skip ( directive : Directive ) -> bool :
485485 return (
486- 'if engine_bundle::ctx_get(&next, "blocked") != "1" '
487- '&& engine_bundle::ctx_get(&next, "skip") == "" { '
488- f"{ call } }}"
486+ directive .kind == "SecRule"
487+ and directive .chain_index == 0
488+ and directive .targets .upper () == "TX:DETECTION_PARANOIA_LEVEL"
489+ and directive .operator == "@lt"
490+ and directive .pattern in {"1" , "2" , "3" , "4" }
491+ and not action_values (directive .actions , "t" )
492+ and anomaly_score (directive .actions ) == 0
493+ and not has_action (directive .actions , "chain" )
494+ and not has_action (directive .actions , "deny" )
495+ and directive .message == ""
496+ and action_value (directive .actions , "skipAfter" ) != ""
489497 )
490498
491499
@@ -494,8 +502,49 @@ def render_entry_directive_call(
494502 data_contents : dict [str , str ],
495503 target_updates : dict [int , list [str ]],
496504) -> str :
497- call = render_directive_call (directive , data_contents , target_updates )
498- return guard_rule_call (call ) if directive .kind == "SecRule" else call
505+ if is_detection_paranoia_skip (directive ):
506+ return (
507+ "next = engine_bundle::apply_detection_paranoia_skip("
508+ f"next, { directive .rule_id } , { directive .pattern } , "
509+ f"{ rss_string (action_value (directive .actions , 'skipAfter' ))} );"
510+ )
511+ # apply_rule owns the blocked/skip checks. Repeating them around every
512+ # generated call only adds map lookups and branches to the hot path.
513+ return render_directive_call (directive , data_contents , target_updates )
514+
515+
516+ def render_entry_phase_calls (
517+ directives : list [tuple [Directive , int ]],
518+ markers : list [Directive ],
519+ data_contents : dict [str , str ],
520+ target_updates : dict [int , list [str ]],
521+ ) -> list [str ]:
522+ """Render a phase while collapsing skipAfter no-op call tails."""
523+ lines : list [str ] = []
524+ open_skip_guards = 0
525+ for directive , _ in directives :
526+ lines .append (
527+ " " * open_skip_guards
528+ + render_entry_directive_call (
529+ directive , data_contents , target_updates
530+ )
531+ )
532+ if directive .kind == "SecRule" and action_value (
533+ directive .actions , "skipAfter"
534+ ):
535+ lines .append (
536+ " " * open_skip_guards
537+ + 'if engine_bundle::ctx_get(&next, "skip") == "" {'
538+ )
539+ open_skip_guards += 1
540+ while open_skip_guards > 0 :
541+ open_skip_guards -= 1
542+ lines .append (" " * open_skip_guards + "}" )
543+ lines .extend (
544+ render_directive_call (marker , data_contents , target_updates )
545+ for marker in markers
546+ )
547+ return lines
499548
500549
501550def render_module (
@@ -578,10 +627,10 @@ def render_entry(
578627 if module_name (directive .source ) in enabled_categories :
579628 grouped .setdefault (directive .source , []).append (directive )
580629
581- phase_records : dict [int , list [str ]] = {}
630+ phase_sections : dict [int , list [tuple [ str , list [ str ]] ]] = {}
582631 lines = [
583632 f"// Executable OWASP CRS { version } ruleset." ,
584- "// Default ModSecurity and CRS rules execute from phase rule blobs ." ,
633+ "// Default ModSecurity and CRS rules execute as generated phase-specific calls ." ,
585634 "use engine_bundle;" ,
586635 "" ,
587636 ]
@@ -611,79 +660,31 @@ def render_entry(
611660 phased_directives .setdefault (effective_phase , []).append (
612661 (directive , effective_paranoia )
613662 )
614- def encoded_rule_records (
615- body_directives : list [tuple [Directive , int ]], body_markers : list [Directive ]
616- ) -> list [str ]:
617- field_separator = "\t "
618- record_separator = "\r "
619- rows : list [str ] = []
620- for directive , _ in body_directives :
621- if directive .kind == "SecRule" :
622- arguments = rule_arguments (
623- directive , data_contents , target_updates
624- )
625- text = json .loads (arguments [3 ])
626- fields = [
627- "R" ,
628- category ,
629- arguments [0 ],
630- arguments [1 ],
631- "1" if arguments [2 ] == "true" else "0" ,
632- arguments [4 ],
633- arguments [5 ],
634- arguments [6 ],
635- "1" if arguments [7 ] == "true" else "0" ,
636- arguments [8 ],
637- * text ,
638- ]
639- if any (
640- field_separator in field or record_separator in field
641- for field in fields
642- ):
643- raise ValueError (
644- f"rule { directive .rule_id } contains reserved blob separator"
645- )
646- rows .append (field_separator .join (fields ))
647- for marker in body_markers :
648- fields = ("M" , category , marker .marker )
649- if any (
650- field_separator in field or record_separator in field
651- for field in fields
652- ):
653- raise ValueError (
654- f"marker { marker .marker } contains reserved blob separator"
655- )
656- rows .append (field_separator .join (fields ))
657- return rows
658663
659664 for phase , phase_directives in phased_directives .items ():
660- phase_records . setdefault ( phase , []). extend (
661- encoded_rule_records ( phase_directives , markers )
665+ calls = render_entry_phase_calls (
666+ phase_directives , markers , data_contents , target_updates
662667 )
668+ phase_sections .setdefault (phase , []).append ((category , calls ))
663669
664- def phase_blob (phase : int ) -> str :
665- return "\r " .join (phase_records .get (phase , []))
666-
667- lines .append ("pub fn inspect_request(next: map<string>) -> map<string> {" )
668- for phase in (1 , 2 ):
670+ def append_phase (phase : int ) -> None :
669671 lines .append (f" next = engine_bundle::ctx_set_phase(next, { phase } );" )
670- blob = phase_blob (phase )
671- if blob :
672+ for category , calls in phase_sections .get (phase , []):
672673 lines .append (
673- " next = engine_bundle::apply_rule_blob ("
674- f" next, { rss_string ( blob ) } );"
674+ " if engine_bundle::category_enabled ("
675+ f'& next, " { category } ") {{'
675676 )
677+ lines .extend (f" { call } " for call in calls )
678+ lines .append (" }" )
679+
680+ lines .append ("pub fn inspect_request(next: map<string>) -> map<string> {" )
681+ for phase in (1 , 2 ):
682+ append_phase (phase )
676683 lines .extend ([" next" , "}" , "" ])
677684
678685 lines .append ("pub fn inspect_response(next: map<string>) -> map<string> {" )
679686 for phase in (3 , 4 , 5 ):
680- lines .append (f" next = engine_bundle::ctx_set_phase(next, { phase } );" )
681- blob = phase_blob (phase )
682- if blob :
683- lines .append (
684- " next = engine_bundle::apply_rule_blob("
685- f"next, { rss_string (blob )} );"
686- )
687+ append_phase (phase )
687688 lines .extend (
688689 [
689690 " next" ,
0 commit comments