
    Pin                    n   d Z ddlmZ ddlmZmZ ddlmZ ddlm	Z	 ddl
mZmZ ddlmZmZ ddlmZmZmZ dd	lmZmZ  eh d
          Ze G d d                      Zddd2dZddd3dZddd3dZddd4dZddd5dZd6d!Zd7d"Z d8d$Z!d9d&Z"ddd4d'Z#ddd4d(Z$ddd:d*Z%d;d,Z&d<d.Z'd=d0Z(d1S )>z
Centralized AST analyzer for Dippy.

Single recursive walk of bash AST with consistent decision-making.
Unknown constructs default to ask. Decisions bubble up (deny > ask > allow).
    )annotations)	dataclassfield)Path)Literal)Configmatch_redirect)SIMPLE_SAFEWRAPPER_COMMANDS)get_handlerget_descriptionHandlerContext)parse
ParseError>   	/dev/null
/dev/stdin/dev/stdout-c                  R    e Zd ZU dZded<   ded<    ee          Zded<   dd
ZdS )Decisionz Result of analyzing an AST node.zLiteral['allow', 'ask', 'deny']actionstrreason)default_factoryzlist['Decision']childrenreturnc                (    d| j         d| j        dS )Nz	Decision(, )r   r   )selfs    4/root/projects/gits/Dippy/src/dippy/core/analyzer.py__repr__zDecision.__repr__    s    <4;<<DK<<<<    Nr   r   )	__name__
__module____qualname____doc____annotations__r   listr   r#    r$   r"   r   r      sc         **++++KKK!&t!<!<!<H<<<<= = = = = =r$   r   Fremotecommandr   configr   cwdr   r.   boolr   c               6   |                                  } | st          dd          S 	 t          |           }n/# t          $ r"}t          dd|j                   cY d}~S d}~ww xY w|st          dd          S fd|D             }t          |          S )a  
    Analyze a bash command string.

    Parses the command and recursively analyzes all nodes.
    Returns the combined decision (most restrictive wins).

    Args:
        command: Bash command string to analyze.
        config: Configuration with rules.
        cwd: Current working directory for path resolution.
        remote: If True, command runs in remote context (container, ssh).
                Skips path-based checks since paths are remote, not local.
    askempty commandzparse error: Nc                6    g | ]}t          |           S r-   _analyze_node).0noder0   r1   r.   s     r"   
<listcomp>zanalyze.<locals>.<listcomp>@   s*    SSSTtVS@@@SSSr$   )stripr   r   r   message_combine)r/   r0   r1   r.   nodese	decisionss    ```   r"   analyzerC   $   s      mmooG 0///<g < < <:qy::;;;;;;;;<  0///SSSSSSUSSSIIs   ; 
A'A"A'"A'c          	     `   t          | dd          }|dk    rt          |           S |dk    rbfd| j        D             }t          |          }|j        dk    r1d |D             }t          dd	                    |          |
          S |S |dk    rd | j        D             }|r)s't          |d                   }	|	rt          |	          fd|D             }t          |          }|j        dk    r1d |D             }t          dd	                    |          |
          S |S |dk    rt          | j                  g}|                    t          | j                             t          | d          r2| j        r+|                    t          | j                             |                    t#          |                      t          |          S |dv ret          | j                  t          | j                  g}|                    t#          |                      t          |          S |dk    rt          | j                  g}t          | dg           D ](}
|                    t'          |
                     )|                    t#          |                      t          |          S |dk    rt          | j                  g}| j        | j        | j        fD ]*}|r&|                    t/          |                     +|                    t#          |                      t          |          S |dk    rt          | j                  g}t          | dg           D ](}
