Beispiel #1
0
 private static void CheckRootTextNodes(PortableHtmlContext context)
 {
     if (context.SourceXml.Nodes().OfType <XText>().Any())
     {
         context.SetError(PortableHtmlSchemaErorr.RootText);
     }
 }
Beispiel #2
0
        private static void CheckBrPosition(PortableHtmlContext context)
        {
            var brs = context.SourceXml.Descendants("br").Where(context.InChecking).ToArray();

            foreach (var e in brs)
            {
                if (!context.IsActive)
                {
                    break;
                }
                var parentTag = e.Parent.Name.LocalName;
                if (-1 == Array.IndexOf(ContentElements, parentTag))
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidBrPosition, e);
                    if (!context.InChecking(e))
                    {
                        continue;
                    }
                }
                if (!checkBrNeighbro(e.PreviousNode) || !checkBrNeighbro(e.NextNode))
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidBrPosition, e);
                }
            }
        }
Beispiel #3
0
 /// <summary>
 /// Проверяет наличие запрещенных атрибутов
 /// </summary>
 /// <param name="context"></param>
 private static void CheckDeprecatedAttributes(PortableHtmlContext context)
 {
     foreach (var e in context.SourceXml.DescendantsAndSelf().Where(context.InChecking))
     {
         if (!context.IsActive)
         {
             break;
         }
         foreach (var a in e.Attributes())
         {
             if (!context.IsActive)
             {
                 break;
             }
             if (!context.InChecking(e))
             {
                 break;
             }
             var state = context.GetAttributeErrorState(a.Name.LocalName, a.Parent.Name.LocalName);
             if (state != PortableHtmlSchemaErorr.None)
             {
                 context.SetError(state, a: a);
             }
         }
     }
 }
 private void CheckSourceTrust(XElement result, PortableHtmlContext ctx)
 {
     foreach (var descendant in result.Descendants("img").ToArray())
     {
         if (null == descendant.Attribute("src") || string.IsNullOrWhiteSpace(descendant.Attribute("src").Value))
         {
             descendant.Remove();
         }
         else
         {
             var isTrust = ctx.GetUriTrustState(descendant.Attribute("src").Value, true) == PortableHtmlSchemaErorr.None;
             if (!isTrust)
             {
                 descendant.SetAttributeValue("phtml_src", descendant.Attribute("src").Value);
                 descendant.SetAttributeValue("src", "/phtml_non_trust_image.png");
             }
         }
     }
     foreach (var descendant in result.Descendants("a").ToArray())
     {
         if (null == descendant.Attribute("href") || string.IsNullOrWhiteSpace(descendant.Attribute("href").Value))
         {
             descendant.ReplaceWith(descendant.Nodes());
         }
         else
         {
             var isTrust = ctx.GetUriTrustState(descendant.Attribute("href").Value, false) == PortableHtmlSchemaErorr.None;
             if (!isTrust)
             {
                 descendant.SetAttributeValue("phtml_href", descendant.Attribute("href").Value);
                 descendant.SetAttributeValue("href", "/phtml_non_trust_link.html");
             }
         }
     }
 }
Beispiel #5
0
        /// <summary>
        /// Проверка наличия корневого элемента div в соответствии с 'has_root_container'
        /// </summary>
        /// <param name="context"></param>
        private static void CheckRootElement(PortableHtmlContext context)
        {
            var srcXml = context.SourceXml;

            if (srcXml.Name.LocalName != AllowedRootName)
            {
                context.SetError(PortableHtmlSchemaErorr.InvalidRootTag, srcXml);
            }
        }
Beispiel #6
0
        /// <summary>
        /// Проверка на отсутствие комментариев
        /// </summary>
        private static void CheckNoComments(PortableHtmlContext context)
        {
            var hasComment = context.SourceXml.DescendantNodes().OfType <XComment>().Any();

            if (hasComment)
            {
                context.SetError(PortableHtmlSchemaErorr.CommentsDetected);
            }
        }
Beispiel #7
0
        /// <summary>
        /// Проверка на отсутствие инструкций препроцессора
        /// </summary>
        private static void CheckNoProcessingInstructions(PortableHtmlContext context)
        {
            var hasProcessingInstructions = context.SourceXml.DescendantNodes().OfType <XProcessingInstruction>().Any();

            if (hasProcessingInstructions)
            {
                context.SetError(PortableHtmlSchemaErorr.ProcessingInstructionsDetected);
            }
        }
Beispiel #8
0
 private static void CheckRootInlineElements(PortableHtmlContext context)
 {
     foreach (var e in context.SourceXml.Elements())
     {
         if (!context.IsActive)
         {
             break;
         }
         if (-1 == Array.IndexOf(ParaElements, e.Name.LocalName))
         {
             context.SetError(PortableHtmlSchemaErorr.RootInline, e);
         }
     }
 }
Beispiel #9
0
 /// <summary>
 /// Проверяет наличие запрещенных элементов из списка
 /// </summary>
 /// <param name="context"></param>
 private static void CheckDeprecatedElements(PortableHtmlContext context)
 {
     foreach (var e in context.SourceXml.Descendants().Where(context.InChecking))
     {
         if (!context.IsActive)
         {
             break;
         }
         var error = context.GetTagState(e.Name.LocalName);
         if (error != PortableHtmlSchemaErorr.None)
         {
             context.SetError(error, e);
         }
     }
 }
		public void DeactivatesInForcedResultMode(){
			var ctx = new PortableHtmlContext { Strategy = PortableHtmlVerificationStrategy.ForcedResult };
			Assert.True(ctx.IsActive);
			ctx.SetError(PortableHtmlSchemaErorr.InvalidRootTag);
			Assert.False(ctx.IsActive,"Forced");

			ctx = new PortableHtmlContext { Strategy = PortableHtmlVerificationStrategy.ForcedElementResult };
			Assert.True(ctx.IsActive);
			ctx.SetError(PortableHtmlSchemaErorr.InvalidRootTag);
			Assert.True(ctx.IsActive, "ForcedResult");

			ctx = new PortableHtmlContext { Strategy = PortableHtmlVerificationStrategy.Full };
			Assert.True(ctx.IsActive);
			ctx.SetError(PortableHtmlSchemaErorr.InvalidRootTag);
			Assert.True(ctx.IsActive, "Full");
		}
Beispiel #11
0
 /// <summary>
 /// Проверка на отсутствие определения пространств имен
 /// </summary>
 /// <param name="context"></param>
 private static void CheckNamespaces(PortableHtmlContext context)
 {
     foreach (var e in context.SourceXml.DescendantsAndSelf().Where(context.InChecking))
     {
         if (e.Name.NamespaceName != "")
         {
             context.SetError(PortableHtmlSchemaErorr.NamespaceDeclarationDetected, e);
         }
         foreach (var a in e.Attributes())
         {
             if (a.Name.NamespaceName != "" || a.Name.LocalName == "xmlns")
             {
                 context.SetError(PortableHtmlSchemaErorr.NamespaceDeclarationDetected, a: a);
             }
         }
     }
 }
		public void InForcedStrategiesMarksElementsNotToReproduce(){
			var ctx = new PortableHtmlContext{Strategy = PortableHtmlVerificationStrategy.Full};
			var e = new XElement("test");
			Assert.True(ctx.InChecking(e));
			ctx.SetError(PortableHtmlSchemaErorr.None, e);
			Assert.True(ctx.InChecking(e), "Full");
			ctx.Strategy = PortableHtmlVerificationStrategy.ForcedElementResult;
			e = new XElement("test");
			Assert.True(ctx.InChecking(e));
			ctx.SetError(PortableHtmlSchemaErorr.None, e);
			Assert.False(ctx.InChecking(e), "ForcedElement");
			ctx.Strategy = PortableHtmlVerificationStrategy.ForcedResult;
			e = new XElement("test");
			Assert.True(ctx.InChecking(e));
			ctx.SetError(PortableHtmlSchemaErorr.None, e);
			Assert.False(ctx.InChecking(e), "ForcedResult");
		}
Beispiel #13
0
 /// <summary>
 /// Проверяет наличие запрещенных атрибутов
 /// </summary>
 /// <param name="context"></param>
 private static void CheckEmptyElements(PortableHtmlContext context)
 {
     foreach (var e in context.SourceXml.Descendants().Where(context.InChecking))
     {
         if (-1 != Array.IndexOf(EmptyRequiredElements, e.Name.LocalName))
         {
             if (e.Nodes().Any())
             {
                 context.SetError(PortableHtmlSchemaErorr.NonEmptyNonContentTag, e);
             }
         }
         else
         {
             if (string.IsNullOrWhiteSpace(e.Value) && !e.Descendants("img").Any())
             {
                 context.SetError(PortableHtmlSchemaErorr.EmptyElement, e);
             }
         }
     }
 }
Beispiel #14
0
 /// <summary>
 /// Проверяет наличие запрещенных атрибутов
 /// </summary>
 /// <param name="context"></param>
 private static void CheckUpperCase(PortableHtmlContext context)
 {
     foreach (var e in context.SourceXml.DescendantsAndSelf().Where(context.InChecking))
     {
         if (e.Name.LocalName.ToLowerInvariant() != e.Name.LocalName)
         {
             context.SetError(PortableHtmlSchemaErorr.UpperCaseDetected, e);
         }
         if (!context.InChecking(e))
         {
             continue;
         }
         foreach (var a in e.Attributes())
         {
             if (a.Name.LocalName.ToLowerInvariant() != a.Name.LocalName)
             {
                 context.SetError(PortableHtmlSchemaErorr.UpperCaseDetected, a: a);
             }
         }
     }
 }
Beispiel #15
0
        /// <summary>
        /// Проверка валидности настройки ссылок у A и IMG
        /// </summary>
        /// <param name="context"></param>
        private static void CheckAnchorLinks(PortableHtmlContext context)
        {
            var anchors = context.SourceXml.Descendants("a").Where(context.InChecking).ToArray();

            foreach (var anchor in anchors)
            {
                var href = anchor.Attribute("href");
                if (null == href)
                {
                    context.SetError(PortableHtmlSchemaErorr.NoRequiredHrefAttributeInA, anchor);
                }
                else
                {
                    var error = context.GetUriTrustState(href.Value, false);
                    if (error != PortableHtmlSchemaErorr.None)
                    {
                        context.SetError(error, anchor);
                    }
                }
            }
        }
Beispiel #16
0
 private static void CheckNestedParas(PortableHtmlContext context)
 {
     foreach (var p in context.SourceXml.Descendants())
     {
         if (!context.IsActive)
         {
             break;
         }
         if (!context.InChecking(p))
         {
             continue;
         }
         if (-1 != Array.IndexOf(ParaElements, p.Name.LocalName))
         {
             if (p.Parent != context.SourceXml)
             {
                 context.SetError(PortableHtmlSchemaErorr.NestedParaElements, p);
             }
         }
     }
 }
