You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2014/11/19 10:01:55 UTC

svn commit: r1640505 - in /sling/trunk/contrib: scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/ xss/src/main/java/org/apache/sling/xss/ xss/src/main/java/org/apache/sling/xss/impl/ xss/src/test/java/org/apach...

Author: fmeschbe
Date: Wed Nov 19 09:01:54 2014
New Revision: 1640505

URL: http://svn.apache.org/r1640505
Log:
SLING-4176 - Added validation/filtering for StyleToken context

* Adding a new method for checking CSS tokens to the XSSAPI
* Incl. implementation and unit tests

This closes #38

(Applying patch by Vlad Bailescue; Thanks alot)

Modified:
    sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/XSSRuntimeExtension.java
    sling/trunk/contrib/xss/src/main/java/org/apache/sling/xss/XSSAPI.java
    sling/trunk/contrib/xss/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java
    sling/trunk/contrib/xss/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java

Modified: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/XSSRuntimeExtension.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/XSSRuntimeExtension.java?rev=1640505&r1=1640504&r2=1640505&view=diff
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/XSSRuntimeExtension.java (original)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/XSSRuntimeExtension.java Wed Nov 19 09:01:54 2014
@@ -103,35 +103,29 @@ public class XSSRuntimeExtension extends
             }
 
             private String applyXSSFilter(String text, MarkupContext xssContext) {
-                if (xssContext.equals(MarkupContext.ATTRIBUTE)) {
-                    return xssapi.encodeForHTMLAttr(text);
-                }
-                if (xssContext.equals(MarkupContext.COMMENT) ||
-                        xssContext.equals(MarkupContext.TEXT)) {
-                    return xssapi.encodeForHTML(text);
-                }
-                if (xssContext.equals(MarkupContext.ATTRIBUTE_NAME)) {
-                    return escapeAttributeName(text);
-                }
-                if (xssContext.equals(MarkupContext.NUMBER)) {
-                    return xssapi.getValidLong(text, 0).toString();
-                }
-                if (xssContext.equals(MarkupContext.URI)) {
-                    return xssapi.getValidHref(text);
-                }
-                if (xssContext.equals(MarkupContext.SCRIPT_TOKEN)
-                        || xssContext.equals(MarkupContext.SCRIPT_COMMENT)) {
-                    return xssapi.getValidJSToken(text, "");
-                }
-                if (xssContext.equals(MarkupContext.SCRIPT_STRING)
-                        || xssContext.equals(MarkupContext.STYLE_STRING)) {
-                    return xssapi.encodeForJSString(text);
-                }
-                if (xssContext.equals(MarkupContext.ELEMENT_NAME)) {
-                    return escapeElementName(text);
-                }
-                if (xssContext.equals(MarkupContext.HTML)) {
-                    return xssapi.filterHTML(text);
+                switch (xssContext) {
+                    case ATTRIBUTE:
+                        return xssapi.encodeForHTMLAttr(text);
+                    case COMMENT:
+                    case TEXT:
+                        return xssapi.encodeForHTML(text);
+                    case ATTRIBUTE_NAME:
+                        return escapeAttributeName(text);
+                    case NUMBER:
+                        return xssapi.getValidLong(text, 0).toString();
+                    case URI:
+                        return xssapi.getValidHref(text);
+                    case SCRIPT_TOKEN:
+                    case SCRIPT_COMMENT:
+                        return xssapi.getValidJSToken(text, "");
+                    case STYLE_TOKEN:
+                        return xssapi.getValidStyleToken(text, "");
+                    case SCRIPT_STRING:
+                        return xssapi.encodeForJSString(text);
+                    case ELEMENT_NAME:
+                        return escapeElementName(text);
+                    case HTML:
+                        return xssapi.filterHTML(text);
                 }
                 return text; //todo: apply the rest of XSS filters
             }

Modified: sling/trunk/contrib/xss/src/main/java/org/apache/sling/xss/XSSAPI.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/xss/src/main/java/org/apache/sling/xss/XSSAPI.java?rev=1640505&r1=1640504&r2=1640505&view=diff
==============================================================================
--- sling/trunk/contrib/xss/src/main/java/org/apache/sling/xss/XSSAPI.java (original)
+++ sling/trunk/contrib/xss/src/main/java/org/apache/sling/xss/XSSAPI.java Wed Nov 19 09:01:54 2014
@@ -86,6 +86,16 @@ public interface XSSAPI {
     public String getValidJSToken(String token, String defaultValue);
 
     /**
+     * Validate a style/CSS token. Valid CSS tokens are specified at http://www.w3.org/TR/css3-syntax/
+     *
+     * @param token        the source token
+     * @param defaultValue a default value to use if the source doesn't meet validity constraints.
+     *
+     * @return a string containing sanitized style token
+     */
+    public String getValidStyleToken(String token, String defaultValue);
+
+    /**
      * Validate a CSS color value. Color values as specified at http://www.w3.org/TR/css3-color/#colorunits
      * are safe and definitively allowed. Vulnerable constructs will be disallowed. Currently known
      * vulnerable constructs include url(...), expression(...), and anything with a semicolon.

Modified: sling/trunk/contrib/xss/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/xss/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java?rev=1640505&r1=1640504&r2=1640505&view=diff
==============================================================================
--- sling/trunk/contrib/xss/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java (original)
+++ sling/trunk/contrib/xss/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java Wed Nov 19 09:01:54 2014
@@ -190,6 +190,46 @@ public class XSSAPIImpl implements XSSAP
         }
     }
 
+    private static final String NON_ASCII = "\\x00\\x08\\x0B\\x0C\\x0E-\\x1F";
+    /** http://www.w3.org/TR/css-syntax-3/#number-token-diagram */
+    private static final String NUMBER = "[+-]?[\\d]*[\\.]?[\\d]*(?:[e][+-]?\\d+)?";
+    /** http://www.w3.org/TR/css-syntax-3/#hex-digit-diagram */
+    private static final String HEX_DIGITS = "#[0-9a-f]*";
+    /** http://www.w3.org/TR/css-syntax-3/#ident-token-diagram */
+    private static final String IDENTIFIER = "-?[a-z_" + NON_ASCII + "][\\w_\\-" + NON_ASCII + "]*";
+    /** http://www.w3.org/TR/css-syntax-3/#string-token-diagram */
+    private static final String STRING = "\"(?:[^\"^\\\\^\\n]|(?:\\\\\"))*\"|'(?:[^'^\\\\^\\n]|(?:\\\\'))*'";
+    /** http://www.w3.org/TR/css-syntax-3/#dimension-token-diagram */
+    private static final String DIMENSION = NUMBER + IDENTIFIER;
+    /** http://www.w3.org/TR/css-syntax-3/#percentage-token-diagram */
+    private static final String PERCENT = NUMBER + "%";
+    /** http://www.w3.org/TR/css-syntax-3/#function-token-diagram */
+    private static final String FUNCTION = IDENTIFIER + "\\((?:(?:" + NUMBER + ")|(?:" + IDENTIFIER + ")|(?:[\\s]*)|(?:,))*\\)";
+    /** http://www.w3.org/TR/css-syntax-3/#url-unquoted-diagram */
+    private static final String URL_UNQUOTED = "[^\"^'^\\(^\\)^[" + NON_ASCII + "]]*";
+    /** http://www.w3.org/TR/css-syntax-3/#url-token-diagram */
+    private static final String URL = "url\\((?:(?:" + URL_UNQUOTED + ")|(?:" + STRING + "))\\)";
+    /** composite regular expression for style token validation */
+    private static final String CSS_TOKEN = "(?i)" // case insensitive
+            + "(?:" + NUMBER + ")"
+            + "|(?:" + DIMENSION + ")"
+            + "|(?:" + PERCENT + ")"
+            + "|(?:" + HEX_DIGITS + ")"
+            + "|(?:" + IDENTIFIER + ")"
+            + "|(?:" + STRING + ")"
+            + "|(?:" + FUNCTION + ")"
+            + "|(?:" + URL + ")";
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getValidStyleToken(String, String)
+     */
+    public String getValidStyleToken(String token, String defaultValue) {
+        if (token.matches(CSS_TOKEN)) {
+            return token;
+        }
+        return defaultValue;
+    }
+
     /**
      * @see org.apache.sling.xss.XSSAPI#getValidCSSColor(String, String)
      */

Modified: sling/trunk/contrib/xss/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/xss/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java?rev=1640505&r1=1640504&r2=1640505&view=diff
==============================================================================
--- sling/trunk/contrib/xss/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java (original)
+++ sling/trunk/contrib/xss/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java Wed Nov 19 09:01:54 2014
@@ -39,6 +39,8 @@ import org.owasp.validator.html.Policy;
 
 public class XSSAPIImplTest {
 
+    public static final String RUBBISH = "rubbish";
+
     private XSSAPI xssAPI;
 
     @Before
@@ -310,25 +312,25 @@ public class XSSAPIImplTest {
                 {"simple", "simple"},
                 {"clickstreamcloud.thingy", "clickstreamcloud.thingy"},
 
-                {"break out", "rubbish"},
-                {"break,out", "rubbish"},
+                {"break out", RUBBISH},
+                {"break,out", RUBBISH},
 
                 {"\"literal string\"", "\"literal string\""},
                 {"'literal string'", "'literal string'"},
-                {"\"bad literal'", "rubbish"},
+                {"\"bad literal'", RUBBISH},
                 {"'literal'); junk'", "'literal\\x27); junk'"},
 
                 {"1200", "1200"},
                 {"3.14", "3.14"},
-                {"1,200", "rubbish"},
-                {"1200 + 1", "rubbish"}
+                {"1,200", RUBBISH},
+                {"1200 + 1", RUBBISH}
         };
 
         for (String[] aTestData : testData) {
             String source = aTestData[0];
             String expected = aTestData[1];
 
-            String result = xssAPI.getValidJSToken(source, "rubbish");
+            String result = xssAPI.getValidJSToken(source, RUBBISH);
             if (!result.equals(expected)) {
                 fail("Validating Javascript token '" + source + "', expecting '" + expected + "', but got '" + result + "'");
             }
@@ -336,6 +338,72 @@ public class XSSAPIImplTest {
     }
 
     @Test
+    public void TestGetValidStyleToken() {
+        String[][] testData = {
+                // Source                           Expected result
+
+                // CSS close
+                {"}"                                , RUBBISH},
+
+                // line break
+                {"br\neak"                          , RUBBISH},
+
+                // no javascript:
+                {"javascript:alert(1)"              , RUBBISH},
+                {"url(javascript:alert(1))"         , RUBBISH},
+
+                // no expression
+                {"expression(alert(1))"             , RUBBISH},
+                {"expression  (alert(1))"           , RUBBISH},
+                {"expression(this.location='a.co')" , RUBBISH},
+
+                // html tags
+                {"</style><script>alert(1)</script>", RUBBISH},
+
+                // usual CSS stuff
+                {"background-color"                 , "background-color"},
+                {"-moz-box-sizing"                  , "-moz-box-sizing"},
+                {".42%"                             , ".42%"},
+                {"#fff"                             , "#fff"},
+
+                // valid strings
+                {"'literal string'"                 , "'literal string'"},
+                {"\"literal string\""               , "\"literal string\""},
+                {"'it\\'s here'"                    , "'it\\'s here'"},
+                {"\"it\\\"s here\""                 , "\"it\\\"s here\""},
+
+                // invalid strings
+                {"\"bad string"                     , RUBBISH},
+                {"'it's here'"                      , RUBBISH},
+                {"\"it\"s here\""                   , RUBBISH},
+
+                // valid parenthesis
+                {"rgb(255, 255, 255)"               , "rgb(255, 255, 255)"},
+
+                // invalid parenthesis
+                {"rgb(255, 255, 255"               , RUBBISH},
+                {"255, 255, 255)"                  , RUBBISH},
+
+                // valid tokens
+                {"url(http://example.com/test.png)", "url(http://example.com/test.png)"},
+                {"url('image/test.png')"           , "url('image/test.png')"},
+
+                // invalid tokens
+                {"color: red"                      , RUBBISH}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            String result = xssAPI.getValidStyleToken(source, RUBBISH);
+            if (!result.equals(expected)) {
+                fail("Validating style token '" + source + "', expecting '" + expected + "', but got '" + result + "'");
+            }
+        }
+    }
+
+    @Test
     public void TestGetValidCSSColor() {
         String[][] testData = {
                 //      Source                          Expected Result
@@ -352,16 +420,16 @@ public class XSSAPIImplTest {
                 {"transparent", "transparent"},
 
                 {"\f\r\n\t MenuText\f\r\n\t ", "MenuText"},
-                {"expression(99,99,99)", "rubbish"},
-                {"blue;", "rubbish"},
-                {"url(99,99,99)", "rubbish"}
+                {"expression(99,99,99)", RUBBISH},
+                {"blue;", RUBBISH},
+                {"url(99,99,99)", RUBBISH}
         };
 
         for (String[] aTestData : testData) {
             String source = aTestData[0];
             String expected = aTestData[1];
 
-            String result = xssAPI.getValidCSSColor(source, "rubbish");
+            String result = xssAPI.getValidCSSColor(source, RUBBISH);
             if (!result.equals(expected)) {
                 fail("Validating CSS Color '" + source + "', expecting '" + expected + "', but got '" + result + "'");
             }