|                    t'          |
                     )|                    t#          |                      t          |          S |dk    rg }t          | d          r2| j        r+|                    t'          | j                             | j        D ]D}t          |d          r2|j        r+|                    t          |j                             E|                    t#          |                      |rt          |          nt          dd          S |dk    rt          | j                  S |dk    rNt          | j                  g}|                    t#          |                      t          |          S |dk    rNt          | j                  g}|                    t#          |                      t          |          S |dk    rt          | j                  S |dk    rt          | j                  S |d k    rt          | j                  S |d!k    rg }t          | d          r2| j        r+|                    t9          | j                             |                    t#          |                      |rt          |          nt          dd"          S |d#k    rg }t;          | j                  D ]n}t          |j                  }|j        dk    r4|                    t          |j        d$|j         |g
                     Y|                    |           o|                    t#          |                      |rt          |          nt          dd%          S |d&k    rt          dd&          S |d'k    rt          dd'          S t          d(d)|           S )*z&Recursively analyze a single AST node.kindNr/   r-   pipelinec                6    g | ]}t          |           S r7   r8   )r:   cmdr0   r1   r.   s     r"   r<   z!_analyze_node.<locals>.<listcomp>M   s7     
 
 
?BM#vs6:::
 
 
r$   allowc                    g | ]	}|j         
S r,   r   r:   ds     r"   r<   z!_analyze_node.<locals>.<listcomp>R       333Aqx333r$   r   r   r+   c                <    g | ]}t          |d d          dk    |S )rE   Noperator)getattr)r:   ps     r"   r<   z!_analyze_node.<locals>.<listcomp>X   s.    QQQq'!VT*B*Bj*P*P*P*P*Pr$   r   c                6    g | ]}t          |           S r7   r8   )r:   rS   r0   effective_cwdr.   s     r"   r<   z!_analyze_node.<locals>.<listcomp>_   s7     
 
 
GHM!V]6BBB
 
 
r$   c                    g | ]	}|j         
S r,   rK   rL   s     r"   r<   z!_analyze_node.<locals>.<listcomp>d   rN   r$   if	else_body)whileuntilforwordsz	for-arithselectcasewordbodyz
empty casefunctionsubshellzbrace-grouptimenegationcoprocz	cond-exprconditionalz	arith-cmdzarithmetic cmdsub: 
arithmeticcommentemptyr4   zunrecognized construct: ) rR   _analyze_commandcommandsr?   r   r   joinparts_extract_cd_target_resolve_cd_targetr9   	conditionappend	then_bodyhasattrrX   extend_analyze_redirectsr`   _analyze_word_partsinitcondincr_analyze_string_cmdsubsr_   patternsrF   r/   _analyze_cond_node_find_cmdsubs_in_arith
expressionr   )r;   r0   r1   r.   rE   rB   resultreasonsrm   	cd_targetr_   exprpatterncmdsubinner_decisionrU   s    ```           @r"   r9   r9   D   s   4&&Dyfc&AAAA			
 
 
 
 
 
FJm
 
 
	 )$$=G##33333GGTYYw%7%7)LLLL	QQDJQQQ 	C 	C*5844I C 29c B B
 
 
 
 
 
LQ
 
 
	 )$$=G##33333GGTYYw%7%7)LLLL	"4>63vNNNO	t~vs6RRRSSS4%% 	X$. 	X]4>63vVVVWWW+D&#fMMMNNN	"""	#	#	#$.&#fEEE$)VS@@@
	 	+D&#fMMMNNN	"""	"49fc&IIIJ	D'2.. 	T 	TD0vs6RRRSSSS+D&#fMMMNNN	"""			"49fc&IIIJ	Y	495 	 	D   +D&#fMMM   	+D&#fMMMNNN	"""			"49fc&IIIJ	D'2.. 	T 	TD0vs6RRRSSSS+D&#fMMMNNN	"""		4   	YTY 	Y0FCPVWWWXXX} 	 	Gw'' GL   !',FKKK   	+D&#fMMMNNN&/Tx	"""Xg|5T5TT			 TYFCCCC			"49fc&IIIJ	+D&#fMMMNNN	"""			"49fc&IIIJ	+D&#fMMMNNN	"""	T]FCGGGG			T]FCGGGG			T\63vFFFF				4   	XTY 	X/	63vVVVWWW+D&#fMMMNNN&/Ux	"""Xg}5U5UU				,T_== 	1 	1F*6>63vVVVN$//  &-En.CEE"0!1        0000+D&#fMMMNNN&/Tx	"""Xg|5T5TT			+++	))) @$@@AAAr$   c          	        g }d | j         D             }d}|t          |          k     rbd||         v rX||                             d          s=|dz  }|t          |          k     r%d||         v r||                             d          =|t          |          k     r||         nd}t          |          du}|t          v }	t          | j                   D ]j\  }
}t          |dg           }t          |d	d          }t          |          dk    oDt          |d         d
d          dk    o)|                    d          o|                    d          }|D ]}t          |d
d          }|dk    rqt          |j	        |||          }|j
        dk    r8t          |dd          }t          |j
        d| d|j         |g          c c S |                    |           |dk    rt          |j	        |||          }|j
        dk    r$t          |j
        d|j         |g          c c S |                    |           |r|r|	s|
