Summary
awscli/customizations/codeartifact/login.py's CommandFailedError is meant to scrub the CodeArtifact authorization token out of error messages before they are shown to the user / logged, but it only redacts the token from str(called_process_error) — the decoded stderr from the failed subprocess is appended unredacted.
class CommandFailedError(Exception):
def __init__(self, called_process_error, auth_token):
msg = str(called_process_error).replace(auth_token, '******')
if called_process_error.stderr is not None:
msg += (
f' Stderr from command:\n'
f'{called_process_error.stderr.decode(get_stderr_encoding())}'
)
Exception.__init__(self, msg)
str(subprocess.CalledProcessError(...)) only ever includes the cmd list and return code — it never includes stderr content (verified directly against CPython's subprocess module). So the .replace(auth_token, '******') call can only ever redact the token if it appears inside the invoked cmd list; it can never redact anything appearing in stderr, which is concatenated onto the message afterward with no scrubbing at all.
Why this is exploitable
For NuGetLogin/DotNetLogin (login.py), the auth token is passed as a literal CLI argument to the underlying tool:
# NuGetLogin._get_configure_command
return [
'nuget', 'sources', operation,
'-name', source_name,
'-source', nuget_index_url,
'-username', 'aws',
'-password', self.auth_token
]
# DotNetLogin._get_configure_command
command += [
'--username', 'aws',
'--password', self.auth_token
]
Both nuget.exe and dotnet nuget are known to echo the full failing command line (including all passed arguments) into stderr/output when a sources add/nuget add source operation fails — e.g. an invalid/duplicate source name, a malformed URL, or a permissions error. PipLogin similarly embeds the token directly in a URL (https://aws:{auth_token}@{netloc}...) passed to pip config, and pip/curl-style tools commonly print the failing URL (credentials included) on error.
Impact
Running aws codeartifact login --tool nuget|dotnet|pip (etc.) in a scenario where the underlying tool invocation fails causes CommandFailedError to be raised with the live, unexpired CodeArtifact repository authorization token embedded in its message — printed to the user's terminal and captured by anything logging CLI output (CI logs, terminal scrollback capture, support tickets/bug reports). This defeats the explicit intent of the .replace(auth_token, '******') redaction in the same function.
Suggested fix
Redact auth_token from the decoded stderr (and stdout, if present) before appending it to the message, not just from str(called_process_error):
class CommandFailedError(Exception):
def __init__(self, called_process_error, auth_token):
msg = str(called_process_error).replace(auth_token, '******')
if called_process_error.stderr is not None:
stderr = called_process_error.stderr.decode(get_stderr_encoding())
stderr = stderr.replace(auth_token, '******')
msg += f' Stderr from command:\n{stderr}'
Exception.__init__(self, msg)
Environment
Reviewed against the current aws-cli source (vendored botocore confirmed unmodified/upstream; this bug is in aws-cli-specific customization code, not botocore).
Summary
awscli/customizations/codeartifact/login.py'sCommandFailedErroris meant to scrub the CodeArtifact authorization token out of error messages before they are shown to the user / logged, but it only redacts the token fromstr(called_process_error)— the decodedstderrfrom the failed subprocess is appended unredacted.str(subprocess.CalledProcessError(...))only ever includes thecmdlist and return code — it never includes stderr content (verified directly against CPython'ssubprocessmodule). So the.replace(auth_token, '******')call can only ever redact the token if it appears inside the invokedcmdlist; it can never redact anything appearing instderr, which is concatenated onto the message afterward with no scrubbing at all.Why this is exploitable
For
NuGetLogin/DotNetLogin(login.py), the auth token is passed as a literal CLI argument to the underlying tool:Both
nuget.exeanddotnet nugetare known to echo the full failing command line (including all passed arguments) into stderr/output when asources add/nuget add sourceoperation fails — e.g. an invalid/duplicate source name, a malformed URL, or a permissions error.PipLoginsimilarly embeds the token directly in a URL (https://aws:{auth_token}@{netloc}...) passed topip config, and pip/curl-style tools commonly print the failing URL (credentials included) on error.Impact
Running
aws codeartifact login --tool nuget|dotnet|pip(etc.) in a scenario where the underlying tool invocation fails causesCommandFailedErrorto be raised with the live, unexpired CodeArtifact repository authorization token embedded in its message — printed to the user's terminal and captured by anything logging CLI output (CI logs, terminal scrollback capture, support tickets/bug reports). This defeats the explicit intent of the.replace(auth_token, '******')redaction in the same function.Suggested fix
Redact
auth_tokenfrom the decodedstderr(andstdout, if present) before appending it to the message, not just fromstr(called_process_error):Environment
Reviewed against the current
aws-clisource (vendored botocore confirmed unmodified/upstream; this bug is in aws-cli-specific customization code, not botocore).