Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 62 additions & 3 deletions src/azure-cli/azure/cli/command_modules/mysql/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import secrets
import string
import subprocess
import sys
import yaml
from time import sleep
import datetime as dt
Expand All @@ -22,7 +23,7 @@
from azure.mgmt.core.tools import parse_resource_id
from azure.cli.core.commands.client_factory import get_subscription_id
from azure.cli.core.commands.progress import IndeterminateProgressBar
from azure.cli.core.util import CLIError, run_cmd
from azure.cli.core.util import CLIError, run_cmd as core_run_cmd
from azure.core.exceptions import HttpResponseError
from azure.core.paging import ItemPaged
from azure.core.rest import HttpRequest
Expand Down Expand Up @@ -376,11 +377,69 @@ def _resolve_api_version(client, provider_namespace, resource_type, parent_path)
.format(resource_type))


def find_executable(executable):
"""Find a dependency on PATH, excluding working-directory lookup on Windows."""
if sys.platform != 'win32':
import shutil
return shutil.which(executable)

if os.path.dirname(executable):
return os.path.abspath(executable) if os.path.isfile(executable) else None

# Some supported Python versions let shutil.which search the current directory first.
# Search PATH ourselves so a git.exe or gh.exe in the project cannot win that lookup.
# Keep Windows' .exe default; picking up a .cmd or .bat file could introduce shell parsing.
name = executable if os.path.splitext(executable)[1] else executable + '.exe'
current_directory = os.path.abspath(os.getcwd())
for directory in os.environ.get('PATH', '').split(os.pathsep):
directory = directory.strip('"')
# Empty and relative entries, or the project directory itself, would undo that protection.
if not os.path.isabs(directory) or not os.path.splitdrive(directory)[0]:
continue
try:
# Different names can refer to the same directory (for example, a mapped drive).
if os.path.samefile(directory, current_directory):
continue
candidate = os.path.join(directory, name)
if not os.path.isfile(candidate) or not os.access(candidate, os.X_OK):
continue
# A tool found outside the project may still be a link to a file inside it.
target_directory = os.path.dirname(os.path.realpath(candidate))
if os.path.samefile(target_directory, current_directory):
continue
except (OSError, ValueError) as ex:
logger.debug("Skipping executable lookup in '%s': unable to verify path safety: %s", directory, ex)
continue
return os.path.abspath(candidate)
return None


def resolve_executable(executable):
"""Bind Windows bare-name launches to PATH; retain explicit paths and Unix lookup behavior."""
if sys.platform != 'win32' or os.path.dirname(executable):
return executable
resolved = find_executable(executable)
if resolved is None:
# Do not retry the bare name: Windows could fall back to the file we meant to avoid.
import errno
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), executable)
return resolved


def run_cmd(args, **kwargs):
"""Run MySQL deployment tools without implicit Windows working-directory lookup."""
if isinstance(args, list) and args:
args = [resolve_executable(args[0]), *args[1:]]
return core_run_cmd(args, **kwargs)


def run_subprocess(command, stdout_show=None):
commands = list(command)
commands[0] = resolve_executable(commands[0])
if stdout_show:
process = subprocess.Popen(command)
process = subprocess.Popen(commands)
else:
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
process = subprocess.Popen(commands, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
process.wait()
if process.returncode:
logger.warning(process.stderr.read().strip().decode('UTF-8'))
Expand Down
10 changes: 7 additions & 3 deletions src/azure-cli/azure/cli/command_modules/mysql/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
from azure.cli.core.commands.client_factory import get_subscription_id
from azure.cli.command_modules.mysql.random.generate import generate_username
from azure.cli.core.util import CLIError, sdk_no_wait, user_confirmation, run_cmd
from azure.cli.core.util import CLIError, sdk_no_wait, user_confirmation
from azure.cli.core.local_context import ALL
from azure.mgmt.mysqlflexibleservers import models
from azure.cli.core.azclierror import ClientRequestError, RequiredArgumentMissingError, InvalidArgumentValueError, ValidationError
Expand All @@ -28,7 +28,7 @@
from ._util import resolve_poller, generate_missing_parameters, get_mysql_list_skus_info, generate_password, parse_maintenance_window, \
_get_list_from_paged_response, build_identity_and_data_encryption, get_identity_and_data_encryption, get_tenant_id, run_subprocess, \
fill_action_template, get_git_root_dir, get_single_to_flex_sku_mapping, get_firewall_rules_from_paged_response, \
ImportFromStorageProgressHook, OperationProgressBar, GITHUB_ACTION_PATH
ImportFromStorageProgressHook, OperationProgressBar, GITHUB_ACTION_PATH, run_cmd
from ._network import prepare_mysql_exist_private_dns_zone, prepare_mysql_exist_private_network, prepare_private_network, prepare_private_dns_zone, prepare_public_network
from ._validators import mysql_arguments_validator, mysql_auto_grow_validator, mysql_georedundant_backup_validator, mysql_restore_tier_validator, mysql_accelerated_logs_validator, \
mysql_retention_validator, mysql_sku_name_validator, mysql_storage_validator, validate_mysql_replica, validate_server_name, \
Expand Down Expand Up @@ -242,7 +242,11 @@ def github_actions_run(action_name, branch):


def gitcli_check_and_login():
output = run_cmd(["gh"], capture_output=True)
try:
output = run_cmd(["gh"], capture_output=True)
except FileNotFoundError as ex:
# A gh.exe in the project folder does not count as an installed GitHub CLI.
raise ClientRequestError('Please install "Github CLI" to run this command.') from ex
if output.returncode:
raise ClientRequestError('Please install "Github CLI" to run this command.')

Expand Down
Loading
Loading