
    >/i/                     2   S SK r S SKrS SKrS SKr S SKr\R                  " \	5      r
 S rS rS rS rS r " S S\5      r " S	 S
\5      r " S S\5      r\SSS4S jrS rS rS rS rS rS rSS jrS rS rS rS r S r!g! \ a    S SKr Nf = f)    Nc                 4   [        U [        R                  5      (       a*  U R                  U;   a  XR                     $ U R                  $ [        U [        R                  5      (       a0  [        U R                  U5       SU R                   3nX!;   a  X   $ U$ g)a]  Get a the full name for the attribute node.

This will resolve a pseudo-qualified name for the attribute
rooted at node as long as all the deeper nodes are Names or
Attributes. This will give you how the code referenced the name but
will not tell you what the name actually refers to. If we
encounter a node without a static name we punt with an
empty string. If this encounters something more complex, such as
foo.mylist[0](a,b) we just return empty string.

:param node: AST Name or Attribute node
:param aliases: Import aliases dictionary
:returns: Qualified name referred to by the attribute or name.
. )
isinstanceastNameid	Attribute_get_attr_qual_namevalueattr)nodealiasesnames      K/home/james-whalen/.local/lib/python3.13/site-packages/bandit/core/utils.pyr   r      s}     $!!77g77##ww	D#--	(	(%djj':;1TYYKH?=     c                 *   [        U R                  [        R                  5      (       a+  [	        U S5      U;   a  U[	        U S5         $ [	        U S5      $ [        U R                  [        R
                  5      (       a  [        U R                  U5      $ g)Nzfunc.idr   )r   funcr   r   deepgetattrr
   r   )r   r   s     r   get_call_namer   1   sn    $))SXX&&tY'72;tY7884++	DIIs}}	-	-"499g66r   c                     U R                   $ N)r   )r   s    r   get_func_namer   <   s    99r   c                     [        U [        R                  5      (       a5   [        U S5      nX!;   a  X   nO[        U S5      n U SU R
                   3$ g! [         a    Sn N f = f)Nzvalue.idr   r   )r   r   r
   r   	Exceptionr   )r   r   valprefixs       r   get_qual_attrr   @   sn    $&&		dJ/C~ $T:6 499+&&  	 F	s   A A A%$A%c                 L    UR                  S5       H  n[        X5      n M     U $ )z>Recurses through an attribute chain to get the ultimate value.r   )splitgetattr)objr   keys      r   r   r   R   s#    zz#c Jr   c                       \ rS rSrSrg)InvalidModulePathY    N)__name__
__module____qualname____firstlineno____static_attributes__r'   r   r   r%   r%   Y   s    r   r%   c                   ,   ^  \ rS rSrSrU 4S jrSrU =r$ )ConfigError]   z-Raised when the config file fails validation.c                 \   > X l         U SU 3U l        [        TU ]  U R                  5        g )Nz : )config_filemessagesuper__init__)selfr2   r1   	__class__s      r   r4   ConfigError.__init__`   s,    &%c'3&r   )r1   r2   r(   r)   r*   r+   __doc__r4   r,   __classcell__r6   s   @r   r.   r.   ]   s    7' 'r   r.   c                   ,   ^  \ rS rSrSrU 4S jrSrU =r$ )ProfileNotFoundf   z+Raised when chosen profile cannot be found.c                    > Xl         X l        SR                  U R                  U R                   5      n[        TU ]  U5        g )Nz.Unable to find profile ({}) in config file: {})r1   profileformatr3   r4   )r5   r1   r@   r2   r6   s       r   r4   ProfileNotFound.__init__i   s=    &BIILL
 	!r   )r1   r@   r8   r;   s   @r   r=   r=   f   s    5" "r   r=   r   c                     U  S3$ )z8Monkey patch for warnings.warn to suppress cruft output.
r'   )r2   categoryfilenamelinenolines        r   warnings_formatterrJ   s   s     Yb>r   c                    [         R                  R                  U 5      u  pUS:X  d  US:X  a  [        SU  S35      e[         R                  R	                  U5      S   /nUS;  a  [         R                  R                  [         R                  R                  US5      5      (       a4  [         R                  R                  U5      u  pUR                  SU5        OOUS;  a  M  SR                  U5      nU$ )al  Get the module's qualified name by analysis of the path.

