Repository: shapesecurity/salvation
Pull request #88 (2015-10-18 00:49:04)

fixes GH-87 : Add query API for child-src and frame-src
Although I think spec should be be clear on what to do if both child-src and frame-src exist. FF doesn't implement child-src yet, so technically it is not CSP 2.0 compliant, as caniuse says.


Patch:
diff --git a/src/main/java/com/shapesecurity/salvation/data/Policy.java b/src/main/java/com/shapesecurity/salvation/data/Policy.java
index c4dc44e..4dc42a7 100644
--- a/src/main/java/com/shapesecurity/salvation/data/Policy.java
+++ b/src/main/java/com/shapesecurity/salvation/data/Policy.java
@@ -510,4 +510,20 @@ public class Policy implements Show {
     public boolean allowsStyleWithNonce(@Nonnull Base64Value nonce) {
         return this.allowsStyleWithNonce(nonce.value);
     }
+
+    public boolean allowsChildFromSource(@Nonnull URI source) {
+        ChildSrcDirective childSrcDirective = this.getDirectiveByType(ChildSrcDirective.class);
+        if (childSrcDirective == null) {
+            return this.defaultsAllowSource(source);
+        }
+        return childSrcDirective.matchesSource(this.origin, source);
+    }
+
+    public boolean allowsFrameFromSource(@Nonnull URI source) {
+        FrameSrcDirective frameSrcDirective = this.getDirectiveByType(FrameSrcDirective.class);
+        if (frameSrcDirective == null) {
+            return this.allowsChildFromSource(source);
+        }
+        return frameSrcDirective.matchesSource(this.origin, source);
+    }
 }


Test patch:
diff --git a/src/test/java/com/shapesecurity/salvation/ParserTest.java b/src/test/java/com/shapesecurity/salvation/ParserTest.java
index bb2e6b0..132d1c6 100644
--- a/src/test/java/com/shapesecurity/salvation/ParserTest.java
+++ b/src/test/java/com/shapesecurity/salvation/ParserTest.java
@@ -149,7 +149,6 @@ public class ParserTest extends CSPTest {
 
         assertEquals("optimisation", "", parseAndShow("script-src example.com *"));
         assertEquals("optimisation", "", parseAndShow("script-src 'self' *"));
-        assertEquals("optimisation", "script-src 'unsafe-inline'; style-src 'unsafe-inline'", parseAndShow("script-src 'unsafe-inline'; style-src 'unsafe-inline';"));
 
 
     }
diff --git a/src/test/java/com/shapesecurity/salvation/PolicyQueryingTest.java b/src/test/java/com/shapesecurity/salvation/PolicyQueryingTest.java
index 2d3a707..06b3241 100644
--- a/src/test/java/com/shapesecurity/salvation/PolicyQueryingTest.java
+++ b/src/test/java/com/shapesecurity/salvation/PolicyQueryingTest.java
@@ -69,11 +69,32 @@ public class PolicyQueryingTest extends CSPTest {
         assertFalse("resource is not allowed", p.allowsImgFromSource(URI.parse("https://abc.am")));
         assertFalse("resource is not allowed", p.allowsStyleFromSource(URI.parse("ftp://www.abc.am:555")));
         assertFalse("resource is not allowed", p.allowsScriptFromSource(URI.parse("https://www.def.am:555")));
+        assertFalse("resource is not allowed", p.allowsFrameFromSource(URI.parse("https://www.def.am:555")));
+        assertFalse("resource is not allowed", p.allowsChildFromSource(
+            URI.parse("https://www.def.am:555")));
+
 
         p = Parser.parse("default-src *:*", "http://abc.com");
         assertTrue("resource is allowed", p.allowsImgFromSource(URI.parse("http://abc.am")));
         assertFalse("resource is not allowed", p.allowsScriptFromSource(URI.parse("https://www.def.am:555")));
         assertFalse("resource is not allowed", p.allowsStyleFromSource(URI.parse("ftp://www.abc.am:555")));
+
+        p = Parser.parse("default-src 'none'; frame-src http:;", URI.parse("https://abc.com"));
+        assertFalse("resource is not allowed", p.allowsFrameFromSource(URI.parse("https://www.def.am:555")));
+        assertTrue("resource is allowed", p.allowsFrameFromSource(URI.parse("http://www.def.am:555")));
+        assertFalse("resource is not allowed", p.allowsChildFromSource(URI.parse("http://www.def.am:555")));
+
+        p = Parser.parse("child-src http:;", URI.parse("https://abc.com"));
+        assertFalse("resource is not allowed", p.allowsFrameFromSource(URI.parse("https://www.def.am:555")));
+        assertTrue("resource is allowed", p.allowsFrameFromSource(URI.parse("http://www.def.am:555")));
+        assertFalse("resource is not allowed", p.allowsChildFromSource(URI.parse("https://www.def.am:555")));
+        assertTrue("resource is allowed", p.allowsChildFromSource(URI.parse("http://www.def.am:555")));
+
+        p = Parser.parse("frame-src https:; child-src http:;", URI.parse("https://abc.com"));
+        assertTrue("resource is allowed", p.allowsFrameFromSource(URI.parse("https://www.def.am:555")));
+        assertFalse("resource is not allowed", p.allowsFrameFromSource(URI.parse("http://www.def.am:555")));
+        assertFalse("resource is not allowed", p.allowsChildFromSource(URI.parse("https://www.def.am:555")));
+        assertTrue("resource is allowed", p.allowsChildFromSource(URI.parse("http://www.def.am:555")));
     }
 
     @Test