Beispiel #17
0
 private static void CheckNoTextElementsText(PortableHtmlContext context)
 {
     foreach (var e in context.SourceXml.Descendants())
     {
         if (!context.IsActive)
         {
             break;
         }
         if (!context.InChecking(e))
         {
             continue;
         }
         if (-1 != Array.IndexOf(NoTextElements, e.Name.LocalName))
         {
             if (e.Nodes().OfType <XText>().Any())
             {
                 context.SetError(PortableHtmlSchemaErorr.TextInNonTextElement, e);
             }
         }
     }
 }
Beispiel #18
0
        /// <summary>
        /// Проверка валидности настройки ссылок у A и IMG
        /// </summary>
        /// <param name="context"></param>
        private static void CheckImageLinks(PortableHtmlContext context)
        {
            var srcXml = context.SourceXml;
            var images = srcXml.Descendants("img").Where(context.InChecking).ToArray();

            foreach (var img in images)
            {
                var src = img.Attribute("src");
                if (null == src)
                {
                    context.SetError(PortableHtmlSchemaErorr.NoRequiredSrcAttributeInImg, img);
                }
                else
                {
                    var error = context.GetUriTrustState(src.Value, true);
                    if (error != PortableHtmlSchemaErorr.None)
                    {
                        context.SetError(error, img);
                    }
                }
            }
        }
Beispiel #19
0
        private static void CheckListSchema(PortableHtmlContext context)
        {
            var lists = context.SourceXml.Descendants().Where(_ => _.Name.LocalName == "ul" || _.Name.LocalName == "ol").Where(context.InChecking);

            foreach (var e in lists)
            {
                if (!context.IsActive)
                {
                    return;
                }
                foreach (var ch in e.Elements())
                {
                    if (ch.Name.LocalName != "li")
                    {
                        context.SetError(PortableHtmlSchemaErorr.InvalidList, ch);
                    }
                }
            }
            if (!context.IsActive)
            {
                return;
            }
            var listitems = context.SourceXml.Descendants("li").Where(context.InChecking);

            foreach (var listitem in listitems)
            {
                if (!context.IsActive)
                {
                    return;
                }
                var pname = listitem.Parent.Name.LocalName;
                if (pname != "ul" && pname != "ol")
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidList, listitem);
                }
            }
        }
Beispiel #20
0
 private static void CheckNoTextElementsInlines(PortableHtmlContext context)
 {
     foreach (var e in context.SourceXml.Descendants())
     {
         if (!context.IsActive)
         {
             break;
         }
         if (!context.InChecking(e))
         {
             continue;
         }
         if (-1 != Array.IndexOf(NoTextElements, e.Name.LocalName))
         {
             foreach (var sube in e.Elements())
             {
                 if (-1 != Array.IndexOf(InlineElements, sube.Name.LocalName))
                 {
                     context.SetError(PortableHtmlSchemaErorr.InlineInNonTextElement, e);
                 }
             }
         }
     }
 }
		/// <summary>
		/// Проверяет наличие запрещенных атрибутов
		/// </summary>
		/// <param name="context"></param>
		private static void CheckEmptyElements(PortableHtmlContext context){
			foreach (var e in context.SourceXml.Descendants().Where(context.InChecking))
			{
				if (-1 != Array.IndexOf(EmptyRequiredElements,e.Name.LocalName)){
					
					if (e.Nodes().Any()){
						context.SetError(PortableHtmlSchemaErorr.NonEmptyNonContentTag, e);

					}
				}
				else{
					if (string.IsNullOrWhiteSpace(e.Value) && !e.Descendants("img").Any()){
						context.SetError(PortableHtmlSchemaErorr.EmptyElement, e);
					}
				}
			}

		}
		/// <summary>
		/// Проверяет наличие запрещенных атрибутов
		/// </summary>
		/// <param name="context"></param>
		private static void CheckUpperCase(PortableHtmlContext context){
			foreach (var e in context.SourceXml.DescendantsAndSelf().Where(context.InChecking))
			{
				if (e.Name.LocalName.ToLowerInvariant() != e.Name.LocalName){
					context.SetError(PortableHtmlSchemaErorr.UpperCaseDetected, e);
				}
				if (!context.InChecking(e)) continue;
				foreach (var a in e.Attributes()){
					if (a.Name.LocalName.ToLowerInvariant() != a.Name.LocalName){
						context.SetError(PortableHtmlSchemaErorr.UpperCaseDetected, a: a);
					}
				}
			}

		}
		/// <summary>
		/// Проверка валидности настройки ссылок у A и IMG
		/// </summary>
		/// <param name="context"></param>
		private static void CheckAnchorLinks(PortableHtmlContext context)
		{
			var anchors = context.SourceXml.Descendants("a").Where(context.InChecking).ToArray();
			foreach (var anchor in anchors)
			{
				var href = anchor.Attribute("href");
				if (null == href)
				{
					context.SetError(PortableHtmlSchemaErorr.NoRequiredHrefAttributeInA, anchor);
				}
				else
				{
					var error = context.GetUriTrustState(href.Value, false);
					if (error != PortableHtmlSchemaErorr.None)
					{
						context.SetError(error, anchor);
					}
				}
			}
		}
		/// <summary>
		/// Проверка валидности настройки ссылок у A и IMG
		/// </summary>
		/// <param name="context"></param>
		private static void CheckImageLinks(PortableHtmlContext context){
			var srcXml = context.SourceXml;
			var images = srcXml.Descendants("img").Where(context.InChecking).ToArray();
			
			foreach (var img in images){
				var src = img.Attribute("src");
				if (null == src){
					context.SetError(PortableHtmlSchemaErorr.NoRequiredSrcAttributeInImg, img);
				}
				else{
					var error = context.GetUriTrustState(src.Value,true);
					if (error != PortableHtmlSchemaErorr.None){
						context.SetError(error, img);
					}
				}
			}

		
		}
		private static void CheckRootTextNodes(PortableHtmlContext context)
		{
			if (context.SourceXml.Nodes().OfType<XText>().Any())
			{
				context.SetError(PortableHtmlSchemaErorr.RootText);
			}
		}
		public void AllowBaseUriAbsoluteLinks()
		{
			var ctx = new PortableHtmlContext();
			ctx.BaseUri = new Uri("https://my.site.com:443/u");
			Assert.AreEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com/x", false));
			Assert.AreEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com/x", true));
			Assert.AreEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com:8080/x", false));
			Assert.AreEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com:8080/x", true));
		}
		/// <summary>
		/// Проверка на отсутствие определения пространств имен
		/// </summary>
		/// <param name="context"></param>
		private static void CheckNamespaces(PortableHtmlContext context)
		{
			foreach (var e in context.SourceXml.DescendantsAndSelf().Where(context.InChecking)){
				if (e.Name.NamespaceName!=""){
					context.SetError(PortableHtmlSchemaErorr.NamespaceDeclarationDetected, e);
				}
				foreach (var a in e.Attributes()){
					if (a.Name.NamespaceName != "" || a.Name.LocalName == "xmlns"){
						context.SetError(PortableHtmlSchemaErorr.NamespaceDeclarationDetected, a: a);
					}
				}
			}
		}
		private static void CheckRootInlineElements(PortableHtmlContext context){
			foreach (var e in context.SourceXml.Elements()){
				if(!context.IsActive)break;
				if (-1 == Array.IndexOf(ParaElements, e.Name.LocalName)){
					context.SetError(PortableHtmlSchemaErorr.RootInline,e);
				}
			}
		}
		private static void CheckNestedParas(PortableHtmlContext context){
			foreach (var p in context.SourceXml.Descendants()){
				if(!context.IsActive)break;
				if (!context.InChecking(p)) continue;
				if (-1 != Array.IndexOf(ParaElements, p.Name.LocalName)){
					if (p.Parent != context.SourceXml){
						context.SetError(PortableHtmlSchemaErorr.NestedParaElements, p);
					}	
				}
			}
		}
