Skip to content

Fix credential_process failing when executable path contains spaces on Windows.#4413

Draft
ashishdhingra wants to merge 1 commit into
developmentfrom
user/ashdhin/ProcessAWSCredentials-WindowsCmdWorkaround
Draft

Fix credential_process failing when executable path contains spaces on Windows.#4413
ashishdhingra wants to merge 1 commit into
developmentfrom
user/ashdhin/ProcessAWSCredentials-WindowsCmdWorkaround

Conversation

@ashishdhingra
Copy link
Copy Markdown
Contributor

@ashishdhingra ashishdhingra commented May 21, 2026

Description

ProcessAWSCredentials previously delegated to cmd.exe /c to run the credential_process command. This caused two issues:

  1. Quoted paths with spaces (e.g., "C:\Program Files\foo.exe") were broken because cmd.exe strips quotes when arguments follow, causing it to interpret "C:\Program" as the executable (GitHub #393).

  2. On systems where cmd.exe is disabled via Group Policy, the SDK hung indefinitely waiting for cmd.exe to exit (GitHub #1845).

The fix removes the cmd.exe intermediary on Windows and instead parses the credential_process value directly — extracting the double-quoted executable path and arguments per the AWS SDK specification — then launches the process via ProcessStartInfo.FileName. This is consistent with how botocore handles credential_process on Windows.

Refer https://docs.aws.amazon.com/sdkref/latest/guide/feature-process-credentials.html for some guidance around using double-quotes.

Motivation and Context

Issues:

Testing

  • Added test cases.
  • Tested locally:
    • File Path with spaces
    • File Path with spaces enclosed in double-quotes
    • File Path enclosed in double quotes and arguments also in double-quotes

Dry-runs

Breaking Changes Assessment

  1. Identify all breaking changes including the following details:
    • What functionality was changed?
    • How will this impact customers?
    • Why does this need to be a breaking change and what are the most notable non-breaking alternatives?
    • Are best practices being followed?
    • How have you tested this breaking change?
  2. Has a senior/+ engineer been assigned to review this PR?

Screenshots (if appropriate)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • My code follows the code style of this project
  • My change requires a change to the documentation
  • I have updated the documentation accordingly
  • I have read the README document
  • I have added tests to cover my changes
  • All new and existing tests passed

License

  • I confirm that this pull request can be released under the Apache 2 license

@ashishdhingra ashishdhingra force-pushed the user/ashdhin/ProcessAWSCredentials-WindowsCmdWorkaround branch from fececa6 to c9324b2 Compare May 21, 2026 18:26
@dscpinheiro dscpinheiro marked this pull request as ready for review May 21, 2026 18:45
@dscpinheiro dscpinheiro requested a review from a team as a code owner May 21, 2026 18:45
Copilot AI review requested due to automatic review settings May 21, 2026 18:45
@dscpinheiro dscpinheiro requested a review from a team as a code owner May 21, 2026 18:45
@dscpinheiro dscpinheiro requested review from AlexDaines and peterrsongg and removed request for a team May 21, 2026 18:45
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates ProcessAWSCredentials to avoid routing credential_process execution through cmd.exe on Windows, addressing failures with quoted executable paths containing spaces and hangs when cmd.exe is disabled by Group Policy.

Changes:

  • Replace cmd.exe /c invocation on Windows with direct parsing of credential_process into ProcessStartInfo.FileName + Arguments.
  • Add Windows-focused unit tests for quoted executable paths (including paths with spaces) and for malformed quoted input.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
sdk/src/Core/Amazon.Runtime/Credentials/ProcessAWSCredentials.cs Parses and directly executes credential_process on Windows instead of using cmd.exe.
sdk/test/UnitTests/Custom/Runtime/Credentials/ProcessAWSCredentialsTest.cs Adds test coverage for quoted executable paths with spaces/quoted args and unmatched quote behavior.
Comments suppressed due to low confidence (3)

sdk/src/Core/Amazon.Runtime/Credentials/ProcessAWSCredentials.cs:104

  • On Windows this now parses and launches credential_process directly via ProcessStartInfo.FileName with UseShellExecute = false. That breaks profiles that point at script files like .bat/.cmd (including this repo’s own windows-credentials-script.bat used by unit tests), because Process.Start with UseShellExecute=false uses CreateProcess and cannot execute batch files. Consider either (a) detecting script extensions on Windows and invoking cmd.exe /c with correct quoting only for those cases, or (b) switching tests/docs to use an actual executable/interpreter (e.g., powershell.exe -File ..., python.exe script.py) and explicitly documenting that credential_process must start with an executable on Windows.
#if NETSTANDARD
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                fileName = "sh";
                var escapedArgs = processCredentialInfo.Replace("\\", "\\\\").Replace("\"", "\\\"");
                arguments = $"-c \"{escapedArgs}\"";
            }
            else
            {
                (fileName, arguments) = ParseCredentialProcessCommand(processCredentialInfo);
            }
#else
            (fileName, arguments) = ParseCredentialProcessCommand(processCredentialInfo);
#endif
            _processStartInfo = new ProcessStartInfo
            {
                FileName = fileName,
                Arguments = arguments,
                UseShellExecute = false,
                RedirectStandardError = true,
                RedirectStandardOutput = true,
                CreateNoWindow = true
            };

sdk/test/UnitTests/Custom/Runtime/Credentials/ProcessAWSCredentialsTest.cs:206

  • This test repeats the fixed temp directory name pattern and can collide with other runs/parallel tests. Use a unique temp directory name and ensure cleanup handles pre-existing directories.
            // Create a directory with a space in the name and copy the script there
            var dirWithSpace = Path.Combine(Path.GetTempPath(), "aws sdk test dir");
            Directory.CreateDirectory(dirWithSpace);
            var scriptCopy = Path.Combine(dirWithSpace, "windows-credentials-script.bat");
            File.Copy(Executable, scriptCopy, overwrite: true);

sdk/test/UnitTests/Custom/Runtime/Credentials/ProcessAWSCredentialsTest.cs:221

  • Same cleanup brittleness as the earlier test: deleting the copied script and then Directory.Delete(..., recursive:false) can intermittently fail on Windows (locks/AV) and leave temp artifacts. Prefer recursive directory deletion and resilient cleanup so the test doesn’t flake.
            finally
            {
                File.Delete(scriptCopy);
                Directory.Delete(dirWithSpace, recursive: false);
            }

{
// Fail fast on malformed input, consistent with botocore's _windows_shell_split().
throw new ProcessAWSCredentialException(
$"No closing quotation mark in credential_process command: {command}");
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines +161 to +165
// Create a directory with a space in the name and copy the script there
var dirWithSpace = Path.Combine(Path.GetTempPath(), "aws sdk test dir");
Directory.CreateDirectory(dirWithSpace);
var scriptCopy = Path.Combine(dirWithSpace, "windows-credentials-script.bat");
File.Copy(Executable, scriptCopy, overwrite: true);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines +175 to +179
finally
{
File.Delete(scriptCopy);
Directory.Delete(dirWithSpace, recursive: false);
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@dscpinheiro dscpinheiro marked this pull request as draft May 21, 2026 18:52
@ashishdhingra ashishdhingra force-pushed the user/ashdhin/ProcessAWSCredentials-WindowsCmdWorkaround branch 4 times, most recently from aa1321d to 4e208a2 Compare May 21, 2026 22:16
@dscpinheiro dscpinheiro requested review from normj and removed request for AlexDaines May 21, 2026 22:51
@ashishdhingra ashishdhingra force-pushed the user/ashdhin/ProcessAWSCredentials-WindowsCmdWorkaround branch from 4e208a2 to 91a75b9 Compare May 22, 2026 20:41
@ashishdhingra ashishdhingra force-pushed the user/ashdhin/ProcessAWSCredentials-WindowsCmdWorkaround branch from 91a75b9 to 6da0c02 Compare May 22, 2026 21:28
/// This avoids using cmd.exe, which has issues with quoted paths
/// and hangs when cmd.exe is disabled via Group Policy.
/// </summary>
private static (string FileName, string Arguments) ParseCredentialProcessCommand(string command)
Copy link
Copy Markdown
Contributor

@dscpinheiro dscpinheiro May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked offline, but this suggestion (from Claude) achieves the same result and it was much easier for me to parse (no pun intended):

private static void ParseCommand(string command, out string fileName, out string arguments)
{
    if (command[0] == '"')
    {
        var rest = command.Substring(1);
        var closingQuote = rest.IndexOf('"');
        if (closingQuote < 0)
            throw new ProcessAWSCredentialException("Unmatched quote in credential_process value.");
        fileName = rest.Substring(0, closingQuote);
        arguments = rest.Substring(closingQuote + 1).TrimStart();
    }
    else
    {
        var space = command.IndexOf(' ');
        fileName = space < 0 ? command : command.Substring(0, space);
        arguments = space < 0 ? "" : command.Substring(space + 1).TrimStart();
    }
}

The other topic was even though this isn't documented anywhere, there are some edge cases that could be relying on cmd behavior. For example, credential_process = set-env.cmd && get-creds.exe and credential_process = get-creds.exe 2>nul

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants