Skip to content

Commit

Permalink
Add processing of target inputs in decorator task (#3550)
Browse files Browse the repository at this point in the history
* Add check of runtime secrets in decorator task input

* Remove extra changes

* add separate method for checking

* Better messaging

* Move logic for inputs validation to the separate method

* Resolved comments

* Change name

* Revert changes in dev.sh

* Resolve comments

* Removed extra code

* Removed extra changes part 2

* Fix string.json

* Resolve comments

* Remove extra quote
  • Loading branch information
EzzhevNikita committed Oct 14, 2021
1 parent 9839e29 commit 2b45edd
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 0 deletions.
94 changes: 94 additions & 0 deletions src/Agent.Worker/TaskDecoratorManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;


namespace Microsoft.VisualStudio.Services.Agent.Worker
{
[ServiceLocator(Default = typeof(TaskDecoratorManager))]
public interface ITaskDecoratorManager : IAgentService
{
bool IsInjectedTaskForTarget(string taskName);
bool IsInjectedInputsContainsSecrets(Dictionary<string, string> inputs, out List<string> inputsWithSecrets);
string GenerateTaskResultMessage(List<string> inputsWithSecrets);
}

public sealed class TaskDecoratorManager : AgentService, ITaskDecoratorManager
{
/// <summary>
/// Checks if current task is injected by decorator with posttargettask or pretargettask target
/// </summary>
/// <param name="taskName">Name of the task to check</param>
/// <returns>Returns `true` if task is injected by decorator for target task, otherwise `false`</returns>
public bool IsInjectedTaskForTarget(string taskName)
{
return taskName.StartsWith(InjectedTasksNamesPrefixes.PostTargetTask)
|| taskName.StartsWith(InjectedTasksNamesPrefixes.PreTargetTask);
}

/// <summary>
/// Verifies that there are inputs with secrets, if secrets were found task will be marked as skipped and won't be executed
/// </summary>
/// <param name="inputs">Inputs presented as a dictionary with input name as key and input's value as the value of the corresponding key</param>
/// <param name="inputsWithSecrets">Out value that will contain the list of task inputs with secrets</param>
/// <returns> Return `true` if task contains injected inputs with secrets, otherwise `false`</returns>
public bool IsInjectedInputsContainsSecrets(Dictionary<string, string> inputs, out List<string> inputsWithSecrets)
{
inputsWithSecrets = this.GetInputsWithSecrets(inputs);

return inputsWithSecrets.Count > 0;
}

/// <summary>
/// Generates list of inputs that should be included into task result message
/// </summary>
/// <param name="inputsWithSecrets">List of inputs with secrets, that should be included in message</param>
public string GenerateTaskResultMessage(List<string> inputsWithSecrets)
{
string inputsForReport = string.Join(Environment.NewLine,
inputsWithSecrets.Select(input => string.Join("\n", input)));

return inputsForReport;
}

/// <summary>
/// Used to check if provided input value contain any secret
/// </summary>
/// <param name="inputValue">Value of input to check</param>
/// <returns>Returns `true` if provided string contain secret, otherwise `false`</returns>
private bool ContainsSecret(string inputValue)
{
string maskedString = HostContext.SecretMasker.MaskSecrets(inputValue);
return maskedString != inputValue;
}

/// <summary>
/// Used to get list of inputs in injected task that started with target_ prefix and contain secrets,
/// such inputs are autoinjected from target tasks
/// </summary>
/// <param name="inputs">Inputs presented as a dictionary with input name as key and input's value as the value of the corresponding key</param>
/// <returns>Returns list of inputs' names that contain secret values</returns>
private List<string> GetInputsWithSecrets(Dictionary<string, string> inputs)
{
var inputsWithSecrets = new List<string>();
foreach (var input in inputs)
{
if (input.Key.StartsWith("target_") && this.ContainsSecret(input.Value))
{
inputsWithSecrets.Add(input.Key);
}
}

return inputsWithSecrets;
}
}

internal static class InjectedTasksNamesPrefixes
{
public static readonly String PostTargetTask = "__system_posttargettask_";
public static readonly String PreTargetTask = "__system_pretargettask_";
}
}
16 changes: 16 additions & 0 deletions src/Agent.Worker/TaskRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,22 @@ public async Task RunAsync()
// Expand the inputs.
Trace.Verbose("Expanding inputs.");
runtimeVariables.ExpandValues(target: inputs);

// We need to verify inputs of the tasks that were injected by decorators, to check if they contain secrets,
// for security reasons execution of tasks in this case should be skipped.
// Target task inputs could be injected into the decorator's tasks if the decorator has post-task-tasks or pre-task-tasks targets,
// such tasks will have names that start with __system_pretargettask_ or __system_posttargettask_.
var taskDecoratorManager = HostContext.GetService<ITaskDecoratorManager>();
if (taskDecoratorManager.IsInjectedTaskForTarget(Task.Name) &&
taskDecoratorManager.IsInjectedInputsContainsSecrets(inputs, out var inputsWithSecrets))
{
var inputsForReport = taskDecoratorManager.GenerateTaskResultMessage(inputsWithSecrets);

ExecutionContext.Result = TaskResult.Skipped;
ExecutionContext.ResultCode = StringUtil.Loc("SecretsAreNotAllowedInInjectedTaskInputs", inputsForReport);
return;
}

VarUtil.ExpandEnvironmentVariables(HostContext, target: inputs);

// Translate the server file path inputs to local paths.
Expand Down
1 change: 1 addition & 0 deletions src/Misc/layoutbin/en-US/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@
"ScanToolCapabilities": "Scanning for tool capabilities.",
"ScreenSaverPoliciesInspection": "Checking for policies that may prevent screensaver from being disabled.",
"ScreenSaverPolicyWarning": "Screensaver policy is defined on the machine. This may lead to screensaver being enabled again. Active screensaver may impact UI operations, for e.g., automated UI tests may fail.",
"SecretsAreNotAllowedInInjectedTaskInputs": "Task is trying to access the following inputs of a target task which contain secrets:\n{0}\nIt is not allowed to pass inputs that contain secrets to the tasks injected by decorators.",
"SelfManageGitCreds": "You are in self manage git creds mode. Make sure your agent host machine can bypass any git authentication challenge.",
"ServerTarpit": "The job is currently being throttled by the server. You may experience delays in console line output, job status reporting, and task log uploads.",
"ServerTarpitUrl": "Link to resource utilization page (global 1-hour view): {0}.",
Expand Down

0 comments on commit 2b45edd

Please sign in to comment.