Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.1.29
2.1.30
Original file line number Diff line number Diff line change
Expand Up @@ -275,11 +275,6 @@ private Task ExecuteWorkloadAsync(string pathToExe, string commandLineArguments,
await this.LogProcessDetailsAsync(process, telemetryContext, this.PackageName, logToFile: true);
process.ThrowIfWorkloadFailed();

if (process.StandardError.Length > 0)
{
process.ThrowOnStandardError<WorkloadException>(errorReason: ErrorReason.WorkloadFailed);
}

string standardOutput = process.StandardOutput.ToString();
this.CaptureMetrics(process, standardOutput, commandLineArguments, telemetryContext, cancellationToken);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,6 @@ public void ObscureSecretsObfuscatesSasUriSignatures_Scenario1()
Assert.AreEqual(dataContainingSecrets.Item2, obscuredString);
}

[Test]
public void ObscureSecretsObfuscatesSasUriSignatures_Scenario2()
{
Tuple<string, string> dataContainingSecrets = SensitiveDataTests.GetSasUriPairScenario2();
string obscuredString = SensitiveData.ObscureSecrets(dataContainingSecrets.Item1);

Assert.AreEqual(dataContainingSecrets.Item2, obscuredString);
}

[Test]
[TestCase("Password=AnySecretHereae09g34YT112", "Password=...")]
[TestCase("Password AnySecretHereae09g34YT112", "Password ...")]
Expand Down Expand Up @@ -216,6 +207,18 @@ public void ObscureSecretsHandlesCasesWhereThePasswordTermIsASubstringThatIsPart
Assert.AreEqual(expectedString, obscuredString);
}

[Test]
[TestCase("user@10.2.3.5;pass_;wor;d", "user@10.2.3.5;...")]
[TestCase("user@10.2.3.5;pass__w@rd", "user@10.2.3.5;...")]
[TestCase("user@machine@somewhere;pass", "user@machine@somewhere;...")]
[TestCase("user@machine@somewhere;pass;_w@rd", "user@machine@somewhere;...")]
[TestCase("user@2001:db8:85a3:0:0:8a2e:370:7334;pass;_w@rd", "user@2001:db8:85a3:0:0:8a2e:370:7334;...")]
public void ObscureSecretsObfuscatesPasswordsInAgentSshConnections(string originalString, string expectedString)
{
string obscuredString = SensitiveData.ObscureSecrets(originalString);
Assert.AreEqual(expectedString, obscuredString);
}

[Test]
public void ObscureSecretsObfuscatesSecretsThatMatchAGivenRegularExpression_Scenario1()
{
Expand Down
4 changes: 4 additions & 0 deletions src/VirtualClient/VirtualClient.Common/SensitiveData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public static class SensitiveData
// of expressions to allow the handling of special cases (e.g. delimited key/value pair groups (e.g. Password=s@me,val;ue,,,Property1=Value1).
new Regex("(?<=Password[=\x20]+\"*)(?:,{0,2}[\x21\x23-\x2B\x2D-\x7E]+,{0,2}[\x21\x23-\x2B\x2D-\x7E]+)+\"*", RegexOptions.IgnoreCase),
new Regex("(?<=Pwd[=\x20]+\"*)(?:,{0,2}[\x21\x23-\x2B\x2D-\x7E]+,{0,2}[\x21\x23-\x2B\x2D-\x7E]+)+\"*", RegexOptions.IgnoreCase),

// Agent SSH connections allow for passwords
// (e.g. user@10.2.3.5;pass_;wor;d).
new Regex(@"[0-9a-z_\-\. ]+@[^;]+;([\x20-\x7E]+)", RegexOptions.IgnoreCase)
};

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace VirtualClient.Contracts
/// <summary>
/// Defines one or more aliases for a given class for reflection support.
/// </summary>
[AttributeUsage(validOn: AttributeTargets.Class)]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class AliasAttribute : Attribute
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace VirtualClient.Contracts
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using Microsoft.Extensions.Logging;
using VirtualClient.Common.Extensions;
Expand Down
15 changes: 0 additions & 15 deletions src/VirtualClient/VirtualClient.Contracts/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,21 +150,6 @@ public static class EnvironmentVariable
/// </summary>
public const string PATH = nameof(PATH);

/// <summary>
/// Name = SDK_EVENTHUB_CONNECTION
/// </summary>
public const string SDK_EVENTHUB_CONNECTION = nameof(SDK_EVENTHUB_CONNECTION);

