점검 시각 2026-08-29 05:06~05:16 KST · 대상 http://13.125.131.126:8018/?debug=1 · 배포/로컬 HTML 해시 일치 · 상태 변경 승인 API는 안전상 호출하지 않음
| 항목 | 관찰값 | 판정 |
|---|---|---|
| 배포 일치 | 공인 IP와 localhost의 H1/H8 HTML SHA-256이 각각 동일 | 현재 프로세스와 조사 소스 일치 |
| Git 정본 | main = origin/main = c0eae55, 총 24커밋 | 원격 최신과 대조 완료 |
| 저장소 구성 | 추적 파일 5개, Python 본체 963줄, GET 라우트 16개 | 테스트·의존성·배포 계약 없음 |
| 페이지/API | H1/H2/H3/H4/H5/H8/H9 및 안전한 읽기 API 응답 점검 | 주요 HTML 200, 잘못된 자산/조합도 200 |
| 서비스 프로브 | 12포트 직접 확인. 8005는 인증 없이 401, 인증 시 200 | H2가 이를 다운으로 오판 |
| 응답 시간 표본 | H1 약 33ms, H8 약 2ms, H9 약 76ms, stats 약 279ms | 현재는 빠르나 동기 외부 I/O로 최악 지연 큼 |
| 공개 노출 | Werkzeug 개발 서버가 0.0.0.0:8018에서 HTTP 제공, 외부 스캔성 접근 로그 확인 | 보안 경계 없음 |
/api/dossier-approve는 호출하지 않았습니다. 하위 서비스는 루트 응답과 화면 동선까지만 읽기 전용으로 확인했습니다.| 사용자/목표 | 현재 여정 | 막히는 지점 | 필요한 화면 |
|---|---|---|---|
| 이사님 · 지금 결정할 것 확인 | H1 정상 문구 → 8005 승인보드 | H1 0건, 실제 13건. 첫 화면만 보면 업무를 놓침 | 단일 정본의 결정 큐, 오류/지연 표시, 증거 우선 카드 |
| 운영자 · 장애/병목 확인 | H1 숫자 → H2 서비스 → H9 창고 | 401을 다운으로 표시하고 실패를 0으로 숨김. 집계 기준도 다름 | 생존/준비/업무 가능 상태와 마지막 성공 시각 분리 |
| 작업자 · 다음 조합 실행 | H1 흐름 → W10 장바구니 → S1 | 현재 두 항목의 캐릭터명이 빈 문자열. 단계명도 화면마다 충돌 | 공통 스키마와 단계 정의, 실패한 필드의 명시적 경고 |
| 승인자 · 근거 보고 발행 | H4 카드 → GET 승인 링크 | 품질 게이트가 실제 품질을 검증하지 않고, 이미지 없는 링크도 노출 | 읽기/승인 분리, POST+재인증, 아티팩트·게이트 증거 |
| 관리자 · H8에서 상황 공유 | 운영/결정/ELI5 목록 → 상세 | 기존 운영 보고 0건, 심각도·상태·담당·검색 없음 | 진단 요약, 근거/재현, 담당/기한, 해결 상태와 변경 이력 |
| ID | 등급 | 문제와 영향 | 근거 | 우선 조치 |
|---|---|---|---|---|
| P0-01 | P0 | 공개 H1/H4 HTML에 승인 비밀이 query 형태로 포함. HTTP·무인증이라 기록/로그/referrer로 유출 가능 | main.py:308, 340, 345-351, 619-625 | 포트 차단/allowlist, 키 회전, 세션 인증+TLS, query 비밀 제거 |
| P0-02 | P0 | 상태 변경과 release 발행이 GET. CSRF·프리패치·재시도·중복 클릭 위험 | main.py:685-704 | POST+CSRF+재인증+idempotency key+감사 로그 |
| P0-03 | P0 | legacy decision이 서명/아티팩트 없이 발행 계약을 우회하고, ledger 동시쓰기·push 실패가 성공처럼 끝날 수 있음 | release_publisher.py:67,104,109,130,139 / main.py:701-704 | 검증된 승인 레코드→원자적 상태 머신→push 확인 후 성공 |
| P0-04 | P0 | Jira/Mongo/보고 메타/stdout을 HTML에 직접 삽입. regex 보고서 세척은 실제 sandbox가 아님 | main.py:553-558, 589-609, 721-728, 840-847, 952-958 | Jinja autoescape/textContent, parser allowlist, CSP, 출력 escape |
| P1-01 | P1 | H1은 승인 대기 0건·전 라인 정상, 실제 8005는 13건. 의사결정 누락 유발 | /api/stats board_pending=0 vs 인증된 8005 화면 13건 | 승인보드의 합성 board_items를 단일 정본 API로 제공 |
| P1-02 | P1 | 8005의 정상적인 401을 다운으로 오판 | main.py:488-499; 직접 응답 401, 인증 시 200 | 전용 /healthz, unauthorized/healthy 분리 |
| P1-03 | P1 | H1 장바구니가 persona.title만 읽어 현재 2개 항목 모두 캐릭터명이 빈 값 | main.py:364-366 vs workbench_web.py:1375-1378 | 공통 BasketItem 스키마; 임시로 title-or-name 호환 |
| P1-04 | P1 | Mongo/Jira/파일 실패를 0·빈 배열·“-”로 바꿔 false-green 생성 | main.py:223-245, 355-378, 629-682 | 값/오류/신선도를 함께 반환하고 UI에 unknown/degraded 표시 |
| P1-05 | P1 | 같은 H1에서 S2가 검수대이기도, 이미지 스튜디오이기도 함. H4 화면 ID도 단계처럼 혼용 | main.py:298-305, 321-327, 416-460 | canonical stage map 1개를 H1/H3/H8/H9가 공통 소비 |
| P1-06 | P1 | “채팅품위”는 횟수만 보고, “하드차단 통과”는 항상 초록. 승인 근거가 실제 검증과 다름 | main.py:645-651, 600-607, 692-700 | 게이트별 evidence ID와 서버 검증을 승인 API에서 재확인 |
| P1-07 | P1 | release 수치가 서로 다른 경로를 읽어 화면 간 드리프트 가능 | main.py:201, 248, 374 | ledger repository 한 곳으로 통합하고 provenance/시각 표기 |
| P1-08 | P1 | H4는 미완성 카드를 대체 노출하고 이미지가 없어도 404 링크 제공. RISU와 전문 버튼도 같은 URL | main.py:586, 639-641, 603-607 | disabled 상태+이유, 실제 준비된 CTA만 렌더 |
| P1-09 | P1 | 없는 자산/잘못된 ObjectId가 빈 200이며, 공개 dossier·H6/H7에 서버 경로/파일명이 노출 | main.py:660-665, 709-736, 767-830 | 404/400 계약, 공개 DTO에서 내부 경로·해시 최소화 |
| P2-01 | P2 | debug가 기본 켜짐이고 배지 클릭으로 꺼지지 않음. .dbgchip 스타일도 없음 | main.py:19-24, 40-70 | 명시적 운영자 모드에서만 켜고 cookie/aria-hidden/안정 ID 수정 |
| P2-02 | P2 | H1 핵심 흐름이 div onclick이라 키보드 접근 불가; landmark/focus/aria-live 부족 | main.py:283-305, 328 | 실제 a/button, main/nav, focus, aria-live |
| P2-03 | P2 | H2/H9 다열 표가 390px에서 과도하게 압축되고 H9는 검색/필터 없이 매우 긴 목록 | 실배포 390px 점검; H9 세로 약 2,474px | 모바일 카드/가로 스크롤, 필터·검색·페이지네이션 |
| P2-04 | P2 | H1/H4 fetch 실패 처리와 자동 갱신이 없어 “-”/“불러오는 중”에 멈춤 | main.py:319-341, 588-610 | loading/error/stale/empty 4상태와 재시도·마지막 성공 시각 |
| P2-05 | P2 | 963줄 단일 파일에 HTML/CSS/데이터/승인 로직 결합; 테스트·manifest·README·CI·배포 설정 없음 | Git 전체 추적 파일 5개, 24커밋 | app factory + routes/services/repositories/templates/tests 분리 |
| P2-06 | P2 | 절대 경로 18곳, 공인 IP 20곳, broad exception 14곳으로 환경 이동·장애 추적이 어려움 | 전체 main.py 정적 집계 | typed config, URL registry, 구조화 로그, 예외별 처리 |
| P2-07 | P2 | 요청 중 Jira 8초, 12포트 순차 최대 24초, S0 전체 파일 순회와 다중 Mongo 집계 | main.py:78-116, 119-167, 237-245, 488-499 | 백그라운드 snapshot+TTL cache, 병렬 probe, timeout budget |
| P3-01 | P3 | H8 상세 debug ID가 H?이며 H8 링크는 자동 ID 대상에서 빠짐 | main.py:20-35, 55-69 | /reports/ prefix 매핑과 안정 식별자 |
| P3-02 | P3 | “다ossier” 혼용, favicon 404, 고정 IP 이동, H5가 H4의 중복 별칭 | main.py:614-625 및 실배포 | 용어 사전·정규 URL·환경별 base URL·기본 자산 정리 |
공개 HTML의 query 비밀 → HTTP 전달 → GET 상태 변경 → legacy 발행기 → push 실패를 확인하지 않는 응답이 한 경로에 이어집니다. 어느 한 군데만 고쳐서는 승인 무결성이 회복되지 않습니다.
main.py:345-351이 파일을 읽어 HTML에 치환하고, 685-704가 GET으로 subprocess를 실행합니다. sibling Git의 release_publisher.py는 legacy 입력을 허용하고 git push 실패를 최종 성공과 분리하지 않습니다./api/stats의 승인 대기 0건과 실제 13건, H2의 승인보드 다운과 실제 인증 시 200, H1 장바구니의 빈 캐릭터명이 모두 같은 원인입니다. 각 화면이 서로 다른 파일/필드/해석을 직접 읽고, 읽기 실패를 빈 값으로 바꿉니다.
/api/flow의 현재 대기 2항목은 module/status가 있으나 persona가 모두 빈 문자열입니다.화면은 “채팅품위·하드차단 통과”를 결정 근거처럼 제시하지만, 채팅품위는 실사 횟수일 뿐이고 하드차단은 정적 초록 문구입니다. 서버 승인도 status == done만 검사합니다. 승인 UI는 증거가 아니라 검증된 gate 결과의 읽기 모델이어야 합니다.
기존 H8은 운영 0건·결정 0건·ELI5 1건의 단순 목록이었습니다. 최소 메타데이터는 title/bot/time/ticket뿐이고 해결 상태·담당·심각도·배포 commit이 없습니다. 본 보고서는 현 계약을 지켜 게시했지만, 장기적으로 arbitrary HTML 대신 구조화된 진단 schema가 필요합니다.
| 관찰 | 배포 영향 | 대조 근거 |
|---|---|---|
5일간 24커밋, main.py 순증 963줄 | 빠른 기능 추가가 모듈 경계·테스트 없이 한 파일에 누적 | 2026-08-24~28, added 1015/deleted 52 |
H8은 2af8567에서 추가 후 본체 개선 없음 | 보고 유형과 운영 요구가 늘었지만 정보 구조는 초기 형태 | Git history 전체 |
W10은 persona title or name 처리 | H1만 구 schema를 고정해 캐릭터가 공백 | workbench_web.py:1375-1378, basket.py:93-99 |
| 승인보드는 decisions 외 requests/Jira/합성 카드를 결합 | H1이 decisions.json만 세어 실제 13건을 놓침 | approval-board/main.py:811-815, 845-881 |
| release publisher는 legacy 입력·무잠금 append·push 실패 허용 | H4의 단순 GET이 정본 발행 무결성을 끝까지 보장하지 못함 | release_publisher.py:67-139 |
| 프로젝트 계약 파일 없음 | 재현 가능한 설치·테스트·운영·rollback 기준 부재 | tracked 5 files; README/requirements/tests/CI/service unit 없음 |
lang, viewport, 일관된 어두운 색상 체계를 갖췄습니다. 주요 화면과 안전한 API는 응답했습니다.aria-hidden 처리해야 합니다.| 영역 | 책임 | 핵심 계약 |
|---|---|---|
app/config.py | 경로·URL·timeout·환경 설정 | 하드코딩 금지, 시작 시 검증 |
domain/pipeline.py | S0~S7와 화면 ID 분리, snapshot schema | 모든 화면이 같은 stage map 사용 |
repositories/ | Mongo/Jira/File/Approval adapter | typed result + error + observed_at |
services/health.py | 병렬 probe와 readiness | healthy/degraded/unauthorized/unknown/down |
services/approval.py | gate 검증·idempotency·감사 | 읽기와 command 분리, 실패 시 성공 금지 |
routes/ + templates/ | 얇은 HTTP·접근 가능한 UI | autoescape, 명시적 4xx/5xx |
reports/schema.py | H8 구조화 진단 | severity/status/owner/commit/evidence/acceptance |
tests/ | 계약·회귀·보안 검증 | false-green, 승인 gate, XSS, 404, schema 고정 |
| 기한 | 작업 | 완료 조건 |
|---|---|---|
| 즉시 0~4h | 8018/관리 포트 외부 차단 또는 allowlist, 노출 키 회전, HTML/query에서 키 제거, 승인 GET 비활성화 | 비인증 인터넷 사용자가 비밀/내부 경로/승인 동작에 접근 불가 |
| 당일 | POST+CSRF+세션 역할, server-side gate, idempotency, push/영수증 확인 | 같은 승인 재전송은 1건만 발행; 모든 실패는 비-2xx와 감사 로그 |
| 1일 | 승인 13건 단일 정본, 401 health, basket name, stage map, false-green 제거 | H1/8005/H2/H3 숫자·단계·상태가 같은 snapshot ID로 일치 |
| 2~3일 | DTO에서 내부 경로 제거, autoescape/CSP/보안 헤더, 명시적 404/오류 UI | XSS/경로노출 회귀 테스트 통과; invalid resource 404 |
| 3~5일 | routes/services/repositories/templates 분리, dependency/README/systemd or container/CI 추가 | 깨끗한 환경에서 설치→테스트→실행→rollback 재현 가능 |
| 1주 | background snapshot, 운영자/승인자/작업자 홈 분리, H8 구조화 schema와 필터 | 의존성 장애에도 마지막 성공값·stale·오류가 1초 내 화면에 표시 |
name/title 계약 테스트와 schema version 고정.H8 계약에 맞춰 동명 HTML fragment와 JSON metadata를 사용했습니다. 금지된 script/iframe/object와 외부 이미지·외부 stylesheet는 포함하지 않았습니다. 이 보고서는 시점 진단이며, 아래 항목이 고쳐졌다는 뜻은 아닙니다.