|k    r}t          |          }|                    t          ||d                             }|j
        dk    r9t!          |                              d          }t          dd|           c c S x|dk    rht          |dd          }|rUt%          |t&                    r@t)          ||||          }|D ]}|j
        dk    r|c c c S |                    |           lt-          | |||          }|D ]}|j
        dk    r|c S |                    |           |st          dd          S |dv r2|                    t          dd                     t/          |          S t1          ||||          }|                    |           t/          |          S )zAnalyze a simple command node.c                ,    g | ]}t          |          S r,   )_get_word_value)r:   ws     r"   r<   z$_analyze_command.<locals>.<listcomp>   s     444A_Q444r$   r   =r       Nrm   valuerE   r   $(r   procsubr-   rI   	direction?zprocess substitution (...): rO   zcommand substitution: z$()r4   zcmdsub injection risk: paramargr5   )[testzconditional test)r\   len
startswithr   r
   	enumeraterR   endswithr9   r/   r   r   r   rq   classifyr   r   r=   
isinstancer   rz   rt   ru   r?   _analyze_simple_command)r;   r0   r1   r.   rB   r\   base_idxbasehas_handleris_simple_safepositionr_   rm   
word_valueis_pure_cmdsubpart	part_kindr   r   handlerouter_result	inner_cmdr   param_decisionspdredirect_decisionsrdcmd_decisions                               r"   rj   rj      s    I 54444EH3u::5?""h**3// # 	A	 	3u::5?""h**3// # 'U335??Dd##4/K[(N $DJ// 96 96$gr**T7B//
 JJ!O )a&$//8;)%%d++) ##C((	 	  .	6 .	6Dfd33II%%!.t|VSQW!X!X!X!(G33 'k3 ? ?I#&-Y	YY.BWYY"0!1       
   0000h&&!.t|VSQW!X!X!X!(G33#&-H1FHH"0!1       
   000 #
V#
V +
V !8++)$//G#*#3#3N5CS4T4T#U#UL#*g55$3D$9$9$?$?$F$F	'/T/T/TUUUUUUUg%%dE400 6:c3// 6&=VS' ' 'O . & &9//#%IIIIIII 0$$_555].	6b ,D&#fMMM   9III  '(((  2111 }'+=>>???	"""*5&#fMMML\"""Ir$   list[Decision]c          
        g }t          | dd          pg }|D ]}t          |dd          }|dk    rKt          |dd          s9t          |dd          }|r&|                    t          ||||	                     et          |d
d          }	|j        rt	          |j                  nd}
|j        r-t          |j        |||	          }|                    |           |r|
t          v s|
                    d          r|	dv rt          |
