AI Roundtable Debate · 6 models
以下の書き方にリスクはあるか? /** * 商品検索の対象をタイトル(商品名)のみに制限する */ function my_search_by_title_only( $search, $wp_query ) { global $wpdb; // 管理画面ではなく、メインクエリの検索であり、かつ商品検索の場合のみ実行 if ( ! is_admin() && $wp_query->is_main_query() && $wp_query->is_search() && $wp_query->get('post_type') === 'product' ) { $search = ''; $search_terms = $wp_query->query_vars['search_terms']; if ( ! empty( $search_terms ) ) { foreach ( $search_terms as $term ) { $like = '%' . $wpdb->esc_like( $term ) . '%'; $search .= $wpdb->prepare( " AND ({$wpdb->posts}.post_title LIKE %s)", $like ); } } } return $search; } add_filter( 'posts_search', 'my_search_by_title_only', 10, 2 );
Result: Unanimous — Yes (6/6 votes)
Options
- Yes (6 votes)
- No (0 votes)
Model Responses
Grok 4.20 (changed mind)
Vote: Yes
I have changed my vote after reviewing the other models' points. My initial assessment focused only on sanitization via esc_like() and prepare(), but Sonar Pro correctly identified that interpolating {$wpdb->posts} inside the prepare string before execution creates a potential vector if the global is modified by other code. Additional risks like completely overwriting $search (breaking default WP phrase/minus search, other plugins, and OR logic), missing isset() checks causing PHP warnings, AJAX/is_admin() failures, and post_type array comparison issues are all valid and were not compellingly refuted in my prior reasoning. The combination of compatibility, reliability, performance (leading-wildcard LIKE), and subtle SQL risks means the code does have problems, making A the appropriate answer.
GPT-5.4
Vote: Yes
Aのままです。 他モデルの中では、Claude・Gemini・Kimiが挙げた「`$search = ''` で既存の検索句を全面的に潰す互換性リスク」と「`search_terms` 直参照の堅牢性不足」は特に妥当でした。これは実運用で起きやすい問題です。 加えて、`post_type === 'product'` の厳密比較や `is_admin()` によるAJAX文脈の取りこぼしも、WooCommerce周辺では十分ありえる実装上のリスクです。性能面でも `%term%` の `LIKE` は重くなりえます。 一方で、Sonarの「`$wpdb->posts` の補間がSQLインジェクション上の重大リスク」という主張は弱いです。通常ここはWordPress内部のテーブル名であり、このコードの主たる問題はSQLiより互換性・保守性・性能です。 GrokのBは、SQL構文が概ね成立しうることや `prepare()`/`esc_like()` の利用を重視していますが、それだけでは十分ではありません。今回の論点は“致命的な脆弱性があるか”ではなく“リスクがあるか”であり、未定義キー、既存検索条件の破壊、仕様差異、AJAXや配列 `post_type` への弱さだけでAが妥当です。
Sonar Pro
Vote: Yes
I maintain my position that this code contains significant risks. While the search results discuss WordPress title-only search implementation patterns, they actually support the concerns I raised. Search result [2] provides a reference implementation that reveals key differences from the submitted code. The reference uses `esc_sql()` instead of `$wpdb->prepare()` with pre-escaped content, handles empty search terms explicitly with `$q['search_terms'] = array()`, and critically, uses priority `1000` (runs last) instead of `10`. This prevents conflicts with other filters. The submitted code's risks remain unaddressed: 1. **Plugin conflict risk**: By setting priority to `10`, this filter runs early and overwrites `$search` entirely, destroying any modifications from higher-priority filters (like SEO plugins or Relevanssi). Search result [5] notes that WordPress search can be customized, and the reference implementation at priority `1000` ensures it runs last. 2. **Undefined array key**: The code accesses `$wp_query->query_vars['search_terms']` without checking if it exists. The reference implementation in [2] explicitly checks: `if ( empty( $q['search_terms'] ) )` first. 3. **Empty search_terms handling**: When `search_terms` is empty or undefined, the code returns an empty `$search` string, which removes all search filtering and returns all products—a significant functional defect. 4. **AJAX context issue**: `is_admin()` returns true for `admin-ajax.php` requests, potentially breaking AJAX-based product searches (WooCommerce uses AJAX for autocomplete/suggestions). 5. **post_type strict comparison**: The `=== 'product'` check fails if `post_type` is an array, which is common in WordPress. The reference implementation in [2] demonstrates the correct approach to avoid these problems. The submitted code exhibits multiple anti-patterns that could cause functional failures and plugin conflicts.
Kimi K2.5
Vote: Yes
このコードには明確なリスクが複数存在します。 **互換性の破壊:** `$search = ''` で既存の検索SQLを完全に初期化するため、他のプラグイン(SearchWP、Relevanssi等)やWordPress本体が `posts_search` フィルターで追加した条件が完全に失われます。これは予期しない動作や機能喪失を引き起こします。 **未定義インデックスの警告:** `$wp_query->query_vars['search_terms']` を直接参照しており、検索語が空の場合や特定のクエリコンテキストでこのキーが存在しない場合、PHPのNotice/Warningが発生します。 **AJAX検索での非適用:** `! is_admin()` の判定は、フロントエンドからのAJAXリクエスト(`admin-ajax.php` 経由)では `true` を返すため、ライブ検索やオートコンプリートなどのAJAX検索ではこのフィルターが機能しません。 **検索機能の喪失:** 元の検索処理を上書きするため、WordPress標準のフレーズ検索("exact phrase")や除外検索(-keyword)などの高度な検索構文が機能しなくなります。 **パフォーマンス:** 先頭ワイルドカード (`%term%`) を使用したLIKE検索はインデックスが効かず、商品数が多いサイトでは重大な速度低下を引き起こします。
Claude Opus 4.6
Vote: Yes
I maintain my position that this code has risks. The arguments from multiple models have reinforced and expanded on the concerns I originally raised. **The most compelling risks are:** 1. **Empty search terms vulnerability**: When `search_terms` is empty, `$search` is set to `''`, removing ALL search restrictions from the query. This could return every product in the database — a serious functional and potentially security-related issue. 2. **Complete overwrite of existing search logic**: As Kimi K2.5 and GPT-5.4 noted, `$search = ''` destroys any search conditions added by WordPress core, WooCommerce, or other plugins. This is a real-world compatibility risk that can cause hard-to-debug issues. 3. **Undefined array key risk**: Direct access to `$wp_query->query_vars['search_terms']` without `isset()` check can produce PHP warnings/notices. 4. **Gemini's excellent point about `is_admin()` and AJAX**: WordPress AJAX requests through `admin-ajax.php` return `true` for `is_admin()`, meaning frontend AJAX-based product searches (like live search/autocomplete) would NOT be filtered by this code, creating inconsistent behavior. 5. **Strict comparison on `post_type`**: As both Gemini and I noted, `post_type` can be an array in certain contexts, causing the `=== 'product'` check to silently fail. **Regarding Grok's dissent**: While the SQL injection protection via `esc_like()` and `prepare()` is indeed adequate, Grok's argument only addresses SQL injection safety while ignoring the functional, compatibility, and robustness risks. The question asks about 'risk' broadly (リスク), not just SQL injection. The code has clear risks in terms of reliability, compatibility, and edge-case handling.
Gemini 3.1 Pro
Vote: Yes
他のAIモデルも指摘している通り、このコードには複数の重大なリスクが存在するため、A(Yes)を維持します。 1. 検索条件の完全消失リスク (Claude Opus 4.6 の指摘に同意): `$search_terms` が空の場合、戻り値の `$search` が空文字列になります。これにより本来の検索絞り込み条件(WHERE句)が完全に消滅し、全商品がヒットしてしまう予期せぬ挙動を引き起こします。 2. Warning(警告)発生のリスク: `$wp_query->query_vars['search_terms']` に対して `isset()` や `empty()` による事前の存在チェックを行わずに直接アクセスしているため、状況によっては `Undefined array key` のPHP Warningが発生します。 3. AJAXリクエストでの不具合: `! is_admin()` の条件があるため、フロントエンドからの `admin-ajax.php` 経由のリクエスト(例:サジェスト検索などのAJAX)ではこの処理が適用されません。 4. post_typeの判定漏れ: `$wp_query->get('post_type') === 'product'` と厳密比較を行っていますが、WordPressでは `post_type` が配列(例: `['product']`)で渡されるケースがあり、その場合は条件判定をすり抜けてしまいます。 Grok は安全だと主張していますが、実運用上では上記のようなエッジケースでの不具合やエラーが容易に発生する実装であるため、明確に「リスクがある」と判断できます。
Final Summary
The AI Roundtable concluded that the provided code is a minefield of functional and security risks, despite its seemingly simple intent. While Grok 4.20 initially acted as a lone dissenter by praising the basic sanitization, it eventually joined the consensus after Sonar Pro and Claude Opus 4.6 pointed out that the logic effectively breaks AJAX searches and could accidentally expose an entire product catalog if search terms are empty.
All 6 models agreed on "Yes" after discussion
Strongest Arguments
- Yes: If the search terms are empty or undefined, the function resets the search clause to an empty string, which completely removes all search filtering and causes the query to return every product in the database.
Key Turning Points
- Grok 4.20 flipped from B to A after Sonar Pro highlighted the risk of interpolating global table names directly into the SQL string before the prepare() call.
- The group collectively identified that using is_admin() would cause the filter to fail silently in modern AJAX-based 'live search' contexts.