
    `gj&                        d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	m
Z
  ej                  d      ZdaddZdd	Zd
ZdZddZddZddZddZddZddZddd	 	 	 	 	 ddZdddd	 	 	 	 	 	 	 ddZy)u9  Startup security posture audit (warn-on-load, never blocks).

Surfaces dangerous host / deployment posture at process start so operators
get an at-a-glance "you're exposed" signal. Motivated by the June 2026
MCP-config persistence campaign, where compromised boxes ran as root with an
exposed dashboard / API server and no firewall — and nothing ever told the
operator. These checks are advisory: they emit ``logger.warning`` records
and return human-readable strings; they never raise or block startup.

Checks (each is independent and fail-safe — any internal error is swallowed
and simply yields no finding):

1. Running as root (POSIX uid 0).
2. SSH daemon present with password authentication enabled.
3. Running inside a container with no persistent volume mount over the
   HERMES_HOME data dir (state is ephemeral — lost on container restart).
4. A network-accessible gateway listener (dashboard / API server) with no
   authentication configured.

Cross-platform: the root and SSH checks are POSIX-only and no-op on Windows.
Everything is best-effort and read-only.
    )annotationsN)Path)AnyOptionalzhermes.security_auditFc                     t        t        dd      xs t        t        dd      } | y	  |        dk(  S # t        $ r Y yw xY w)zCTrue when the process runs as POSIX uid 0. Always False on Windows.geteuidNgetuidFr   )getattros	Exception)r	   s    T/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/security_audit_startup.py_is_rootr   &   sI    RD)HWR4-HF~x1} s   	3 	??c                     t               sy 	 y)Nu	  Running as ROOT. The agent's terminal/file tools execute with full root privileges — a single prompt-injection or exposed endpoint is a full host compromise. Run Hermes as an unprivileged user (or in a sandboxed terminal backend / container with a non-root user).)r        r   _running_as_rootr   1   s    :	Hr   )z/etc/ssh/sshd_configz/etc/ssh/sshd_config.dc                    g } t         D cg c]  }t        |       }}	 t        t              }|j                         r)|j	                  t        |j                  d                   |D ]_  }	 |j                  dd      j                         D ]8  }|j                         }|s|j                  d      r(| j                  |       : a | S c c}w # t        $ r Y vw xY w# t        $ r Y w xY w)zAYield non-comment lines from sshd_config + its drop-in directory.z*.confutf-8replaceencodingerrors#)_SSHD_CONFIG_PATHSr   _SSHD_CONFIG_DIRis_dirextendsortedglobr   	read_text
splitlinesstrip
startswithappend)linesppathsdrawstrippeds         r   _iter_sshd_config_linesr+   B   s    E*<=Qa=E=!"88:LLx 012  	{{GI{FQQS +99;H$7$7$<LL*+ L >
    		s5   CAC .6C"%C"7C"	CC"	C.-C.c                     t               } | syd}d}| D ]<  }t        j                  d|      }|s|j                  d      j	                         }d}> |dk(  ry|rdnd	}d
| dS )a(  Warn when an SSH daemon has password authentication enabled.

    Password auth on a public SSH daemon is the classic brute-force surface
    and pairs badly with a root-capable agent box. POSIX-only; returns None
    when there's no sshd config to read (e.g. Windows, or SSH not installed).
    NyesFz#(?i)^PasswordAuthentication\s+(\w+)   Tno u$    (default — no explicit directive)z&SSH password authentication is ENABLEDz. Password auth is brute-forceable and dangerous on an internet-facing box. Set 'PasswordAuthentication no' in sshd_config and use key-based auth.)r+   rematchgrouplower)r%   verdictsaw_directivelinem	qualifiers         r   _ssh_password_auth_enabledr:   W   s     $%EGM !HH;TBggaj&&(G M	!
 $#)OI
0 <M 	Mr   c                 
    t         j                  j                  d      ryt         j                  j	                  d      ry	 t        d      j                  dd       t         fd	d
D              ry	 y# t        $ r Y yw xY w)z@Best-effort container detection (Docker / Podman / generic OCI).z/.dockerenvTHERMES_DESKTOP_CHILD_PIDFz/proc/1/cgroupr   r   r   c              3  &   K   | ]  }|v  
 y w)Nr   ).0tokcgroups     r   	<genexpr>z _in_container.<locals>.<genexpr>{   s     Wsf}Ws   )docker
containerdkubepodslibpod)	r   pathexistsenvirongetr   r    anyr   )r@   s   @r   _in_containerrK   s   s~    	ww~~m$	zz~~01&'11791UW(VWW X   s   0A6 6	BBc                   	 | j                         }	 t        d      j                  dd      j	                         }d}d}|D ]{  }|j                         }t        |      dk  r"|d	   |d
   }}	 t        |      }	|	|k(  s|	|j                  v sL|*t        t        |	            t        t        |            kD  sx|	}|}} |y|dvS # t        $ r | }Y w xY w# t        $ r Y yw xY w# t        $ r Y w xY w)a;  True if *path* sits on (or under) a real mount point per /proc/mounts.

    Container overlay/root filesystems are ephemeral; a bind/volume mount over
    the data dir shows up as a distinct mount entry. We treat the path as
    persisted when a mountpoint at or above it is NOT the container root
    overlay.
    z/proc/mountsr   r   r   TNr0      r.      )overlaytmpfsaufs)	resolver   r   r    r!   splitlenparentsstr)
rF   targetmountsbestbest_fstyper7   parts
mountpointfstypemps
             r   _path_is_mountedr_      s   n%///S^^` DK %

