private static void CheckRootTextNodes(PortableHtmlContext context) { if (context.SourceXml.Nodes().OfType <XText>().Any()) { context.SetError(PortableHtmlSchemaErorr.RootText); } }
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); } } }
/// <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"); } } } }
/// <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); } }
/// <summary> /// Проверка на отсутствие комментариев /// </summary> private static void CheckNoComments(PortableHtmlContext context) { var hasComment = context.SourceXml.DescendantNodes().OfType <XComment>().Any(); if (hasComment) { context.SetError(PortableHtmlSchemaErorr.CommentsDetected); } }
/// <summary> /// Проверка на отсутствие инструкций препроцессора /// </summary> private static void CheckNoProcessingInstructions(PortableHtmlContext context) { var hasProcessingInstructions = context.SourceXml.DescendantNodes().OfType <XProcessingInstruction>().Any(); if (hasProcessingInstructions) { context.SetError(PortableHtmlSchemaErorr.ProcessingInstructionsDetected); } }
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); } } }
/// <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"); }
/// <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"); }
/// <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); } } } }
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); } } } }
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> /// Проверка валидности настройки ссылок у 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 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); } } }
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 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); } } } }
/// <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)); } }
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); } }
/// <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); } } }