Skip to content

Commit bc979be

Browse files
praval-microsoftazure-pipelines-botrajmishra1997
authored
Deactivate Base64 Encoded Vso Commands AB#2008236 (#5158)
* Deactivate Base64 Encoded Vso Commands * typo * simplified isbase64encoded func * Simplifying DeactivateBase64EncodedVsoCommands * test cases * Addressing comments * Addressing comments * Addressed comments * Addressed comments * PR comments * PR comments * Simplified DeactivateVsoCommandsIfBase64Encoded function as per review comment --------- Co-authored-by: azure-pipelines-bot <[email protected]> Co-authored-by: Rajani Priya Mishra <[email protected]>
1 parent 8ada61f commit bc979be

File tree

3 files changed

+118
-3
lines changed

3 files changed

+118
-3
lines changed

src/Agent.Sdk/Util/StringUtil.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ public static bool AreHashesEqual(string leftValue, string rightValue)
282282

283283
/// <summary>
284284
/// Finds all vso commands in the line and deactivates them
285+
/// Also, assuming line to be base64 encoded, finds all vso commands in the decoded string and deactivates them and re-encode
285286
/// </summary>
286287
/// <returns>String without vso commands that can be executed</returns>
287288
public static string DeactivateVsoCommands(string input)
@@ -291,6 +292,41 @@ public static string DeactivateVsoCommands(string input)
291292
return string.Empty;
292293
}
293294

295+
try
296+
{
297+
input = DeactivateVsoCommandsIfBase64Encoded(input);
298+
}
299+
catch (FormatException)
300+
{
301+
// Ignore exception and continue to deactivate vso commands in the input string.
302+
}
303+
return ScrapVsoCommands(input);
304+
}
305+
306+
/// <summary>
307+
/// Tries to decode the input string assuming it to be base64 encoded and
308+
/// scraps vso command if any and re-encodes the updated string.
309+
/// An exception is thrown for the case when the input is not base64 encoded.
310+
/// </summary>
311+
/// <returns>String without vso commands that can be executed</returns>
312+
public static string DeactivateVsoCommandsIfBase64Encoded(string input)
313+
{
314+
if (input == null)
315+
{
316+
throw new ArgumentNullException(nameof(input), "Input string cannot be null.");
317+
}
318+
if (input.Length == 0)
319+
{
320+
return string.Empty;
321+
}
322+
byte[] decodedBytes = Convert.FromBase64String(input);
323+
string decodedString = Encoding.UTF8.GetString(decodedBytes);
324+
decodedString = ScrapVsoCommands(decodedString);
325+
return Convert.ToBase64String(Encoding.UTF8.GetBytes(decodedString));
326+
}
327+
328+
private static string ScrapVsoCommands(string input)
329+
{
294330
return Regex.Replace(input, "##vso", "**vso", RegexOptions.IgnoreCase);
295331
}
296332
}

src/Test/L0/Util/StringUtilL0.cs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the MIT License.
33

44
using Microsoft.VisualStudio.Services.Agent.Util;
5+
using System;
6+
using System.Text;
57
using System.Globalization;
68
using Xunit;
79

@@ -22,14 +24,66 @@ public class StringUtilL0
2224
[InlineData("##VsO", "**vso")]
2325
[InlineData("", "")]
2426
[InlineData(null, "")]
25-
[InlineData(" ", " ")]
27+
[InlineData(" ", "")]
2628
public void DeactivateVsoCommandsFromStringTest(string input, string expected)
2729
{
2830
var result = StringUtil.DeactivateVsoCommands(input);
2931

3032
Assert.Equal(expected, result);
3133
}
3234

