
    ̦j~C                    r   d Z ddlmZ ddlZddlmc 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mZ ddlZ ee      j)                         j*                  d   Z ee      ej0                  vr"ej0                  j3                  d ee             ddlmZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$ ddd	Z%d
Z&dZ'edz  dz  Z(ddd	 	 	 	 	 	 	 	 	 	 	 ddZ)ejT                  jW                  dg d      	 	 	 	 	 	 	 	 dd       Z,ddZ-	 	 	 	 	 	 d dZ.	 	 	 	 	 	 d dZ/	 	 	 	 	 	 d dZ0	 	 	 	 	 	 d dZ1ddZ2	 	 	 	 	 	 	 	 d!dZ3ddZ4y)"u  tests.regression.test_owner_trigger_security_boundaries_2553 — fixture 5 + 보안 경계.

회장 §명시:
  - fixture 5: **non-queue-head blocked** — queue-head 아닌 PR 에 trigger 시 fail-fast
  - 15 금지 박제 (보안 경계 적대적 평가 핵심, Lv.4 security)

본 regression 의 7 테스트 케이스는 다음 doctrine 위반 시나리오를 차단한다:
  1. queue-head 아닌 PR 에 trigger (금지 #9)
  2. comment body strict equality 위반 (금지 #7)
  3. endpoint allowlist 위반 (금지 #1~5: merge/approve/close/reopen/push)
  4. token value log (금지 #12, #13)
  5. default GH_TOKEN fallback (금지 #14)
  6. owner_trigger_pat.py 소스 안에 forbidden endpoint 호출 표현식 0
  7. OWNER PAT env value leak (금지 #12, #13 — stdout/stderr/audit 어디에도 노출 0)

본 모듈은 `anu_v2.owner_trigger_pat` 를 **수정하지 않고** 정적 + behavioral 검증만 수행.
    )annotationsN)Path)AnyMappingSequence   )
ALLOWED_COMMENT_BODYDECISION_REJECTERR_BODY_NOT_ALLOWED!EVIDENCE_MISSING_FOR_CURRENT_HEAD
OUTCOME_OKOUTCOME_REJECTEDOWNER_PAT_ENV_NAMEOwnerTriggerPatassert_body_allowedwrite_decision_json c                4    t        j                  g | ||      S )N)args
returncodestdoutstderr)
subprocessCompletedProcess)r   r   r   s      S/home/jay/workspace/tests/regression/test_owner_trigger_security_boundaries_2553.py_cpr   1   s    &&B:f]cdd    ghp_SECRET12345z2026-05-11T12:00:00+00:00anu_v2zowner_trigger_pat.pyfail_returncodefail_stderrc               X     d fd}d	fd}d
fd}t        |||ddd       S )Nc                r    j                  t        |       t        |xs i       d       t              S )N)r   env)r   r   )appendlistdictr   )r   r%   r!   r"   gh_callss     r   	gh_runnerz _make_trigger.<locals>.gh_runnerF   s-    dDODEokBBr   c                :    j                  t        |              y N)r&   r(   )recordaudit_callss    r   audit_writerz#_make_trigger.<locals>.audit_writerJ   s    4<(r   c                j    j                  t        |       t        |      d       t        | |       y )N)payloadpath)r&   r(   strr   )r1   r2   decision_callss     r   decision_writerz&_make_trigger.<locals>.decision_writerM   s(    $w-TKLGT*r   zjeon-jonghyukztaskctl-anuc                     t         S r,   )	_FIXED_TS r   r   <lambda>z_make_trigger.<locals>.<lambda>W   s    i r   )r*   r/   r5   ownerrepoclock)r   zSequence[str]r%   zMapping[str, str]returnsubprocess.CompletedProcess)r-   Mapping[str, Any]r=   None)r1   r?   r2   r   r=   r@   )r   )r)   r.   r4   r!   r"   r*   r/   r5   s   `````   r   _make_triggerrA   >   s4    C)+ !' r   bad_queue_position)   r      
   c           	     	   |j                  t        t               g }g }g }t        |||      }|j	                  dd|t
        | dz  | dz        }|j                  }|t        k(  }	|	st        j                  d|	fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	t        j                         v st        j                  t              rt        j                  t              nd	d
z  }
dd|
iz  }t        t        j                  |            dx}}	|j                   j#                         }g }d}||v }|}|sd}||v }|}|sXt        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }|j%                  |       |st        j                  dfd|f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }|j%                  |       t        j&                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}}t)        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t(              rt        j                  t(              ndd t        j                         v st        j                  |      rt        j                  |      nd t        j                  |      t        j                  |      d!z  }d"d#|iz  }t        t        j                  |            dx}x}}t+        j,                  | dz  j/                  d$%            }|d&   }|t0        k(  }|st        j                  d|fd'|t0        f      t        j                  |      d(t        j                         v st        j                  t0              rt        j                  t0              nd(d)z  }d*d+|iz  }t        t        j                  |            dx}}y),uw   queue_position != 0 인 PR trigger 시도 → REJECT + gh 호출 0.

    회장 §명시 fixture 5 + 15 금지 #9.
    *   nqhaudit.jsonldecision.json	pr_numberhead_shaqueue_positiongemini_evidence_stateaudit_log_pathdecision_json_path==z/%(py2)s
{%(py2)s = %(py0)s.outcome
} == %(py4)soutr   py0py2py4assert %(py6)spy6N
QUEUE_HEADNOT_QUEUE_HEADin)z%(py3)s in %(py5)sreason_upper)py3py5z%(py7)spy7)z%(py10)s in %(py12)s)py10py12z%(py14)spy14rC   zassert %(py17)spy17r   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenr)   rW   py1ra   r[   assert %(py8)spy8utf-8encodingdecisionz%(py1)s == %(py3)sr
   rk   ra   assert %(py5)srb   )setenvr   _FAKE_OWNER_PATrA   trigger_gemini_reviewr   outcomer   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationreasonupperr&   _format_boolopri   jsonloads	read_textr
   )tmp_pathrB   monkeypatchr)   r.   r4   triggerrU   @py_assert1@py_assert3@py_format5@py_format7r`   @py_assert2@py_assert4@py_assert0@py_assert9@py_assert11@py_format6@py_format8@py_format13@py_format15@py_format16@py_format18@py_assert5@py_format9decision_data@py_format4s                               r   test_non_queue_head_blockedr   \   s    )?;%'H(*K+-NHk>BG