Resolve the absolute pathname and eliminate symlinks. This could result in
an incorrect name if symlinks are used to restructure the python lib
directory.

Starting from the right-most directory component look for __init__.py in
the directory component. If it exists then the directory name is part of
the module name. Move left to the subsequent directory components until a
directory is found without __init__.py.

:param: Path to module file. Relative paths will be resolved relative to
        current working directory.
:return: fully qualified module name
r   zInvalid python file path: "z" Missing path or file namer   )/r   r   z__init__.pyr   )ospathr    r%   splitextisfilejoininsert)rN   headtailqnamequalnames        r   get_module_qualname_from_pathrW   z   s    " 77==&LTrzTRZ)$/JK
 	
 WWd#A&'E
n
$77>>"'',,t];<<77==.LTLLD! n
$ xxHOr   c                     U  SU 3$ )a  Extend the current namespace path with an additional name

Take a namespace path (i.e., package.module.class) and extends it
with an additional name (i.e., package.module.class.subclass).
This is similar to how os.path.join works.

:param base: (String) The base namespace path.
:param name: (String) The new name to append to the base path.
:returns: (String) A new namespace path resulting from combination of
          base and name.
r   r'   )baser   s     r   namespace_path_joinrZ      s     V1TFr   c                 8    [        U R                  SS5      5      $ )am  Split the namespace path into a pair (head, tail).

Tail will be the last namespace path component and head will
be everything leading up to that in the path. This is similar to
os.path.split.

:param path: (String) A namespace path.
:returns: (String, String) A tuple where the first component is the base
          path and the second is the last path component.
r      )tuplersplit)rN   s    r   namespace_path_splitr_      s     S!$%%r   c                 B    U R                  S5      R                  S5      $ )aC  PY3 bytes need escaping for comparison with other strings.

In practice it turns control characters into acceptable codepoints then
encodes them into bytes again to turn unprintable bytes into printable
escape sequences.

This is safe to do for the whole range 0..255 and result matches
unicode_escape on a unicode string.
unicode_escape)decodeencode)bs    r   escaped_bytes_representationre      s      88$%,,-=>>r   c                 4   [        U S5      (       a  U R                  $ SnSn[        U S5      (       a  U R                  nU R                  n[        R                  " U 5       H*  n[        U5      n[        XS   5      n[        X$S   5      nM,     X4U l        X4$ )zCalculate linerange for subtree_bandit_linerange   c(	 rC   rH   r   r\   )hasattrrg   rH   r   iter_child_nodescalc_linerangeminmax)r   	lines_min	lines_maxnlines_minmaxs        r   rk   rk      s    t())%%%IItXKK	KK	!!$'%a(	?3		?3	 (
 (3D!!r   c                    [        U S5      (       a,  [        [        U R                  U R                  S-   5      5      $ [        U S5      (       a*  U R
                  n[        [        US   US   S-   5      5      $ SSSSS.nUR                  5        H.  n[        X5      (       d  M  [        X5      X#'   [        X/ 5        M0     SnSn[        U S5      (       a  U R                  nU R                  n[        R                  " U 5       H*  n[        U5      n[        XAS   5      n[        XQS   5      nM,     UR                  5        H  nX#   c  M
  [        XX#   5        M     US:X  a  SnSnXE4U l        [        [        XES-   5      5      n [        U S	5      (       am  [        U R                  S5      (       aR  [        U5      nU R                  R                  U-
  n	U	S:  a(  [        [        XR                  R                  5      5      $ U$ )
z"Get line number range from a node.rH   r\   _bandit_linerange_strippedr   N)bodyorelsehandlers	finalbodyrh   rC   _bandit_sibling)ri   listrangerH   
end_linenors   keysr!   setattrr   rj   rk   rl   rm   rx   )
r   rq   stripr#   rn   ro   rp   linesstartdeltas
             r   	lineranger      s   tXE$++t':;<<4566::Ll1o|A/BCDD 	
 ::<Ct!!$T/
2&  
 		4""II%%d+A)!,LIA7IIA7I ,
 ::<Cz%5:.   ?II+4*@'U9!m45M4*++  (1
 1
 JE((//%7EqyE%)=)=)D)DEFFr   c           
        ^ SU4S jjmU /n[        U R                  [        R                  5      (       a7  U R                  n [        U R                  [        R                  5      (       a  M7  [        U [        R                  5      (       a	  T" XU5        U SR	                  U Vs/ s HQ  n[        U[        R
                  5      (       d  M$  [        UR                  [        5      (       d  ME  UR                  PMS     sn5      4$ s  snf )a  Builds a string from a ast.BinOp chain.

This will build a string from a series of ast.Constant nodes wrapped in
ast.BinOp nodes. Something like "a" + "b" + "c" or "a %s" % val etc.
The provided node can be any participant in the BinOp chain.

:param node: (ast.Constant or ast.BinOp) The node to process
:param stop: (ast.Constant or ast.BinOp) Optional base node to stop at
:returns: (Tuple) the root node of the expression, the string value
c                 n  > X:w  a  UR                  [        U R                  [        R                  5      (       a  T" U R                  X5      OU R                  5        UR                  [        U R
                  [        R                  5      (       a  T" U R
                  X5      OU R
                  5        g g r   )appendr   leftr   BinOpright)r   bitsstop_gets      r   r   concat_string.<locals>._get  s    <KKdii33 TYY+YY
 KKdjj#))44 TZZ,ZZ r    r   )r   _bandit_parentr   r   rQ   Constantr   str)r   r   r   xr   s       @r   concat_stringr     s     6D
T((#))
4
4"" T((#))
4
4$		""T Aa. 3=aggs3K 	
	 	s   %#D
D
-D
c                     U R                   n [        U[        R                  5      (       a  UR                  $ UR
                  $ ! [         a     gf = f)a@  Get a function name from an ast.Call node.

An ast.Call node representing a method call with present differently to one
wrapping a function call: thing.call() vs call(). This helper will grab the
unqualified call name correctly in either case.

:param node: (ast.Call) the call node
:returns: (String) the function name
r   )r   r   r   r
   r   r	   AttributeError)r   r   s     r   get_called_namer   ;  sG     99D&tS]];;tyyHH s   *A A 
AAc                 J   [        U S5      (       a  U R                  nO?[        U S5      (       a  U R                  R                  nO[        R	                  SU 5        g[
        R                  U   n[        US5      (       a  UR                  $ [        R	                  SU5        g)zGet the path of the file where the function is defined.

:returns: the path, or None if one could not be found or f is not a real
    function
r)   im_funcz'Cannot resolve file where %s is definedN__file__z&Cannot resolve file path for module %s)ri   r)   r   LOGwarningsysmodulesr   )fmodule_namemodules      r   get_path_for_functionr   L  s     q,ll	I		ii**=qA[[%Fvz""<kJr   c                    [         R                  " 5       n UR                  U 5        UR                  S5       VVs0 s H  u  p#X#_M	     snn$ s  snnf ! [         R                  [
        [        4 a    [        R                  SU 5         g f = f)Nbanditz:Unable to parse config file %s or missing [bandit] section)	configparserConfigParserreaditemsErrorKeyError	TypeErrorr   r   )f_locconfigkvs       r   parse_ini_filer   c  s{    &&(F
E!'h!78!7!7888)4 
K	

 
s"   %A A
A A 5BBc                      [         R                  S:  a  U S;   a  U $ [        [        U 5      n[	        U[        R
                  5      (       a  U $  [        SU  S35      e! [         a     Nf = f)z4Check if the given name is that of a valid AST node.)      )NumStrEllipsisNameConstantByteszError: z  is not a valid node type in AST)r   version_infor!   r   
issubclassASTr   r   )r   r   s     r   check_ast_noder   r  s|     w&4 4
 ,
 KsD!dCGG$$K %
 gdV#CD
EE  s   A 0A 
A,+A,c                 N    US    H  nU R                  US 5      nUc  M  Us  $    g )Nr   )get)nosec_linescontextrH   nosecs       r   	get_nosecr     s1    +&-L ' r   r   )"r   loggingos.pathrM   r   r   ImportErrorr   	getLoggerr(   r   r   r   r   r   r   r   r%   r.   r=   UserWarningrJ   rW   rZ   r_   re   rk   r   r   r   r   r   r   r   r'   r   r   <module>r      s   
    
( !  8$		 	') '
"i 
" "Br F&
?"(3l'T".F.{  ('(s   B	 	
BB