
    ^j=              
          d Z ddlZddlZdZg dZg dZ ej                  d      Z ej                  d      Z ej                  d       ej                  d	       ej                  d
       ej                  dej                         ej                  dej                        gZ
 ej                  dej                         ej                  d       ej                  d      gZ ej                  d       ej                  dej                         ej                  dej                         ej                  dej                         ej                  d      gZ ej                  d       ej                  d       ej                  d       ej                  d       ej                  d       ej                  dej                         ej                  dej                        gZ ej                  dej                        Z ej                  d      ZdZe de dZd Z ej                  e e d!ej                        Z ej                  d"ej                        Z ej                  d#      Z ej                  d$      Z ej                  d%      Zd&ed'ee   fd(Zd&ed'efd)Z ej                  d*ej                        Z ej                  d+      Z ej                  d,ej                         ej                  d-ej                         ej                  d.ej                        gZ  ej                  d/ej                        gZ! ej                  d0ej                        Z"d1ed'ee#eef      fd2Z$d3ee   d4ed'efd5Z%d=d3ee   d1ed'ee#eef      fd6Z&d3ee   d7ed'efd8Z'd=d9ed:ed'e(fd;Z)d&ed'efd<Z*y)>u   
critical_gap.py - 보고서 CRITICAL 이슈 미수정 검증 verifier

보고서에서 CRITICAL 이슈가 보고되었으나 수정이 확인되지 않으면 FAIL.
    Nz"/home/jay/workspace/memory/reports)CRITICALcriticalu   심각u	   블로커)u   수정 완료u   해결fixedresolvedz\b(CRITICAL|critical)\bu:   (?:^|[\s,。、!?])(?:심각|블로커)(?:[\s,。、!?]|$)u5   ^\s*#{1,6}\s+.*(?:CRITICAL|critical|심각|블로커)u3   ^\s*[-*]\s+.*(?:CRITICAL|critical|심각|블로커)u4   ^\s*\d+\.\s+.*(?:CRITICAL|critical|심각|블로커)zseverity\s*[:=]\s*criticalz(!\[(?:security-critical|critical|high)\]z\*\*\[.*resolved.*\]\*\*u   ✅\s*RESOLVEDu   ~~.*(critical|심각).*~~u   수정\s*완료z	\bfixed\bz\bresolved\bu>   해결(?:됨|되었|되었음|됐|책|책을|책이|\s*완료)u   미\s*해결u   미\s*완료u   해결\s*되지\s*않u   해결\s*안\s*됨u   해결\s*못
unresolvedznot\s+(?:fixed|resolved)zP\[(?:CRITICAL|HIGH)\]|!\[(?:critical|high)\]|severity\s*[:=]\s*(?:critical|high)uu   (?:인증|권한\s*검증|권한|암호화|패치|수정|조치|해결)\s*없음|(?:secure_mode|timeout|port)\s*=\s*0u"   (?:CRITICAL|HIGH|심각|블로커)z
(?:\s*/\s*z)*u=   (?:\s*[:=]\s*|\s+)(?:(?:신규|총|new|fresh|unresolved)\s*)*u   (\d+)\s*(?:건|개)?us   (?:HIGH|CRITICAL|심각|블로커|이슈|issue|finding|findings|취약점|건수|count|미해결|unresolved|신규)u   없음|없습니다|없다u   (?<!\d)0\s*(?:건|개)u   (?<!\d)[1-9]\d*\s*(?:건|개)linereturnc                     t         j                  |       D cg c]  }t        |j                  d             c}S c c}w )u_  줄에서 severity 토큰에 결합된 count 들을 (좌→우) 추출한다.

    예:
      "CRITICAL: 0, HIGH: 1"               -> [0, 1]
      "CRITICAL 0개, HIGH 2개"             -> [0, 2]
      "HIGH/CRITICAL 0건"                  -> [0]
      "fresh unresolved HIGH/CRITICAL = 0" -> [0]
      "CRITICAL=0"                         -> [0]
       )_ZC_BIND_REfinditerintgroup)r   ms     &teams/shared/verifiers/critical_gap.pyparse_severity_countsr   k   s/     &1%9%9$%?@C
