├── .editorconfig
├── .gitignore
├── Joonasw.AspNetCore.SecurityHeaders.sln
├── LICENSE.txt
├── README.md
├── azure-pipelines.yml
├── src
└── Joonasw.AspNetCore.SecurityHeaders
│ ├── AppBuilderExtensions.cs
│ ├── Csp
│ ├── Builder
│ │ ├── CspBaseUriBuilder.cs
│ │ ├── CspBuilder.cs
│ │ ├── CspChildBuilder.cs
│ │ ├── CspConnectionBuilder.cs
│ │ ├── CspDefaultBuilder.cs
│ │ ├── CspFontsBuilder.cs
│ │ ├── CspFormActionBuilder.cs
│ │ ├── CspFrameAncestorsBuilder.cs
│ │ ├── CspFrameBuilder.cs
│ │ ├── CspImageBuilder.cs
│ │ ├── CspManifestBuilder.cs
│ │ ├── CspMediaBuilder.cs
│ │ ├── CspPluginBuilder.cs
│ │ ├── CspPrefetchBuilder.cs
│ │ ├── CspRequireSriBuilder.cs
│ │ ├── CspSandboxBuilder.cs
│ │ ├── CspScriptsBuilder.cs
│ │ ├── CspStylesBuilder.cs
│ │ └── CspWorkerBuilder.cs
│ ├── CspMiddleware.cs
│ ├── CspNonceService.cs
│ ├── CspSendingHeaderContext.cs
│ ├── ICspNonceService.cs
│ └── Options
│ │ ├── CspBaseUriOptions.cs
│ │ ├── CspChildSrcOptions.cs
│ │ ├── CspConnectSrcOptions.cs
│ │ ├── CspDefaultSrcOptions.cs
│ │ ├── CspFontSrcOptions.cs
│ │ ├── CspFormActionOptions.cs
│ │ ├── CspFrameAncestorsOptions.cs
│ │ ├── CspFrameSrcOptions.cs
│ │ ├── CspImgSrcOptions.cs
│ │ ├── CspManifestSrcOptions.cs
│ │ ├── CspMediaSrcOptions.cs
│ │ ├── CspObjectSrcOptions.cs
│ │ ├── CspPluginTypesOptions.cs
│ │ ├── CspPrefetchSrcOptions.cs
│ │ ├── CspRequireSriOptions.cs
│ │ ├── CspSandboxOptions.cs
│ │ ├── CspScriptSrcOptions.cs
│ │ ├── CspSrcOptionsBase.cs
│ │ ├── CspStyleSrcOptions.cs
│ │ └── CspWorkerSrcOptions.cs
│ ├── CspOptions.cs
│ ├── EnumExtensions.cs
│ ├── ExpectCT
│ ├── ExpectCTMiddleware.cs
│ └── ExpectCTOptions.cs
│ ├── FeaturePolicy
│ ├── Builder
│ │ ├── FeaturePolicyBuilder.cs
│ │ ├── FeaturePolicyFeatureBuilder.cs
│ │ ├── FeaturePolicyOtherFeatureBuilder.cs
│ │ └── IFeaturePolicyFeatureBuilder.cs
│ ├── FeaturePolicyMiddleware.cs
│ └── Options
│ │ ├── FeaturePolicyAccelerometerOptions.cs
│ │ ├── FeaturePolicyAmbientLightSensorOptions.cs
│ │ ├── FeaturePolicyAutoplayOptions.cs
│ │ ├── FeaturePolicyCameraOptions.cs
│ │ ├── FeaturePolicyEncryptedMediaOptions.cs
│ │ ├── FeaturePolicyFullscreenOptions.cs
│ │ ├── FeaturePolicyGeolocationOptions.cs
│ │ ├── FeaturePolicyGyroscopeOptions.cs
│ │ ├── FeaturePolicyMagnetometerOptions.cs
│ │ ├── FeaturePolicyMicrophoneOptions.cs
│ │ ├── FeaturePolicyMidiOptions.cs
│ │ ├── FeaturePolicyNotificationsOptions.cs
│ │ ├── FeaturePolicyOptionsBase.cs
│ │ ├── FeaturePolicyOtherFeatureOptions.cs
│ │ ├── FeaturePolicyPaymentOptions.cs
│ │ ├── FeaturePolicyPictureInPictureOptions.cs
│ │ ├── FeaturePolicyPushOptions.cs
│ │ ├── FeaturePolicySpeakerOptions.cs
│ │ ├── FeaturePolicySyncXhrOptions.cs
│ │ ├── FeaturePolicyUsbOptions.cs
│ │ ├── FeaturePolicyVibrateOptions.cs
│ │ └── FeaturePolicyVrOptions.cs
│ ├── FeaturePolicyOptions.cs
│ ├── Hpkp
│ ├── Builder
│ │ └── HpkpBuilder.cs
│ └── HpkpMiddleware.cs
│ ├── HpkpOptions.cs
│ ├── Hsts
│ ├── HstsMiddleware.cs
│ └── HstsOptionsExtensions.cs
│ ├── HstsOptions.cs
│ ├── Joonasw.AspNetCore.SecurityHeaders.csproj
│ ├── ReferrerPolicy
│ └── ReferrerPolicyMiddleware.cs
│ ├── ReferrerPolicyOptions.cs
│ ├── ServiceCollectionExtensions.cs
│ ├── TagHelpers
│ └── NonceTagHelper.cs
│ ├── XContentTypeOptions
│ └── XContentTypeOptionsMiddleware.cs
│ ├── XContentTypeOptionsOptions.cs
│ ├── XFrameOptions
│ ├── XFrameOptionsExtensions.cs
│ └── XFrameOptionsMiddleware.cs
│ ├── XFrameOptionsOptions.cs
│ ├── XXssProtection
│ └── XXssProtectionMiddleware.cs
│ └── XXssProtectionOptions.cs
└── test
├── Joonasw.AspNetCore.SecurityHeaders.Samples
├── .bowerrc
├── Controllers
│ ├── HomeController.cs
│ └── ReportController.cs
├── Joonasw.AspNetCore.SecurityHeaders.Samples.csproj
├── Middleware
│ ├── AppBuilderExtensions.cs
│ └── EnforceHttpsMiddleware.cs
├── Models
│ ├── CspViolationReport.cs
│ └── HpkpViolationReport.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Startup.cs
├── Views
│ ├── Home
│ │ ├── About.cshtml
│ │ ├── Contact.cshtml
│ │ └── Index.cshtml
│ ├── Shared
│ │ ├── ApplicationError.cshtml
│ │ ├── Error.cshtml
│ │ └── _Layout.cshtml
│ ├── _ViewImports.cshtml
│ └── _ViewStart.cshtml
├── appsettings.json
├── bower.json
├── bundleconfig.json
├── web.config
└── wwwroot
│ ├── _references.js
│ ├── css
│ ├── site.css
│ └── site.min.css
│ ├── favicon.ico
│ ├── images
│ ├── banner1.svg
│ ├── banner2.svg
│ ├── banner3.svg
│ └── banner4.svg
│ ├── js
│ ├── OtherScript.js
│ ├── site.js
│ └── site.min.js
│ └── lib
│ ├── bootstrap
│ ├── .bower.json
│ ├── LICENSE
│ └── dist
│ │ ├── css
│ │ ├── bootstrap-theme.css
│ │ ├── bootstrap-theme.css.map
│ │ ├── bootstrap-theme.min.css
│ │ ├── bootstrap-theme.min.css.map
│ │ ├── bootstrap.css
│ │ ├── bootstrap.css.map
│ │ ├── bootstrap.min.css
│ │ └── bootstrap.min.css.map
│ │ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ │ └── js
│ │ ├── bootstrap.js
│ │ ├── bootstrap.min.js
│ │ └── npm.js
│ ├── jquery-validation-unobtrusive
│ ├── .bower.json
│ ├── jquery.validate.unobtrusive.js
│ └── jquery.validate.unobtrusive.min.js
│ ├── jquery-validation
│ ├── .bower.json
│ ├── LICENSE.md
│ └── dist
│ │ ├── additional-methods.js
│ │ ├── additional-methods.min.js
│ │ ├── jquery.validate.js
│ │ └── jquery.validate.min.js
│ └── jquery
│ ├── .bower.json
│ ├── LICENSE.txt
│ └── dist
│ ├── jquery.js
│ ├── jquery.min.js
│ └── jquery.min.map
└── Joonasw.AspNetCore.SecurityHeaders.Tests
├── ContentTypeOptionsMiddlewareTests.cs
├── CspBuilderTests.cs
├── CspMiddlewareTests.cs
├── CspOptionsTests.cs
├── CspRequireSriBuilderTests.cs
├── CspSandboxBuilderTests.cs
├── CspScriptsBuilderTests.cs
├── CspStylesBuilderTests.cs
├── CspWorkerBuilderTests.cs
├── ExpectCtMiddlewareTests.cs
├── FeaturePolicyBuilderTests.cs
├── FeaturePolicyMiddlewareTests.cs
├── FrameOptionsMiddlewareTests.cs
├── HpkpMiddlewareTests.cs
├── HstsMiddlewareTests.cs
├── Joonasw.AspNetCore.SecurityHeaders.Tests.csproj
├── ReferrerPolicyMiddlewareTests.cs
├── XFrameOptionsTests.cs
├── XXssProtectionTests.cs
└── XssProtectionMiddlewareTests.cs
/Joonasw.AspNetCore.SecurityHeaders.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26621.2
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C93B021C-E01E-497C-97EB-61598F8F8E2A}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Joonasw.AspNetCore.SecurityHeaders.Samples", "test\Joonasw.AspNetCore.SecurityHeaders.Samples\Joonasw.AspNetCore.SecurityHeaders.Samples.csproj", "{8FF34C33-8DF3-41D6-9A4A-C461C201B389}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Joonasw.AspNetCore.SecurityHeaders", "src\Joonasw.AspNetCore.SecurityHeaders\Joonasw.AspNetCore.SecurityHeaders.csproj", "{E3185634-6192-4C1D-8BB1-65DE700901BB}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{568F7557-6509-4F00-BD44-3CB7804D87FC}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Joonasw.AspNetCore.SecurityHeaders.Tests", "test\Joonasw.AspNetCore.SecurityHeaders.Tests\Joonasw.AspNetCore.SecurityHeaders.Tests.csproj", "{B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}"
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20FDC4F1-21F4-4D9D-8E0A-A063491C5AB7}"
17 | ProjectSection(SolutionItems) = preProject
18 | .editorconfig = .editorconfig
19 | LICENSE.txt = LICENSE.txt
20 | EndProjectSection
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Debug|x64 = Debug|x64
26 | Debug|x86 = Debug|x86
27 | Release|Any CPU = Release|Any CPU
28 | Release|x64 = Release|x64
29 | Release|x86 = Release|x86
30 | EndGlobalSection
31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
32 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|x64.ActiveCfg = Debug|Any CPU
35 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|x64.Build.0 = Debug|Any CPU
36 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|x86.ActiveCfg = Debug|Any CPU
37 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|x86.Build.0 = Debug|Any CPU
38 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|x64.ActiveCfg = Release|Any CPU
41 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|x64.Build.0 = Release|Any CPU
42 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|x86.ActiveCfg = Release|Any CPU
43 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|x86.Build.0 = Release|Any CPU
44 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|x64.ActiveCfg = Debug|Any CPU
47 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|x64.Build.0 = Debug|Any CPU
48 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|x86.ActiveCfg = Debug|Any CPU
49 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|x86.Build.0 = Debug|Any CPU
50 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|x64.ActiveCfg = Release|Any CPU
53 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|x64.Build.0 = Release|Any CPU
54 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|x86.ActiveCfg = Release|Any CPU
55 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|x86.Build.0 = Release|Any CPU
56 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
58 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|x64.ActiveCfg = Debug|Any CPU
59 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|x64.Build.0 = Debug|Any CPU
60 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|x86.ActiveCfg = Debug|Any CPU
61 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|x86.Build.0 = Debug|Any CPU
62 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
63 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|Any CPU.Build.0 = Release|Any CPU
64 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|x64.ActiveCfg = Release|Any CPU
65 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|x64.Build.0 = Release|Any CPU
66 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|x86.ActiveCfg = Release|Any CPU
67 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|x86.Build.0 = Release|Any CPU
68 | EndGlobalSection
69 | GlobalSection(SolutionProperties) = preSolution
70 | HideSolutionNode = FALSE
71 | EndGlobalSection
72 | GlobalSection(NestedProjects) = preSolution
73 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389} = {568F7557-6509-4F00-BD44-3CB7804D87FC}
74 | {E3185634-6192-4C1D-8BB1-65DE700901BB} = {C93B021C-E01E-497C-97EB-61598F8F8E2A}
75 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F} = {568F7557-6509-4F00-BD44-3CB7804D87FC}
76 | EndGlobalSection
77 | GlobalSection(ExtensibilityGlobals) = postSolution
78 | SolutionGuid = {B16EDA54-6190-47A7-B948-8E6C55972B9A}
79 | EndGlobalSection
80 | EndGlobal
81 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Joonas Westlin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Add CSP, HSTS or HPKP headers to an ASP.NET Core app
2 |
3 | This library allows you to add Content Security Policy, Strict Transport Security and Public Key Pin headers via middleware.
4 |
5 | You can get the library from NuGet: [https://www.nuget.org/packages/Joonasw.AspNetCore.SecurityHeaders](https://www.nuget.org/packages/Joonasw.AspNetCore.SecurityHeaders)
6 |
7 | ## Example configuration
8 |
9 | ```cs
10 | // Enable Strict Transport Security with a 30-day caching period
11 | // Do not include subdomains
12 | // Do not allow preload
13 | app.UseStrictTransportSecurity(new HstsOptions(TimeSpan.FromDays(30), includeSubDomains: false, preload: false));
14 |
15 | // Use certificate pinning with:
16 | // - 30-day caching period
17 | // - One pin in SHA-256 form
18 | // - Report-Only = Invalid certificate should not be reported, but:
19 | // - Report problems to /hpkp-report
20 | app.UseHpkp(hpkp =>
21 | {
22 | hpkp.UseMaxAgeSeconds(30 * 24 * 60 * 60)
23 | .AddSha256Pin("nrmpk4ZI3wbRBmUZIT5aKAgP0LlKHRgfA2Snjzeg9iY=")
24 | .SetReportOnly()
25 | .ReportViolationsTo("/hpkp-report");
26 | });
27 |
28 | // Content Security Policy
29 | app.UseCsp(csp =>
30 | {
31 | // If nothing is mentioned for a resource class, allow from this domain
32 | csp.ByDefaultAllow
33 | .FromSelf();
34 |
35 | // Allow JavaScript from:
36 | csp.AllowScripts
37 | .FromSelf() //This domain
38 | .From("localhost:1591") //These two domains
39 | .From("ajax.aspnetcdn.com");
40 |
41 | // CSS allowed from:
42 | csp.AllowStyles
43 | .FromSelf()
44 | .From("ajax.aspnetcdn.com");
45 |
46 | csp.AllowImages
47 | .FromSelf();
48 |
49 | // HTML5 audio and video elemented sources can be from:
50 | csp.AllowAudioAndVideo
51 | .FromNowhere();
52 |
53 | // Contained iframes can be sourced from:
54 | csp.AllowFrames
55 | .FromNowhere(); //Nowhere, no iframes allowed
56 |
57 | // Allow AJAX, WebSocket and EventSource connections to:
58 | csp.AllowConnections
59 | .To("ws://localhost:1591")
60 | .To("http://localhost:1591")
61 | .ToSelf();
62 |
63 | // Allow fonts to be downloaded from:
64 | csp.AllowFonts
65 | .FromSelf()
66 | .From("ajax.aspnetcdn.com");
67 |
68 | // Allow object, embed, and applet sources from:
69 | csp.AllowPlugins
70 | .FromNowhere();
71 |
72 | // Allow other sites to put this in an iframe?
73 | csp.AllowFraming
74 | .FromNowhere(); // Block framing on other sites, equivalent to X-Frame-Options: DENY
75 |
76 | // Do not block violations, only report
77 | // This is a good idea while testing your CSP
78 | // Remove it when you know everything will work
79 | csp.SetReportOnly();
80 | // Where should the violation reports be sent to?
81 | csp.ReportViolationsTo("/csp-report");
82 |
83 | // Do not include the CSP header for requests to the /api endpoints
84 | csp.OnSendingHeader = context =>
85 | {
86 | context.ShouldNotSend = context.HttpContext.Request.Path.StartsWithSegments("/api");
87 | return Task.CompletedTask;
88 | };
89 | });
90 | ```
91 |
92 | Content Security Policy can be quite daunting. Here is a nice page to find out what the options do: [https://content-security-policy.com/](https://content-security-policy.com/.)
93 |
94 | For violation reports, I recommend using Scott Helme's Report URI service at [https://report-uri.io/](https://report-uri.io/).
95 |
96 | ## Nonces
97 |
98 | CSP allows you to also specify a nonce value, which makes it easier to have inline script and style elements like this on a page:
99 |
100 | ```html
101 |
102 |
105 |
110 |
111 | ```
112 |
113 | To allow them without nonces, you might have to use the unsafe-inline option.
114 |
115 | Instead of doing that, we can add the following service in `Startup`:
116 |
117 | ```cs
118 | public void ConfigureServices(IServiceCollection services)
119 | {
120 | // ... other service registrations
121 |
122 | // Add services necessary for nonces in CSP, 32-byte nonces
123 | services.AddCsp(nonceByteAmount: 32);
124 | }
125 | ```
126 |
127 | Then you need to modify your CSP definition to include the nonce:
128 |
129 | ```cs
130 | csp.AllowScripts
131 | .FromSelf()
132 | .From("localhost:1591")
133 | .From("ajax.aspnetcdn.com")
134 | .AddNonce(); //<----
135 |
136 | csp.AllowStyles
137 | .FromSelf()
138 | .From("ajax.aspnetcdn.com")
139 | .AddNonce(); //<-----
140 | ```
141 |
142 | Then to use the nonce tag helper, we need to import it in *_ViewImports.cshtml*:
143 |
144 | ```c#
145 | @addTagHelper *, Joonasw.AspNetCore.SecurityHeaders
146 | ```
147 |
148 | Then we just need to use it in the Razor view:
149 |
150 | ```html
151 |
152 |
155 |
160 |
161 | ```
162 |
163 | Now a unique nonce is generated every request and inserted into the CSP header + the elements you want.
164 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | pool:
2 | name: Azure Pipelines
3 | vmImage: windows-2022
4 |
5 | variables:
6 | buildConfiguration: "Release"
7 |
8 | steps:
9 | - task: DotNetCoreCLI@2
10 | displayName: "dotnet build"
11 | inputs:
12 | projects: "**/*.csproj"
13 | arguments: "--configuration $(buildConfiguration)"
14 |
15 | - task: DotNetCoreCLI@2
16 | displayName: "dotnet test"
17 | inputs:
18 | command: test
19 | projects: "**/*Tests/*.csproj"
20 | arguments: "--configuration $(buildConfiguration)"
21 |
22 | - task: PublishBuildArtifacts@1
23 | displayName: "Publish Artifact: Build results"
24 | inputs:
25 | PathtoPublish: "src/Joonasw.AspNetCore.SecurityHeaders/bin/$(buildConfiguration)"
26 | ArtifactName: "Build results"
27 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspBaseUriBuilder.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
2 | using System;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | public class CspBaseUriBuilder
7 | {
8 | private readonly CspBaseUriOptions _options = new CspBaseUriOptions();
9 |
10 | ///
11 | /// Block <base> element usage.
12 | ///
13 | public void FromNowhere()
14 | {
15 | _options.AllowNone = true;
16 | }
17 |
18 | ///
19 | /// Allow <base> element value to be the
20 | /// current host.
21 | ///
22 | /// The builder for call chaining
23 | public CspBaseUriBuilder FromSelf()
24 | {
25 | _options.AllowSelf = true;
26 | return this;
27 | }
28 |
29 | ///
30 | /// Allow <base> element value to be the given
31 | /// URI.
32 | ///
33 | /// The URI to allow.
34 | /// The builder for call chaining
35 | public CspBaseUriBuilder From(string uri)
36 | {
37 | if (uri == null) throw new ArgumentNullException(nameof(uri));
38 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
39 |
40 | _options.AllowedSources.Add(uri);
41 | return this;
42 | }
43 |
44 | ///
45 | /// Allow <base> element values from anywhere, except
46 | /// data:, blob:, and filesystem: schemes.
47 | ///
48 | /// The builder for call chaining
49 | public CspBaseUriBuilder FromAnywhere()
50 | {
51 | _options.AllowAny = true;
52 | return this;
53 | }
54 |
55 | ///
56 | /// Require <base> element values to
57 | /// use only HTTPS.
58 | ///
59 | /// The builder for call chaining
60 | public CspBaseUriBuilder OnlyOverHttps()
61 | {
62 | _options.AllowOnlyHttps = true;
63 | return this;
64 | }
65 |
66 | public CspBaseUriOptions BuildOptions()
67 | {
68 | return _options;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspChildBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy
8 | /// rules related to embedded content.
9 | ///
10 | public class CspChildBuilder
11 | {
12 | private readonly CspChildSrcOptions _options = new CspChildSrcOptions();
13 |
14 | ///
15 | /// Block all embedded content.
16 | ///
17 | public void FromNowhere()
18 | {
19 | _options.AllowNone = true;
20 | }
21 |
22 | ///
23 | /// Allow embedded content from current domain.
24 | ///
25 | /// The builder for call chaining
26 | public CspChildBuilder FromSelf()
27 | {
28 | _options.AllowSelf = true;
29 | return this;
30 | }
31 |
32 | ///
33 | /// Allow embedded content from given .
34 | ///
35 | /// The URI to allow.
36 | /// The builder for call chaining
37 | public CspChildBuilder From(string uri)
38 | {
39 | if (uri == null) throw new ArgumentNullException(nameof(uri));
40 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
41 |
42 | _options.AllowedSources.Add(uri);
43 | return this;
44 | }
45 |
46 | ///
47 | /// Allow embedded content from anywhere, except
48 | /// data:, blob:, and filesystem: schemes.
49 | ///
50 | /// The builder for call chaining
51 | public CspChildBuilder FromAnywhere()
52 | {
53 | _options.AllowAny = true;
54 | return this;
55 | }
56 |
57 | ///
58 | /// Allow embedded content only over secure connections.
59 | ///
60 | /// The builder for call chaining
61 | public CspChildBuilder OnlyOverHttps()
62 | {
63 | _options.AllowOnlyHttps = true;
64 | return this;
65 | }
66 |
67 | public CspChildSrcOptions BuildOptions()
68 | {
69 | return _options;
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspConnectionBuilder.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
4 | {
5 | ///
6 | /// Builder for Content Security Policy
7 | /// rules related to AJAX, WebSockets and EventSource.
8 | ///
9 | public class CspConnectionBuilder
10 | {
11 | private readonly CspConnectSrcOptions _options = new CspConnectSrcOptions();
12 |
13 | ///
14 | /// Allow AJAX, WebSockets and EventSource
15 | /// to the current domain.
16 | ///
17 | /// The builder for call chaining
18 | public CspConnectionBuilder ToSelf()
19 | {
20 | _options.AllowSelf = true;
21 | return this;
22 | }
23 |
24 | ///
25 | /// Allow AJAX, WebSockets and EventSource
26 | /// to the given .
27 | ///
28 | /// The URI to allow.
29 | /// The builder for call chaining
30 | public CspConnectionBuilder To(string uri)
31 | {
32 | _options.AllowedSources.Add(uri);
33 | return this;
34 | }
35 |
36 | ///
37 | /// Allow AJAX, WebSockets and EventSource
38 | /// to anywhere.
39 | ///
40 | /// The builder for call chaining
41 | public CspConnectionBuilder ToAnywhere()
42 | {
43 | _options.AllowAny = true;
44 | return this;
45 | }
46 |
47 | ///
48 | /// Block all usage of AJAX, WebSockets and
49 | /// EventSource.
50 | ///
51 | /// The builder for call chaining
52 | public void ToNowhere()
53 | {
54 | _options.AllowNone = true;
55 | }
56 |
57 | ///
58 | /// Allow AJAX, WebSockets and EventSource
59 | /// only over secure connections.
60 | ///
61 | /// The builder for call chaining
62 | public CspConnectionBuilder OnlyOverHttps()
63 | {
64 | _options.AllowOnlyHttps = true;
65 | return this;
66 | }
67 |
68 | public CspConnectSrcOptions BuildOptions()
69 | {
70 | return _options;
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspDefaultBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for default fallback
8 | /// Content Security Policy rules.
9 | ///
10 | public class CspDefaultBuilder
11 | {
12 | private readonly CspDefaultSrcOptions _options = new CspDefaultSrcOptions();
13 |
14 | ///
15 | /// Block the resource.
16 | ///
17 | public void FromNowhere()
18 | {
19 | _options.AllowNone = true;
20 | }
21 |
22 | ///
23 | /// Allow resources from current domain.
24 | ///
25 | /// The builder for call chaining
26 | public CspDefaultBuilder FromSelf()
27 | {
28 | _options.AllowSelf = true;
29 | return this;
30 | }
31 |
32 | ///
33 | /// Allow resources from the given
34 | /// .
35 | ///
36 | /// The URI to allow.
37 | /// The builder for call chaining
38 | public CspDefaultBuilder From(string uri)
39 | {
40 | if (uri == null) throw new ArgumentNullException(nameof(uri));
41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
42 |
43 | _options.AllowedSources.Add(uri);
44 | return this;
45 | }
46 |
47 | ///
48 | /// Allow resources from any source, except
49 | /// data:, blob:, and filesystem: schemes.
50 | ///
51 | /// The builder for call chaining
52 | public CspDefaultBuilder FromAnywhere()
53 | {
54 | _options.AllowAny = true;
55 | return this;
56 | }
57 |
58 | ///
59 | /// Allow resources only over secure connections.
60 | ///
61 | /// The builder for call chaining
62 | public CspDefaultBuilder OnlyOverHttps()
63 | {
64 | _options.AllowOnlyHttps = true;
65 | return this;
66 | }
67 |
68 | public CspDefaultSrcOptions BuildOptions()
69 | {
70 | return _options;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspFontsBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy
8 | /// rules related to fonts.
9 | ///
10 | public class CspFontsBuilder
11 | {
12 | private readonly CspFontSrcOptions _options = new CspFontSrcOptions();
13 |
14 | ///
15 | /// Block all fonts.
16 | ///
17 | public void FromNowhere()
18 | {
19 | _options.AllowNone = true;
20 | }
21 |
22 | ///
23 | /// Allow fonts from current domain.
24 | ///
25 | /// The builder for call chaining
26 | public CspFontsBuilder FromSelf()
27 | {
28 | _options.AllowSelf = true;
29 | return this;
30 | }
31 |
32 | ///
33 | /// Allow fonts from the given
34 | /// .
35 | ///
36 | /// The URI to allow.
37 | /// The builder for call chaining
38 | public CspFontsBuilder From(string uri)
39 | {
40 | if (uri == null) throw new ArgumentNullException(nameof(uri));
41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
42 |
43 | _options.AllowedSources.Add(uri);
44 | return this;
45 | }
46 |
47 | ///
48 | /// Allow fonts from anywhere, except
49 | /// data:, blob:, and filesystem: schemes.
50 | ///
51 | /// The builder for call chaining
52 | public CspFontsBuilder FromAnywhere()
53 | {
54 | _options.AllowAny = true;
55 | return this;
56 | }
57 |
58 | ///
59 | /// Allow fonts only over secure
60 | /// connections.
61 | ///
62 | /// The builder for call chaining
63 | public CspFontsBuilder OnlyOverHttps()
64 | {
65 | _options.AllowOnlyHttps = true;
66 | return this;
67 | }
68 |
69 | public CspFontSrcOptions BuildOptions()
70 | {
71 | return _options;
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspFormActionBuilder.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
4 | {
5 | ///
6 | /// Builder for Content Security Policy
7 | /// rules related to form action targets.
8 | ///
9 | public class CspFormActionBuilder
10 | {
11 | private readonly CspFormActionOptions _options = new CspFormActionOptions();
12 |
13 | ///
14 | /// Allow form submissions to current domain.
15 | ///
16 | /// The builder for call chaining
17 | public CspFormActionBuilder ToSelf()
18 | {
19 | _options.AllowSelf = true;
20 | return this;
21 | }
22 |
23 | ///
24 | /// Allow form submissions to the given
25 | /// .
26 | ///
27 | ///
28 | /// The builder for call chaining
29 | public CspFormActionBuilder To(string uri)
30 | {
31 | _options.AllowedSources.Add(uri);
32 | return this;
33 | }
34 |
35 | ///
36 | /// Allow form submissions anywhere.
37 | ///
38 | /// The builder for call chaining
39 | public CspFormActionBuilder ToAnywhere()
40 | {
41 | _options.AllowAny = true;
42 | return this;
43 | }
44 |
45 | ///
46 | /// Block form submissions.
47 | ///
48 | /// The builder for call chaining
49 | public void ToNowhere()
50 | {
51 | _options.AllowNone = true;
52 | }
53 |
54 | ///
55 | /// Allow form submissions only over secure
56 | /// connections.
57 | ///
58 | /// The builder for call chaining
59 | public CspFormActionBuilder OnlyOverHttps()
60 | {
61 | _options.AllowOnlyHttps = true;
62 | return this;
63 | }
64 |
65 | public CspFormActionOptions BuildOptions()
66 | {
67 | return _options;
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspFrameAncestorsBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy
8 | /// rules related to embedding this app.
9 | ///
10 | public class CspFrameAncestorsBuilder
11 | {
12 | private readonly CspFrameAncestorsOptions _options = new CspFrameAncestorsOptions();
13 |
14 | ///
15 | /// Deny embedding this app.
16 | ///
17 | public void FromNowhere()
18 | {
19 | _options.AllowNone = true;
20 | }
21 |
22 | ///
23 | /// Allow embedding this app in itself.
24 | ///
25 | /// The builder for call chaining
26 | public CspFrameAncestorsBuilder FromSelf()
27 | {
28 | _options.AllowSelf = true;
29 | return this;
30 | }
31 |
32 | ///
33 | /// Allow embedding this app in the given
34 | /// .
35 | ///
36 | /// The URI to allow.
37 | /// The builder for call chaining
38 | public CspFrameAncestorsBuilder From(string uri)
39 | {
40 | if (uri == null) throw new ArgumentNullException(nameof(uri));
41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
42 |
43 | _options.AllowedSources.Add(uri);
44 | return this;
45 | }
46 |
47 | ///
48 | /// Allow embedding this app anywhere.
49 | ///
50 | /// The builder for call chaining
51 | public CspFrameAncestorsBuilder FromAnywhere()
52 | {
53 | _options.AllowAny = true;
54 | return this;
55 | }
56 |
57 | ///
58 | /// Allow embedding this app only
59 | /// over secure connections.
60 | ///
61 | /// The builder for call chaining
62 | public CspFrameAncestorsBuilder OnlyOverHttps()
63 | {
64 | _options.AllowOnlyHttps = true;
65 | return this;
66 | }
67 |
68 | public CspFrameAncestorsOptions BuildOptions()
69 | {
70 | return _options;
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspFrameBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy rules
8 | /// related to <frame> and <iframe> sources.
9 | ///
10 | public class CspFrameBuilder
11 | {
12 | private readonly CspFrameSrcOptions _options = new CspFrameSrcOptions();
13 |
14 | ///
15 | /// Block all <frame>
16 | /// and <iframe> sources.
17 | ///
18 | public void FromNowhere()
19 | {
20 | _options.AllowNone = true;
21 | }
22 |
23 | ///
24 | /// Allow <frame> and <iframe> sources
25 | /// from current domain.
26 | ///
27 | /// The builder for call chaining
28 | public CspFrameBuilder FromSelf()
29 | {
30 | _options.AllowSelf = true;
31 | return this;
32 | }
33 |
34 | ///
35 | /// Allow <frame> and <iframe> sources
36 | /// from the given .
37 | ///
38 | /// The URI to allow.
39 | /// The builder for call chaining
40 | public CspFrameBuilder From(string uri)
41 | {
42 | if (uri == null) throw new ArgumentNullException(nameof(uri));
43 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
44 |
45 | _options.AllowedSources.Add(uri);
46 | return this;
47 | }
48 |
49 | ///
50 | /// Allow <frame> and <iframe> sources
51 | /// from anywhere, except data:, blob:,
52 | /// and filesystem: schemes.
53 | ///
54 | /// The builder for call chaining
55 | public CspFrameBuilder FromAnywhere()
56 | {
57 | _options.AllowAny = true;
58 | return this;
59 | }
60 |
61 | ///
62 | /// Allow <frame> and <iframe> sources
63 | /// only over secure connections.
64 | ///
65 | /// The builder for call chaining
66 | public CspFrameBuilder OnlyOverHttps()
67 | {
68 | _options.AllowOnlyHttps = true;
69 | return this;
70 | }
71 |
72 | public CspFrameSrcOptions BuildOptions()
73 | {
74 | return _options;
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspImageBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy
8 | /// rules related to images.
9 | ///
10 | public class CspImageBuilder
11 | {
12 | private readonly CspImgSrcOptions _options = new CspImgSrcOptions();
13 |
14 | ///
15 | /// Block all images.
16 | ///
17 | public void FromNowhere()
18 | {
19 | _options.AllowNone = true;
20 | }
21 |
22 | ///
23 | /// Allow images from current domain.
24 | ///
25 | /// The builder for call chaining
26 | public CspImageBuilder FromSelf()
27 | {
28 | _options.AllowSelf = true;
29 | return this;
30 | }
31 |
32 | ///
33 | /// Allow images from the given
34 | /// .
35 | ///
36 | /// The URI to allow.
37 | /// The builder for call chaining
38 | public CspImageBuilder From(string uri)
39 | {
40 | if (uri == null) throw new ArgumentNullException(nameof(uri));
41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
42 |
43 | _options.AllowedSources.Add(uri);
44 | return this;
45 | }
46 |
47 | ///
48 | /// Allow images from anywhere, except
49 | /// data:, blob:, and filesystem: schemes.
50 | ///
51 | /// The builder for call chaining
52 | public CspImageBuilder FromAnywhere()
53 | {
54 | _options.AllowAny = true;
55 | return this;
56 | }
57 |
58 | ///
59 | /// Allow images over secure connections.
60 | ///
61 | /// The builder for call chaining
62 | public CspImageBuilder OnlyOverHttps()
63 | {
64 | _options.AllowOnlyHttps = true;
65 | return this;
66 | }
67 |
68 | ///
69 | /// Allow data: scheme embedding of images.
70 | ///
71 | /// The builder for call chaining
72 | public CspImageBuilder DataScheme()
73 | {
74 | _options.AllowDataScheme = true;
75 | return this;
76 | }
77 |
78 | public CspImgSrcOptions BuildOptions()
79 | {
80 | return _options;
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspManifestBuilder.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
4 | {
5 | ///
6 | /// Builder for controlling where web manifests can be loaded from.
7 | ///
8 | public class CspManifestBuilder
9 | {
10 | private readonly CspManifestSrcOptions _options = new CspManifestSrcOptions();
11 |
12 | ///
13 | /// Allow manifests to be loaded from the current domain.
14 | ///
15 | /// The builder for call chaining
16 | public CspManifestBuilder FromSelf()
17 | {
18 | _options.AllowSelf = true;
19 | return this;
20 | }
21 |
22 | ///
23 | /// Allow manifests to load from the given .
24 | ///
25 | /// The URI to allow.
26 | /// The builder for call chaining
27 | public CspManifestBuilder From(string uri)
28 | {
29 | _options.AllowedSources.Add(uri);
30 | return this;
31 | }
32 |
33 | ///
34 | /// Allow manifests to load from anywhere.
35 | ///
36 | /// The builder for call chaining
37 | public CspManifestBuilder FromAnywhere()
38 | {
39 | _options.AllowAny = true;
40 | return this;
41 | }
42 |
43 | ///
44 | /// Block all loading of manifests.
45 | ///
46 | /// The builder for call chaining
47 | public void FromNowhere()
48 | {
49 | _options.AllowNone = true;
50 | }
51 |
52 | ///
53 | /// Allow manifests to load from secure connections.
54 | ///
55 | /// The builder for call chaining
56 | public CspManifestBuilder OnlyOverHttps()
57 | {
58 | _options.AllowOnlyHttps = true;
59 | return this;
60 | }
61 |
62 | public CspManifestSrcOptions BuildOptions()
63 | {
64 | return _options;
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspMediaBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy rules
8 | /// related to <audio> and <video> sources.
9 | ///
10 | public class CspMediaBuilder
11 | {
12 | private readonly CspMediaSrcOptions _options = new CspMediaSrcOptions();
13 |
14 | ///
15 | /// Block all <audio>
16 | /// and <video> sources.
17 | ///
18 | public void FromNowhere()
19 | {
20 | _options.AllowNone = true;
21 | }
22 |
23 | ///
24 | /// Allow <audio> and <video> sources
25 | /// from current domain.
26 | ///
27 | /// The builder for call chaining
28 | public CspMediaBuilder FromSelf()
29 | {
30 | _options.AllowSelf = true;
31 | return this;
32 | }
33 |
34 | ///
35 | /// Allow <audio> and <video> sources
36 | /// from the given .
37 | ///
38 | /// The URI to allow.
39 | /// The builder for call chaining
40 | public CspMediaBuilder From(string uri)
41 | {
42 | if (uri == null) throw new ArgumentNullException(nameof(uri));
43 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
44 |
45 | _options.AllowedSources.Add(uri);
46 | return this;
47 | }
48 |
49 | ///
50 | /// Allow <audio> and <video> sources
51 | /// from anywhere, except data:, blob:,
52 | /// and filesystem: schemes.
53 | ///
54 | /// The builder for call chaining
55 | public CspMediaBuilder FromAnywhere()
56 | {
57 | _options.AllowAny = true;
58 | return this;
59 | }
60 |
61 | ///
62 | /// Allow <audio> and <video> sources
63 | /// only over secure connections.
64 | ///
65 | /// The builder for call chaining
66 | public CspMediaBuilder OnlyOverHttps()
67 | {
68 | _options.AllowOnlyHttps = true;
69 | return this;
70 | }
71 |
72 | public CspMediaSrcOptions BuildOptions()
73 | {
74 | return _options;
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspPluginBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy rules
8 | /// related to plugins in e.g. <object>,
9 | /// <embed>, and <applet> elements.
10 | ///
11 | public class CspPluginBuilder
12 | {
13 | private readonly CspPluginTypesOptions _pluginOptions = new CspPluginTypesOptions();
14 | private readonly CspObjectSrcOptions _objectOptions = new CspObjectSrcOptions();
15 |
16 | ///
17 | /// Block all <object>, <embed>,
18 | /// and <applet> sources.
19 | ///
20 | public void FromNowhere()
21 | {
22 | _objectOptions.AllowNone = true;
23 | }
24 |
25 | ///
26 | /// Allow <object>, <embed>, and <applet>
27 | /// sources from current domain.
28 | ///
29 | /// The builder for call chaining
30 | public CspPluginBuilder FromSelf()
31 | {
32 | _objectOptions.AllowSelf = true;
33 | return this;
34 | }
35 |
36 | ///
37 | /// Allow <object>, <embed>, and <applet>
38 | /// sources from the given .
39 | ///
40 | /// The URI to allow.
41 | /// The builder for call chaining
42 | public CspPluginBuilder From(string uri)
43 | {
44 | if (uri == null) throw new ArgumentNullException(nameof(uri));
45 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
46 |
47 | _objectOptions.AllowedSources.Add(uri);
48 | return this;
49 | }
50 |
51 | ///
52 | /// Allow <object>, <embed>, and <applet>
53 | /// sources from anywhere.
54 | ///
55 | /// The builder for call chaining
56 | public CspPluginBuilder FromAnywhere()
57 | {
58 | _objectOptions.AllowAny = true;
59 | return this;
60 | }
61 |
62 | ///
63 | /// Add valid MIME type for plugins invoked via
64 | /// <object>, <embed>, or <applet>.
65 | /// In order to use <applet>, you must add
66 | /// application/x-java-applet.
67 | ///
68 | /// The MIME type to allow.
69 | /// The builder for call chaining
70 | public CspPluginBuilder WithMimeType(string mimeType)
71 | {
72 | _pluginOptions.AllowedMediaTypes.Add(mimeType);
73 | return this;
74 | }
75 |
76 | ///
77 | /// Allow <object>, <embed>, and <applet>
78 | /// sources only over secure connections.
79 | ///
80 | /// The builder for call chaining
81 | public CspPluginBuilder OnlyOverHttps()
82 | {
83 | _objectOptions.AllowOnlyHttps = true;
84 | return this;
85 | }
86 |
87 | public Tuple BuildOptions()
88 | {
89 | return Tuple.Create(_objectOptions, _pluginOptions);
90 | }
91 | }
92 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspPrefetchBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy rules
8 | /// related to pre-fetching and pre-rendering content
9 | ///
10 | public class CspPrefetchBuilder
11 | {
12 | private readonly CspPrefetchSrcOptions _options = new CspPrefetchSrcOptions();
13 |
14 | ///
15 | /// Block all pre-fetching/pre-rendering
16 | ///
17 | public void FromNowhere()
18 | {
19 | _options.AllowNone = true;
20 | }
21 |
22 | ///
23 | /// Allow pre-fetching/pre-rendering
24 | /// from current domain.
25 | ///
26 | /// The builder for call chaining
27 | public CspPrefetchBuilder FromSelf()
28 | {
29 | _options.AllowSelf = true;
30 | return this;
31 | }
32 |
33 | ///
34 | /// Allow pre-fetching/pre-rendering
35 | /// from the given .
36 | ///
37 | /// The URI to allow.
38 | /// The builder for call chaining
39 | public CspPrefetchBuilder From(string uri)
40 | {
41 | if (uri == null) throw new ArgumentNullException(nameof(uri));
42 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
43 |
44 | _options.AllowedSources.Add(uri);
45 | return this;
46 | }
47 |
48 | ///
49 | /// Allow pre-fetching/pre-rendering
50 | /// from anywhere, except data:, blob:,
51 | /// and filesystem: schemes.
52 | ///
53 | /// The builder for call chaining
54 | public CspPrefetchBuilder FromAnywhere()
55 | {
56 | _options.AllowAny = true;
57 | return this;
58 | }
59 |
60 | ///
61 | /// Allow pre-fetching/pre-rendering
62 | /// only over secure connections.
63 | ///
64 | /// The builder for call chaining
65 | public CspPrefetchBuilder OnlyOverHttps()
66 | {
67 | _options.AllowOnlyHttps = true;
68 | return this;
69 | }
70 |
71 | public CspPrefetchSrcOptions BuildOptions()
72 | {
73 | return _options;
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspRequireSriBuilder.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
4 | {
5 | public class CspRequireSriBuilder
6 | {
7 | private readonly CspRequireSriOptions _options = new CspRequireSriOptions();
8 |
9 | ///
10 | /// Require subresource integrity attributes for scripts loaded on this page
11 | ///
12 | public CspRequireSriBuilder ForScripts()
13 | {
14 | _options.ForScripts = true;
15 | return this;
16 | }
17 |
18 | ///
19 | /// Require subresource integrity attributes for styles loaded on this page
20 | ///
21 | public CspRequireSriBuilder ForStyles()
22 | {
23 | _options.ForStyles = true;
24 | return this;
25 | }
26 |
27 | public CspRequireSriOptions BuildOptions()
28 | {
29 | return _options;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspSandboxBuilder.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
4 | {
5 | ///
6 | /// Builder for Content Security Policy rules
7 | /// related to sandbox exceptions.
8 | ///
9 | public class CspSandboxBuilder
10 | {
11 | private readonly CspSandboxOptions _options = new CspSandboxOptions();
12 |
13 | ///
14 | /// Allow submission of forms.
15 | ///
16 | /// The builder for call chaining
17 | public CspSandboxBuilder AllowForms()
18 | {
19 | _options.AllowForms = true;
20 | return this;
21 | }
22 |
23 | ///
24 | /// Allow execution of JavaScript.
25 | ///
26 | /// The builder for call chaining
27 | public CspSandboxBuilder AllowScripts()
28 | {
29 | _options.AllowScripts = true;
30 | return this;
31 | }
32 |
33 | ///
34 | /// Allow modal windows.
35 | ///
36 | /// The builder for call chaining
37 | public CspSandboxBuilder AllowModals()
38 | {
39 | _options.AllowModals = true;
40 | return this;
41 | }
42 |
43 | ///
44 | /// Allow to disable the ability
45 | /// to lock the screen orientation.
46 | ///
47 | /// The builder for call chaining
48 | public CspSandboxBuilder AllowOrientationLock()
49 | {
50 | _options.AllowOrientationLock = true;
51 | return this;
52 | }
53 |
54 | ///
55 | /// Allow to use the Pointer Lock API.
56 | ///
57 | /// The builder for call chaining
58 | public CspSandboxBuilder AllowPointerLock()
59 | {
60 | _options.AllowPointerLock = true;
61 | return this;
62 | }
63 |
64 | ///
65 | /// Allow pop-ups, e.g. ones created with
66 | /// window.open().
67 | ///
68 | /// The builder for call chaining
69 | public CspSandboxBuilder AllowPopups()
70 | {
71 | _options.AllowPopups = true;
72 | return this;
73 | }
74 |
75 | ///
76 | /// Allow pop-ups opened to run outside the sandbox.
77 | ///
78 | /// The builder for call chaining
79 | public CspSandboxBuilder AllowPopupsToEscapeSandbox()
80 | {
81 | _options.AllowPopupsToEscapeSandbox = true;
82 | return this;
83 | }
84 |
85 | ///
86 | /// Allow embedders to have control over
87 | /// whether an iframe can start a presentation session.
88 | ///
89 | /// The builder for call chaining
90 | public CspSandboxBuilder AllowPresentation()
91 | {
92 | _options.AllowPresentation = true;
93 | return this;
94 | }
95 |
96 | ///
97 | /// Allow the content to be treated as being from its
98 | /// normal origin. If this exception is not enabled, the
99 | /// embedded content is treated as being from a unique origin.
100 | ///
101 | /// The builder for call chaining
102 | public CspSandboxBuilder AllowSameOrigin()
103 | {
104 | _options.AllowSameOrigin = true;
105 | return this;
106 | }
107 |
108 | ///
109 | /// Allow the embedded browsing context to navigate
110 | /// (load) content to the top-level browsing context.
111 | ///
112 | /// The builder for call chaining
113 | public CspSandboxBuilder AllowTopNavigation()
114 | {
115 | _options.AllowTopNavigation = true;
116 | return this;
117 | }
118 |
119 | public CspSandboxOptions BuildOptions()
120 | {
121 | return _options;
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspScriptsBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy
8 | /// rules related to JavaScript.
9 | ///
10 | public class CspScriptsBuilder
11 | {
12 | private readonly CspScriptSrcOptions _options = new CspScriptSrcOptions();
13 |
14 | ///
15 | /// Block all JavaScript.
16 | ///
17 | public void FromNowhere()
18 | {
19 | _options.AllowNone = true;
20 | }
21 |
22 | ///
23 | /// Allow JavaScript from current domain.
24 | ///
25 | /// The builder for call chaining
26 | public CspScriptsBuilder FromSelf()
27 | {
28 | _options.AllowSelf = true;
29 | return this;
30 | }
31 |
32 | ///
33 | /// Allow JavaScript from the given
34 | /// .
35 | ///
36 | /// The URI to allow.
37 | /// The builder for call chaining
38 | public CspScriptsBuilder From(string uri)
39 | {
40 | if (uri == null) throw new ArgumentNullException(nameof(uri));
41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
42 |
43 | _options.AllowedSources.Add(uri);
44 | return this;
45 | }
46 |
47 | ///
48 | /// Allow JavaScript with the given
49 | /// .
50 | ///
51 | /// The hash to allow.
52 | /// The builder for call chaining
53 | public CspScriptsBuilder WithHash(string hash)
54 | {
55 | if (hash == null) throw new ArgumentNullException(nameof(hash));
56 | if (hash.Length == 0) throw new ArgumentException("Hash can't be empty", nameof(hash));
57 |
58 | _options.AllowedHashes.Add(hash);
59 | return this;
60 | }
61 |
62 | ///
63 | /// Allow inline scripts.
64 | /// If you have an XSS vulnerability, this will allow them to execute.
65 | /// Cannot be enabled together with AddNonce.
66 | ///
67 | /// The builder for call chaining
68 | public CspScriptsBuilder AllowUnsafeInline()
69 | {
70 | _options.AllowUnsafeInline = true;
71 | return this;
72 | }
73 |
74 | ///
75 | /// Allow usage of eval(). Do not enable unless you really
76 | /// need it.
77 | ///
78 | /// The builder for call chaining
79 | public CspScriptsBuilder AllowUnsafeEval()
80 | {
81 | _options.AllowUnsafeEval = true;
82 | return this;
83 | }
84 |
85 | ///
86 | /// Generates a random nonce for each request and
87 | /// adds it to the CSP header. Allows inline scripts that
88 | /// have the nonce attribute set to the random value on
89 | /// the script element.
90 | /// Note that you must call AddCsp() on the service collection
91 | /// in ConfigureServices() to add the necessary dependencies.
92 | ///
93 | /// The builder for call chaining
94 | public CspScriptsBuilder AddNonce()
95 | {
96 | _options.AddNonce = true;
97 | return this;
98 | }
99 |
100 | ///
101 | /// Allow JavaScript from anywhere, except
102 | /// data:, blob:, and filesystem: schemes.
103 | ///
104 | /// The builder for call chaining
105 | public CspScriptsBuilder FromAnywhere()
106 | {
107 | _options.AllowAny = true;
108 | return this;
109 | }
110 |
111 | ///
112 | /// Allow JavaScript only over secure connections.
113 | ///
114 | /// The builder for call chaining
115 | public CspScriptsBuilder OnlyOverHttps()
116 | {
117 | _options.AllowOnlyHttps = true;
118 | return this;
119 | }
120 |
121 | ///
122 | /// Allow scripts that have been loaded with
123 | /// a trusted hash/nonce to load additional
124 | /// scripts.
125 | /// This enabled a "strict" mode
126 | /// for scripts, requiring a hash or nonce
127 | /// on all of them.
128 | ///
129 | /// The builder for call chaining
130 | public CspScriptsBuilder WithStrictDynamic()
131 | {
132 | _options.StrictDynamic = true;
133 | return this;
134 | }
135 |
136 | public CspScriptSrcOptions BuildOptions()
137 | {
138 | return _options;
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspStylesBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy
8 | /// rules related to CSS.
9 | ///
10 | public class CspStylesBuilder
11 | {
12 | private readonly CspStyleSrcOptions _options = new CspStyleSrcOptions();
13 |
14 | ///
15 | /// Block all CSS.
16 | ///
17 | public void FromNowhere()
18 | {
19 | _options.AllowNone = true;
20 | }
21 |
22 | ///
23 | /// Allow CSS from current domain.
24 | ///
25 | /// The builder for call chaining
26 | public CspStylesBuilder FromSelf()
27 | {
28 | _options.AllowSelf = true;
29 | return this;
30 | }
31 |
32 | ///
33 | /// Allow CSS from the given
34 | /// .
35 | ///
36 | /// The URI to allow.
37 | /// The builder for call chaining
38 | public CspStylesBuilder From(string uri)
39 | {
40 | if (uri == null) throw new ArgumentNullException(nameof(uri));
41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
42 |
43 | _options.AllowedSources.Add(uri);
44 | return this;
45 | }
46 |
47 | ///
48 | /// Allow CSS with the given
49 | /// .
50 | ///
51 | /// The hash to allow.
52 | /// The builder for call chaining
53 | public CspStylesBuilder WithHash(string hash)
54 | {
55 | if (hash == null) throw new ArgumentNullException(nameof(hash));
56 | if (hash.Length == 0) throw new ArgumentException("Hash can't be empty", nameof(hash));
57 |
58 | _options.AllowedHashes.Add(hash);
59 | return this;
60 | }
61 |
62 | ///
63 | /// Allow CSS from anywhere, except
64 | /// data:, blob:, and filesystem: schemes.
65 | ///
66 | /// The builder for call chaining
67 | public CspStylesBuilder FromAnywhere()
68 | {
69 | _options.AllowAny = true;
70 | return this;
71 | }
72 |
73 | ///
74 | /// Allow inline CSS.
75 | /// Cannot be enabled together with AddNonce.
76 | ///
77 | /// The builder for call chaining
78 | public CspStylesBuilder AllowUnsafeInline()
79 | {
80 | _options.AllowUnsafeInline = true;
81 | return this;
82 | }
83 |
84 | ///
85 | /// Generates a random nonce for each request and
86 | /// adds it to the CSP header. Allows inline styles that
87 | /// have the nonce attribute set to the random value on
88 | /// the script element.
89 | /// Note that you must call AddCsp() on the service collection
90 | /// in ConfigureServices() to add the necessary dependencies.
91 | ///
92 | /// The builder for call chaining
93 | public CspStylesBuilder AddNonce()
94 | {
95 | _options.AddNonce = true;
96 | return this;
97 | }
98 |
99 | ///
100 | /// Allow CSS only over secure connections.
101 | ///
102 | /// The builder for call chaining
103 | public CspStylesBuilder OnlyOverHttps()
104 | {
105 | _options.AllowOnlyHttps = true;
106 | return this;
107 | }
108 |
109 | public CspStyleSrcOptions BuildOptions()
110 | {
111 | return _options;
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspWorkerBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder
5 | {
6 | ///
7 | /// Builder for Content Security Policy rules
8 | /// related to Worker, SharedWorker and ServiceWorker script sources.
9 | ///
10 | public class CspWorkerBuilder
11 | {
12 | private readonly CspWorkerSrcOptions _options = new CspWorkerSrcOptions();
13 |
14 | ///
15 | /// Block all Worker, SharedWorker or ServiceWorker sources.
16 | ///
17 | public void FromNowhere()
18 | {
19 | _options.AllowNone = true;
20 | }
21 |
22 | ///
23 | /// Allow Worker, SharedWorker or ServiceWorker sources
24 | /// from current domain.
25 | ///
26 | /// The builder for call chaining
27 | public CspWorkerBuilder FromSelf()
28 | {
29 | _options.AllowSelf = true;
30 | return this;
31 | }
32 |
33 | ///
34 | /// Allow Worker, SharedWorker or ServiceWorker sources
35 | /// from the given .
36 | ///
37 | /// The URI to allow.
38 | /// The builder for call chaining
39 | public CspWorkerBuilder From(string uri)
40 | {
41 | if (uri == null) throw new ArgumentNullException(nameof(uri));
42 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
43 |
44 | _options.AllowedSources.Add(uri);
45 | return this;
46 | }
47 |
48 | ///
49 | /// Allow Worker, SharedWorker or ServiceWorker sources
50 | /// from anywhere, except data:, blob:,
51 | /// and filesystem: schemes.
52 | ///
53 | /// The builder for call chaining
54 | public CspWorkerBuilder FromAnywhere()
55 | {
56 | _options.AllowAny = true;
57 | return this;
58 | }
59 |
60 | ///
61 | /// Allow Worker, SharedWorker or ServiceWorker sources
62 | /// only over secure connections.
63 | ///
64 | /// The builder for call chaining
65 | public CspWorkerBuilder OnlyOverHttps()
66 | {
67 | _options.AllowOnlyHttps = true;
68 | return this;
69 | }
70 |
71 | public CspWorkerSrcOptions BuildOptions()
72 | {
73 | return _options;
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/CspMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp
8 | {
9 | public class CspMiddleware
10 | {
11 | private const string CspHeaderName = "Content-Security-Policy";
12 | private const string CspReportOnlyHeaderName = "Content-Security-Policy-Report-Only";
13 | private readonly RequestDelegate _next;
14 | private readonly CspOptions _options;
15 | private readonly string _headerName;
16 | private readonly string _headerValue;
17 |
18 | public CspMiddleware(RequestDelegate next, IOptions options)
19 | {
20 | if (options == null)
21 | {
22 | throw new ArgumentNullException(nameof(options));
23 | }
24 |
25 | _next = next;
26 | _options = options.Value;
27 | // If a nonce is needed to be generated, we can't cache the header value
28 | if (_options.IsNonceNeeded)
29 | {
30 | _headerName = null;
31 | _headerValue = null;
32 | }
33 | else
34 | {
35 | //If nonces are not needed, we can cache them immediately
36 | var (headerName, headerValue) = _options.ToString(null);
37 | _headerName = headerName;
38 | _headerValue = headerValue;
39 | }
40 | }
41 |
42 | public async Task Invoke(HttpContext context)
43 | {
44 | // Check if a CSP header has already been added to the response
45 | // This can happen for example if a middleware re-executes the pipeline
46 | if (!ContainsCspHeader(context.Response))
47 | {
48 | var sendingHeaderContext = new CspSendingHeaderContext(context);
49 | //Call the per-request check if CSP should be sent
50 | await _options.OnSendingHeader(sendingHeaderContext);
51 |
52 | if (!sendingHeaderContext.ShouldNotSend)
53 | {
54 | string headerName;
55 | string headerValue;
56 | if (_options.IsNonceNeeded)
57 | {
58 | var nonceService = (ICspNonceService)context.RequestServices.GetService(typeof(ICspNonceService));
59 | (headerName, headerValue) = _options.ToString(nonceService);
60 | }
61 | else
62 | {
63 | headerName = _headerName;
64 | headerValue = _headerValue;
65 | }
66 | context.Response.Headers.Add(headerName, headerValue);
67 | }
68 | }
69 |
70 | await _next.Invoke(context);
71 | }
72 |
73 | private bool ContainsCspHeader(HttpResponse response)
74 | {
75 | return response.Headers.Any(h => h.Key.Equals(CspHeaderName, StringComparison.OrdinalIgnoreCase)
76 | || h.Key.Equals(CspReportOnlyHeaderName, StringComparison.OrdinalIgnoreCase));
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/CspNonceService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Cryptography;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp
5 | {
6 | public class CspNonceService : ICspNonceService
7 | {
8 | private readonly string _nonce;
9 |
10 | public CspNonceService(int nonceByteAmount = 32)
11 | {
12 | byte[] nonceBytes = new byte[nonceByteAmount];
13 | using(var rng = RandomNumberGenerator.Create())
14 | {
15 | rng.GetBytes(nonceBytes);
16 | }
17 |
18 | _nonce = Convert.ToBase64String(nonceBytes);
19 | }
20 |
21 | public string GetNonce()
22 | {
23 | return _nonce;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/CspSendingHeaderContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp
4 | {
5 | public class CspSendingHeaderContext
6 | {
7 | public HttpContext HttpContext { get; }
8 |
9 | ///
10 | /// true iff the CSP header should not be sent, false otherwise.
11 | ///
12 | public bool ShouldNotSend { get; set; }
13 |
14 | public CspSendingHeaderContext(HttpContext httpContext)
15 | {
16 | HttpContext = httpContext;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/ICspNonceService.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp
2 | {
3 | public interface ICspNonceService
4 | {
5 | ///
6 | /// Gets the generated nonce.
7 | ///
8 | /// Nonce generated when the service was initialized.
9 | /// Must be attached to CSP header and any inline style or script
10 | /// you want to use.
11 | string GetNonce();
12 | }
13 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspBaseUriOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspBaseUriOptions : CspSrcOptionsBase
4 | {
5 | public CspBaseUriOptions()
6 | : base("base-uri")
7 | {
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspChildSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspChildSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspChildSrcOptions()
6 | : base("child-src")
7 | {
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspConnectSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspConnectSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspConnectSrcOptions()
6 | : base("connect-src")
7 | {
8 |
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspDefaultSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspDefaultSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspDefaultSrcOptions()
6 | : base("default-src")
7 | {
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspFontSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspFontSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspFontSrcOptions()
6 | : base("font-src")
7 | {
8 |
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspFormActionOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspFormActionOptions : CspSrcOptionsBase
4 | {
5 | public CspFormActionOptions()
6 | : base("form-action")
7 | {
8 |
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspFrameAncestorsOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
4 | {
5 | public class CspFrameAncestorsOptions
6 | {
7 | ///
8 | /// Collection of sources that can embed this app.
9 | ///
10 | public ICollection AllowedSources { get; set; }
11 | ///
12 | /// Allow embedding from the same domain as the app.
13 | /// Equivalent to X-Frame-Options: SAMEORIGIN.
14 | ///
15 | public bool AllowSelf { get; set; }
16 | ///
17 | /// Block iframe embedding.
18 | /// Equivalent to X-Frame-Options: DENY.
19 | ///
20 | public bool AllowNone { get; set; }
21 | ///
22 | /// Allow loading only through secure channels.
23 | ///
24 | public bool AllowOnlyHttps { get; set; }
25 | ///
26 | /// Allows any source.
27 | ///
28 | public bool AllowAny { get; set; }
29 |
30 | public CspFrameAncestorsOptions()
31 | {
32 | AllowedSources = new List();
33 | }
34 |
35 | public override string ToString()
36 | {
37 | ICollection parts = new List();
38 |
39 | if (AllowNone)
40 | {
41 | parts.Add("'none'");
42 | }
43 | else
44 | {
45 | if (AllowAny)
46 | {
47 | parts.Add("*");
48 | }
49 | if (AllowSelf)
50 | {
51 | parts.Add("'self'");
52 | }
53 | if (AllowOnlyHttps)
54 | {
55 | parts.Add("https:");
56 | }
57 |
58 | foreach (string allowedSource in AllowedSources)
59 | {
60 | parts.Add(allowedSource);
61 | }
62 | }
63 |
64 | if(parts.Count == 0)
65 | {
66 | return string.Empty;
67 | }
68 |
69 | return "frame-ancestors " + string.Join(" ", parts);
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspFrameSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspFrameSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspFrameSrcOptions()
6 | : base("frame-src")
7 | {
8 |
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspImgSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspImgSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspImgSrcOptions()
6 | : base("img-src")
7 | {
8 |
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspManifestSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspManifestSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspManifestSrcOptions()
6 | : base ("manifest-src")
7 | {
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspMediaSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspMediaSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspMediaSrcOptions()
6 | : base("media-src")
7 | {
8 |
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspObjectSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspObjectSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspObjectSrcOptions()
6 | : base("object-src")
7 | {
8 |
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspPluginTypesOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
4 | {
5 | public class CspPluginTypesOptions
6 | {
7 | ///
8 | /// Collection of MIME types allowed for loading resources
9 | /// in <object> and <embed> tags.
10 | ///
11 | public ICollection AllowedMediaTypes { get; }
12 |
13 | public CspPluginTypesOptions()
14 | {
15 | AllowedMediaTypes = new List();
16 | }
17 |
18 | public override string ToString()
19 | {
20 | if (AllowedMediaTypes.Count == 0)
21 | {
22 | return string.Empty;
23 | }
24 | return "plugin-types " + string.Join(" ", AllowedMediaTypes);
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspPrefetchSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspPrefetchSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspPrefetchSrcOptions()
6 | : base("prefetch-src")
7 | {
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspRequireSriOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
4 | {
5 | public class CspRequireSriOptions
6 | {
7 | ///
8 | /// If true subresource integrity attributes will be required for scripts loaded from this page
9 | ///
10 | public bool ForScripts { get; set; }
11 |
12 | ///
13 | /// If true subresource integrity attributes will be required for stylesheets loaded from this page
14 | ///
15 | public bool ForStyles { get; set; }
16 |
17 | ///
18 | public override string ToString()
19 | {
20 | if (!ForScripts && !ForStyles)
21 | {
22 | return string.Empty;
23 | }
24 |
25 | var requiredOn = new List(2);
26 | if (ForScripts)
27 | {
28 | requiredOn.Add("script");
29 | }
30 |
31 | if (ForStyles)
32 | {
33 | requiredOn.Add("style");
34 | }
35 |
36 | return $"require-sri-for {string.Join(" ", requiredOn)}";
37 |
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspSandboxOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
4 | {
5 | public class CspSandboxOptions
6 | {
7 | ///
8 | /// Allow page to submit forms.
9 | ///
10 | public bool AllowForms { get; set; }
11 | ///
12 | /// Allow content as being from its normal origin.
13 | ///
14 | public bool AllowSameOrigin { get; set; }
15 | ///
16 | /// Allow content to run scripts, but not create pop-ups.
17 | ///
18 | public bool AllowScripts { get; set; }
19 | ///
20 | /// Allow popups with e.g. window.open().
21 | ///
22 | public bool AllowPopups { get; set; }
23 | ///
24 | /// Allow modal windows.
25 | ///
26 | public bool AllowModals { get; set; }
27 | ///
28 | /// Allow the page to disable the ability to lock the screen orientation.
29 | ///
30 | public bool AllowOrientationLock { get; set; }
31 | ///
32 | /// Allow the page to use the Pointer Lock API.
33 | ///
34 | public bool AllowPointerLock { get; set; }
35 | ///
36 | /// Allow embedders to have control over whether an iframe can
37 | /// start a presentation session.
38 | ///
39 | public bool AllowPresentation { get; set; }
40 | ///
41 | /// Allow a sandboxed document to open new windows without
42 | /// forcing the sandboxing flags upon them. This will allow,
43 | /// for example, a third-party advertisement to be safely
44 | /// sandboxed without forcing the same restrictions upon a landing page.
45 | ///
46 | public bool AllowPopupsToEscapeSandbox { get; set; }
47 | ///
48 | /// Allows the embedded browsing context to navigate (load)
49 | /// content to the top-level browsing context.
50 | ///
51 | public bool AllowTopNavigation { get; set; }
52 |
53 | public override string ToString()
54 | {
55 | ICollection parts = new List();
56 |
57 | if (AllowForms)
58 | {
59 | parts.Add("allow-forms");
60 | }
61 | if (AllowSameOrigin)
62 | {
63 | parts.Add("allow-same-origin");
64 | }
65 | if (AllowScripts)
66 | {
67 | parts.Add("allow-scripts");
68 | }
69 | if (AllowPopups)
70 | {
71 | parts.Add("allow-popups");
72 | }
73 | if (AllowModals)
74 | {
75 | parts.Add("allow-modals");
76 | }
77 | if (AllowOrientationLock)
78 | {
79 | parts.Add("allow-orientation-lock");
80 | }
81 | if (AllowPointerLock)
82 | {
83 | parts.Add("allow-pointer-lock");
84 | }
85 | if (AllowPresentation)
86 | {
87 | parts.Add("allow-presentation");
88 | }
89 | if (AllowPopupsToEscapeSandbox)
90 | {
91 | parts.Add("allow-popups-to-escape-sandbox");
92 | }
93 | if (AllowTopNavigation)
94 | {
95 | parts.Add("allow-top-navigation");
96 | }
97 |
98 | if (parts.Count == 0)
99 | {
100 | return string.Empty;
101 | }
102 | return "sandbox " + string.Join(" ", parts);
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspScriptSrcOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
5 | {
6 | public class CspScriptSrcOptions : CspSrcOptionsBase
7 | {
8 | public bool AddNonce { get; set; }
9 |
10 | public bool AllowUnsafeEval { get; set; }
11 |
12 | public bool AllowUnsafeInline { get; set; }
13 |
14 | ///
15 | /// Allow scripts that have been loaded with
16 | /// a trusted hash/nonce to load additional
17 | /// scripts.
18 | /// This enabled a "strict" mode
19 | /// for scripts, requiring a hash or nonce
20 | /// on all of them.
21 | ///
22 | public bool StrictDynamic { get; set; }
23 |
24 | ///
25 | /// Collection of hashes that can be loaded.
26 | ///
27 | public ICollection AllowedHashes { get; set; }
28 |
29 | public CspScriptSrcOptions()
30 | : base("script-src")
31 | {
32 | AllowedHashes = new List();
33 | }
34 |
35 | protected override ICollection GetParts(ICspNonceService nonceService)
36 | {
37 | ICollection parts = base.GetParts(nonceService);
38 |
39 | foreach (string allowedHash in AllowedHashes)
40 | {
41 | parts.Add($"'{allowedHash}'");
42 | }
43 |
44 | if (AddNonce)
45 | {
46 | if (nonceService == null)
47 | {
48 | throw new ArgumentNullException(
49 | nameof(nonceService),
50 | "Nonce service was not found, it needs to be added to the service collection");
51 | }
52 |
53 | parts.Add($"'nonce-{nonceService.GetNonce()}'");
54 | }
55 |
56 | if (AllowUnsafeEval)
57 | {
58 | parts.Add("'unsafe-eval'");
59 | }
60 |
61 | if (AllowUnsafeInline)
62 | {
63 | parts.Add("'unsafe-inline'");
64 | }
65 |
66 | if (StrictDynamic)
67 | {
68 | parts.Add("'strict-dynamic'");
69 | }
70 |
71 | return parts;
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspSrcOptionsBase.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
4 | {
5 | public abstract class CspSrcOptionsBase
6 | {
7 | private readonly string _directiveName;
8 |
9 | ///
10 | /// Collection of sources from where these resources can be loaded.
11 | ///
12 | public ICollection AllowedSources { get; set; }
13 |
14 | ///
15 | /// Allow loading these resources from the same domain as the app.
16 | ///
17 | public bool AllowSelf { get; set; }
18 | ///
19 | /// Block loading of these resources.
20 | ///
21 | public bool AllowNone { get; set; }
22 | ///
23 | /// Allow loading only through secure channels.
24 | ///
25 | public bool AllowOnlyHttps { get; set; }
26 | ///
27 | /// Allows loading via data scheme, e.g. Base64-encoded images.
28 | ///
29 | public bool AllowDataScheme { get; set; }
30 | ///
31 | /// Allows any source except data:, blob:, and filesystem: schemes.
32 | ///
33 | public bool AllowAny { get; set; }
34 |
35 | protected CspSrcOptionsBase(string directiveName)
36 | {
37 | _directiveName = directiveName + " ";
38 | AllowedSources = new List();
39 | }
40 |
41 | protected virtual ICollection GetParts(ICspNonceService nonceService)
42 | {
43 | ICollection parts = new List();
44 |
45 | if (AllowNone)
46 | {
47 | parts.Add("'none'");
48 | }
49 | else
50 | {
51 | if (AllowAny)
52 | {
53 | parts.Add("*");
54 | }
55 | if (AllowSelf)
56 | {
57 | parts.Add("'self'");
58 | }
59 | if (AllowOnlyHttps)
60 | {
61 | parts.Add("https:");
62 | }
63 | if (AllowDataScheme)
64 | {
65 | parts.Add("data:");
66 | }
67 |
68 | foreach (string allowedSource in AllowedSources)
69 | {
70 | parts.Add(allowedSource);
71 | }
72 | }
73 | return parts;
74 | }
75 |
76 | public string ToString(ICspNonceService nonceService)
77 | {
78 | ICollection parts = GetParts(nonceService);
79 |
80 | if (parts.Count == 0)
81 | {
82 | return string.Empty;
83 | }
84 | return _directiveName + string.Join(" ", parts);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspStyleSrcOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
5 | {
6 | public class CspStyleSrcOptions : CspSrcOptionsBase
7 | {
8 | public bool AddNonce { get; set; }
9 |
10 | public bool AllowUnsafeInline { get; set; }
11 |
12 | ///
13 | /// Collection of hashes that can be loaded.
14 | ///
15 | public ICollection AllowedHashes { get; set; }
16 |
17 | public CspStyleSrcOptions()
18 | : base("style-src")
19 | {
20 | AllowedHashes = new List();
21 | }
22 |
23 | protected override ICollection GetParts(ICspNonceService nonceService)
24 | {
25 | ICollection parts = base.GetParts(nonceService);
26 |
27 | foreach (string allowedHash in AllowedHashes)
28 | {
29 | parts.Add($"'{allowedHash}'");
30 | }
31 |
32 | if (AddNonce)
33 | {
34 | if (nonceService == null)
35 | {
36 | throw new ArgumentNullException(
37 | nameof(nonceService),
38 | "Nonce service was not found, it needs to be added to the service collection");
39 | }
40 |
41 | parts.Add($"'nonce-{nonceService.GetNonce()}'");
42 | }
43 |
44 | if (AllowUnsafeInline)
45 | {
46 | parts.Add("'unsafe-inline'");
47 | }
48 |
49 | return parts;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspWorkerSrcOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options
2 | {
3 | public class CspWorkerSrcOptions : CspSrcOptionsBase
4 | {
5 | public CspWorkerSrcOptions()
6 | : base("worker-src")
7 | {
8 |
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/EnumExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Linq;
4 | using System.Reflection;
5 |
6 | namespace Joonasw.AspNetCore.SecurityHeaders
7 | {
8 | internal static class EnumExtensions
9 | {
10 | ///
11 | /// Gets the string value associated with the the corresponding enum option
12 | ///
13 | /// Any Enum where DefaultValue attribute is defined.
14 | ///
15 | internal static string DefaultValue(this Enum enumValue)
16 | {
17 | var fieldInfo = enumValue.GetType().GetField(enumValue.ToString());
18 |
19 | var defaultValueAttribute = fieldInfo
20 | .GetCustomAttributes(typeof(DefaultValueAttribute), inherit: false)
21 | .Cast()
22 | .SingleOrDefault();
23 |
24 | return defaultValueAttribute?.Value.ToString() ?? enumValue.ToString();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/ExpectCT/ExpectCTMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.ExpectCT
8 | {
9 | public class ExpectCTMiddleware
10 | {
11 | private const string HeaderName = "Expect-CT";
12 | private readonly RequestDelegate _next;
13 | private readonly string _headerValue;
14 |
15 | public ExpectCTMiddleware(RequestDelegate next, IOptions options)
16 | {
17 | _next = next;
18 | _headerValue = options.Value.HeaderValue;
19 | }
20 |
21 | public async Task Invoke(HttpContext context)
22 | {
23 | //Expect-CT can only be applied to secure requests according to spec
24 | if (context.Request.IsHttps && !ContainsExpectCTHeader(context.Response))
25 | {
26 | context.Response.Headers.Add(HeaderName, _headerValue);
27 | }
28 | await _next(context);
29 | }
30 |
31 | private static bool ContainsExpectCTHeader(HttpResponse response)
32 | {
33 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase));
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/ExpectCT/ExpectCTOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.ExpectCT
5 | {
6 | ///
7 | /// Options for the Expect-CT Header
8 | ///
9 | public class ExpectCTOptions
10 | {
11 | ///
12 | /// Defines the parameters for the Expect-CT header with only the max-age set to 30 days.
13 | /// Please see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT for support and more info
14 | ///
15 | public ExpectCTOptions()
16 | {
17 |
18 | }
19 |
20 | ///
21 | /// Defines the parameters for the Expect-CT header sent to clients.
22 | /// Please see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT for support and more info
23 | ///
24 | /// The required max-age directive specifies the number of seconds that
25 | /// the browser should cache and apply the received policy for, whether enforced or report-only.
26 | /// the report-uri directive specifies where the browser should send reports
27 | /// if it does not receive valid CT information. This is specified as an absolute URI.
28 | /// The optional enforce directive controls whether the browser should
29 | /// enforce the policy. Omitting this will treat it as report-only mode. The directive has no value so you simply
30 | /// include it or not depending on whether or not you want the browser to enforce the policy.
31 | public ExpectCTOptions(TimeSpan maxAge, string reportUri, bool enforce = false)
32 | {
33 | MaxAge = maxAge;
34 | Enforce = enforce;
35 | ReportUri = reportUri;
36 | }
37 |
38 | ///
39 | /// Specifies the TimeSpan from seconds after reception of the Expect-CT header field
40 | /// during which the user agent should regard the host from whom the message was
41 | /// received as a known Expect-CT host.
42 | ///
43 | public TimeSpan MaxAge { get; set; } = TimeSpan.FromDays(30);
44 |
45 | ///
46 | /// Specifies the number of seconds after reception of the Expect-CT header field
47 | /// during which the user agent should regard the host from whom the message was
48 | /// received as a known Expect-CT host.
49 | ///
50 | public int DurationSeconds => (int)MaxAge.TotalSeconds;
51 |
52 | ///
53 | /// If true, browsers will enforce the Certificate Transparency policy and refuse future connections that violate the policy.
54 | ///
55 | public bool Enforce { get; set; }
56 |
57 | ///
58 | /// The URL where violation reports should be sent.
59 | ///
60 | public string ReportUri { get; set; }
61 |
62 | public string HeaderValue
63 | {
64 | get
65 | {
66 | var values = new List(3)
67 | {
68 | "max-age=" + DurationSeconds
69 | };
70 |
71 | if (Enforce)
72 | {
73 | values.Add("enforce");
74 | }
75 |
76 | if (ReportUri != null)
77 | {
78 | values.Add($"report-uri=\"{ReportUri}\"");
79 | }
80 |
81 | return string.Join(", ", values);
82 | }
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Builder/FeaturePolicyFeatureBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Builder
5 | {
6 | public class FeaturePolicyFeatureBuilder : IFeaturePolicyFeatureBuilder>
7 | where TOptions : FeaturePolicyOptionsBase, new()
8 | {
9 | private readonly FeaturePolicyOptionsBase _options;
10 |
11 | public FeaturePolicyFeatureBuilder()
12 | {
13 | _options = new TOptions();
14 | }
15 |
16 | ///
17 | public void FromNowhere()
18 | {
19 | _options.AllowNone = true;
20 | }
21 |
22 | ///
23 | public FeaturePolicyFeatureBuilder FromSelf()
24 | {
25 | _options.AllowSelf = true;
26 | return this;
27 | }
28 |
29 | ///
30 | public FeaturePolicyFeatureBuilder FromAnywhere()
31 | {
32 | _options.AllowAny = true;
33 | return this;
34 | }
35 |
36 | ///
37 | public FeaturePolicyFeatureBuilder From(string uri)
38 | {
39 | if (uri == null) throw new ArgumentNullException(nameof(uri));
40 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
41 |
42 | _options.AllowedOrigins.Add(uri);
43 | return this;
44 | }
45 |
46 | internal FeaturePolicyOptionsBase BuildOptions()
47 | {
48 | return _options;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Builder/FeaturePolicyOtherFeatureBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Builder
5 | {
6 | public class FeaturePolicyOtherFeatureBuilder : IFeaturePolicyFeatureBuilder
7 | {
8 | private readonly FeaturePolicyOtherFeatureOptions _options;
9 |
10 | public FeaturePolicyOtherFeatureBuilder(FeaturePolicyOtherFeatureOptions options)
11 | {
12 | _options = options;
13 | }
14 |
15 | public void FromNowhere()
16 | {
17 | _options.AllowNone = true;
18 | }
19 |
20 | public FeaturePolicyOtherFeatureBuilder FromSelf()
21 | {
22 | _options.AllowSelf = true;
23 | return this;
24 | }
25 |
26 | public FeaturePolicyOtherFeatureBuilder FromAnywhere()
27 | {
28 | _options.AllowAny = true;
29 | return this;
30 | }
31 |
32 | public FeaturePolicyOtherFeatureBuilder From(string uri)
33 | {
34 | if (uri == null) throw new ArgumentNullException(nameof(uri));
35 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri));
36 |
37 | _options.AllowedOrigins.Add(uri);
38 | return this;
39 | }
40 |
41 | internal FeaturePolicyOptionsBase BuildOptions()
42 | {
43 | return _options;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Builder/IFeaturePolicyFeatureBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Builder
2 | {
3 | internal interface IFeaturePolicyFeatureBuilder
4 | {
5 | ///
6 | /// Block access to feature from all domains.
7 | ///
8 | void FromNowhere();
9 | ///
10 | /// Allowed from current domain.
11 | ///
12 | ///
13 | T FromSelf();
14 | ///
15 | /// Allowed from any domain.
16 | ///
17 | ///
18 | T FromAnywhere();
19 | ///
20 | /// Allowed from the given
21 | /// .
22 | ///
23 | /// The URI to allow.
24 | /// The builder for call chaining
25 | T From(string uri);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/FeaturePolicyMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy
8 | {
9 | public class FeaturePolicyMiddleware
10 | {
11 | private const string HeaderName = "Feature-Policy";
12 | private readonly RequestDelegate _next;
13 | private readonly string _headerValue;
14 |
15 | public FeaturePolicyMiddleware(RequestDelegate next, IOptions options)
16 | {
17 | if (options == null)
18 | {
19 | throw new ArgumentNullException(nameof(options));
20 | }
21 |
22 | _next = next;
23 | _headerValue = options.Value.ToString();
24 | }
25 |
26 | public async Task Invoke(HttpContext context)
27 | {
28 | // Check if a Feature Policy header has already been added to the response
29 | // This can happen for example if a middleware re-executes the pipeline
30 | if (!ContainsFeaturePolicyHeader(context.Response))
31 | {
32 | context.Response.Headers.Add(HeaderName, _headerValue);
33 | }
34 |
35 | await _next.Invoke(context);
36 | }
37 |
38 | private static bool ContainsFeaturePolicyHeader(HttpResponse response)
39 | {
40 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase));
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyAccelerometerOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyAccelerometerOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyAccelerometerOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Accelerometer.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyAmbientLightSensorOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyAmbientLightSensorOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyAmbientLightSensorOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.AmbientLightSensor.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyAutoplayOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyAutoplayOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyAutoplayOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Autoplay.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyCameraOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyCameraOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyCameraOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Camera.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyEncryptedMediaOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyEncryptedMediaOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyEncryptedMediaOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.EncryptedMedia.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyFullscreenOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyFullscreenOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyFullscreenOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Fullscreen.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyGeolocationOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyGeolocationOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyGeolocationOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Geolocation.DefaultValue();
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyGyroscopeOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyGyroscopeOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyGyroscopeOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Gyroscope.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyMagnetometerOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyMagnetometerOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyMagnetometerOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Magnetometer.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyMicrophoneOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyMicrophoneOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyMicrophoneOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Microphone.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyMidiOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyMidiOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyMidiOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Midi.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyNotificationsOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyNotificationsOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyNotificationsOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Notifications.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyOptionsBase.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
4 | {
5 | public class FeaturePolicyOptionsBase
6 | {
7 | public string FeatureName { get; set; }
8 | ///
9 | /// Collection of origins from where these resources can be loaded.
10 | ///
11 | public ICollection AllowedOrigins { get; set; }
12 | ///
13 | /// Allow loading these resources from the same domain as the app.
14 | ///
15 | public bool AllowSelf { get; set; }
16 | ///
17 | /// Block loading of these resources.
18 | ///
19 | public bool AllowNone { get; set; }
20 | ///
21 | /// Allows any source except data:, blob:, and filesystem: schemes.
22 | ///
23 | public bool AllowAny { get; set; }
24 |
25 | public FeaturePolicyOptionsBase()
26 | {
27 | AllowedOrigins = new List();
28 | }
29 |
30 | //protected FeaturePolicyOptionsBase(string featureName)
31 | //{
32 | // FeatureName = featureName;
33 | // AllowedOrigins = new List();
34 | //}
35 |
36 | protected virtual ICollection GetParts()
37 | {
38 | ICollection parts = new List();
39 |
40 | if (AllowNone)
41 | {
42 | parts.Add("'none'");
43 | }
44 | else
45 | {
46 | if (AllowAny)
47 | {
48 | parts.Add("*");
49 | }
50 | if (AllowSelf)
51 | {
52 | parts.Add("'self'");
53 | }
54 | if (AllowedOrigins.Count > 0)
55 | {
56 | parts.Add(string.Join(" ", AllowedOrigins));
57 | }
58 | }
59 | return parts;
60 | }
61 |
62 | public override string ToString()
63 | {
64 | var parts = GetParts();
65 |
66 | return parts.Count == 0 ? string.Empty : $"{FeatureName} {string.Join(" ", parts)}";
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyOtherFeatureOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyOtherFeatureOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyOtherFeatureOptions(string featureName)
6 | {
7 | FeatureName = featureName;
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyPaymentOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyPaymentOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyPaymentOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Payment.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyPictureInPictureOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyPictureInPictureOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyPictureInPictureOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.PictureInPicture.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyPushOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyPushOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyPushOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Push.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicySpeakerOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicySpeakerOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicySpeakerOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Speaker.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicySyncXhrOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicySyncXhrOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicySyncXhrOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.SyncXhr.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyUsbOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyUsbOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyUsbOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Usb.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyVibrateOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyVibrateOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyVibrateOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Vibrate.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyVrOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options
2 | {
3 | public class FeaturePolicyVrOptions : FeaturePolicyOptionsBase
4 | {
5 | public FeaturePolicyVrOptions()
6 | {
7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Vr.DefaultValue();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Hpkp/Builder/HpkpBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Hpkp.Builder
2 | {
3 | ///
4 | /// Builder for building HTTP
5 | /// Public Key Pins rules.
6 | ///
7 | public class HpkpBuilder
8 | {
9 | private readonly HpkpOptions _options = new HpkpOptions();
10 |
11 | ///
12 | /// Sets how long the public keys will be cached
13 | /// in users' browsers in seconds. Decide this carefully,
14 | /// as the rule is quite hard for normal users to remove
15 | /// from cache if something were to go wrong during
16 | /// this period.
17 | ///
18 | /// The caching duration in seconds.
19 | /// The builder for call chaining
20 | public HpkpBuilder UseMaxAgeSeconds(int seconds)
21 | {
22 | _options.MaxAgeSeconds = seconds;
23 | return this;
24 | }
25 |
26 | ///
27 | /// Adds a given SHA-256 public key hash to the header.
28 | /// The hash can be gotten from any certificate in the
29 | /// certificate chain for your certificate. This way
30 | /// you can either pin your specific certificate, a middle
31 | /// certificate, or even the root certificate.
32 | ///
33 | /// The SHA-256 hash of one of the certificates thumbprints.
34 | /// The builder for call chaining
35 | public HpkpBuilder AddSha256Pin(string pin)
36 | {
37 | _options.Pins.Add("pin-sha256=\"" + pin + "\"");
38 | return this;
39 | }
40 |
41 | ///
42 | /// Enables this rule for all subdomains of the
43 | /// current domain as well. Be very careful with
44 | /// this rule, as you must be sure the given keys
45 | /// will be used on all future subdomains.
46 | ///
47 | /// The builder for call chaining
48 | public HpkpBuilder IncludeSubDomains()
49 | {
50 | _options.IncludeSubDomains = true;
51 | return this;
52 | }
53 |
54 | ///
55 | /// Sets the pinning to report-only mode.
56 | /// Invalid certificates are not blocked, but
57 | /// you will receive reports for violations.
58 | /// Excellent for testing settings.
59 | ///
60 | /// The builder for call chaining
61 | public HpkpBuilder SetReportOnly()
62 | {
63 | _options.ReportOnly = true;
64 | return this;
65 | }
66 |
67 | ///
68 | /// The URI to where violations should be reported to.
69 | /// Can be a relative or absolute URL.
70 | ///
71 | /// The URL where violations are reported to.
72 | /// The builder for call chaining
73 | public HpkpBuilder ReportViolationsTo(string uri)
74 | {
75 | _options.ReportUri = uri;
76 | return this;
77 | }
78 |
79 | public HpkpOptions BuildHpkpOptions()
80 | {
81 | return _options;
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Hpkp/HpkpMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.Hpkp
8 | {
9 | public class HpkpMiddleware
10 | {
11 | private readonly RequestDelegate _next;
12 | private readonly string _headerName;
13 | private readonly string _headerValue;
14 |
15 | public HpkpMiddleware(RequestDelegate next, IOptions options)
16 | {
17 | _next = next;
18 | _headerName = options.Value.HeaderName;
19 | _headerValue = options.Value.HeaderValue;
20 | }
21 |
22 | public async Task Invoke(HttpContext context)
23 | {
24 | if (!ContainsHpkpHeader(context.Response))
25 | {
26 | context.Response.Headers.Add(_headerName, _headerValue);
27 | }
28 | await _next(context);
29 | }
30 |
31 | private bool ContainsHpkpHeader(HttpResponse response)
32 | {
33 | return response.Headers.Any(h => h.Key.Equals(HpkpOptions.BlockingHeaderName, StringComparison.OrdinalIgnoreCase)
34 | || h.Key.Equals(HpkpOptions.ReportOnlyHeaderName, StringComparison.OrdinalIgnoreCase));
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/HpkpOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders
4 | {
5 | public class HpkpOptions
6 | {
7 | public const string ReportOnlyHeaderName = "Public-Key-Pins-Report-Only";
8 | public const string BlockingHeaderName = "Public-Key-Pins";
9 | public int MaxAgeSeconds { get; set; }
10 | public IList Pins { get; set; } = new List();
11 | public bool IncludeSubDomains { get; set; }
12 | public bool ReportOnly { get; set; }
13 | public string ReportUri { get; set; }
14 |
15 | public string HeaderName => ReportOnly ? ReportOnlyHeaderName : BlockingHeaderName;
16 |
17 | public string HeaderValue
18 | {
19 | get
20 | {
21 | string value = string.Join(";", Pins);
22 |
23 | value += ";" + "max-age=" + MaxAgeSeconds;
24 |
25 | if (IncludeSubDomains)
26 | {
27 | value += "; includeSubDomains";
28 | }
29 |
30 | if (ReportUri != null)
31 | {
32 | value += "; report-uri=\"" + ReportUri + "\"";
33 | }
34 |
35 | return value;
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Hsts/HstsMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.Hsts
8 | {
9 | public class HstsMiddleware
10 | {
11 | private const string HeaderName = "Strict-Transport-Security";
12 | private readonly RequestDelegate _next;
13 | private readonly string _headerValue;
14 |
15 | public HstsMiddleware(RequestDelegate next, IOptions options)
16 | {
17 | _next = next;
18 | _headerValue = options.Value.BuildHeaderValue();
19 | }
20 |
21 | public async Task Invoke(HttpContext context)
22 | {
23 | //HSTS can only be applied to secure requests according to spec
24 | // there really is no point adding it to insecure ones since MiTM can just strip the header
25 | if (context.Request.IsHttps && !ContainsHstsHeader(context.Response))
26 | {
27 | context.Response.Headers.Add(HeaderName, _headerValue);
28 | }
29 | await _next(context);
30 | }
31 |
32 | private bool ContainsHstsHeader(HttpResponse response)
33 | {
34 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase));
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Hsts/HstsOptionsExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Hsts
4 | {
5 | internal static class HstsOptionsExtensions
6 | {
7 | public static string BuildHeaderValue(this HstsOptions options)
8 | {
9 | if (options == null)
10 | {
11 | throw new ArgumentNullException(nameof(options));
12 | }
13 | if (options.DurationSeconds <= 0)
14 | {
15 | throw new ArgumentOutOfRangeException(nameof(options.DurationSeconds), "HSTS duration must be positive");
16 | }
17 |
18 | string headerValue = "max-age=" + options.DurationSeconds;
19 | if (options.IncludeSubDomains)
20 | {
21 | headerValue += "; includeSubDomains";
22 | }
23 | if (options.Preload)
24 | {
25 | headerValue += "; preload";
26 | }
27 | return headerValue;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/HstsOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders
4 | {
5 | ///
6 | /// Options for the HTTP Strict Transport Security header.
7 | ///
8 | public class HstsOptions
9 | {
10 | public HstsOptions()
11 | {
12 |
13 | }
14 |
15 | ///
16 | /// Defines the parameters for the HSTS header sent
17 | /// to clients.
18 | ///
19 | /// The amount of time the clients should remember that
20 | /// this domain should only be accessed over HTTPS.
21 | /// If true, clients will also apply the rule on
22 | /// any subdomains of the current domain. Enable this only if you know what you are doing.
23 | /// False by default.
24 | /// If true, allows this rule to be built into browsers,
25 | /// preventing the first insecure connection. Enable this only if you know what you are doing.
26 | /// False by default.
27 | public HstsOptions(TimeSpan duration, bool includeSubDomains = false, bool preload = false)
28 | {
29 | Duration = duration;
30 | IncludeSubDomains = includeSubDomains;
31 | Preload = preload;
32 | }
33 |
34 | ///
35 | /// Gets the duration browsers should remember that
36 | /// this domain should only be accessed over HTTPS.
37 | ///
38 | public TimeSpan Duration { get; set; } = TimeSpan.FromDays(30);
39 |
40 | ///
41 | /// Gets the duration browsers should remember
42 | /// this domain should only be accessed over HTTPS,
43 | /// in seconds.
44 | ///
45 | public int DurationSeconds => (int)Duration.TotalSeconds;
46 |
47 | ///
48 | /// Gets if this rule also applies to any subdomains.
49 | ///
50 | public bool IncludeSubDomains { get; set; }
51 |
52 | ///
53 | /// Gets if this domain should be allowed to be
54 | /// added to preload lists in browsers.
55 | ///
56 | public bool Preload { get; set; }
57 | }
58 | }
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/Joonasw.AspNetCore.SecurityHeaders.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0;net9.0
4 | 6.0.0
5 | Joonasw.AspNetCore.SecurityHeaders
6 | Joonas Westlin
7 | Middleware for adding security headers to an ASP.NET Core application. Allows you to easily add Content Security Policy, Strict Transport Security, and Public Key Pins to an app.
8 | Copyright 2025 Joonas Westlin
9 | True
10 | LICENSE.txt
11 | https://github.com/juunas11/aspnetcore-security-headers
12 | Remove support for .NET 6 and 7, add support for 8 and 9.
13 | aspnetcore security headers hsts hpkp csp
14 | README.md
15 | https://github.com/juunas11/aspnetcore-security-headers
16 |
17 | true
18 | true
19 | true
20 | snupkg
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/ReferrerPolicy/ReferrerPolicyMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.ReferrerPolicy
8 | {
9 | public class ReferrerPolicyMiddleware
10 | {
11 | private const string HeaderName = "Referrer-Policy";
12 | private readonly RequestDelegate _next;
13 | private readonly string _headerValue;
14 |
15 | public ReferrerPolicyMiddleware(RequestDelegate next, IOptions options)
16 | {
17 | _next = next;
18 | _headerValue = options.Value.PolicyValue.DefaultValue();
19 | }
20 |
21 | public async Task Invoke(HttpContext context)
22 | {
23 | if (!ContainsReferrerPolicyHeader(context.Response))
24 | {
25 | context.Response.Headers.Add(HeaderName, _headerValue);
26 | }
27 | await _next(context);
28 | }
29 |
30 | private static bool ContainsReferrerPolicyHeader(HttpResponse response)
31 | {
32 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase));
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/ReferrerPolicyOptions.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders
4 | {
5 | public class ReferrerPolicyOptions
6 | {
7 | ///
8 | /// Defines the parameters for the Referrer Policy header with the 'no-referrer' option set
9 | ///
10 | public ReferrerPolicyOptions()
11 | {
12 |
13 | }
14 |
15 | ///
16 | /// Defines the parameters for the Referrer Policy header sent in the response to the client
17 | ///
18 | /// The value to be applied to the Referrer Policy header
19 | public ReferrerPolicyOptions(ReferrerPolicyValue value)
20 | {
21 | PolicyValue = value;
22 | }
23 |
24 | ///
25 | /// Gets the referrer policy value to be applied to the ReferrerPolicy header
26 | /// no-referrer is set by default
27 | ///
28 | public ReferrerPolicyValue PolicyValue { get; set; } = ReferrerPolicyValue.NoReferrer;
29 |
30 | public enum ReferrerPolicyValue
31 | {
32 | [DefaultValue("")]
33 | None = 0,
34 | [DefaultValue("no-referrer")]
35 | NoReferrer = 1,
36 | [DefaultValue("no-referrer-when-downgrade")]
37 | NoReferrerWhenDowngrade = 2,
38 | [DefaultValue("same-origin")]
39 | SameOrigin = 3,
40 | [DefaultValue("origin")]
41 | Origin = 4,
42 | [DefaultValue("strict-origin")]
43 | StrictOrigin = 5,
44 | [DefaultValue("origin-when-cross-origin")]
45 | OriginWhenCrossOrigin = 6,
46 | [DefaultValue("strict-origin-when-cross-origin")]
47 | StrictOriginWhenCrossOrigin = 7,
48 | [DefaultValue("unsafe-url")]
49 | UnsafeUrl = 8
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Csp;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders
5 | {
6 | public static class ServiceCollectionExtensions
7 | {
8 | ///
9 | /// Adds dependencies required for adding
10 | /// nonces to Content Security Policy headers
11 | /// and inline scripts and styles.
12 | ///
13 | /// The
14 | /// Length of the nonces to generate in bytes.
15 | /// The
16 | public static IServiceCollection AddCsp(this IServiceCollection services, int nonceByteAmount = 32)
17 | {
18 | return services.AddScoped(svcProvider => new CspNonceService(nonceByteAmount));
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/TagHelpers/NonceTagHelper.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Csp;
2 | using Microsoft.AspNetCore.Razor.TagHelpers;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.TagHelpers
5 | {
6 | ///
7 | /// Tag helper for adding a nonce to
8 | /// inline scripts and styles.
9 | ///
10 | [HtmlTargetElement("script", Attributes = "asp-add-nonce")]
11 | [HtmlTargetElement("style", Attributes = "asp-add-nonce")]
12 | public class NonceTagHelper : TagHelper
13 | {
14 | private readonly ICspNonceService _nonceService;
15 | [HtmlAttributeName("asp-add-nonce")]
16 | public bool AddNonce { get; set; }
17 |
18 | public NonceTagHelper(ICspNonceService nonceService)
19 | {
20 | _nonceService = nonceService;
21 | }
22 |
23 | public override void Process(TagHelperContext context, TagHelperOutput output)
24 | {
25 | if (AddNonce)
26 | {
27 | // The nonce service is created per request, so we
28 | // get the same nonce here as the CSP header
29 | output.Attributes.Add("nonce", _nonceService.GetNonce());
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/XContentTypeOptions/XContentTypeOptionsMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.XContentTypeOptions
8 | {
9 | public class XContentTypeOptionsMiddleware
10 | {
11 | private const string HeaderName = "X-Content-Type-Options";
12 | private readonly RequestDelegate _next;
13 | private readonly string _headerValue;
14 |
15 | public XContentTypeOptionsMiddleware(RequestDelegate next, IOptions options)
16 | {
17 | _next = next;
18 | _headerValue = options.Value.AllowSniffing ? string.Empty : "nosniff";
19 | }
20 |
21 | public async Task Invoke(HttpContext context)
22 | {
23 | // Let's just not bother with adding the header if they want to allow sniffing
24 | if (!string.IsNullOrWhiteSpace(_headerValue) && !ContainsXContentTypeOptionsHeader(context.Response))
25 | {
26 | context.Response.Headers.Add(HeaderName, _headerValue);
27 | }
28 | await _next(context);
29 | }
30 |
31 | private static bool ContainsXContentTypeOptionsHeader(HttpResponse response)
32 | {
33 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase));
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/XContentTypeOptionsOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders
2 | {
3 | public class XContentTypeOptionsOptions
4 | {
5 | ///
6 | /// Defines the X-Content-Type-Options header with the 'nosniff' option
7 | ///
8 | public XContentTypeOptionsOptions()
9 | : this(false)
10 | {
11 |
12 | }
13 |
14 | ///
15 | /// Defines the X-Content-Type-Options header and whether to allow sniffing
16 | ///
17 | ///
18 | public XContentTypeOptionsOptions(bool allowSniffing)
19 | {
20 | AllowSniffing = allowSniffing;
21 | }
22 |
23 | ///
24 | /// Gets the value to allow sniffing on your site
25 | ///
26 | public bool AllowSniffing { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/XFrameOptions/XFrameOptionsExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.XFrameOptions
4 | {
5 | internal static class XFrameOptionsExtensions
6 | {
7 | public static string BuildHeaderValue(this XFrameOptionsOptions options)
8 | {
9 | if (options == null)
10 | {
11 | throw new ArgumentNullException(nameof(options));
12 | }
13 | var headerValue = options.HeaderValue.DefaultValue();
14 | if (options.HeaderValue == XFrameOptionsOptions.XFrameOptionsValues.AllowFrom)
15 | {
16 | headerValue += " " + options.AllowFromUrl;
17 | }
18 |
19 | return headerValue;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/XFrameOptions/XFrameOptionsMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.XFrameOptions
8 | {
9 | public class XFrameOptionsMiddleware
10 | {
11 | private const string HeaderName = "X-Frame-Options";
12 | private readonly RequestDelegate _next;
13 | private readonly string _headerValue;
14 |
15 | public XFrameOptionsMiddleware(RequestDelegate next, IOptions options)
16 | {
17 | _next = next;
18 | _headerValue = options.Value.BuildHeaderValue();
19 | }
20 |
21 | public async Task Invoke(HttpContext context)
22 | {
23 | if (!ContainsXFrameOptionsHeader(context.Response))
24 | {
25 | context.Response.Headers.Add(HeaderName, _headerValue);
26 | }
27 | await _next(context);
28 | }
29 |
30 | private static bool ContainsXFrameOptionsHeader(HttpResponse response)
31 | {
32 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase));
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/XFrameOptionsOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders
5 | {
6 | public class XFrameOptionsOptions
7 | {
8 | ///
9 | /// Defines the parameters for the X-Frame-Options header with the 'deny' option set
10 | ///
11 | public XFrameOptionsOptions()
12 | : this(XFrameOptionsValues.Deny)
13 | {
14 |
15 | }
16 |
17 | ///
18 | /// Defines the parameters for the X-Frame-Options header sent in response to clients
19 | ///
20 | /// The Value to be applied to the header X-Frame-Options header
21 | /// If ALLOW-FROM is selected, then this value is
22 | /// required and must have sited that are permitted to frame your site.
23 | /// Note: Chrome does not support the ALLOW-FROM option
24 | /// Null by default
25 | public XFrameOptionsOptions(XFrameOptionsValues value, string allowFromUrl = null)
26 | {
27 | HeaderValue = value;
28 |
29 | if (value == XFrameOptionsValues.AllowFrom && string.IsNullOrWhiteSpace(allowFromUrl))
30 | {
31 | throw new ArgumentException("ALLOW-FROM URL string cannot be empty when ALLOW-FROM option is selected.");
32 | }
33 |
34 | AllowFromUrl = allowFromUrl;
35 | }
36 |
37 | ///
38 | /// Gets the predefined value to be applied to the header X-Frame-Options header.
39 | /// DENY is set by default.
40 | /// Note: Chrome does not support the ALLOW-FROM option
41 | ///
42 | public XFrameOptionsValues HeaderValue { get; set; }
43 |
44 | ///
45 | /// Gets the url allowed from a single domain.
46 | /// Note: Chrome does not support the ALLOW-FROM option
47 | ///
48 | public string AllowFromUrl { get; set; }
49 |
50 | public enum XFrameOptionsValues
51 | {
52 | [DefaultValue("DENY")]
53 | Deny = 0,
54 | [DefaultValue("SAMEORIGIN")]
55 | SameOrigin = 1,
56 | [DefaultValue("ALLOW-FROM")]
57 | AllowFrom = 2
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/XXssProtection/XXssProtectionMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.XXssProtection
8 | {
9 | public class XXssProtectionMiddleware
10 | {
11 | private const string HeaderName = "X-XSS-Protection";
12 | private readonly RequestDelegate _next;
13 | private readonly string _headerValue;
14 |
15 | public XXssProtectionMiddleware(RequestDelegate next, IOptions options)
16 | {
17 | _next = next;
18 | _headerValue = options.Value.BuildHeaderValue();
19 | }
20 |
21 | public async Task Invoke(HttpContext context)
22 | {
23 | if (!ContainsXXssProtectionHeader(context.Response))
24 | {
25 | context.Response.Headers.Add(HeaderName, _headerValue);
26 | }
27 | await _next(context);
28 | }
29 |
30 | private static bool ContainsXXssProtectionHeader(HttpResponse response)
31 | {
32 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase));
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Joonasw.AspNetCore.SecurityHeaders/XXssProtectionOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders
2 | {
3 | public class XXssProtectionOptions
4 | {
5 | ///
6 | /// Defines the parameters for the X-Xss-Protection header with protection and block enabled
7 | ///
8 | public XXssProtectionOptions()
9 | : this(true, true)
10 | {
11 |
12 | }
13 |
14 | ///
15 | /// Defines the parameters for the X-Xss-Protection header sent in response to clients
16 | ///
17 | /// Enable protection for this host
18 | /// Block the response if it detects an attack rather than sanitising the script
19 | public XXssProtectionOptions(bool enableProtection, bool enableAttackBlock)
20 | {
21 | EnableProtection = enableProtection;
22 | EnableAttackBlock = enableAttackBlock;
23 | }
24 |
25 | ///
26 | /// Get the Enable Protection value for enabling or disabling Protection
27 | /// Enabled by default.
28 | ///
29 | public bool EnableProtection { get; set; }
30 |
31 | ///
32 | /// Gets the Enable Block value to block the response if it detects an attack rather than sanitising the script
33 | /// Enabled by default.
34 | ///
35 | public bool EnableAttackBlock { get; set; }
36 |
37 | public string BuildHeaderValue()
38 | {
39 | var headerValue = EnableProtection ? "1" : "0";
40 | if (EnableAttackBlock)
41 | {
42 | headerValue += "; mode=block";
43 | }
44 |
45 | return headerValue;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "wwwroot/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 |
3 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Controllers
4 | {
5 | public class HomeController : Controller
6 | {
7 | public IActionResult Index()
8 | {
9 | return View();
10 | }
11 |
12 | public IActionResult About()
13 | {
14 | ViewData["Message"] = "Your application description page.";
15 | return View();
16 | }
17 |
18 | public IActionResult Contact()
19 | {
20 | ViewData["Message"] = "Your contact page.";
21 |
22 | return View();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Controllers/ReportController.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Samples.Models;
2 | using Microsoft.AspNetCore.Mvc;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Controllers
5 | {
6 | public class ReportController : ControllerBase
7 | {
8 | public IActionResult Csp(CspViolationReport report)
9 | {
10 | return Ok();
11 | }
12 |
13 | public IActionResult Hpkp(HpkpViolationReport report)
14 | {
15 | return Ok();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Joonasw.AspNetCore.SecurityHeaders.Samples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 33ce3d09-4099-4a8e-93af-049b12b6eb90
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Middleware/AppBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.AspNetCore.Builder;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Middleware
5 | {
6 | public static class AppBuilderExtensions
7 | {
8 | public static IApplicationBuilder UseHttpsEnforcement(this IApplicationBuilder app)
9 | {
10 | if (app == null)
11 | {
12 | throw new ArgumentNullException(nameof(app));
13 | }
14 | return app.UseMiddleware();
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Middleware/EnforceHttpsMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Middleware
5 | {
6 | public class EnforceHttpsMiddleware
7 | {
8 | private readonly RequestDelegate _next;
9 |
10 | public EnforceHttpsMiddleware(RequestDelegate next)
11 | {
12 | _next = next;
13 | }
14 |
15 | public async Task Invoke(HttpContext context)
16 | {
17 | HttpRequest req = context.Request;
18 | if (req.IsHttps == false)
19 | {
20 | string url = "https://" + req.Host + req.Path + req.QueryString;
21 | context.Response.Redirect(url, permanent: true);
22 | }
23 | else
24 | {
25 | await _next(context);
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Models/CspViolationReport.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Models
2 | {
3 | public class CspViolationReport
4 | {
5 | /* JSON format:
6 | *
7 | * {"csp-report":
8 | * {"document-uri":"http://localhost:15490/",
9 | * "referrer":"",
10 | * "violated-directive":"connect-src 'self' https:",
11 | * "effective-directive":"connect-src",
12 | * "original-policy":"default-src https:;script-src 'self' https: ajax.aspnetcdn.com 'nonce-ZlcKG0OPAdcghg6c1EJpFOPbCHQKv6IsZRKZbBNaakE=';style-src 'self' https: ajax.aspnetcdn.com 'nonce-ZlcKG0OPAdcghg6c1EJpFOPbCHQKv6IsZRKZbBNaakE=';child-src 'none';connect-src 'self' https:;font-src 'self' https:;media-src 'none';object-src 'none';frame-ancestors 'none';report-uri /csp-report",
13 | * "blocked-uri":"http://localhost:1591/2be9ad3d3c2e4eb3aee5c0f7b76f8714/browserLinkSignalR/negotiate?requestUrl=http%3A%2F%2Flocalhost%3A15490%2F&browserName=&userAgent=Mozilla%2F5.0+(Windows+NT+10.0%3B+Win64%3B+x64)+AppleWebKit%2F537.36+(KHTML%2C+like+Gecko)+Chrome%2F55.0.2883.87+Safari%2F537.36&clientProtocol=1.3&_=1485109104042",
14 | * "line-number":37,
15 | * "column-number":85935,
16 | * "source-file":"http://localhost:1591/2be9ad3d3c2e4eb3aee5c0f7b76f8714/browserLink",
17 | * "status-code":200
18 | * }
19 | * }
20 | */
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Models/HpkpViolationReport.cs:
--------------------------------------------------------------------------------
1 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Models
2 | {
3 | public class HpkpViolationReport
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IHostBuilder CreateHostBuilder(string[] args) =>
14 | Host.CreateDefaultBuilder(args)
15 | .ConfigureWebHostDefaults(webBuilder =>
16 | {
17 | webBuilder.UseStartup();
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "https://localhost:44342/",
7 | "sslPort": 44342
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "Samples": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "http://localhost:15491"
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/Home/About.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewData["Title"] = "About";
3 | }
4 | @ViewData["Title"].
5 | @ViewData["Message"]
6 |
7 | Use this area to provide additional information.
8 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/Home/Contact.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewData["Title"] = "Contact";
3 | }
4 | @ViewData["Title"].
5 | @ViewData["Message"]
6 |
7 |
8 | One Microsoft Way
9 | Redmond, WA 98052-6399
10 | P:
11 | 425.555.0100
12 |
13 |
14 |
15 | Support: Support@example.com
16 | Marketing: Marketing@example.com
17 |
18 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/Shared/ApplicationError.cshtml:
--------------------------------------------------------------------------------
1 |
2 | Error page
3 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/Shared/Error.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewData["Title"] = "Error";
3 | }
4 |
5 | Error.
6 | An error occurred while processing your request.
7 |
8 | Development Mode
9 |
10 | Swapping to Development environment will display more detailed information about the error that occurred.
11 |
12 |
13 | Development environment should not be enabled in deployed applications , as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development , and restarting the application.
14 |
15 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/Shared/_Layout.cshtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @* *@
5 |
6 |
7 | @ViewData["Title"] - Sample App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
27 |
30 |
31 |
32 |
52 |
53 | @RenderBody()
54 |
55 |
56 | © 2018 - WebApplication1
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
68 |
69 |
71 |
72 |
73 |
74 |
75 | @RenderSection("scripts", required: false)
76 |
77 |
78 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
2 | @addTagHelper *, Joonasw.AspNetCore.SecurityHeaders
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = "_Layout";
3 | }
4 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "IncludeScopes": false,
4 | "LogLevel": {
5 | "Default": "Debug",
6 | "System": "Information",
7 | "Microsoft": "Information"
8 | }
9 | },
10 | "Hsts": {
11 | "Duration": "30:00:00:00",
12 | "IncludeSubDomains": false,
13 | "Preload": false
14 | },
15 | "Csp": {
16 | "ReportOnly": true,
17 | "ReportUri": "/csp-report",
18 | "UpgradeInsecureRequests": true,
19 | "BlockAllMixedContent": true, //Ignored if UpgradeInsecureRequests is true
20 | "Default": {
21 | "AllowedSources": [
22 | "'none'"
23 | ]
24 | },
25 | "Script": {
26 | "AddNonce": true,
27 | "AllowUnsafeEval": false,
28 | "AllowUnsafeInline": false,
29 | "AllowedSources": [
30 | "'self'",
31 | "localhost:1591",
32 | "localhost:44342",
33 | "ajax.aspnetcdn.com"
34 | ],
35 | "AllowedHashes": [
36 | "sha256-MV3a0caNPWq8CgfaHwRc3OtqWiEXK6Mo0nt+/2nFwV0="
37 | ]
38 | //"StrictDynamic": true
39 | },
40 | "Style": {
41 | "AddNonce": true,
42 | "AllowedSources": [
43 | "'self'",
44 | "ajax.aspnetcdn.com"
45 | ]
46 | },
47 | "Img": {
48 | "AllowedSources": [
49 | "'self'"
50 | ]
51 | },
52 | "Connect": {
53 | "AllowedSources": [
54 | "'self'",
55 | "ws://localhost:1591",
56 | "http://localhost:1591"
57 | ]
58 | },
59 | "Font": {
60 | "AllowedSources": [
61 | "'self'",
62 | "ajax.aspnetcdn.com"
63 | ]
64 | },
65 | "Frame": {
66 | "AllowNone": true
67 | },
68 | "Worker": {
69 | "AllowNone": true
70 | },
71 | "BaseUri": {
72 | "AllowSelf": true
73 | },
74 | "Prefetch": {
75 | "AllowSelf": true
76 | },
77 | "RequireSri": {
78 | "ForScripts": true,
79 | "ForStyles": true
80 | }
81 | },
82 | "FeaturePolicy": {
83 | "Camera": {
84 | "AllowSelf": true
85 | },
86 | "Payment": {
87 | "AllowNone": true
88 | },
89 | "Other": {
90 | "some-new-thing": {
91 | "AllowNone": true
92 | }
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "asp.net",
3 | "private": true,
4 | "dependencies": {
5 | "bootstrap": "3.3.6",
6 | "jquery": "2.2.0",
7 | "jquery-validation": "1.14.0",
8 | "jquery-validation-unobtrusive": "3.2.6"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/bundleconfig.json:
--------------------------------------------------------------------------------
1 | // Configure bundling and minification for the project.
2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241
3 | [
4 | {
5 | "outputFileName": "wwwroot/css/site.min.css",
6 | // An array of relative input file paths. Globbing patterns supported
7 | "inputFiles": [
8 | "wwwroot/css/site.css"
9 | ]
10 | },
11 | {
12 | "outputFileName": "wwwroot/js/site.min.js",
13 | "inputFiles": [
14 | "wwwroot/js/site.js"
15 | ],
16 | // Optionally specify minification options
17 | "minify": {
18 | "enabled": true,
19 | "renameLocals": true
20 | },
21 | // Optinally generate .map file
22 | "sourceMap": false
23 | }
24 | ]
25 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/web.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/_references.js:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 | ///
5 | ///
6 | ///
7 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/css/site.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 50px;
3 | padding-bottom: 20px;
4 | }
5 |
6 | /* Wrapping element */
7 | /* Set some basic padding to keep content from hitting the edges */
8 | .body-content {
9 | padding-left: 15px;
10 | padding-right: 15px;
11 | }
12 |
13 | /* Set widths on the form inputs since otherwise they're 100% wide */
14 | input,
15 | select,
16 | textarea {
17 | max-width: 280px;
18 | }
19 |
20 | /* Carousel */
21 | .carousel-caption p {
22 | font-size: 20px;
23 | line-height: 1.4;
24 | }
25 |
26 | /* Make .svg files in the carousel display properly in older browsers */
27 | .carousel-inner .item img[src$=".svg"]
28 | {
29 | width: 100%;
30 | }
31 |
32 | /* Hide/rearrange for smaller screens */
33 | @media screen and (max-width: 767px) {
34 | /* Hide captions */
35 | .carousel-caption {
36 | display: none
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/css/site.min.css:
--------------------------------------------------------------------------------
1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}input,select,textarea{max-width:280px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}@media screen and (max-width:767px){.carousel-caption{display:none}}
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/js/OtherScript.js:
--------------------------------------------------------------------------------
1 | console.log("Script loaded dynamically!");
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/js/site.js:
--------------------------------------------------------------------------------
1 |
2 | // Test for strict-dynamic
3 | //var otherScript = document.createElement("script");
4 | //otherScript.src = "/js/OtherScript.js";
5 | //document.head.appendChild(otherScript);
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/js/site.min.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/js/site.min.js
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/.bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bootstrap",
3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
4 | "keywords": [
5 | "css",
6 | "js",
7 | "less",
8 | "mobile-first",
9 | "responsive",
10 | "front-end",
11 | "framework",
12 | "web"
13 | ],
14 | "homepage": "http://getbootstrap.com",
15 | "license": "MIT",
16 | "moduleType": "globals",
17 | "main": [
18 | "less/bootstrap.less",
19 | "dist/js/bootstrap.js"
20 | ],
21 | "ignore": [
22 | "/.*",
23 | "_config.yml",
24 | "CNAME",
25 | "composer.json",
26 | "CONTRIBUTING.md",
27 | "docs",
28 | "js/tests",
29 | "test-infra"
30 | ],
31 | "dependencies": {
32 | "jquery": "1.9.1 - 2"
33 | },
34 | "version": "3.3.6",
35 | "_release": "3.3.6",
36 | "_resolution": {
37 | "type": "version",
38 | "tag": "v3.3.6",
39 | "commit": "81df608a40bf0629a1dc08e584849bb1e43e0b7a"
40 | },
41 | "_source": "git://github.com/twbs/bootstrap.git",
42 | "_target": "3.3.6",
43 | "_originalSource": "bootstrap"
44 | }
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2011-2015 Twitter, Inc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.min.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA"}
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/js/npm.js:
--------------------------------------------------------------------------------
1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
2 | require('../../js/transition.js')
3 | require('../../js/alert.js')
4 | require('../../js/button.js')
5 | require('../../js/carousel.js')
6 | require('../../js/collapse.js')
7 | require('../../js/dropdown.js')
8 | require('../../js/modal.js')
9 | require('../../js/tooltip.js')
10 | require('../../js/popover.js')
11 | require('../../js/scrollspy.js')
12 | require('../../js/tab.js')
13 | require('../../js/affix.js')
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery-validation-unobtrusive/.bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-validation-unobtrusive",
3 | "version": "3.2.6",
4 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive",
5 | "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.",
6 | "main": [
7 | "jquery.validate.unobtrusive.js"
8 | ],
9 | "ignore": [
10 | "**/.*",
11 | "*.json",
12 | "*.md",
13 | "*.txt",
14 | "gulpfile.js"
15 | ],
16 | "keywords": [
17 | "jquery",
18 | "asp.net",
19 | "mvc",
20 | "validation",
21 | "unobtrusive"
22 | ],
23 | "authors": [
24 | "Microsoft"
25 | ],
26 | "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm",
27 | "repository": {
28 | "type": "git",
29 | "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git"
30 | },
31 | "dependencies": {
32 | "jquery-validation": ">=1.8",
33 | "jquery": ">=1.8"
34 | },
35 | "_release": "3.2.6",
36 | "_resolution": {
37 | "type": "version",
38 | "tag": "v3.2.6",
39 | "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7"
40 | },
41 | "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git",
42 | "_target": "3.2.6",
43 | "_originalSource": "jquery-validation-unobtrusive"
44 | }
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | ** Unobtrusive validation support library for jQuery and jQuery Validate
3 | ** Copyright (C) Microsoft Corporation. All rights reserved.
4 | */
5 | !function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a(" ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function m(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=p.unobtrusive.options||{},m=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),m("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),m("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),m("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var u,p=a.validator,v="unobtrusiveValidation";p.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=m(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){p.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=m(this);a&&a.attachValidation()})}},u=p.unobtrusive.adapters,u.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},u.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},u.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},u.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},p.addMethod("__dummy__",function(a,e,n){return!0}),p.addMethod("regex",function(a,e,n){var t;return this.optional(e)?!0:(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),p.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),p.methods.extension?(u.addSingleVal("accept","mimtype"),u.addSingleVal("extension","extension")):u.addSingleVal("extension","extension","accept"),u.addSingleVal("regex","pattern"),u.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),u.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),u.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),u.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),u.add("required",function(a){("INPUT"!==a.element.tagName.toUpperCase()||"CHECKBOX"!==a.element.type.toUpperCase())&&e(a,"required",!0)}),u.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),u.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),a(function(){p.unobtrusive.parse(document)})}(jQuery);
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery-validation/.bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-validation",
3 | "homepage": "http://jqueryvalidation.org/",
4 | "repository": {
5 | "type": "git",
6 | "url": "git://github.com/jzaefferer/jquery-validation.git"
7 | },
8 | "authors": [
9 | "Jörn Zaefferer "
10 | ],
11 | "description": "Form validation made easy",
12 | "main": "dist/jquery.validate.js",
13 | "keywords": [
14 | "forms",
15 | "validation",
16 | "validate"
17 | ],
18 | "license": "MIT",
19 | "ignore": [
20 | "**/.*",
21 | "node_modules",
22 | "bower_components",
23 | "test",
24 | "demo",
25 | "lib"
26 | ],
27 | "dependencies": {
28 | "jquery": ">= 1.7.2"
29 | },
30 | "version": "1.14.0",
31 | "_release": "1.14.0",
32 | "_resolution": {
33 | "type": "version",
34 | "tag": "1.14.0",
35 | "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48"
36 | },
37 | "_source": "git://github.com/jzaefferer/jquery-validation.git",
38 | "_target": ">=1.8",
39 | "_originalSource": "jquery-validation"
40 | }
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery-validation/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | =====================
3 |
4 | Copyright Jörn Zaefferer
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery/.bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery",
3 | "main": "dist/jquery.js",
4 | "license": "MIT",
5 | "ignore": [
6 | "package.json"
7 | ],
8 | "keywords": [
9 | "jquery",
10 | "javascript",
11 | "browser",
12 | "library"
13 | ],
14 | "homepage": "https://github.com/jquery/jquery-dist",
15 | "version": "2.2.0",
16 | "_release": "2.2.0",
17 | "_resolution": {
18 | "type": "version",
19 | "tag": "2.2.0",
20 | "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5"
21 | },
22 | "_source": "git://github.com/jquery/jquery-dist.git",
23 | "_target": "2.2.0",
24 | "_originalSource": "jquery"
25 | }
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright jQuery Foundation and other contributors, https://jquery.org/
2 |
3 | This software consists of voluntary contributions made by many
4 | individuals. For exact contribution history, see the revision history
5 | available at https://github.com/jquery/jquery
6 |
7 | The following license applies to all parts of this software except as
8 | documented below:
9 |
10 | ====
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining
13 | a copy of this software and associated documentation files (the
14 | "Software"), to deal in the Software without restriction, including
15 | without limitation the rights to use, copy, modify, merge, publish,
16 | distribute, sublicense, and/or sell copies of the Software, and to
17 | permit persons to whom the Software is furnished to do so, subject to
18 | the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be
21 | included in all copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 |
31 | ====
32 |
33 | All files located in the node_modules and external directories are
34 | externally maintained libraries used by this software which have their
35 | own licenses; we recommend you read them, as their terms may differ from
36 | the terms above.
37 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/ContentTypeOptionsMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using Joonasw.AspNetCore.SecurityHeaders.XContentTypeOptions;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.Extensions.Options;
8 | using Xunit;
9 |
10 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
11 | {
12 | public class ContentTypeOptionsMiddlewareTests
13 | {
14 | [Fact]
15 | public async Task HeaderSetCorrectlyWithNoSniffing()
16 | {
17 | string headerValue = null;
18 | RequestDelegate mockNext = (HttpContext ctx) =>
19 | {
20 | headerValue = ctx.Response.Headers["X-Content-Type-Options"];
21 | return Task.CompletedTask;
22 | };
23 | var options = Options.Create(new XContentTypeOptionsOptions
24 | {
25 | AllowSniffing = false
26 | });
27 | var mockContext = new DefaultHttpContext();
28 | var sut = new XContentTypeOptionsMiddleware(mockNext, options);
29 |
30 | await sut.Invoke(mockContext);
31 |
32 | Assert.Equal("nosniff", headerValue);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/CspRequireSriBuilderTests.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Builder;
2 | using Xunit;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
5 | {
6 | public class CspRequireSriBuilderTests
7 | {
8 | [Fact]
9 | public void NoOptions_DoesNotGenerateHeader()
10 | {
11 | var builder = new CspRequireSriBuilder();
12 |
13 | var options = builder.BuildOptions();
14 |
15 | Assert.True(string.IsNullOrWhiteSpace(options.ToString()));
16 | }
17 |
18 | [Fact]
19 | public void RequireScript_GeneratesHeader()
20 | {
21 | var builder = new CspRequireSriBuilder();
22 | builder.ForScripts();
23 |
24 | var options = builder.BuildOptions();
25 |
26 | Assert.Equal("require-sri-for script", options.ToString());
27 | }
28 |
29 | [Fact]
30 | public void RequireStyle_GeneratesHeader()
31 | {
32 | var builder = new CspRequireSriBuilder();
33 | builder.ForStyles();
34 |
35 | var options = builder.BuildOptions();
36 |
37 | Assert.Equal("require-sri-for style", options.ToString());
38 | }
39 |
40 | [Fact]
41 | public void RequireScriptAndStyle_GeneratesHeader()
42 | {
43 | var builder = new CspRequireSriBuilder();
44 | builder.ForScripts();
45 | builder.ForStyles();
46 |
47 | var options = builder.BuildOptions();
48 |
49 | Assert.Equal("require-sri-for script style", options.ToString());
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/CspSandboxBuilderTests.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Builder;
2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
3 | using Xunit;
4 |
5 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
6 | {
7 | public class CspSandboxBuilderTests
8 | {
9 | [Fact]
10 | public void AllowForms_SetsAllowFormsToTrue()
11 | {
12 | var builder = new CspSandboxBuilder();
13 |
14 | builder.AllowForms();
15 | CspSandboxOptions options = builder.BuildOptions();
16 |
17 | Assert.True(options.AllowForms);
18 | }
19 |
20 | [Fact]
21 | public void AllowModals_SetsAllowModalsToTrue()
22 | {
23 | var builder = new CspSandboxBuilder();
24 |
25 | builder.AllowModals();
26 | CspSandboxOptions options = builder.BuildOptions();
27 |
28 | Assert.True(options.AllowModals);
29 | }
30 |
31 | [Fact]
32 | public void AllowOrientationLock_SetsAllowOrientationLockToTrue()
33 | {
34 | var builder = new CspSandboxBuilder();
35 |
36 | builder.AllowOrientationLock();
37 | CspSandboxOptions options = builder.BuildOptions();
38 |
39 | Assert.True(options.AllowOrientationLock);
40 | }
41 |
42 | [Fact]
43 | public void AllowPointerLock_SetsAllowPointerLockToTrue()
44 | {
45 | var builder = new CspSandboxBuilder();
46 |
47 | builder.AllowPointerLock();
48 | CspSandboxOptions options = builder.BuildOptions();
49 |
50 | Assert.True(options.AllowPointerLock);
51 | }
52 |
53 | [Fact]
54 | public void AllowPopups_SetsAllowPopupsToTrue()
55 | {
56 | var builder = new CspSandboxBuilder();
57 |
58 | builder.AllowPopups();
59 | CspSandboxOptions options = builder.BuildOptions();
60 |
61 | Assert.True(options.AllowPopups);
62 | }
63 |
64 | [Fact]
65 | public void AllowPopupsToEscapeSandbox_SetsAllowPopupsToEscapeSandboxToTrue()
66 | {
67 | var builder = new CspSandboxBuilder();
68 |
69 | builder.AllowPopupsToEscapeSandbox();
70 | CspSandboxOptions options = builder.BuildOptions();
71 |
72 | Assert.True(options.AllowPopupsToEscapeSandbox);
73 | }
74 |
75 | [Fact]
76 | public void AllowPresentation_SetsAllowPresentationToTrue()
77 | {
78 | var builder = new CspSandboxBuilder();
79 |
80 | builder.AllowPresentation();
81 | CspSandboxOptions options = builder.BuildOptions();
82 |
83 | Assert.True(options.AllowPresentation);
84 | }
85 |
86 | [Fact]
87 | public void AllowSameOrigin_SetsAllowSameOriginToTrue()
88 | {
89 | var builder = new CspSandboxBuilder();
90 |
91 | builder.AllowSameOrigin();
92 | CspSandboxOptions options = builder.BuildOptions();
93 |
94 | Assert.True(options.AllowSameOrigin);
95 | }
96 |
97 | [Fact]
98 | public void AllowScripts_SetsAllowScriptsToTrue()
99 | {
100 | var builder = new CspSandboxBuilder();
101 |
102 | builder.AllowScripts();
103 | CspSandboxOptions options = builder.BuildOptions();
104 |
105 | Assert.True(options.AllowScripts);
106 | }
107 |
108 | [Fact]
109 | public void AllowTopNavigation_SetsAllowTopNavigationToTrue()
110 | {
111 | var builder = new CspSandboxBuilder();
112 |
113 | builder.AllowTopNavigation();
114 | CspSandboxOptions options = builder.BuildOptions();
115 |
116 | Assert.True(options.AllowTopNavigation);
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/CspStylesBuilderTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Builder;
4 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
5 | using Xunit;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
8 | {
9 | public class CspStylesBuilderTests
10 | {
11 | [Fact]
12 | public void FromNowhere_SetsAllowNoneToTrue()
13 | {
14 | var builder = new CspStylesBuilder();
15 |
16 | builder.FromNowhere();
17 | CspStyleSrcOptions options = builder.BuildOptions();
18 |
19 | Assert.True(options.AllowNone);
20 | }
21 |
22 | [Fact]
23 | public void FromSelf_SetsAllowSelfToTrue()
24 | {
25 | var builder = new CspStylesBuilder();
26 |
27 | builder.FromSelf();
28 | CspStyleSrcOptions options = builder.BuildOptions();
29 |
30 | Assert.True(options.AllowSelf);
31 | }
32 |
33 | [Fact]
34 | public void From_AddsUrlToAllowedSources()
35 | {
36 | var builder = new CspStylesBuilder();
37 |
38 | builder.From("www.google.com");
39 | CspStyleSrcOptions options = builder.BuildOptions();
40 |
41 | Assert.Equal("www.google.com", options.AllowedSources.Single());
42 | }
43 |
44 | [Fact]
45 | public void From_ThrowsArgumentNullException_WithNullUrl()
46 | {
47 | var builder = new CspStylesBuilder();
48 |
49 | Assert.Throws(() => builder.From(null));
50 | }
51 |
52 | [Fact]
53 | public void From_ThrowsArgumentException_WithEmptyUrl()
54 | {
55 | var builder = new CspStylesBuilder();
56 |
57 | Assert.Throws(() => builder.From(string.Empty));
58 | }
59 |
60 | [Fact]
61 | public void WithHash_AddsHashToAllowedHashes()
62 | {
63 | var builder = new CspStylesBuilder();
64 |
65 | builder.WithHash("sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc=");
66 | CspStyleSrcOptions options = builder.BuildOptions();
67 |
68 | Assert.Equal("sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc=", options.AllowedHashes.Single());
69 | }
70 |
71 | [Fact]
72 | public void WithHash_ThrowsArgumentNullException_WithNullUrl()
73 | {
74 | var builder = new CspStylesBuilder();
75 |
76 | Assert.Throws(() => builder.WithHash(null));
77 | }
78 |
79 | [Fact]
80 | public void WithHash_ThrowsArgumentException_WithEmptyUrl()
81 | {
82 | var builder = new CspStylesBuilder();
83 |
84 | Assert.Throws(() => builder.WithHash(string.Empty));
85 | }
86 |
87 | [Fact]
88 | public void AllowUnsafeInline_SetsUnsafeInlineToTrue()
89 | {
90 | var builder = new CspStylesBuilder();
91 |
92 | builder.AllowUnsafeInline();
93 | CspStyleSrcOptions options = builder.BuildOptions();
94 |
95 | Assert.True(options.AllowUnsafeInline);
96 | }
97 |
98 | [Fact]
99 | public void AddNonce_SetsAddNonceToTrue()
100 | {
101 | var builder = new CspStylesBuilder();
102 |
103 | builder.AddNonce();
104 | CspStyleSrcOptions options = builder.BuildOptions();
105 |
106 | Assert.True(options.AddNonce);
107 | }
108 |
109 | [Fact]
110 | public void FromAnywhere_SetsAllowAnyToTrue()
111 | {
112 | var builder = new CspStylesBuilder();
113 |
114 | builder.FromAnywhere();
115 | CspStyleSrcOptions options = builder.BuildOptions();
116 |
117 | Assert.True(options.AllowAny);
118 | }
119 |
120 | [Fact]
121 | public void OnlyOverHttps_SetsAllowOnlyHttpsToTrue()
122 | {
123 | var builder = new CspStylesBuilder();
124 |
125 | builder.OnlyOverHttps();
126 | CspStyleSrcOptions options = builder.BuildOptions();
127 |
128 | Assert.True(options.AllowOnlyHttps);
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/CspWorkerBuilderTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Builder;
4 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options;
5 | using Xunit;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
8 | {
9 | public class CspWorkerBuilderTests
10 | {
11 | [Fact]
12 | public void FromNowhere_SetsAllowNoneToTrue()
13 | {
14 | var builder = new CspWorkerBuilder();
15 |
16 | builder.FromNowhere();
17 | CspWorkerSrcOptions options = builder.BuildOptions();
18 |
19 | Assert.True(options.AllowNone);
20 | }
21 |
22 | [Fact]
23 | public void FromSelf_SetsAllowSelfToTrue()
24 | {
25 | var builder = new CspWorkerBuilder();
26 |
27 | builder.FromSelf();
28 | CspWorkerSrcOptions options = builder.BuildOptions();
29 |
30 | Assert.True(options.AllowSelf);
31 | }
32 |
33 | [Fact]
34 | public void FromSelf_ReturnsCorrectString()
35 | {
36 | var builder = new CspWorkerBuilder();
37 |
38 | builder.FromSelf();
39 | CspWorkerSrcOptions options = builder.BuildOptions();
40 |
41 | Assert.Equal("worker-src 'self'", options.ToString(null));
42 | }
43 |
44 | [Fact]
45 | public void From_AddsUrlToAllowedSources()
46 | {
47 | var builder = new CspWorkerBuilder();
48 |
49 | builder.From("www.google.com");
50 | CspWorkerSrcOptions options = builder.BuildOptions();
51 |
52 | Assert.Equal("www.google.com", options.AllowedSources.Single());
53 | }
54 |
55 | [Fact]
56 | public void From_ThrowsArgumentNullException_WithNullUrl()
57 | {
58 | var builder = new CspWorkerBuilder();
59 |
60 | Assert.Throws(() => builder.From(null));
61 | }
62 |
63 | [Fact]
64 | public void From_ThrowsArgumentException_WithEmptyUrl()
65 | {
66 | var builder = new CspWorkerBuilder();
67 |
68 | Assert.Throws(() => builder.From(string.Empty));
69 | }
70 |
71 | [Fact]
72 | public void FromAnywhere_SetsAllowAnyToTrue()
73 | {
74 | var builder = new CspWorkerBuilder();
75 |
76 | builder.FromAnywhere();
77 | CspWorkerSrcOptions options = builder.BuildOptions();
78 |
79 | Assert.True(options.AllowAny);
80 | }
81 |
82 | [Fact]
83 | public void OnlyOverHttps_SetsAllowOnlyHttpsToTrue()
84 | {
85 | var builder = new CspWorkerBuilder();
86 |
87 | builder.OnlyOverHttps();
88 | CspWorkerSrcOptions options = builder.BuildOptions();
89 |
90 | Assert.True(options.AllowOnlyHttps);
91 | }
92 | }
93 | }
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/ExpectCtMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Joonasw.AspNetCore.SecurityHeaders.ExpectCT;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 | using Xunit;
7 |
8 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
9 | {
10 | public class ExpectCtMiddlewareTests
11 | {
12 | [Fact]
13 | public async Task SetsHeaderCorrectlyWithoutEnforce()
14 | {
15 | string headerValue = null;
16 | RequestDelegate mockNext = (HttpContext ctx) =>
17 | {
18 | headerValue = ctx.Response.Headers["Expect-CT"];
19 | return Task.CompletedTask;
20 | };
21 | var options = Options.Create(new ExpectCTOptions
22 | {
23 | Enforce = false,
24 | ReportUri = "https://reporting.com/report",
25 | MaxAge = TimeSpan.FromHours(1)
26 | });
27 | var mockContext = new DefaultHttpContext();
28 | mockContext.Request.Scheme = "https";
29 | var sut = new ExpectCTMiddleware(mockNext, options);
30 |
31 | await sut.Invoke(mockContext);
32 |
33 | Assert.Equal("max-age=3600, report-uri=\"https://reporting.com/report\"", headerValue);
34 | }
35 |
36 | [Fact]
37 | public async Task SetsHeaderCorrectlyWithEnforce()
38 | {
39 | string headerValue = null;
40 | RequestDelegate mockNext = (HttpContext ctx) =>
41 | {
42 | headerValue = ctx.Response.Headers["Expect-CT"];
43 | return Task.CompletedTask;
44 | };
45 | var options = Options.Create(new ExpectCTOptions
46 | {
47 | Enforce = true,
48 | ReportUri = "https://reporting.com/report",
49 | MaxAge = TimeSpan.FromHours(1)
50 | });
51 | var mockContext = new DefaultHttpContext();
52 | mockContext.Request.Scheme = "https";
53 | var sut = new ExpectCTMiddleware(mockNext, options);
54 |
55 | await sut.Invoke(mockContext);
56 |
57 | Assert.Equal("max-age=3600, enforce, report-uri=\"https://reporting.com/report\"", headerValue);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/FeaturePolicyBuilderTests.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Builder;
2 | using Xunit;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
5 | {
6 | public class FeaturePolicyBuilderTests
7 | {
8 | [Fact]
9 | public void PaymentFromNowhere_SetsValue()
10 | {
11 | var builder = new FeaturePolicyBuilder();
12 |
13 | builder.AllowPayment.FromNowhere();
14 | var options = builder.BuildFeaturePolicyOptions();
15 | string headerValue = options.ToString();
16 |
17 | Assert.Equal("payment 'none'", headerValue);
18 | }
19 |
20 | [Fact]
21 | public void PaymentFromSelfAndGoogle_SetsValue()
22 | {
23 | var builder = new FeaturePolicyBuilder();
24 |
25 | builder.AllowPayment.FromSelf().From("https://www.google.com");
26 | var options = builder.BuildFeaturePolicyOptions();
27 | string headerValue = options.ToString();
28 |
29 | Assert.Equal("payment 'self' https://www.google.com", headerValue);
30 | }
31 |
32 | [Fact]
33 | public void PaymentFromNowhereAndFullScreenFromSelf_SetsValue()
34 | {
35 | var builder = new FeaturePolicyBuilder();
36 |
37 | builder.AllowPayment.FromNowhere();
38 | builder.AllowFullscreen.FromSelf();
39 | var options = builder.BuildFeaturePolicyOptions();
40 | string headerValue = options.ToString();
41 |
42 | Assert.Equal("fullscreen 'self'; payment 'none'", headerValue);
43 | }
44 |
45 | [Fact]
46 | public void OtherNewFeatureFromSelf_SetsValue()
47 | {
48 | var builder = new FeaturePolicyBuilder();
49 |
50 | builder.AllowOtherFeature("new-feature").FromSelf();
51 | var options = builder.BuildFeaturePolicyOptions();
52 | string headerValue = options.ToString();
53 |
54 | Assert.Equal("new-feature 'self'", headerValue);
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/FeaturePolicyMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy;
6 | using Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options;
7 | using Microsoft.AspNetCore.Http;
8 | using Microsoft.Extensions.Options;
9 | using Xunit;
10 |
11 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
12 | {
13 | public class FeaturePolicyMiddlewareTests
14 | {
15 | [Fact]
16 | public async Task FeaturePolicyHeaderAddedCorrectly()
17 | {
18 | string headerValue = null;
19 | RequestDelegate mockNext = (HttpContext ctx) =>
20 | {
21 | headerValue = ctx.Response.Headers["Feature-Policy"];
22 | return Task.CompletedTask;
23 | };
24 | var options = Options.Create(new FeaturePolicyOptions
25 | {
26 | Autoplay = new FeaturePolicyAutoplayOptions
27 | {
28 | AllowSelf = true
29 | },
30 | Payment = new FeaturePolicyPaymentOptions
31 | {
32 | AllowNone = true
33 | },
34 | Speaker = new FeaturePolicySpeakerOptions
35 | {
36 | AllowSelf = true,
37 | AllowedOrigins = new List
38 | {
39 | "https://site1",
40 | "https://site2"
41 | }
42 | },
43 | Other = new Dictionary
44 | {
45 | ["some-new-one"] = new FeaturePolicyOtherFeatureOptions("some-new-one")
46 | {
47 | AllowSelf = true
48 | }
49 | }
50 | });
51 | var mockContext = new DefaultHttpContext();
52 | var sut = new FeaturePolicyMiddleware(mockNext, options);
53 |
54 | await sut.Invoke(mockContext);
55 |
56 | Assert.Equal("speaker 'self' https://site1 https://site2; payment 'none'; autoplay 'self'; some-new-one 'self'", headerValue);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/FrameOptionsMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Joonasw.AspNetCore.SecurityHeaders.XFrameOptions;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.Extensions.Options;
5 | using Xunit;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
8 | {
9 | public class FrameOptionsMiddlewareTests
10 | {
11 | [Theory]
12 | [InlineData(XFrameOptionsOptions.XFrameOptionsValues.Deny, null, "DENY")]
13 | [InlineData(XFrameOptionsOptions.XFrameOptionsValues.SameOrigin, null, "SAMEORIGIN")]
14 | [InlineData(XFrameOptionsOptions.XFrameOptionsValues.AllowFrom, "https://site.com", "ALLOW-FROM https://site.com")]
15 | public async Task HeaderSetCorrectly(
16 | XFrameOptionsOptions.XFrameOptionsValues value,
17 | string allowedUrl,
18 | string expectedValue)
19 | {
20 | string headerValue = null;
21 | RequestDelegate mockNext = (HttpContext ctx) =>
22 | {
23 | headerValue = ctx.Response.Headers["X-Frame-Options"];
24 | return Task.CompletedTask;
25 | };
26 | var options = Options.Create(new XFrameOptionsOptions
27 | {
28 | HeaderValue = value,
29 | AllowFromUrl = allowedUrl
30 | });
31 | var mockContext = new DefaultHttpContext();
32 | var sut = new XFrameOptionsMiddleware(mockNext, options);
33 |
34 | await sut.Invoke(mockContext);
35 |
36 | Assert.Equal(expectedValue, headerValue);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/HpkpMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using Joonasw.AspNetCore.SecurityHeaders.Hpkp;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.Extensions.Options;
8 | using Xunit;
9 |
10 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
11 | {
12 | public class HpkpMiddlewareTests
13 | {
14 | [Fact]
15 | public async Task HpkpHeaderIsNotIncluded_WhenRequestHeadersAlreadyContainHpkpHeader()
16 | {
17 | RequestDelegate mockNext = (HttpContext ctx) =>
18 | {
19 | return Task.CompletedTask;
20 | };
21 | var options = Options.Create(new HpkpOptions()
22 | {
23 | MaxAgeSeconds = 60,
24 | Pins = new List{"a"}
25 | });
26 | var sut = new HpkpMiddleware(mockNext, options);
27 | var mockContext = new DefaultHttpContext();
28 | mockContext.Response.Headers.Append("Public-Key-Pins", "abc; max-age=60");
29 |
30 | await sut.Invoke(mockContext);
31 | //Invoke throws System.ArgumentException if it tries to add the header again
32 | }
33 |
34 | [Fact]
35 | public async Task HpkpHeaderIsNotIncluded_WhenRequestHeadersAlreadyContainHpkpReportOnlyHeader()
36 | {
37 | bool hpkpHeaderExists = true;
38 | RequestDelegate mockNext = (HttpContext ctx) =>
39 | {
40 | hpkpHeaderExists = ctx.Response.Headers.ContainsKey("Public-Key-Pins");
41 | return Task.CompletedTask;
42 | };
43 | var options = Options.Create(new HpkpOptions()
44 | {
45 | MaxAgeSeconds = 60,
46 | Pins = new List{"a"}
47 | });
48 | var sut = new HpkpMiddleware(mockNext, options);
49 | var mockContext = new DefaultHttpContext();
50 | mockContext.Response.Headers.Append("Public-Key-Pins-Report-Only", "abc; max-age=60");
51 |
52 | await sut.Invoke(mockContext);
53 |
54 | Assert.False(hpkpHeaderExists);
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/HstsMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using Joonasw.AspNetCore.SecurityHeaders.Hsts;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.Extensions.Options;
8 | using Xunit;
9 |
10 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
11 | {
12 | public class HstsMiddlewareTests
13 | {
14 | [Fact]
15 | public async Task HstsHeaderIsNotIncluded_WhenRequestHeadersAlreadyContainHstsHeader()
16 | {
17 | RequestDelegate mockNext = (HttpContext ctx) =>
18 | {
19 | return Task.CompletedTask;
20 | };
21 | var options = Options.Create(new HstsOptions()
22 | {
23 | Duration = TimeSpan.FromHours(1)
24 | });
25 | var sut = new HstsMiddleware(mockNext, options);
26 | var mockContext = new DefaultHttpContext();
27 | mockContext.Request.Scheme = "https";
28 | mockContext.Response.Headers.Append("Strict-Transport-Security", "max-age=31536000");
29 |
30 | await sut.Invoke(mockContext);
31 | //Invoke throws System.ArgumentException if it tries to add the header again
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/Joonasw.AspNetCore.SecurityHeaders.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0;net9.0
4 | false
5 |
6 |
7 |
8 |
9 |
10 | all
11 | runtime; build; native; contentfiles; analyzers; buildtransitive
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/ReferrerPolicyMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Joonasw.AspNetCore.SecurityHeaders.ReferrerPolicy;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Options;
6 | using Xunit;
7 |
8 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
9 | {
10 | public class ReferrerPolicyMiddlewareTests
11 | {
12 | [Fact]
13 | public async Task SetsHeaderCorrectlyWithSameOrigin()
14 | {
15 | string headerValue = null;
16 | RequestDelegate mockNext = (HttpContext ctx) =>
17 | {
18 | headerValue = ctx.Response.Headers["Referrer-Policy"];
19 | return Task.CompletedTask;
20 | };
21 | var options = Options.Create(new ReferrerPolicyOptions
22 | {
23 | PolicyValue = ReferrerPolicyOptions.ReferrerPolicyValue.SameOrigin
24 | });
25 | var mockContext = new DefaultHttpContext();
26 | var sut = new ReferrerPolicyMiddleware(mockNext, options);
27 |
28 | await sut.Invoke(mockContext);
29 |
30 | Assert.Equal("same-origin", headerValue);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/XFrameOptionsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Joonasw.AspNetCore.SecurityHeaders.XFrameOptions;
3 | using Xunit;
4 |
5 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
6 | {
7 | public class XFrameOptionsTests
8 | {
9 | [Fact]
10 | public void DenySetByDefault_ResultIsCorrect()
11 | {
12 | var options = new XFrameOptionsOptions();
13 |
14 | Assert.Equal(XFrameOptionsOptions.XFrameOptionsValues.Deny, options.HeaderValue);
15 | }
16 |
17 | [Fact]
18 | public void WhenSetAllowFromUrlIsRequired_ResultIsCorrect()
19 | {
20 | Assert.Throws(() =>
21 | {
22 | var options = new XFrameOptionsOptions(XFrameOptionsOptions.XFrameOptionsValues.AllowFrom);
23 | });
24 | }
25 |
26 | [Fact]
27 | public void WhenSetAllowFromUrlDoesNotThrow_ResultIsCorrect()
28 | {
29 | var options = Record.Exception(() =>
30 | new XFrameOptionsOptions(XFrameOptionsOptions.XFrameOptionsValues.AllowFrom, "https://google.com"));
31 |
32 | Assert.Null(options);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/XXssProtectionTests.cs:
--------------------------------------------------------------------------------
1 | using Joonasw.AspNetCore.SecurityHeaders.XXssProtection;
2 | using Xunit;
3 |
4 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
5 | {
6 | public class XXssProtectionTests
7 | {
8 | [Fact]
9 | public void EnabledAndBlockedByDefault_ResultIsCorrect()
10 | {
11 | var options = new XXssProtectionOptions();
12 |
13 | Assert.True(options.EnableProtection);
14 | Assert.True(options.EnableAttackBlock);
15 | }
16 |
17 | [Fact]
18 | public void EnabledAndBlockedAreOffWhenSet_ResultIsCorrect()
19 | {
20 | var options = new XXssProtectionOptions(false, false);
21 |
22 | Assert.False(options.EnableProtection);
23 | Assert.False(options.EnableAttackBlock);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test/Joonasw.AspNetCore.SecurityHeaders.Tests/XssProtectionMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Joonasw.AspNetCore.SecurityHeaders.XXssProtection;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.Extensions.Options;
5 | using Xunit;
6 |
7 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests
8 | {
9 | public class XssProtectionMiddlewareTests
10 | {
11 | [Theory]
12 | [InlineData(false, false, "0")]
13 | [InlineData(false, true, "1")]
14 | [InlineData(true, true, "1; mode=block")]
15 | public async Task HeaderSetCorrectly(bool enableBlock, bool enableProtection, string expectedValue)
16 | {
17 | string headerValue = null;
18 | RequestDelegate mockNext = (HttpContext ctx) =>
19 | {
20 | headerValue = ctx.Response.Headers["X-XSS-Protection"];
21 | return Task.CompletedTask;
22 | };
23 | var options = Options.Create(new XXssProtectionOptions
24 | {
25 | EnableAttackBlock = enableBlock,
26 | EnableProtection = enableProtection
27 | });
28 | var mockContext = new DefaultHttpContext();
29 | var sut = new XXssProtectionMiddleware(mockNext, options);
30 |
31 | await sut.Invoke(mockContext);
32 |
33 | Assert.Equal(expectedValue, headerValue);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------