fix: NinjaFirewall 相容性 — 保護 WAF 設定選項不被重導向

評估 NinjaFirewall (WP Edition) 4.9 的相容性,結論是兩者可共存且
不需要開發 AddOn(該外掛全 codebase 零個 apply_filters('nfw_*') /
do_action('nfw_*'),官方相容手段全在部署層)。但核心有一個隱患必須補。

TMDO_Options_Manager::register_settings_group() 以 pre_update_option_{key}
回傳 $old_value,讓選項不再落地 wp_options、改存專屬設定表。而
NinjaFirewall 的 Full WAF 走 auto_prepend_file,在 WordPress 載入前就以
原生 mysqli 直查 wp_options 取 nfw_options / nfw_rules。一旦這些鍵被
重導向,WAF 會讀不到設定而靜默停止防護 —— 不報錯、不寫 log。

nfw_rules 約 77KB 且 autoload=auto,正是 autoload 瘦身最誘人的目標,
因此這條路徑並非理論風險。

Added
- PROTECTED_OPTIONS 常數與註冊守衛(nfw_options / nfw_rules / nfw_checked),
  命中時發出 _doing_it_wrong()。守衛置於方法開頭,全部鍵都被擋時提前返回,
  不再建立空的設定表。
- tests/unit/OptionsManagerProtectedTest.php(4 tests / 10 assertions),
  鎖住「受保護鍵絕不會被掛上 pre_option_* / pre_update_option_* 攔截」。
- docs/WAF-COMPATIBILITY.md:模式差異、symlink 多租戶部署、WP SaaS 開站
  流程與驗證清單、三條開發約束、實測風險矩陣。

Changed
- Migration Wizard 輪詢 500ms → 2s,與四個 stress-test 面板一致。
  原本 2 req/s 打同一 REST endpoint,易觸發 WAF rate-limit 與 bot 偵測。

autoload 最佳化不受影響:optimize_autoload() 只改 autoload 欄位、不刪列,
而 Full WAF 的 SELECT * 不看 autoload。

驗證:591 tests / 1166 assertions 通過,PHPCS 零違規,版本一致性 1.0.2。
dev30 於 Full WAF 與 WP WAF 兩種模式下實測,firewall log 中 TMDO 相關
攔截 0 筆。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdjXAU473eekjPBB8vPVRS
This commit is contained in:
2026-08-15 20:38:09 +08:00
parent 9f587c39dc
commit 9fa84845be
7 changed files with 484 additions and 5 deletions
+43
View File
@@ -7,6 +7,49 @@ Versioning follows [Semantic Versioning](https://semver.org/).
---
## [1.0.2] — 2026-08-15 — NinjaFirewall 相容性:WAF 設定選項保護
**性質**:防禦性修正 + 文件。無 schema 變更,無破壞性變更。
### Added
- **`docs/WAF-COMPATIBILITY.md`** — NinjaFirewall (WP Edition) 4.9 相容性評估
結論:兩者可共存,**不需要開發 AddOn**NinjaFirewall 全 codebase 零個
`apply_filters('nfw_*')` / `do_action('nfw_*')`,官方相容手段全在部署層)。
文件涵蓋 WP WAF 與 Full WAF 的模式差異、symlink 多租戶部署的必要設定、
三條開發約束,以及實測風險矩陣。
- **`TMDO_Options_Manager::PROTECTED_OPTIONS`** 常數與註冊守衛
`modules/options/class-tmdo-options-manager.php`
`register_settings_group()` 會以 `pre_update_option_{key}` 回傳 `$old_value`
讓選項不再落地 `wp_options`、改存專屬設定表。但 NinjaFirewall 的 Full WAF 走
`auto_prepend_file`,在 WordPress 載入前就以**原生 mysqli 直查 `wp_options`**
`nfw_options` / `nfw_rules`。一旦這些鍵被重導向,WAF 會讀不到設定而
**靜默停止防護 —— 不報錯、不寫 log**
現以 `PROTECTED_OPTIONS``nfw_options` / `nfw_rules` / `nfw_checked`)在註冊
階段擋下,並發出 `_doing_it_wrong()`。守衛置於方法開頭,全部鍵都被擋時提前
返回,不再建立空的設定表。
註:autoload 最佳化不受影響 —— `optimize_autoload()` 只改 `autoload` 欄位、
不刪列,而該處的 `SELECT *` 不看 autoload。
- **`tests/unit/OptionsManagerProtectedTest.php`** — 4 tests / 10 assertions
鎖住「受保護鍵絕不會被掛上 `pre_option_*` / `pre_update_option_*` 攔截」這個
核心保證,並驗證全數受保護時不對資料庫發出查詢。
### Changed
- **Migration Wizard 輪詢間隔 500ms → 2s**`admin/assets/wpdo-migration-wizard.js`
原本 2 req/s 打同一個 REST endpoint,容易觸發 WAF 的 rate-limit 與 bot 偵測。
改為 2 秒,與四個 stress-test 面板既有的輪詢節奏一致。
---
## [1.0.1] — 2026-08-08 — Schema Registry 冪等性修正(cold zone 欄位重複累積)
**性質**:核心 bug 修復。無 schema 變更,無 API 變更,無破壞性變更。