using System; using System.Collections.Generic; using System.Linq; using System.Text; using NBitcoin.Crypto; namespace BTCPayServer.Security { public class ConsentSecurityPolicy { public ConsentSecurityPolicy(string name, string value) { if (value.Contains(';', StringComparison.OrdinalIgnoreCase)) throw new FormatException(); _Value = value; _Name = name; } private readonly string _Name; public string Name { get { return _Name; } } private readonly string _Value; public string Value { get { return _Value; } } public override bool Equals(object obj) { ConsentSecurityPolicy item = obj as ConsentSecurityPolicy; if (item == null) return false; return GetHashCode().Equals(item.GetHashCode()); } public static bool operator ==(ConsentSecurityPolicy a, ConsentSecurityPolicy b) { if (System.Object.ReferenceEquals(a, b)) return true; if (((object)a == null) || ((object)b == null)) return false; return a.GetHashCode() == b.GetHashCode(); } public static bool operator !=(ConsentSecurityPolicy a, ConsentSecurityPolicy b) { return !(a == b); } public override int GetHashCode() { return HashCode.Combine(Name, Value); } } public class ContentSecurityPolicies { public ContentSecurityPolicies() { } readonly HashSet _Policies = new HashSet(); /// /// Allow a specific script as event handler /// /// public void AllowUnsafeHashes(string script = null) { if (!allowUnsafeHashes) { Add("script-src", $"'unsafe-hashes'"); allowUnsafeHashes = true; } if (script != null) { var sha = GetSha256(script); Add("script-src", $"'sha256-{sha}'"); } } bool allowUnsafeHashes = false; /// /// Allow the injection of script tag with the following script /// /// public void AllowInline(string script) { if (script is null) throw new ArgumentNullException(nameof(script)); var sha = GetSha256(script); Add("script-src", $"'sha256-{sha}'"); } static string GetSha256(string script) { return Convert.ToBase64String(Hashes.SHA256(Encoding.UTF8.GetBytes(script.Replace("\r\n", "\n", StringComparison.Ordinal)))); } public void Add(string name, string value) { Add(new ConsentSecurityPolicy(name, value)); } public void Add(ConsentSecurityPolicy policy) { _Policies.Add(policy); } public IEnumerable Rules => _Policies; public bool HasRules => _Policies.Count != 0; public override string ToString() { StringBuilder value = new StringBuilder(); bool firstGroup = true; foreach (var group in Rules.GroupBy(r => r.Name)) { if (!firstGroup) { value.Append(';'); } HashSet values = new HashSet(); List valuesList = new List(); values.Add(group.Key); valuesList.Add(group.Key); foreach (var v in group) { if (values.Add(v.Value)) valuesList.Add(v.Value); } value.Append(String.Join(" ", valuesList.OfType().ToArray())); firstGroup = false; } return value.ToString(); } } }