Skip to content

Commit 513f39f

Browse files
MigaroezZeegaan
andauthored
Prevent template circular references (#15907)
* Added a simpel circular reference check to templates * Upgraded detecting to all levels * Rename method and add comment to clarify why we have 2 similar methods * Apply suggestions from code review Co-authored-by: Nikolaj Geisle <[email protected]> --------- Co-authored-by: Sven Geusens <[email protected]> Co-authored-by: Nikolaj Geisle <[email protected]>
1 parent acae5f2 commit 513f39f

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

src/Umbraco.Cms.Api.Management/Controllers/Template/TemplateControllerBase.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ protected IActionResult TemplateOperationStatusResult(TemplateOperationStatus st
3030
.WithTitle("Duplicate alias")
3131
.WithDetail("A template with that alias already exists.")
3232
.Build()),
33+
TemplateOperationStatus.CircularMasterTemplateReference => BadRequest(problemDetailsBuilder
34+
.WithTitle("Invalid master template")
35+
.WithDetail("The master template referenced in the template leads to a circular reference.")
36+
.Build()),
3337
_ => StatusCode(StatusCodes.Status500InternalServerError, problemDetailsBuilder
3438
.WithTitle("Unknown template operation status.")
3539
.Build()),

src/Umbraco.Core/Services/OperationStatus/TemplateOperationStatus.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ public enum TemplateOperationStatus
77
InvalidAlias,
88
DuplicateAlias,
99
TemplateNotFound,
10-
MasterTemplateNotFound
10+
MasterTemplateNotFound,
11+
CircularMasterTemplateReference
1112
}

src/Umbraco.Core/Services/TemplateService.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,14 @@ private async Task<Attempt<ITemplate, TemplateOperationStatus>> SaveAsync(ITempl
242242
return Attempt.FailWithStatus(TemplateOperationStatus.MasterTemplateNotFound, template);
243243
}
244244

245+
// detect circular references
246+
if (masterTemplateAlias is not null
247+
&& masterTemplate is not null
248+
&& await HasCircularReference(masterTemplateAlias, template, masterTemplate))
249+
{
250+
return Attempt.FailWithStatus(TemplateOperationStatus.CircularMasterTemplateReference, template);
251+
}
252+
245253
await SetMasterTemplateAsync(template, masterTemplate, userKey);
246254

247255
EventMessages eventMessages = EventMessagesFactory.Get();
@@ -413,4 +421,42 @@ private void Audit(AuditType type, int userId, int objectId, string? entityType)
413421

414422
private static bool IsValidAlias(string alias)
415423
=> alias.IsNullOrWhiteSpace() == false && alias.Length <= 255;
424+
425+
private async Task<bool> HasCircularReference(string parsedMasterTemplateAlias, ITemplate template, ITemplate masterTemplate)
426+
{
427+
// quick check without extra DB calls as we already have both templates
428+
if (parsedMasterTemplateAlias.IsNullOrWhiteSpace() is false
429+
&& masterTemplate.MasterTemplateAlias is not null
430+
&& masterTemplate.MasterTemplateAlias.Equals(template.Alias))
431+
{
432+
return true;
433+
}
434+
435+
var processedTemplates = new List<ITemplate> { template, masterTemplate };
436+
return await HasRecursiveCircularReference(processedTemplates, masterTemplate.MasterTemplateAlias);
437+
}
438+
439+
private async Task<bool> HasRecursiveCircularReference(List<ITemplate> referencedTemplates, string? masterTemplateAlias)
440+
{
441+
if (masterTemplateAlias is null)
442+
{
443+
return false;
444+
}
445+
446+
if (referencedTemplates.Any(template => template.Alias.Equals(masterTemplateAlias)))
447+
{
448+
return true;
449+
}
450+
451+
ITemplate? masterTemplate = await GetAsync(masterTemplateAlias);
452+
if (masterTemplate is null)
453+
{
454+
// this should not happen unless somebody manipulated the data by hand as this function is only called between persisted items
455+
return false;
456+
}
457+
458+
referencedTemplates.Add(masterTemplate);
459+
460+
return await HasRecursiveCircularReference(referencedTemplates, masterTemplate.MasterTemplateAlias);
461+
}
416462
}

0 commit comments

Comments
 (0)