azure-cli icon indicating copy to clipboard operation
azure-cli copied to clipboard

Bad input handling for --scope parameter

Open TheOnlyWei opened this issue 2 years ago • 3 comments

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

TheOnlyWei avatar Dec 20 '23 22:12 TheOnlyWei

Thank you for opening this issue, we will look into it.

yonzhan avatar Dec 20 '23 22:12 yonzhan

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.

jiasli avatar Dec 21 '23 12:12 jiasli

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!