You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by jo...@apache.org on 2010/03/08 18:54:33 UTC
svn commit: r920421 - in /shindig/trunk/java/gadgets/src:
main/java/org/apache/shindig/gadgets/render/
main/java/org/apache/shindig/gadgets/render/old/
main/java/org/apache/shindig/gadgets/rewrite/
main/java/org/apache/shindig/gadgets/templates/ main/j...
Author: johnh
Date: Mon Mar 8 17:54:32 2010
New Revision: 920421
URL: http://svn.apache.org/viewvc?rev=920421&view=rev
Log:
Same treatment; move all "old"-based (LinkRewriter-using, etc.) render/ classes to "old" package ahead of refactoring/reimpl.
The last remaining hook from non-"old" to "old" code is in template-handling code which uses SanitizingGadgetRewriter.bypassSanitization(elem, flag),
whose impl is the same between new and old versions, thereby still allowing the ability to switch between impls.
Added:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/DefaultSanitizingProxyingLinkRewriterFactory.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingGadgetRewriter.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingProxyingLinkRewriter.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingProxyingLinkRewriterFactory.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingRequestRewriter.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/SanitizingGadgetRewriterTest.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/SanitizingRequestRewriterTest.java
Removed:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/DefaultSanitizingProxyingLinkRewriterFactory.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/SanitizingGadgetRewriter.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/SanitizingProxyingLinkRewriter.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/SanitizingProxyingLinkRewriterFactory.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/SanitizingRequestRewriter.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/SanitizingGadgetRewriterTest.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/SanitizingRequestRewriterTest.java
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderModule.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/XmlTemplateLibrary.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/tags/FlashTagHandler.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessorTest.java
Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderModule.java?rev=920421&r1=920420&r2=920421&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderModule.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderModule.java Mon Mar 8 17:54:32 2010
@@ -20,6 +20,8 @@
import java.util.Set;
+import org.apache.shindig.gadgets.render.old.SanitizingGadgetRewriter;
+
import com.google.common.collect.ImmutableSet;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/DefaultSanitizingProxyingLinkRewriterFactory.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/DefaultSanitizingProxyingLinkRewriterFactory.java?rev=920421&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/DefaultSanitizingProxyingLinkRewriterFactory.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/DefaultSanitizingProxyingLinkRewriterFactory.java Mon Mar 8 17:54:32 2010
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.render.old;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.rewrite.ContentRewriterFeature;
+import org.apache.shindig.gadgets.rewrite.old.ContentRewriterUris;
+
+@Singleton
+public class DefaultSanitizingProxyingLinkRewriterFactory implements
+ SanitizingProxyingLinkRewriterFactory {
+
+ private final ContentRewriterUris rewriterUris;
+
+ @Inject
+ public DefaultSanitizingProxyingLinkRewriterFactory(
+ ContentRewriterUris rewriterUris) {
+ this.rewriterUris = rewriterUris;
+ }
+
+ public SanitizingProxyingLinkRewriter create(Uri gadgetUri,
+ ContentRewriterFeature.Config rewriterFeature, String container,
+ String expectedMime, boolean debug, boolean nocache) {
+ return new SanitizingProxyingLinkRewriter(rewriterUris, gadgetUri,
+ rewriterFeature, container, expectedMime, debug, nocache);
+ }
+
+}
Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingGadgetRewriter.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingGadgetRewriter.java?rev=920421&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingGadgetRewriter.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingGadgetRewriter.java Mon Mar 8 17:54:32 2010
@@ -0,0 +1,445 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.render.old;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.parse.caja.CajaCssSanitizer;
+import org.apache.shindig.gadgets.rewrite.ContentRewriterFeature;
+import org.apache.shindig.gadgets.rewrite.GadgetRewriter;
+import org.apache.shindig.gadgets.rewrite.MutableContent;
+import org.apache.shindig.gadgets.rewrite.old.LinkRewriter;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.UserDataHandler;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableList;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.Inject;
+
+/**
+ * A content rewriter that will sanitize output for simple 'badge' like display.
+ *
+ * This is intentionally not as robust as Caja. It is a simple element whitelist. It can not be used
+ * for sanitizing either javascript or CSS. CSS is desired in the long run, but it can't be proven
+ * safe in the short term.
+ *
+ * Generally used in conjunction with a gadget that gets its dynamic behavior externally (proxied
+ * rendering, OSML, etc.)
+ */
+public class SanitizingGadgetRewriter implements GadgetRewriter {
+
+ /** Key stored as element user-data to bypass sanitization */
+ private static final String BYPASS_SANITIZATION_KEY = "shindig.bypassSanitization";
+
+ /**
+ * Is the Gadget to be rendered sanitized?
+ * @return true if sanitization will be enabled
+ */
+ public static boolean isSanitizedRenderingRequest(Gadget gadget) {
+ return ("1".equals(gadget.getContext().getParameter("sanitize")));
+ }
+
+ /**
+ * Marks that an element and all its attributes are trusted content.
+ * This status is preserved across {@link Node#cloneNode} calls. Be
+ * extremely careful when using this, especially with {@code includingChildren}
+ * set to {@code true}, as untrusted content that gets inserted (e.g, via
+ * os:RenderAll in templating) would become trusted.
+ *
+ * @param element the trusted element
+ * @param includingChildren if true, children of this element will are also
+ * trusted. Never set this to true on an element that will ever have
+ * untrusted children inserted (e.g., if it contains or may contain os:Render).
+ */
+ public static void bypassSanitization(Element element, boolean includingChildren) {
+ element.setUserData(BYPASS_SANITIZATION_KEY,
+ includingChildren ? Bypass.ALL : Bypass.ONLY_SELF, copyOnClone);
+ }
+
+ private static enum Bypass { ALL, ONLY_SELF, NONE }
+
+ private static UserDataHandler copyOnClone = new UserDataHandler() {
+ public void handle(short operation, String key, Object data, Node src, Node dst) {
+ if (operation == NODE_IMPORTED || operation == NODE_CLONED) {
+ dst.setUserData(key, data, copyOnClone);
+ }
+ }
+ };
+
+ private final Set<String> allowedTags;
+ private final Set<String> allowedAttributes;
+ private final CajaCssSanitizer cssSanitizer;
+ private final ContentRewriterFeature.Factory rewriterFeatureFactory;
+ private final SanitizingProxyingLinkRewriterFactory sanitizingProxyingLinkRewriterFactory;
+
+ @Inject
+ public SanitizingGadgetRewriter(@AllowedTags Set<String> allowedTags,
+ @AllowedAttributes Set<String> allowedAttributes,
+ ContentRewriterFeature.Factory rewriterFeatureFactory,
+ CajaCssSanitizer cssSanitizer,
+ SanitizingProxyingLinkRewriterFactory sanitizingProxyingLinkRewriterFactory) {
+ this.allowedTags = allowedTags;
+ this.allowedAttributes = allowedAttributes;
+ this.cssSanitizer = cssSanitizer;
+ this.rewriterFeatureFactory = rewriterFeatureFactory;
+ this.sanitizingProxyingLinkRewriterFactory = sanitizingProxyingLinkRewriterFactory;
+ }
+
+
+ public void rewrite(Gadget gadget, MutableContent content) {
+ if (gadget.sanitizeOutput()) {
+ boolean sanitized = false;
+ try {
+ new NodeSanitizer(gadget).sanitize(content.getDocument().getDocumentElement());
+ content.documentChanged();
+ sanitized = true;
+ } finally {
+ // Defensively clean the content in case of failure
+ if (!sanitized) {
+ content.setContent("");
+ }
+ }
+ }
+ }
+
+ /**
+ * Utiliity class to sanitize HTML nodes recursively.
+ */
+ class NodeSanitizer {
+ private final Uri context;
+ private final List<DomFilter> filters;
+
+ NodeSanitizer(Gadget gadget) {
+ this.context = gadget.getSpec().getUrl();
+ Integer expires = rewriterFeatureFactory.getDefault().getExpires();
+ ContentRewriterFeature.Config rewriterFeature =
+ rewriterFeatureFactory.createRewriteAllFeature(expires == null ? -1 : expires);
+
+ SanitizingProxyingLinkRewriter cssImportRewriter = sanitizingProxyingLinkRewriterFactory
+ .create(gadget.getSpec().getUrl(), rewriterFeature, gadget
+ .getContext().getContainer(), "text/css", gadget.getContext()
+ .getDebug(), gadget.getContext().getIgnoreCache());
+ SanitizingProxyingLinkRewriter imageRewriter = sanitizingProxyingLinkRewriterFactory
+ .create(gadget.getSpec().getUrl(), rewriterFeature, gadget
+ .getContext().getContainer(), "image/*", gadget.getContext()
+ .getDebug(), gadget.getContext().getIgnoreCache());
+
+ // Create the set of filters to process in order.
+ filters = ImmutableList.of(
+ new BasicElementFilter(allowedTags, allowedAttributes),
+ new LinkSchemeCheckFilter(),
+ new StyleFilter(cssSanitizer, cssImportRewriter, imageRewriter),
+ new LinkFilter(cssImportRewriter),
+ new ImageFilter(imageRewriter),
+ new TargetFilter()
+ );
+ }
+
+ private void sanitize(Node node) {
+ switch (node.getNodeType()) {
+ case Node.CDATA_SECTION_NODE:
+ case Node.TEXT_NODE:
+ case Node.ENTITY_REFERENCE_NODE:
+ break;
+ case Node.ELEMENT_NODE:
+ case Node.DOCUMENT_NODE:
+ Element element = (Element) node;
+ Bypass bypass = canBypassSanitization(element);
+ if (bypass == Bypass.ALL) {
+ return;
+ } else if (bypass == Bypass.ONLY_SELF) {
+ for (Node child : toList(node.getChildNodes())) {
+ sanitize(child);
+ }
+ } else {
+ boolean removed = false;
+ for (DomFilter filter : filters) {
+ DomFilter.Result tagResult = filter.filterTag(element, context);
+ if (tagResult == DomFilter.Result.REMOVE) {
+ element.getParentNode().removeChild(element);
+ removed = true;
+ break;
+ } else if (tagResult == DomFilter.Result.CONTINUE) {
+ for (Attr attr : toList(node.getAttributes())) {
+ DomFilter.Result attrResult = filter.filterAttribute(attr, context);
+ if (attrResult == DomFilter.Result.PASS) {
+ break; // No need to process more attributes
+ } else if (attrResult == DomFilter.Result.REMOVE) {
+ element.removeAttributeNode(attr);
+ }
+ }
+ }
+ }
+ if (!removed) {
+ for (Node child : toList(node.getChildNodes())) {
+ sanitize(child);
+ }
+ }
+ }
+ break;
+ case Node.COMMENT_NODE:
+ default:
+ // Must remove all comments to avoid conditional comment evaluation.
+ // There might be other, unknown types as well. Don't trust them.
+ node.getParentNode().removeChild(node);
+ break;
+ }
+ }
+ }
+
+
+ /** Convert a NamedNodeMap to a list for easy and safe operations */
+ private static List<Attr> toList(NamedNodeMap nodes) {
+ List<Attr> list = new ArrayList<Attr>(nodes.getLength());
+
+ for (int i = 0, j = nodes.getLength(); i < j; ++i) {
+ list.add((Attr) nodes.item(i));
+ }
+
+ return list;
+ }
+
+ /** Convert a NamedNodeMap to a list for easy and safe operations */
+ private static List<Node> toList(NodeList nodes) {
+ List<Node> list = new ArrayList<Node>(nodes.getLength());
+
+ for (int i = 0, j = nodes.getLength(); i < j; ++i) {
+ list.add(nodes.item(i));
+ }
+
+ return list;
+ }
+
+ private static Bypass canBypassSanitization(Element element) {
+ Bypass bypass = (Bypass) element.getUserData(BYPASS_SANITIZATION_KEY);
+ if (bypass == null) {
+ bypass = Bypass.NONE;
+ }
+ return bypass;
+ }
+
+ /**
+ * Filter DOM elements and attributes to check their validity and
+ * restrict their allowed content
+ */
+ static interface DomFilter {
+ enum Result {
+ PASS, // Check passed, do not process further checks on this filter
+ CONTINUE, // Check passed, process further checks on this filter
+ REMOVE // Check failed, remove item
+ }
+
+ /**
+ * Filter the element and possibly transform it.
+ */
+ Result filterTag(Element elem, Uri context);
+
+ /**
+ * Filter the attribute and possibly transform it
+\ */
+ Result filterAttribute(Attr attr, Uri context);
+ }
+
+ /**
+ * Restrict the set of allowed tags and attributes
+ */
+ static class BasicElementFilter implements DomFilter {
+ final Set<String> allowedTags;
+ final Set<String> allowedAttrs;
+
+ BasicElementFilter(Set<String> allowedTags, Set<String> allowedAttrs) {
+ this.allowedTags = allowedTags;
+ this.allowedAttrs = allowedAttrs;
+ }
+
+ public Result filterTag(Element elem, Uri context) {
+ return allowedTags.contains(elem.getNodeName().toLowerCase()) ?
+ Result.CONTINUE : Result.REMOVE;
+ }
+
+ public Result filterAttribute(Attr attr, Uri context) {
+ return allowedAttrs.contains(attr.getName().toLowerCase()) ?
+ Result.CONTINUE : Result.REMOVE;
+ }
+ }
+
+ /**
+ * Enfore that all uri's in the document have either http or https as
+ * their scheme
+ */
+ static class LinkSchemeCheckFilter implements DomFilter {
+ protected Set<String> uriAttributes;
+
+ LinkSchemeCheckFilter() {
+ uriAttributes = ImmutableSet.of("href", "src");
+ }
+
+ public Result filterTag(Element elem, Uri context) {
+ return Result.CONTINUE;
+ }
+
+ public Result filterAttribute(Attr attr, Uri context) {
+ if (uriAttributes.contains(attr.getName().toLowerCase())) {
+ try {
+ Uri uri = Uri.parse(attr.getValue());
+ String scheme = uri.getScheme();
+ if (scheme != null && !scheme.equals("http") && !scheme.equals("https")) {
+ return Result.REMOVE;
+ }
+ } catch (IllegalArgumentException iae) {
+ return Result.REMOVE;
+ }
+ }
+ return Result.CONTINUE;
+ }
+ }
+
+ /**
+ * Enfore that all images in the document are rewritten through the proxy.
+ * Prevents issues in IE where the image content contains script
+ */
+ static class ImageFilter implements DomFilter {
+ protected final LinkRewriter imageRewriter;
+
+ ImageFilter(LinkRewriter imageRewriter) {
+ this.imageRewriter = imageRewriter;
+ }
+
+ public Result filterTag(Element elem, Uri context) {
+ if ("img".equalsIgnoreCase(elem.getNodeName())) {
+ return Result.CONTINUE;
+ }
+ return Result.PASS;
+ }
+
+ public Result filterAttribute(Attr attr, Uri context) {
+ if ("src".equalsIgnoreCase(attr.getName())) {
+ attr.setValue(imageRewriter.rewrite(attr.getValue(), context));
+ }
+ return Result.PASS;
+ }
+ }
+
+ /**
+ * Pass the contents of style tags through the CSS sanitizer
+ */
+ static class StyleFilter implements DomFilter {
+ final CajaCssSanitizer sanitizer;
+ final LinkRewriter importRewriter;
+ final LinkRewriter imageRewriter;
+
+ StyleFilter(CajaCssSanitizer sanitizer, LinkRewriter importRewriter,
+ LinkRewriter imageRewriter) {
+ this.sanitizer = sanitizer;
+ this.importRewriter = importRewriter;
+ this.imageRewriter = imageRewriter;
+ }
+
+ public Result filterTag(Element elem, Uri context) {
+ if ("style".equalsIgnoreCase(elem.getNodeName())) {
+ sanitizer.sanitize(elem, context, importRewriter, imageRewriter);
+ }
+ return Result.PASS;
+ }
+
+ public Result filterAttribute(Attr attr, Uri context) {
+ return Result.PASS;
+ }
+ }
+
+ /**
+ * Restrict link tags to stylesheet content only and force the link to
+ * be rewritten through the proxy and sanitized
+ */
+ static class LinkFilter implements DomFilter {
+ final LinkRewriter rewriter;
+
+ LinkFilter(LinkRewriter rewriter) {
+ this.rewriter = rewriter;
+ }
+
+ public Result filterTag(Element elem, Uri context) {
+ if (!elem.getNodeName().equalsIgnoreCase("link")) {
+ return Result.CONTINUE;
+ }
+ boolean hasType = false;
+ for (Attr attr : toList(elem.getAttributes())) {
+ if ("rel".equalsIgnoreCase(attr.getName())) {
+ hasType |= "stylesheet".equalsIgnoreCase(attr.getValue());
+ } else if ("type".equalsIgnoreCase(attr.getName())) {
+ hasType |= "text/css".equalsIgnoreCase(attr.getValue());
+ } else if ("href".equalsIgnoreCase(attr.getName())) {
+ attr.setValue(rewriter.rewrite(attr.getValue(), context));
+ }
+ }
+ return hasType ? Result.PASS : Result.REMOVE;
+ }
+
+ public Result filterAttribute(Attr attr, Uri context) {
+ return Result.PASS;
+ }
+ }
+
+ /**
+ * Restrict the value of the target attribute on anchors etc. to
+ * _blank or _self or remove the node
+ */
+ static class TargetFilter implements DomFilter {
+
+ TargetFilter() {
+ }
+
+ public Result filterTag(Element elem, Uri context) {
+ return Result.CONTINUE;
+ }
+
+ public Result filterAttribute(Attr attr, Uri context) {
+ if ("target".equalsIgnoreCase(attr.getName())) {
+ String value = attr.getValue().toLowerCase();
+ if (!("_blank".equals(value) || "_self".equals(value))) {
+ return Result.REMOVE;
+ }
+ }
+ return Result.CONTINUE;
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.PARAMETER)
+ @BindingAnnotation
+ public @interface AllowedTags { }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.PARAMETER)
+ @BindingAnnotation
+ public @interface AllowedAttributes { }
+}
Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingProxyingLinkRewriter.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingProxyingLinkRewriter.java?rev=920421&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingProxyingLinkRewriter.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingProxyingLinkRewriter.java Mon Mar 8 17:54:32 2010
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.shindig.gadgets.render.old;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.rewrite.ContentRewriterFeature;
+import org.apache.shindig.gadgets.rewrite.old.ContentRewriterUris;
+import org.apache.shindig.gadgets.rewrite.old.ProxyingLinkRewriter;
+import org.apache.shindig.gadgets.servlet.ProxyBase;
+
+/**
+ * Forcible rewrite the link through the proxy and force sanitization with
+ * an expected mime type
+ */
+public class SanitizingProxyingLinkRewriter extends ProxyingLinkRewriter {
+
+ private final String expectedMime;
+
+ public SanitizingProxyingLinkRewriter(ContentRewriterUris rewriterUris,
+ Uri gadgetUri, ContentRewriterFeature.Config rewriterFeature, String container,
+ String expectedMime, boolean debug, boolean nocache) {
+ super(rewriterUris, gadgetUri, rewriterFeature, container, debug, nocache);
+ this.expectedMime = expectedMime;
+ }
+
+ @Override
+ public String rewrite(String link, Uri context) {
+ try {
+ Uri.parse(link);
+ } catch (RuntimeException re) {
+ // Any failure in parse
+ return "about:blank";
+ }
+ String rewritten = super.rewrite(link, context);
+ rewritten += '&' + ProxyBase.SANITIZE_CONTENT_PARAM + "=1";
+ rewritten += '&' + ProxyBase.REWRITE_MIME_TYPE_PARAM + '=' + expectedMime;
+ return rewritten;
+ }
+}
\ No newline at end of file
Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingProxyingLinkRewriterFactory.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingProxyingLinkRewriterFactory.java?rev=920421&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingProxyingLinkRewriterFactory.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingProxyingLinkRewriterFactory.java Mon Mar 8 17:54:32 2010
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.render.old;
+
+import com.google.inject.ImplementedBy;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.rewrite.ContentRewriterFeature;
+
+@ImplementedBy(DefaultSanitizingProxyingLinkRewriterFactory.class)
+public interface SanitizingProxyingLinkRewriterFactory {
+ public SanitizingProxyingLinkRewriter create(Uri gadgetUri,
+ ContentRewriterFeature.Config rewriterFeature, String container,
+ String expectedMime, boolean debug, boolean nocache);
+}
Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingRequestRewriter.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingRequestRewriter.java?rev=920421&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingRequestRewriter.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/old/SanitizingRequestRewriter.java Mon Mar 8 17:54:32 2010
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.render.old;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.sanselan.ImageFormat;
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.Sanselan;
+import org.apache.sanselan.common.byteSources.ByteSourceInputStream;
+import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.parse.caja.CajaCssSanitizer;
+import org.apache.shindig.gadgets.rewrite.ContentRewriterFeature;
+import org.apache.shindig.gadgets.rewrite.MutableContent;
+import org.apache.shindig.gadgets.rewrite.RequestRewriter;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.google.inject.Inject;
+
+/**
+ * Rewriter that sanitizes CSS and image content.
+ */
+public class SanitizingRequestRewriter implements RequestRewriter {
+ private static final Logger logger =
+ Logger.getLogger(SanitizingRequestRewriter.class.getName());
+
+ private final CajaCssSanitizer cssSanitizer;
+ private final ContentRewriterFeature.Factory rewriterFeatureFactory;
+ private final SanitizingProxyingLinkRewriterFactory sanitizingProxyingLinkRewriterFactory;
+
+ @Inject
+ public SanitizingRequestRewriter(
+ ContentRewriterFeature.Factory rewriterFeatureFactory,
+ CajaCssSanitizer cssSanitizer,
+ SanitizingProxyingLinkRewriterFactory sanitizingProxyingLinkRewriterFactory) {
+ this.cssSanitizer = cssSanitizer;
+ this.rewriterFeatureFactory = rewriterFeatureFactory;
+ this.sanitizingProxyingLinkRewriterFactory = sanitizingProxyingLinkRewriterFactory;
+ }
+
+ public boolean rewrite(HttpRequest request, HttpResponse resp, MutableContent content) {
+ // Content fetched through the proxy can stipulate that it must be sanitized.
+ if (request.isSanitizationRequested()) {
+ ContentRewriterFeature.Config rewriterFeature =
+ rewriterFeatureFactory.createRewriteAllFeature(request.getCacheTtl());
+ if (StringUtils.isEmpty(request.getRewriteMimeType())) {
+ logger.log(Level.WARNING, "Request to sanitize without content type for "
+ + request.getUri());
+ content.setContent("");
+ return true;
+ } else if (request.getRewriteMimeType().equalsIgnoreCase("text/css")) {
+ return rewriteProxiedCss(request, resp, content, rewriterFeature);
+ } else if (request.getRewriteMimeType().toLowerCase().startsWith("image/")) {
+ return rewriteProxiedImage(request, resp, content);
+ } else {
+ logger.log(Level.WARNING, "Request to sanitize unknown content type "
+ + request.getRewriteMimeType()
+ + " for " + request.getUri());
+ content.setContent("");
+ return true;
+ }
+ } else {
+ // No Op
+ return false;
+ }
+ }
+
+ /**
+ * We don't actually rewrite the image we just ensure that it is in fact a valid
+ * and known image type.
+ */
+ private boolean rewriteProxiedImage(HttpRequest request, HttpResponse resp,
+ MutableContent content) {
+ boolean imageIsSafe = false;
+ try {
+ String contentType = resp.getHeader("Content-Type");
+ if (contentType == null || contentType.toLowerCase().startsWith("image/")) {
+ // Unspecified or unknown image mime type.
+ try {
+ ImageFormat imageFormat = Sanselan
+ .guessFormat(new ByteSourceInputStream(resp.getResponse(),
+ request.getUri().getPath()));
+ if (imageFormat == ImageFormat.IMAGE_FORMAT_UNKNOWN) {
+ logger.log(Level.INFO, "Unable to sanitize unknown image type "
+ + request.getUri().toString());
+ return true;
+ }
+ imageIsSafe = true;
+ // Return false to indicate that no rewriting occurred
+ return false;
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ } catch (ImageReadException ire) {
+ // Unable to read the image so its not safe
+ logger.log(Level.INFO, "Unable to detect image type for " +request.getUri().toString() +
+ " for sanitized content", ire);
+ return true;
+ }
+ } else {
+ return true;
+ }
+ } finally {
+ if (!imageIsSafe) {
+ content.setContent("");
+ }
+ }
+ }
+
+ /**
+ * Sanitize a CSS file.
+ */
+ private boolean rewriteProxiedCss(HttpRequest request, HttpResponse response,
+ MutableContent content, ContentRewriterFeature.Config rewriterFeature) {
+ String sanitized = "";
+ try {
+ String contentType = response.getHeader("Content-Type");
+ if (contentType == null || contentType.toLowerCase().startsWith("text/")) {
+ SanitizingProxyingLinkRewriter cssImportRewriter = sanitizingProxyingLinkRewriterFactory
+ .create(request.getGadget(), rewriterFeature, request
+ .getContainer(), "text/css", false, request.getIgnoreCache());
+ SanitizingProxyingLinkRewriter cssImageRewriter = sanitizingProxyingLinkRewriterFactory
+ .create(request.getGadget(), rewriterFeature, request
+ .getContainer(), "image/*", false, request.getIgnoreCache());
+ sanitized = cssSanitizer.sanitize(content.getContent(), request.getUri(), cssImportRewriter,
+ cssImageRewriter);
+ }
+
+ return true;
+ } finally {
+ // Set sanitized content in finally to ensure it is always cleared in
+ // the case of errors
+ content.setContent(sanitized);
+ }
+ }
+}
Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java?rev=920421&r1=920420&r2=920421&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java Mon Mar 8 17:54:32 2010
@@ -20,8 +20,8 @@
import org.apache.shindig.gadgets.render.OpenSocialI18NGadgetRewriter;
import org.apache.shindig.gadgets.render.RenderingGadgetRewriter;
-import org.apache.shindig.gadgets.render.SanitizingGadgetRewriter;
-import org.apache.shindig.gadgets.render.SanitizingRequestRewriter;
+import org.apache.shindig.gadgets.render.old.SanitizingGadgetRewriter;
+import org.apache.shindig.gadgets.render.old.SanitizingRequestRewriter;
import org.apache.shindig.gadgets.rewrite.old.CssRequestRewriter;
import org.apache.shindig.gadgets.rewrite.old.HTMLContentRewriter;
import org.apache.shindig.gadgets.servlet.CajaContentRewriter;
Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java?rev=920421&r1=920420&r2=920421&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java Mon Mar 8 17:54:32 2010
@@ -27,7 +27,7 @@
import org.apache.shindig.gadgets.GadgetException;
import org.apache.shindig.gadgets.MessageBundleFactory;
import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
-import org.apache.shindig.gadgets.render.SanitizingGadgetRewriter;
+import org.apache.shindig.gadgets.render.old.SanitizingGadgetRewriter;
import org.apache.shindig.gadgets.spec.Feature;
import org.apache.shindig.gadgets.spec.MessageBundle;
import org.apache.shindig.gadgets.templates.ContainerTagLibraryFactory;
Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/XmlTemplateLibrary.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/XmlTemplateLibrary.java?rev=920421&r1=920420&r2=920421&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/XmlTemplateLibrary.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/XmlTemplateLibrary.java Mon Mar 8 17:54:32 2010
@@ -21,7 +21,7 @@
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.common.xml.DomUtil;
import org.apache.shindig.gadgets.GadgetException;
-import org.apache.shindig.gadgets.render.SanitizingGadgetRewriter;
+import org.apache.shindig.gadgets.render.old.SanitizingGadgetRewriter;
import org.apache.shindig.gadgets.templates.tags.DefaultTagRegistry;
import org.apache.shindig.gadgets.templates.tags.TagHandler;
import org.apache.shindig.gadgets.templates.tags.TemplateBasedTagHandler;
Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/tags/FlashTagHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/tags/FlashTagHandler.java?rev=920421&r1=920420&r2=920421&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/tags/FlashTagHandler.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/tags/FlashTagHandler.java Mon Mar 8 17:54:32 2010
@@ -31,7 +31,7 @@
import org.apache.shindig.common.JsonSerializer;
import org.apache.shindig.gadgets.features.FeatureRegistry;
import org.apache.shindig.gadgets.features.FeatureResource;
-import org.apache.shindig.gadgets.render.SanitizingGadgetRewriter;
+import org.apache.shindig.gadgets.render.old.SanitizingGadgetRewriter;
import org.apache.shindig.gadgets.templates.TemplateProcessor;
import org.apache.shindig.protocol.conversion.BeanJsonConverter;
import org.json.JSONObject;
Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/SanitizingGadgetRewriterTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/SanitizingGadgetRewriterTest.java?rev=920421&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/SanitizingGadgetRewriterTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/SanitizingGadgetRewriterTest.java Mon Mar 8 17:54:32 2010
@@ -0,0 +1,418 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.render.old;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.parse.caja.CajaCssParser;
+import org.apache.shindig.gadgets.parse.caja.CajaCssSanitizer;
+import org.apache.shindig.gadgets.render.old.DefaultSanitizingProxyingLinkRewriterFactory;
+import org.apache.shindig.gadgets.render.old.SanitizingGadgetRewriter;
+import org.apache.shindig.gadgets.rewrite.BaseRewriterTestCase;
+import org.apache.shindig.gadgets.rewrite.ContentRewriterFeature;
+import org.apache.shindig.gadgets.rewrite.GadgetRewriter;
+import org.apache.shindig.gadgets.rewrite.MutableContent;
+import org.apache.shindig.gadgets.servlet.ProxyBase;
+import org.apache.shindig.gadgets.spec.GadgetSpec;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SanitizingGadgetRewriterTest extends BaseRewriterTestCase {
+ private static final Set<String> DEFAULT_TAGS = ImmutableSet.of("html", "head", "body");
+ private static final Pattern BODY_REGEX = Pattern.compile(".*<body>(.+)</body>.*");
+
+ private final GadgetContext sanitaryGadgetContext = new GadgetContext() {
+ @Override
+ public String getParameter(String name) {
+ return ProxyBase.SANITIZE_CONTENT_PARAM.equals(name) ? "1" : null;
+ }
+
+ @Override
+ public String getContainer() {
+ return MOCK_CONTAINER;
+ }
+ };
+
+ private final GadgetContext unsanitaryGadgetContext = new GadgetContext();
+ private final GadgetContext unsanitaryGadgetContextNoCacheAndDebug = new GadgetContext(){
+ @Override
+ public boolean getIgnoreCache() {
+ return true;
+ }
+ @Override
+ public boolean getDebug() {
+ return true;
+ }
+ };
+ private Gadget gadget;
+ private Gadget gadgetNoCacheAndDebug;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ gadget = new Gadget().setContext(unsanitaryGadgetContext);
+ gadget.setSpec(new GadgetSpec(Uri.parse("www.example.org/gadget.xml"),
+ "<Module><ModulePrefs title=''/><Content type='x-html-sanitized'/></Module>"));
+ gadget.setCurrentView(gadget.getSpec().getViews().values().iterator().next());
+
+ gadgetNoCacheAndDebug = new Gadget().setContext(unsanitaryGadgetContextNoCacheAndDebug);
+ gadgetNoCacheAndDebug.setSpec(new GadgetSpec(Uri.parse("www.example.org/gadget.xml"),
+ "<Module><ModulePrefs title=''/><Content type='x-html-sanitized'/></Module>"));
+ gadgetNoCacheAndDebug.setCurrentView(gadgetNoCacheAndDebug.getSpec().getViews().values().iterator().next());
+}
+
+ private String rewrite(Gadget gadget, String content, Set<String> tags, Set<String> attributes)
+ throws Exception {
+ GadgetRewriter rewriter = createRewriter(tags, attributes);
+
+ MutableContent mc = new MutableContent(parser, content);
+ rewriter.rewrite(gadget, mc);
+
+ Matcher matcher = BODY_REGEX.matcher(mc.getContent());
+ if (matcher.matches()) {
+ return matcher.group(1);
+ }
+ return mc.getContent();
+ }
+
+ private static Set<String> set(String... items) {
+ return Sets.newHashSet(items);
+ }
+
+ private GadgetRewriter createRewriter(Set<String> tags, Set<String> attributes) {
+ Set<String> newTags = new HashSet<String>(tags);
+ newTags.addAll(DEFAULT_TAGS);
+ ContentRewriterFeature.Factory rewriterFeatureFactory =
+ new ContentRewriterFeature.Factory(null,
+ new ContentRewriterFeature.DefaultConfig(".*", "", "HTTP", "embed,img,script,link,style", "false", "false"));
+ return new SanitizingGadgetRewriter(newTags, attributes, rewriterFeatureFactory,
+ new CajaCssSanitizer(new CajaCssParser()), new DefaultSanitizingProxyingLinkRewriterFactory(rewriterUris));
+ }
+
+ @Test
+ public void enforceTagWhiteList() throws Exception {
+ String markup =
+ "<p><style type=\"text/css\">A { font : bold }</style>text <b>bold text</b></p>" +
+ "<b>Bold text</b><i>Italic text<b>Bold text</b></i>";
+
+ String sanitized = "<p>text <b>bold text</b></p><b>Bold text</b>";
+
+ assertEquals(sanitized, rewrite(gadget, markup, set("p", "b"), set()));
+ }
+
+ @Test
+ public void enforceStyleSanitized() throws Exception {
+ String markup =
+ "<p><style type=\"text/css\">A { font : bold; behavior : bad }</style>text <b>bold text</b></p>" +
+ "<b>Bold text</b><i>Italic text<b>Bold text</b></i>";
+
+ String sanitized = "<html><head></head><body><p><style>A {\n font: bold\n}</style>text " +
+ "<b>bold text</b></p><b>Bold text</b></body></html>";
+ assertEquals(sanitized, rewrite(gadget, markup, set("p", "b", "style"), set()));
+ }
+
+ @Test
+ public void enforceStyleLinkRewritten() throws Exception {
+ String markup =
+ "<link rel=\"stylesheet\" "
+ + "href=\"http://www.test.com/dir/proxy?"
+ + "url=http%3A%2F%2Fwww.evil.com%2Fx.css&gadget=www.example.org%2Fgadget.xml&"
+ + "fp=45508rewriteMime=text/css\"/>";
+ String sanitized =
+ "<html><head><link href=\"http://www.test.com/dir/proxy?"
+ + "url=http%3A%2F%2Fwww.evil.com%2Fx.css&gadget=www.example.org%2Fgadget.xml&"
+ + "fp=45508&sanitize=1&rewriteMime=text/css\" rel=\"stylesheet\"></head><body></body></html>";
+ String rewritten = rewrite(gadget, markup, set("link"), set("rel", "href"));
+ assertEquals(sanitized, rewritten);
+ }
+
+ @Test
+ public void enforceStyleLinkRewrittenNoCacheAndDebug() throws Exception {
+ String markup =
+ "<link rel=\"stylesheet\" "
+ + "href=\"http://www.test.com/dir/proxy?"
+ + "url=http%3A%2F%2Fwww.evil.com%2Fx.css&gadget=www.example.org%2Fgadget.xml&"
+ + "fp=45508rewriteMime=text/css\"/>";
+ String sanitized =
+ "<html><head><link href=\"http://www.test.com/dir/proxy?"
+ + "url=http%3A%2F%2Fwww.evil.com%2Fx.css&gadget=www.example.org%2Fgadget.xml&"
+ + "fp=45508&debug=1&nocache=1&sanitize=1&rewriteMime=text/css\" rel=\"stylesheet\"></head><body></body></html>";
+ String rewritten = rewrite(gadgetNoCacheAndDebug, markup, set("link"), set("rel", "href"));
+ assertEquals(sanitized, rewritten);
+ }
+
+ @Test
+ public void enforceNonStyleLinkStripped() throws Exception {
+ String markup =
+ "<link rel=\"script\" "
+ + "href=\"www.exmaple.org/evil.js\"/>";
+ String rewritten = rewrite(gadget, markup, set("link"), set("rel", "href", "type"));
+ assertEquals("<html><head></head><body></body></html>", rewritten);
+ }
+
+ @Test
+ public void enforceNonStyleLinkStrippedNoCacheAndDebug() throws Exception {
+ String markup =
+ "<link rel=\"script\" "
+ + "href=\"www.exmaple.org/evil.js\"/>";
+ String rewritten = rewrite(gadgetNoCacheAndDebug, markup, set("link"), set("rel", "href", "type"));
+ assertEquals("<html><head></head><body></body></html>", rewritten);
+ }
+
+ @Test
+ public void enforceCssImportLinkRewritten() throws Exception {
+ String markup =
+ "<style type=\"text/css\">@import url('www.evil.com/x.js');</style>";
+ // The caja css sanitizer does *not* remove the initial colon in urls
+ // since this does not work in IE
+ String sanitized =
+ "<html><head><style>"
+ + "@import url('http://www.test.com/dir/proxy?url=www.example.org%2F"
+ + "www.evil.com%2Fx.js&gadget=www.example.org%2Fgadget.xml&"
+ + "fp=45508&sanitize=1&rewriteMime=text%2Fcss');"
+ + "</style></head><body></body></html>";
+ String rewritten = rewrite(gadget, markup, set("style"), set());
+ assertEquals(sanitized, rewritten);
+ }
+
+ @Test
+ public void enforceCssImportLinkRewrittenNoCacheAndDebug() throws Exception {
+ String markup =
+ "<style type=\"text/css\">@import url('www.evil.com/x.js');</style>";
+ // The caja css sanitizer does *not* remove the initial colon in urls
+ // since this does not work in IE
+ String sanitized =
+ "<html><head><style>"
+ + "@import url('http://www.test.com/dir/proxy?url=www.example.org%2F"
+ + "www.evil.com%2Fx.js&gadget=www.example.org%2Fgadget.xml&"
+ + "fp=45508&debug=1&nocache=1&sanitize=1&rewriteMime=text%2Fcss');"
+ + "</style></head><body></body></html>";
+ String rewritten = rewrite(gadgetNoCacheAndDebug, markup, set("style"), set());
+ assertEquals(sanitized, rewritten);
+ }
+
+ @Test
+ public void enforceCssImportBadLinkStripped() throws Exception {
+ String markup =
+ "<style type=\"text/css\">@import url('javascript:doevil()'); A { font : bold }</style>";
+ String sanitized = "<html><head><style>A {\n"
+ + " font: bold\n"
+ + "}</style></head><body></body></html>";
+ assertEquals(sanitized, rewrite(gadget, markup, set("style"), set()));
+ }
+
+ @Test
+ public void enforceAttributeWhiteList() throws Exception {
+ String markup = "<p foo=\"bar\" bar=\"baz\">Paragraph</p>";
+ String sanitized = "<p bar=\"baz\">Paragraph</p>";
+ assertEquals(sanitized, rewrite(gadget, markup, set("p"), set("bar")));
+ }
+
+ @Test
+ public void enforceImageSrcProxied() throws Exception {
+ String markup = "<img src='http://www.evil.com/x.js'>Evil happens</img>";
+ String sanitized = "<img src=\"http://www.test.com/dir/proxy?url=http%3A%2F%2Fwww.evil.com%2Fx.js&gadget=www.example.org%2Fgadget.xml&fp=45508&sanitize=1&rewriteMime=image/*\">Evil happens";
+ assertEquals(sanitized, rewrite(gadget, markup, set("img"), set("src")));
+ }
+
+ @Test
+ public void enforceImageSrcProxiedNoCacheAndDebug() throws Exception {
+ String markup = "<img src='http://www.evil.com/x.js'>Evil happens</img>";
+ String sanitized = "<img src=\"http://www.test.com/dir/proxy?url=http%3A%2F%2Fwww.evil.com%2Fx.js&gadget=www.example.org%2Fgadget.xml&fp=45508&debug=1&nocache=1&sanitize=1&rewriteMime=image/*\">Evil happens";
+ assertEquals(sanitized, rewrite(gadgetNoCacheAndDebug, markup, set("img"), set("src")));
+ }
+
+ @Test
+ public void enforceBadImageUrlStripped() throws Exception {
+ String markup = "<img src='java\\ script:evil()'>Evil happens</img>";
+ String sanitized = "<img>Evil happens";
+ assertEquals(sanitized, rewrite(gadget, markup, set("img"), set("src")));
+ }
+
+ @Test
+ public void enforceTargetTopRestricted() throws Exception {
+ String markup = "<a href=\"http://www.example.com\" target=\"_top\">x</a>";
+ String sanitized = "<a href=\"http://www.example.com\">x</a>";
+ assertEquals(sanitized, rewrite(gadget, markup, set("a"), set("href", "target")));
+ }
+
+ @Test
+ public void enforceTargetSelfAllowed() throws Exception {
+ String markup = "<a href=\"http://www.example.com\" target=\"_self\">x</a>";
+ assertEquals(markup, rewrite(gadget, markup, set("a"), set("href", "target")));
+ }
+
+ @Test
+ public void enforceTargetBlankAllowed() throws Exception {
+ String markup = "<a href=\"http://www.example.com\" target=\"_BlAnK\">x</a>";
+ assertEquals(markup, rewrite(gadget, markup, set("a"), set("href", "target")));
+ }
+
+ @Test
+ public void sanitizationBypassAllowed() throws Exception {
+ String markup = "<p foo=\"bar\"><b>Parag</b><!--raph--></p>";
+ // Create a rewriter that would strip everything
+ GadgetRewriter rewriter = createRewriter(set(), set());
+
+ MutableContent mc = new MutableContent(parser, markup);
+ Document document = mc.getDocument();
+ // Force the content to get re-serialized
+ MutableContent.notifyEdit(document);
+ String fullMarkup = mc.getContent();
+
+ Element paragraphTag = (Element) document.getElementsByTagName("p").item(0);
+ // Mark the paragraph tag element as trusted
+ SanitizingGadgetRewriter.bypassSanitization(paragraphTag, true);
+ rewriter.rewrite(gadget, mc);
+
+ // The document should be unchanged
+ assertEquals(fullMarkup, mc.getContent());
+ }
+
+ @Test
+ public void sanitizationBypassOnlySelf() throws Exception {
+ String markup = "<p foo=\"bar\"><b>Parag</b><!--raph--></p>";
+ // Create a rewriter that would strip everything
+ GadgetRewriter rewriter = createRewriter(set(), set());
+
+ MutableContent mc = new MutableContent(parser, markup);
+ Document document = mc.getDocument();
+
+ Element paragraphTag = (Element) document.getElementsByTagName("p").item(0);
+ // Mark the paragraph tag element as trusted
+ SanitizingGadgetRewriter.bypassSanitization(paragraphTag, false);
+ rewriter.rewrite(gadget, mc);
+
+ // The document should be unchanged
+ String content = mc.getContent();
+ Matcher matcher = BODY_REGEX.matcher(content);
+ matcher.matches();
+ assertEquals("<p foo=\"bar\"></p>", matcher.group(1));
+ }
+
+ @Test
+ public void sanitizationBypassPreservedAcrossClone() throws Exception {
+ String markup = "<p foo=\"bar\"><b>Parag</b><!--raph--></p>";
+ // Create a rewriter that would strip everything
+ GadgetRewriter rewriter = createRewriter(set(), set());
+
+ MutableContent mc = new MutableContent(parser, markup);
+ Document document = mc.getDocument();
+
+ Element paragraphTag = (Element) document.getElementsByTagName("p").item(0);
+ // Mark the paragraph tag element as trusted
+ SanitizingGadgetRewriter.bypassSanitization(paragraphTag, false);
+
+ // Now, clone the paragraph tag and replace the paragraph tag
+ Element cloned = (Element) paragraphTag.cloneNode(true);
+ paragraphTag.getParentNode().replaceChild(cloned, paragraphTag);
+
+ rewriter.rewrite(gadget, mc);
+
+ // The document should be unchanged
+ String content = mc.getContent();
+ Matcher matcher = BODY_REGEX.matcher(content);
+ matcher.matches();
+ assertEquals("<p foo=\"bar\"></p>", matcher.group(1));
+ }
+
+ @Test
+ public void restrictHrefAndSrcAttributes() throws Exception {
+ String markup =
+ "<element " +
+ "href=\"http://example.org/valid-href\" " +
+ "src=\"http://example.org/valid-src\"/> " +
+ "<element " +
+ "href=\"https://example.org/valid-href\" " +
+ "src=\"https://example.org/valid-src\"/> " +
+ "<element " +
+ "href=\"http-evil://example.org/valid-href\" " +
+ "src=\"http-evil://example.org/valid-src\"/> " +
+ "<element " +
+ "href=\"javascript:evil()\" " +
+ "src=\"javascript:evil()\" /> " +
+ "<element " +
+ "href=\"//example.org/valid-href\" " +
+ "src=\"//example.org/valid-src\"/>";
+
+ // TODO: This test is only valid when using a parser that converts empty tags to
+ // balanced tags. The default (Neko) parser does this, with special case logic for handling
+ // empty tags like br or link.
+ String sanitized =
+ "<element " +
+ "href=\"http://example.org/valid-href\" " +
+ "src=\"http://example.org/valid-src\"></element> " +
+ "<element " +
+ "href=\"https://example.org/valid-href\" " +
+ "src=\"https://example.org/valid-src\"></element> " +
+ "<element></element> " +
+ "<element></element> " +
+ "<element " +
+ "href=\"//example.org/valid-href\" " +
+ "src=\"//example.org/valid-src\"></element>";
+
+ assertEquals(sanitized, rewrite(gadget, markup, set("element"), set("href", "src")));
+ }
+
+ @Test
+ public void allCommentsStripped() throws Exception {
+ String markup = "<b>Hello, world</b><!--<b>evil</b>-->";
+ assertEquals("<b>Hello, world</b>", rewrite(gadget, markup, set("b"), set()));
+ }
+
+ @Test
+ public void doesNothingWhenNotSanitized() throws Exception {
+ String markup = "<script src=\"http://evil.org/evil\"></script> <b>hello</b>";
+ Gadget gadget = new Gadget().setContext(unsanitaryGadgetContext);
+ gadget.setSpec(new GadgetSpec(Uri.parse("www.example.org/gadget.xml"),
+ "<Module><ModulePrefs title=''/><Content type='html'/></Module>"));
+ gadget.setCurrentView(gadget.getSpec().getViews().values().iterator().next());
+ assertEquals(markup, rewrite(gadget, markup, set("b"), set()));
+ }
+
+ @Test
+ public void forceSanitizeUnsanitaryGadget() throws Exception {
+ String markup =
+ "<p><style type=\"text/css\">A { font : bold; behavior : bad }</style>text <b>bold text</b></p>" +
+ "<b>Bold text</b><i>Italic text<b>Bold text</b></i>";
+
+ String sanitized = "<html><head></head><body><p><style>A {\n font: bold\n}</style>text " +
+ "<b>bold text</b></p><b>Bold text</b></body></html>";
+
+ Gadget gadget = new Gadget().setContext(sanitaryGadgetContext);
+ gadget.setSpec(new GadgetSpec(Uri.parse("www.example.org/gadget.xml"),
+ "<Module><ModulePrefs title=''/><Content type='html'/></Module>"));
+ gadget.setCurrentView(gadget.getSpec().getViews().values().iterator().next());
+ assertEquals(sanitized, rewrite(gadget, markup, set("p", "b", "style"), set()));
+ }
+}
Added: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/SanitizingRequestRewriterTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/SanitizingRequestRewriterTest.java?rev=920421&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/SanitizingRequestRewriterTest.java (added)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/old/SanitizingRequestRewriterTest.java Mon Mar 8 17:54:32 2010
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.render.old;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
+import org.apache.shindig.gadgets.parse.caja.CajaCssParser;
+import org.apache.shindig.gadgets.parse.caja.CajaCssSanitizer;
+import org.apache.shindig.gadgets.render.old.DefaultSanitizingProxyingLinkRewriterFactory;
+import org.apache.shindig.gadgets.render.old.SanitizingRequestRewriter;
+import org.apache.shindig.gadgets.rewrite.BaseRewriterTestCase;
+import org.apache.shindig.gadgets.rewrite.ContentRewriterFeature;
+import org.apache.shindig.gadgets.rewrite.MutableContent;
+import org.apache.shindig.gadgets.rewrite.RequestRewriter;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class SanitizingRequestRewriterTest extends BaseRewriterTestCase {
+ private static final Uri CONTENT_URI = Uri.parse("www.example.org/content");
+
+ private String rewrite(HttpRequest request, HttpResponse response) throws Exception {
+ request.setSanitizationRequested(true);
+ RequestRewriter rewriter = createRewriter(Collections.<String>emptySet(),
+ Collections.<String>emptySet());
+
+ MutableContent mc = new MutableContent(parser, response);
+ if (!rewriter.rewrite(request, response, mc)) {
+ return null;
+ }
+ return mc.getContent();
+ }
+
+ private RequestRewriter createRewriter(Set<String> tags, Set<String> attributes) {
+ ContentRewriterFeature.Factory rewriterFeatureFactory =
+ new ContentRewriterFeature.Factory(null,
+ new ContentRewriterFeature.DefaultConfig(".*", "", "HTTP", "embed,img,script,link,style", "false", "false"));
+ return new SanitizingRequestRewriter(rewriterFeatureFactory,
+ new CajaCssSanitizer(new CajaCssParser()), new DefaultSanitizingProxyingLinkRewriterFactory(rewriterUris));
+ }
+
+ @Test
+ public void enforceInvalidProxedCssRejected() throws Exception {
+ HttpRequest req = new HttpRequest(CONTENT_URI);
+ req.setRewriteMimeType("text/css");
+ HttpResponse response = new HttpResponseBuilder().setResponseString("doEvil()").create();
+ String sanitized = "";
+ assertEquals(sanitized, rewrite(req, response));
+ }
+
+ @Test
+ public void enforceValidProxedCssAccepted() throws Exception {
+ HttpRequest req = new HttpRequest(CONTENT_URI);
+ req.setRewriteMimeType("text/css");
+ HttpResponse response = new HttpResponseBuilder().setResponseString(
+ "@import url('http://www.evil.com/more.css'); A { font : BOLD }").create();
+ // The caja css sanitizer does *not* remove the initial colon in urls
+ // since this does not work in IE
+ String sanitized =
+ "@import url('http://www.test.com/dir/proxy?"
+ + "url=http%3A%2F%2Fwww.evil.com%2Fmore.css"
+ + "&fp=45508&sanitize=1&rewriteMime=text%2Fcss');\n"
+ + "A {\n"
+ + " font: BOLD\n"
+ + '}';
+ String rewritten = rewrite(req, response);
+ assertEquals(sanitized, rewritten);
+ }
+
+ @Test
+ public void enforceValidProxedCssAcceptedNoCache() throws Exception {
+ HttpRequest req = new HttpRequest(CONTENT_URI);
+ req.setRewriteMimeType("text/css");
+ req.setIgnoreCache(true);
+ HttpResponse response = new HttpResponseBuilder().setResponseString(
+ "@import url('http://www.evil.com/more.css'); A { font : BOLD }").create();
+ // The caja css sanitizer does *not* remove the initial colon in urls
+ // since this does not work in IE
+ String sanitized =
+ "@import url('http://www.test.com/dir/proxy?"
+ + "url=http%3A%2F%2Fwww.evil.com%2Fmore.css"
+ + "&fp=45508&nocache=1&sanitize=1&rewriteMime=text%2Fcss');\n"
+ + "A {\n"
+ + " font: BOLD\n"
+ + '}';
+ String rewritten = rewrite(req, response);
+ assertEquals(sanitized, rewritten);
+ }
+
+ @Test
+ public void enforceInvalidProxedImageRejected() throws Exception {
+ HttpRequest req = new HttpRequest(CONTENT_URI);
+ req.setRewriteMimeType("image/*");
+ HttpResponse response = new HttpResponseBuilder().setResponse("NOTIMAGE".getBytes()).create();
+ String sanitized = "";
+ assertEquals(sanitized, rewrite(req, response));
+ }
+
+ @Test
+ public void validProxiedImageAccepted() throws Exception {
+ HttpRequest req = new HttpRequest(CONTENT_URI);
+ req.setRewriteMimeType("image/*");
+ HttpResponse response = new HttpResponseBuilder().setResponse(
+ IOUtils.toByteArray(getClass().getClassLoader().getResourceAsStream(
+ "org/apache/shindig/gadgets/rewrite/image/inefficient.png"))).create();
+ assertNull(rewrite(req, response));
+ }
+
+ @Test
+ public void enforceUnknownMimeTypeRejected() throws Exception {
+ HttpRequest req = new HttpRequest(CONTENT_URI);
+ req.setRewriteMimeType("text/foo");
+ HttpResponse response = new HttpResponseBuilder().setResponseString("doEvil()").create();
+ String sanitized = "";
+ assertEquals(sanitized, rewrite(req, response));
+ }
+
+ @Test
+ public void enforceMissingMimeTypeRejected() throws Exception {
+ HttpRequest req = new HttpRequest(CONTENT_URI);
+ // A request without a mime type, but requesting sanitization, should be rejected
+ req.setRewriteMimeType(null);
+ HttpResponse response = new HttpResponseBuilder().setResponseString("doEvil()").create();
+ String sanitized = "";
+ assertEquals(sanitized, rewrite(req, response));
+ }
+}
Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessorTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessorTest.java?rev=920421&r1=920420&r2=920421&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessorTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessorTest.java Mon Mar 8 17:54:32 2010
@@ -27,7 +27,7 @@
import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
import org.apache.shindig.gadgets.parse.ParseModule;
import org.apache.shindig.gadgets.parse.nekohtml.NekoSimplifiedHtmlParser;
-import org.apache.shindig.gadgets.render.SanitizingGadgetRewriter;
+import org.apache.shindig.gadgets.render.old.SanitizingGadgetRewriter;
import org.apache.shindig.gadgets.templates.tags.AbstractTagHandler;
import org.apache.shindig.gadgets.templates.tags.DefaultTagRegistry;
import org.apache.shindig.gadgets.templates.tags.TagHandler;