'
')?-/#o5 ( C ;;*;*****;*******3***3***;******************::##%LOLOLL(O.>O.>,.NOOOOLLOOOLOOOOOOLOOOLOOOOOOO.>,OOO.>OOOOOO,OOO,OOOOOOOOOOOOOOx=A=A=A33xx=AJJ? :EEwEWXM$7$7777$777$7777777777777777r   c            	     J   t        t               g d} | D ]  }t        j                  t              5 }t        |       ddd       j
                  }t        |      }t        |v }|sCt        j                  d|fdt        |f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}} d}t        |k(  }|st        j                  d|fdt        |f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}}t!        t        t              }|sddt        j                         v st        j                  t               rt        j                  t               nddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d}y# 1 sw Y   [xY w)u   `/gemini review` 외 body 입력 시 RuntimeError("BODY_NOT_ALLOWED") raise.

    회장 §명시 15 금지 #7 + 6 허용 #1: comment body 정확히 `/gemini review` only.
    strict equality (대소문자/공백/접미사 등 모두 차단).
    )z/gemini review pleasez /gemini reviewz/gemini review z/gemini Reviewz/Gemini reviewz/gemini-reviewz/gemini  reviewz/gemini review
z/gemini review;r   zgemini reviewz/gemini review!Nr^   )zK%(py0)s in %(py7)s
{%(py7)s = %(py2)s(%(py5)s
{%(py5)s = %(py3)s.value
})
}r   r3   exc)rW   rX   ra   rb   rc   assert %(py9)spy9z/gemini reviewrR   z%(py0)s == %(py3)sr	   rW   ra   rt   rb   5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancerW   rk   rX   rY   )r   r	   pytestraisesRuntimeErrorvaluer3   r   ry   rz   r{   r|   r}   r~   r   r   r   )
bad_bodiesbodyr   r   @py_assert6r   r   @py_format10r   r   r   r   r   s                r   2test_comment_body_strict_equality_rejects_variantsr      s    ,-J  6]]<( 	&C%	&+.995s9~5#~5555#~555555#555#555555s555s555555355535559555~55555556 $43#33333#3333333333333#33333333*C00000000:000:000000*000*000000C000C0000000000	& 	&s   NN"	c           	        |j                  t        t               g }g }g }t        |||      }d}|j	                  |ddt
        | dz  | dz        }|j                  }|t        k(  }	|	st        j                  d|	fd|t        f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