/// <summary>
/// Name = SDK_PACKAGES_CONNECTION
/// </summary>
public const string SDK_PACKAGES_CONNECTION = nameof(SDK_PACKAGES_CONNECTION);

/// <summary>
/// Name = SDK_PACKAGES_DIR
/// </summary>
public const string SDK_PACKAGES_DIR = nameof(SDK_PACKAGES_DIR);

/// <summary>
/// Name = SUDO_USER
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public PlatformSpecifics(PlatformID platform, Architecture architecture, string
public string StateDirectory { get; set; }

/// <summary>
/// The directory where scripts related to workloads exist.
/// The directory where temp files can be saved.
/// </summary>
public string TempDirectory { get; set; }

Expand Down Expand Up @@ -280,8 +280,8 @@ public static string ResolveRelativePaths(string text)
{
// Ensure that relative working directory paths are fully expanded. Preserve case-sensitivity
// to avoid anomalies on Linux.
string relativeDirectory = match.Value;
resolved = resolved.Replace(relativeDirectory, Path.GetFullPath(relativeDirectory), StringComparison.Ordinal);
string relativePath = match.Value;
resolved = resolved.Replace(relativePath, Path.GetFullPath(relativePath), StringComparison.Ordinal);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ protected set
/// placeholders and well-known terms to be replaced in the values of the parameters before
/// execution of workloads, monitors or dependencies.
/// </summary>
public bool ParametersEvaluated { get; internal set; }
public bool ParametersEvaluated { get; protected set; }

/// <summary>
/// The OS/system platform (e.g. Windows, Unix).
Expand Down Expand Up @@ -659,6 +659,28 @@ public static bool IsSupported(VirtualClientComponent component)
return platformSupported && component.IsSupported();
}

/// <summary>
/// Evaluates each of the parameters provided to the component to replace
/// supported placeholder expressions (e.g. {PackagePath:anytool} -> replace with path to 'anytool' package).
/// </summary>
/// <param name="cancellationToken">A token that can be used to cancel the operations.</param>
/// <param name="force">Forces the evaluation of the parameters for scenarios where re-evaluation is necessary after an initial pass. Default = false.</param>
public async Task EvaluateParametersAsync(CancellationToken cancellationToken, bool force = false)
{
if (!this.ParametersEvaluated || force)
{
if (this.Parameters?.Any() == true)
{
if (this.Dependencies.TryGetService<IExpressionEvaluator>(out IExpressionEvaluator evaluator))
{
await evaluator.EvaluateAsync(this.Dependencies, this.Parameters, cancellationToken);
}
}

this.ParametersEvaluated = true;
}
}

/// <summary>
/// When overriden in a derived class, executes the component logic.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,31 +220,6 @@ public static IEnumerable<FileUploadDescriptor> CreateFileUploadDescriptors(this
return descriptors;
}

/// <summary>
/// Evaluates each of the parameters provided to the component to replace
/// supported placeholder expressions (e.g. {PackagePath:anytool} -> replace with path to 'anytool' package).
/// </summary>
/// <param name="component">The component whose parameters to evaluate.</param>
/// <param name="cancellationToken">A token that can be used to cancel the operations.</param>
/// <param name="force">Forces the evaluation of the parameters for scenarios where re-evaluation is necessary after an initial pass. Default = false.</param>
public static async Task EvaluateParametersAsync(this VirtualClientComponent component, CancellationToken cancellationToken, bool force = false)
{
component.ThrowIfNull(nameof(component));

if (!component.ParametersEvaluated || force)
{
if (component.Parameters?.Any() == true)
{
if (component.Dependencies.TryGetService<IExpressionEvaluator>(out IExpressionEvaluator evaluator))
{
await evaluator.EvaluateAsync(component.Dependencies, component.Parameters, cancellationToken);
}
}

component.ParametersEvaluated = true;
}
}

/// <summary>
/// Returns the client instance defined in the environment layout provided to the Virtual Client
/// whose ID matches.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static class VirtualClientRuntime
public static string[] CommandLineArguments { get; internal set; }

/// <summary>
/// Metadata provided to VC on the command line.
/// The current experiment ID for the application.
/// </summary>
public static IReadOnlyDictionary<string, IConvertible> CommandLineMetadata { get; internal set; }

