Skip to content

Commit 2bb5769

Browse files
Merge pull request #206 from GeekInTheNorth/release/v2.6.0
Release 2.6.0
2 parents 145e96b + 65a7f73 commit 2bb5769

58 files changed

Lines changed: 2901 additions & 232 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Images/TabList.png

389 Bytes
Loading

Images/ToolsTab.png

36.4 KB
Loading

README.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ Stott Security is a free to use module, however if you want to show your support
1616

1717
## Interface
1818

19-
The user interface is split into 7 tabs:
19+
The user interface is split into 8 tabs:
2020

2121
- Tabs 1 to 3 focus on the Content Security Policy.
2222
- Tab 4 focuses on the Cross Origin Resource Sharing functionality.
2323
- Tab 5 focuses on miscellaneous response headers.
2424
- Tab 6 provides you with a preview of the headers the module will generate.
2525
- Tab 7 provides you with the audit history for all changes made within the module.
26+
- Tab 8 provides you with additional tools to import and export settings.
2627

2728
![CSP Settings Tab](/Images/TabList.png)
2829

@@ -87,15 +88,15 @@ Recommendations:
8788

8889
The CSP Violations tab is the forth tab dedicated to managing your Content Security Policy. This tab requires a developer to add the reporting view component to the website (read more below under CSP Reporting). When the plugin receives a report of a violation of the Content Security Policy, it will make a record of the third party source and what directive was violated. This is then presented to the user so that that can see how often a violation is happening and when it last happened. A handy **Create CSP Entry** button allows the user to quickly merge the violated source and directive into the Content Security Policy.
8990

90-
**Updated in version 2.0.0.0 to include source and directive filtering.**
91+
**Updated in version 2.0.0 to include source and directive filtering.**
9192

9293
![CSP Violations Tab](/Images/CspViolationTab.png)
9394

9495
### Cross Origin Resource Sharing
9596

96-
**New in version 2.0.0.0**
97+
**New in version 2.0.0**
9798

98-
The CORS tab is new in version 2.0.0.0 and allows the user to configure the Cross-Origin Resource Sharing headers for the website. This is used to grant permissions to third party websites to consume APIs and content from your website. As trends have moved towards headless and hybrid solutions, controlling your CORS headers can be essential to allowing hybrid solutions to work.
99+
The CORS tab is new in version 2.0.0 and allows the user to configure the Cross-Origin Resource Sharing headers for the website. This is used to grant permissions to third party websites to consume APIs and content from your website. As trends have moved towards headless and hybrid solutions, controlling your CORS headers can be essential to allowing hybrid solutions to work.
99100

100101
![CORS Tab](/Images/CorsTab.png)
101102

@@ -113,7 +114,7 @@ The CORS tab is new in version 2.0.0.0 and allows the user to configure the Cros
113114

114115
The Security Headers tab is a catch all for many simple security headers. Some of these are deprecated by the existance of a Content Security Policy, but may still be required for older browsers which do not support a Content Security Policy.
115116

116-
![CORS Tab](/Images/SecurityHeadersTab1.png)
117+
![Security Headers Tab](/Images/SecurityHeadersTab1.png)
117118

118119
| Setting | Default | Recommended |
119120
|---------|---------|-------------|
@@ -124,15 +125,15 @@ The Security Headers tab is a catch all for many simple security headers. Some
124125

125126
Please note that the X-XSS-Protection header is classed as non-standard and deprecated by the Content Security Policy and in some implementations can introduce vulnerabilities. This option may be removed in future. You can read more here: [X-XSS-Protection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection)
126127

127-
![CORS Tab](/Images/SecurityHeadersTab2.png)
128+
![Security Headers Tab](/Images/SecurityHeadersTab2.png)
128129

129130
| Setting | Default | Recommended |
130131
|---------|---------|-------------|
131132
| Include Cross Origin Embedder Policy (Cross-Origin-Embedder-Policy) | disabled | Requires CORP |
132133
| Include Cross Origin Opener Policy (Cross-Origin-Opener-Policy) | disabled | Same Origin |
133134
| Include Cross Origin Resource Policy (Cross-Origin-Resource-Policy) | disabled | Same Origin |
134135

135-
![CORS Tab](/Images/SecurityHeadersTab3.png)
136+
![Security Headers Tab](/Images/SecurityHeadersTab3.png)
136137