u:>"1XuQxF
	j!B <2/|s3r7|c#d)n<$% |:::3      		s4   C *C 0C(CC	C%$C%(	C43C4c                    t               sy | xsF t        t        j                  j	                  dt        j
                  j                  d                  }	 t        |      ry 	 d| dS # t        $ r Y y w xY w)NHERMES_HOMEz	~/.hermesz)Running in a container but the data dir (u   ) is NOT on a persistent volume mount — sessions, memory, skills, and API keys are ephemeral and lost on container restart. Mount a host volume over the HERMES_HOME data directory.)	rK   r   r   rH   rI   rF   
expanduserr_   r   )hermes_homehomes     r   _container_no_volume_mountre      s}    ? $


}bgg&8&8&EFDD! "
 4D6 :& 	&  s   A* *	A65A6c                   g }	 ddl m} | xs i }	 |j                  d      xs i }t	        |t
              r|j                  d      nd}t	        |t
              r|j                  d      r|j                  d      xs i }|j                  d      xs  t        j                  j                  d	d
      }|j                  d      xs  t        j                  j                  dd      } |t        |            r.t        |      j                         s|j                  d| d       |S # t        $ r |cY S w xY w# t        $ r Y |S w xY w)a,  Warn about network-accessible gateway listeners with no auth.

    Covers the API server (no API_SERVER_KEY) and the dashboard (non-loopback
    bind with no auth provider). Read-only against config + env; overlaps the
    hard fail-closed guards but surfaces the posture proactively at startup.
    r   )is_network_accessible	platforms
api_serverNenabledextrahostAPI_SERVER_HOSTz	127.0.0.1keyAPI_SERVER_KEYr0   z4OpenAI-compatible API server is network-accessible (u   ) with NO API_SERVER_KEY. It dispatches terminal-capable agent work — an unauthenticated network endpoint is remote code execution. Set a strong API_SERVER_KEY.)gateway.platforms.baserg   r   rI   
isinstancedictr   rH   rV   r"   r$   )	configfindingsrg   cfgplatsapirk   rl   rn   s	            r   _network_listener_without_authrx      s'    H@ ,BC%+)3E4)@eii%dc4 SWWY%7GGG$*E99V$V

7H+(VD))E"Jbjjnn5Er&JC$SY/C8HJ4& Q> > O/  (  Os#   D& DD7 &D43D47	EErc   rs   c                2   g }t         t        fD ]  }	  |       }|r|j                  |        	 t	        |       }|r|j                  |       	 |j                  t        |             |S # t        $ r Y gw xY w# t        $ r Y 7w xY w# t        $ r Y |S w xY w)zRun all checks and return a list of human-readable warning strings.

    Pure: no logging, no side effects. Each check is independently
    fail-safe. Used directly by tests; the logging wrapper is
    :func:`log_startup_security_warnings`.
    )r   r:   r$   r   re   r   rx   )rc   rs   rt   checkrs        r   run_security_auditr}      s     H" 		A"	&{3OOA6v>? O  		    Os4   A+A: B	 +	A76A7:	BB		BB)rc   rs   forcec                   t         r|sg S da 	 t        | |      }|rTt        j	                  dt        |             t        |d      D ]&  \  }}t        j	                  d|t        |      |       ( |S # t        $ r g cY S w xY w)zRun the audit once per process and emit each finding via logger.warning.

    Returns the findings (also for tests). Never raises. Idempotent unless
    ``force=True`` (used by tests).
    Try   uD   Security posture audit found %d issue(s) — review your deployment:r.   z  [security %d/%d] %s)
_AUDIT_RANr}   r   loggerwarningrT   	enumerate)rc   rs   r~   rt   ifs         r   log_startup_security_warningsr      s     %	J%+fM RM	
 h* 	IDAqNN2As8}aH	IO  	s   A3 3B B)returnbool)r   Optional[str])r   	list[str])rF   r   r   r   )rc   Optional[Path]r   r   )rs   Optional[dict]r   r   )rc   r   rs   r   r   r   )rc   r   rs   r   r~   r   r   r   )__doc__
__future__r   loggingr   r1   pathlibr   typingr   r   	getLoggerr   r   r   r   r   r   r+   r:   rK   r_   re   rx   r}   r   r   r   r   <module>r      s   , #  	 	   			2	3 
  , *8#;L&!J &*D"3AF #'!	  	
 r   