Skip to content

Commit af255b6

Browse files
committed
File upload vulnerability fixes
1 parent df79564 commit af255b6

File tree

8 files changed

+141
-2
lines changed

8 files changed

+141
-2
lines changed

LearningHub.Nhs.WebUI/Controllers/Api/ResourceController.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,33 @@ public async Task<ActionResult> RecordExternalReferenceUserAgreementAsync([FromB
258258
}
259259
}
260260

261+
/// <summary>
262+
/// The RecordExternalReferenceUserAgreementAsync.
263+
/// </summary>
264+
/// <param name="model">model.</param>
265+
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
266+
[HttpPost]
267+
[Route(" CreateResourceVersionValidationResult")]
268+
public async Task<ActionResult> CreateResourceVersionValidationResultAsync([FromBody] ResourceVersionValidationResultViewModel model)
269+
{
270+
await this.resourceService.CreateResourceVersionValidationResultAsync(model);
271+
return this.Ok();
272+
}
273+
274+
/// <summary>
275+
/// The RecordExternalReferenceUserAgreementAsync.
276+
/// </summary>
277+
/// <param name="filename">model.</param>
278+
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
279+
[HttpPost]
280+
[Route(" CreateResourceVersionValidationResults")]
281+
public async Task<ActionResult> CreateResourceVersionValidationResultAsync([FromBody] string filename)
282+
{
283+
ResourceVersionValidationResultViewModel model = new ResourceVersionValidationResultViewModel();
284+
await this.resourceService.CreateResourceVersionValidationResultAsync(model);
285+
return this.Ok();
286+
}
287+
261288
/// <summary>
262289
/// The GetHeaderById.
263290
/// </summary>

LearningHub.Nhs.WebUI/Interfaces/IResourceService.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,13 @@ public interface IResourceService
269269
/// <returns>The <see cref="Task{LearningHubValidationResult}"/>.</returns>
270270
Task<LearningHubValidationResult> CreateResourceVersionProviderAsync(ResourceVersionProviderViewModel model);
271271

272+
/// <summary>
273+
/// Creates resource version validation results corresponding to the value in the corresponding input view model.
274+
/// </summary>
275+
/// <param name="validationResultViewModel">Details of the validation results.</param>
276+
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
277+
Task CreateResourceVersionValidationResultAsync(ResourceVersionValidationResultViewModel validationResultViewModel);
278+
272279
/// <summary>
273280
/// Delete resource version provider.
274281
/// </summary>

LearningHub.Nhs.WebUI/Scripts/vuesrc/helpers/fileUpload.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const MAX_FILE_SIZE = 10 * 1000 * 1000 * 1000; // 10GB
4949

5050
// This list should correspond to the disallowed extensions contained in the FileType table
5151
const BLOCKED_FILE_EXTENSIONS = ['.app', '.asp', '.aspx', '.dll', '.dmg', '.exe', '.flv', '.f4v', '.js', '.jsp',
52-
'.php', '.shtm', '.shtml', '.swf','.webm'];
52+
'.php', '.shtm', '.shtml', '.swf', '.webm', '.bat', '.cmd', '.vbs', '.msi', '.pif', '.sh', '.tar', '.gz', '.7z', '.rar', '.sys', '.bak', '.iso', '.torrent'];
5353

5454
const IMAGE_FILE_EXTENSIONS = ['.apng', '.avif', '.bmp', '.cur', '.gif', '.ico', '.jfif', '.jpeg', '.jpg', '.pjp',
5555
'.pjpeg', '.png', '.psd', '.svg', '.tif', '.tiff', '.webp'];

LearningHub.Nhs.WebUI/Services/ContributeService.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
using LearningHub.Nhs.WebUI.Models.Contribute;
1818
using Microsoft.AspNetCore.Http;
1919
using Microsoft.Extensions.Logging;
20+
using Microsoft.VisualBasic;
21+
using MK.IO.Models;
2022
using Newtonsoft.Json;
2123