Expand All @@ -67,11 +67,6 @@ public static class VirtualClientRuntime
/// </summary>
public static IReadOnlyDictionary<string, IConvertible> CommandLineParameters { get; internal set; }

/// <summary>
/// The current experiment ID for the application.
/// </summary>
public static string ExperimentId { get; internal set; }

/// <summary>
/// The current platform-specifics for the application.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace VirtualClient.Controller
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using NUnit.Framework;
using Renci.SshNet;
using VirtualClient.Common;

[TestFixture]
[Category("Unit")]
internal class ExecuteSshCommandTests : MockFixture
{
private ConnectionInfo mockConnection;
private Mock<ISshClientProxy> mockSshClient;

public void SetupTest(PlatformID platform, Architecture architecture = Architecture.X64)
{
this.Setup(platform, architecture);

string mockCommand = "bash -c \"anycommand.sh -argument1 1234 -argument2 5678\"";
this.Parameters[nameof(ExecuteSshCommand.Command)] = mockCommand;

this.mockConnection = new ConnectionInfo("192.168.1.15", "user01", new PasswordAuthenticationMethod("user01", "pw"));
this.mockSshClient = new Mock<ISshClientProxy>();

// Setup:
// The SSH client executes a command and returns a valid result.
this.mockSshClient.Setup(client => client.ConnectionInfo)
.Returns(this.mockConnection);

this.mockSshClient.Setup(client => client.ExecuteCommandAsync(It.IsAny<string>(), It.IsAny<CancellationToken>(), It.IsAny<Action<SshCommandOutputInfo>>()))
.ReturnsAsync(new ProcessDetails
{
Id = 1234,
CommandLine = mockCommand,
StandardOutput = $"Executed command"
});

this.Dependencies.AddSingleton<IEnumerable<ISshClientProxy>>(new List<ISshClientProxy> { this.mockSshClient.Object });
}

[Test]
public void ExecuteSshCommandCannotBeRanAsAMonitor()
{
this.SetupTest(PlatformID.Unix);

using (var component = new TestExecuteSshCommand(this))
{
component.ComponentType = ComponentType.Monitor;
Assert.ThrowsAsync<NotSupportedException>(() => component.ExecuteAsync(CancellationToken.None));
}
}

[Test]
[TestCase("bash -c \"anycommand.sh -argument1 11 -argument2 22\"")]
[TestCase("cmd -c \"anycommand.cmd -argument1 11 -argument2 22\"")]
public async Task ExecuteSshCommandExecutesTheExpectedCommand(string expectedCommand)
{
this.SetupTest(PlatformID.Unix);
this.Parameters[nameof(ExecuteSshCommand.Command)] = expectedCommand;

using (var component = new TestExecuteSshCommand(this))
{
this.mockSshClient.Setup(client => client.ExecuteCommandAsync(expectedCommand, It.IsAny<CancellationToken>(), It.IsAny<Action<SshCommandOutputInfo>>()))
.ReturnsAsync(new ProcessDetails
{
Id = 1234,
CommandLine = expectedCommand,
StandardOutput = $"Executed '{expectedCommand}'"
});

await component.ExecuteAsync(CancellationToken.None);

this.mockSshClient.Verify(client => client.ExecuteCommandAsync(expectedCommand, It.IsAny<CancellationToken>(), It.IsAny<Action<SshCommandOutputInfo>>()), Times.Once);
}
}

private class TestExecuteSshCommand : ExecuteSshCommand
{
public TestExecuteSshCommand(MockFixture mockFixture)
: base(mockFixture?.Dependencies, mockFixture?.Parameters)
{
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<IsPackable>false</IsPackable>
<RunCodeAnalysis>false</RunCodeAnalysis>
<RunAnalyzers>false</RunAnalyzers>
<RootNamespace>VirtualClient.Controller.UnitTests</RootNamespace>
<NoWarn>AsyncFixer02;SA1005;SA1120</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Moq" Version="$(Moq_PackageVersion)" />
<PackageReference Include="NUnit" Version="$(NUnit_PackageVersion)" />
<PackageReference Include="NUnit3TestAdapter" Version="$(NUnit3TestAdapter_PackageVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(Microsoft_NET_Test_Sdk_PackageVersion)" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\VirtualClient.Controller\VirtualClient.Controller.csproj" />
<ProjectReference Include="..\VirtualClient.TestExtensions\VirtualClient.TestExtensions.csproj" />
</ItemGroup>

<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Module.props))\Module.props" />

</Project>
Loading
Loading