||          }|r|j	        dk    r(|
                    t          dd|
                      3|j	        dk    r9|j        p|j        }|
                    t          dd|
 d|                      w|j        p|j        }|
                    t          dd|
 d|                      |
                    t          dd|
                      |S )zAnalyze redirects on a node.	redirectsNrE   heredocquotedTcontentr   r-   op&)>z>>z&>z&>>z2>z2>>rI   zredirect to deny: r4   )rR   rt   rz   targetr   rv   SAFE_REDIRECT_TARGETSr   r	   decisionrq   r   r>   r   )r;   r0   r1   r.   rB   r   rr_kindr   r   r   target_cmdsub_decisionsredirect_matchmsgs                 r"   ru   ru   H  sl    Ik4006BI ,K ,KFD))Y1h-- !!Y33 $$/VTTT   Qb!!./h>***B 8 	6&9&#f' ' '# 4555  	 ***f.?.?.D.D* 666+FFC@@N K!*g55$$Xg7Nf7N7N%O%OPPPP#,66(0JN4JC$$Xf6TV6T6Ts6T6T%U%UVVVV(0JN4JC$$Xe5SF5S5Sc5S5S%T%TUUUU   %1H1H1H!I!IJJJr$   r\   	list[str]c               .   | st          dd          S d}|t          |           k     rbd| |         v rX| |                             d          s=|dz  }|t          |           k     r%d| |         v r| |                             d          =|t          |           k    rt          dd          S | |         }| |d         }dd	lm}m}  || 
          }	 ||	|||          }
|
rw|
j        dk    rt          d| d|
j         d          S |
j        dk    r#|
j        p|
j        }t          d| d|           S |
j        p|
j        }t          d| d|           S |t          v rt          |          dk    r|dk    r-t          |          dk    r|d         dv rt          dd          S d}|t          |          k     rw||         }|
                                s(|                    dd          
                                r|dz  }]|                    d          r|dk    r|dz  }~|dk    r|dz  }	 |t          |          k     rt          ||d         |||          S t          d|          S |t          v rt          d|          S t          |          rt          d| d          S t          |          }|r7|                    t#          |                    }|j        pt'          ||          }|j        r|s|j        D ]}|t*          v rt-          |||          }|ra|j        dk    r%|j        p|j        }t          d| d|           c S |j        dk    r%|j        p|j        }t          d| d|           c S t          d|          c S |j        dk    rt          d|          S |j        dk    r&|j        rt3          |j        |||j                  }|S t          d|          S t          dt'          ||                    S )z)Analyze a simple command (list of words).rI   ri   r   r   r   r   zenv assignmentN)SimpleCommandmatch_command)r\   r-   z (r   r   r   r4   r/   )z-vz-Vz
command -v.r   z--z --helpdelegate)r   r   r   dippy.core.configr   r   r   r   r>   r   isdigitreplacer   r
   _is_version_or_helpr   r   r   descriptionr   redirect_targetsr   r	   r   inner_commandrC   r.   )r\   r0   r1   r.   ir   tokensr   r   rH   config_matchr   jtokenr   r   descr   r   r   s                       r"   r   r     s     *))) 	
