
    6j"                        d Z ddlmZ ddlZddlZddlZddlZddlm	Z	  ej
        e          ZdZdZdefdeffZh d	Zh d
Zd&d'dZd(dZd)dZedfd*dZd+d,d!Zd+d,d"Zd-d#Zd.d$Zd+d,d%ZdS )/a{  Safe server-side probe for the official Hermes Agent dashboard.

The official `hermes dashboard` binds to 127.0.0.1:9119 by default and exposes
GET /api/status as a public, read-only identity/status endpoint.  Keep all
probing server-side to avoid browser CORS/mixed-content failures, and only allow
loopback targets so a user-controlled setting cannot become an SSRF primitive.
    )annotationsN)urlparsei#  g      ?	127.0.0.1	localhost>   autoneveralways>   ::1r   r   httphoststrportintschemereturnc                X    d| v r|                      d          sd|  dn| }| d| d| S )N:[]z://)
startswith)r   r   r   display_hosts       )/root/hermes-webui/api/dashboard_probe.py	_base_urlr      sH    "%++dooc6J6J+;t;;;;PTL.......    raw_url
str | None tuple[str, int, str, str] | Nonec                   t          | pd                                          }|sdS t          |          }|j        dvrt	          d          |j        s|j        rt	          d          |j        pd}|                                                                }|t          vrt	          d          	 |j
        }n"# t          $ r}t	          d          |d}~ww xY wt          |t                    rd|cxk    rd	k    sn t	          d          |j        pd}|d
vs|j        s|j        s|j        rt	          d          t#          |||j                  }|||j        |fS )a]  Return (host, port, scheme, base_url) for a safe loopback dashboard URL.

    Overrides intentionally accept only scheme + loopback host + explicit port.
    Paths, query strings, fragments, and credentials are rejected: the probe
    appends the official `/api/status` fingerprint itself and must not become an
    arbitrary local URL fetcher.
     N>   r   httpszinvalid dashboard URL schemez!invalid dashboard URL credentialszinvalid dashboard URL hostzinvalid dashboard URL port     )r   /zinvalid dashboard URL path)r   stripr   r   
ValueErrorusernamepasswordhostnamelower_LOOPBACK_HOSTSr   
isinstancer   pathparamsqueryfragmentr   )	r   rawparsedr   normalized_hostr   excr,   bases	            r   normalize_dashboard_urlr5      s    gm


"
"
$
$C tc]]F}---7888 >&/ ><===? bDjjll((**Oo--5666@{ @ @ @566C?@dC   7d););););e););););5666;"D95666_dFM::DD&-55s   4B< <
CCCpayloadobjectboolc                     t           t                    sdS                      d          }t          |t                    r|                                sdS t           fddD                       S )NFversionc              3      K   | ]}|v V  	d S N ).0keyr6   s     r   	<genexpr>z1_looks_like_official_dashboard.<locals>.<genexpr>J   s'      kk#sg~kkkkkkr   )release_datehermes_homeconfig_pathgateway_running)r+   dictgetr   r$   any)r6   r:   s   ` r   _looks_like_official_dashboardrH   @   sw    gt$$ ukk)$$Ggs## 7==?? u
 kkkk)jkkkkkkr   timeoutfloatrE   c                   	 t          | pd                                                                          }|t          vrt	          d          t          |          }d|cxk    rdk    sn t	          d          |dvrt	          d          t          |||          }t          j        	                    | dd	d
d          }t          j        
                    ||          5 }t          |dd          dk    rddicddd           S t          j        |                                                    d                    }ddd           n# 1 swxY w Y   t!          |          sddiS d|||d}	|                    d          }
t%          |
t                     r+|
                                r|
                                |	d<   |	S # t&          $ r# t(                              dd           ddicY S w xY w)zBBest-effort check that `hermes dashboard` is running on host:port.r   z%dashboard probe host must be loopbackr!   r"   z!dashboard probe port out of range>   r   r    z,dashboard probe scheme must be http or httpsz/api/statuszapplication/jsonzhermes-webui-dashboard-probe)Acceptz
User-Agent)headers)rI   statusN   runningFzutf-8T)rP   r   r   urlr:   z&official Hermes dashboard probe failed)exc_info)r   r$   r)   r*   r%   r   r   urllibrequestRequesturlopengetattrjsonloadsreaddecoderH   rF   r+   	Exceptionloggerdebug)r   r   rI   r   r2   r4   rT   responser6   resultr:   s              r   probe_official_dashboardra   M   s   "djb////117799/11DEEE4yyT""""U""""@AAA***KLLL$77.((   1A_`` ) 
 
 ^##GW#== 	Bx400C77!5)	B 	B 	B 	B 	B 	B 	B 	B j!7!7!@!@AAG	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B .g66 	&u%%!?DQUVV++i((gs## 	0 	0 'F9 " " "=MMM5!!!!"sI   C&G (EG 9EG EG EG 1AG *G<;G<config_datadict | Nonec                4   | $	 ddl m}  |            } n# t          $ r i } Y nw xY wt          | t                    r|                     di           ni }t          |t                    r|                    di           ni }t          |t                    r|ni S )Nr   )
