Skip to content

Conversation

@mihainradu
Copy link
Collaborator

This is how are pull requests handled by FreeRDP

  1. Every new pull request needs to build and pass the unit tests at https://ci.freerdp.com
  2. At least 1 (better two) people need to review and test a pull request and agree to accept

Preparations before creating a pull

  • Rebase your branch to current master, no merges allowed!
  • Try to clean up your commit history, group changes to commits
  • Check your formatting! A clang-format script can be found at .clang-format
    • The cmake target clangformat reformats the whole codebase
  • Optional (but higly recommended)
    • Run a clang scanbuild before and after your changes to avoid introducing new bugs
    • Run your compiler at pedantic level to check for new warnings

To ease accepting your contribution

  • Give the pull request a proper name so people looking at it have an basic idea what it is for
  • Add at least a brief description what it does (or should do :) and what it's good for
  • Give instructions on how to test your changes
  • Ideally add unit tests if adding new features

What you should be prepared for

  • fix issues found during the review phase
  • Joining IRC #freerdp to talk to other developers or help them test your pull might accelerate acceptance
  • Joining our mailing list freerdp-devel@lists.sourceforge.net may be helpful too.

Please remove this text before submitting your pull!

danutboanta and others added 30 commits November 7, 2023 12:31
* build scripts
* update README
* default implementation for 'freerdp_channels_load_static_addin_entry' (bug in freerdp build when disable channels)

Changes not added:
* /libfreerdp/core/transport.c (???)
* /libfreerdp\core\proxy.c (can be set from outside: settings->ProxyType = PROXY_TYPE_IGNORE;)
* /winpr\libwinpr\utils\debug.c (seems to be fixed)
* /winpr/include/winpr/nt.h (fixed)
* nogui free rdp client (nuget package)
* Add customport option
* don't check colordepth for session as it may be negotiated or smth
* fwd freerdp logs to logging

* fwd runid from Activity

* add freerdp logs filter

* arrange

* filters update long fire place video

* remove activity support for runid

* filter logging and arrange code

* remove debug except ....nego

* revert tests

* Revert "revert tests"

This reverts commit b5dcac9.

* adjust filter

* Revert "Revert "revert tests""

This reverts commit 1447abd.

* update logs filter

* TryAcquireInitLock and rearrange to use DI

* review
- modify the CI to publish the NuGet package
- write tests
- centralize NuGet package reference via MSBuild
* - stop using Marshal.PtrToStructure
- write local test

* add test for non-enum structs
* add loggong tests

* update logging filter for order falgs

* ensure tests do not run in parallel

* remove the need for host to configure logs forwarding

* check for nulls on input
… about the need for manually recording the current TAG and HASH from the offical FreeRDP repo

