Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new agent capability AllowWorkDirectoryRepositories so repositories can be checked out below the work directory level too on self hosted agents #3475

3 changes: 3 additions & 0 deletions src/Agent.Listener/CommandLine/ConfigureAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ public class ConfigureAgent : ConfigureOrRemoveBase
[Option(Constants.Agent.CommandLine.Flags.AddMachineGroupTags)]
public bool AddMachineGroupTags { get; set; }

[Option(Constants.Agent.CommandLine.Flags.AllowWorkDirectoryRepositories)]
public bool AllowWorkDirectoryRepositories { get; set; }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really necessary to have this setting? Since to my understanding the (public) hosted agents will create a "fresh" agent each time, so there is no risk of having anyone else's code on you agent. If the hosted agents might still have code from another organisation on it this would be a very serious security issue.


[Option(Constants.Agent.CommandLine.Flags.AlwaysExtractTask)]
public bool AlwaysExtractTask { get; set; }

Expand Down
5 changes: 5 additions & 0 deletions src/Agent.Listener/CommandSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,11 @@ public bool GetDisableLogUploads()
return TestFlag(Configure?.DisableLogUploads, Constants.Agent.CommandLine.Flags.DisableLogUploads);
}

public bool GetAllowWorkDirectoryRepositories()
{
return TestFlag(Configure?.AllowWorkDirectoryRepositories, Constants.Agent.CommandLine.Flags.AllowWorkDirectoryRepositories);
}

