From af3cf497a67f290d9185d5ba3aaa994309c5701e Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Thu, 6 Apr 2017 15:50:59 -0700 Subject: [PATCH] Update TagHelperMatchingConventions to disallow opt-out prefix. - Prior to this change the `TagHelper` parsing would strip the opt-out character (`!`) from tag names that got passed to the TagHelper matching services. At design time this proved to be a problem because they have their own understanding of the HTML document and only pass us full tag names (names that include `!`). This changes the matching conventions to immediately return false if a tag name is seen to contain the `TagHelper` opt-out. - Added two `DefaultTagHelperFactService` tests to verify that tag names with opt-out prefixes are denied `TagHelperDescriptor`s. #1186 --- .../TagHelperMatchingConventions.cs | 13 ++++++ .../DefaultTagHelperFactsService.cs | 5 +-- .../DefaultTagHelperFactsServiceTest.cs | 40 +++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperMatchingConventions.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperMatchingConventions.cs index 9aa0ef8059..c1e01760ae 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperMatchingConventions.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperMatchingConventions.cs @@ -12,6 +12,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution { public const string ElementCatchAllName = "*"; + public const char ElementOptOutCharacter = '!'; + public static bool SatisfiesRule( string tagNameWithoutPrefix, string parentTagName, @@ -66,6 +68,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution throw new ArgumentNullException(nameof(rule)); } + if (string.IsNullOrEmpty(tagNameWithoutPrefix)) + { + return false; + } + + if (tagNameWithoutPrefix[0] == ElementOptOutCharacter) + { + // TagHelpers can never satisfy tag names that are prefixed with the opt-out character. + return false; + } + if (rule.TagName != ElementCatchAllName && rule.TagName != null && !string.Equals(tagNameWithoutPrefix, rule.TagName, StringComparison.OrdinalIgnoreCase)) diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperFactsService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperFactsService.cs index 555b46792c..30e2c381af 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperFactsService.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperFactsService.cs @@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Razor.Evolution.Legacy; using System; using System.Collections.Generic; using System.ComponentModel.Composition; -using System.Linq; namespace Microsoft.VisualStudio.LanguageServices.Razor { @@ -48,8 +47,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor } public override IEnumerable GetBoundTagHelperAttributes( - TagHelperDocumentContext documentContext, - string attributeName, + TagHelperDocumentContext documentContext, + string attributeName, TagHelperBinding binding) { if (documentContext == null) diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultTagHelperFactsServiceTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultTagHelperFactsServiceTest.cs index bffe640ab8..90f0df6420 100644 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultTagHelperFactsServiceTest.cs +++ b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultTagHelperFactsServiceTest.cs @@ -13,6 +13,26 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor // Purposefully not thoroughly testing DefaultTagHelperFactsService.GetTagHelperBinding because it's a pass through // into TagHelperDescriptorProvider.GetTagHelperBinding. + [Fact] + public void GetTagHelperBinding_DoesNotAllowOptOutCharacterPrefix() + { + // Arrange + var documentDescriptors = new[] + { + ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly") + .TagMatchingRule(rule => rule.RequireTagName("*")) + .Build() + }; + var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); + var service = new DefaultTagHelperFactsService(); + + // Act + var binding = service.GetTagHelperBinding(documentContext, "!a", Enumerable.Empty>(), parentTag: null); + + // Assert + Assert.Null(binding); + } + [Fact] public void GetTagHelperBinding_WorksAsExpected() { @@ -133,6 +153,26 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor Assert.Equal(expectedAttributeDescriptors, descriptors, BoundAttributeDescriptorComparer.CaseSensitive); } + [Fact] + public void GetTagHelpersGivenTag_DoesNotAllowOptOutCharacterPrefix() + { + // Arrange + var documentDescriptors = new[] + { + ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly") + .TagMatchingRule(rule => rule.RequireTagName("*")) + .Build() + }; + var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors); + var service = new DefaultTagHelperFactsService(); + + // Act + var descriptors = service.GetTagHelpersGivenTag(documentContext, "!strong", parentTag: null); + + // Assert + Assert.Empty(descriptors); + } + [Fact] public void GetTagHelpersGivenTag_RequiresTagName() {