- install the `Microsoft.SourceLink.GitHub` NuGet in all CSPROJs so that we'll get the current Git hash appended to AssemblyInformationalVersionAttribute
- drop the TFM's "windows" constraint
- add the admin parameter to the EnsureUserIsSetUp method
- stop adding created users to "Remote Desktop Users"
eduard-dumitru and others added 15 commits November 7, 2023 12:31
- create an helper method for the 8 cases where we create RdpConnectionSettings from UserExistsDetail
* FreeRDP updates
- copied branch "uipath" and rebased onto tag "2.11.2"
- bumped OpenSSL to tag "openssl-3.0.12"
- fixed breaks:
- updated linker deps in FreeRdpWrapper for both Debug and Release
- build openssl with no dynamic legacy modules
- build freerdp with channels (wfreerdp.exe didn't work without channels)
- ProcessRunner simplification
- Increase DefaultTimeout to 2 minutes (on CI some tine "netsh" coomand takes more than 30 seconds)
Error handling: Make error message pretty
* commission separate list of prefixes for filtering original warnings
* unit test
[ROBO-3413] fix rdp license leak
…tes (#38)

Usernames containing these character would lead to command errors when
being parsed, they have been surrounded by double quotes where possible
or explicit handling has been added.

Co-authored-by: Daniel Stanciu <daniel.stanciu@uipath.com>
#39)

* Add optional callback that's called when FreeRDP session disconnects ROBO-3822
…OBO-4059 (#40)

* Create git tag when publishing package

Added option to create a tag with the same name as the published
package. The tag is only created if a package was also published
successfully.

* Set version suffix based on branch + only create tag from uipath branch
Comment on lines 25 to 71
name: Analyze
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed

steps:
- name: Checkout repository
uses: actions/checkout@v2

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
# - name: Autobuild
# uses: github/codeql-action/autobuild@v1

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language

- run: |
sudo apt update
sudo apt install libxrandr-dev libxinerama-dev libusb-1.0-0-dev xserver-xorg-dev libswscale-dev libswresample-dev libavutil-dev libavcodec-dev libcups2-dev libpulse-dev libasound2-dev libpcsclite-dev xsltproc libxcb-cursor-dev libxcursor-dev libcairo2-dev libfaac-dev libfaad-dev libjpeg-dev libgsm1-dev ninja-build libxfixes-dev libxkbcommon-dev libwayland-dev libpam0g-dev libxdamage-dev libxcb-damage0-dev ccache libxtst-dev libfuse-dev libsystemd-dev libcairo2-dev libsoxr-dev
mkdir ci-build
cd ci-build
cmake -GNinja ../ci/cmake-preloads/config-linux-all.txt ..
cmake --build .

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 4 months ago

To fix this issue, the permissions key should be added to the job or to the top level of the workflow file to explicitly specify the minimum required permissions for running the workflow. The minimal necessary permissions for most CodeQL-only analysis workflows are contents: read (enough to read the repository contents and run analysis, not enough to write changes or interact elsewhere). This should be inserted either at the root level, or directly under the job that requires it; in this case, following the flagged issue on line 25, the block should be added under the analyze job in the .github/workflows/codeql-analysis.yml file. No new imports, methods, or other variable changes are required.


Suggested changeset 1
.github/workflows/codeql-analysis.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -24,6 +24,8 @@
   analyze:
     name: Analyze
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
 
     strategy:
       fail-fast: false
EOF
@@ -24,6 +24,8 @@
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
contents: read

strategy:
fail-fast: false
Copilot is powered by AI and may make mistakes. Always verify output.
process.ErrorDataReceived += (_, e) => stderr.AppendLine(e.Data);

_ = process.Start();
_logger?.LogRunStarted(startInfo.FileName, startInfo.Arguments);

Check failure

Code scanning / CodeQL

Clear text storage of sensitive information High

This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to field _password : String
as clear text.

Copilot Autofix

AI 4 months ago

How to, in general terms, fix the problem:
Avoid logging sensitive information such as passwords in cleartext. When logging command invocations or arguments that potentially contain sensitive information, the password should be redacted, masked, or replaced with a safe placeholder before logging. This can be done by parsing and removing/replacing the sensitive portion in any logged string, based on knowledge of which commands/parameters may include it.

Best way to fix without changing existing functionality:

  • In ProcessRunner.Run, ensure that logging methods (LogRunStarted, LogRunWaitCanceled, LogRunSucceeded, LogRunFailed) do not receive sensitive values unredacted in the arguments field.
  • Implement a logic to redact passwords in commands known to include literal passwords, such as when calling net user ... [password] ... or similar commands as seen in the data flow.
  • Add a utility method in ProcessRunner to identify and mask passwords in command arguments (e.g., replace password with "****" when logging).
  • Use the redacted command arguments in all logger invocations within this method.

What is needed:

  • A static method or private helper in ProcessRunner to redact passwords from argument strings based on the command (likely a regular expression for net user ... [password]).
  • Use this helper to create a redacted version of startInfo.Arguments for all logger calls.
  • There is no need for third-party dependencies—.NET regexes are sufficient.
  • No changes to the business logic of how arguments are passed to the process: the actual command launched is unchanged; only logs are sanitized.

Files/lines to change:

  • In UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs, update every place startInfo.Arguments is passed to _logger?.Log* within Run.
  • Add the redaction helper as a private static method to ProcessRunner.

Suggested changeset 1
UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs b/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
--- a/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
+++ b/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
@@ -1,7 +1,7 @@
 using Microsoft.Extensions.Logging;
 using System.Diagnostics;
 using System.Text;
-
+using System.Text.RegularExpressions;
 namespace UiPath.SessionTools;
 
 using static Logging;
@@ -39,8 +39,10 @@
         process.ErrorDataReceived += (_, e) => stderr.AppendLine(e.Data);
 
         _ = process.Start();
-        _logger?.LogRunStarted(startInfo.FileName, startInfo.Arguments);
 
+        var redactedArguments = RedactSensitiveArguments(startInfo.FileName, startInfo.Arguments);
+        _logger?.LogRunStarted(startInfo.FileName, redactedArguments);
+
         process.BeginOutputReadLine();
         process.BeginErrorReadLine();
 
@@ -50,25 +51,26 @@
         }
         catch (OperationCanceledException)
         {
-            _logger?.LogRunWaitCanceled(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());
+            _logger?.LogRunWaitCanceled(startInfo.FileName, redactedArguments, stdout.ToString(), stderr.ToString());
 
             throw;
         }
 
         if (process.ExitCode is 0)
         {
-            _logger?.LogRunSucceeded(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());
+            _logger?.LogRunSucceeded(startInfo.FileName, redactedArguments, stdout.ToString(), stderr.ToString());
             return (stdout.ToString(), process.ExitCode);
         }
 
         string strStdout = stdout.ToString();
         string strStderr = stderr.ToString();
-        _logger?.LogRunFailed(startInfo.FileName, startInfo.Arguments, process.ExitCode, strStdout, strStderr);
+        _logger?.LogRunFailed(startInfo.FileName, redactedArguments, process.ExitCode, strStdout, strStderr);
 
+
         if (throwOnNonZero)
         {
             throw new NonZeroExitCodeException(process.ExitCode, strStdout, strStderr,
-                FormatRunFailed(startInfo.FileName, startInfo.Arguments, $"{process.ExitCode}", strStdout, strStderr));
+                FormatRunFailed(startInfo.FileName, redactedArguments, $"{process.ExitCode}", strStdout, strStderr));
         }
 
         return (strStdout, process.ExitCode);
@@ -87,4 +74,30 @@
             Error = error;
         }
     }
