# 워드프레스 플러그인 검토 보안 요청사항 대응 보고서

검토 ID: R youelblocks/mosksmin/12Jan26/T4 29Jan26/3.8RC2

## 1. Unclosed ob_start()

**요청**: `ob_start()` 사용 시 같은 논리 흐름(함수 스코프) 내에서 반드시 `ob_get_clean()` 등으로 버퍼를 닫을 것. 훅·예외 등으로 닫기 로직이 우회되지 않도록 할 것.

**대응**:

- **파일**: `includes/class-youelblocks-admin-pages.php` (마이그레이션 페이지 인라인 스크립트)
- **수정**: `ob_start()` 직후부터 `ob_get_clean()` 직전까지를 **try-finally** 블록으로 감쌌습니다.
  - `finally`에서 `ob_get_clean()` 및 `wp_add_inline_script()` 호출로, 예외·훅으로 인한 중간 종료 시에도 버퍼가 항상 닫히도록 했습니다.
- **기타**: `class-youelblocks-public.php`의 4곳 `ob_start()`는 모두 같은 함수 내에서 `return ob_get_clean();`으로 닫혀 있음을 확인했습니다.

---

## 2. Nonces and User Permissions

**요청**: `$_POST`/`$_GET`/`$_REQUEST` 사용 시 nonce 검증. AJAX는 `wp_ajax_*`에서도 nonce 검증. 권한은 `current_user_can()`으로 검사하고, nonce만으로 권한 판단하지 말 것.

**대응 현황**:

| 위치                                | 함수/메서드                        | 비고                                                                                                                                                                                                                                                                                              |
| ----------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `class-youelblocks-admin-ajax.php`  | `ajax_get_table_structure()`       | ✅ 함수 시작 시 nonce 검증(`YOUELBLOCKS_NONCE_ADMIN`) 후 `verify_permission('manage_options')`                                                                                                                                                                                                    |
| `class-youelblocks-admin-ajax.php`  | `ajax_get_block_table_structure()` | ✅ 동일하게 nonce + 권한 검증                                                                                                                                                                                                                                                                     |
| `class-youelblocks-admin.php`       | `ajax_run_sync()`                  | ✅ `current_user_can('manage_options')` 후 nonce 검증(`YOUELBLOCKS_NONCE_MIGRATION`)                                                                                                                                                                                                              |
| `class-youelblocks-admin-pages.php` | `process_field_migration()`        | ✅ `$_POST['_wpnonce']`로 nonce 검증(`YOUELBLOCKS_NONCE_FIELD_MIGRATION`) 후 `wp_die` on failure                                                                                                                                                                                                  |
| `class-youelblocks-admin.php`       | `pages_page()`                     | ✅ `current_user_can('manage_options')` 후, `action === 'manage_blocks'`일 때만 `$_GET['page_id']` 사용하며, 해당 경로에서 `wp_verify_nonce('youelblocks_manage_blocks_' . $page_id)` 검증. GET은 “어떤 뷰를 보여줄지” 선택용이며, 민감한 동작(manage_blocks) 전에 nonce 검증함. 주석으로 명시함. |

- **추가 수정**: `pages_page()`의 `$_GET['action']`/`$_GET['page_id']` 사용 직전에, “nonce는 manage_blocks 표시 시 검증됨”을 설명하는 주석과 phpcs:ignore(Recommended)를 추가했습니다.

---

## 3. Data Must be Sanitized, Escaped, and Validated

**요청**: 입력은 가능한 빨리 sanitize·validate, 출력 시점에 escape. “Sanitize early, Escape late, Always validate.”

**대응 현황**:

| 위치                                | 내용                                | 비고                                                                                                                                       |
| ----------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `class-youelblocks-admin-pages.php` | 조건 JSON / 스텝 JSON textarea 출력 | ✅ `sanitize_textarea_field(wp_unslash($_POST['...']))` 후 `esc_textarea()`로 출력. nonce는 폼 저장(save_conditions/save_steps) 시 검증됨. |
| `class-youelblocks-public.php`      | 이메일 HTML 본문의 필드값 `$value`  | ✅ `is_scalar($value)` 확인 후 `sanitize_text_field((string)$value)` 적용 후 `esc_html()`로 출력 (라인 2062, 2391 등).                     |

- **정리**: 검토서에 언급된 예시(condition_json, steps_json, 이메일 value)는 현재 코드에서 모두 **먼저 sanitize 후 escape** 되고 있습니다.

---

## 4. 기타 보안 관련 보완 (이전 대화에서 반영된 사항)

- **Admin AJAX**: `ajax_create_table`, `ajax_delete_table`, `ajax_force_update_table`, `ajax_cleanup_orphaned_table`, `ajax_get_data_row`, `ajax_update_data_row`, `ajax_delete_data_row`, `ajax_export_data` 등 관리자 전용 AJAX에 nonce 검증(`YOUELBLOCKS_NONCE_ADMIN`) 추가.
- **Public**: `ajax_get_page_content`에서 nonce 실패 시 `return` 추가, `$page_id`는 `absint()` 사용.
- **Utils**: `get_page_table_name($page_id)`에서 테이블명에 `absint($page_id)` 사용.
- **Admin**: 필드 목록 “예/아니오” 출력에 `esc_html()` 적용.
- **Admin AJAX**: 외부 데이터 fetch 시 `$_POST['params']` JSON 디코드 후 `YOUELBLOCKS_Utils::sanitize_json_data()` 적용.

---

## 5. 검토 시 참고 사항

- **60 incidences**: 검토팀에서 언급한 “60건”은 동일 유형의 여러 발생을 포함할 수 있습니다. 위 1~3항과 기타 보완으로, nonce/권한/sanitize/escape 관련 패턴을 플러그인 전반에서 맞추었습니다.
- **False positives**: nonce는 “입력 출처 검증”용이며, `pages_page()`처럼 GET으로 뷰만 선택하고 실제 민감한 동작(manage_blocks) 전에 nonce를 검증하는 경우는 주석으로 의도를 명시했습니다.
- **테스트**: 수정 후 깨끗한 워드프레스 설치에서 `WP_DEBUG` 켜고 동작·콘솔/로그 확인을 권장합니다.

---

_이 문서는 검토 보안 요청사항에 대한 대응 내역을 정리한 것입니다. 최종 판단은 워드프레스 플러그인 검토팀에 있습니다._
