diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Docs/Security.md b/Lombiq.HelpfulLibraries.AspNetCore/Docs/Security.md
index 6bf831d8..f48e316b 100644
--- a/Lombiq.HelpfulLibraries.AspNetCore/Docs/Security.md
+++ b/Lombiq.HelpfulLibraries.AspNetCore/Docs/Security.md
@@ -8,6 +8,7 @@
- `EmbeddedMediaContentSecurityPolicyProvider`: An optional policy provider that permits additional host names used by usual media embedding sources (like YouTube) for the `frame-scr` directive.
- `IContentSecurityPolicyProvider`: Interface for services that update the dictionary that will be turned into the `Content-Security-Policy` header value.
- `ServiceCollectionExtensions`: Extensions methods for `IServiceCollection`, e.g. `AddContentSecurityPolicyProvider()` is a shortcut to register `IContentSecurityPolicyProvider` in dependency injection.
+- `XWidgetsContentSecurityPolicyProvider`: An optional content security policy provider that provides configuration to allow the usage of X (Twitter) social widgets.
There is a similar section for security extensions related to Orchard Core [here](../../Lombiq.HelpfulLibraries.OrchardCore/Docs/Security.md).
diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Security/CdnContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.AspNetCore/Security/CdnContentSecurityPolicyProvider.cs
index 543f7ddc..bd9b7a8a 100644
--- a/Lombiq.HelpfulLibraries.AspNetCore/Security/CdnContentSecurityPolicyProvider.cs
+++ b/Lombiq.HelpfulLibraries.AspNetCore/Security/CdnContentSecurityPolicyProvider.cs
@@ -17,43 +17,53 @@ public class CdnContentSecurityPolicyProvider : IContentSecurityPolicyProvider
///
/// Gets the sources that will be added to the directive.
///
- public static ConcurrentBag PermittedStyleSources { get; } = new(
+ public static ConcurrentBag PermittedStyleSources { get; } =
[
- "fonts.googleapis.com",
- "fonts.gstatic.com", // #spell-check-ignore-line
"cdn.jsdelivr.net", // #spell-check-ignore-line
- "fastly.jsdelivr.net", // #spell-check-ignore-line
"cdnjs.cloudflare.com", // #spell-check-ignore-line
+ "fastly.jsdelivr.net", // #spell-check-ignore-line
+ "fonts.cdnfonts.com", // #spell-check-ignore-line
+ "fonts.googleapis.com",
+ "fonts.gstatic.com", // #spell-check-ignore-line
"maxcdn.bootstrapcdn.com", // #spell-check-ignore-line
- ]);
+ "unpkg.com", // #spell-check-ignore-line
+ ];
///
/// Gets the sources that will be added to the directive.
///
- public static ConcurrentBag PermittedScriptSources { get; } = new(
+ public static ConcurrentBag PermittedScriptSources { get; } =
[
"cdn.jsdelivr.net", // #spell-check-ignore-line
"cdnjs.cloudflare.com", // #spell-check-ignore-line
"code.jquery.com",
"fastly.jsdelivr.net", // #spell-check-ignore-line
"maxcdn.bootstrapcdn.com", // #spell-check-ignore-line
- ]);
+ "unpkg.com", // #spell-check-ignore-line
+ ];
///
/// Gets the sources that will be added to the directive.
///
- public static ConcurrentBag PermittedFontSources { get; } = new(
+ public static ConcurrentBag PermittedFontSources { get; } =
[
"cdn.jsdelivr.net", // #spell-check-ignore-line
+ "cdnjs.cloudflare.com", // #spell-check-ignore-line
+ "fonts.cdnfonts.com", // #spell-check-ignore-line
"fonts.googleapis.com",
"fonts.gstatic.com", // #spell-check-ignore-line
- ]);
+ ];
///
/// Gets the sources that will be added to the directive.
///
public static ConcurrentBag PermittedFrameSources { get; } = [];
+ ///
+ /// Gets the sources that will be added to the directive.
+ ///
+ public static ConcurrentBag PermittedImgSources { get; } = [];
+
public ValueTask UpdateAsync(IDictionary securityPolicies, HttpContext context)
{
var any = false;
@@ -82,6 +92,12 @@ public ValueTask UpdateAsync(IDictionary securityPolicies, HttpC
CspHelper.MergeValues(securityPolicies, FrameSrc, PermittedFrameSources);
}
+ if (!PermittedImgSources.IsEmpty)
+ {
+ any = true;
+ CspHelper.MergeValues(securityPolicies, ImgSrc, PermittedImgSources);
+ }
+
if (any)
{
var allPermittedSources = PermittedStyleSources.Concat(PermittedScriptSources).Concat(PermittedFontSources);
diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Security/ContentSecurityPolicyDirectives.cs b/Lombiq.HelpfulLibraries.AspNetCore/Security/ContentSecurityPolicyDirectives.cs
index 81c0c321..8ae1cd92 100644
--- a/Lombiq.HelpfulLibraries.AspNetCore/Security/ContentSecurityPolicyDirectives.cs
+++ b/Lombiq.HelpfulLibraries.AspNetCore/Security/ContentSecurityPolicyDirectives.cs
@@ -1,8 +1,9 @@
-namespace Lombiq.HelpfulLibraries.AspNetCore.Security;
+namespace Lombiq.HelpfulLibraries.AspNetCore.Security;
///
-/// The Content-Security-Policy directives defined in the W3C
-/// Recommendation.
+/// The Content-Security-Policy directives defined in the W3C
+/// Recommendation (also see the MDN page).
///
public static class ContentSecurityPolicyDirectives
{
@@ -15,13 +16,20 @@ public static class ContentSecurityPolicyDirectives
public const string FrameAncestors = "frame-ancestors";
public const string FrameSrc = "frame-src";
public const string ImgSrc = "img-src";
+ public const string ManifestSrc = "manifest-src";
public const string MediaSrc = "media-src";
public const string ObjectSrc = "object-src";
public const string PluginTypes = "plugin-types";
+ public const string ReportTo = "report-to";
public const string ReportUri = "report-uri";
public const string Sandbox = "sandbox";
public const string ScriptSrc = "script-src";
+ public const string ScriptSrcAttr = "script-src-attr";
+ public const string ScriptSrcElem = "script-src-elem";
public const string StyleSrc = "style-src";
+ public const string StyleSrcAttr = "style-src-attr";
+ public const string UpgradeInsecureRequests = "upgrade-insecure-requests";
+ public const string StyleSrcElem = "style-src-elem";
public const string WorkerSrc = "worker-src";
public static class CommonValues
diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Security/XWidgetsContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.AspNetCore/Security/XWidgetsContentSecurityPolicyProvider.cs
new file mode 100644
index 00000000..02a12cf9
--- /dev/null
+++ b/Lombiq.HelpfulLibraries.AspNetCore/Security/XWidgetsContentSecurityPolicyProvider.cs
@@ -0,0 +1,25 @@
+using Microsoft.AspNetCore.Http;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using static Lombiq.HelpfulLibraries.AspNetCore.Security.ContentSecurityPolicyDirectives;
+
+namespace Lombiq.HelpfulLibraries.AspNetCore.Security;
+
+///
+/// An optional content security policy provider that provides configuration to allow the usage of X (Twitter) social
+/// widgets.
+///
+public class XWidgetsContentSecurityPolicyProvider : IContentSecurityPolicyProvider
+{
+ private const string PlatformDotTwitter = "platform.twitter.com";
+
+ public ValueTask UpdateAsync(IDictionary securityPolicies, HttpContext context)
+ {
+ CspHelper.MergeValues(securityPolicies, FrameSrc, PlatformDotTwitter);
+ CspHelper.MergeValues(securityPolicies, ImgSrc, PlatformDotTwitter, "syndication.twitter.com");
+ CspHelper.MergeValues(securityPolicies, StyleSrc, PlatformDotTwitter);
+ CspHelper.MergeValues(securityPolicies, ScriptSrc, PlatformDotTwitter);
+
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Docs/Security.md b/Lombiq.HelpfulLibraries.OrchardCore/Docs/Security.md
index 56e3e66d..62d20897 100644
--- a/Lombiq.HelpfulLibraries.OrchardCore/Docs/Security.md
+++ b/Lombiq.HelpfulLibraries.OrchardCore/Docs/Security.md
@@ -1,13 +1,13 @@
# Lombiq Helpful Libraries - Orchard Core Libraries - Security
-## Extensions
-
-- `SecurityOrchardCoreBuilderExtensions`: Adds `BuilderExtensions` extensions. For example, the `ConfigureSecurityDefaultsWithStaticFiles()` that provides some default security configuration for Orchard Core.
-
There is a similar section for security extensions related to ASP.NET Core [here](../../Lombiq.HelpfulLibraries.AspNetCore/Docs/Security.md). All of the services mentioned in both documents are included in the `ConfigureSecurityDefaults()` and `ConfigureSecurityDefaultsWithStaticFiles()` extensions.
These extensions provide additional security and can resolve issues reported by the [ZAP security scanner](https://github.com/Lombiq/UI-Testing-Toolbox/blob/dev/Lombiq.Tests.UI/Docs/SecurityScanning.md).
+## Extensions
+
+- `SecurityOrchardCoreBuilderExtensions`: Adds `BuilderExtensions` extensions. For example, the `ConfigureSecurityDefaultsWithStaticFiles()` that provides some default security configuration for Orchard Core.
+
## Attributes
- `ContentSecurityPolicyAttribute`: You can add the `[ContentSecurityPolicy(value, name)]` attribute to any MVC action's method. This way you can grant per-action content security policy permissions, right there in the controller. These attributes are handled by the `ContentSecurityPolicyAttributeContentSecurityPolicyProvider`.
@@ -19,3 +19,5 @@ These extensions provide additional security and can resolve issues reported by
- `ReCaptchaContentSecurityPolicyProvider`: Provides various directives for the `Content-Security-Policy` header, allowing using ReCaptcha captchas. Is automatically enabled when the `OrchardCore.ReCaptcha` feature is enabled.
- `ResourceManagerContentSecurityPolicyProvider`: An abstract base class for implementing content security policy providers that trigger when the specified resource is included.
- `VueContentSecurityPolicyProvider`: An implementation of `ResourceManagerContentSecurityPolicyProvider` that adds `script-src: unsafe-eval` permission to the page if it uses the `vuejs` resource. This includes any Vue.js app in stock Orchard Core, apps you create in your view files, and SFCs created with the Lombiq.VueJs module. This is necessary, because without `unsafe-eval` Vue.js only supports templates that are pre-compiled into JS code.
+
+You can configure optional or custom content security policy providers by implementing the `IContentSecurityPolicyProvider` interface and registering them in the DI container with `AddContentSecurityPolicyProvider()`, e.g. `services.AddContentSecurityPolicyProvider();` in a `Startup` class. You can also register providers for the whole app (i.e. all tenants) from the root `Program` class via `OrchardCoreBuilder.ApplicationServices`.
diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Security/GoogleAnalyticsContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.OrchardCore/Security/GoogleAnalyticsContentSecurityPolicyProvider.cs
index ed4e96b7..c29ca7b3 100644
--- a/Lombiq.HelpfulLibraries.OrchardCore/Security/GoogleAnalyticsContentSecurityPolicyProvider.cs
+++ b/Lombiq.HelpfulLibraries.OrchardCore/Security/GoogleAnalyticsContentSecurityPolicyProvider.cs
@@ -13,9 +13,11 @@ public class GoogleAnalyticsContentSecurityPolicyProvider : IContentSecurityPoli
{
private const string HttpContextItemKey = nameof(GoogleAnalyticsContentSecurityPolicyProvider);
+ public static bool AlwaysEnabled { get; set; }
+
public async ValueTask UpdateAsync(IDictionary securityPolicies, HttpContext context)
{
- var googleAnalyticsIsEnabled = context.Items.ContainsKey(HttpContextItemKey);
+ var googleAnalyticsIsEnabled = AlwaysEnabled || context.Items.ContainsKey(HttpContextItemKey);
if (!googleAnalyticsIsEnabled)
{
diff --git a/Lombiq.HelpfulLibraries.Tests/Lombiq.HelpfulLibraries.Tests.csproj b/Lombiq.HelpfulLibraries.Tests/Lombiq.HelpfulLibraries.Tests.csproj
index a13da007..834c188c 100644
--- a/Lombiq.HelpfulLibraries.Tests/Lombiq.HelpfulLibraries.Tests.csproj
+++ b/Lombiq.HelpfulLibraries.Tests/Lombiq.HelpfulLibraries.Tests.csproj
@@ -17,7 +17,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -31,7 +31,7 @@
-
+