@@ -110,11 +131,15 @@ public class PolicyQueryingTest extends CSPTest {
         Policy p;
 
         assertTrue("plugin is allowed", parse("plugin-types a/b c/d").allowsPlugin(new MediaType("a", "b")));
-        assertTrue("plugin is allowed", parse("plugin-types a/b c/d").allowsPlugin(new MediaType("a", "b")));
-        assertFalse("plugin is not allowed", parse("default-src 'none'").allowsPlugin(new MediaType("z", "b")));
+        assertTrue("plugin is allowed", parse("plugin-types a/b c/d").allowsPlugin(
+            new MediaType("a", "b")));
+        assertFalse("plugin is not allowed", parse("default-src 'none'").allowsPlugin(
+            new MediaType("z", "b")));
         assertFalse("plugin is not allowed", parse("plugin-types a/b c/d").allowsPlugin(new MediaType("z", "b")));
-        assertFalse("plugin is not allowed", parse("plugin-types a/b c/d").allowsPlugin(new MediaType("a", "d")));
-        assertFalse("plugin is not allowed", parse("plugin-types a/b c/d").allowsPlugin(new MediaType("", "b")));
+        assertFalse("plugin is not allowed", parse("plugin-types a/b c/d").allowsPlugin(
+            new MediaType("a", "d")));
+        assertFalse("plugin is not allowed", parse("plugin-types a/b c/d").allowsPlugin(
+            new MediaType("", "b")));
     }
 
     @Test
@@ -123,21 +148,25 @@ public class PolicyQueryingTest extends CSPTest {
 
         p = parse("script-src 'sha512-vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg=='");
         assertTrue("script hash is allowed",
-            p.allowsScriptWithHash(HashSource.HashAlgorithm.SHA512, new Base64Value("vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==")));
+            p.allowsScriptWithHash(HashSource.HashAlgorithm.SHA512, new Base64Value(
+                "vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==")));
         assertFalse("script hash is not allowed",
             p.allowsScriptWithHash(HashSource.HashAlgorithm.SHA512, new Base64Value("cGl6ZGE=")));
 
         p = parse("style-src 'sha512-vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg=='");
         assertTrue("style hash is allowed",
-            p.allowsStyleWithHash(HashSource.HashAlgorithm.SHA512, new Base64Value("vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==")));
+            p.allowsStyleWithHash(HashSource.HashAlgorithm.SHA512, new Base64Value(
+                "vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==")));
         assertFalse("style hash is not allowed",
             p.allowsStyleWithHash(HashSource.HashAlgorithm.SHA512, new Base64Value("cGl6ZGE=")));
 
         p = Parser.parse("default-src 'none'", "https://abc.com");
         assertFalse("script hash is not allowed",
-            p.allowsScriptWithHash(HashSource.HashAlgorithm.SHA512, new Base64Value("vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==")));
+            p.allowsScriptWithHash(HashSource.HashAlgorithm.SHA512, new Base64Value(
+                "vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==")));
         assertFalse("style hash is not allowed",
-            p.allowsStyleWithHash(HashSource.HashAlgorithm.SHA512, new Base64Value("vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==")));
+            p.allowsStyleWithHash(HashSource.HashAlgorithm.SHA512, new Base64Value(
+                "vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==")));
     }
 
     @Test
@@ -150,11 +179,14 @@ public class PolicyQueryingTest extends CSPTest {
 
         p = parse("style-src 'nonce-0gQAAA=='");
         assertTrue("style nonce is allowed", p.allowsStyleWithNonce(new Base64Value("0gQAAA==")));
-        assertFalse("style nonce is not allowed", p.allowsStyleWithNonce(new Base64Value("cGl6ZGE=")));
+        assertFalse("style nonce is not allowed", p.allowsStyleWithNonce(
+            new Base64Value("cGl6ZGE=")));
 
         p = Parser.parse("default-src 'none'", "https://abc.com");
-        assertFalse("script nonce is not allowed", p.allowsScriptWithNonce(new Base64Value("0gQAAA==")));
-        assertFalse("style nonce is not allowed", p.allowsStyleWithNonce(new Base64Value("0gQAAA==")));
+        assertFalse("script nonce is not allowed", p.allowsScriptWithNonce(
+            new Base64Value("0gQAAA==")));
+        assertFalse("style nonce is not allowed", p.allowsStyleWithNonce(
+            new Base64Value("0gQAAA==")));
     }
 
     @Test