// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information. using System; using System.Linq; using NWebsec.Core.HttpHeaders.Configuration; using NWebsec.Core.HttpHeaders.Csp; namespace NWebsec.Middleware { public static class CspDirectiveExtensions { /// /// Sets the "none" source for the CSP directive. This source cannot be combined with other sources on a CSP directive. /// /// The type of the CSP directive configuration object. /// The CSP directive configuration object. /// Thrown when sources have already been configured for the directive. public static void None(this T directive) where T : class, ICspDirectiveBasicConfiguration { if (directive == null) throw new ArgumentNullException("directive"); ValidateBeforeSettingNoneSource(directive); directive.NoneSrc = true; } /// /// Sets the "self" source for the CSP directive. /// /// The type of the CSP directive configuration object. /// The CSP directive configuration object. /// The CSP directive configuration object. public static T Self(this T directive) where T : class, ICspDirectiveBasicConfiguration { if (directive == null) throw new ArgumentNullException("directive"); directive.SelfSrc = true; return directive; } /// /// Sets custom sources for the CSP directive. /// /// The type of the CSP directive configuration object. /// The CSP directive configuration object. /// One or more custom sources. /// The CSP directive configuration object. public static T CustomSources(this T directive, params string[] sources) where T : class, ICspDirectiveBasicConfiguration { if (directive == null) throw new ArgumentNullException("directive"); if (sources.Length == 0) throw new ArgumentException("You must supply at least one source.", "sources"); try { directive.CustomSources = sources.Select(s => CspUriSource.Parse(s).ToString()).ToArray(); } catch (InvalidCspSourceException e) { throw new ArgumentException("Invalid source. Details: " + e.Message, "sources", e); } return directive; } /// /// Sets the "unsafe-inline" source for the CSP directive. /// /// The type of the CSP directive configuration object. /// The CSP directive configuration object. /// The CSP directive configuration object. public static T UnsafeInline(this T directive) where T : class, ICspDirectiveUnsafeInlineConfiguration { if (directive == null) throw new ArgumentNullException("directive"); directive.UnsafeInlineSrc = true; return directive; } /// /// Sets the "unsafe-eval" source for the CSP directive. /// /// The type of the CSP directive configuration object. /// The CSP directive configuration object. /// The CSP directive configuration object. public static T UnsafeEval(this T directive) where T : class, ICspDirectiveConfiguration { if (directive == null) throw new ArgumentNullException("directive"); directive.UnsafeEvalSrc = true; return directive; } private static void ValidateBeforeSettingNoneSource(ICspDirectiveBasicConfiguration directive) { if (directive.SelfSrc || (directive.CustomSources != null && directive.CustomSources.Any())) { throw new InvalidOperationException("It is a logical error to combine the \"None\" source with other sources."); } var unsafeInline = directive as ICspDirectiveUnsafeInlineConfiguration; if (unsafeInline != null && unsafeInline.UnsafeInlineSrc) { throw new InvalidOperationException("It is a logical error to combine the \"None\" source with other sources."); } var unsafeEval = directive as ICspDirectiveConfiguration; if (unsafeEval != null && unsafeEval.UnsafeEvalSrc) { throw new InvalidOperationException("It is a logical error to combine the \"None\" source with other sources."); } } } }