%W&P3dZddlmZddlZddlZddlmZmZmZm Z m Z m Z ddl m Z mZejeZGdde ZdS)aWordPress-specific disabled rules data model. This module provides a separate data model for WordPress disabled rules, independent of the existing DisabledRule/DisabledRuleDomain models used by modsec/ossec plugins. Disable Behavior: Global and domain-level disables are independent and can coexist. A rule is considered effectively disabled for a given WordPress domain if EITHER of these conditions is true: - A global disable exists for the rule (applies to all domains) - A domain-specific disable exists for the rule and that domain Enabling a rule at one scope does not affect disables at the other scope. For example, removing a global disable leaves any domain-specific disables intact, and vice versa. )IteratorN) CharField FloatField IntegerFieldIntegrityErrorPrimaryKeyFieldfn)Modelinstancec eZdZdZGddZeZedZedZ edZ e dZ edZ edZdZdZd Zd Ze d$d ed eed zdededed zdef dZed ededededef dZed ed eededededef dZed ededed zdedededefdZed ed eed zdefdZed$d eded zdefdZe d%dededeefdZ ede!efdZ"edeed zdefdZ#e d&d!ed"edeed zdede$eee%ff d#Z&d S)'WPDisabledRulezStores disabled WordPress protection rules. Uses a scope-based design: - scope='global', scope_value=NULL: Rule disabled for all domains (root only) - scope='domain', scope_value='example.com': Rule disabled for specific domain c$eZdZejZdZdZdS)WPDisabledRule.Metawp_disabled_rules)))rule_idscope scope_valueTN)__name__ __module__ __qualname__r dbdatabasedb_tableindexes[/opt/imunify360/venv/lib/python3.11/site-packages/defence360agent/model/wp_disabled_rule.pyMetar-s;&@rrF)nullTglobaldomain wordpressagentNrdomainssourceuser_id timestampreturnc|tj}|r||||||S|||||S)a> Disable a rule globally or for specific domains. Args: rule_id: The rule identifier (e.g., "CVE-2025-001") domains: List of domains to disable for, or None/empty for global disable source: Origin of the action ("wordpress" or "agent") user_id: UID of the user performing the action (0 for root) timestamp: Unix timestamp for when the rule was disabled. If None, uses current time. Returns: Number of new entries created (0 if all were no-ops). )time_disable_for_domains_disable_globally)clsrr$r%r&r's rstorezWPDisabledRule.storeHs\.   I  ++)VW $$WiIIIrc|||jd|||}|rtd|||t |S)zADisable a rule globally (independent of domain-specific entries).Nrrr disabled_atr%r&z1Disabled rule %s globally (source=%s, user_id=%s))_create_if_not_exists SCOPE_GLOBALloggerdebugint)r-rr'r%r&createds rr,z WPDisabledRule._disable_globallyhsm++"! ,     LLC     7||rc d}|D]G}|||j||||}|r#|dz }td||||H|S)zBDisable a rule for specific domains (independent of global state).rr0z6Disabled rule %s for domain %s (source=%s, user_id=%s))r2 SCOPE_DOMAINr4r5) r-rr$r'r%r&countr!r7s rr+z#WPDisabledRule._disable_for_domainss  F//&"% 0G   L  rrrr1c |||||||dS#t$rYdSwxYw)z Create a new disabled rule entry if it doesn't already exist. Returns: True if a new entry was created, False if it already existed (no-op) )rrrr1r%created_by_user_idTF)insertexecuter)r-rrrr1r%r&s rr2z$WPDisabledRule._create_if_not_existssc  JJ''#*   giii4   55 s -1 ??c|so||j|k|j|jk}|rt d|n||j|k|j|jk|j |}|rt d|||S)a Re-enable a rule globally or for specific domains. Args: rule_id: The rule identifier domains: List of domains to enable for, or None/empty to enable globally Returns: Number of rows deleted zEnabled rule %s globallyz Enabled rule %s for %d domain(s)) deletewhererrr3r?r4r5r:rin_)r-rr$r;s rremovezWPDisabledRule.removes  K7*I!11  B 7AAA K7*I!11O''00    6  rc|Q||j|k|j|jkS||j|k|j|jk|j|jk|j|kzzS)a" Check if a rule is disabled globally or for a specific domain. Args: rule_id: The rule identifier domain: The domain to check. If None, only checks global disable. Returns: True if the rule is disabled, False otherwise )selectrBrrr3existsr:r)r-rr!s ris_rule_disabledzWPDisabledRule.is_rule_disableds > K7*I!11   JJLL U w&Y#"22c&66?f46  VXX rinclude_globalc~|rk||j|j|jk|j|jk|j|kzz}nE||j|j|jk|j|k}d|DS)a Get all rule IDs that are disabled for a specific domain. Args: domain: The domain to get disabled rules for include_global: If True, also include globally disabled rules. If False (default), only return domain-specific disables. Returns: List of rule IDs that are disabled for the domain cg|] }|j Srr.0rows r z6WPDisabledRule.get_domain_disabled../s--- ---r)rFrrBrr3r:rdistinct)r-r!rIquerys rget_domain_disabledz"WPDisabledRule.get_domain_disableds   3;''Y#"22c&66?f46 EJJs{++11 S--6)E.-u----rc||j|j|jk}d|DS)z Get all rule IDs that are disabled globally. Returns: Iterator of globally disabled rule IDs c3$K|] }|jV dSNrLrMs r z5WPDisabledRule.get_global_disabled..:s$-- ------r)rFrrBrr3)r-rRs rget_global_disabledz"WPDisabledRule.get_global_disabled1sC 3;''--ci3;K.KLL--u----r user_domainsc|B|j|jk|j|z}|r|j|jk|zS|S|s|j|jkSdS)z Build the WHERE condition for filtering rules. Returns: A Peewee expression for the WHERE clause, or None if no filter needed. N)rr:rrCr3)r-rYrI domain_matchs r_build_filter_conditionz&WPDisabledRule._build_filter_condition<st  #I)99##L11L F S%55EE  19 00 0trrlimitoffsetc|||}||j|jt j|j}|| |}| }d| | |D}|s|gfS| |j |} || |} i} | D]z} | j| vr| jdgd| | j<| j|jkrd| | jd<?| j|jkr+| | jd| j{g} |D]7} | | }t'|d|d<| |8|| fS)a> List disabled rules with aggregation by rule_id. Multiple domain entries for the same rule are aggregated into a single result with a list of domains. Results are ordered by most recently disabled first (using the latest disabled_at timestamp per rule_id). Uses a two-pass approach for efficiency: 1. First pass: Get rule_ids ordered by latest disabled_at with pagination 2. Second pass: Fetch only rows for the paginated rule_ids Args: limit: Maximum number of rules to return offset: Number of rules to skip user_domains: If provided, only return rules for these domains. If None, return all rules (for root users). include_global: Whether to include global rules in the result Returns: Tuple of (total_count, list of rule dicts) Each dict has: {"rule_id": str, "is_global": bool, "domains": list[str]} is_global is True if rule has a global disable, domains lists domain-specific disables Ncg|] }|j SrrLrMs rrPz(WPDisabledRule.fetch..s'   CK   rF)r is_globalr$Trar$)r\rFrgroup_byorder_byr MAXr1descrBr;r^r]rCrr3r:appendrsorted)r-r]r^rYrI conditionrule_ids_query total_countpaginated_rule_ids rows_query rules_by_idrOresultr rule_datas rfetchzWPDisabledRule.fetchSs@// nMM  JJs{ # # Xck " " XbfS_--2244 5 5   +11)<#?#?Ii MM) $ $ $ $F""rrV)F)rNF)'rrr__doc__rridrrrrrr1r%rr=r3r:SOURCE_WORDPRESS SOURCE_AGENT classmethodstrlistr6floatr.r,r+boolr2rDrHrSrrXr\tupledictrprrrr r %sAAAAAAAA   BiU###G I5 ! ! !E)&&&K*%(((K YE " " "F%5111LL#L#' JJJcT!J J  J 4< J JJJ[J>    [2c      [<4Z      [:(S(49t+;(((([(T" " s" C$J" $" " " [" H16...*.. c...[.B.HSM...[.3i$&[,)-$ O#O#O#O#3i$& O#  O# sDJ  O#O#O#[O#O#O#rr )rqcollections.abcrloggingr*peeweerrrrrr defence360agent.modelr r getLoggerrr4r rrrrs$%$$$$$ 21111111  8 $ $~#~#~#~#~#U~#~#~#~#~#r