35+
/// <summary>
36+
/// In this test, a vso command is encoded as a bse64 string and being passed as input to the DeactivateBase64EncodedVsoCommands
37+
/// The returned string when decoded will have ## replaced with **, deactivating the vso command.
38+
/// </summary>
39+
[Fact]
40+
[Trait("Level", "L0")]
41+
[Trait("Category", "Common")]
42+
public void DeactivateVsoCommandsIfBase64Encoded_EncodedVsoCommands_Returns_DeactivatedVsoCommands()
43+
{
44+
string vsoCommand = "##vso[task.setvariable variable=downloadUrl]https://www.evil.com";
45+
string encodedVsoCommand = Convert.ToBase64String(Encoding.UTF8.GetBytes(vsoCommand));
46+
47+
string result = StringUtil.DeactivateVsoCommandsIfBase64Encoded(encodedVsoCommand);
48+
49+
string deactivatedVsoCommand = "**vso[task.setvariable variable=downloadUrl]https://www.evil.com";
50+
var expected = Convert.ToBase64String(Encoding.UTF8.GetBytes(deactivatedVsoCommand));
51+
52+
Assert.Equal(expected, result);
53+
}
54+
55+
/// <summary>
56+
/// In this test, a vso command is being passed as input to the DeactivateBase64EncodedVsoCommands
57+
/// The unmodified string would be returned.
58+
/// </summary>
59+
[Fact]
60+
[Trait("Level", "L0")]
61+
[Trait("Category", "Common")]
62+
public void DeactivateVsoCommandsIfBase64Encoded_NotEncodedVsoCommands_Throws_FormatException()
63+
{
64+
string vsoCommand = "##vso[task.setvariable variable=downloadUrl]https://www.evil.com";
65+
Assert.Throws<FormatException>(() => StringUtil.DeactivateVsoCommandsIfBase64Encoded(vsoCommand));
66+
}
67+
68+
[Fact]
69+
[Trait("Level", "L0")]
70+
[Trait("Category", "Common")]
71+
public void DeactivateVsoCommandsIfBase64Encoded_InputEmpty_Returns_UnmodifiedString()
72+
{
73+
string vsoCommand = "";
74+
string result = StringUtil.DeactivateVsoCommandsIfBase64Encoded(vsoCommand);
75+
Assert.Equal(vsoCommand, result);
76+
}
77+
78+
[Fact]
79+
[Trait("Level", "L0")]
80+
[Trait("Category", "Common")]
81+
public void DeactivateVsoCommandsIfBase64Encoded_InputNull_Throws_Exception()
82+
{
83+
string vsoCommand = null;
84+
Assert.Throws<ArgumentNullException>(() => StringUtil.DeactivateVsoCommandsIfBase64Encoded(vsoCommand));
85+
}
86+
3387
[Fact]
3488
[Trait("Level", "L0")]
3589
[Trait("Category", "Common")]

src/Test/L0/Worker/WorkerL0.cs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Xunit;
1212
using Microsoft.VisualStudio.Services.WebApi;
1313
using Pipelines = Microsoft.TeamFoundation.DistributedTask.Pipelines;
14+
using System.Text;
1415

1516
namespace Microsoft.VisualStudio.Services.Agent.Tests.Worker
1617
{
@@ -295,13 +296,37 @@ public void VerifyJobRequestMessageVsoCommandsDeactivatedIfVariableCasesHandlesN
295296

296297
message.Variables[Constants.Variables.Build.SourceVersionMessage] = "";
297298
message.Variables[Constants.Variables.System.SourceVersionMessage] = null;
298-
message.Variables[Constants.Variables.Build.DefinitionName] = " ";
299+
message.Variables[Constants.Variables.Build.DefinitionName] = "";
299300

300301
var scrubbedMessage = WorkerUtilities.DeactivateVsoCommandsFromJobMessageVariables(message);
301302

302303
Assert.Equal("", scrubbedMessage.Variables[Constants.Variables.Build.SourceVersionMessage]);
303304
Assert.Equal("", scrubbedMessage.Variables[Constants.Variables.System.SourceVersionMessage]);
304-
Assert.Equal(" ", scrubbedMessage.Variables[Constants.Variables.Build.DefinitionName]);
305+
Assert.Equal("", scrubbedMessage.Variables[Constants.Variables.Build.DefinitionName]);
306+
}
307+
308+
309+
[Fact]
310+
[Trait("Level", "L0")]
311+
[Trait("Category", "Worker")]
312+
public void VerifyJobRequestMessageVsoCommandsDeactivatedIfVariableCasesHandlesBase64EncodedVsoCommands()
313+
{
314+
Pipelines.AgentJobRequestMessage message = CreateJobRequestMessage("jobWithVsoCommands");
315+
// Set up
316+
// A build variable is assigned a VSO command encoded as base 64 string
317+
string vsoCommand = "##vso[task.setvariable variable=downloadUrl]https://www.evil.com";
318+
string encodedVsoCommand = Convert.ToBase64String(Encoding.UTF8.GetBytes(vsoCommand));
319+
message.Variables[Constants.Variables.Build.SourceVersionMessage] = encodedVsoCommand;
320+
321+
// Act
322+
var scrubbedMessage = WorkerUtilities.DeactivateVsoCommandsFromJobMessageVariables(message);
323+
324+
// Expected
325+
// Returned string in it's decode form would have ## replaced with ** to deactivate vso command
326+
string deactivatedVsoCommand = "**vso[task.setvariable variable=downloadUrl]https://www.evil.com";
327+
string expected = Convert.ToBase64String(Encoding.UTF8.GetBytes(deactivatedVsoCommand));
328+
329+
Assert.Equal(expected, scrubbedMessage.Variables[Constants.Variables.Build.SourceVersionMessage]);
305330
}
306331

307332
private bool IsMessageIdentical(Pipelines.AgentJobRequestMessage source, Pipelines.AgentJobRequestMessage target)

0 commit comments

Comments
 (0)