get_configwebui	dashboard)
api.configre   r\   r+   rE   rF   )rb   re   	webui_cfgdashboard_cfgs       r   _dashboard_configrk   r   s    	------$*,,KK 	 	 	KKK	0:;0M0MU,,,SUI6@D6Q6QYIMM+r222WYM&}d;;C==Cs    $$c                j   t          |           }t          |                    dd          pd                                                                          }|t
          vrd}t          |                    d          pd                                          }|rt          |          \  }}}}||dS )uF   Return normalized profile config for the Settings → System controls.enabledr   rQ   r   rm   rQ   )rk   r   rF   r$   r)   _DASHBOARD_ENABLED_VALUESr5   )rb   rj   rm   r   _host_port_schemes          r   get_dashboard_configrs      s    %k22M-##Iv66@&AAGGIIOOQQG///-##E**0b117799G J)@)I)I&ugww///r   c                0   t          | pi                     dd          pd                                                                          }|t          vrt          d          t          | pi                     dd          pd                                          }d}|rt          |          \  }}}}ddlm} |	                                }|
                    |          }	|	                    d          }
t          |
t                    si }
|
|	d<   |
                    d	          }t          |t                    si }||
d	<   ||d<   |r||d<   n|                    dd
           |                    ||	           |                                 ||dS )zEPersist dashboard link settings under webui.dashboard in config.yaml.rm   r   zinvalid dashboard enabled moderQ   r   r   )configrf   rg   Nrn   )r   rF   r$   r)   ro   r%   r5   apiru   _get_config_path_load_yaml_config_filer+   rE   pop_save_yaml_config_filereload_config)r6   rm   r   normalized_urlrp   rq   rr   webui_configrC   rb   webui_sectiondashboard_sections               r   save_dashboard_configr      s   7=b%%i88BFCCIIKKQQSSG///9:::7=b%%eR006B77==??GN Q0G0P0P-ug~******//11K55kBBKOOG,,MmT** -,G%))+66'.. 7%6k"#*i  +#1%  eT***''[AAA   ~666r   c                    t          t          j                            d          pd                                                                          } |                     dd                              dd          }|t          v S )NHERMES_WEBUI_HOSTr   r   r   r   )r   osenvironrF   r$   r)   replacer*   )raw_hostr   s     r   "_webui_bind_host_allows_auto_prober      sk    2:>>"566E+FFLLNNTTVVHC$$,,S"55D?""r   c                   t          |           }t          |                    dd          pd                                                                          }|t
          vrd}|dk    rdddS |                    d          p|                    d          pd}	 t          |          }n# t          $ r	 d|d	d
cY S w xY w|r|g}nd t          D             }|dk    r|d         \  }}}}	d||||	dS t                      sd|dS |D ]=\  }}}}
t          ||t          |          }|                    d          r	||d<   |c S >d|dS )zEReturn the safe status payload consumed by GET /api/dashboard/status.rm   r   r   F)rP   rm   rQ   targetr   zinvalid dashboard url)rP   rm   errorc           	     <    g | ]\  }}||d t          ||          fS r   )r   )r>   r   r   s      r   
<listcomp>z(get_dashboard_status.<locals>.<listcomp>   s/    kkk:4D$	$(=(=>kkkr   r	   r   T)rP   rm   r   r   rQ   )rI   r   rP   )rk   r   rF   r$   r)   ro   r5   r%   DEFAULT_DASHBOARD_TARGETSr   ra   DEFAULT_DASHBOARD_TIMEOUT)rb   rj   rm   r   overridetargetsr   r   r   r4   _baser`   s               r   get_dashboard_statusr      s   %k22M-##Iv66@&AAGGIIOOQQG///' W555&&K-*;*;H*E*EKGX*733 X X X W?VWWWWWX  l*kkQjkkk(#*1: dFDGT4X\]]]-// 6 W555%,  !dFE)$>W`fggg::i   	 'F9MMM	 111s   B, ,B?>B?r   )r   r   r   r   r   r   r   r   )r   r   r   r   )r6   r7   r   r8   )
r   r   r   r   rI   rJ   r   r   r   rE   r<   )rb   rc   r   rE   )r6   rE   r   rE   )r   r8   )__doc__
__future__r   rX   loggingr   urllib.requestrS   urllib.parser   	getLogger__name__r]   DEFAULT_DASHBOARD_PORTr   r   ro   r*   r   r5   rH   ra   rk   rs   r   r   r   r=   r   r   <module>r      s    # " " " " "   				     ! ! ! ! ! !		8	$	$  )+AB[RhDij 777 333/ / / / /
6 6 6 6B
l 
l 
l 
l  /	"" "" "" "" ""J
D 
D 
D 
D 
D
0 
0 
0 
0 
07 7 7 7@# # # #!2 !2 !2 !2 !2 !2 !2r   