137138
| Setting | Default | Recommended |
138139
|---------|---------|-------------|
@@ -144,7 +145,7 @@ Please note that the X-XSS-Protection header is classed as non-standard and depr
144145

145146
The preview screen will show you the compiled headers that will be returned as part of any GET request. This does not include CORS headers as these vary based on request or may only be exposed as part of a pre-flight request by the browser.
146147

147-
**New in version 2.2.0.0**
148+
**New in version 2.2.0**
148149

149150
![CORS Tab](/Images/PreviewTab.png)
150151

@@ -156,6 +157,14 @@ Please note that this module does not contain any code that clears down the audi
156157

157158
![CORS Tab](/Images/AuditTab.png)
158159

160+
## Tools
161+
162+
The tools tab introduces the ability to import and export your entire configuration. The Export function will provide you with a JSON file of all of your configuration settings. The Import function will require the same JSON file structure and will validate the content of the configuration before applying it.
163+
164+
**New in version 2.6.0**
165+
166+
![Tools Tab](/Images/ToolsTab.png)
167+
159168
## Configuration
160169

161170
After pulling in a reference to the Stott.Security.Optimizely project, you only need to ensure the following lines are added to the startup class of your solution:

Sample/OptimizelyTwelveTest/Startup.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
namespace OptimizelyTwelveTest;
22

3+
using System;
4+
35
using EPiServer.Cms.UI.AspNetIdentity;
46
using EPiServer.Scheduler;
57
using EPiServer.Web.Routing;
68

79
using Microsoft.AspNetCore.Builder;
810
using Microsoft.AspNetCore.Hosting;
11+
using Microsoft.AspNetCore.Http;
912
using Microsoft.Extensions.DependencyInjection;
1013
using Microsoft.Extensions.Hosting;
1114
using Microsoft.Net.Http.Headers;
@@ -38,7 +41,6 @@ public void ConfigureServices(IServiceCollection services)
3841
});
3942
}
4043

41-
services.AddRazorPages();
4244
services.AddCmsAspNetIdentity<ApplicationUser>();
4345

4446
// Various serialization formats.
@@ -107,15 +109,15 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
107109
app.UseResponseCaching();
108110
app.Use(async (context, next) =>
109111
{
110-
if (context.Request is not null)
112+
if (context.Request is not null && !context.Request.Path.Value.StartsWith("/episerver/", StringComparison.OrdinalIgnoreCase))
111113
{
112114
if (context.Response.Headers.ContainsKey(HeaderNames.CacheControl))
113115
{
114116
context.Response.Headers[HeaderNames.CacheControl] = "no-cache, max-age=0";
115117
}
116118
else
117119
{
118-
context.Response.Headers.Add(HeaderNames.CacheControl, "no-cache, max-age=0");
120+
context.Response.Headers.Append(HeaderNames.CacheControl, "no-cache, max-age=0");
119121
}
120122
}
121123

@@ -133,7 +135,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
133135
app.UseEndpoints(endpoints =>
134136
{
135137
endpoints.MapContent();
136-
endpoints.MapRazorPages();
138+
endpoints.MapControllers();
137139
});
138140
}
139141
}

