コンテンツへスキップ

`update-core.php` のバージョン番号抽出が誤検出する罠 — プラグイン版番号と本体バージョンを区別する3段階ガード

SSH が使えない環境では、Playwright で update-core.php(WordPress 更新画面)を開き、ページのテキストから「WordPress 本体が今何バージョンか」を読み取る処理を書くことがある。ある現場の運用ログで、本体バージョンとして 1.7.11 のようなありえない値が記録される事故が起きた。

補足: update-core.php は WordPress 管理画面の「更新」ページ。本体・プラグイン・テーマ・翻訳の更新候補が一覧表示される。

何が起きていたか

このページには、本体のバージョン文字列だけでなく、更新候補のプラグインや翻訳パッケージの版番号も大量に含まれている。たとえば「プラグイン X を 1.7.11 に更新」のような文言だ。

# 最初の実装 — ページ全体から最初に見つかった数字列を拾う
match = re.search(r'\d+\.\d+(?:\.\d+)?', page_text)
version = match.group(0) if match else None

この素朴な正規表現は「最初に出てきた N.N.N 形式の数字列」を本体バージョンとして拾ってしまう。ページの構造上、プラグインの更新候補がリストの上位に出ることがあり、結果として 1.7.11(プラグインの版番号)が WordPress 本体のバージョンとして記録された。

なぜ気づきにくいか

このバグは「正規表現がマッチしない」のではなく「間違った値に正しくマッチする」ため、エラーも例外も出ない。レポート上に明らかにおかしい値(WordPress に 1.x 系のバージョンは存在しない)が出力されて初めて気づく類の不具合だった。

対処 — 3段階ガード

  1. 専用セレクタを優先するp.response > strong#wp-version-message strong など、コアの更新メッセージが出る特定の DOM 位置をまず探す
  2. キーワード付き正規表現でフォールバック — セレクタで取れなければ、「WordPress」「バージョン」「Version」という単語の直後に続く数字列だけを拾う
  3. 妥当性チェックで最終防衛 — どちらの経路で取れた値も、最後に「メジャー番号が 4〜9 の範囲内か」を検証する関数に通す
def is_plausible_wp_core_version(ver: str) -> bool:
    m = re.match(r'^(\d+)\.(\d+)(?:\.(\d+))?$', ver.strip())
    if not m:
        return False
    major = int(m.group(1))
    return 4 <= major <= 9

WordPress 本体のメジャーバージョンは現在 4〜9 系に収まっている。プラグインの版番号は 1.x3.x 系が多く、この範囲チェック一つだけで大半の誤検出を弾ける。

設計のポイント

これは「正規表現を厳しくする」だけの話ではない。抽出元(DOM セレクタ)を信頼できる順に並べ、それでも取れた値の意味を最後にもう一度検証するという二重の防御線になっている。スクレイピングしたテキストは、たとえ正規表現にマッチしても「意味として正しいか」は別の話だという前提を、コードの構造に落とし込んだ形だ。

まとめ

内容
1. セレクタ優先 コアの更新メッセージが出る特定の DOM 位置を最初に探す
2. キーワード文脈 「WordPress」「Version」直後の数字列に限定してフォールバック
3. 妥当性検証 メジャー番号 4〜9 の範囲チェックで誤検出を最終防衛

Playwright の strict mode 違反と wp-admin の同一セレクタ問題「管理者メール確認」画面で自動操作が止まる罠と同じく、これも wp-admin の画面を素直に信用できないという前提が出発点になっている。今回は「クリックする対象」ではなく「読み取った値の意味」が信用できないケースで、スクレイピングしたテキストには常に妥当性チェックを挟む癖が、長期的には事故を未然に防ぐと思う。同じ「操作が成功したように見えても実際の状態は別」という罠は、一括選択チェックボックスが反映されない罠にも当てはまる。