Skip to content

Commit

Permalink
WI 2048209 (#4367)
Browse files Browse the repository at this point in the history
* Add disableInlineExecution

* Add parentheses for clarity
  • Loading branch information
KonstantinTyukalov authored Jul 21, 2023
1 parent 3517c9f commit b5ff440
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 30 deletions.
13 changes: 9 additions & 4 deletions src/Agent.Sdk/Knob/AgentKnobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -456,15 +456,20 @@ public class AgentKnobs
public static readonly Knob ProcessHandlerSecureArguments = new Knob(
nameof(ProcessHandlerSecureArguments),
"Enables passing arguments for process handler secure way",
new RuntimeKnobSource("AGENT_PH_ENABLE_SECURE_ARGUMENTS"),
new EnvironmentKnobSource("AGENT_PH_ENABLE_SECURE_ARGUMENTS"),
new RuntimeKnobSource("AZP_75787_ENABLE_NEW_LOGIC"),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob ProcessHandlerSecureArgumentsAudit = new Knob(
nameof(ProcessHandlerSecureArguments),
"Enables logging of passing arguments for process handler secure way",
new RuntimeKnobSource("AZP_75787_ENABLE_NEW_LOGIC_LOG"),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob ProcessHandlerTelemetry = new Knob(
nameof(ProcessHandlerTelemetry),
"Enables publishing telemetry about processing of arguments for Process Handler",
new RuntimeKnobSource("AGENT_PH_ENABLE_TELEMETRY"),
new EnvironmentKnobSource("AGENT_PH_ENABLE_TELEMETRY"),
new RuntimeKnobSource("AZP_75787_ENABLE_COLLECT"),
new EnvironmentKnobSource("AZP_75787_ENABLE_COLLECT"),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob DisableDrainQueuesAfterTask = new Knob(
Expand Down
81 changes: 55 additions & 26 deletions src/Agent.Worker/Handlers/ProcessHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ public async Task RunAsync()

Trace.Info($"Command is rooted: {isCommandRooted}");

var disableInlineExecution = StringUtil.ConvertToBoolean(Data.DisableInlineExecution);
ExecutionContext.Debug($"Disable inline execution: '{disableInlineExecution}'");

if (disableInlineExecution && !File.Exists(command))
{
throw new Exception(StringUtil.Loc("FileNotFound", command));
}

// Determine the working directory.
string workingDirectory;
if (!string.IsNullOrEmpty(Data.WorkingDirectory))
Expand Down Expand Up @@ -117,33 +125,46 @@ public async Task RunAsync()
_modifyEnvironment = StringUtil.ConvertToBoolean(Data.ModifyEnvironment);
ExecutionContext.Debug($"Modify environment: '{_modifyEnvironment}'");

var enableSecureArguments = AgentKnobs.ProcessHandlerSecureArguments.GetValue(ExecutionContext).AsBoolean();
ExecutionContext.Debug($"Enable secure arguments: '{enableSecureArguments}'");

// Resolve cmd.exe.
string cmdExe = System.Environment.GetEnvironmentVariable("ComSpec");
if (string.IsNullOrEmpty(cmdExe))
{
cmdExe = "cmd.exe";
}

if (enableSecureArguments)
var enableSecureArguments = AgentKnobs.ProcessHandlerSecureArguments.GetValue(ExecutionContext).AsBoolean();
ExecutionContext.Debug($"Enable secure arguments: '{enableSecureArguments}'");
var enableSecureArgumentsAudit = AgentKnobs.ProcessHandlerSecureArgumentsAudit.GetValue(ExecutionContext).AsBoolean();
ExecutionContext.Debug($"Enable secure arguments audit: '{enableSecureArgumentsAudit}'");
var enableTelemetry = AgentKnobs.ProcessHandlerTelemetry.GetValue(ExecutionContext).AsBoolean();
ExecutionContext.Debug($"Enable telemetry: '{enableTelemetry}'");

var enableFileArgs = disableInlineExecution && enableSecureArguments;

if ((disableInlineExecution && (enableSecureArgumentsAudit || enableSecureArguments)) || enableTelemetry)
{
GenerateScriptFile(cmdExe, command, arguments);
var (processedArgs, telemetry) = ProcessHandlerHelper.ProcessInputArguments(arguments);

if (disableInlineExecution && enableSecureArgumentsAudit)
{
ExecutionContext.Warning($"The following arguments will be executed: '{processedArgs}'");
}
if (enableFileArgs)
{
GenerateScriptFile(cmdExe, command, processedArgs);
}
if (enableTelemetry)
{
ExecutionContext.Debug($"Agent PH telemetry: {JsonConvert.SerializeObject(telemetry.ToDictionary(), Formatting.None)}");
PublishTelemetry(telemetry.ToDictionary(), "ProcessHandler");
}
}

// Format the input to be invoked from cmd.exe to enable built-in shell commands. For example, RMDIR.
var cmdExeArgs = enableSecureArguments
? $"/c \"{_generatedScriptPath}"
: $"/c \"{command} {arguments}";

cmdExeArgs += _modifyEnvironment && !enableSecureArguments
? $" && echo {OutputDelimiter} && set \""
: "\"";
string cmdExeArgs = PrepareCmdExeArgs(command, arguments, enableFileArgs);

// Invoke the process.
ExecutionContext.Debug($"{cmdExe} {cmdExeArgs}");
ExecutionContext.Command($"{command} {arguments}");
ExecutionContext.Command($"{cmdExeArgs}");
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{
processInvoker.OutputDataReceived += OnOutputDataReceived;
Expand Down Expand Up @@ -190,23 +211,31 @@ public async Task RunAsync()
}
}

private string PrepareCmdExeArgs(string command, string arguments, bool enableFileArgs)
{
string cmdExeArgs;
if (enableFileArgs)
{
cmdExeArgs = $"/c \"{_generatedScriptPath}\"";
}
else
{
// Format the input to be invoked from cmd.exe to enable built-in shell commands. For example, RMDIR.
cmdExeArgs = $"/c \"{command} {arguments}";
cmdExeArgs += _modifyEnvironment
? $" && echo {OutputDelimiter} && set \""
: "\"";
}

return cmdExeArgs;
}

private void GenerateScriptFile(string cmdExe, string command, string arguments)
{
var scriptId = Guid.NewGuid().ToString();
string inputArgsEnvVarName = VarUtil.ConvertToEnvVariableFormat("AGENT_PH_ARGS_" + scriptId[..8]);

var (processedArgs, telemetry) = ProcessHandlerHelper.ProcessInputArguments(arguments);

ExecutionContext.Debug($"Agent PH telemetry: {JsonConvert.SerializeObject(telemetry.ToDictionary(), Formatting.None)}");

var enableTelemetry = AgentKnobs.ProcessHandlerTelemetry.GetValue(ExecutionContext).AsBoolean();
ExecutionContext.Debug($"Enable telemetry: '{enableTelemetry}'");
if (enableTelemetry)
{
PublishTelemetry(telemetry.ToDictionary(), "ProcessHandler");
}

System.Environment.SetEnvironmentVariable(inputArgsEnvVarName, processedArgs);
System.Environment.SetEnvironmentVariable(inputArgsEnvVarName, arguments);

var agentTemp = ExecutionContext.GetVariableValueOrDefault(Constants.Variables.Agent.TempDirectory);
_generatedScriptPath = Path.Combine(agentTemp, $"processHandlerScript_{scriptId}.cmd");
Expand Down
12 changes: 12 additions & 0 deletions src/Agent.Worker/TaskManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,18 @@ public string WorkingDirectory
SetInput(nameof(WorkingDirectory), value);
}
}

public string DisableInlineExecution
{
get
{
return GetInput(nameof(DisableInlineExecution));
}
set
{
SetInput(nameof(DisableInlineExecution), value);
}
}
}

public sealed class AgentPluginHandlerData : HandlerData
Expand Down

0 comments on commit b5ff440

Please sign in to comment.