+
+    // Redacts potential passwords from command arguments for logging purposes
+    private static string RedactSensitiveArguments(string fileName, string arguments)
+    {
+        if (string.Equals(fileName, "net", StringComparison.OrdinalIgnoreCase))
+        {
+            // net user "User" password, or net user "User" password /add
+            // Match: user "something" <password>...
+            // Do not redact /active, /expires, or similar, only redact the 3rd word!
+            // Regex: user\s+"[^"]+"\s+(\S+)
+            return Regex.Replace(
+                arguments,
+                @"(user\s+""[^""]+""\s+)(\S+)",
+                m =>
+                {
+                    // Don't redact if the parameter is a switch (starts with /)
+                    // For "net user \"user\" /active", 2nd group is "/active"
+                    if (m.Groups.Count > 2 && !m.Groups[2].Value.StartsWith("/"))
+                        return m.Groups[1].Value + "****";
+                    return m.Value;
+                }
+            );
+        }
+        // Optionally handle other commands as needed, e.g., wmic or custom scripts
+        return arguments;
+    }
 }
EOF
Copilot is powered by AI and may make mistakes. Always verify output.
}
catch (OperationCanceledException)
{
_logger?.LogRunWaitCanceled(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());

Check failure

Code scanning / CodeQL

Clear text storage of sensitive information High

This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to field _password : String
as clear text.

Copilot Autofix

AI 4 months ago

To prevent accidental storage of sensitive information, especially passwords, in clear text logs, the logging statements that include startInfo.Arguments need to be sanitized before being logged.

  • The affected regions are wherever _logger?.LogRunWaitCanceled, _logger?.LogRunStarted, _logger?.LogRunFailed, and _logger?.LogRunSucceeded are called, passing startInfo.Arguments.
  • The recommended fix is to mask any sensitive information in startInfo.Arguments before logging it. For the currently known use case (the net user ... password ... command), password values are space-delimited in the arguments. We can implement a utility method that scans command lines for likely passwords and replaces them with a mask (e.g., *****).
  • Specifically:
    • Add a method SanitizeArgumentsForLogging(string arguments) that detects and masks passwords in command strings; call this method when passing startInfo.Arguments as a log parameter.
    • Only edit code within ProcessRunner.cs as that's where the logging happens.
    • Ensure the masking function is straightforward, deterministic, and does not break normal logging for non-sensitive usages.

Suggested changeset 1
UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs b/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
--- a/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
+++ b/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
@@ -1,6 +1,7 @@
 using Microsoft.Extensions.Logging;
 using System.Diagnostics;
 using System.Text;
+using System.Collections.Generic;
 
 namespace UiPath.SessionTools;
 
@@ -39,7 +40,8 @@
         process.ErrorDataReceived += (_, e) => stderr.AppendLine(e.Data);
 
         _ = process.Start();
-        _logger?.LogRunStarted(startInfo.FileName, startInfo.Arguments);
+        var sanitizedArgs = SanitizeArgumentsForLogging(startInfo.Arguments);
+        _logger?.LogRunStarted(startInfo.FileName, sanitizedArgs);
 
         process.BeginOutputReadLine();
         process.BeginErrorReadLine();
@@ -50,25 +52,25 @@
         }
         catch (OperationCanceledException)
         {
-            _logger?.LogRunWaitCanceled(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());
+            _logger?.LogRunWaitCanceled(startInfo.FileName, sanitizedArgs, stdout.ToString(), stderr.ToString());
 
             throw;
         }
 
         if (process.ExitCode is 0)
         {
-            _logger?.LogRunSucceeded(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());
+            _logger?.LogRunSucceeded(startInfo.FileName, sanitizedArgs, stdout.ToString(), stderr.ToString());
             return (stdout.ToString(), process.ExitCode);
         }
 
         string strStdout = stdout.ToString();
         string strStderr = stderr.ToString();
