
    <i0                         S r SSKrSSKrSSKJrJr  SSKJr  SSKJ	r	  SSK
JrJrJr  SSKJrJrJrJrJr  S r " S	 S
\	5      r " S S\5      r " S S\5      r " S S\5      r " S S\5      r " S S\5      rg)zC
Transformation utilities for STIX pattern comparison expressions.
    N)iter_initer_lex_cmp)comparison_expression_cmp)Transformer)	ipv4_addr	ipv6_addrwindows_reg_key)AndBooleanExpressionOrBooleanExpressionParentheticalExpression_BooleanExpression_ComparisonExpressionc                    [        U [        5      (       a1  [        U R                   Vs/ s H  n[        U5      PM     sn5      nU$ [        U [        5      (       a1  [	        U R                   Vs/ s H  n[        U5      PM     sn5      nU$ [        U [
        5      (       a  U nU$ [        S[        U 5      R                  -   5      es  snf s  snf )aG  
Create a duplicate of the given AST.

Note: the comparison expression "leaves", i.e. simple <path> <op> <value>
comparisons are currently not duplicated.  I don't think it's necessary as
of this writing; they are never changed.  But revisit this if/when
necessary.

:param ast: The AST to duplicate
:return: The duplicate AST
zCan't duplicate )	
isinstancer
   operands	_dupe_astr   r   	TypeErrortype__name__)astoperandresults      i/home/james-whalen/.local/lib/python3.13/site-packages/stix2/equivalence/patterns/transform/comparison.pyr   r      s     #+,,%.1ll'
.:7Igl'
 " M 
C,	-	-$.1ll&
.:7Igl&
  M 
C.	/	/ 
 M *T#Y-?-??@@'

&
s   C/Cc                   *    \ rS rSrSrS rS rS rSrg)ComparisonExpressionTransformer6   a5  
Transformer base class with special support for transforming comparison
expressions.  The transform method implemented here performs a bottom-up
in-place transformation, with support for some comparison
expression-specific callbacks.

Specifically, subclasses can implement methods:
    "transform_or" for OR nodes
    "transform_and" for AND nodes
    "transform_comparison" for plain comparison nodes (<prop> <op> <value>)
    "transform_default" for both types of nodes

"transform_default" is a fallback, if a type-specific callback is not
found.  The default implementation does nothing to the AST.  The
type-specific callbacks are preferred over the default, if both exist.

In all cases, the callbacks are called with an AST for a subtree rooted at
the appropriate node type, where the subtree's children have already been
transformed.  They must return the same thing as the base transform()
method: a 2-tuple with the transformed AST and a boolean for change
detection.  See doc for the superclass' method.

This process currently silently drops parenthetical nodes.
c                    [        U[        5      (       ai  Sn[        UR                  5       H/  u  p4U R	                  U5      u  pVU(       a  SnXQR                  U'   M1     U R                  U5      u  pvU(       a  SnXr4$ [        U[        5      (       a  U R                  U5      u  prXr4$ [        U[        5      (       a   U R	                  UR                  5      u  prXr4$ [        S[        U5      -   5      e)NFTzNot a comparison expression: )r   r   	enumerater   	transform4_ComparisonExpressionTransformer__dispatch_transformr   r   
expressionr   str)selfr   changedir   operand_resultthis_changedr   s           r   r   )ComparisonExpressionTransformer.transformP   s    c-..G'5
/3~~g/F,"G"0Q 6 $(#<#<S#A F  233"77<OF  455"nnS^^<OF
  ;c#hFGG    c                 8   [        U[        5      (       a  [        U SU R                  5      nOf[        U[        5      (       a  [        U SU R                  5      nO9[        U[
        5      (       a  [        U SU R                  5      nOU R                  nU" U5      $ )z
Invoke a transformer callback method based on the given ast root node
type.

:param ast: The AST
:return: The callback's result
transform_andtransform_ortransform_comparison)r   r
   getattrtransform_defaultr   r   )r#   r   meths      r   __dispatch_transform4ComparisonExpressionTransformer.__dispatch_transformj   s     c/004$2H2HID01141G1GHD233,d.D.DD
 ))DCyr)   c                 
    US4$ )z`
Override to handle transforming AST nodes which don't have a more
specific method implemented.
F r#   r   s     r   r/   1ComparisonExpressionTransformer.transform_default   s    
 Ezr)   r4   N)	r   
__module____qualname____firstlineno____doc__r   r    r/   __static_attributes__r4   r)   r   r   r   6   s    242r)   r   c                   *    \ rS rSrSrS rS rS rSrg)OrderDedupeTransformer   z
Canonically order the children of all nodes in the AST.  Because the
deduping algorithm is based on sorted data, this transformation also does
deduping.

E.g.:
    A and A => A
    A or A => A
c                 J   [        UR                  [        R                  " [        5      S9n[
        R                  " U[        R                  " [        5      S9 VVs/ s H  u  p4UR                  PM     nnn[        UR                  U[        5      S:g  nXQl        X4$ s  snnf )z
Sort/dedupe children.  AND and OR can be treated identically.

:param ast: The comparison expression AST
:return: The same AST node, but with sorted children
)keyr   )	sortedr   	functools
cmp_to_keyr   	itertoolsgroupbyobjr   )r#   r   sorted_childrenk_deduped_childrenr$   s          r   __transform"OrderDedupeTransformer.__transform   s     !LLi223LM
 (//Y%9%9-&		
daAEE 	 	 	
 LL*,E
 (|#	
s   Bc                 $    U R                  U5      $ N"_OrderDedupeTransformer__transformr5   s     r   r,   #OrderDedupeTransformer.transform_or       $$r)   c                 $    U R                  U5      $ rN   rO   r5   s     r   r+   $OrderDedupeTransformer.transform_and   rR   r)   r4   N)	r   r7   r8   r9   r:   rP   r,   r+   r;   r4   r)   r   r=   r=      s    <%%r)   r=   c                   *    \ rS rSrSrS rS rS rSrg)FlattenTransformer   zz
Flatten all nodes of the AST.  E.g.:

    A and (B and C) => A and B and C
    A or (B or C) => A or B or C
    (A) => A
c                 Z   Sn[        UR                  5      S:X  a  UR                  S   nSnX4$ / nUR                   Hb  n[        U[        5      (       a9  UR                  UR                  :X  a  UR                  UR                  5        SnMQ  UR                  U5        Md     X1l        X4$ )a	  
Flatten children.  AND and OR can be treated mostly identically.  The
little difference is that we can absorb AND children if we're an AND
ourselves; and OR for OR.

:param ast: The comparison expression AST
:return: The same AST node, but with flattened children
F   r   T)lenr   r   r   operatorextendappend)r#   r   r$   flat_operandsr   s        r   rK   FlattenTransformer.__transform   s     s||!,,q/CG | M<<g'9::LLG,<,<<!(()9)9:"G "((1 ( )L|r)   c                 $    U R                  U5      $ rN   _FlattenTransformer__transformr5   s     r   r,   FlattenTransformer.transform_or   rR   r)   c                 $    U R                  U5      $ rN   ra   r5   s     r   r+    FlattenTransformer.transform_and   rR   r)   r4   N)	r   r7   r8   r9   r:   rb   r,   r+   r;   r4   r)   r   rV   rV      s    >%%r)   rV   c                   *    \ rS rSrSrS rS rS rSrg)AbsorptionTransformer   zr
Applies boolean "absorption" rules for AST simplification.  E.g.:

    A and (A or B) = A
    A or (A and B) = A
c                   ^ SnUR                   S:X  a  SOSn[        5       n[        UR                  5       H  u  pVXT;   a  M  [        UR                  5       H  u  nmXW:X  d  Xt;   a  M  [	        T[
        5      (       a  TR                   U:w  a  M9  [        UTR                  [        5      (       a  UR                  U5        Ml  UR                   TR                   :X  d  M  [        U4S jUR                   5       5      (       d  M  UR                  U5        M     M     U(       a*  Sn[        [        U5      5       H  nUR                  U	 M     X4$ )NFORANDc              3   Z   >#    U  H   n[        UTR                  [        5      v   M"     g 7frN   )r   r   r   ).0child1_operandchild2s     r   	<genexpr>4AbsorptionTransformer.__transform.<locals>.<genexpr>  s2      
 />N	  *FOO5  />s   (+T)r[   setr   r   r   r   r   r   addallreversedrA   )	r#   r   r$   secondary_op	to_deleter%   child1jro   s	           @r   rK   !AbsorptionTransformer.__transform   s    # 4u$E	 #3<<0IA~&s||4	66Q^ "&*<==!??l: FOO-F  MM!$ __7 
 /5oo   "a(9 5	 1D GfY/0LLO 1 |r)   c                 $    U R                  U5      $ rN   !_AbsorptionTransformer__transformr5   s     r   r,   "AbsorptionTransformer.transform_or(  rR   r)   c                 $    U R                  U5      $ rN   r|   r5   s     r   r+   #AbsorptionTransformer.transform_and+  rR   r)   r4   N)	r   r7   r8   r9   r:   r}   r,   r+   r;   r4   r)   r   rg   rg      s    0d%%r)   rg   c                       \ rS rSrSrS rSrg)DNFTransformeri/  zb
Convert a comparison expression AST to DNF.  E.g.:

    A and (B or C) => (A and B) or (A and C)
c                 0   / n/ nSnUR                    HV  n[        U[        5      (       a-  UR                  S:X  a  UR	                  UR                   5        ME  UR	                  U5        MX     U(       a  [
        R                  " U6  VVs/ s H<  n[        [
        R                  " X65       Vs/ s H  n[        U5      PM     sn5      PM>     nnnU Vs/ s H  oPR                  U5      S   PM     nn[        U5      n	SnX4$ Un	X4$ s  snf s  snnf s  snf )NFrj   r   T)r   r   r   r[   r]   rD   productr
   chainr   r   r   )
r#   r   or_childrenother_childrenr$   childprod_seqsub_astdistributed_childrenr   s
             r   r+   DNFTransformer.transform_and5  s/    \\E%!34449O ""5>>2%%e, "  !* 1 1; ?
$ !@H % 7@oo&7	&77Ig& 7	&  !@ ! 
$* 7K$6JUu%a(6J ! $ ))=>FG  F=&
$($s   
#D-DDDDr4   N)r   r7   r8   r9   r:   r+   r;   r4   r)   r   r   r   /  s    
.r)   r   c                       \ rS rSrSrS rSrg)SpecialValueCanonicalizationif  z
Try to find particular leaf-node comparison expressions whose rhs (i.e. the
constant) can be canonicalized.  This is an idiosyncratic transformation
based on some ideas people had for context-sensitive semantic equivalence
in constant values.
c                     UR                   R                  S:X  a  [        U5        US4$ UR                   R                  S:X  a  [        U5        US4$ UR                   R                  S:X  a  [	        U5        US4$ )Nzwindows-registry-keyz	ipv4-addrz	ipv6-addrF)lhsobject_type_namer	   r   r   r5   s     r   r-   1SpecialValueCanonicalization.transform_comparisonm  sw    77##'==C  Ez WW%%4cN Ez WW%%4cN
 Ezr)   r4   N)r   r7   r8   r9   r:   r-   r;   r4   r)   r   r   r   f  s    r)   r   )r:   rB   rD   "stix2.equivalence.patterns.comparer   r   -stix2.equivalence.patterns.compare.comparisonr   $stix2.equivalence.patterns.transformr   -stix2.equivalence.patterns.transform.specialsr   r   r	   stix2.patternsr
   r   r   r   r   r   r   r=   rV   rg   r   r   r4   r)   r   <module>r      s      D =  BRk Rj/%#/%d,%8 ,%^@%#@%F44 4n#B r)   