t        j                         v st        j                  t              rt        j                  t              nd
dz  }
dd|
iz  }t        t        j                  |            dx}}	t!        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   d   }dddd| dddt"         g}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  |      rt        j                  |      ndd z  }d!d"|iz  }
t        t        j                  |
            d}d#j%                  |      }d$}|D ]  }||v}|st        j                  d%|fd&||f      d't        j                         v st        j                  |      rt        j                  |      nd'd(t        j                         v st        j                  |      rt        j                  |      nd(d z  }t        j&                  d)|d*|       d+z   d"|iz  }
t        t        j                  |
            d} y),u   gh_runner 에 전달되는 args 가 정확히 issue comments POST 1 endpoint 형식.

    회장 §명시 15 금지 #1~5: merge / approve / close / reopen / push 호출 0.
    Q   ep_testr   rI   rJ   rK   rR   rT   rU   r   rV   rZ   r[   NrC   rh   ri   r)   rj   rl   rm   r   apiz-XPOSTz(/repos/jeon-jonghyuk/taskctl-anu/issues/z	/commentsz-fzbody=)z%(py0)s == %(py2)sexpectedrW   rX   assert %(py4)srY    )z/mergesz/mergez
/approvalsz/approvez/reviewszstate=closedz
state=openzpr mergez
pr approvezpr closez	pr reopenzgit pushz	/git/refsz
/branches/z/pulls/not inz%(py0)s not in %(py2)sfragflatzforbidden fragment z found in args: 
>assert %(py4)s)ru   r   rv   rA   rw   r   rx   r   ry   rz   r{   r|   r}   r~   r   r   ri   r	   join_format_assertmsg)r   r   r)   r.   r4   r   prrU   r   r   r   r   r   r   r   r   r   r   @py_format3r   forbidden_fragmentsr   s                         r   +test_endpoint_allowlist_enforced_exact_argsr      s    )?;%'H(*K+-NHk>BG	B

'
'?-/#o5 ( C ;;$;*$$$$;*$$$$$$3$$$3$$$;$$$$$$*$$$*$$$$$$$x=A=A=A33xx=AA;vD 	tV
22$i@*+,H
 8484488 88D>D $ V4UUUt4UUUUUUtUUUtUUUUUU4UUU4UUUU#6th>Ntf!UUUUUUUVr   c           	        |j                  t        t               g }g }g }t        |||      }|j	                  dddt
        | dz  | dz        }|j                  }|t        k(  }|st        j                  d|fd|t        f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
