trAvis - MANAGER
Edit File: wp_cli.sh
#!/bin/bash # ============================================================ # WP-CLI実行スクリプト # # [使用方法] # /opt/sh/wp_cli/wp_cli.sh "PHP_VERSION" "WP_PATH" "WP_COMMAND" # # [引数] # - PHP_VERSION: PHPのバージョン(例: 8.2) # - WP_PATH: WordPressのディレクトリパス(例: /home/user001/example.com/public_html/) # - WP_COMMAND: WP-CLIのコマンド(例: core install) # # [補足] # - cpadmin(WebAPI)がsudoでユーザー権限で実行します(ユーザーが直接実行することはできません) # - ホワイトリスト(ALLOWED_COMMANDS)で許可されていないコマンドは実行できません # ============================================================ # ============================================================ # 事前準備(引数取得、エラーハンドリング) # ============================================================ # エラーハンドリングの強化 set -euo pipefail # 許可されたWP-CLIコマンドのリスト ALLOWED_COMMANDS=( "core" "plugin" "theme" "user" "db" "export" "import" "maintenance-mode" "config" "rewrite" "cache" "cron" "search-replace" "option" "site" "network" "role" "widget" "sidebar" "taxonomy" "term" "comment-meta" "post-meta" "user-meta" "rest" "scaffold" "package" "doctor" "post" "comment" "eval-file" # 当社管理の定型スクリプト実行のみなので許可 "transient" "media" "cldsec-wp-security" # CloudSecure WP Security "super-cache" # WP Super Cache "w3-total-cache" # W3 Total Cache "litespeed-purge" # LiteSpeed Cache "rocket" # WP Rocket "wc" # WooCommerce "yoast" # Yoast SEO "rankmath" # Rank Math ) # wp-cliバイナリのパス設定 WP_CLI_PATH="/usr/bin/wp" # 引数チェック if [ $# -ne 3 ]; then echo "エラー: 引数が正しくありません。" echo "使用方法: $0 \"PHPバージョン\" \"WordPressディレクトリパス\" \"WP-CLIコマンド\"" exit 1 fi PHP_VERSION="$1" WP_PATH="$2" WP_COMMAND="$3" # ============================================================ # 入力値サニタイゼーション関数 # ============================================================ # パスの安全性チェック関数 validate_path() { local path="$1" # 異常に長いパスの拒否 if [ ${#path} -gt 4096 ]; then echo "エラー: パスが長すぎます(最大4096文字)" return 1 fi # 危険な文字をチェック if [[ "$path" =~ [\;\|\&\$\`\\\(\)\{\}\[\]\<\>] ]]; then echo "エラー: パスに危険な文字が含まれています: $path" >&2 return 1 fi # その他NG文字列チェック local dangerous_patterns=( ".." # ディレクトリトラバーサル "\0" # ヌル文字 "%2e%2e" # URLエンコードされた.. "%00" # URLエンコードされたヌル文字 "//" # ダブルスラッシュ "%2f%2e%2e" # URLエンコードされた/.. "%2e%2e%2f" # URLエンコードされた../ "%252e%252e" # 二重URLエンコードされた.. "%c0%ae" # UTF-8オーバーロング. "%c1%9c" # UTF-8オーバーロング\ "~" # ホームディレクトリ参照 ) for pattern in "${dangerous_patterns[@]}"; do if [[ "$path" == *"$pattern"* ]]; then echo "エラー: パスに危険なパターンが検出されました: $pattern" return 1 fi done return 0 } # ============================================================ # PHPバージョンの検証・PHPバイナリのパス構築 # ============================================================ # PHPバージョンの検証 if [[ ! "$PHP_VERSION" =~ ^[0-9]+\.[0-9]+$ ]]; then echo "エラー: PHPバージョンの形式が正しくありません。(例: 8.2)" exit 1 fi # PHPバイナリのパス構築 PHP_BINARY="/opt/php-${PHP_VERSION}/bin/php" # PHPバイナリの存在確認 if [ ! -f "$PHP_BINARY" ]; then echo "エラー: PHPバイナリが見つかりません: $PHP_BINARY" exit 1 fi # ============================================================ # WP_PATHの正規化・チェック # ============================================================ # パスの安全性チェック if ! validate_path "$WP_PATH"; then exit 1 fi # realpath を使用してパスを正規化 WP_PATH_REAL=$(realpath "$WP_PATH" 2>/dev/null) || { echo "エラー: パスの解決に失敗しました。" exit 1 } WP_PATH="$WP_PATH_REAL" # シンボリックリンクチェック if [ -e "$WP_PATH" ] && [ -L "$WP_PATH" ]; then echo "エラー: シンボリックリンクが指定されています: $WP_PATH" exit 1 fi # WordPressディレクトリの存在確認 if [ ! -d "$WP_PATH" ]; then echo "エラー: WordPressディレクトリが見つかりません: $WP_PATH" exit 1 fi # ディレクトリの読み書き権限確認 if [ ! -r "$WP_PATH" ] || [ ! -w "$WP_PATH" ]; then echo "エラー: WordPressディレクトリへの適切な権限がありません。" exit 1 fi # ============================================================ # 実行ユーザーの検証 # ============================================================ # パスからサーバーIDとドメイン名を抽出 SERVER_ID=$(echo "$WP_PATH" | sed -n 's|^/home/\([^/]*\)/.*|\1|p') DOMAIN_NAME=$(echo "$WP_PATH" | sed -n 's|^/home/[^/]*/\([^/]*\)/.*|\1|p') if [ -z "$SERVER_ID" ] || [ -z "$DOMAIN_NAME" ]; then echo "エラー: サーバーIDまたはドメイン名を抽出できませんでした。" exit 1 fi # cpadminからのsudo実行のみ許可 if [ "${SUDO_USER:-}" != "cpadmin" ]; then echo "エラー: このスクリプトは実行できません。" exit 1 fi # SERVER_IDと実行ユーザーが一致するかチェック if [ "$SERVER_ID" != "$USER" ]; then echo "エラー: 実行ユーザー($USER)とパスのサーバーID($SERVER_ID)が一致しません。" echo "指定されたパスは別のユーザーのサイトです。" exit 1 fi # システムユーザーかどうかを判定 check_system_user() { local username="$1" if [ -z "$username" ]; then return 0 # システムユーザー(true) fi # UIDとGIDを取得 local uid=$(id -u "$username" 2>/dev/null || echo "") local gid=$(id -g "$username" 2>/dev/null || echo "") # id コマンドが失敗した場合はシステムユーザーとして扱う if [ -z "$uid" ] || [ -z "$gid" ]; then return 0 # システムユーザー(true) fi # uidが2000以上の場合、システムユーザではない if [ "$uid" -ge 2000 ]; then return 1 # 非システムユーザー(false) fi # uidが1000-2000かつgidが1000の場合 if [ "$uid" -ge 1000 ] && [ "$uid" -lt 2000 ] && [ "$gid" -eq 1000 ]; then # 除外対象のユーザー名リスト local system_users=("phpmyadmin" "phpmyadmin2" "phpmyadmin4a" "phpmyadmin4b" "webftp" "webmail" "analyzer") # 除外対象のユーザー名以外の場合はシステムユーザではない for sys_user in "${system_users[@]}"; do if [ "$username" = "$sys_user" ]; then return 0 # システムユーザー(true) fi done return 1 # 非システムユーザー(false) fi return 0 # その他の場合はシステムユーザー(true) } if check_system_user "$USER"; then echo "エラー: システムユーザーでは実行できません。" exit 1 fi # ============================================================ # WP-CLIコマンドの検証 # ============================================================ # まず「wp」で始まるかチェック if [[ ! "$WP_COMMAND" =~ ^wp[[:space:]] ]]; then echo "エラー: コマンドは'wp'で始まる必要があります。" exit 1 fi # コマンドの安全性チェック check_command_safety() { local cmd="$1" # NULLバイトチェック if printf '%s' "$cmd" | od -An -tx1 | grep -q " 00"; then echo "エラー: コマンドにNULLバイトが含まれています。" return 1 fi # 引用符で囲まれていないバッククォートのみを危険として検出 # 単純なパーサーで引用符の状態を追跡 local in_single_quote=false local in_double_quote=false local escaped=false local i=0 while [ $i -lt ${#cmd} ]; do local char="${cmd:$i:1}" if [ "$escaped" = true ]; then escaped=false elif [ "$char" = "\\" ]; then escaped=true elif [ "$char" = "'" ] && [ "$in_double_quote" = false ]; then in_single_quote=$( [ "$in_single_quote" = true ] && echo false || echo true ) elif [ "$char" = '"' ] && [ "$in_single_quote" = false ]; then in_double_quote=$( [ "$in_double_quote" = true ] && echo false || echo true ) elif [ "$char" = '`' ] && [ "$in_single_quote" = false ] && [ "$in_double_quote" = false ]; then echo "エラー: 引用符で囲まれていないバッククォート(\`)は使用できません。" return 1 fi i=$((i + 1)) done # 引用符で囲まれた部分を除去して危険文字をチェック local temp_cmd="$cmd" # 引用符で囲まれた部分を一時的に置換 temp_cmd=$(echo "$temp_cmd" | sed 's/"[^"]*"/__QUOTED__/g' | sed "s/'[^']*'/__QUOTED__/g") if [[ "$temp_cmd" =~ [\|\&\;\<\>] ]]; then echo "エラー: 危険文字は引用符で囲んでください: | & ; < >" echo "例: --title=\"含む&文字\"" return 1 fi return 0 } # 安全性チェック実行 if ! check_command_safety "$WP_COMMAND"; then exit 1 fi # コマンドからwpプレフィックスを除去してコマンド部分を抽出 COMMAND_PART=$(echo "$WP_COMMAND" | sed 's/^wp[[:space:]]*//') MAIN_COMMAND=$(echo "$COMMAND_PART" | awk '{print $1}') # 許可されたコマンドかチェック COMMAND_ALLOWED=false for allowed in "${ALLOWED_COMMANDS[@]}"; do if [ "$MAIN_COMMAND" = "$allowed" ]; then COMMAND_ALLOWED=true break fi done if [ "$COMMAND_ALLOWED" = false ]; then echo "エラー: 許可されていないコマンドです: $MAIN_COMMAND" echo "許可されているコマンド: ${ALLOWED_COMMANDS[*]}" exit 1 fi # wp-cliの存在確認 if [ ! -f "$WP_CLI_PATH" ]; then echo "エラー: WP-CLIが見つかりません: $WP_CLI_PATH" exit 1 fi # ============================================================ # WP-CLIコマンドの実行 # ============================================================ # WP-CLIコマンドの実行 cd "$WP_PATH" || { echo "エラー: WordPressディレクトリに移動できません: $WP_PATH" exit 1 } # wp cli実行 eval "$PHP_BINARY" "$WP_CLI_PATH" --path="$WP_PATH" $COMMAND_PART # 終了 exit $?