
    j"                        U d Z ddlmZ ddlZddlZddlZddlZddlm	Z	m
Z
 ddlmZ ddlmZmZmZ dZded	<   d
Zded<   dZded<    G d de      Zd Z	 d	 	 	 ddZeddd	 	 	 	 	 ddZy)u  anu_v2.owner_trigger_http_post — production GitHub comment http_post 구현체 + token_provider.

회장 verbatim §1~§4 1:1 박제 (2026-05-27 KST, task-2699):

§1  OWNER_GEMINI_TRIGGER_TOKEN 전용 token_provider:
    ``make_owner_trigger_token_provider(env)`` 는 env(또는 os.environ)에서
    ``OWNER_GEMINI_TRIGGER_TOKEN`` 만 읽는다. 부재/빈문자/비문자열 → TokenBoundaryViolation
    raise. BOT_GITHUB_TOKEN/GH_TOKEN/GITHUB_TOKEN/OWNER_PAT 등 fallback 절대 0.

§2  production http_post 구현체:
    ``make_production_http_post(*, api_base, dry_run, opener)`` 가 반환하는 callable은
    urllib.request 로 POST 를 실행한다. dry_run=True 면 실제 네트워크 호출 0.
    방어적 재검증 (assert_endpoint_allowed) 을 http_post 내부에서 이중 실행.

§3  보안 원칙:
    token raw value / Authorization header 를 예외 메시지 / 로그 / 반환 dict 어디에도
    미포함. 반환 dict 는 {"status": code, "endpoint": path, "method": method,
    "response": <_redact_tokens 적용 결과>} 형식.

§4  one-way isolation:
    본 모듈은 anu_v2 내부 + stdlib 만 import. 외부(utils/dispatch/scripts) 의존성 0.
    테스트 시 make_production_http_post(opener=<mock>) 으로 네트워크 교체 가능.
    )annotationsN)CallableMapping_redact_tokens)TOKEN_ENV_NAMETokenBoundaryViolationassert_endpoint_allowedzhttps://api.github.comstrGITHUB_API_BASE   int_HTTP_TIMEOUT)ghp_ghs_gho_ghu_ghr_github_pat_ztuple[str, ...]_GITHUB_TOKEN_PREFIXESc                      e Zd ZdZy)OwnerTriggerHttpErroru   GitHub API HTTP 실패 시 raise.

    보안 원칙: 메시지에 token / Authorization / headers 절대 미포함.
    status code + endpoint path 만 포함.
    N)__name__
__module____qualname____doc__     @/home/jay/workspace/scripts/../anu_v2/owner_trigger_http_post.pyr   r   =   s    r   r   c                0    fd t        |             S )u  _redact_tokens + _GITHUB_TOKEN_PREFIXES 이중 redaction (defense-in-depth).

    먼저 기존 _redact_tokens(value) 를 적용한 뒤, 결과를 재귀 순회하며
    str 값이 _GITHUB_TOKEN_PREFIXES 에 속하는 prefix 로 시작하면 "***REDACTED***"
    로 치환. dict key/value, list, tuple 모두 재귀 처리.

    Args:
        value: 임의 JSON-serializable 값.

    Returns:
        token prefix 로 시작하는 str 이 ***REDACTED*** 로 치환된 결과.
    c                f   t        | t              r| j                  t              ry| S t        | t              r0| j                         D ci c]  \  }} |       |       c}}S t        | t        t        f      r&| D cg c]
  } |       }} t        |       |      S | S c c}}w c c}w )Nz***REDACTED***)	