O@@@s   !<c                 F   t         j                  |       ryt        j                  |       ryt        |       }|rt	        d |D              S t
        j                  |       rAt        j                  |       ryt        j                  |       rt        j                  |       syy)u,  줄의 모든 severity count 가 0(이슈 0건) 맥락이면 True(=이슈 후보에서 제외).

    redesign(task-2723+3): 줄 전체 suppress 가 아니라 severity-count 구조 파싱.
    하나라도 count>0 이면 실제 이슈로 보고 suppress 하지 않는다(False → 탐지 유지).
    Fc              3   &   K   | ]	  }|d k(    yw)r   N ).0cs     r   	<genexpr>z)_is_zero_count_context.<locals>.<genexpr>   s     *a16*s   T)	_ZC_STRONG_ISSUE_MARKERsearch_ZC_VULN_NEGATION_DENYLISTr   all_ZC_COUNT_ANCHOR_ZC_ABSENCE_EXPR_ZC_ZERO_UNIT_ZC_NONZERO_UNIT)r   countss     r   _is_zero_count_contextr"   x   s     %%d+!((."4(F*6***t$""4(%.>.E.Ed.K    ur   ^#{1,6}\s+(?:CRITICAL\s*이슈|발견된?\s*이슈|발견된?\s*문제|미해결\s*항목|문제점|이슈|issues)z
^#{1,6}\s+ul   ^\s*(?:-\s*)?(?:우선순위|priority|level|발사\s*우선순위|dispatch\s*level|작업\s*레벨)\s*[:：]z"^\s*(?:#{1,6}\s+)?\[CRITICAL\]\s*$z--level\s+criticalz:\bcritical\s+(?:path|chain|section|mass|period|thinking)\buG   level|priority|우선순위|\[CRITICAL\]|--level|dispatch|발사|발행contentc                 "   g }d}t        | j                         d      D ]l  \  }}t        j                  |j	                               rd}|j                  ||f       ?|sBt        j                  |      rd}Z|j                  ||f       n |S )uu  
    보고서 전체 문자열에서 이슈 섹션 안의 줄들만 추출한다.

    Args:
        content: 보고서 전체 문자열

    Returns:
        (line_num, line) 튜플 리스트 — 이슈 섹션 헤더 제외, 섹션 내 모든 줄 포함.
        이슈 섹션 헤더를 만나면 수집 시작, 다른 마크다운 헤더를 만나면 수집 종료.
    Fr   startT)	enumerate
splitlines_ISSUE_SECTION_HEADERSmatchstripappend_ANY_HEADER)r$   resultin_issue_sectionir   s        r   _extract_issue_sectionsr2      s     %'FW//1; %4!''

5# MM1d)$  &#( MM1d)$% Mr#   linesline_numc                     |dz
  }t        d|dz
        }t        t        |       |dz         }t        ||      D ]"  }||k(  r	t        j                  | |         s" y y)u  
    line_num(1-based) 줄 앞뒤 ±2 줄에 메타 키워드가 있으면 True.

    Args:
        lines: 전체 줄 목록 (0-indexed)
        line_num: 검사 기준 줄 번호 (1-based)

    Returns:
        True if metadata context detected in surrounding ±2 lines.
    r   r         TF)maxminlenrange_METADATA_CONTEXT_KEYWORDSr   )r3   r4   idxr'   endjs         r   _is_metadata_contextr@      sj     Q,C37OE
c%j#'
"C5# 8%,,U1X6	
 r#   c                    g }|rt        |      }|sg S |}nt        t        | d            }|D ]  \  }t        j	                        st
        j	                        s2t        fdt        D              rKt        t        j                              }|rt               }t        D ]n  }|j                        D ]X  }	|	j                  d      j                         j                  d      }
|
dk\  s7|j                  |	j                         |
z          Z p |D 	ch c]  }	|	j                          }}	|r|j!                  |      rt#        | |      r,t        fdt$        D              sFt'              rSt        fdt(        D              rm|j+                  |j-                         f        |S c c}	w )Nr   r&   c              3   @   K   | ]  }|j                          y wNr   r   pr   s     r   r   z)_detect_critical_lines.<locals>.<genexpr>  s     <!qxx~<   r   r   c              3   @   K   | ]  }|j                          y wrC   rD   rE   s     r   r   z)_detect_critical_lines.<locals>.<genexpr>  s     Aa188D>ArG   c              3   @   K   | ]  }|j                          y wrC   rD   rE   s     r   r   z)_detect_critical_lines.<locals>.<genexpr>  s     @!qxx~@rG   )r2   listr(   _CRITICAL_RE_ENr   _CRITICAL_RE_KOany_META_LABEL_PATTERNSr   set_GENERIC_PHRASE_PATTERNSr   lowerfindaddr'   issubsetr@   ISSUE_MARKER_PATTERNSr"   RESOLVED_INLINE_PATTERNSr-   r,   )r3   r$   foundissue_lines
candidatesr4   critical_matchesgeneric_spansgpr   cicritical_positionsr   s               @r   _detect_critical_linesr_      s   #%E -g6I 
)E34
$ !/$&&t,0F0Ft0L<';<< 8 8 >?EM. :T* :A))+00<BQw%))!'')b.9	:: 6F!F!'')!F!F!&8&A&A-&Px0A+@AA "$'@'?@@h