t        j                         v st        j                  t              rt        j                  t              nd
dz  }	dd|	iz  }
t        t        j                  |
            dx}}|d   }t!        j"                  |d      }t        |v}|st        j                  d|fdt        |f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }	t        t        j                  |	            d}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}d}||v }|st        j                  d|fd||f      t        j                  |      d t        j                         v st        j                  |      rt        j                  |      nd d!z  }d"d#|iz  }t        t        j                  |            dx}}|d   }t%        |t&              }|sd$d%t        j                         v st        j                  t$              rt        j                  t$              nd%dt        j                         v st        j                  |      rt        j                  |      ndd&t        j                         v st        j                  t&              rt        j                  t&              nd&t        j                  |      d'z  }	t        t        j                  |	            d}t)        |      }d(}||k(  }|st        j                  d|fd)||f      d*t        j                         v st        j                  t(              rt        j                  t(              nd*dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d+z  }
d,d-|
iz  }t        t        j                  |            dx}x}}d. |D        }t+        |      }|sd/d0t        j                         v st        j                  t*              rt        j                  t*              nd0t        j                  |      t        j                  |      dz  }	t        t        j                  |	            dx}}| dz  j-                  d12      }t        |v}|st        j                  d|fdt        |f      dt        j                         v st        j                  t              rt        j                  t              ndd3t        j                         v st        j                  |      rt        j                  |      nd3dz  }dd|iz  }	t        t        j                  |	            d}|j.                  }t        |v}|st        j                  d|fd4t        |f      dt        j                         v st        j                  t              rt        j                  t              ndd	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}}y)5u   token=`ghp_SECRET12345` 환경에서 trigger 후 audit dict 직렬화에 token raw 0 + token_hash 12자.

    회장 §명시 15 금지 #12, #13. audit 에는 token_present (bool) 또는 token_hash
    (sha256 prefix 12자 hex) 만 등장.
    r   logtestr   rI   rJ   rK   rR   rT   rU   r   rV   rZ   r[   NT)	sort_keysr   r   rv   
serializedr   r   rY   token_present)is)z%(py1)s is %(py4)s)rk   rY   
token_hashr^   )z%(py1)s in %(py3)sr-   rs   rt   rb   r   r   r3   r      rh   ri   rj   rl   rm   c              3  $   K   | ]  }|d v  
 yw)0123456789abcdefNr8   ).0cs     r   	<genexpr>z9test_token_value_never_logged_in_audit.<locals>.<genexpr>   s     ;1q&&;s   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}allrn   ro   decision_textz2%(py0)s not in %(py4)s
{%(py4)s = %(py2)s.reason
})ru   r   rv   rA   rw   r   rx   r   ry   rz   r{   r|   r}   r~   r   r   r   dumpsr   r3   ri   r   r   r   )r   r   r)   r.   r4   r   rU   r   r   r   r   r-   r   r   r   r   r   r   r   r   r   r   r   s                          r   &test_token_value_never_logged_in_auditr      s    )?;%'H(*K+-NHk>BG

