dKjwiddlZddlZddlZddlZddlZddlZddlZddlZddlZddl Z ddl m Z ddl m Z mZmZddlmZddlmZddlmZejdZdZd d d d d dZe GddZGddZdS)N) dataclass)OptionalDictList)drop_user_privileges) socket_utils)run_in_cagefs_if_neededz^[1-9][0-9]*\Zz&/opt/alt/php-xray/cl-smart-advice-user object_cachecdn critical_cssimage_optimizationsite_optimization) OBJECT_CACHECDNCPCSSIMAGE_OPTIMIZATIONSITE_OPTIMIZATIONceZdZUeed<eed<eed<eed<eed<eed<dZe eed<dZ e eed <d Z eed <dS) EnableFeatureTaskuiddomainwp_pathfeature ignore_errorstimeout_timestampN advice_idpidF in_progress) __name__ __module__ __qualname__int__annotations__strlistboolrrrr(py/clwpos/daemon_subscription_handler.pyrr(s HHH KKK LLLMMM $Ix}###C# Kr(rceZdZdZdZdZdZdZdZdZ dZ dd Z e d Z e d Zd Zd ZdZdZdZdZdZddejdejfdZdejdedefdZdejdedefdZdS)PendingSubscriptionWatcherzr Listen to set-suite and user events in order to automatically enable module when suite is purchased. g?Nct|_tj|_|pt jt|_dSN) dict_pending_enable_tasks threadingRLock _tasks_locklogging getLoggerr_logger)selfloggers r)__init__z#PendingSubscriptionWatcher.__init___s?JN"%?,,<!28!z=PendingSubscriptionWatcher._known_features..ts$III!##%%IIIr()%clwpos.optimization_features.featuresr@r?s r)_known_featuresz*PendingSubscriptionWatcher._known_featuresos, TSSSSSII/HIIIIr(c\|sdS tj|ddS#t$rYdSwxYw)z;True if a child pid has been assigned and is still running.FrT)oskillOSError)rs r) _pid_alivez%PendingSubscriptionWatcher._pid_alivevsL 5  GCOOO4   55 s  ++c \|j5t|jD]\}}|D]d}|jt jkr||jr:|jrd|_d|_| |e|s|j ||j dtd|jDt|jddddS#1swxYwYdS)zE Cleanup list of pending tasks by removing outdated. FNzCCleanup of pending tasks. Still active: %d task(s) across %d uid(s)c34K|]}t|VdSr2len)rDtaskss r) zDPendingSubscriptionWatcher._cleanup_pending_tasks..s(PP5CJJPPPPPPr()r7r%r4itemsrtimerMrrremovepopr:infosumvaluesrQ)r;r pending_tasks pending_tasks r)_cleanup_pending_tasksz1PendingSubscriptionWatcher._cleanup_pending_taskss   &*4+E+K+K+M+M&N&N 8 8"]$177L#5DD |'788! $/038 0+/ (!((6666$8.223777 L  UPPD,F,M,M,O,OPPPPPD.//   /                  sD D!!D%(D%c|jdd| t|d|j}|jd|ng#t j$r0}|jd|j|j Yd}~n-d}~wt$r|jdYnwxYw|j dd|dS) Nz Running %s Tchecktimeoutz#Command succeded with output: `%s`zDUnable to activate feature in background. Stdout is %s. Stderr is %sz(Unable to activate feature in backgroundz Finished %s) r:rXjoinr _CHILD_WAIT_TIMEOUT subprocessCalledProcessError exceptionstdoutstderr Exceptiondebug)r;argsoutputes r)_run_and_log_resultsz/PendingSubscriptionWatcher._run_and_log_resultss  ,777 O-TtG_```F L  Df M M M M,    L " "VXYX`bcbj         O O O L " "#M N N N N N O =#((4..99999s2A##C2&B'CCcl ttddt|gd|jj}t j|}n#tj$r|j d|YdStj $r2}|j d||j|j Yd}~dSd}~wttf$r|j d|YdSwxYwt|t r|d d kr|j d ||dS |d d d}n3#t$tf$r|j d|YdSwxYwt&|}||j d|||S)a Resolve advice_id -> the AWP feature it really belongs to via the read-only `cl-smart-advice-user details` query, run in the (already privilege-dropped) caller's context so php-xray's owner check applies. Returns the AWP feature name, or None if the advice cannot be resolved (owner mismatch / unknown id / unparsable response / unknown type). The caller MUST treat None as "deny" - fail closed. details --advice_idTr`zBadvice details query timed out for advice_id=%s; refusing to applyNzHadvice details query failed for advice_id=%s. Stdout is %s. Stderr is %sz@advice details query returned unparsable output for advice_id=%sresultsuccessz! X 77GGL#|;K'K'K !!wN    % %'A7M[^_c_m[n[n&o p p p p p [ X\ / / !!"NPQRRR)KL %2JJ0111))#.... / / LV W W W W Wr(c |jrd}nm|jsd}nc|}tt |j|z}t dt |t|}|j|z|jzS)a Worst-case wall-clock the forked child may legitimately need. The child (``_run_enable_command``) runs one bounded CLI invocation per feature sequentially, each capped at ``_CHILD_WAIT_TIMEOUT`` by its own subprocess timeout. The advice path runs TWO sequential bounded calls - the read-only ``cl-smart-advice-user details`` resolve followed by the ``apply`` - so it is budgeted for two per-CLI windows, not one. For the plain enable path the number of invocations is derived from the count of DISTINCT KNOWN features, clamped to the size of the known-feature set. task.feature is attacker-supplied; although the IPC path now dedups and caps it, deriving the budget from |distinct known features| keeps it capped by a small server constant (_CHILD_WAIT_TIMEOUT * |known_features| + margin) regardless of input. ) rrrHrQsetmaxminrd_CHILD_WAIT_MARGIN)r;r invocationsknown_featuresdistinct_knowns r)_child_runtime_budgetz0PendingSubscriptionWatcher._child_runtime_budgets" > KKK KKK!1133N T\!2!2^!CDDNa^S5H5H!I!IJJK'+58OOOr(cT tj|ddS#t$rYdSwxYw)z?Reap a child pid that is known to have exited, ignoring ECHILD.rN)rJwaitpidrL)r;rs r) _reap_childz&PendingSubscriptionWatcher._reap_child sA  JsA         DD s  ''c tj|tj\}}n#t$rYdSwxYw|rdSt j|krnt j|jk|jd| tj |tj n&#t$r| |YdSwxYwt j|j z}t j|kri tj|tj\}}n#t$rYdSwxYw|rdSt j|jt j|ki|jd| tj |tjn#t$rYnwxYw| |dS)a Wait for a forked child to exit, bounded by ``deadline`` (epoch). Polls with WNOHANG instead of blocking on os.waitpid(pid, 0): a child running the user CLI against an attacker-controlled wp_path (e.g. a FIFO or an unresponsive path) could otherwise hang forever and stall the sequential pending-task loop for every other user. On deadline the child is SIGTERM'd, given a short grace, then SIGKILL'd, and reaped to avoid a zombie. TNzzEPendingSubscriptionWatcher.suite_allowed_callback..s$%[%[%[AJZAZAZaAZAZAZr(zCunable to activate feature in background: feature=%s is not allowedcg|]}|v| Sr'r'rs r)rzEPendingSubscriptionWatcher.suite_allowed_callback..s$'Y'Y'Ya1HXCXCXCXCXCXr(rsrt) r]r7r%r4rTrrMrrJforkrLr:rgrrXrUrrrVcloseclwpos.feature_suitesrrpwdgetpwuidpw_namerr_exitrr(send_dict_to_socket_connection_and_close)r;rrsnapshotrrRrfprr disallowedrEresponsers @r)suite_allowed_callbackz1PendingSubscriptionWatcher.suite_allowed_callbackVs ##%%%  @ @D6<<>>??H @ @ @ @ @ @ @ @ @ @ @ @ @ @ @#W 8W 8JC U O $O $%,,'!4??48+D+D! ,,,,,,,(,D$,,,,,,,,,,,,,,, BBL**[]a]e)11#'+0(111111111111111H2$3 ))*MrRRR#%$(9;;1K1KD1Q1Q#Q,,R::: ))*OQSTTT!-33/4D, $u}} % T 2 2 2333333333333333T-33/4D, $u}} % T 2 2 23333333333333333$(4)//111)//111MMMMMM-S\$(-C-C-K_cmqrrrr+>+>tx+H+H(%[%[%[%[%[%[%[ !+xxA L--.suvwwww'Y'Y'Y'Y4<'Y'Y'Y $~#dl#HQKKK" 007GHHH  ! 8 8$444T=WX[=\4237 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8#I.=>OQYZZZZZs'AAA<#B>+B>>C C C1D8D*  D8*D. .D81D. 2D87D8?A.s(?_?_1A?_?_?_?_?_?_r()defaultrrerrorzfeature list is too long)rscontext,zunknown feature: %srzinvalid advice_id formatrzwp_path is too longz../z1invalid wp_path: must not contain '..' componentsrz'domain and wp_path must be set togetherch|]\}}|Sr'r')rDdrs r)rFzFPendingSubscriptionWatcher.add_pending_upgrade_task..s@@@$!QA@@@r(z"domain does not belong to the userc3K|][}|jkoK|jko@t|jtko|jko|jdkV\dS)rN)rrrrrr)rDexistingrrrequested_featuresrrs r)rSzFPendingSubscriptionWatcher.add_pending_upgrade_task.. s  6)L$/L())S1C-D-DDL&)3L*l?.KK r(rsrtztoo many pending tasksr)rrrrrrrz7Successfully added pending upgrade subscription task %s)clcommon.cpapirrrHrQrrrr%r3fromkeyssplitrr$ _ADVICE_ID_REmatch_MAX_WP_PATH_LENGTHr&r7r]r4any_MAX_PENDING_TASKS_PER_UIDrrr"rU_FEATURE_STATUS_CHECK_TIMEOUTr:rX)r;rrrrrrmax_feature_string_lengthrrEusername user_domainspending duplicaterrrrs ` @@@@r)add_pending_upgrade_taskz3PendingSubscriptionWatcher.add_pending_upgrade_tasksHHHHHHHH--// %($7$73?_?_P^?_?_?_ij;k;k;kno;o$p! |I& ' '*C C C(/OQYZZZZZs?C-L88L<?L<rc(|j5||j|g}t fd|D}dddn #1swxYwYdt |d}t j||dS)Nc3*K|] }|jvVdSr2)r)rDr\rs r)rSzEPendingSubscriptionWatcher.get_upgrade_task_status...s+\\l'\%99\\\\\\r(rt)rsr)r7r]r4rrr&rr)r;rrrr[rrs ` r)get_upgrade_task_statusz2PendingSubscriptionWatcher.get_upgrade_task_status*s   ] ]  ' ' ) ) ) 6::3CCM\\\\m\\\\\G ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] %.$w--HH=>OQYZZZZZsA A  A$'A$r2)rr r!__doc__rrrrrrdrr= staticmethodrHrMr]rorrrrrsocketrr3r"rr$rr'r(r)r+r+Hs %(!!#==== JJ\J \B : : :,,,\"X"X"XHPPP:---^e[e[ e[V\Vce[e[e[e[Nk[&-k[W[k[bek[k[k[k[Z[[S[[^[[[[[[r(r+)ryr8rJrrerrrer5rU dataclassesrtypingrrrclcommon.clpwdrclwposr clwpos.utilsr compilerrxrrr+r'r(r)rs}   !!!!!!''''''''''//////000000 ,-- E#  .-    >i[i[i[i[i[i[i[i[i[i[r(