-.C!/D L# "Gs    Gcritical_line_numc                     | |d D ]5  t        fdt        D              rt        fdt        D              s5 y y)u  
    critical_line_num 이후 줄에서 RESOLVED 마커 탐지. 부정 컨텍스트는 차단.

    Args:
        lines: 전체 줄 목록
        critical_line_num: CRITICAL 이슈가 발견된 줄 번호 (1-based)

    Returns:
        True if resolved marker found after critical_line_num (단, 같은 줄에
        부정 힌트가 있으면 해당 줄은 후보에서 제외).
    Nc              3   @   K   | ]  }|j                          y wrC   rD   rE   s     r   r   z)_detect_resolved_after.<locals>.<genexpr>3  s     9!qxx~9rG   c              3   @   K   | ]  }|j                          y wrC   rD   rE   s     r   r   z)_detect_resolved_after.<locals>.<genexpr>6  s     :!qxx~:rG   TF)rM   _UNRESOLVED_HINTS_RESOLVED_PATTERNS)r3   r`   r   s     @r   _detect_resolved_afterrf   $  sF     '() 9'899:'9:: r#   task_idreport_pathc                 x   | sddgdS |r|}n't         j                  j                  t        |  d      }t         j                  j	                  |      s	dd| gdS 	 t        |dd      5 }|j                         }d	d	d	       j                         }|r|j                         sddgdS t        ||      }|s	dd| gdS g }g }	|D ]d  \  }
}t        ||
      }t        |      }|r|j                  d|
 d|        7|j                  d|
 d|        |	j                  d|
 d|        f |	r$|j!                  ddt#        |	       d       d|dS |j!                  ddt#        |       d       d|dS # 1 sw Y   xY w# t        $ r)}dd
t        |      j                   d| gdcY d	}~S d	}~ww xY w)u  
    보고서에서 CRITICAL 이슈 확인 + 수정 여부 검증.

    보고서 경로: /home/jay/workspace/memory/reports/{task_id}.md
    - CRITICAL 이슈가 있는데 수정 확인이 없으면 FAIL
    - CRITICAL 이슈가 있고 수정 확인도 있으면 PASS
    - CRITICAL 이슈가 없으면 PASS (이슈 없음)
    - 보고서 파일 없으면 SKIP
    - 빈 보고서 → PASS

    Returns:
        {"status": "PASS"|"FAIL"|"WARN"|"SKIP", "details": [...]}
    SKIPzNo task_id provided)statusdetailsz.mdzReport not found: rzutf-8)encodingNzFailed to read report: z: PASSu&   Report is empty — no CRITICAL issuesz$No CRITICAL issues found in report: zCRITICAL (line z) RESOLVED: z) UNRESOLVED: zline r   u	   FAIL — z CRITICAL issue(s) not resolvedFAILu	   PASS — z CRITICAL issue(s) all resolved)ospathjoinDEFAULT_REPORTS_DIRexistsopenreadOSErrortype__name__r)   r,   r_   rf   _extract_issue_descriptionr-   insertr:   )rg   rh   rr   fr$   er3   critical_linesrl   r   r4   line_contentr   
issue_descs                 r   verifyr   ;  s
     .C-DEE ww||/G9CA 77>>$,TF34
 	

$g. 	!ffhG	  E  .V-WXX ,E7;N>tfEF
 	
 GJ"0 @,)%:/=
NN_XJl:,OPNN_XJnZLQRhZr*>?@ qIc*o%66UVW W55NN1	#n"5!66UVW11S	 	 
1$q'2B2B1C2aSIJ
 	

s6   F ,E;=F ;F F 	F9F4.F94F9c                     t        j                  dd|       j                         }t        |      dkD  r|dd dz   S |r|S | dd S )uA   CRITICAL 이슈 줄에서 설명 텍스트 추출 (최대 80자).z^[#\-*\d\.\s]+ P   Nu   …)resubr,   r:   )r   cleaneds     r   r{   r{     sP     ff&D1779G
7|bs|e##7,49,r#   )r   )+__doc__rq   r   rt   CRITICAL_KEYWORDSRESOLVED_KEYWORDScompilerK   rL   
IGNORECASErU   rV   re   rd   r   r   _ZC_SEVERITY_ZC_SEVERITY_CLUSTER_ZC_GAPr   r   r   r   r    strrJ   r   r   boolr"   r*   r.   rN   rP   r<   tupler2   r@   r_   rf   dictr   r{   r   r#   r   <module>r      sL   
 	:  D  E  "**78"**Z[ BJJGHBJJEFBJJFGBJJ,bmm<BJJ:BMMJ  BJJ*BMM:BJJ !BJJ+,  BJJ!"BJJ|R]]+BJJ.BJJ "--0 BJJPQ	  BJJBJJBJJ'(BJJ$%BJJBJJ}bmm,BJJ*BMM: " %"**WMM  (RZZ-  5'.
<.C  K bjjgY&:;MM 2::%MM 
 2::;< 

452::>? 
A 
AS	 
A  6 $	 MM  bjj' BJJw
 BJJ4bmmDBJJ$bmm4  BJJE
  (RZZNMM S T%S/-B <S	 S T 0/$s) /c /4cSVhCX /d$s)   .I2C I2c I24 I2X-S -S -r#   