'
'?-/#o5 ( C ;;$;*$$$$;*$$$$$$3$$$3$$$;$$$$$$*$$$*$$$$$$$ _FFd3J*,,,,?*,,,,,,?,,,?,,,,,,*,,,*,,,,,,,/"*d*"d****"d***"***d*******!<6!!!!<6!!!<!!!!!!6!!!6!!!!!!!%Jj#&&&&&&&&:&&&:&&&&&&j&&&j&&&&&&#&&&#&&&&&&&&&&z? b ?b    ?b      3   3      z   z   ?   b       ;
;;3;;;;;;;;;3;;;3;;;;;;;;;;;;;; /::G:LM-////?-//////?///?//////-///-/////// #&**,?*,,,,?*,,,,,,?,,,?,,,,,,#,,,#,,,*,,,,,,,r   c           	     (   |j                  t        t               g }g }g }t        |||ddt         d      }|j	                  dddt
        | dz  | d	z  
      }|j                  }d}||k(  }	|	st        j                  d|	fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}|j                  }	t        |	v}|st        j                  d|fdt        |	f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}}	|d   }t         j"                  }	 |	|      }t        |v}|sCt        j                  d|fdt        |f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  t               rt        j                  t               ndt        j                  |	      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}	}y) uf   gh stderr 에 token 이 우연히 박혀도 redact 처리되어 reason/audit 어디에도 token raw 0.rC   z401 unauthorized: token=z expiredr    r   failr   rI   rJ   rK   failedrR   )z/%(py2)s
{%(py2)s = %(py0)s.outcome
} == %(py5)srU   )rW   rX   rb   assert %(py7)src   Nr   r   rv   rV   rZ   r[   r   )zO%(py0)s not in %(py7)s
{%(py7)s = %(py4)s
{%(py4)s = %(py2)s.dumps
}(%(py5)s)
}r   r-   )rW   rX   rY   rb   rc   r   r   )ru   r   rv   rA   rw   r   rx   ry   rz   r{   r|   r}   r~   r   r   r   r   r   )r   r   r)   r.   r4   r   rU   r   r   r   r   r   r   r   r-   r   r   s                    r   .test_token_value_redacted_on_gh_runner_failurer     s   
 )?;%'H(*K+-N+~..?xHG 
'
'?-/#o5 ( C ;;"(";("""";(""""""3"""3""";"""(""""""""%**,?*,,,,?*,,,,,,?,,,?,,,,,,#,,,#,,,*,,,,,,,_F"&**4*V"44?"44444?"4444444?444?444444$444$444*444444V444V444"44444444r   c           	     8
   |j                  t        d       |j                  dd       |j                  dd       g }g }g }t        |||      }|j	                  dddt
        | d	z  | d
z        }|j                  }|t        k(  }|st        j                  d|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }	dd|	iz  }
t        t        j                  |
            dx}}d}|j                   }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}t#        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t"              rt        j                  t"              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}}d}t$        j&                  }|d    } ||      }||v}|st        j                  d!|fd"||f      t        j                  |      d#t        j                         v st        j                  t$              rt        j                  t$              nd#t        j                  |      t        j                  |      t        j                  |      d$z  }d%d&|iz  }t        t        j                  |            dx}x}x}x}}d}|j                   }||v}|st        j                  d!|fd'||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)(u   OWNER_GEMINI_TRIGGER_PAT 누락 + GH_TOKEN 존재 시 fallback X — REJECT.

    회장 §명시 15 금지 #14: default GH_TOKEN fallback. 누락 시 즉시 REJECT
    (silent skip 금지).
    F)raisingGH_TOKENghp_FALLBACK_FAKEGITHUB_TOKENr   
nofallbackr   rI   rJ   rK   rR   rT   rU   r   rV   rZ   r[   Ntoken_missingr^   )z.%(py1)s in %(py5)s
{%(py5)s = %(py3)s.reason
})rk   ra   rb   r   rc   rh   ri   r)   rj   rl   rm   r   r   )zO%(py1)s not in %(py9)s
{%(py9)s = %(py5)s
{%(py5)s = %(py3)s.dumps
}(%(py7)s)
}r   )rk   ra   rb   rc   r   zassert %(py11)spy11)z2%(py1)s not in %(py5)s
{%(py5)s = %(py3)s.reason
})delenvr   ru   rA   rw   r   rx   r   ry   rz   r{   r|   r}   r~   r   r   r   ri   r   r   )r   r   r)   r.   r4   r   rU   r   r   r   r   r   r   r   r   r   r   r   r   @py_assert8r   @py_format12s                         r   &test_default_gh_token_fallback_blockedr   "  s    )59z#67~':;%'H(*K+-NHk>BG

'
'?-/#o5 ( C ;;*;*****;*******3***3***;******************(cjj(?j((((?j(((?((((((c(((c(((j(((((((x=A=A=A33xx=AAdjjARAj&AA&AAAAA&AAAAAAAAAAdAAAdAAAjAAAAAA&AAAAAAAA0cjj0j0000j000000000c000c000j0000000r   c                    t         j                  d      } d}|D ]  }|| v}|st        j                  d|fd|| f      dt	        j
                         v st        j                  |      rt        j                  |      nddt	        j
                         v st        j                  |       rt        j                  |       nddz  }t        j                  d	|      d
