diff --git a/src/Microsoft.Health.Fhir.Core/Features/Validation/Narratives/NarrativeHtmlSanitizer.cs b/src/Microsoft.Health.Fhir.Core/Features/Validation/Narratives/NarrativeHtmlSanitizer.cs index e86efc1318..3b86dd38eb 100644 --- a/src/Microsoft.Health.Fhir.Core/Features/Validation/Narratives/NarrativeHtmlSanitizer.cs +++ b/src/Microsoft.Health.Fhir.Core/Features/Validation/Narratives/NarrativeHtmlSanitizer.cs @@ -131,7 +131,7 @@ public class NarrativeHtmlSanitizer : INarrativeHtmlSanitizer "xmlns", }; - private static readonly ISet Src = new HashSet(StringComparer.OrdinalIgnoreCase) + private static readonly ISet AllowedSrcSchemes = new HashSet(StringComparer.OrdinalIgnoreCase) { "#", "data:", @@ -139,6 +139,20 @@ public class NarrativeHtmlSanitizer : INarrativeHtmlSanitizer "https:", }; + private static readonly ISet DangerousHrefSchemes = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "javascript:", + "vbscript:", + "data:", + "livescript:", + "file:", + "blob:", + "ftp:", + "ms-its:", + "mhtml:", + "jar:", + }; + // Obvious invalid structural parsing errors to report private static readonly HashSet RaiseErrorTypes = new HashSet { @@ -292,7 +306,15 @@ private static void ValidateAttributes(IElement element, Action if (string.Equals("src", attr.Name, StringComparison.OrdinalIgnoreCase)) { - if (!Src.Any(x => attr.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase))) + if (!AllowedSrcSchemes.Any(x => attr.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase))) + { + onInvalidAttr(element, attr); + } + } + + if (string.Equals("href", attr.Name, StringComparison.OrdinalIgnoreCase)) + { + if (DangerousHrefSchemes.Any(x => attr.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase))) { onInvalidAttr(element, attr); } diff --git a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Validation/Narratives/NarrativeHtmlSanitizerTests.cs b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Validation/Narratives/NarrativeHtmlSanitizerTests.cs index 6d3f400389..f4af402d21 100644 --- a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Validation/Narratives/NarrativeHtmlSanitizerTests.cs +++ b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Validation/Narratives/NarrativeHtmlSanitizerTests.cs @@ -84,5 +84,41 @@ public void GivenHtmlWithDivAndText_WhenCleaningHtml_ThenResultIsSuccessful(stri Assert.Equal(output, results); } + + [Theory] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + public void GivenHtmlWithMaliciousHref_WhenValidating_ThenValidationErrorIsReturned(string html) + { + var results = _sanitizer.Validate(html); + + Assert.NotEmpty(results); + } + + [Theory] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + public void GivenHtmlWithSafeHref_WhenValidating_ThenValidationIsSuccessful(string html) + { + var results = _sanitizer.Validate(html); + + Assert.Empty(results); + } + + [Theory] + [InlineData("", "")] + [InlineData("", "")] + [InlineData("", "")] + public void GivenHtmlWithMaliciousHref_WhenSanitizing_ThenHrefIsRemoved(string input, string output) + { + var results = _sanitizer.Sanitize(input); + + Assert.Equal(output, results); + } } }