-        _logger?.LogRunFailed(startInfo.FileName, startInfo.Arguments, process.ExitCode, strStdout, strStderr);
+        _logger?.LogRunFailed(startInfo.FileName, sanitizedArgs, process.ExitCode, strStdout, strStderr);
 
         if (throwOnNonZero)
         {
             throw new NonZeroExitCodeException(process.ExitCode, strStdout, strStderr,
-                FormatRunFailed(startInfo.FileName, startInfo.Arguments, $"{process.ExitCode}", strStdout, strStderr));
+                FormatRunFailed(startInfo.FileName, sanitizedArgs, $"{process.ExitCode}", strStdout, strStderr));
         }
 
         return (strStdout, process.ExitCode);
@@ -87,4 +74,52 @@
             Error = error;
         }
     }
+    /// <summary>
+    /// Masks passwords embedded in command line arguments for logging.
+    /// For lines of the form: net user "username" password
+    /// (and variations) it will replace the password only.
+    /// </summary>
+    private static string SanitizeArgumentsForLogging(string arguments)
+    {
+        // Simple heuristic for "net user" with password: mask third argument if present
+        // e.g., user "user" password
+        if (string.IsNullOrWhiteSpace(arguments))
+            return arguments;
+
+        // Tokenize spaced args, but preserve quoted segments (naive splitting for simple cases)
+        var tokens = new List<string>();
+        var current = new StringBuilder();
+        bool inQuotes = false;
+        for (int i = 0; i < arguments.Length; i++)
+        {
+            var c = arguments[i];
+            if (c == '"') inQuotes = !inQuotes;
+            if (!inQuotes && c == ' ')
+            {
+                if (current.Length > 0)
+                {
+                    tokens.Add(current.ToString());
+                    current.Clear();
+                }
+            }
+            else
+            {
+                current.Append(c);
+            }
+        }
+        if (current.Length > 0)
+            tokens.Add(current.ToString());
+
+        // For "user", third token is the password (net user "name" password ...) or (net user "name" password /add)
+        // For "localgroup", no password. For most others, password not public.
+        if (tokens.Count >= 3 && tokens[0].Equals("user", StringComparison.OrdinalIgnoreCase))
+        {
+            // Only mask if third token does not look like a switch (starts with '/')
+            if (!tokens[2].StartsWith("/"))
+                tokens[2] = "*****";
+        }
+
+        // Reconstruct arguments
+        return string.Join(" ", tokens);
+    }
 }
