LVgA#tdZddlZddlZddlZddlmZmZddlmZddl m Z m Z m Z m Z ddlmZmZddlmZddlmZmZdd lmZdd lmZejeZeZGd d eZd e de de fdZ!efd e de de fdZ"edZ#de$dzde$dzfdZ%d e de defdZ&d e de defdZ'dede fdZ( d0de)de)de)dzde$dzde$dzd e$dzd!e$dzd"e$dzd#e)dzd$e)dzd%e*dzd&e+fd'Z,d(e*e de*e fd)Z-d*e)fd+Z.d e de$fd,Z/d-e$dzde)fd.Z0de$dzfd/Z1dS)1a0Helper functions for WordPress CVE protection incidents. WordPress incidents are stored in a dedicated wordpress_incident table with plugin-specific data stored in the extra_info JSON field. This module provides helper functions to work with WordPress incidents. Available for both AV and IM360 modes. N) ExitStackcontextmanager) timedelta) CharField FloatField IntegerField TextField) JSONFieldfn)geo)Modelinstance)apply_order_by)OrderBycReZdZdZeddZedZedZe dZ edZ edZ edZ edZedZeddZeddZedZGd d ZdS) WordpressIncidentz WordPress incident model for CVE protection. Uses dedicated wordpress_incident table created in migration 191. Unique constraint on (abuser, name, plugin, rule, severity, domain) allows deduplication similar to the aggregate plugin. T) primary_keynull)r country_id)r column_nameN)rdefaultc$eZdZejZdZdZdS)WordpressIncident.Metawordpress_incident)))abusernamepluginruleseveritydomainTN)__name__ __module__ __qualname__rdbdatabasedb_tableindexes]/opt/imunify360/venv/lib/python3.11/site-packages/defence360agent/model/wordpress_incident.pyMetar8s!;' r)r+)r!r"r#__doc__ridrrrr timestampretriesrrr descriptionrcountryr r extra_infor+r(r)r*rr"s& $T 2 2 2B YD ! ! !F 9$   D %%%Il%%%G|&&&H 9$   D)&&&K YD ! ! !FiT|<<??  M%%j11  ]&&{33  }((77! "  )).99# $  1 12H I I% & =,,->??' (&)).99#%!%%j113   r)c x|dpt|}t||}|dp|d}|tur7t 5}t ||}dddn #1swxYwYnt ||}d|ddt |dd d t|d d |d d||||d|d S)a Build complete incident dict ready for database insertion. This is used for both single incident creation and bulk insertion. Args: incident_data: Dict with incident fields from PHP incident file site_info: Dict with site information (domain, site_path, username, user_id) geo_reader: An open geo Reader (or None) to resolve the abuser country. Pass one from country_reader() when building many incidents in a loop to avoid reopening the mmdb per incident. When omitted, a short-lived reader is opened for this single call. Returns: Dict with all fields ready for Incident.create() or bulk insert message REMOTE_ADDR attacker_ipN wordpressrule_idunknowntsrr;zWordPress CVE: r:Unknownr ) rrr.r/rrr0rr1r r2)r[build_message_fallbackr__UNSETcountry_reader _country_codefloatcalculate_severity)r3r4 geo_readerrar2 abuser_ipreaderr1s r*build_incident_dictrsns& **.D//G"-;;J!!-00M4E4E55IV    7#FI66G 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 I66!!)Y77=,,T15566&}'8'8'@'@AAG-"3"3E9"E"EGG--))   s6BBBc#4Kt5} |tj}nB#t$r5}t d|dVYd}~ddddSd}~wwxYw|VddddS#1swxYwYdS)zYield an open geo Reader, or None when the mmdb can't be opened. Lets bulk callers open the mmap'd reader once instead of per incident, while keeping enrichment non-blocking when the geo bundle is missing. zGeoIP reader unavailable: %sN)r enter_contextr rr Exceptionloggerdebug)stackrrexcs r*rlrls   ((66FF    LL7 = = =JJJ FFF   s7B &:B  A9A4#B 4A99B  BBipc||sdS ||S#t$r'}td||Yd}~dSd}~wwxYw)NzGeoIP lookup failed for %s: %s)get_codervrwrx)rrr{rzs r*rmrmsj ~R~tr"""  5r3???ttttts AA  AcDt||}tjdi|S)aD Create a WordPress incident in the wordpress_incident table. Args: incident_data: Dict with incident fields from PHP incident file site_info: Dict with site information (domain, site_path, username) Returns: WordpressIncident instance with WordPress fields populated in extra_info r()rsrcreate)r3r4 incident_dicts r*create_wordpress_incidentrs*( yAAM  # 4 4m 4 44r)ct||}tjdi|tjtjtjtjtjtj gtj tj dztj |di t }t|dS)ai Insert or update a WordPress incident in the wordpress_incident table. If an incident with the same aggregate key (abuser, name, plugin, rule, severity, domain) exists, increment its retries counter and update timestamp. Otherwise, create a new incident with retries=1. This implements similar deduplication logic as the aggregate plugin. Args: incident_data: Dict with incident fields from PHP incident file site_info: Dict with site information (domain, site_path, username, user_id) Returns: WordpressIncident instance (either newly created or updated) rhr.)conflict_targetupdaterr()rsrinsert on_conflictrrrrrr r/r. returningexecutelist)r3r4rresults r*upsert_wordpress_incidentrs&( yAAM  11=11 !(!&!(!&!*!( ")+<+Dq+H!+];-G   $ % % # * <<?r)incidentc |j|j|j|j|j|j|j|j|j|j |j |j d S)z Convert a WordpressIncident model instance to a dictionary. Args: incident: WordpressIncident model instance Returns: Dictionary representation of the incident r-rrr.r/rrr0rr1r r2r)rs r*wordpress_incident_to_dictrsUk/ '#% +/#/)   r)FlimitoffsetrA by_abuser_ipby_country_code by_domainsearch site_searchsincetoorder_byinclude_hiddenc tttjdk} | sR| tjtjdz} |6| tjtj d|k} |2| tj |} |#| tj |k} |2| tj |} || tj |tj |ztj |ztj |z} |6| tjtj d|k} |6| tjd|k} | 6| tjd| k} | pg} | D]T}t%|t&r(| t+j|?| |Ut/| t| } n1| tj} | |} | |} d| DS)a Get WordPress incidents as dictionaries. Args: limit: Maximum number of incidents to return offset: Offset for pagination user_id: Filter by user ID (None = all) by_abuser_ip: Filter by abuser IP address (None = all) by_country_code: Filter by country code (None = all) by_domain: Filter by domain (None = all) search: Search in IP address, name, description, or domain (None = all) site_search: Filter by site path in extra_info (None = all) since: Filter by timestamp >= this value (unix timestamp, None = all) to: Filter by timestamp <= this value (unix timestamp, None = all) order_by: List of fields to order by (None = default order by timestamp desc). Can be either strings (e.g., ["timestamp+", "severity-"]) or OrderBy objects. Strings are automatically converted. include_hidden: When False (default), exclude incidents whose rule has the TEST- prefix (internal probe rules that the WordPress plugin hides from its admin UI). Returns: List of incident dictionaries rdzTEST-Nz $.user_idz $.site_pathREALc,g|]}t|Sr(r.0incs r* z+get_wordpress_incidents..zs! G G G &s + + G G Gr))rselectwhererris_null startswithr json_extractr2rcontainsr1r rr0r.cast isinstancestrappendr fromstringrrdescrrr)rrrArrrrrrrrrqueryconverted_order_byitems r*get_wordpress_incidentsrsL  $ $%6 7 7 = =  ![ 0  E    " * * , , %00999 :    O-8+ F F     -4==lKKLL" -5HII -4==iHHII   " + +F 3 3+44V<< =&//77 8 &//77 8   O-8- H H      -7<.s! > > > &s + + > > >r))r insert_manyrr)rrs r*bulk_create_wordpress_incidentsr}sV   %%n55 $ % %  ? >v > > >>r)dayscRtjt|z }ttjdktjd|kz }|S)N)rrdr) timer total_secondsrdeleterrr.rr)r cutoff_timedeleteds r*delete_old_wordpress_incidentsrs)++ t 4 4 4 B B D DDK  ""   % 4 *//77+E G      Nr)cdg}|dr||d|dr||d|dr||d|dr||d|dr||dd|S)z=Build message if plugin didn't provide one (per spec format).z IM WP plugin:rer:r=r>r; )r[rjoin)r3partss r*rjrjs  E##/ ]9-...+ ]5)***  , ]6*+++##/ ]9-...  , ]6*+++ 88E??r)r;c&|dkrdS|dkrdSdS)z!Calculate severity based on mode.blockpassr()r;s r*roros# wq qqr)c`|dSt|tr|Stj|S)z>Serialize a value to JSON string if it's not already a string.N)rrjsondumps)values r*rZrZs3 }t% :e  r)) rrNNNNNNNNNF)2r,loggingrr contextlibrrdatetimerpeeweerrrr playhouse.sqlite_extr r defence360agent.internalsr defence360agent.modelr r$defence360agent.model.simplificationr"defence360agent.rpc_tools.validater getLoggerr!rwobjectrkrdictr_rsrlrrmrrrintrboolrrrrjrorZr(r)r*rs 00000000 /.......))))))11111111??????666666  8 $ $         >*D*T*d****\6<///$(/ ////d    cDjS4Z55$(55555"--$(-----`):t6#"& "  dHdH dH dH4ZdH* dH 4Z dH Tz dH $JdHtdH :dH d dHTkdHdHdHdHdHN?J? $Z????0     $3$S4ZC3:r)