A
c%jj..SE!H__U1X5H5H5M5M_	Q c%jj..SE!H__U1X5H5H5M5M_ 	CJJ!12228D122YF ?>>>>>>>
-e
$
$
$C =fc&AAAL 5 G++G%G%G0D%G%G%GHHH"f,,&>,*>CFt$4$4s$4$4555&>,*>CEd#3#3c#3#3444 CKK!OO9VqVAY,5N5NG\222 #f++oo1IE}} %--R"8"8"@"@"B"B Q$$ $Q}}Qs6{{??*6!"":vs6RRRRt$$$ {&&& 6"" 3D!1!1!1222 $G )!!."8"899!B_VT%B%B" 	16 	1 1 1 1222!/!D!D! 
1%.&88,4N8N'40@0@30@0@AAAAA'0E99,4N8N'$/?/?#/?/?@@@@@ : $E400000=G##GT***]j((V-A($$fc&-  N "!E4((( E?6488999r$   r   c                    t          |           dk     rdS t          |           dk    r| d         dv rdS t          |           dk    r| d         dv rdS | d         dv rt          |           d	k    rdS dS )
z)Check if command is a version/help check.   Fr   )helpversionT)z	--version--help-h)r   r      r   )r   s    r"   r   r     s    
6{{Qu
6{{aF1I)<<<t
6{{aF1I)FFFtbz%%%#f++*:*:t5r$   c                    t          | t                    r| }nt          | dt          |                     }t          |          S )z>Extract string value from a word node, stripping outer quotes.r   )r   r   rR   _strip_quotes)r_   r   s     r"   r   r     s@    $ 2gs4yy11r$   r   c                    t          |           dk    r:| d         dk    r| d         dk    s| d         dk    r| d         dk    r
| dd         S | S )z&Strip surrounding quotes from a value.r   r   "r   'r   r   )r   s    r"   r   r      sZ    
5zzQ!HOOb	S 0 0!HOOb	S 0 02;Lr$   r+   c                    g }| |S t          | dd          }|dk    r|                    |            |S dD ]7}t          | |d          }|"|                    t          |                     8|S )zGRecursively find command substitutions in an arithmetic expression AST.NrE   r   )r   r   leftrightoperandindexr~   )rR   rq   rt   r}   )r;   resultsrE   attrchilds        r"   r}   r}   
  s    G|4&&DxtV : :dD))NN1%88999Nr$   c               `   | g S t          | dd          }|dk    rt          | j        |||          S |dk    rZg }|                    t          | j        |||                     |                    t          | j        |||                     |S |dv rZg }|                    t          | j        |||                     |                    t          | j        |||                     |S |dk    rt          | j        |||          S |dk    rt          | j        |||          S g S )	z>Recursively analyze a conditional expression node for cmdsubs.NrE   z
unary-testr-   zbinary-test)zcond-andzcond-orzcond-notz
cond-paren)rR   rv   r   rt   r   r   r|   inner)r;   r0   r1   r.   rE   rB   s         r"   r|   r|     s]    |	4&&D|"4<VLLLL				,TYFSSSTTT,TZVTTTUUU	(	(	(	+DIvs6RRRSSS+DJFSSSTTT			!$,FKKKK			!$*fc&IIIIIr$   c          
        g }t          | dg           }|D ]c}t          |dd          }|dk    rmt          |j        |||          }|j        dk    r4|                    t          |j        d|j         |g                     q|                    |           |d	k    rt          |j        |||          }|j        dk    rHt          |d
d          }	|                    t          |j        d|	 d|j         |g                     |                    |           |dk    rNt          |dd          }
|
r;t          |
t                    r&|	                    t          |
|||                     e|S )zLAnalyze word parts for command/process substitutions, including nested ones.rm   rE   Nr   r-   rI   cmdsub: rO   r   r   r   zprocsub r   r   r   )rR   r9   r/   r   rq   r   r   r   r   rt   rz   )r_   r0   r1   r.   rB   rm   r   r   r   r   r   s              r"   rv   rv   :  s    ID'2&&E " "D&$//	  *4<VTTTN$//  &-:>#8::"0!1        0000)##*4<VTTTN$//#D+s;;	  &-L9LL^5JLL"0!1        0000'!! $t,,C z#s++   +CVLLL   r$   sc               D   g }d}|t          |           k     r| ||dz            dk    rd}|dz   }|}|t          |           k     rW|dk    rQ| ||dz            dk    r|dz  }|dz  }n| |         dk    r|dz  }|dz  }n|dz  }|t          |           k     r|dk    Q|dk    rw| ||dz
           }	t          |	|||          }
|
j        dk    r4|                    t	          |
j        d|
j         |
g	                     n|                    |
           |}n|dz  }n| |         d
k    r|dz   }|t          |           k     r0| |         d
k    r$|dz  }|t          |           k     r| |         d
k    $|t          |           k     rz| |dz   |         }	t          |	|||          }
|
j        dk    r4|                    t	          |
j        d|
j         |
g	                     n|                    |
           |dz   }n|dz  }n|dz  }|t          |           k     |S )z<Extract and analyze command substitutions from a raw string.r   r   r   r   r   r-   rI   r   rO   `)r   rC   r   rq   r   r   )r   r0   r1   r.   rB   r   depthstartr   r   r   s              r"   rz   rz   f  s    I	A
c!ff**QQY<4EEEAc!ff**QQY<4''QJEFAAqTS[[QJEFAAFA c!ff** zzea!em,	!(FC!O!O!O!(G33$$ *1>~'<>>&4%5      $$^444QqTS[[AAc!ff**1Q c!ff**13q66zza!eaiL	!(FC!O!O!O!(G33$$ *1>~'<>>&4%5      $$^444EQFAo c!ff**p r$   
str | Nonec                J   t          | dd          dk    rdS t          | dg           }t          |          dk    rdS t          |d                   }|dk    rdS |d         }t          |d	d          r"|j        D ]}t          |dd          }|d
v r dS t          |          S )zMExtract target path from a `cd <literal>` command, or None if not applicable.rE   Nr/   r\   r   r   cdr   rm   )r   r   r   )rR   r   r   rm   )r;   r\   r   target_wordr   r   s         r"   rn   rn     s    tVT""i//tD'2&&E
5zzQt58$$Dt||t(K{GT** % 	 	Dfd33I:::tt ;;'''r$   r   c                    |                      d          r(t          j                    }| dk    r|S || dd         z  S |                      d          rt          |           S || z                                  S )z-Resolve a cd target path to an absolute Path.~r   N/)r   r   homeresolve)r   r1   r   s      r"   ro   ro     s|     !y{{S==KfQRRj   F||&L!!###r$   rB   c                T   | st          dd          S d | D             }d | D             }d | D             }|r%t          dd                    |          |           S |r%t          d	d                    |          |           S t          dd                    |          |           S )
zNCombine multiple decisions - most restrictive wins, all reasons at that level.rI   ri   c                2    g | ]}|j         d k    |j        S )r   r    rL   s     r"   r<   z_combine.<locals>.<listcomp>  s&    FFF18v3E3EAH3E3E3Er$   c                2    g | ]}|j         d k    |j        S )r4   r    rL   s     r"   r<   z_combine.<locals>.<listcomp>  s&    DDD!(e2C2C182C2C2Cr$   c                2    g | ]}|j         d k    |j        S )rI   r    rL   s     r"   r<   z_combine.<locals>.<listcomp>  s&    HHH!AH4G4GQX4G4G4Gr$   r   r   rO   r4   )r   rl   )rB   deny_reasonsask_reasonsallow_reasonss       r"   r?   r?     s     *))) GFiFFFLDDYDDDKHHyHHHM  M		, 7 7)LLLL Ktyy55	JJJJ GTYY}55	JJJJr$   N)
r/   r   r0   r   r1   r   r.   r2   r   r   )r0   r   r1   r   r.   r2   r   r   )r0   r   r1   r   r.   r2   r   r   )
r\   r   r0   r   r1   r   r.   r2   r   r   )r   r   r   r2   r%   )r   r   r   r   )r   r+   )
r   r   r0   r   r1   r   r.   r2   r   r   )r   r   )r   r   r1   r   r   r   )rB   r   r   r   ))r)   
__future__r   dataclassesr   r   pathlibr   typingr   r   r   r	   dippy.core.allowlistsr
   r   	dippy.clir   r   r   dippy.vendor.parabler   r   	frozensetr   r   rC   r9   rj   ru   r   r   r   r   r}   r|   rv   rz   rn   ro   r?   r,   r$   r"   <module>r     s    # " " " " " ( ( ( ( ( ( ( (             4 4 4 4 4 4 4 4 ? ? ? ? ? ? ? ? B B B B B B B B B B 2 2 2 2 2 2 2 2 "	"Q"Q"QRR  	= 	= 	= 	= 	= 	= 	= 	= @E     @ FK [B [B [B [B [B [B~ 8=c c c c c cN 8=5 5 5 5 5 5r DIc: c: c: c: c: c:L   "             $ 8=     @ 8=) ) ) ) ) )Z :?> > > > > >B( ( ( ((	$ 	$ 	$ 	$K K K K K Kr$   