2224
/// <summary>
@@ -38,7 +40,7 @@ public class ContributeService : BaseService<ContributeService>, IContributeServ
3840
/// <param name="mediaService">MKIO media service.</param>
3941
/// <param name="learningHubHttpClient">Learning hub http client.</param>
4042
/// <param name="logger">Logger.</param>
41-
public ContributeService(IFileService fileService, IResourceService resourceService, IAzureMediaService azureMediaService, ILearningHubHttpClient learningHubHttpClient, ILogger<ContributeService> logger, IAzureMediaService mediaService)
43+
public ContributeService(IFileService fileService, IResourceService resourceService, IAzureMediaService azureMediaService, ILearningHubHttpClient learningHubHttpClient, ILogger<ContributeService> logger, IAzureMediaService mediaService)
4244
: base(learningHubHttpClient, logger)
4345
{
4446
this.fileService = fileService;
@@ -502,6 +504,31 @@ public async Task<FileUploadResult> ProcessResourceFileAsync(int resourceVersion
502504

503505
if ((fileType == null) || (fileType != null && fileType.NotAllowed) || file.Length <= 0)
504506
{
507+
// Define dangerous file extensions
508+
string[] dangerousExtensions = { ".exe", ".dll", ".bat", ".js", ".vbs", ".sh", ".ps1" };
509+
if (dangerousExtensions.Any(ext => file.FileName.EndsWith(ext, StringComparison.OrdinalIgnoreCase)))
510+
{
511+
var error = $"A potentially harmful file has been detected and blocked: {file.FileName}.";
512+
var validationDetail = new ResourceVersionValidationResultViewModel
513+
{
514+
ResourceVersionId = resourceVersionId,
515+
Success = false,
516+
Details = string.Empty,
517+
AmendUserId = currentUserId,
518+
ResourceVersionValidationRuleResultViewModels = new[]
519+
{
520+
new ResourceVersionValidationRuleResultViewModel
521+
{
522+
ResourceTypeValidationRuleEnum = ResourceTypeValidationRuleEnum.HtmlResource_RootIndexPresent,
523+
Success = false,
524+
Details = error,
525+
},
526+
}.ToList(),
527+
};
528+
529+
await this.resourceService.CreateResourceVersionValidationResultAsync(validationDetail);
530+
}
531+
505532
return new FileUploadResult()
506533
{
507534
FileName = file.FileName,

LearningHub.Nhs.WebUI/Services/ResourceService.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,38 @@ public async Task<LearningHubValidationResult> CreateResourceVersionProviderAsyn
11951195
return apiResponse.ValidationResult;
11961196
}
11971197

1198+
/// <summary>
1199+
/// Creates resource version validation results corresponding to the value in the corresponding input view model.
1200+
/// </summary>
1201+
/// <param name="validationResultViewModel">Details of the validation results.</param>
1202+
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
1203+
public async Task CreateResourceVersionValidationResultAsync(ResourceVersionValidationResultViewModel validationResultViewModel)
1204+
{
1205+
ApiResponse apiResponse = null;
1206+
var json = JsonConvert.SerializeObject(validationResultViewModel);
1207+
var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
1208+
1209+
var client = await this.LearningHubHttpClient.GetClientAsync();
1210+
1211+
var request = $"Resource/CreateResourceVersionValidationResult";
1212+
var response = await client.PostAsync(request, stringContent).ConfigureAwait(false);
1213+
1214+
if (response.IsSuccessStatusCode)
1215+
{
1216+
var result = response.Content.ReadAsStringAsync().Result;
1217+
apiResponse = JsonConvert.DeserializeObject<ApiResponse>(result);
1218+
1219+
if (!apiResponse.Success)
1220+
{
1221+
throw new Exception("save failed!");
1222+
}
1223+
}
1224+
else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || response.StatusCode == System.Net.HttpStatusCode.Forbidden)
1225+
{
1226+
throw new Exception("AccessDenied");
1227+
}
1228+
}
1229+
11981230
/// <summary>
11991231
/// The delete resource version provider.
12001232
/// </summary>

WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.sqlproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@
536536
<None Include="Scripts\Post-Deploy\Scripts\UpdateFileTypes.sql" />
537537
<None Include="Scripts\Post-Deploy\Scripts\AddDigitalLearningSolutionsExternalSystem.sql" />
538538
<Build Include="Stored Procedures\Activity\GetAssessmentActivityCompletionPercentage.sql" />
539+
<None Include="Scripts\LH Database Scripts\CreateResourceTypeValidationRule.sql" />
539540
</ItemGroup>
540541
<ItemGroup>
541542
<None Include="Scripts\Pre-Deploy\Scripts\Card5766_AuthorTableChanges.PreDeployment.sql" />
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
IF NOT EXISTS(SELECT 'X' FROM [resources].[ResourceTypeValidationRule] WHERE Id = 7)
2+
BEGIN
3+
INSERT INTO [resources].[ResourceTypeValidationRule]
4+
([Id]
5+
,[ResourceTypeId]
6+
,[Description]
7+
,[Deleted]
8+
,[CreateUserId]
9+
,[CreateDate]
10+
,[AmendUserId]
11+
,[AmendDate])
12+
VALUES
13+
(7
14+
,9
15+
,'GenericFile_ValidZipFile'
16+
,0
17+
,4
18+
,SYSDATETIMEOFFSET()
19+
,4
20+
,SYSDATETIMEOFFSET())
21+
END
22+
GO
23+
IF NOT EXISTS(SELECT 'X' FROM [resources].[ResourceTypeValidationRule] WHERE Id = 8)
24+
BEGIN
25+
INSERT INTO [resources].[ResourceTypeValidationRule]
26+
([Id]
27+
,[ResourceTypeId]
28+
,[Description]
29+
,[Deleted]
30+
,[CreateUserId]
31+
,[CreateDate]
32+
,[AmendUserId]
33+
,[AmendDate])
34+
VALUES
35+
(8
36+
,9
37+
,'GenericFile_ValidFileTypes'
38+
,0
39+
,4
40+
,SYSDATETIMEOFFSET()
41+
,4
42+
,SYSDATETIMEOFFSET())
43+
END
44+
GO

WebAPI/LearningHub.Nhs.Database/Scripts/Post-Deploy/Script.PostDeployment.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,5 @@ UPDATE [resources].[ResourceVersion] SET CertificateEnabled = 0 WHERE VersionSta
8686
:r .\Scripts\AttributeData.sql
8787
:r .\Scripts\PPSXFileType.sql
8888
:r .\Scripts\UpdateFileTypes.sql
89+
:r .\Scripts\CreateResourceTypeValidationRule.sql
8990

0 commit comments

Comments
 (0)