isinstancer   
startswithr   dictitemslisttupletype)vkvalitemwalked_walks        r   r.   z%_redact_owner_response.<locals>._walkV   s    a||23'Ha78wwyAVQE!HeCj(AAa$'./0deDk0F0476?"	 B0s   B(B.r   )valuer.   s    @r   _redact_owner_responser0   H   s    
 &''r   c                     d fd}|S )u  ``OWNER_GEMINI_TRIGGER_TOKEN`` 전용 token_provider 팩토리.

    반환 callable 은 ``env``(또는 os.environ)에서 ``OWNER_GEMINI_TRIGGER_TOKEN``
    만 읽는다.

    부재 / 빈문자 / 비문자열 → TokenBoundaryViolation raise.
    BOT_GITHUB_TOKEN / GH_TOKEN / GITHUB_TOKEN / OWNER_PAT 등 fallback 절대 0.

    Args:
        env: 환경변수 dict. None 이면 os.environ 에서 읽음.

    Returns:
        Callable[[], str] — 호출 시마다 TOKEN_ENV_NAME 값 반환.
    c                     nt         j                  } | j                  t              }t	        |t
              r|st        t         d      |S )Nut    not set or empty — owner trigger fail-closed. BOT_GITHUB_TOKEN/GH_TOKEN/GITHUB_TOKEN/OWNER_PAT fallback 절대 0.)osenvirongetr   r"   r   r	   )sourcetokenenvs     r   	_providerz4make_owner_trigger_token_provider.<locals>._providerz   sR    +.?C



>*%%U(!" #V W  r   )returnr   r   )r8   r9   s   ` r   !make_owner_trigger_token_providerr;   h   s    $ r   F)api_basedry_runopenerc                0     	 	 	 	 	 	 	 	 	 	 d fd}|S )u#  production GitHub comment http_post callable 팩토리.

    반환 callable ``_http_post(method, path, body, headers) -> dict``:
      1. assert_endpoint_allowed 방어적 재검증 (이중 안전).
      2. dry_run=True 면 네트워크 호출 0 — {"dry_run": True, ...} 반환.
      3. dry_run=False 면 urllib.request 로 POST 실행.
      4. 응답 dict 는 _redact_tokens 적용 후 반환.
      5. 예외 시 OwnerTriggerHttpError raise. headers/token 미포함.

    Args:
        api_base: GitHub API 베이스 URL. 기본 "https://api.github.com".
        dry_run: True 면 실제 네트워크 호출 없이 dry_run dict 반환.
        opener: 테스트 주입용 urllib opener. None 이면 urllib.request.urlopen 사용.

    Returns:
        Callable[[str, str, dict, dict], dict]
    c                   t        | |       j                  d      |z   }rdd|| dS t        j                  |      j	                  d      }t
        j                  j                  |||d      }	 j                  |t              }n%t
        j                  j                  |t              }|j                  }|j                         }		 t        j                  |	      }
||| t!        |
      dS # t        $ r |	j                  dd	
      }
Y .w xY w# t
        j"                  j$                  $ r#}|j&                  }t)        d| d|       d d }~wt
        j"                  j*                  $ r2}t!        t-        |j.                               t)        d|       d d }~wt        $ r(}t!        t-        |             t)        d|       d d }~ww xY w)N/Tr   )r=   statusendpointmethodzutf-8POST)dataheadersrD   )timeoutreplace)errors)rB   rC   rD   responsezhttp_post failed: status=z
 endpoint=z$http_post failed: status=0 endpoint=z%http_post failed: status=-1 endpoint=)r
   rstripjsondumpsencodeurllibrequestRequestopenr   urlopenrB   readloads	Exceptiondecoder0   error	HTTPErrorcoder   URLErrorr   reason)rD   pathbodyrG   urlrF   reqrK   status_coderaw_bodyparsedexcr<   r=   r>   s               r   
_http_postz-make_production_http_post.<locals>._http_post   s    	 - ooc"T)   	  zz$&&w/nn$$	 % 
(	!!;;sM;B!>>11#}1M"//K}}HDH- &  26:	 	  D!CD ||%% 	((K'+K=
4&I ||$$ 	"3szz?3'6tf=  	"3s8,'7v>	sU   3AD C4 $D 4DD DD G3E G1-FG*#GG)
rD   r   r^   r   r_   r$   rG   r$   r:   r$   r   )r<   r=   r>   rf   s   ``` r   make_production_http_postrg      sE    0EEE E 	E
 
EN r   )N)r8   zMapping[str, str] | Noner:   zCallable[[], str])r<   r   r=   boolr:   z&Callable[[str, str, dict, dict], dict])r   
__future__r   rM   r3   urllib.errorrP   urllib.requesttypingr   r   anu_v2.auto_gemini_triager   anu_v2.owner_trigger_onlyr   r	   r
   r   __annotations__r   r   RuntimeErrorr   r0   r;   rg   r   r   r   <module>rq      s   0 #  	   $ 4  0 /s +  L (B %)	!H $	__ _
 ,_r   