EOF
Copilot is powered by AI and may make mistakes. Always verify output.

if (process.ExitCode is 0)
{
_logger?.LogRunSucceeded(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());

Check failure

Code scanning / CodeQL

Clear text storage of sensitive information High

This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to field _password : String
as clear text.

Copilot Autofix

AI 4 months ago

To remediate this issue, we need to ensure that sensitive information—specifically passwords included in command arguments—is not written in cleartext to logs. The fix entails redacting passwords from the arguments string before passing it to any logging functions. This can be achieved by creating and using a helper method that detects password parameters in the command arguments and redacts their value, replacing them with a placeholder (e.g., "*****"). This method should be applied whenever process arguments are logged (e.g., in calls to _logger?.LogRunStarted(...), _logger?.LogRunSucceeded(...), _logger?.LogRunFailed(...), and _logger?.LogRunWaitCanceled(...)). Only the logged value should be redacted; the value used to execute the process must remain unmodified so functionality is unchanged.

Concretely:

  • Add a method to redact password values from argument strings.
  • Use this method to sanitize startInfo.Arguments before logging, e.g., RedactSensitiveArguments(startInfo.Arguments).
  • Update all logger invocations in ProcessRunner.Run (lines 42, 53, 60, 66) to use the redacted version.

Depending on the exact command grammar, basic redaction could, for example, replace value after a flag or positional password. For demonstration, we'll apply a simple regular expression to mask sequences that look like passwords in the common command constructions shown.


Suggested changeset 1
UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs b/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
--- a/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
+++ b/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
@@ -5,6 +5,7 @@
 namespace UiPath.SessionTools;
 
 using static Logging;
+using System.Text.RegularExpressions;
 
 public partial class ProcessRunner
 {
@@ -15,6 +16,21 @@
         _logger = logger;
     }
 
+    /// <summary>
+    /// Redacts sensitive arguments (like passwords) from a process argument string.
+    /// Very basic implementation for "net user ... [password]" and similar formats.
+    /// </summary>
+    private static string RedactSensitiveArguments(string arguments)
+    {
+        // Mask password argument for commands like: net user "username" password
+        // This replaces any argument after 'user "..."' as password with "*****"
+        // Adjust this if your command grammar varies!
+        var userPwdPattern = @"(user\s+""[^""]+""\s+)(\S+)";
+        arguments = Regex.Replace(arguments, userPwdPattern, "$1*****");
+        // Optionally, extend to /password:xxx switches or other schemes as needed.
+        return arguments;
+    }
+
     public virtual async Task<(string output, int exitCode)> Run(string fileName, string arguments, string workingDirectory = "", bool throwOnNonZero = false, CancellationToken ct = default)
     {
         var startInfo = new ProcessStartInfo()
@@ -39,7 +55,8 @@
         process.ErrorDataReceived += (_, e) => stderr.AppendLine(e.Data);
 
         _ = process.Start();
-        _logger?.LogRunStarted(startInfo.FileName, startInfo.Arguments);
+        var redactedArgs = RedactSensitiveArguments(startInfo.Arguments);
+        _logger?.LogRunStarted(startInfo.FileName, redactedArgs);
 
         process.BeginOutputReadLine();
         process.BeginErrorReadLine();
@@ -50,20 +67,20 @@
         }
         catch (OperationCanceledException)
         {
-            _logger?.LogRunWaitCanceled(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());
+            _logger?.LogRunWaitCanceled(startInfo.FileName, redactedArgs, stdout.ToString(), stderr.ToString());
 
             throw;
         }
 
         if (process.ExitCode is 0)
         {
-            _logger?.LogRunSucceeded(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());
+            _logger?.LogRunSucceeded(startInfo.FileName, redactedArgs, stdout.ToString(), stderr.ToString());
             return (stdout.ToString(), process.ExitCode);
         }
 
         string strStdout = stdout.ToString();
         string strStderr = stderr.ToString();
-        _logger?.LogRunFailed(startInfo.FileName, startInfo.Arguments, process.ExitCode, strStdout, strStderr);
+        _logger?.LogRunFailed(startInfo.FileName, redactedArgs, process.ExitCode, strStdout, strStderr);
 
         if (throwOnNonZero)
         {
EOF
@@ -5,6 +5,7 @@
namespace UiPath.SessionTools;

using static Logging;
using System.Text.RegularExpressions;

public partial class ProcessRunner
{
@@ -15,6 +16,21 @@
_logger = logger;
}

/// <summary>
/// Redacts sensitive arguments (like passwords) from a process argument string.
/// Very basic implementation for "net user ... [password]" and similar formats.
/// </summary>
private static string RedactSensitiveArguments(string arguments)
{
// Mask password argument for commands like: net user "username" password
// This replaces any argument after 'user "..."' as password with "*****"
// Adjust this if your command grammar varies!
var userPwdPattern = @"(user\s+""[^""]+""\s+)(\S+)";
arguments = Regex.Replace(arguments, userPwdPattern, "$1*****");
// Optionally, extend to /password:xxx switches or other schemes as needed.
return arguments;
}

public virtual async Task<(string output, int exitCode)> Run(string fileName, string arguments, string workingDirectory = "", bool throwOnNonZero = false, CancellationToken ct = default)
{
var startInfo = new ProcessStartInfo()
@@ -39,7 +55,8 @@
process.ErrorDataReceived += (_, e) => stderr.AppendLine(e.Data);

_ = process.Start();
_logger?.LogRunStarted(startInfo.FileName, startInfo.Arguments);
var redactedArgs = RedactSensitiveArguments(startInfo.Arguments);
_logger?.LogRunStarted(startInfo.FileName, redactedArgs);

process.BeginOutputReadLine();
process.BeginErrorReadLine();
@@ -50,20 +67,20 @@
}
catch (OperationCanceledException)
{
_logger?.LogRunWaitCanceled(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());
_logger?.LogRunWaitCanceled(startInfo.FileName, redactedArgs, stdout.ToString(), stderr.ToString());

throw;
}

if (process.ExitCode is 0)
{
_logger?.LogRunSucceeded(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());
_logger?.LogRunSucceeded(startInfo.FileName, redactedArgs, stdout.ToString(), stderr.ToString());
return (stdout.ToString(), process.ExitCode);
}

string strStdout = stdout.ToString();
string strStderr = stderr.ToString();
_logger?.LogRunFailed(startInfo.FileName, startInfo.Arguments, process.ExitCode, strStdout, strStderr);
_logger?.LogRunFailed(startInfo.FileName, redactedArgs, process.ExitCode, strStdout, strStderr);

if (throwOnNonZero)
{
Copilot is powered by AI and may make mistakes. Always verify output.

string strStdout = stdout.ToString();
string strStderr = stderr.ToString();
_logger?.LogRunFailed(startInfo.FileName, startInfo.Arguments, process.ExitCode, strStdout, strStderr);

Check failure

Code scanning / CodeQL

Clear text storage of sensitive information High

This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to parameter password : String
as clear text.
This stores sensitive data returned by
access to field _password : String
as clear text.

Copilot Autofix

AI 4 months ago

Generally, to remediate "clear text storage of sensitive information", we should avoid logging secret values. In this context, the arguments field constructed for process invocation may contain passwords in clear text (notably for commands like net user ... password), but logging infrastructure currently logs the entire arguments string.

A robust fix is to sanitize or redact sensitive information (such as passwords) before logging. This means masking or removing any passwords from arguments before passing it to the logger. A helper function should be added to redact passwords from such parameter strings. This helper can use a regular expression or simple text replacement to replace passwords in "net user" commands by replacing the password argument with a constant value like "REDACTED". All logging calls using startInfo.Arguments should instead use a sanitized version (e.g., RedactSensitiveArguments(startInfo.FileName, startInfo.Arguments)).

This requires:

  • Adding a method, e.g., RedactSensitiveArguments(string fileName, string arguments), to perform redaction.
  • Replacing all logger calls that use startInfo.Arguments with sanitized arguments.
  • Importing System.Text.RegularExpressions for regex if needed.

All changes should be performed in UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs, as that is where the logging (and the original problem) occurs.


Suggested changeset 1
UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs b/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
--- a/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
+++ b/UiPath.FreeRdpClient/UiPath.SessionTools/Processes/ProcessRunner.cs
@@ -1,7 +1,7 @@
 using Microsoft.Extensions.Logging;
 using System.Diagnostics;
 using System.Text;
-
+using System.Text.RegularExpressions;
 namespace UiPath.SessionTools;
 
 using static Logging;
@@ -10,6 +10,29 @@
 {
     private readonly ILogger? _logger;
 
+    /// <summary>
+    /// Redact sensitive data (such as passwords) from arguments before logging.
+    /// </summary>
+    private static string RedactSensitiveArguments(string fileName, string arguments)
+    {
+        // Only redact if the command is one we expect a password in.
+        // For "net user" commands: net user "user" password
+        if (string.Equals(fileName, "net", StringComparison.OrdinalIgnoreCase))
+        {
+            // regex to match: user "username" password [..] or user username password [...]
+            // Net user ADD: user "username" password /add
+            // Net user SET: user "username" password
+            // Replace password with ***
+            var netUserPattern = @"(user\s+""[^""]+""\s+)([^\s""]+)";
+            arguments = Regex.Replace(arguments, netUserPattern, m => m.Groups[1].Value + "***REDACTED***", RegexOptions.IgnoreCase);
+            
+            // Also handle user username password without quotes
+            netUserPattern = @"(user\s+[^\s""]+\s+)([^\s""]+)";
+            arguments = Regex.Replace(arguments, netUserPattern, m => m.Groups[1].Value + "***REDACTED***", RegexOptions.IgnoreCase);
+        }
+        // For "wmic useraccount ... set PasswordExpires=false" there is no password, so nothing to redact.
+        return arguments;
+    }
     public ProcessRunner(ILogger? logger = null)
     {
         _logger = logger;
@@ -39,7 +62,7 @@
         process.ErrorDataReceived += (_, e) => stderr.AppendLine(e.Data);
 
         _ = process.Start();
-        _logger?.LogRunStarted(startInfo.FileName, startInfo.Arguments);
+        _logger?.LogRunStarted(startInfo.FileName, RedactSensitiveArguments(startInfo.FileName, startInfo.Arguments));
 
         process.BeginOutputReadLine();
         process.BeginErrorReadLine();
@@ -50,20 +73,20 @@
         }
         catch (OperationCanceledException)
         {
-            _logger?.LogRunWaitCanceled(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());
+            _logger?.LogRunWaitCanceled(startInfo.FileName, RedactSensitiveArguments(startInfo.FileName, startInfo.Arguments), stdout.ToString(), stderr.ToString());
 
             throw;
         }
 
         if (process.ExitCode is 0)
         {
-            _logger?.LogRunSucceeded(startInfo.FileName, startInfo.Arguments, stdout.ToString(), stderr.ToString());
+            _logger?.LogRunSucceeded(startInfo.FileName, RedactSensitiveArguments(startInfo.FileName, startInfo.Arguments), stdout.ToString(), stderr.ToString());
             return (stdout.ToString(), process.ExitCode);
         }
 
         string strStdout = stdout.ToString();
         string strStderr = stderr.ToString();
-        _logger?.LogRunFailed(startInfo.FileName, startInfo.Arguments, process.ExitCode, strStdout, strStderr);
+        _logger?.LogRunFailed(startInfo.FileName, RedactSensitiveArguments(startInfo.FileName, startInfo.Arguments), process.ExitCode, strStdout, strStderr);
 
         if (throwOnNonZero)
         {
EOF
Copilot is powered by AI and may make mistakes. Always verify output.
winpr_RC4_Free(rdp->rc4_encrypt_key);
rdp->rc4_encrypt_key = NULL;
}
rdp_free_rc4_decrypt_keys(rdp);

Check failure

Code scanning / CodeQL

Use of a broken or risky cryptographic algorithm High

This file makes use of a broken or weak cryptographic algorithm (specified by
call to rdp_free_rc4_decrypt_keys
).
This file makes use of a broken or weak cryptographic algorithm (specified by
call to winpr_RC4_New
).
This file makes use of a broken or weak cryptographic algorithm (specified by
call to rdp_free_rc4_encrypt_keys
).
This file makes use of a broken or weak cryptographic algorithm (specified by
call to winpr_RC4_New
).
This file makes use of a broken or weak cryptographic algorithm (specified by
call to rdp_free_rc4_decrypt_keys
).
This file makes use of a broken or weak cryptographic algorithm (specified by
call to rdp_free_rc4_encrypt_keys
).
This file makes use of a broken or weak cryptographic algorithm (specified by
call to rdp_free_rc4_decrypt_keys
).
This file makes use of a broken or weak cryptographic algorithm (specified by
call to rdp_free_rc4_encrypt_keys
).

for (x = 0; x < ctx->pos; x++)
{
char* msg = str + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE;

Check failure

Code scanning / CodeQL

Suspicious add with sizeof High

Suspicious sizeof offset in a pointer arithmetic expression. The type of the pointer is
char **
.

Copilot Autofix

AI 4 months ago

The problem is on line 113, where pointer arithmetic is performed on a char**, which automatically scales by sizeof(char*), but the code multiplies the offset by an additional sizeof(char*), effectively causing a miscalculation. The intention seems to be to compute a pointer to a buffer region after the array of pointers. This should be done by first casting the pointer to a char*, so arithmetic operates in bytes. The correct way is:

  • Calculate the base address for the buffer portion by taking the start address: (char*)str + ctx->pos * sizeof(char*)
  • Add the offset for the current message: x * UNWIND_MAX_LINE_SIZE

So, the fixed line is:

char* msg = ((char*)str) + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE;

Only line 113 needs this fix, with no extra includes or definitions.


Suggested changeset 1
winpr/libwinpr/utils/unwind/debug.c

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/winpr/libwinpr/utils/unwind/debug.c b/winpr/libwinpr/utils/unwind/debug.c
--- a/winpr/libwinpr/utils/unwind/debug.c
+++ b/winpr/libwinpr/utils/unwind/debug.c
@@ -110,7 +110,7 @@
 
 	for (x = 0; x < ctx->pos; x++)
 	{
-		char* msg = str + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE;
+		char* msg = ((char*)str) + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE;
 		const unwind_info_t* info = &ctx->info[x];
 		Dl_info dlinfo = { 0 };
 		int rc = dladdr(info->pc, &dlinfo);
EOF
@@ -110,7 +110,7 @@

for (x = 0; x < ctx->pos; x++)
{
char* msg = str + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE;
char* msg = ((char*)str) + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE;
const unwind_info_t* info = &ctx->info[x];
Dl_info dlinfo = { 0 };
int rc = dladdr(info->pc, &dlinfo);
Copilot is powered by AI and may make mistakes. Always verify output.
@mihainradu mihainradu changed the base branch from master to stable-2.0 October 20, 2025 09:51
@mihainradu mihainradu closed this Dec 5, 2025
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.

6 participants