|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Text.RegularExpressions; |
| 4 | +using GitHub.DistributedTask.Logging; |
| 5 | +using GitHub.DistributedTask.ObjectTemplating.Tokens; |
| 6 | +using GitHub.DistributedTask.Pipelines.ContextData; |
| 7 | + |
| 8 | +namespace Runner.Server.Services |
| 9 | +{ |
| 10 | + public class ReusableWorkflowSecretsProvider : ISecretsProvider |
| 11 | + { |
| 12 | + private ISecretsProvider parent; |
| 13 | + private TemplateToken secretsMapping; |
| 14 | + private DictionaryContextData contextData; |
| 15 | + private string jobName; |
| 16 | + private WorkflowContext workflowContext; |
| 17 | + private List<TemplateToken> environment; |
| 18 | + |
| 19 | + public ReusableWorkflowSecretsProvider(string jobName, ISecretsProvider parent, TemplateToken secretsMapping, DictionaryContextData contextData, WorkflowContext workflowContext, List<TemplateToken> environment){ |
| 20 | + this.parent = parent; |
| 21 | + this.secretsMapping = secretsMapping; |
| 22 | + this.contextData = contextData; |
| 23 | + this.jobName = jobName; |
| 24 | + this.workflowContext = workflowContext; |
| 25 | + this.environment = environment; |
| 26 | + } |
| 27 | + public IDictionary<string, string> GetSecretsForEnvironment(GitHub.DistributedTask.ObjectTemplating.ITraceWriter traceWriter, string name = null) |
| 28 | + { |
| 29 | + var parentSecrets = parent.GetSecretsForEnvironment(traceWriter, name); |
| 30 | + traceWriter.Info("{0}", $"Evaluating Secrets of {jobName} for environment '{name?.Replace("'","''")}'"); |
| 31 | + SecretMasker masker = new SecretMasker(); |
| 32 | + var linesplitter = new Regex("\r?\n"); |
| 33 | + foreach(var variable in parentSecrets) { |
| 34 | + if(!string.IsNullOrEmpty(variable.Value)) { |
| 35 | + masker.AddValue(variable.Value); |
| 36 | + if(variable.Value.Contains('\r') || variable.Value.Contains('\n')) { |
| 37 | + foreach(var line in linesplitter.Split(variable.Value)) { |
| 38 | + masker.AddValue(line); |
| 39 | + } |
| 40 | + } |
| 41 | + } |
| 42 | + } |
| 43 | + masker.AddValueEncoder(ValueEncoders.Base64StringEscape); |
| 44 | + masker.AddValueEncoder(ValueEncoders.Base64StringEscapeShift1); |
| 45 | + masker.AddValueEncoder(ValueEncoders.Base64StringEscapeShift2); |
| 46 | + masker.AddValueEncoder(ValueEncoders.CommandLineArgumentEscape); |
| 47 | + masker.AddValueEncoder(ValueEncoders.ExpressionStringEscape); |
| 48 | + masker.AddValueEncoder(ValueEncoders.JsonStringEscape); |
| 49 | + masker.AddValueEncoder(ValueEncoders.UriDataEscape); |
| 50 | + masker.AddValueEncoder(ValueEncoders.XmlDataEscape); |
| 51 | + masker.AddValueEncoder(ValueEncoders.TrimDoubleQuotes); |
| 52 | + masker.AddValueEncoder(ValueEncoders.PowerShellPreAmpersandEscape); |
| 53 | + masker.AddValueEncoder(ValueEncoders.PowerShellPostAmpersandEscape); |
| 54 | + var secureTraceWriter = new TraceWriter2(line => { |
| 55 | + traceWriter.Info("{0}", masker.MaskSecrets(line)); |
| 56 | + }); |
| 57 | + var result = new DictionaryContextData(); |
| 58 | + foreach (var variable in parentSecrets) { |
| 59 | + result[variable.Key] = new StringContextData(variable.Value); |
| 60 | + } |
| 61 | + var jobEnvCtx = new DictionaryContextData(); |
| 62 | + foreach(var envBlock in environment) { |
| 63 | + var envTemplateContext = SecretHelper.CreateTemplateContext(secureTraceWriter, workflowContext, contextData); |
| 64 | + envTemplateContext.ExpressionValues["env"] = jobEnvCtx; |
| 65 | + envTemplateContext.ExpressionValues["secrets"] = result; |
| 66 | + var cEnv = GitHub.DistributedTask.ObjectTemplating.TemplateEvaluator.Evaluate(envTemplateContext, "job-env", envBlock, 0, null, true); |
| 67 | + // Best effort, don't check for errors |
| 68 | + // templateContext.Errors.Check(); |
| 69 | + // Best effort, make global env available this is not available on github actions |
| 70 | + if(cEnv is MappingToken genvToken) { |
| 71 | + foreach(var kv in genvToken) { |
| 72 | + if(kv.Key is StringToken key && kv.Value is StringToken val) { |
| 73 | + jobEnvCtx[key.Value] = new StringContextData(val.Value); |
| 74 | + } |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + var templateContext = SecretHelper.CreateTemplateContext(secureTraceWriter, workflowContext, contextData); |
| 79 | + templateContext.ExpressionValues["env"] = jobEnvCtx; |
| 80 | + templateContext.ExpressionValues["secrets"] = result; |
| 81 | + var evalSec = secretsMapping != null ? GitHub.DistributedTask.ObjectTemplating.TemplateEvaluator.Evaluate(templateContext, templateContext.Schema.Definitions.ContainsKey("job-secrets") ? "job-secrets" : "workflow-job-secrets-mapping", secretsMapping, 0, null, true)?.AssertMapping($"jobs.{name}.secrets") : null; |
| 82 | + templateContext.Errors.Check(); |
| 83 | + IDictionary<string, string> ret = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); |
| 84 | + if(evalSec != null) { |
| 85 | + foreach(var entry in evalSec) { |
| 86 | + ret[entry.Key.AssertString($"jobs.{jobName}.secrets mapping key").Value] = entry.Value.AssertString($"jobs.{jobName}.secrets mapping value").Value; |
| 87 | + } |
| 88 | + } |
| 89 | + return SecretHelper.WithReservedSecrets(ret, parentSecrets); |
| 90 | + } |
| 91 | + |
| 92 | + public IDictionary<string, string> GetReservedSecrets() |
| 93 | + { |
| 94 | + return parent.GetReservedSecrets(); |
| 95 | + } |
| 96 | + |
| 97 | + public IDictionary<string, string> GetVariablesForEnvironment(string name = null) |
| 98 | + { |
| 99 | + return parent.GetVariablesForEnvironment(name); |
| 100 | + } |
| 101 | + } |
| 102 | +} |
0 commit comments