Beispiel #30
0
        /// <summary>
        /// Валидизация исходного HTML
        /// </summary>
        /// <param name="srcHtml">Строка HTML для проверки соответствия PHTML</param>
        /// <param name="context">Контекст обработки PHTML (дополнительные настройки, опции, списки доверения)</param>
        /// <returns></returns>
        public static PortableHtmlContext Validate(string srcHtml, PortableHtmlContext context = null)
        {
            context = context ?? new PortableHtmlContext();
            if (string.IsNullOrWhiteSpace(srcHtml))
            {
                return(context.SetError(PortableHtmlSchemaErorr.EmptyInput));
            }
            var rootedHtml = "<root>" + srcHtml + "</root>";

            try{
                var xml = XElement.Parse(rootedHtml);

                var rootElementsCount = xml.Elements().Count();
                if (1 < rootElementsCount)
                {
                    context.SetError(PortableHtmlSchemaErorr.NoRootTag);
                }
                if (!context.IsActive)
                {
                    return(context);
                }
                //необходимо эту проверку вызывать в том числе здесь,
                //так как иначе при передаче в XML могут быть проигнорированы трейловые комментарии
                var hasComment = xml.DescendantNodes().OfType <XComment>().Any();
                if (hasComment)
                {
                    context.SetError(PortableHtmlSchemaErorr.CommentsDetected);
                }
                if (!context.IsActive)
                {
                    return(context);
                }
                //необходимо эту проверку вызывать в том числе здесь,
                //так как иначе при передаче в XML могут быть проигнорированы трейловые инструкции
                var hasProcessing = xml.DescendantNodes().OfType <XProcessingInstruction>().Any();
                if (hasProcessing)
                {
                    context.SetError(PortableHtmlSchemaErorr.ProcessingInstructionsDetected);
                }
                if (!context.IsActive)
                {
                    return(context);
                }

                //необходимо эту проверку вызывать в том числе здесь,
                //так как иначе при передаче в XML могут быть проигнорированы трейловые CDATA
                var hasCData = xml.DescendantNodes().OfType <XCData>().Any();
                if (hasCData)
                {
                    context.SetError(PortableHtmlSchemaErorr.CdataDetected);
                }
                if (!context.IsActive)
                {
                    return(context);
                }
                var realRoot = XElement.Parse(srcHtml, LoadOptions.SetLineInfo);

                if (realRoot.Name.LocalName != AllowedRootName)
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidRootTag, realRoot);
                }
                if (!context.IsActive)
                {
                    return(context);
                }
                //hack way to provide addition info to method
                realRoot.AddAnnotation(CheckedBySourceCheckerAnnotation.Default);
                return(Validate(realRoot, context));
            }
            catch (XmlException e) {
                return(context.SetError(PortableHtmlSchemaErorr.NonXml, e: e));
            }
        }
