diff --git a/pom.xml b/pom.xml
index f28bda08..05af1f94 100644
--- a/pom.xml
+++ b/pom.xml
@@ -238,6 +238,11 @@
gwt-servlet
2.5.1
+
+ net.sourceforge.cssparser
+ cssparser
+ 0.9.9
+
com.google.oauth-client
diff --git a/src/main/java/com/commafeed/backend/feeds/FeedUtils.java b/src/main/java/com/commafeed/backend/feeds/FeedUtils.java
index 4cd295c2..2ebeb323 100644
--- a/src/main/java/com/commafeed/backend/feeds/FeedUtils.java
+++ b/src/main/java/com/commafeed/backend/feeds/FeedUtils.java
@@ -1,5 +1,8 @@
package com.commafeed.backend.feeds;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
@@ -11,12 +14,17 @@ import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.math.stat.descriptive.SummaryStatistics;
import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
import org.jsoup.nodes.Document.OutputSettings;
+import org.jsoup.nodes.Element;
import org.jsoup.nodes.Entities.EscapeMode;
+import org.jsoup.safety.Cleaner;
import org.jsoup.safety.Whitelist;
import org.mozilla.universalchardet.UniversalDetector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.w3c.css.sac.InputSource;
+import org.w3c.dom.css.CSSStyleDeclaration;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedEntry;
@@ -24,10 +32,15 @@ import com.commafeed.backend.model.FeedSubscription;
import com.google.api.client.util.Lists;
import com.google.gwt.i18n.client.HasDirection.Direction;
import com.google.gwt.i18n.shared.BidiUtils;
+import com.steadystate.css.parser.CSSOMParser;
public class FeedUtils {
protected static Logger log = LoggerFactory.getLogger(FeedUtils.class);
+ private static final List ALLOWED_IFRAME_CSS_RULES = Arrays.asList(
+ "height", "width", "border");
+ private static final char[] DISALLOWED_IFRAME_CSS_RULE_CHARACTERS = new char[] {
+ '(', ')' };
public static String truncate(String string, int length) {
if (string != null) {
@@ -72,7 +85,7 @@ public class FeedUtils {
whitelist.addAttributes("col", "span", "width");
whitelist.addAttributes("colgroup", "span", "width");
whitelist.addAttributes("iframe", "src", "height", "width",
- "allowfullscreen", "frameborder");
+ "allowfullscreen", "frameborder", "style");
whitelist.addAttributes("img", "alt", "height", "src", "title",
"width");
whitelist.addAttributes("ol", "start", "type");
@@ -93,13 +106,52 @@ public class FeedUtils {
whitelist.addEnforcedAttribute("a", "target", "_blank");
- content = Jsoup.clean(content, baseUri, whitelist,
- new OutputSettings().escapeMode(EscapeMode.base)
- .prettyPrint(false));
+ Document dirty = Jsoup.parseBodyFragment(content, baseUri);
+ Cleaner cleaner = new Cleaner(whitelist);
+ Document clean = cleaner.clean(dirty);
+
+ for (Element e : clean.select("iframe[style]")) {
+ String style = e.attr("style");
+ String escaped = escapeIFrameCss(style);
+ e.attr("style", escaped);
+ }
+
+ clean.outputSettings(new OutputSettings().escapeMode(
+ EscapeMode.base).prettyPrint(false));
+ content = clean.body().html();
+
}
return content;
}
+ public static String escapeIFrameCss(String orig) {
+ List rules = Lists.newArrayList();
+ CSSOMParser parser = new CSSOMParser();
+ try {
+ CSSStyleDeclaration decl = parser
+ .parseStyleDeclaration(new InputSource(new StringReader(
+ orig)));
+
+ for (int i = 0; i < decl.getLength(); i++) {
+ String property = decl.item(i);
+ String value = decl.getPropertyValue(property);
+ if (StringUtils.isBlank(property) || StringUtils.isBlank(value)) {
+ continue;
+ }
+
+ if (ALLOWED_IFRAME_CSS_RULES.contains(property)
+ && StringUtils.containsNone(value,
+ DISALLOWED_IFRAME_CSS_RULE_CHARACTERS)) {
+ rules.add(property + ":" + decl.getPropertyValue(property)
+ + ";");
+ }
+ }
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ return StringUtils.join(rules, "");
+ }
+
public static FeedEntry findEntry(Collection list,
FeedEntry entry) {
FeedEntry found = null;
@@ -267,4 +319,5 @@ public class FeedUtils {
return removeTrailingSlash(publicUrl) + "/rest/feed/favicon/"
+ subscription.getId();
}
+
}