在解析之后,我想删除危险的代码并再次将其格式化。
目的是防止脚本通过电子邮件进入,但仍然允许大量错误的HTML工作(至少不会完全失败)。
有没有图书馆?有没有更好的方法让脚本远离浏览器?
重要的是该程序不会抛出Parse异常。该程序可能会做出最好的猜测,即使它是错误的,也可以接受。
编辑:如果您认为哪些解析器更好以及为什么会有任何意见,我将不胜感激。
答案 0 :(得分:3)
对于灵活的解析,您可能需要查看JSoup。但白名单是这里的方式。如果你只是禁止一堆“危险”元素,有人可能会找到一种方法来解析你的解析器。相反,你应该只允许一小部分安全元素。
答案 1 :(得分:0)
使用一种将HTML转换为XHTML的可用工具。
例如
http://www.chilkatsoft.com/java-html.asp
http://java-source.net/open-source/html-parsers
http://htmlcleaner.sourceforge.net/
等
然后使用常规XML解析器。
答案 2 :(得分:0)
我为此目的使用Jericho HTML parser。
他们的消毒剂示例的一些调整版本:
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 + '>';
}
}
答案 3 :(得分:0)
查看http://nekohtml.sourceforge.net/它具有内置的标记平衡功能。 另请查看Nekohtml http://nekohtml.sourceforge.net/filters.html#filters.removing的自定义过滤器部分。这是一个非常好的HTML解析器。