Beispiel #31
0
        private static void CheckTableSchema(PortableHtmlContext context)
        {
            var tables  = context.SourceXml.Descendants("table").Where(context.InChecking);
            var theads  = context.SourceXml.Descendants("thead").Where(context.InChecking);
            var tbodies = context.SourceXml.Descendants("tbody").Where(context.InChecking);
            var trs     = context.SourceXml.Descendants("tr").Where(context.InChecking);
            var tds     = context.SourceXml.Descendants("td").Where(context.InChecking);
            var ths     = context.SourceXml.Descendants("th").Where(context.InChecking);

            foreach (var e in ths)
            {
                if (!context.IsActive)
                {
                    return;
                }
                var p  = e.Parent;
                var pp = p.Parent ?? new XElement("stub");
                if (p.Name.LocalName != "tr")
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                }
                if (!context.InChecking(e))
                {
                    continue;
                }
                if (pp.Name.LocalName != "thead")
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                }
            }
            foreach (var e in tds)
            {
                if (!context.IsActive)
                {
                    return;
                }
                var p  = e.Parent;
                var pp = p.Parent ?? new XElement("stub");
                if (p.Name.LocalName != "tr")
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                }
                if (!context.InChecking(e))
                {
                    continue;
                }
                if (pp.Name.LocalName != "table" && pp.Name.LocalName != "tbody")
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                }
            }

            foreach (var e in trs)
            {
                if (!context.IsActive)
                {
                    return;
                }
                var p = e.Parent;
                if (p.Name.LocalName != "table" && p.Name.LocalName != "tbody" && p.Name.LocalName != "thead")
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                }
                foreach (var cell in  e.Elements().Where(context.InChecking))
                {
                    if (!context.IsActive)
                    {
                        return;
                    }
                    if (cell.Name.LocalName != "td" && cell.Name.LocalName != "th")
                    {
                        context.SetError(PortableHtmlSchemaErorr.InvalidTable, cell);
                    }
                }
            }

            foreach (var e in tbodies)
            {
                if (!context.IsActive)
                {
                    return;
                }
                if (e.Parent.Name.LocalName != "table")
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                    if (!context.InChecking(e))
                    {
                        continue;
                    }
                }
                if (e.ElementsAfterSelf().Any())
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                    if (!context.InChecking(e))
                    {
                        continue;
                    }
                }
                var eb = e.ElementsBeforeSelf().ToArray();
                if (eb.Length > 1 || (eb.Length == 1 && eb[0].Name.LocalName != "thead"))
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                }
            }

            foreach (var e in theads)
            {
                if (!context.IsActive)
                {
                    return;
                }
                if (e.Parent.Name.LocalName != "table")
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                    if (!context.InChecking(e))
                    {
                        continue;
                    }
                }
                if (e.ElementsBeforeSelf().Any())
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                    if (!context.InChecking(e))
                    {
                        continue;
                    }
                }
                var eb = e.ElementsAfterSelf().ToArray();
                if (eb.Length > 1 || (eb.Length == 1 && eb[0].Name.LocalName != "tbody"))
                {
                    context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
                }
            }

            foreach (var table in tables)
            {
                if (!context.IsActive)
                {
                    return;
                }
                foreach (var t in table.Elements().Where(context.InChecking))
                {
                    if (!context.IsActive)
                    {
                        return;
                    }
                    var tn = t.Name.LocalName;
                    if (tn != "tbody" && tn != "tr" && tn != "thead")
                    {
                        context.SetError(PortableHtmlSchemaErorr.InvalidTable, t);
                    }
                }
            }
        }
		/// <summary>
		/// Проверка на отсутствие инструкций препроцессора
		/// </summary>
		private static void CheckNoProcessingInstructions(PortableHtmlContext context)
		{
			var hasProcessingInstructions = context.SourceXml.DescendantNodes().OfType<XProcessingInstruction>().Any();
			if (hasProcessingInstructions){
				context.SetError(PortableHtmlSchemaErorr.ProcessingInstructionsDetected);
			}
		}
		public void CanTrustAllLinks()
		{
			var ctx = new PortableHtmlContext();
			ctx.Level|=PortableHtmlStrictLevel.TrustAllLinks;
			Assert.AreEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com:8080/x", false));
			Assert.AreNotEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com:8080/x", true));
		}
		/// <summary>
		/// Проверяет наличие запрещенных атрибутов
		/// </summary>
		/// <param name="context"></param>
		private static void CheckDeprecatedAttributes(PortableHtmlContext context)
		{
			foreach (var e in context.SourceXml.DescendantsAndSelf().Where(context.InChecking))
			{
				if(!context.IsActive)break;
				foreach (var a in e.Attributes()){
					if (!context.IsActive) break;
					if(!context.InChecking(e))break;
					var state =  context.GetAttributeErrorState(a.Name.LocalName,a.Parent.Name.LocalName);
					if (state != PortableHtmlSchemaErorr.None){
						context.SetError(state, a:a);
					}					
				}
			}

		}
		private static void CheckNoTextElementsText(PortableHtmlContext context)
		{
			foreach (var e in context.SourceXml.Descendants()){
				if(!context.IsActive)break;
				if(!context.InChecking(e))continue;
				if (-1 != Array.IndexOf(NoTextElements, e.Name.LocalName)){
					if (e.Nodes().OfType<XText>().Any()){
						context.SetError(PortableHtmlSchemaErorr.TextInNonTextElement,e);
					}
				}
			}
			
		}
		/// <summary>
		/// Проверяет наличие запрещенных элементов из списка
		/// </summary>
		/// <param name="context"></param>
		private static void CheckDeprecatedElements(PortableHtmlContext context)
		{
			foreach (var e in context.SourceXml.Descendants().Where(context.InChecking)){
				if(!context.IsActive)break;
				var error = context.GetTagState(e.Name.LocalName);
				if (error != PortableHtmlSchemaErorr.None){
					context.SetError(error, e);
				}
			}

		}
		private static void CheckNoTextElementsInlines(PortableHtmlContext context){
			foreach (var e in context.SourceXml.Descendants()){
				if (!context.IsActive) break;
				if (!context.InChecking(e)) continue;
				if (-1 != Array.IndexOf(NoTextElements, e.Name.LocalName)){
					foreach (var sube in e.Elements()){
						if (-1 != Array.IndexOf(InlineElements, sube.Name.LocalName)){
							context.SetError(PortableHtmlSchemaErorr.InlineInNonTextElement, e);
						}
					}

				}
			}
		}
		/// <summary>
		/// Проверка на отсутствие комментариев
		/// </summary>
		private static void CheckNoComments(PortableHtmlContext context)
		{
			var hasComment = context.SourceXml.DescendantNodes().OfType<XComment>().Any();
			if (hasComment){
				context.SetError(PortableHtmlSchemaErorr.CommentsDetected);
			}
		}