public bool Unattended()
{
if (TestFlag(GetConfigureOrRemoveBase()?.Unattended, Constants.Agent.CommandLine.Flags.Unattended))
Expand Down
2 changes: 2 additions & 0 deletions src/Agent.Listener/Configuration/ConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ public async Task ConfigureAsync(CommandSettings command)

agentSettings.DisableLogUploads = command.GetDisableLogUploads();

agentSettings.AllowWorkDirectoryRepositories = command.GetAllowWorkDirectoryRepositories();

agentSettings.AlwaysExtractTask = command.GetAlwaysExtractTask();

_store.SaveSettings(agentSettings);
Expand Down
19 changes: 17 additions & 2 deletions src/Agent.Plugins/RepositoryPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ protected bool HasMultipleCheckouts(AgentTaskPluginExecutionContext executionCon
return executionContext != null && RepositoryUtil.HasMultipleCheckouts(executionContext.JobSettings);
}

protected bool AllowWorkDirectoryRepositories(AgentTaskPluginExecutionContext executionContext)
{
return executionContext != null && RepositoryUtil.AllowWorkDirectoryRepositories(executionContext.AgentSettings);
}

protected TeeUtil teeUtil;
protected void initializeTeeUtil(AgentTaskPluginExecutionContext executionContext, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -140,24 +145,34 @@ public override async Task RunAsync(AgentTaskPluginExecutionContext executionCon
MergeCheckoutOptions(executionContext, repo);

var currentRepoPath = repo.Properties.Get<string>(Pipelines.RepositoryPropertyNames.Path);
var workDirectory = executionContext.Variables.GetValueOrDefault("agent.workfolder")?.Value;
var buildDirectory = executionContext.Variables.GetValueOrDefault("agent.builddirectory")?.Value;
var tempDirectory = executionContext.Variables.GetValueOrDefault("agent.tempdirectory")?.Value;

ArgUtil.NotNullOrEmpty(currentRepoPath, nameof(currentRepoPath));
ArgUtil.NotNullOrEmpty(workDirectory, nameof(workDirectory));
ArgUtil.NotNullOrEmpty(buildDirectory, nameof(buildDirectory));
ArgUtil.NotNullOrEmpty(tempDirectory, nameof(tempDirectory));

// Determine the path that we should clone/move the repository into
const string sourcesDirectory = "s"; //Constants.Build.Path.SourcesDirectory
string expectRepoPath;
var path = executionContext.GetInput("path");
var maxRootDirectory = buildDirectory;

if (!string.IsNullOrEmpty(path))
{
// When the checkout task provides a path, always use that one
expectRepoPath = IOUtil.ResolvePath(buildDirectory, path);
if (!expectRepoPath.StartsWith(buildDirectory.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar))

if (AllowWorkDirectoryRepositories(executionContext))
{
maxRootDirectory = workDirectory;
}

if (!expectRepoPath.StartsWith(maxRootDirectory.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar))
{
throw new ArgumentException($"Input path '{path}' should resolve to a directory under '{buildDirectory}', current resolved path '{expectRepoPath}'.");
throw new ArgumentException($"Input path '{path}' should resolve to a directory under '{maxRootDirectory}', current resolved path '{expectRepoPath}'.");
}
}
else if (HasMultipleCheckouts(executionContext))
Expand Down
6 changes: 6 additions & 0 deletions src/Agent.Sdk/TaskPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public class WellKnownJobSettings
public static readonly string WorkspaceIdentifier = "WorkspaceIdentifier";
}

public class WellKnownAgentSettings
{
public static readonly string AllowWorkDirectoryRepositories = "AllowWorkDirectoryRepositories";
}

public class AgentTaskPluginExecutionContext : ITraceWriter, IKnobValueContext
{
private VssConnection _connection;
Expand Down Expand Up @@ -62,6 +67,7 @@ public AgentTaskPluginExecutionContext(ITraceWriter trace)
public Dictionary<string, string> Inputs { get; set; }
public ContainerInfo Container { get; set; }
public Dictionary<string, string> JobSettings { get; set; }
public Dictionary<string, string> AgentSettings { get; set; }
public AgentWebProxySettings WebProxySettings { get; private set; }

[JsonIgnore]
Expand Down
13 changes: 13 additions & 0 deletions src/Agent.Sdk/Util/RepositoryUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ public static bool HasMultipleCheckouts(Dictionary<string, string> jobSettings)
return false;
}

/// <summary>
/// Returns true if the dictionary contains the 'AllowWorkDirectoryRepository' key and the value is set to 'true'.
/// </summary>
public static bool AllowWorkDirectoryRepositories(Dictionary<string, string> agentSettings)
{
if (agentSettings != null && agentSettings.TryGetValue(WellKnownAgentSettings.AllowWorkDirectoryRepositories, out string allowWorkDirectoryRepositoriesText))
{
return bool.TryParse(allowWorkDirectoryRepositoriesText, out bool allowWorkDirectoryRepositories) && allowWorkDirectoryRepositories;
}

return false;
}

/// <summary>
/// Returns true if the string matches the primary repository name (self)
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/Agent.Worker/AgentPluginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public AgentTaskPluginExecutionContext GeneratePluginExecutionContext(IExecution
Endpoints = context.Endpoints,
Container = containerInfo, //TODO: Figure out if this needs to have all the containers or just the one for the current step
JobSettings = context.JobSettings,
AgentSettings = context.AgentSettings,
};

// variables
Expand Down
25 changes: 22 additions & 3 deletions src/Agent.Worker/Build/BuildDirectoryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,17 +190,36 @@ public string GetRelativeRepositoryPath(
string buildDirectory,
string repositoryPath)
{
var maxRootDirectory = buildDirectory;
var workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);
var allowWorkDirectoryRepositories = HostContext.GetService<IConfigurationStore>().GetSettings().AllowWorkDirectoryRepositories;

ArgUtil.NotNullOrEmpty(buildDirectory, nameof(buildDirectory));
ArgUtil.NotNullOrEmpty(repositoryPath, nameof(repositoryPath));

if (repositoryPath.StartsWith(buildDirectory + Path.DirectorySeparatorChar) || repositoryPath.StartsWith(buildDirectory + Path.AltDirectorySeparatorChar))
// resolve any potentially left over relative part of the path
repositoryPath = Path.GetFullPath(repositoryPath);

if (allowWorkDirectoryRepositories)
{
maxRootDirectory = workDirectory;
}

if (repositoryPath.StartsWith(maxRootDirectory + Path.DirectorySeparatorChar) || repositoryPath.StartsWith(maxRootDirectory + Path.AltDirectorySeparatorChar))
{
// The sourcesDirectory in tracking file is a relative path to agent's work folder.
return repositoryPath.Substring(HostContext.GetDirectory(WellKnownDirectory.Work).Length + 1).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
return repositoryPath.Substring(workDirectory.Length + 1).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
}
else
{
throw new ArgumentException($"Repository path '{repositoryPath}' should be located under agent's work directory '{buildDirectory}'.");
if (allowWorkDirectoryRepositories)
{
throw new ArgumentException($"Repository path '{repositoryPath}' should be located under agent's work directory '{workDirectory}'.");
}
else
{
throw new ArgumentException($"Repository path '{repositoryPath}' should be located under agent's build directory '{buildDirectory}'.");
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/Agent.Worker/ExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public interface IExecutionContext : IAgentService, IKnobValueContext
List<ServiceEndpoint> Endpoints { get; }
List<SecureFile> SecureFiles { get; }
List<Pipelines.RepositoryResource> Repositories { get; }
Dictionary<string, string> AgentSettings { get; }
Dictionary<string, string> JobSettings { get; }

PlanFeatures Features { get; }
Expand Down Expand Up @@ -130,6 +131,7 @@ public sealed class ExecutionContext : AgentService, IExecutionContext, IDisposa
public List<SecureFile> SecureFiles { get; private set; }
public List<Pipelines.RepositoryResource> Repositories { get; private set; }
public Dictionary<string, string> JobSettings { get; private set; }
public Dictionary<string, string> AgentSettings { get; private set; }
public Variables Variables { get; private set; }
public Variables TaskVariables { get; private set; }
public HashSet<string> OutputVariables => _outputvariables;
Expand Down Expand Up @@ -183,6 +185,10 @@ public override void Initialize(IHostContext hostContext)
}

_jobServerQueue = HostContext.GetService<IJobServerQueue>();

// Agent Settings
AgentSettings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
AgentSettings[WellKnownAgentSettings.AllowWorkDirectoryRepositories] = HostContext.GetService<IConfigurationStore>().GetSettings().AllowWorkDirectoryRepositories ? Boolean.TrueString : Boolean.FalseString;
}

public void CancelToken()
Expand Down Expand Up @@ -226,6 +232,7 @@ public IExecutionContext CreateChild(Guid recordId, string displayName, string r
child.Endpoints = Endpoints;
child.Repositories = Repositories;
child.JobSettings = JobSettings;
child.AgentSettings = AgentSettings;
child.SecureFiles = SecureFiles;
child.TaskVariables = taskVariables;
child._cancellationTokenSource = new CancellationTokenSource();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public Task<List<Capability>> GetCapabilitiesAsync(AgentSettings settings, Cance
Add(capabilities, "Agent.Version", BuildConstants.AgentPackage.Version);
Add(capabilities, "Agent.ComputerName", Environment.MachineName ?? string.Empty);
Add(capabilities, "Agent.HomeDirectory", HostContext.GetDirectory(WellKnownDirectory.Root));
Add(capabilities, "AllowWorkDirectoryRepositories", settings.AllowWorkDirectoryRepositories.ToString());
return Task.FromResult(capabilities);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ public string Fingerprint

[DataMember(EmitDefaultValue = false)]
public int MaxDedupParallelism { get; set; }

[DataMember(EmitDefaultValue = false)]
public bool AllowWorkDirectoryRepositories { get; set; }
}

[DataContract]
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.VisualStudio.Services.Agent/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ public static class Flags
public const string AddDeploymentGroupTags = "adddeploymentgrouptags";
public const string AddMachineGroupTags = "addmachinegrouptags";
public const string AddEnvironmentVirtualMachineResourceTags = "addvirtualmachineresourcetags";
public const string AllowWorkDirectoryRepositories = "allowworkdirectoryrepositories";
public const string AlwaysExtractTask = "alwaysextracttask";
public const string Commit = "commit";
public const string DeploymentGroup = "deploymentgroup";
Expand Down
Loading