Sample/OptimizelyTwelveTest/appsettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"urls": "http://*:8000/;https://*:8001/;",
1111
"AllowedHosts": "*",
1212
"ConnectionStrings": {
13-
"EPiServerDB": "Server=.\\SQLEXPRESS;Database=EPiServerDB_565f2030;User Id=EPiServerDB_0aa972a3User;Password=R4&leNE*JIjF4bFA@3d^O42hN;MultipleActiveResultSets=True;trustServerCertificate=true"
13+
"EPiServerDB": "Server=.\\SQLEXPRESS;Database=EPiServerDB_565f2030;User Id=EPiServerDB_0aa972a3User;Password=R4&leNE*JIjF4bFA@3d^O42hN;MultipleActiveResultSets=False;trustServerCertificate=true"
1414
},
1515
"EPiServer": {
1616
"Find": {

src/Stott.Security.Optimizely.Test/Features/AllowList/WhitelistServiceTests.cs

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
using Stott.Security.Optimizely.Features.AllowList;
1616
using Stott.Security.Optimizely.Features.Caching;
1717
using Stott.Security.Optimizely.Features.Permissions.Service;
18-
using Stott.Security.Optimizely.Features.Settings.Repository;
18+
using Stott.Security.Optimizely.Features.Settings.Service;
1919
using Stott.Security.Optimizely.Test.TestCases;
2020

2121
[TestFixture]
2222
public class AllowListServiceTests
2323
{
24-
private Mock<ICspSettingsRepository> _mockSettingsRepository;
24+
private Mock<ICspSettingsService> _mockSettingsService;
2525

2626
private Mock<IAllowListRepository> _mockRepository;
2727

@@ -36,14 +36,14 @@ public class AllowListServiceTests
3636
[SetUp]
3737
public void SetUp()
3838
{
39-
_mockSettingsRepository = new Mock<ICspSettingsRepository>();
39+
_mockSettingsService = new Mock<ICspSettingsService>();
4040
_mockRepository = new Mock<IAllowListRepository>();
4141
_mockCspPermissionService = new Mock<ICspPermissionService>();
4242
_mockLogger = new Mock<ILogger<IAllowListService>>();
4343
_mockCacheWrapper = new Mock<ICacheWrapper>();
4444

4545
_allowListService = new AllowListService(
46-
_mockSettingsRepository.Object,
46+
_mockSettingsService.Object,
4747
_mockRepository.Object,
4848
_mockCspPermissionService.Object,
4949
_mockCacheWrapper.Object,
@@ -72,7 +72,7 @@ public void Constructor_GivenANullAllowListRepositoryThenAnArgumentNullException
7272
Assert.Throws<ArgumentNullException>(() =>
7373
{
7474
new AllowListService(
75-
_mockSettingsRepository.Object,
75+
_mockSettingsService.Object,
7676
null,
7777
_mockCspPermissionService.Object,
7878
_mockCacheWrapper.Object,
@@ -87,7 +87,7 @@ public void Constructor_GivenANullCspPermissionServiceThenAnArgumentNullExceptio
8787
Assert.Throws<ArgumentNullException>(() =>
8888
{
8989
new AllowListService(
90-
_mockSettingsRepository.Object,
90+
_mockSettingsService.Object,
9191
_mockRepository.Object,
9292
null,
9393
_mockCacheWrapper.Object,
@@ -102,7 +102,7 @@ public void Constructor_GivenANullCacheWrapperThenAnArgumentNullExceptionIsThrow
102102
Assert.Throws<ArgumentNullException>(() =>
103103
{
104104
new AllowListService(
105-
_mockSettingsRepository.Object,
105+
_mockSettingsService.Object,
106106
_mockRepository.Object,
107107
_mockCspPermissionService.Object,
108108
null,
@@ -117,7 +117,7 @@ public void Constructor_GivenAValidAllowListOptionsThenAnExceptionIsNotThrown()
117117
Assert.DoesNotThrow(() =>
118118
{
119119
new AllowListService(
120-
_mockSettingsRepository.Object,
120+
_mockSettingsService.Object,
121121
_mockRepository.Object,
122122
_mockCspPermissionService.Object,
123123
_mockCacheWrapper.Object,
@@ -130,8 +130,8 @@ public void Constructor_GivenAValidAllowListOptionsThenAnExceptionIsNotThrown()
130130
public async Task AddFromAllowListToCsp_DoesNotProcessTheAllowListWhenAllowListOptionsIsDisabled(string violationSource, string violationDirective)
131131
{
132132
// Arrange
133-
_mockSettingsRepository.Setup(x => x.GetAsync())
134-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = false });
133+
_mockSettingsService.Setup(x => x.GetAsync())
134+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = false });
135135

136136
// Act
137137
await _allowListService.AddFromAllowListToCspAsync(violationSource, violationDirective);
@@ -146,8 +146,8 @@ public async Task AddFromAllowListToCsp_DoesNotProcessTheAllowListWhenAllowListO
146146
public async Task AddFromAllowListToCsp_DoesNotProcessTheAllowListWhenViolationSourceOrDirectiveIsNullOrEmpty(string violationSource, string violationDirective)
147147
{
148148
// Arrange
149-
_mockSettingsRepository.Setup(x => x.GetAsync())
150-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
149+
_mockSettingsService.Setup(x => x.GetAsync())
150+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
151151

152152
// Act
153153
await _allowListService.AddFromAllowListToCspAsync(violationSource, violationDirective);
@@ -162,8 +162,8 @@ public async Task AddFromAllowListToCsp_DoesNotProcessTheAllowListWhenViolationS
162162
public async Task AddFromAllowListToCsp_WhenGivenAValidSourceAndDomainAndAllowListIsEnabled_ThenTheAllowListIsRetrieved(string violationSource, string violationDirective)
163163
{
164164
// Arrange
165-
_mockSettingsRepository.Setup(x => x.GetAsync())
166-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
165+
_mockSettingsService.Setup(x => x.GetAsync())
166+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
167167

168168
_mockRepository.Setup(x => x.GetAllowListAsync(It.IsAny<string>()))
169169
.ReturnsAsync(new AllowListCollection(new List<AllowListEntry>(0)));
@@ -180,8 +180,8 @@ public async Task AddFromAllowListToCsp_WhenGivenAValidSourceAndDomainAndAllowLi
180180
public async Task AddFromAllowListToCsp_WhenGivenAValidSourceAndDomainAndAnEmptyAllowList_ThenTheCspIsNotUpdated(string violationSource, string violationDirective)
181181
{
182182
// Arrange
183-
_mockSettingsRepository.Setup(x => x.GetAsync())
184-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
183+
_mockSettingsService.Setup(x => x.GetAsync())
184+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
185185

186186
_mockRepository.Setup(x => x.GetAllowListAsync(It.IsAny<string>()))
187187
.ReturnsAsync(new AllowListCollection(new List<AllowListEntry>(0)));
@@ -209,8 +209,8 @@ public async Task AddFromAllowListToCsp_WhenGivenAValidSourceAndDomainThatAreNot
209209
};
210210
var allowListCollection = new AllowListCollection(allowListEntries);
211211

212-
_mockSettingsRepository.Setup(x => x.GetAsync())
213-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
212+
_mockSettingsService.Setup(x => x.GetAsync())
213+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
214214

215215
_mockRepository.Setup(x => x.GetAllowListAsync(It.IsAny<string>()))
216216
.ReturnsAsync(allowListCollection);
@@ -243,8 +243,8 @@ public async Task AddFromAllowListToCsp_WhenGivenAValidSourceAndDomainThatAreOnT
243243
};
244244
var allowListCollection = new AllowListCollection(allowListEntries);
245245

246-
_mockSettingsRepository.Setup(x => x.GetAsync())
247-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
246+
_mockSettingsService.Setup(x => x.GetAsync())
247+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
248248

249249
_mockRepository.Setup(x => x.GetAllowListAsync(It.IsAny<string>()))
250250
.ReturnsAsync(allowListCollection);
@@ -262,8 +262,8 @@ public async Task AddFromAllowListToCsp_WhenGivenAValidSourceAndDomainThatAreOnT
262262
public async Task IsOnAllowList_ReturnsFalseWhenAllowListOptionsIsDisabled(string violationSource, string violationDirective)
263263
{
264264
// Arrange
265-
_mockSettingsRepository.Setup(x => x.GetAsync())
266-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = false });
265+
_mockSettingsService.Setup(x => x.GetAsync())
266+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = false });
267267

268268
// Act
269269
var isOnAllowList = await _allowListService.IsOnAllowListAsync(violationSource, violationDirective);
@@ -277,8 +277,8 @@ public async Task IsOnAllowList_ReturnsFalseWhenAllowListOptionsIsDisabled(strin
277277
public async Task IsOnAllowList_ReturnsFalseWhenViolationSourceOrDirectiveIsNullOrEmpty(string violationSource, string violationDirective)
278278
{
279279
// Arrange
280-
_mockSettingsRepository.Setup(x => x.GetAsync())
281-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
280+
_mockSettingsService.Setup(x => x.GetAsync())
281+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
282282

283283
// Act
284284
var isOnAllowList = await _allowListService.IsOnAllowListAsync(violationSource, violationDirective);
@@ -292,8 +292,8 @@ public async Task IsOnAllowList_ReturnsFalseWhenViolationSourceOrDirectiveIsNull
292292
public async Task IsOnAllowList_DoesNotAttemptToGetAAllowListWhenAllowListOptionsIsDisabled(string violationSource, string violationDirective)
293293
{
294294
// Arrange
295-
_mockSettingsRepository.Setup(x => x.GetAsync())
296-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = false });
295+
_mockSettingsService.Setup(x => x.GetAsync())
296+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = false });
297297

298298
// Act
299299
var isOnAllowList = await _allowListService.IsOnAllowListAsync(violationSource, violationDirective);
@@ -307,8 +307,8 @@ public async Task IsOnAllowList_DoesNotAttemptToGetAAllowListWhenAllowListOption
307307
public async Task IsOnAllowList_DoesNotAttemptToGetAAllowListWhenViolationSourceOrDirectiveIsNullOrEmpty(string violationSource, string violationDirective)
308308
{
309309
// Arrange
310-
_mockSettingsRepository.Setup(x => x.GetAsync())
311-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
310+
_mockSettingsService.Setup(x => x.GetAsync())
311+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
312312

313313
// Act
314314
var isOnAllowList = await _allowListService.IsOnAllowListAsync(violationSource, violationDirective);
@@ -322,8 +322,8 @@ public async Task IsOnAllowList_DoesNotAttemptToGetAAllowListWhenViolationSource
322322
public async Task IsOnAllowList_WhenGivenAValidSourceAndDomainAndAllowListIsEnabled_ThenTheAllowListIsRetrieved(string violationSource, string directive)
323323
{
324324
// Arrange
325-
_mockSettingsRepository.Setup(x => x.GetAsync())
326-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
325+
_mockSettingsService.Setup(x => x.GetAsync())
326+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
327327

328328
// Act
329329
var isOnAllowList = await _allowListService.IsOnAllowListAsync(violationSource, directive);
@@ -349,8 +349,8 @@ public async Task IsOnAllowList_WhenDomainMatchesAAllowListEntry_ThenReturnsTrue
349349
};
350350
var allowListCollection = new AllowListCollection(allowListEntries);
351351

352-
_mockSettingsRepository.Setup(x => x.GetAsync())
353-
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
352+
_mockSettingsService.Setup(x => x.GetAsync())
353+
.ReturnsAsync(new CspSettings { IsAllowListEnabled = true });
354354

355355
_mockRepository.Setup(x => x.GetAllowListAsync(It.IsAny<string>()))
356356
.Returns(Task.FromResult(allowListCollection));

src/Stott.Security.Optimizely.Test/Features/Cors/Provider/CustomCorsPolicyProviderTests.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System;
44
using System.Threading.Tasks;
55

6+
using EPiServer.ServiceLocation;
7+
68
using JetBrains.Annotations;
79

810
using Microsoft.AspNetCore.Cors.Infrastructure;
@@ -29,6 +31,8 @@ public sealed class CustomCorsPolicyProviderTests
2931

3032
private Mock<HttpContext> _mockHttpContext;
3133

34+
private Mock<IServiceProvider> _mockServiceProvider;
35+
3236
private CustomCorsPolicyProvider _provider;
3337

3438
[SetUp]
@@ -39,6 +43,11 @@ public void SetUp()
3943
_mockService = new Mock<ICorsSettingsService>();
4044
_mockService.Setup(x => x.GetAsync()).ReturnsAsync(new CorsConfiguration());
4145

46+
_mockServiceProvider = new Mock<IServiceProvider>();
47+
_mockServiceProvider.Setup(x => x.GetService(typeof(ICorsSettingsService))).Returns(_mockService.Object);
48+
49+
ServiceLocator.SetServiceProvider(_mockServiceProvider.Object);
50+
4251
_mockHttpContext = new Mock<HttpContext>();
4352

4453
var corsOptions = new CorsOptions();
@@ -52,7 +61,7 @@ public void SetUp()
5261
_mockOptions = new Mock<IOptions<CorsOptions>>();
5362
_mockOptions.Setup(x => x.Value).Returns(corsOptions);
5463

55-
_provider = new CustomCorsPolicyProvider(_mockCache.Object, _mockService.Object, _mockOptions.Object);
64+
_provider = new CustomCorsPolicyProvider(_mockCache.Object, _mockOptions.Object);
5665
}
5766

5867
[Test]

0 commit comments

Comments
 (0)