Beispiel #39
0
        /// <summary>
        /// Валидизация исходного XML
        /// </summary>
        /// <param name="srcXml">Элемент для проверки соответствия PHTML</param>
        /// <param name="context">Контекст обработки PHTML (дополнительные настройки, опции, списки доверения)</param>
        /// <returns></returns>
        public static PortableHtmlContext Validate(XElement srcXml, PortableHtmlContext context = null)
        {
            context           = context ?? new PortableHtmlContext();
            context.SourceXml = srcXml;
            //hack way to provide addition info to method
            if (null == srcXml.Annotation <CheckedBySourceCheckerAnnotation>())
            {
                CheckRootElement(context);
                if (!context.IsActive)
                {
                    return(context);
                }
                CheckNoComments(context);
                if (!context.IsActive)
                {
                    return(context);
                }
                CheckNoProcessingInstructions(context);
                if (!context.IsActive)
                {
                    return(context);
                }
            }

            CheckDeprecatedElements(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckNamespaces(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckDeprecatedAttributes(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckUpperCase(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckEmptyElements(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckAnchorLinks(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckImageLinks(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckRootTextNodes(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckRootInlineElements(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckNestedParas(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckNoTextElementsText(context);
            if (!context.IsActive)
            {
                return(context);
            }
            CheckNoTextElementsInlines(context);
            if (!context.IsActive)
            {
                return(context);
            }
            if (context.Level.HasFlag(PortableHtmlStrictLevel.AllowBr))
            {
                CheckBrPosition(context);
                if (!context.IsActive)
                {
                    return(context);
                }
            }
            if (context.Level.HasFlag(PortableHtmlStrictLevel.AllowLists))
            {
                CheckListSchema(context);
                if (!context.IsActive)
                {
                    return(context);
                }
            }
            if (context.Level.HasFlag(PortableHtmlStrictLevel.AllowTables))
            {
                CheckTableSchema(context);
                if (!context.IsActive)
                {
                    return(context);
                }
            }
            return(context);
        }
		/// <summary>
		/// Проверка наличия корневого элемента div в соответствии с 'has_root_container'
		/// </summary>
		/// <param name="context"></param>
		private static void CheckRootElement(PortableHtmlContext context){
			var srcXml = context.SourceXml;
			if (srcXml.Name.LocalName != AllowedRootName){
				context.SetError(PortableHtmlSchemaErorr.InvalidRootTag, srcXml);
			}
		}
		public void DisallowDataOnAnchors()
		{
			var ctx = new PortableHtmlContext();
			Assert.AreNotEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("data:image", false));
			
		}
		public void AllowTrustedAbsoluteLinks()
		{
			var ctx = new PortableHtmlContext();
			ctx.TrustedHosts.Add("my.site.com");
			Assert.AreEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com/x", false));
			Assert.AreEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com/x", true));
			Assert.AreEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com:8080/x", false));
			Assert.AreEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com:8080/x", true));
		}
		/// <summary>
		/// Валидизация исходного HTML
		/// </summary>
		/// <param name="srcHtml">Строка HTML для проверки соответствия PHTML</param>
		/// <param name="context">Контекст обработки PHTML (дополнительные настройки, опции, списки доверения)</param>
		/// <returns></returns>
		public static PortableHtmlContext Validate(string srcHtml, PortableHtmlContext context = null)
		{
			context = context ?? new PortableHtmlContext();
			if (string.IsNullOrWhiteSpace(srcHtml)){
				return context.SetError(PortableHtmlSchemaErorr.EmptyInput); 
			}
			var rootedHtml = "<root>" + srcHtml + "</root>";
			try{
				var xml = XElement.Parse(rootedHtml);
				
				var rootElementsCount = xml.Elements().Count();
				if (1 < rootElementsCount){
					context.SetError(PortableHtmlSchemaErorr.NoRootTag); 
				}
				if (!context.IsActive) return context;
				//необходимо эту проверку вызывать в том числе здесь,
				//так как иначе при передаче в XML могут быть проигнорированы трейловые комментарии
				var hasComment = xml.DescendantNodes().OfType<XComment>().Any();
				if (hasComment)
				{
					context.SetError(PortableHtmlSchemaErorr.CommentsDetected); 
				}
				if (!context.IsActive) return context;
				//необходимо эту проверку вызывать в том числе здесь,
				//так как иначе при передаче в XML могут быть проигнорированы трейловые инструкции
				var hasProcessing = xml.DescendantNodes().OfType<XProcessingInstruction>().Any();
				if (hasProcessing)
				{
					context.SetError(PortableHtmlSchemaErorr.ProcessingInstructionsDetected); 
				}
				if (!context.IsActive) return context;

				//необходимо эту проверку вызывать в том числе здесь,
				//так как иначе при передаче в XML могут быть проигнорированы трейловые CDATA
				var hasCData = xml.DescendantNodes().OfType<XCData>().Any();
				if (hasCData)
				{
					context.SetError(PortableHtmlSchemaErorr.CdataDetected);
				}
				if (!context.IsActive) return context;
				var realRoot = XElement.Parse(srcHtml,LoadOptions.SetLineInfo);

				if (realRoot.Name.LocalName != AllowedRootName){
					context.SetError(PortableHtmlSchemaErorr.InvalidRootTag,realRoot);
				}
				if (!context.IsActive) return context;
				//hack way to provide addition info to method
				realRoot.AddAnnotation(CheckedBySourceCheckerAnnotation.Default);
				return Validate(realRoot,context);
			}
			catch (XmlException e){
				return context.SetError(PortableHtmlSchemaErorr.NonXml,e:e);
			}
			
		}
		public void CoverToString(){
			var ctx = new PortableHtmlContext{
				Strategy = PortableHtmlVerificationStrategy.Full,
				Level = PortableHtmlStrictLevel.AllowLists,
				BaseUri = new Uri("http://ya.ru/x")
			};
			ctx.TrustedHosts.Add("me.com");
			ctx.TrustedHosts.Add("you.com");
			const string html = "<!--x--><DIV a='1'><p/><a href='javascript:hack()'/></DIV>";
			ctx = PortableHtmlSchema.Validate(html, ctx);
			var result = ctx.ToString();
			Console.WriteLine(result);
			Assert.AreEqual(@"Level : AllowLists
Strategy : Full
IsOk : False
SchemaError : InvalidRootTag, CommentsDetected, RootInline, UnknownAttribute, EmptyElement, UpperCaseDetected, DangerousLink
BaseUri : 'http://ya.ru/x'
Trust : 'me.com'
Trust : 'you.com'
CommentsDetected 0:0/ 
InvalidRootTag 1:10/./ 
UnknownAttribute 1:10/.//@a 
UpperCaseDetected 1:10/./ 
EmptyElement 1:21/./p[1] 
EmptyElement 1:25/./a[1] 
DangerousLink 1:25/./a[1] 
RootInline 1:25/./a[1] 
", result);
		}
		/// <summary>
		/// Валидизация исходного XML
		/// </summary>
		/// <param name="srcXml">Элемент для проверки соответствия PHTML</param>
		/// <param name="context">Контекст обработки PHTML (дополнительные настройки, опции, списки доверения)</param>
		/// <returns></returns>
		public static PortableHtmlContext Validate(XElement srcXml, PortableHtmlContext context = null)
		{
			context = context ?? new PortableHtmlContext();
			context.SourceXml = srcXml;
			//hack way to provide addition info to method
			if (null == srcXml.Annotation<CheckedBySourceCheckerAnnotation>()){
				CheckRootElement(context);
				if (!context.IsActive) return context;
				CheckNoComments(context);
				if (!context.IsActive) return context;
				CheckNoProcessingInstructions(context);
				if (!context.IsActive) return context;
			}

			CheckDeprecatedElements(context);
			if (!context.IsActive) return context;
			CheckNamespaces(context);
			if (!context.IsActive) return context;
			CheckDeprecatedAttributes(context);
			if (!context.IsActive) return context;
			CheckUpperCase(context);
			if (!context.IsActive) return context;
			CheckEmptyElements(context);
			if (!context.IsActive) return context;
			CheckAnchorLinks(context);
			if (!context.IsActive) return context;
			CheckImageLinks(context);
			if (!context.IsActive) return context;
			CheckRootTextNodes(context);
			if (!context.IsActive) return context;
			CheckRootInlineElements(context);
			if (!context.IsActive) return context;
			CheckNestedParas(context);
			if (!context.IsActive) return context;
			CheckNoTextElementsText(context);
			if (!context.IsActive) return context;
			CheckNoTextElementsInlines(context);
			if (!context.IsActive) return context;
			if (context.Level.HasFlag(PortableHtmlStrictLevel.AllowBr)){
				CheckBrPosition(context);
				if (!context.IsActive) return context;
			}
			if (context.Level.HasFlag(PortableHtmlStrictLevel.AllowLists))
			{
				CheckListSchema(context);
				if (!context.IsActive) return context;
			}
			if (context.Level.HasFlag(PortableHtmlStrictLevel.AllowTables))
			{
				CheckTableSchema(context);
				if (!context.IsActive) return context;
			}
			return context;
		}
		public void DisallowBadSchemasReferences(string url)
		{
			var ctx = new PortableHtmlContext();
			Assert.AreNotEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState(url, false));
			Assert.AreNotEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState(url, true));
		}
		private static void CheckTableSchema(PortableHtmlContext context){
			var tables = context.SourceXml.Descendants("table").Where(context.InChecking);
			var theads = context.SourceXml.Descendants("thead").Where(context.InChecking);
			var tbodies = context.SourceXml.Descendants("tbody").Where(context.InChecking);
			var trs = context.SourceXml.Descendants("tr").Where(context.InChecking);
			var tds = context.SourceXml.Descendants("td").Where(context.InChecking);
			var ths = context.SourceXml.Descendants("th").Where(context.InChecking);
			foreach (var e in ths){
				if (!context.IsActive) return;
				var p = e.Parent;
				var pp = p.Parent??new XElement("stub");
				if (p.Name.LocalName != "tr"){
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
				}
				if (!context.InChecking(e)) continue;
				if (pp.Name.LocalName != "thead"){
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
				}

			}
			foreach (var e in tds)
			{
				if (!context.IsActive) return;
				var p = e.Parent;
				var pp = p.Parent ?? new XElement("stub");
				if (p.Name.LocalName != "tr")
				{
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
				}
				if (!context.InChecking(e)) continue;
				if (pp.Name.LocalName != "table" && pp.Name.LocalName != "tbody")
				{
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
				}
			}

			foreach (var e in trs)
			{
				if (!context.IsActive) return;
				var p = e.Parent;
				if (p.Name.LocalName != "table" && p.Name.LocalName!="tbody" && p.Name.LocalName!="thead")
				{
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
				}
				foreach (var cell in  e.Elements().Where(context.InChecking)){
					if (!context.IsActive) return;
					if (cell.Name.LocalName != "td" && cell.Name.LocalName != "th"){
						context.SetError(PortableHtmlSchemaErorr.InvalidTable, cell);	
					}
				}
			}

			foreach (var e in tbodies){
				if (!context.IsActive) return;
				if (e.Parent.Name.LocalName != "table"){
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
					if(!context.InChecking(e))continue;
				}
				if (e.ElementsAfterSelf().Any()){
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
					if (!context.InChecking(e)) continue;
				}
				var eb = e.ElementsBeforeSelf().ToArray();
				if (eb.Length>1 || (eb.Length==1 && eb[0].Name.LocalName!="thead")){
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
				}
			}

			foreach (var e in theads)
			{
				if (!context.IsActive) return;
				if (e.Parent.Name.LocalName != "table")
				{
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
					if (!context.InChecking(e)) continue;
				}
				if (e.ElementsBeforeSelf().Any())
				{
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
					if (!context.InChecking(e)) continue;
				}
				var eb = e.ElementsAfterSelf().ToArray();
				if (eb.Length > 1 || (eb.Length == 1 && eb[0].Name.LocalName != "tbody"))
				{
					context.SetError(PortableHtmlSchemaErorr.InvalidTable, e);
				}
			}

			foreach (var table in tables){
				if (!context.IsActive) return;
				foreach (var t in table.Elements().Where(context.InChecking)){
					if (!context.IsActive) return;
					var tn = t.Name.LocalName;
					if (tn != "tbody" && tn != "tr" && tn != "thead"){
						context.SetError(PortableHtmlSchemaErorr.InvalidTable, t);	
					}
				}
			}
		}
		public void		AllowDataOnImages()
		{
			var ctx = new PortableHtmlContext();
			Assert.AreEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("data:image", true));

		}
		private static void CheckListSchema(PortableHtmlContext context){
			var lists = context.SourceXml.Descendants().Where(_ => _.Name.LocalName == "ul" || _.Name.LocalName == "ol").Where(context.InChecking);
			foreach (var e in lists)
			{
				if (!context.IsActive) return;
				foreach (var ch in e.Elements())
				{
					if (ch.Name.LocalName != "li")
					{
						context.SetError(PortableHtmlSchemaErorr.InvalidList, ch);
					}
				}
			}
			if (!context.IsActive) return;
			var listitems = context.SourceXml.Descendants("li").Where(context.InChecking);
			foreach (var listitem in listitems)
			{
				if (!context.IsActive) return;
				var pname = listitem.Parent.Name.LocalName;
				if (pname != "ul" && pname != "ol")
				{
					context.SetError(PortableHtmlSchemaErorr.InvalidList, listitem);
				}
			}
		}
		public void DisallowNonTrustedAbsoluteLinks(){
			var ctx = new PortableHtmlContext();
			Assert.AreNotEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com/x", false));
			Assert.AreNotEqual(PortableHtmlSchemaErorr.None, ctx.GetUriTrustState("http://my.site.com/x", true));
		}
		private static void CheckBrPosition(PortableHtmlContext context){
			var brs = context.SourceXml.Descendants("br").Where(context.InChecking).ToArray();
			foreach (var e in brs){
				if (!context.IsActive) break;
				var parentTag = e.Parent.Name.LocalName;
				if (-1 == Array.IndexOf(ContentElements, parentTag)){
					context.SetError(PortableHtmlSchemaErorr.InvalidBrPosition, e);
					if(!context.InChecking(e))continue;
				}
				if (!checkBrNeighbro(e.PreviousNode) || !checkBrNeighbro(e.NextNode))
				{
					context.SetError(PortableHtmlSchemaErorr.InvalidBrPosition, e);
				}
			}
		}