z   d|iz  }t        t        j                  |            d} | j                  d      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d|       dz   d|iz  }	t        t        j                  |	            dx}}y)un  owner_trigger_pat.py 소스에 merge/approve/close/reopen/push 호출 표현식 0.

    회장 §명시 15 금지 #1~5 박제. 본 테스트는 모듈 소스를 파일 read 후 정적 grep.
    docstring / 주석 / forbidden fragment 상수 (의도된 negative list) 는 검사 대상에서
    제외한다. 실제 gh api 호출 표현식이 존재하면 fail.
    rn   ro   )z"pr", "merge"z"pr", "approve"z"pr", "close"z"pr", "reopen"zsubprocess.run(["git", "push"z("/repos/{owner}/{repo}/pulls/{pr}/merge"z*"/repos/{owner}/{repo}/pulls/{pr}/reviews"z
.merge_pr(z.approve_pr(z
.close_pr(z.reopen_pr(z.push_commit(r   r   patsrcr   z(forbidden call pattern found in source: r   rY   Nzself._gh(args, env)rC   rR   r   gh_call_countr   z%expected 1 self._gh(...) call, found 
>assert %(py5)srb   )_OWNER_TRIGGER_SRCr   ry   rz   r{   r|   r}   r~   r   r   r   count)
r   forbidden_call_patternsr   r   r   r   r   r   r   r   s
             r   6test_merge_approve_close_reopen_push_no_call_in_sourcer   F  s&    
&
&
&
8C" ' R#~QQQs#QQQQQQsQQQsQQQQQQ#QQQ#QQQQ!I#QQQQQQQR
 II34MV=AVVV=AVVVVVV=VVV=VVVAVVV!F}oVVVVVVVr   c           	        |j                  t        t               g }g }g }t        |||      }|j	                  dddt
        | dz  | dz        }|j                  }|t        k(  }	|	st        j                  d|	fd|t        f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
t        j                         v st        j                  t              rt        j                  t              nd
dz  }
dd|
iz  }t        t        j                  |            dx}}	|j!                         }|j"                  }	t        |	v}|st        j                  d|fdt        |	f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }
dd|
iz  }t        t        j                  |            dx}}	|j$                  }	t        |	v}|st        j                  d|fdt        |	f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }
dd|
iz  }t        t        j                  |            dx}}	t'        j(                  |d         }t        |v}|st        j                  d|fdt        |f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }
t        t        j                  |
            d}| dz  j+                  d      }t        |v}|st        j                  d|fdt        |f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }
t        t        j                  |
            d}|j,                  }	t        |	v}|st        j                  d|fdt        |	f      dt        j                         v st        j                  t              rt        j                  t              ndd	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |	      dz  }
dd|
iz  }t        t        j                  |            dx}}	|j.                  }	t        |	v}|st        j                  d|fdt        |	f      dt        j                         v st        j                  t              rt        j                  t              ndd	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |	      dz  }
dd|
iz  }t        t        j                  |            dx}}	|d   d   d    }|t        k(  }|st        j                  d|fd!|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndd"z  }d#d$|iz  }t        t        j                  |            dx}}y)%u   trigger 후 OWNER PAT 값이 stdout/stderr/audit 어디에도 leak 0.

    회장 §명시 15 금지 #12, #13: token value log 0.
    r   	isolationr   rI   rJ   rK   rR   rT   rU   r   rV   rZ   r[   Nr   )z/%(py0)s not in %(py4)s
{%(py4)s = %(py2)s.out
}rv   captured)z/%(py0)s not in %(py4)s
{%(py4)s = %(py2)s.err
}r   r   record_textr   r   rY   rn   ro   r   r   )z9%(py0)s not in %(py4)s
{%(py4)s = %(py2)s.decision_path
}r%   r   rr   rs   rt   rb   )ru   r   rv   rA   rw   r   rx   r   ry   rz   r{   r|   r}   r~   r   r   
readouterrrU   errr   r   r   r   decision_path)r   r   capsysr)   r.   r4   r   rU   r   r   r   r   r   r   r   r   r   r   r   r   s                       r   =test_owner_pat_env_isolated_no_leak_in_stdout_stderr_or_auditr   k  sN    )?;%'H(*K+-NHk>BG

'
'?-/#o5 ( C ;;$;*$$$$;*$$$$$$3$$$3$$$;$$$$$$*$$$*$$$$$$$  "H"*,,.?,....?,......?...?......(...(...,......."*,,.?,....?,......?...?......(...(...,....... **[_-K+----?+------?---?------+---+-------/::G:LM-////?-//////?///?//////-///-/////// #&**,?*,,,,?*,,,,,,?,,,?,,,,,,#,,,#,,,*,,,,,,,"%"3"33?"33333?"3333333?333?333333#333#333"33333333
 A;uj)<)_<<<<)_<<<)<<<<<<_<<<_<<<<<<<r   c                    t         dz  } g }| j                  d      D ]X  }|j                  t               j                         }d|v r+d|v r0	 |j	                  d      }d|v sH|j                  |       Z g }||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}}y# t
        $ r Y 'w xY w)u  OWNER_GEMINI_TRIGGER_PAT env 이름이 owner_trigger_pat.py 외 다른 anu_v2 모듈에
    등장하지 않는지 박제 (one-way isolation, 회장 §명시 secret 별도 저장).

    본 테스트는 anu_v2 디렉토리 내 .py 파일을 스캔하여 OWNER_GEMINI_TRIGGER_PAT
    문자열이 owner_trigger_pat.py 외 다른 곳에 hardcoded 되어 있지 않은지 검증.
    test 파일은 정상적으로 import 하므로 제외.
    r   z*.pyowner_trigger_patz/tests/rn   ro   OWNER_GEMINI_TRIGGER_PATrR   r   	offendersr   z-OWNER_GEMINI_TRIGGER_PAT env name leaked to: r   rb   N)_WORKSPACE_ROOTrglobrelative_toas_posixr   OSErrorr&   ry   rz   r{   r|   r}   r~   r   r   r   )	
anu_v2_dirr   pyreltextr   r   r   r   s	            r   *test_owner_pat_env_name_isolated_in_moduler    s;    !8+JIv& "nn_-668#%	<<<1D &-S!"  9?  9                  8	{C      		s   D88	EE)r   r   r   )r   intr   r3   r   r3   r=   r>   )r)   list[dict[str, Any]]r.   r
  r4   r
  r!   r	  r"   r3   r=   r   )r   r   rB   r	  r   pytest.MonkeyPatchr=   r@   )r=   r@   )r   r   r   r  r=   r@   )r   r   r   r  r   zpytest.CaptureFixture[str]r=   r@   )5__doc__
__future__r   builtinsr{   _pytest.assertion.rewrite	assertionrewritery   r   r   syspathlibr   typingr   r   r   r   __file__resolveparentsr   r3   r2   insertanu_v2.owner_trigger_patr	   r
   r   r   r   r   r   r   r   r   r   rv   r7   r   rA   markparametrizer   r   r   r   r   r   r   r   r  r8   r   r   <module>r     s  $ #     
  ) )  x.((*2215sxx'HHOOAs?+,  e $'	 h!77  "% )
   < -}=888 $8 
	8 >8D 1H.V.V#.V 
.Vd+-+-#+- 
+-\55#5 
5> 1 1# 1 
 1H!WJ+=+=#+= '+= 
	+=^r   