Java 解析格式错误的XML文档(如HTML文件)
在解析之后,我想删除危险的代码,并以正确的格式再次写出它 其目的是防止脚本通过电子邮件进入,但仍然允许大量糟糕的HTML正常工作(至少不会完全失败) 有图书馆吗?有没有更好的方法让脚本远离浏览器 重要的是程序不会抛出解析异常。程序可能会做出最好的猜测,即使它是错误的,也是可以接受的Java 解析格式错误的XML文档(如HTML文件),java,html,xml,xml-parsing,Java,Html,Xml,Xml Parsing,在解析之后,我想删除危险的代码,并以正确的格式再次写出它 其目的是防止脚本通过电子邮件进入,但仍然允许大量糟糕的HTML正常工作(至少不会完全失败) 有图书馆吗?有没有更好的方法让脚本远离浏览器 重要的是程序不会抛出解析异常。程序可能会做出最好的猜测,即使它是错误的,也是可以接受的 编辑:我非常感谢大家对哪些解析器更好以及为什么更好的评论。使用一种可用的工具将HTML转换为XHTML 比如说 等 然后使用常规XML解析器。进行灵活的解析。但白名单才是解决问题的办法。如果您只是不允许一堆“危
编辑:我非常感谢大家对哪些解析器更好以及为什么更好的评论。使用一种可用的工具将HTML转换为XHTML 比如说 等
然后使用常规XML解析器。进行灵活的解析。但白名单才是解决问题的办法。如果您只是不允许一堆“危险”的元素,那么可能有人会找到一种方法,通过您的解析器来隐藏某些内容。相反,您应该只允许一小部分安全元素。为此,我使用 对他们的消毒剂示例进行了一些调整:
public class HtmlSanitizer {
private HtmlSanitizer() {
}
private static final Set<String> VALID_ELEMENTS = Sets.newHashSet(DIV, BR,
P, B, I, OL, UL, LI, A, STRONG, SPAN, EM, TT, IMG);
private static final Set<String> VALID_ATTRIBUTES = Sets.newHashSet("id",
"class", "href", "target", "title", "src");
private static final Object VALID_MARKER = new Object();
public static void sanitize(Reader r, Writer w) {
try {
sanitize(new Source(r)).writeTo(w);
w.flush();
r.close();
} catch (IOException ioe) {
throw new RuntimeException("error during sanitize", ioe);
}
}
public static OutputDocument sanitize(Source source) {
source.fullSequentialParse();
OutputDocument doc = new OutputDocument(source);
List<Tag> tags = source.getAllTags();
int pos = 0;
for (Tag tag : tags) {
if (processTag(tag, doc))
tag.setUserData(VALID_MARKER);
else
doc.remove(tag);
reencodeTextSegment(source, doc, pos, tag.getBegin());
pos = tag.getEnd();
}
reencodeTextSegment(source, doc, pos, source.getEnd());
return doc;
}
private static boolean processTag(Tag tag, OutputDocument doc) {
String elementName = tag.getName();
if (!VALID_ELEMENTS.contains(elementName))
return false;
if (tag.getTagType() == StartTagType.NORMAL) {
Element element = tag.getElement();
if (HTMLElements.getEndTagRequiredElementNames().contains(
elementName)) {
if (element.getEndTag() == null)
return false;
} else if (HTMLElements.getEndTagOptionalElementNames().contains(
elementName)) {
if (elementName == HTMLElementName.LI && !isValidLITag(tag))
return false;
if (element.getEndTag() == null)
doc.insert(element.getEnd(), getEndTagHTML(elementName));
}
doc.replace(tag, getStartTagHTML(element.getStartTag()));
} else if (tag.getTagType() == EndTagType.NORMAL) {
if (tag.getElement() == null)
return false;
if (elementName == HTMLElementName.LI && !isValidLITag(tag))
return false;
doc.replace(tag, getEndTagHTML(elementName));
} else {
return false;
}
return true;
}
private static boolean isValidLITag(Tag tag) {
Element parentElement = tag.getElement().getParentElement();
if (parentElement == null
|| parentElement.getStartTag().getUserData() != VALID_MARKER)
return false;
return parentElement.getName() == HTMLElementName.UL
|| parentElement.getName() == HTMLElementName.OL;
}
private static void reencodeTextSegment(Source source, OutputDocument doc,
int begin, int end) {
if (begin >= end)
return;
Segment textSegment = new Segment(source, begin, end);
String encodedText = encode(decode(textSegment));
doc.replace(textSegment, encodedText);
}
private static CharSequence getStartTagHTML(StartTag startTag) {
StringBuilder sb = new StringBuilder();
sb.append('<').append(startTag.getName());
for (Attribute attribute : startTag.getAttributes()) {
if (VALID_ATTRIBUTES.contains(attribute.getKey())) {
sb.append(' ').append(attribute.getName());
if (attribute.getValue() != null) {
sb.append("=\"");
sb.append(CharacterReference.encode(attribute.getValue()));
sb.append('"');
}
}
}
if (startTag.getElement().getEndTag() == null
&& !HTMLElements.getEndTagOptionalElementNames().contains(
startTag.getName()))
sb.append('/');
sb.append('>');
return sb;
}
private static String getEndTagHTML(String tagName) {
return "</" + tagName + '>';
}
}
公共类HtmlSanitizer{
私有HtmlSanitizer(){
}
私有静态最终集VALID_ELEMENTS=Sets.newHashSet(DIV,BR,
P、 B,I,OL,UL,LI,A,STRONG,SPAN,EM,TT,IMG);
private static final Set VALID_ATTRIBUTES=Sets.newHashSet(“id”,
“类”、“href”、“目标”、“标题”、“src”);
私有静态最终对象有效_标记=新对象();
公共静态无效清理(读卡器r、写卡器w){
试一试{
消毒(新来源(r))。写入(w);
w、 冲洗();
r、 close();
}捕获(ioe异常ioe){
抛出新的运行时异常(“清理期间出错”,ioe);
}
}
公共静态输出文档清理(源){
source.fullSequentialParse();
OutputDocument单据=新的OutputDocument(来源);
List tags=source.getAllTags();
int pos=0;
用于(标记:标记){
if(processTag(tag,doc))
tag.setUserData(有效的_标记);
其他的
文件移除(标签);
reencodeTextSegment(source、doc、pos、tag.getBegin());
pos=tag.getEnd();
}
reencodeTextSegment(source、doc、pos、source.getEnd());
退货单;
}
私有静态布尔processTag(Tag标签,OutputDocument文档){
String elementName=tag.getName();
如果(!VALID_ELEMENTS.contains(elementName))
返回false;
if(tag.getTagType()==StartTagType.NORMAL){
Element=tag.getElement();
如果(HTMLElements.getEndTagRequiredElementNames()包含(
元素名称){
if(element.getEndTag()==null)
返回false;
}else if(HTMLElements.getEndTagOptionalElementNames()包含(
元素名称){
if(elementName==HTMLElementName.LI&&!isValidLITag(标记))
返回false;
if(element.getEndTag()==null)
insert(element.getEnd(),getEndTagHTML(elementName));
}
doc.replace(tag,getStartTagHTML(element.getStartTag());
}else if(tag.getTagType()==EndTagType.NORMAL){
if(tag.getElement()==null)
返回false;
if(elementName==HTMLElementName.LI&&!isValidLITag(标记))
返回false;
doc.replace(tag,getEndTagHTML(elementName));
}否则{
返回false;
}
返回true;
}
私有静态布尔值isValidLITag(标记标记){
元素parentElement=tag.getElement().getParentElement();
if(parentElement==null
||parentElement.getStartTag().getUserData()!=有效的\u标记)
返回false;
返回parentElement.getName()==HTMLElementName.UL
||parentElement.getName()==HTMLElementName.OL;
}
私有静态void reencodeTextSegment(源源、输出文档文档、,
整数开始,整数结束){
如果(开始>=结束)
返回;
段文本段=新段(源、开始、结束);
字符串encodedText=encode(解码(文本段));
文件替换(文本段,编码文本);
}
私有静态字符序列getStartTagHTML(StartTag StartTag){
StringBuilder sb=新的StringBuilder();
sb.append(“看一看,它具有内置的标记平衡功能。
另外,请检查Nekohtml的自定义筛选器部分。它是一个非常好的html解析器。“白名单就是要走的路”,对,我同意:)