gP U ddlZddlZddlZddlZddlZddlZddlmZddlm Z m Z ddl m Z ddl mZddlmZddlmZmZmZmZmZddlmZmZdd lmZdd lmZmZm Z m!Z!dd lm"Z"dd l#m$Z$dd l%m&Z&m'Z'ddl(m)Z)m*Z*ddl+m,Z,ddl-m.Z.m/Z/ddl0m1Z1ddl2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z:m;Z;mm?Z?m@Z@mAZAmBZBmCZCmDZDmEZEmFZFmGZGmHZHmIZImJZJmKZKddlLmMZMejNeOZPdZQejRZSdaTdZUdZVdeWfdZXdeWfdZYdeWfdZZde[fdZ\de[deWfdZ]de[deWfdZ^de[deWfd Z_ed!Z`de[d"efd#Zad$edebdzfd%Zcd&Zdd'ebd(ejedebfd)Zfd(ejed"efd*Zgd+ejhfd,Zidhd.ejfd/Zkd0Zld1Zmd2Znde[fd3Zod4Zpd5e*fd6Zqd5e*derfd7Zsd8Ztd9Zu did:eve*fd;Zwde[d?ebddfd@Zyd5e*dAebfdBZzd5e*dCebddfdDZ{dr?r@rArB_get_waf_defaultrH_rDrEcd ttjS#t$r tcYSwxYw)uSRead WORDPRESS.ai_bot_protection from config, defaulting to False. Returns _AI_BOT_PROTECTION_DEFAULT when the config key is missing — e.g. the ai_bot_protection field hasn't rolled out to this install's imunify360 yet, or a sibling package is still on an older schema. Keeps the feature off in all ambiguous cases. )r<rAI_BOT_PROTECTIONr>_AI_BOT_PROTECTION_DEFAULTr@rArB_get_global_ai_bot_protectionrLfs?*I/000 ***))))*rEcj tj}n#t$r tcYSwxYwt |S)uRead WORDPRESS.ai_bot_protection_preset from config, defaulting to "balanced". Two layers of safety: KeyError on a missing key (older schema, agent upgrade in progress) and _validate_preset() on the value itself (hand-edited override file, future preset rolled in via a sibling package this version doesn't recognise). Both fall back to the same canonical default so all layers — schema, agent, plugin — agree. )rAI_BOT_PROTECTION_PRESETr>!_AI_BOT_PROTECTION_PRESET_DEFAULTr)raws rB$_get_global_ai_bot_protection_presetrQtsF10 11100001 C  s ##usernamectsdS tdd|\}}n#t$rtcYSwxYw|tjkrtSt |S)NF WORDPRESS waf_enabledrR)rCr r>rHr ROOTr<)rRvaluesources rB_is_waf_enabled_for_user_syncrZs " $ $u"0    vv """!!!!!" !!! ;;s(AAcpKtj}|dt|d{VS)u3Async wrapper — runs config file I/O in executor.N)asyncioget_running_looprun_in_executorrZ)rRloops rBis_waf_enabled_for_userr`sR  # % %D%% +X      rAcr tdd|\}}n#t$rYdSwxYw|tjkS)NrTrUrVF)r r>r rW)rR_rYs rB$_user_has_explicit_waf_override_syncrcsY,    66 uu X] ""s  &&zD/var/lib/cloudlinux-app-version-detector/components_versions.sqlite3 admin_configc ddlm}|||S#t$r-tdt jdddfcYSwxYw)z Get user-specific schedule configuration with lazy import fallback. Returns default values if imav.malwarelib is not available. r)get_user_schedule_configz@imav.malwarelib not available, returning default schedule config)(imav.malwarelib.plugins.schedule_watcherrf ImportErrorloggerdebugIntervalNONE)rRrdrfs rB_get_user_schedule_configrns &      (',??? &&& N   }aA%%%% &s4A  A indexct|}|dStr|D] \}}d|d< tt jr fd|D}|S)uI Retrieve WordPress rules with ANTIVIRUS_MODE handling and global disable filtering. In ANTIVIRUS_MODE, all rules are set to monitoring mode ("pass"). Globally disabled rules are filtered out entirely — they should not appear in rules.php. Domain-specific disables are handled separately via disabled-rules.php. Args: index: The Index object used to locate the wp-rules.zip file. Returns: The parsed wp-rules data with mode adjusted for ANTIVIRUS_MODE and globally disabled rules removed, or None if rules cannot be loaded. Npassmodec$i|] \}}|v || Sr@r@).0cveparamsglobally_disableds rB z-get_updated_wp_rules_data..s5   V+++ +++rA)rr itemssetrget_global_disabled)ro rules_datarurvrws @rBget_updated_wp_rules_datar}s #5))Jt$%++-- $ $KC#F6NNN>@@AA     )//11   rAcHttjdS)z#Clear all WordPress-related caches.N)r!rclear_get_content_dir_cacher@rArB clear_cachesrs#(***#%%%%%rAry user_infoct|}d|D}|D]Dfd|D}|r1t|t}||E|S)Nci|]}|gSr@r@)rtpaths rBrxzsite_search..s . . .4dB . . .rAc,g|]}||Sr@r@)rtritemmatchers rB zsite_search..s*MMM4t9L9LM$MMMrA)key)r.maxlenappend)ryrr user_sitesresultmatching_sitesmost_specific_siters ` @rB site_searchrs#I..J . .: . . .F44MMMMM:MMM  4!$^!=!=!=  % & - -d 3 3 3 MrAc:Kt||jd{V}|dd}t|j|\}}}}d} |tjkrt ||||} t|j} t| |d} || | fS)N scan_datecP|ddko|d|S)N resource_typefile) startswith)rrs rBz)_get_scan_data_for_user..s-40F:* L # #D ) )rA) r%pw_namegetrnrlrmr r&r) sinkrrd last_scanlast_scan_timeintervalhour day_of_month day_of_weeknext_scan_timemalware_historymalware_by_sites rB_get_scan_data_for_userrs$D)*;<<<<<<<? ::rA semaphorecK|4d{V |d{Vn4#t$r'}td|Yd}~nd}~wwxYwdddd{VdS#1d{VswxYwYdS)NzTelemetry task failed: ) Exceptionrjerror)corores rB_send_telemetry_taskrs+88888888 8JJJJJJJJ 8 8 8 LL6166 7 7 7 7 7 7 7 7 8888888888888888888888888888888s5AA AAAAA A'*A' coroutinescK|sdStj|fd|D} tj|d{VdS#t$r(}td|Yd}~dSd}~wwxYw)zK Process a list of telemetry coroutines with a concurrency limit.s NcTg|]$}tjt|%Sr@)r\ create_taskr)rtrrs rBrz+process_telemetry_tasks..+s?     0yAABB   rAzSome telemetry tasks failed: )r\ Semaphoregatherrrjr)r concurrencytasksrrs @rBprocess_telemetry_tasksr#s !+..I       E :ne$$$$$$$$$$ ::: 8Q88999999999:sA A3 A..A3cpK ttd}|d{Vt|}n3#t$r&}t d|Yd}~dSd}~wwxYw|st ddSt|}||d}t|S)z Load WordPress rules from the index and format them as PHP. Returns: str or None: PHP-formatted rules data, or None if rules could not be loaded. F)integrity_checkNz>Failed to load wp-rules index: %s, skipping rules installationz>L9;;;;;;;;H(--M& 5 5dh'..t4444,1133\ \  U  # S 1 1I(0HH LLE HHHH 2)\ ""#!6h ? ? !FFD3D$????????! C%(%?%E%EEEEEEE%"KK Et%%6**$ +%- %%% 4D)DDDDDDDDD 8mLLLLLLLLL"/555555555 D)))),(>t(D(D"D"D"D"D"D"D"/3|,0g>>>$(#:#:7#C#CD,8 ',, ,-= > >,?L ,22 ) 4)-,8)B(?(?-A)-,3 !" !" !"   % I !CFP KK@G     %    KK+G           LL?      */:: : : : : : : : :)/:: : : : : : : : : :GC;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;C;s9P(;1E2/2E!!E22E69E6cK t||t|d{V|tj|d||jdS#t $r'}td||Yd}~dSd}~wwxYw)a Process the manually deleted plugin for a single site. Args: site: The site to process. now: The current time. sink: The telemetry/event sink. telemetry_coros: The list of telemetry coroutines to add the event to. The process includes: - marking the site as manually deleted in the database - removing plugin data files - sending telemetry for manual removal Nremoved_by_userrz>Failed to process manually deleted plugin for site=%s error=%s) r4r rrrrrrjr)rnowrrrs rBrrs %dC000"$'''''''''   '              L            sAA B %BB freshly_installed_sitescdKg} t|}|r0tj}|D]}t||||d{Vn2#t$r%}td|Yd}~nd}~wwxYw|rt |d{VdSdS#|rt |d{VwwxYw)a> Tidy up sites that have been manually deleted by the user. Args: sink: The telemetry/event sink. freshly_installed_sites: Optional set of sites that were just installed and should be excluded from being marked as manually deleted to avoid race conditions. Nz&Error occurred during site tidy up. %s)r1rrrrjrr)rrrto_mark_as_manually_removedrrrs rBtidy_up_manually_deletedrs>O;&K #' ' # ' )++C3  5#t_ FFF =uEEEEEEEEF  ;)/:: : : : : : : : : : ; ;? ;)/:: : : : : : : : : ;s0AAB A7A2-B2A77BB/rc K|sdSt}td{V}tt}|D]"}||j|#|D]\}} tj|}|j }n3#t$r&} t d|| Yd} ~ Nd} ~ wwxYwt|||d{V\} } } t|} |D]}t||d{Vr t!| | ||| |}t#||d{Vt%|| d{V\#t$r&} t d|| Yd} ~ d} ~ wwxYwdS)Nrrz.Failed to update site data on site=%s error=%s)r r$rrrrryrrrrrjrrr'rr(rr)rrrdrrrrrrRrrrrrrs rBupdate_data_on_sitesr sQ  >>L133333333H %%M--dh&&t,,,,$))++++ U  S))I (HH    LL=    HHHH  *$ <HH H H H H H H    -h77   D+D$77777777  -""#%  ,D)<<<<<<<<<0mDDDDDDDDDD    D ' '++s1=B C #CC AE FE??FfilenamedatacKtj|j}|j}t ||d{V}t |}t ||z ||j|dS)zKEmbed ``data`` as JSON inside a PHP file at ``/``.Nrgid)rrrpw_gidr"r#r))rr!r"rr%r php_contents rB_write_json_php_data_filer(s TX&&I  C/i@@@@@@@@H/55K%8hCrArc8Kt|d|d{VdS)N scan_data.phpr()rrs rBrrs0 #D/9 E EEEEEEEEEErArc8Kt|d|d{VdS)z Write plugin_config.php for a single WordPress site. Separate file from scan_data.php so a config toggle doesn't force rewriting the malware list, and so the mu-plugin hot path loads only what it needs per request. plugin_config.phpNr+)rrs rBrrs3 $D*=} M MMMMMMMMMMrAc"K|sdSd}tt}|D]"}||j|#|D]\}} t j|}|j}n3#t$r&}t d||Yd}~Md}~wwxYwt|} |D]Q} t|| d{V|dz }#t$r&}t d||Yd}~Jd}~wwxYw|S)us Rewrite plugin_config.php on every managed site in one pass. Used by the ConfigUpdate handler that reacts to WORDPRESS.ai_bot_protection toggles. Writes only plugin_config.php — scan_data.php is untouched, so a toggle doesn't churn the (potentially large) malware payload or wait on a scan cycle. No sink is needed: unlike update_data_on_sites we emit no telemetry here — the per-site write loop just needs local file I/O plus the process-level logger for errors. Returns the number of sites successfully updated so the caller can decide whether to advance its cached state. rrNrgz6Failed to update plugin_config.php on site=%s error=%s) rrrrryrrrrrjrr'r) rrrrrrrrRrrs rBupdate_plugin_config_on_sitesr/s qG .9->->M--dh&&t,,,,(..00Z  S))I (HH    LL=    HHHH  .h77   D /mDDDDDDDDD1     L   Ns0A88 B(B##B(?C D %DD  wp_rules_phprfailedcK|j} t||d{V}|dz }t|||j|||t d|jdS#t$rA}||t d|j|Yd}~dSd}~wwxYw)a= Deploy wp-rules to a single WordPress site and track the result. Args: site: WordPress site to deploy to user_info: User information from pwd wp_rules_php: Formatted PHP rules content updated: Set to add site to if successful failed: Set to add site to if failed N rules.phpr$zUpdated wp-rules for site %sz)Failed to update wp-rules for site %s: %s) r&r"r)rrrjrdocrootrr) rrr0rr1r%r rules_pathrs rBupdate_wp_rules_for_siter6Vs"  C 3D)DDDDDDDD + )  $(      D 2DLAAAAA     4 7 L           sA(A55 C?6B;;C make_task task_name fingerprintskip_waf_disabledc Kt}t}tj|5 t j}t t } |D]"} | | j| #g} | D]+\} } tj | }|j }nW#t$rJ}td|t| | |ddd|| D]} || Yd}~rd}~wwxYw|rr t#|d{V}n/#t$r"t$d|d d}YnwxYw|s*t$d |t| | D]:} t+|| d{Vr| || |||;-d }t-d t| |D]&}| |||z}t/j|d did{V't j|z }t$d|t|t||nh#t.j$r,t$d|t|Yn.t$r"}t$d||d}~wwxYwddddS#1swxYwYdS)a6 Run a per-site async deployment over a list of WordPress sites. Groups sites by user, resolves UIDs, then runs tasks concurrently in batches. Args: sites: WordPress sites to deploy to make_task: Callable that creates a coroutine for one site. Signature: (site, user_info, updated_set, failed_set) -> awaitable task_name: Human-readable name for logging and inactivity tracking fingerprint: Sentry fingerprint for user-lookup failures sink: Optional telemetry sink for remove_site_if_missing zwSkipping {task} update for {count} site(s) belonging to user {user} because username retrieval failed. Reason: {reason})rcountuserreasonr wordpress format_argslevel componentr9NBCould not check WAF status for user %s, proceeding with deploymentTexc_infoz-WAF disabled for user %s, skipping %d site(s)rrreturn_exceptionsz@%s deployment complete. Updated: %d, Failed: %d, Duration: %.2fsz-%s deployment was cancelled. Updated %d sitesz-Error occurred during %s deployment. error=%s)rzrrrrrrrrryrrrrrrrr`rjrrrranger\rrr)rr7r8r9r:rrr1 start_timerrrrrrrRrrUmax_concurrentibatchelapseds rB_deploy_to_sitesrNzs0eeG UUF    y ) )SSR J'--M 5 5dh'..t4444E#0#6#6#8#8- N- NZ # S 1 1I(0HH >%.%(__$'&+ %% ("-$/    !+)) 4((((HHHH#&%! +,CH,M,M&M&M&M&M&M&M $+++:$%) ' '+ +'! K$ OO !&NND3D$????????! LL4GV!L!LMMMMN  N1c%jj.99 E Ea!n"445neDtDDDDDDDDDDikkJ.G KK#G F      %    KK?G           LL?      [SSSSSSSSSSSSSSSSSSsKA(I%)CI% DADI%DI%D54I%5)E!I% E!!DI%$K%8K K K (KK  KK!KcKtt}|stddSfd}t ||ddd|d{VdS)zHDeploy pre-formatted wp-rules PHP content to all active WordPress sites.zNo active WordPress sites foundNc*t||||Sr;)r6)rrrr1r0s rBr7z'_deploy_wp_rules_php..make_tasks ' )\7F   rAzwp-ruleszwp-rules-update-skip-userTr8r9r:r)rr2rjrkrN)r0rinstalled_sitesr7s` rB_deploy_wp_rules_phprSsNNN)++O  6777     /     rA is_updatedcKtjstddS|stddStdt |}|stddSt |}||d}t|}t|d{VdS)z Hook that runs when wp-rules files are updated. Extracts wp-rules.yaml from wp-rules.zip and deploys to all active WordPress sites. Args: index: Index object for wp-rules is_updated: Whether files were actually updated zCwordpress security plugin not enabled, skipping wp-rules deploymentNz)wp-rules not updated, skipping deploymentz/Starting wp-rules deployment to WordPress sitesz,No valid wp-rules found, skipping deploymentr) rSECURITY_PLUGIN_ENABLEDrjrr}rrr#rS)rorTrrrr0s rBupdate_wp_rules_on_sitesrWs  ,        ?@@@ KKABBB-e44M  CDDD.e44#L1>>L | , ,,,,,,,,,,rAc`KtjstddStrdatddSt4d{V datdtd{V}|s.td dddd{VdSt|d{Vt sntd dddd{VdS#1d{VswxYwYdS) aN Re-deploy rules.php to all WordPress sites. Used when globally disabled rules change, requiring rules.php to be regenerated with updated rule filtering. Uses a coalescing lock: if a redeployment is already running, the request is merged into the current run rather than starting a duplicate deployment. zEwordpress security plugin not enabled, skipping wp-rules redeploymentNTz5wp-rules redeployment already in progress, coalescingFz6Starting wp-rules redeployment (global disable change)z(Could not load wp-rules for redeploymentz4Re-running wp-rules redeployment (coalesced request)) rrVrjr_redeploy_wp_rules_locklocked_redeploy_wp_rules_pendingrrrS)r0s rBredeploy_wp_rulesr\/sS  ,      %%''%)" KLLL&PPPPPPPP P). & KKH   "3!4!4444444L IJJJPPPPPPPPPPPPPP'|44 4 4 4 4 4 4 4-  KKN O O O! P PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPs/AD8D D'*D'cKtj}|D]} tj|d{V}|dz }|d|jd{VrG|dt j|d{Vt d|j #t$r+}t d|j |Yd}~d}~wwxYwdS)z&Remove rules.php from the given sites.Nr3z(Removed rules.php from %s (WAF disabled)z&Failed to remove rules.php from %s: %s) r\r]rrr^rrremoverjrr4rr)rr_rrr5rs rB_remove_rules_php_from_sitesr_\s/  # % %D    -d33333333H!K/J))$ 0ABBBBBBBB **4JGGGGGGGGG >L    LL8$,           sBB## C-!CCcKtjsdStj} |dt j|d{Vn,#t$rt d|YdSwxYwtd{V}|st ddS|dtd{V}fd|D}|sdSt}t}|D]}t||||d{Vtd|t|t|dS)z?Redeploy rules.php only for sites belonging to a specific user.Nz.User %s not found, skipping WAF rules redeployz)Could not load wp-rules for user redeployc4g|]}|jjk|Sr@rpw_uidrtsrs rBrz.redeploy_wp_rules_for_user..(@@@aey/?&?&?!&?&?&?rAz6Redeployed wp-rules for user %s: %d updated, %d failed)rrVr\r]r^rgetpwnamr>rjrrr2rzr6rr) rRr_r0rrrr1rrs @rBredeploy_wp_rules_for_userrhos  ,  # % %D..tS\8LLLLLLLL  .rfrA) r\r]r^rrgr>rjrr2r_)rRr_rrrs @rBremove_waf_rules_for_userrks  # % %D..tS\8LLLLLLLL  ;X      &&t-@AA A A A A A AE@@@@U@@@J &z 2 2222222222s'A%A)(A)cKtj}|dtd{V}tdt |t|d{VdS)z?Remove rules.php from all installed sites (global WAF disable).Nz7Global WAF disabled, removing rules.php from %d site(s))r\r]r^r2rjrrr_)r_rs rBremove_waf_rules_for_all_sitesrms  # % %D&&t-@AA A A A A A AE KKA E  'u - ----------rAc6KtjsdStrdat ddSt4d{V dats dddd{VdSt}tj d{V}tj }|D]} |dt|d{Vr&|rt!|d{Vnt#|d{VT#t$$r&}t d||Yd}~d}~wwxYwtsnt d dddd{VdS#1d{VswxYwYdS)zMRedeploy/remove rules.php for users without an explicit waf_enabled override.NTz2waf_default change already in progress, coalescingFz2Failed to apply waf_default change for user %s: %sz1Re-running waf_default change (coalesced request))rrV_apply_waf_default_lockrZ_apply_waf_default_pendingrjrrCrHr HostingPanel get_usersr\r]r^rcrhrkrr) new_default usernamesr_rRrs rBapply_waf_default_changerus  ,%%''%)" HIII& M M M M M M M M M). &*,,  M M M M M M M M M M M M M M+,,K+8::DDFFFFFFFFI+--D%  !11< ! !"B8BBBBBBBBBB7AAAAAAAAA NNL .  KKK L L L? M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M MsOF;AF "D.F/-DF E 'EFE  'F FFdomain timestampcptj|d}|t|d}t|S)a| Generate the disabled-rules.php content for a specific domain. Only includes domain-specific disabled rules. Globally disabled rules are handled separately by filtering them out of rules.php. Args: domain: The domain to generate disabled rules for timestamp: Unix timestamp to embed in the file Returns: PHP file content string F)include_global)tsr)rget_domain_disabledsortedr#)rvrwdisabled_rule_idsr"s rBgenerate_disabled_rules_phpr~sN':u)**  D ) . ..rAcJK|j} t||d{V}|dz }t|j|}t |||j|t j|t j |j k | |t d|j dS#t$rA} | |td|j | Yd} ~ dSd} ~ wwxYw)a[ Deploy disabled-rules.php to a single WordPress site and track the result. Args: site: WordPress site to deploy to user_info: User information from pwd timestamp: Unix timestamp for both file content and DB record updated: Set to add site to if successful failed: Set to add site to if failed Ndisabled-rules.phpr$disabled_rules_sync_tsz"Updated disabled-rules for site %sz/Failed to update disabled-rules for site %s: %s)r&r"r~rvr)rrrwherer4executerrjrrr) rrrwrr1r%rdisabled_rules_pathr'rs rBupdate_disabled_rules_for_sitersO"  C 3D)DDDDDDDD&)==1$+yII ) $(     I>>>DD  !T\ 1  '))) D 8$,GGGGG     4 = L           sC C D"!6DD"domainscbKtjstddStdt |rt |}nt }|stddSd}t||ddd| d{VdS) ai Deploy disabled-rules.php to WordPress sites. If domains are specified, only updates sites for those domains. If domains is None, updates all installed sites (e.g., after a global disable/enable). Args: domains: List of domains to update, or None for all sites sink: Optional telemetry sink for remove_site_if_missing zIwordpress security plugin not enabled, skipping disabled-rules deploymentNz5Starting disabled-rules deployment to WordPress sitesz6No WordPress sites found for disabled-rules deploymentcJt||tj||Sr;)rr)rrrr1s rBr7z1update_disabled_rules_on_sites..make_taskCs%- )TY[['6   rAzdisabled-ruleszdisabled-rules-update-skip-userTrQ)rrVrjrrr,r2rN)rrrr7s rBupdate_disabled_rules_on_sitesr s  ,       KKGHHHNNN&.w77#%%  LMMM    "5     rAc Ktdt}t}tjd5 t t}|s(td ddddStt}|D]"}||j  |#g}| D]\}} tj|} n<#t$r/} t!dt#||| dddd Yd} ~ Od} ~ wwxYw|D]@}t%||d{Vrt'|| ||} | | Ad } t)d t#|| D]&} || | | z}t+j|d did{V'tdt#|t#|nf#t*j$r+tdt#|Yn-t$r!} td| d} ~ wwxYwddddS#1swxYwYdS)z7Update auth.php files for all existing WordPress sites.z4Updating auth.php files for existing WordPress siteszwp-auth-updatez"No installed WordPress sites foundNzSkipping auth update for WordPress sites on {count} site(s) because they belong to user {user} and it is not possible to retrieve username for this user. Reason: {reason}r<r=r>rr?zwp-plugin-auth-update-skip-userr@rrrGTz8Updated auth.php files for %d WordPress sites, %d failedzLAuth update for WordPress sites was cancelled. Auth was updated for %d sitesz+Error occurred during auth update. error=%s)rjrrzrrrrr2rrrrryrrrrrrupdate_site_authrHr\rrr)rrr1rRrrrrrrrrrJrKrLs rBupdate_auth_everywhererRs KKFGGGeeG UUF    / 0 0@@?  NNN233O"  @AAA@@@@@@@@(--M' 5 5dh'..t4444E+1133 ' ' U # S 1 1II D &)ZZ$'&+%% ("-$E    HHHH""''D3D$????????! +D)WfMMDLL&&&& ' N1c%jj.99 E Ea!n"445neDtDDDDDDDDDD KKJG F      %    KK(G           LLF N N N  }@@@@@@@@@@@@@@@@@@stI;8H AH5D  H E%D>9H>ECHI;7I+?I; I+ I&&I++I;;I?I?cK t||d{V||dS#t$r<}||td||Yd}~dSd}~wwxYw)z/Process authentication setup for a single site.Nz*Failed to update auth for site=%s error=%s)r7rrrjr)rrrr1rs rBrrs  'i888888888 D     4 8            s+1 A71A22A7c.Ktj|jrdSt |}|dkr&|#t j|d||jd{Vn1t d|tdd|id d d d S)a Checks if the site directory exists. If not, removes the site from the local database and sends a 'site_removed' telemetry event only if deletion is successful. Returns True if the site was removed (directory missing), False otherwise. Parameters: sink: The telemetry/event sink. site: The WPSite object to check and potentially remove. Side effect: If the site is missing and successfully deleted from database, a telemetry event will be sent. FrN site_removedrz@Failed to delete missing site %s from database, no rows affectedz2Failed to delete missing site {site} from databaserrr?zwp-plugin-site-delete-failedr@T) rrisdirr4r+rrrrjrr)rr rows_deleteds rBrrs w}}T\""ut$$La  &$          N     @!6      4rAfile_permissionscK tj|d{V} t|}nC#t$r6}|jtjtjtjfvrYd}~dSd}~wwxYw tj |j dz}|dkrtj |ddD]} t||5}tj |}|j dz|krtj ||dddn #1swxYwYd#t$rYpt$r<}|jtjkr!td||Yd}~d}~wwxYw tj|n#tj|wxYwdS#t$$r'} td || Yd} ~ dSd} ~ wwxYw) z6Fix data file permissions for a single WordPress site.NFii)r*r-zauth.phpr3rrz"Skipping chmod: %s/%s is a symlinkTz.Failed to fix permissions for site=%s error=%s)rrrrrrrrrstatst_modechmodrfstatFileNotFoundErrorrjrrrr) rrrrrcurrent_dir_mode file_namefile_fdstrs rBfix_site_data_file_permissionsrs{1)$//////// )(33FF   yU\5; FFFuuuuu    !wv6> 5(('''   &y@@@@GXg..:-1AAAHW.>???@@@@@@@@@@@@@@@)HyEK//@$% ! 0 HV    BHV    t  <     uuuuu sF/F A/*A*#F)A**A//F3;E:/D8D8 DD D D DE: E!E: E!%1EE:EE!!E:%F:FF GGGc\Kt}t}tjd5 t t }|s ddddSddlm}ddlm }|j |j krdnd}|D]\}t||d{Vrt||d{V}|r| |G| |]tdt!|t!|nj#t"j$r+td t!|Yn1t&$r%} td | Yd} ~ nd} ~ wwxYwddddS#1swxYwYdS) z Fix data file permissions for all WordPress sites with imunify-security plugin installed. Args: sink: The telemetry/event sink zwp-plugin-fix-permissionsNr)rq)Pleski z=Fixed data file permissions for %d WordPress sites, %d failedzOFixing data file permissions was cancelled. Permissions were fixed for %d sitesz1Error occurred during permission fixing. error=%s)rzrrrrr2+defence360agent.subsys.panels.hosting_panelrq#defence360agent.subsys.panels.pleskrNAMErrrrjrrr\rrr) rfixedr1rRrqrrrsuccessrs rB$fix_data_file_permissions_everywherer s EEE UUF    : ; ;00/  NNN233O" 00000000       B A A A A A&, ::  ( % %/d;;;;;;;; >*!!%IIdOOOOJJt$$$$ KKE F     %    KK&E           LLCU         [000000000000000000sNF!D*,B=D*)F!*7F!F!# F,F F! FF!!F%(F%cdeZdZdZdZdZdZdZdddd d d d d ZdZ dZ dZ dZ de ddfdZdZdS)rz Handles installation of imunify-security plugin on WordPress sites. This class processes WordPress sites and installs the imunify-security plugin, including setting up authentication, scan data files, and rules. Tinstalled_by_imunifyzwp-plugin-installationzwp-plugin-install-skip-userz%Installing imunify-security wp pluginz5Installed imunify-security wp plugin on {count} sitesz&Found {count} site(s) for installationz5Failed to install plugin to site={site} error={error}z`Installation of imunify-security wp plugin was cancelled. Plugin was installed for {count} sitesz8Error occurred during plugin installation. error={error}zSkipping installation of WordPress plugin on {count} site(s) because they belong to user {user} and it is not possible to retrieve username for this user. Reason: {reason}startcompletefoundr cancelled exception skip_usercH||_||_t|_t|_t|_t|_t|_t|_d|_ t|_ d|_ dSr;) rrrz processed authenticatedrules_installedfailed_rules_updatesdisabled_rules_installedfailed_disabled_rules_updatesdisabled_rules_ts failed_auth _current_site)selfrrs rB__init__zWordPressSiteInstaller.__init__hsy   UU"uu$'EE!(+%-0UU*/355,0rAcKtj|d{V}|s3td|t dd|idddd Sd S) a Check if site is ready for processing. Override in subclasses to implement different readiness checks. Args: site: The WordPress site to check. Returns: bool: True if the site is ready for processing, False otherwise. Nz6WordPress site is not accessible using WP CLI. site=%sz:WordPress site is not accessible using WP CLI. site={site}rrr?zwp-plugin-cli-not-accessibler@FT)rrrjrr)rrrs rB is_site_readyz$WordPressSiteInstaller.is_site_readyus(+'A$'G'G!G!G!G!G!G!G%  NNH    L#TN%:     5trAc|j|t|h||dS)u Record a successfully processed site and persist it to the database. Each site is inserted immediately so that it is tracked in the DB at all times — even if the overall installation loop is cancelled mid-run. Override in subclasses to implement different recording logic. Args: site: The WordPress site that was processed. version: The plugin version installed on the site. N)rrr3_stamp_disabled_rules_sync_tsrrrs rB_record_processed_sitez-WordPressSiteInstaller._record_processed_sitesD 4   v&&& **400000rAcK|j}d|_ t|d{Vn3#t$r&}td||Yd}~nd}~wwxYw|jrt j|d{VdSdS)aRevert the site that was mid-processing when cancellation occurred. Deletes data files and, if this processor installs plugins, attempts to uninstall the partially-installed plugin. Each step runs independently so one failure doesn't skip the other. Nz5Failed to delete data files for in-flight site %s: %s)rr rrjrinstall_pluginrtry_plugin_uninstall)rrrs rB_revert_in_flight_sitez-WordPressSiteInstaller._revert_in_flight_sites!! %d++ + + + + + + + +    NNG            1*400 0 0 0 0 0 0 0 0 0 1 1s( AAArr9Nc||jvrdS|jdStj|jtj|jkdS)z Stamp disabled_rules_sync_ts for a single site after it has been inserted into the DB. Called from ``_record_processed_site`` so the DB row already exists. Nr)rrrrrr4r)rrs rBrz4WordPressSiteInstaller._stamp_disabled_rules_sync_tsse t4 4 4 F  ! ) F#'#9   % %5 6 6wwyyyyyrAc Kt|jdg}tj|j5 t|js{td|j |rLtj |ddid{V}|D]2}t|trtd|3cdddSt|jdt!|jt#}t%d{V}t'j|_t+d{V}t-t.}|jD]"}||j|#|D]&\} } t7j| } | j} nL#t$r?} t=|jd t!| | | d d d |j Yd} ~ gd} ~ wwxYw tA| d{V}n/#t$r"td| dd}YnwxYw|s)td| t!| tC|j"| |d{V\}}}tG| }| D]}tI|j"|d{Vr |%|d{Vs<||_&tO||| |||}tQ||d{VtS||d{VtU|| |j+|j,d{V|r%|r#t[|| ||j.|j/d{V|r(ta|| |j|j1|j2d{V|j3rtij5|d{Vtij6|d{V}|rtoj8||}|9||d|_&|tj:twj<|j"|j=||#t$rY} d|_&t>|jd|t| Yd} ~  d} ~ wwxYw(t|jdt!|j |j,r-tdt!|j,|j/r-tdt!|j/|j2r-tdt!|j2n#tj@$ro|j&r|Ad{Vt|jdt!|j YnXt$rL} t>|jdt| d} ~ wwxYw|rLtj |ddid{V}|D]2}t|trtd|3nT#|rLtj |ddid{V}|D]3}t|trtd|3wwxYwdddn #1swxYwY|j S)z Process WordPress sites for imunify-security plugin operations. Returns: set: The set of successfully processed sites. rz'No WordPress sites found, nothing to dorGTNzFailed to send telemetry: %sr)r<rrrr?r@rDrEzFWAF disabled for user %s, skipping WAF rules deployment for %d site(s)rrr)rrrzFailed to authenticate %d sitesz&Failed to install wp-rules on %d sitesz,Failed to install disabled-rules on %d sitesrr)r)Brjrmessagesrrrr8rrrr\r isinstancerrformatrr rrrr$rrrrryrrrrlog_fingerprint_skip_userr`rrr'rrrr(rrrrrr6rrrrrrrplugin_installrrrrrrrtelemetry_eventrreprrr)rtelemetry_tasksresultsrrdr0rrrrrrrRrrUrrrrrrs rBrzWordPressSiteInstaller.runs<   DM'*+++   " "4> 2 2I I H z*KK IJJJ>v#$+N(%<@%%G#*%fi88"NN >OI I I I I I I I  M'*11DJ1HH ,~~ &7%8%8888888 *.&!=!?!???????!,D 1 1  J99D!$(+2248888#0"5"5"7"7AAJC!$'L$5$5 #,#4$ ! ! !# M+6),U(+*/)) #,&1(,(F    ! ! +,CH,M,M&M&M&M&M&M&M $+++:$%) ' '+ +' ?$JJ 6 9l &&'%:($C$CM %TT!7 4!H!HHHHHHH%$P)-););D)A)A#A#A#A#A#A#A) (15D.): . . ( $ /)1 )))I#8i"H"HHHHHHHH"; $m###3 $ ) $ 2 $ 0 ## ," "&>$($-$0$($8$($= '"'"!"!"!"!"!"!"!" +"&D$($-$($:$($A$($F '"'"!"!"!"!"!"!"!" $2?&)&8&>&> > > > > > > >-0,B4,H,H&H&H&H&H&H&HG&P'-'@w'O'O!77gFFF15D.+22 ' 3$-$8-1Y.2.B-107 %&%&%&!"!"     )15D."LL $ g 6 = =)-T%[[!>!"!"]Tj M*-443t~;N;N4OO#NN9D,--,NN@D5665NNFD>?? )   %855777777777 M+.55!$.116     M+.55DKK5HH   #$+N(%<@%%G#*%fi88"NN > #$+N(%<@%%G#*%fi88"NN > KI I I I I I I I I I I I I I I V~s]5WA] C1WG.-W. H785H2-W2H77W;IW)I=:W<I==A>W<R WE1R  W S. AS) #W)S. .C,W[?A;Z-[? Z-!AZ((Z--[?0A]?A]]]#&]#)__name__ __module__ __qualname____doc__rrr8rrrrrrrrrr@rArBrrHsN,O(I =8K9H 5 G 7H( 1 1 18111$111( A& AT A A A AUUUUUrArcVeZdZdZdZdZdZdZdddd d d d d ZfdZ dZ fdZ xZ S)rz Handles adoption of existing WordPress sites with imunify-security plugin. Adoption is a special case of installation where the site already has the plugin installed but is not tracked in our database. F site_foundzwp-plugin-adoptionzwp-plugin-adopt-skip-userz#Adopting imunify-security wp pluginz3Adopted imunify-security wp plugin on {count} sitesz"Found {count} site(s) for adoptionz3Failed to adopt plugin to site={site} error={error}zZAdoption of imunify-security wp plugin was cancelled. Plugin was adopted for {count} sitesz4Error occurred during plugin adoption. error={error}zSkipping adoption of WordPress plugin on {count} site(s) because they belong to user {user} and it is not possible to retrieve username for this user. Reason: {reason}rct||dtjtjD|_dS)Nch|] }|j Sr@)r4)rtrs rB z0WordPressSiteAdopter.__init__..s'" " " AI" " " rA)superrrselectr4existing_docroots)rrr __class__s rBrzWordPressSiteAdopter.__init__sR u%%%" " ,3M4IJJ" " " rAc|j||j|jvr1t |t ||rt ||nt|h||dS)a Record a successfully adopted site and persist it immediately. For adoption, sites that already exist in the database (flagged as manually deleted) have their flag cleared. New sites are inserted into the database right away. Args: site: The WordPress site that was processed. version: The plugin version installed on the site. N) rrr4rr*r5r6r3rrs rBrz+WordPressSiteAdopter._record_processed_sites 4   <41 1 1 ' - - -  & & & 3#D'222 "D6 * * * **400000rAcKt|d{VsdStj|d{V}|std|dSdS)z Check if site is ready for adoption. Args: site: The WordPress site to check. Returns: bool: True if the site is ready for adoption, False otherwise. NFz2Plugin not installed on site %s, skipping adoptionT)rrrr rjr)rrrrs rBrz"WordPressSiteAdopter.is_site_readysWW**400000000 5!4T::::::::   NND   5trA) rrrrrrr8rrrrr __classcell__)rs@rBrrsN"O$I ;6I5F 3L 7H$     111.rAr)rr;)FN)r9N)NN)r\rloggingrrr collectionsrcollections.abcrrdistutils.versionrpathlibrdefence360agent.apir defence360agent.contracts.configr rlr r r r defence360agent.filesrrdefence360agent.sentryrdefence360agent.utils.fd_opsrrrrrdefence360agent.subsys.panelsr"defence360agent.wordpress.wp_rulesrrdefence360agent.model.wordpressrr&defence360agent.model.wp_disabled_rulerdefence360agent.wordpressrr#defence360agent.wordpress.constantsrdefence360agent.wordpress.utilsrr r!r"r#r$r%r&r'r(r))defence360agent.wordpress.site_repositoryr*r+r,r-r.r/r0r1r2r3r4r5r6$defence360agent.wordpress.proxy_authr7 getLoggerrrjr?LockrorprKrOr<rCrHrLstrrQrZr`rcCOMPONENTS_DB_PATHrndictr}r struct_passwdrrrrrrrrrrrr intrrrrzrr r(rrr/r6rNrSrWrYr[r\r_rhrkrmrufloatr~rrrrrrrrrr@rArBrs   ######////////************21111111...... 766666777777BAAAAAAAAAAAAA44444444CCCCCC                           KJJJJJ  8 $ $ &',.." #$.!$$$$$$$$$$$ *t * * * *!c!!!!" C D    CD#3#4####TJ &&<&&&&&"U"td{""""J&&& t (9 t     ;& ;6B ; ; ; ;F80A8888::d::::&777B!!! ! ! ! 3    Q;Q;Q;hF>))#))))X";";";J$ $ $ P26;;#&v;;;;;<:DL::::z    '+      FfFFFFFN&NN$NNNN5tF|55555p! !  ! !  !  !  ! ! ! ! V$ nn <n "C-y>n  n  nn nnnnbS0#-%#-T#-d#-#-#-#-L'',..#*P*P*P*PZd6lt&$s$t$$$$N 3c 3d 3 3 3 3 ....,M,M,M,M^///#////0% %  % %  %  %  % % % % R!% // #Y / ////dGGGGT    &V&&&&&R5 5$'5 5555p:::zSSSSSSSSl RRRRR1RRRRRrA