
    9j&                        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dS ))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_auditFreturnboolc                     t          t          dd          pt          t          dd          } | dS 	  |             dk    S # t          $ r Y dS w xY w)zCTrue when the process runs as POSIX uid 0. Always False on Windows.geteuidNgetuidFr   )getattros	Exception)r   s    G/home/rurouni/.hermes/hermes-agent/hermes_cli/security_audit_startup.py_is_rootr   &   se    RD))HWR4-H-HF~uvxx1}   uus   A   
AAOptional[str]c                 (    t                      sd S 	 dS )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#    :: t	H r   )z/etc/ssh/sshd_configz/etc/ssh/sshd_config.d	list[str]c                    g } d t           D             }	 t          t                    }|                                r5|                    t          |                    d                               n# t          $ r Y nw xY w|D ]}	 |                    dd          	                                D ]B}|
                                }|r*|                    d          s|                     |           Cp# t          $ r Y |w xY w| S )zAYield non-comment lines from sshd_config + its drop-in directory.c                ,    g | ]}t          |          S r   r   ).0ps     r   
<listcomp>z+_iter_sshd_config_lines.<locals>.<listcomp>E   s    ===Qa===r   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pathsdr   rawstrippeds         r   _iter_sshd_config_linesr3   B   s-   E==*<===E!""88:: 	3LLx 0 011222     	{{GI{FFQQSS + +99;; +H$7$7$<$< +LL***+  	 	 	H	Ls%   AA3 3
B ?B A,C55
DDc                     t                      } | sdS d}d}| D ]B}t          j        d|          }|r)|                    d                                          }d}C|dk    rdS |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.)r3   rematchgrouplower)r.   verdictsaw_directivelinem	qualifiers         r   _ssh_password_auth_enabledrB   W   s     $%%E tGM ! !H;TBB 	!ggajj&&((G M$t#O)OI	M 	M 	M 	Mr   c                 2    t           j                            d          rdS t           j                            d          rdS 	 t          d                              dd           t           fd	d
D                       rdS n# t          $ r Y nw xY wdS )z@Best-effort container detection (Docker / Podman / generic OCI).z/.dockerenvTHERMES_DESKTOP_CHILD_PIDFz/proc/1/cgroupr   r   r   c              3      K   | ]}|v V  	d S )Nr   )r   tokcgroups     r   	<genexpr>z _in_container.<locals>.<genexpr>{   s'      WWsf}WWWWWWr   )docker
containerdkubepodslibpod)	r   pathexistsenvirongetr   r)   anyr   )rG   s   @r   _in_containerrR   s   s    	w~~m$$ t	z~~011 u&''11791UUWWWW(VWWWWW 	4	   5s   ?B 
BBrM   r   c                V   	 |                                  }n# t          $ r | }Y nw xY w	 t          d                              dd                                          }n# t          $ r Y dS w xY wd}d}|D ]}|                                }t          |          dk     r*|d	         |d
         }}	 t          |          }	n# t          $ r Y Ww xY w|	|k    s	|	|j        v r@|:t          t          |	                    t          t          |                    k    r|	}|}|dS |dvS )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   TNr8      r6      )overlaytmpfsaufs)	resolver   r   r)   r*   splitlenparentsstr)
rM   targetmountsbestbest_fstyper?   parts
mountpointfstypemps
             r   _path_is_mountedrf      si      n%%///SS^^``   ttDK % %

u::>>"1XuQxF
	j!!BB 	 	 	H	<<2//|s3r77||c#d))nn<<$|t:::s/    &&6A! !
A/.A/3C
CChermes_homeOptional[Path]c                   t                      sd S | pIt          t          j                            dt          j                            d                              }	 t          |          rd S n# t          $ r Y d S w xY wd| dS )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.)	rR   r   r   rO   rP   rM   
expanduserrf   r   )rg   homes     r   _container_no_volume_mountrm      s    ?? t $

}bg&8&8&E&EFF DD!! 	4	   tt	&D 	& 	& 	&s   A0 0
A>=A>configOptional[dict]c                   g }	 ddl m} n# t          $ r |cY S w xY w| pi }	 |                    d          pi }t	          |t
                    r|                    d          nd}t	          |t
                    r|                    d          r|                    d          pi }|                    d          pt          j                            d	d
          }|                    d          pt          j                            dd          } |t          |                    r:t          |          	                                s|
                    d| d           n# t          $ r Y nw xY w|S )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_KEYr8   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.baserq   r   rP   
isinstancedictr   rO   r]   r+   r-   )	rn   findingsrq   cfgplatsapiru   rv   rx   s	            r   _network_listener_without_authr      s    H@@@@@@@    ,BC%%+)3E4)@)@Jeii%%%dc4   
	SWWY%7%7 
	GGG$$*E99V$$V
7H+(V(VD))E""Jbjnn5Er&J&JC$$SYY// C8H8H >4 > > >       Os    E E# #
E0/E0rg   rn   c                h   g }t           t          fD ]4}	  |            }|r|                    |           %# t          $ r Y 1w xY w	 t	          |           }|r|                    |           n# t          $ r Y nw xY w	 |                    t          |                     n# t          $ r Y nw xY w|S )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   rB   r-   r   rm   r&   r   )rg   rn   r}   checkrs        r   run_security_auditr      s     H" 	 		A #""" 	 	 	H	&{33 	OOA   6v>>????   Os3   !6
AA&A. .
A;:A;?"B" "
B/.B/)rg   rn   forcer   c                >   t           r|sg S da 	 t          | |          }n# t          $ r g cY S w xY w|rht                              dt          |                     t          |d          D ]/\  }}t                              d|t          |          |           0|S )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).
    Tr   uD   Security posture audit found %d issue(s) — review your deployment:r6   z  [security %d/%d] %s)
_AUDIT_RANr   r   loggerwarningr[   	enumerate)rg   rn   r   r}   ifs         r   log_startup_security_warningsr      s      % 	J%+fMMM   			 IRMM	
 	
 	
 h** 	I 	IDAqNN2As8}}aHHHHOs   ! 00)r   r	   )r   r   )r   r   )rM   r   r   r	   )rg   rh   r   r   )rn   ro   r   r   )rg   rh   rn   ro   r   r   )rg   rh   rn   ro   r   r	   r   r   )__doc__
__future__r   loggingr   r9   pathlibr   typingr   r   	getLoggerr   r   r   r   r#   r$   r3   rB   rR   rf   rm   r   r   r   r   r   r   <module>r      s   , # " " " " "  				 				                      		2	3	3 
        ,    *   8   #; #; #; #;L   &! ! ! !J &*D     F #'!	       r   