Bad input handling for --scope parameter
Describe the bug
Minor issue, but I discovered some bad input handling for az role commands that causes the command to hang or throw an unhandled error or an unexpected error. The bad input causes these unexpected behaviors when any string contains {x[]}, with any combination or permutation of extra characters, to hang forever. It seems the x can be any alphabet. You can also pass {[]} for Azure CLI to throw various uncaught errors. This seems to affect any command with a --scope parameter.
Passing {x[]} causes the command to hang and never return:
az role assignment create --role Reader --scope "{x[]}" --assignee <assignee username> --description "test description"
az role definition list --scope "{x[]}"
az ad sp create-for-rbac -n MyApp --role Contributor --scopes "{x[]}"
Passing {[]} gives miscellaneous errors that may or may not be handled:
az role assignment create --role Reader --scope "{[]}" --assignee <assignee username> --description "test description"
az role definition list --scope "{[]}"`
az ad sp create-for-rbac -n MyApp --role Contributor --scopes "{[]}"
This affects the user because the user might accidentally run something like this in PowerShell, for example:
az role definition list --scope /subscriptions/$account.id
The --scope parameter value above is expanded into to something like the following (anything like <*> can be ignored, and is added to describe the values that should be there):
/subscriptions/@{environmentName=myEnv; homeTenantId=<home tenant ID>; id=<ID>; isDefault=True; managedByTenants=System.Object[]; name=System Subscription; state=Enabled; tenantId=<tenant ID>; user=}.id
The above expansion contains the pattern {x[]}, which will cause the execution to hang forever, even though the user is at fault.
Related command
Any command with a --scope parameter.
Errors
Hangs forever, or various errors depending on the command.
Issue script & Debug output
The commands hang and throw various errors depending on the command.
Expected behavior
Should not hang, and the bad --scope input should be handled properly if possible, but at the very least it should not hang.
Environment Summary
(env) PS C:\Users\Administrator\Desktop> az --version
azure-cli 2.55.0
core 2.55.0
telemetry 1.1.0
Dependencies:
msal 1.24.0b2
azure-mgmt-resource 23.1.0b2
Python location 'C:\Users\Administrator\Desktop\env\Scripts\python.exe'
Extensions directory 'C:\Users\Administrator\.azure\cliextensions'
Python (Windows) 3.11.7 (tags/v3.11.7:fa7a6f2, Dec 4 2023, 19:24:49) [MSC v.1937 64 bit (AMD64)]
Legal docs and information: aka.ms/AzureCliLegal
Your CLI is up-to-date.
Additional context
No response
Thank you for opening this issue, we will look into it.
This is very similar to https://github.com/Azure/azure-cli/issues/23160 where curly brackets in --scope corrupts string formatting.
[!NOTE] In
str.format()method,[]is used to access element;.is used to access attribute. See https://docs.python.org/3/library/string.html#format-string-syntax
Problem 1: Using --scope '{[]}' (IndexError)
When using --scope '{[]}', the error message is
> az role assignment list --scope '{[]}'
The command failed with an unexpected error. Here is the traceback:
Replacement index 0 out of range for positional args tuple
Traceback (most recent call last):
File "D:\cli\py311\Lib\site-packages\knack\cli.py", line 233, in invoke
cmd_result = self.invocation.execute(args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "d:\cli\azure-cli\src\azure-cli-core\azure\cli\core\commands\__init__.py", line 663, in execute
raise ex
File "d:\cli\azure-cli\src\azure-cli-core\azure\cli\core\commands\__init__.py", line 726, in _run_jobs_serially
results.append(self._run_job(expanded_arg, cmd_copy))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "d:\cli\azure-cli\src\azure-cli-core\azure\cli\core\commands\__init__.py", line 697, in _run_job
result = cmd_copy(params)
^^^^^^^^^^^^^^^^
File "d:\cli\azure-cli\src\azure-cli-core\azure\cli\core\commands\__init__.py", line 333, in __call__
return self.handler(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "d:\cli\azure-cli\src\azure-cli-core\azure\cli\core\commands\command_operation.py", line 121, in handler
return op(**command_args)
^^^^^^^^^^^^^^^^^^
File "d:\cli\azure-cli\src\azure-cli\azure\cli\command_modules\role\custom.py", line 229, in list_role_assignments
assignments = _search_role_assignments(cmd.cli_ctx, assignments_client, definitions_client,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "d:\cli\azure-cli\src\azure-cli\azure\cli\command_modules\role\custom.py", line 554, in _search_role_assignments
assignments = list(assignments_client.list_for_scope(scope=scope, filter=f))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\cli\py311\Lib\site-packages\azure\core\paging.py", line 123, in __next__
return next(self._page_iterator)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\cli\py311\Lib\site-packages\azure\core\paging.py", line 75, in __next__
self._response = self._get_next(self.continuation_token)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\cli\py311\Lib\site-packages\azure\mgmt\authorization\v2022_04_01\operations\_role_assignments_operations.py", line 1079, in get_next
request = prepare_request(next_link)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\cli\py311\Lib\site-packages\azure\mgmt\authorization\v2022_04_01\operations\_role_assignments_operations.py", line 1051, in prepare_request
request.url = self._client.format_url(request.url)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\cli\py311\Lib\site-packages\azure\core\pipeline\transport\_base.py", line 634, in format_url
url = _format_url_section(url_template, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\cli\py311\Lib\site-packages\azure\core\pipeline\transport\_base.py", line 94, in _format_url_section
return template.format(**kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
IndexError: Replacement index 0 out of range for positional args tuple
Actually, you can even repro the same error with --scope '{}':
> az role assignment list --scope '{}'
...
File "D:\cli\py311\Lib\site-packages\azure\core\pipeline\transport\_base.py", line 94, in _format_url_section
return template.format(**kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
IndexError: Replacement index 0 out of range for positional args tuple
or --scope '{.y}':
> az role assignment list --scope '{.y}'
...
File "D:\cli\py311\Lib\site-packages\azure\core\pipeline\transport\_base.py", line 94, in _format_url_section
return template.format(**kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
IndexError: Replacement index 0 out of range for positional args tuple
As shown in the call stack, it fails in Python SDK function azure.core.pipeline.transport._base._format_url_section.
Azure CLI currently uses
https://github.com/Azure/azure-cli/blob/a22079acc81a3aaa5a96bac55970c77755e94f85/src/azure-cli/requirements.py3.windows.txt#L11
In that version, _format_url_section is
https://github.com/Azure/azure-sdk-for-python/blob/azure-core_1.28.0/sdk/core/azure-core/azure/core/pipeline/transport/_base.py#L78
def _format_url_section(template, **kwargs):
"""String format the template with the kwargs, auto-skip sections of the template that are NOT in the kwargs.
By default in Python, "format" will raise a KeyError if a template element is not found. Here the section between
the slashes will be removed from the template instead.
This is used for API like Storage, where when Swagger has template section not defined as parameter.
:param str template: a string template to fill
:keyword dict[str,str] kwargs: Template values as string
:rtype: str
:returns: Template completed
"""
components = template.split("/")
while components:
try:
return template.format(**kwargs)
except KeyError as key:
formatted_components = template.split("/")
components = [c for c in formatted_components if "{{{}}}".format(key.args[0]) not in c]
template = "/".join(components)
# No URL sections left - returning None
IndexError is not captured by _format_url_section so it bubbles up. I created https://github.com/Azure/azure-sdk-for-python/issues/33641 to track it.
Problem 2: Using --scope '{x[]}' (KeyError)
For --scope '{x[]}', _format_url_section tries to remove unrecognized components from the URL, but key.args[0] is x, so "{{{}}}".format(key.args[0]) evaluates to {x} which doesn't match {x[]}, resulting in infinite loop. --scope '{x.y}' has the same effect.
https://github.com/Azure/azure-sdk-for-python/pull/31718 fixed the infinite loop by raising an error if no component is removed.
--scope '{x[]}' now gives:
> az role assignment list --scope '{x[]}'
The command failed with an unexpected error. Here is the traceback:
The value provided for the url part '/{x[]}/providers/Microsoft.Authorization/roleAssignments?$filter=atScope()&api-version=2022-04-01' was incorrect, and resulted in an invalid url
Traceback (most recent call last):
File "D:\cli\py311\Lib\site-packages\azure\core\pipeline\transport\_base.py", line 101, in _format_url_section
return template.format(**kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 'x'
The above exception was the direct cause of the following exception:
...
File "D:\cli\py311\Lib\site-packages\azure\core\pipeline\transport\_base.py", line 107, in _format_url_section
raise ValueError(
ValueError: The value provided for the url part '/{x[]}/providers/Microsoft.Authorization/roleAssignments?$filter=atScope()&api-version=2022-04-01' was incorrect, and resulted in an invalid url
However, I don't think I will bump azure-core immediately for such an edge case. azure-core will be bumped eventually in the future.
Hi, we're sending this friendly reminder because we haven't heard back from you in a while. We need more information about this issue to help address it. Please be sure to give us your input within the next 7 days. If we don't hear back from you within 14 days of this comment the issue will be automatically closed. Thank you!