Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes #91: allow secure variant of scheme when only insecure is given #160

Merged
merged 2 commits into from Dec 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/main/java/com/shapesecurity/salvation/data/Policy.java
Expand Up @@ -364,10 +364,6 @@ private boolean defaultsAllowNonce(@Nonnull String nonce) {
return defaultSrcDirective.matchesNonce(nonce);
}

private boolean defaultsAllowNonce(@Nonnull Base64Value nonce) {
return this.defaultsAllowNonce(nonce.value);
}

private boolean defaultsAllowSource(@Nonnull URI source) {
DefaultSrcDirective defaultSrcDirective = this.getDirectiveByType(DefaultSrcDirective.class);
if (defaultSrcDirective == null) {
Expand Down
Expand Up @@ -60,4 +60,41 @@ public static int defaultPortForProtocol(@Nonnull String scheme) {
this.host +
(isDefaultPort ? "" : ":" + this.port);
}

public static boolean isSchemeNetworkScheme(String scheme) {
return scheme != null && (scheme.equalsIgnoreCase("ftp") || scheme.equalsIgnoreCase("http") ||
scheme.equalsIgnoreCase("https") || scheme.equalsIgnoreCase("ws") ||
scheme.equalsIgnoreCase("wss"));
}

public static boolean isSchemeSecureScheme(String scheme) {
return scheme != null && (scheme.equalsIgnoreCase("https") || scheme.equalsIgnoreCase("wss"));
}

public boolean isNetworkScheme() {
return isSchemeNetworkScheme(this.scheme);
}

public boolean isSecureScheme() {
return isSchemeSecureScheme(this.scheme);
}

public static boolean matchesSecureScheme(@Nonnull String expressionScheme, @Nonnull String resourceScheme) {
expressionScheme = expressionScheme.toLowerCase();
resourceScheme = resourceScheme.toLowerCase();

if (expressionScheme.equals(resourceScheme))
return true;

if(expressionScheme.equals("http") && resourceScheme.equals("https"))
return true;

if(expressionScheme.equals("ws") && (resourceScheme.equals("wss") || resourceScheme.equals("http") || resourceScheme.equals("https")))
return true;

if(expressionScheme.equals("wss") && resourceScheme.equals("https"))
return true;

return false;
}
}
6 changes: 0 additions & 6 deletions src/main/java/com/shapesecurity/salvation/data/URI.java
Expand Up @@ -70,12 +70,6 @@ public URI(@Nonnull SchemeHostPortTriple origin) {
return h;
}

public boolean isNetworkScheme() {
return this.scheme != null && (this.scheme.equalsIgnoreCase("ftp") || this.scheme.equalsIgnoreCase("http") ||
this.scheme.equalsIgnoreCase("https") || this.scheme.equalsIgnoreCase("ws") ||
this.scheme.equalsIgnoreCase("wss"));
}

@Nonnull @Override public String show() {
return super.show() + this.path;
}
Expand Down
Expand Up @@ -72,11 +72,9 @@ public boolean isWildcard() {
}
boolean schemeMatches;
if (this.scheme == null) {
schemeMatches = resource.scheme.equalsIgnoreCase("http") ?
shpOrigin.scheme.equalsIgnoreCase("http") || shpOrigin.scheme.equalsIgnoreCase("https") :
resource.scheme.equalsIgnoreCase(shpOrigin.scheme);
schemeMatches = SchemeHostPortTriple.matchesSecureScheme(shpOrigin.scheme, resource.scheme);
} else {
schemeMatches = this.scheme.equalsIgnoreCase(resource.scheme);
schemeMatches = SchemeHostPortTriple.matchesSecureScheme(this.scheme, resource.scheme);
}
boolean hostMatches = this.host.equals("*") || (this.host.startsWith("*.") ?
resource.host.toLowerCase().endsWith(this.host.substring(1).toLowerCase()) :
Expand All @@ -85,7 +83,8 @@ public boolean isWildcard() {
|| SchemeHostPortTriple.defaultPortForProtocol(resource.scheme) == resource.port;
boolean thisUsesDefaultPort = this.scheme != null && (this.port == Constants.EMPTY_PORT
|| SchemeHostPortTriple.defaultPortForProtocol(this.scheme) == this.port);
boolean portMatches = this.port == Constants.WILDCARD_PORT || (this.port == Constants.EMPTY_PORT ?
boolean portMatches = this.port == Constants.WILDCARD_PORT || (thisUsesDefaultPort && uriUsesDefaultPort) ||
(this.port == Constants.EMPTY_PORT ?
uriUsesDefaultPort :
(resource.port == Constants.EMPTY_PORT ? thisUsesDefaultPort : this.port == resource.port));
boolean pathMatches = this.path == null || (this.path.endsWith("/") ?
Expand Down
Expand Up @@ -18,8 +18,7 @@ public SchemeSource(@Nonnull String value) {
}

@Override public boolean matchesSource(@Nonnull Origin origin, @Nonnull URI resource) {
SchemeHostPortTriple shpOrigin = (SchemeHostPortTriple) origin;
return this.value.matches(resource.scheme);
return SchemeHostPortTriple.matchesSecureScheme(this.value, resource.scheme);
}

@Override public boolean matchesSource(@Nonnull Origin origin, @Nonnull GUID resource) {
Expand Down
121 changes: 106 additions & 15 deletions src/test/java/com/shapesecurity/salvation/PolicyQueryingTest.java
Expand Up @@ -103,24 +103,24 @@ public class PolicyQueryingTest extends CSPTest {

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")));
assertTrue("resource is 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("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("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 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("https://www.def.am:555")));
assertTrue("resource is allowed", p.allowsChildFromSource(URI.parse("http://www.def.am:555")));

p = Parser.parse("font-src https://font.com http://font.org", URI.parse("https://abc.com"));
Expand Down Expand Up @@ -149,6 +149,97 @@ public class PolicyQueryingTest extends CSPTest {

}

@Test public void testSecureSchemes() {
Policy p;

p = Parser.parse("script-src http:;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("http://a")));

p = Parser.parse("script-src http:;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("ws://a")));

p = Parser.parse("script-src http:;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("wss://a")));

p = Parser.parse("script-src http:;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("https://a")));

p = Parser.parse("script-src http:;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("ftp://a")));

p = Parser.parse("script-src http:;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("sftp://a")));

p = Parser.parse("script-src ws:;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("http://a")));

p = Parser.parse("script-src ws:;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("ws://a")));

p = Parser.parse("script-src ws:;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("wss://a")));

p = Parser.parse("script-src ws:;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("https://a")));

p = Parser.parse("script-src ws:;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("ftp://a")));

p = Parser.parse("script-src ws:;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("sftp://a")));

p = Parser.parse("script-src wss:;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("http://a")));

p = Parser.parse("script-src wss:;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("ws://a")));

p = Parser.parse("script-src wss:;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("wss://a")));

p = Parser.parse("script-src wss:;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("https://a")));

p = Parser.parse("script-src wss:;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("ftp://a")));

p = Parser.parse("script-src wss:;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("sftp://a")));

p = Parser.parse("script-src a;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("https://a")));

p = Parser.parse("script-src https://a;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("http://a")));

p = Parser.parse("script-src http://a;", "https://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("http://a")));

p = Parser.parse("script-src http://a;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("http://a")));

p = Parser.parse("script-src http://a;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("https://a")));

p = Parser.parse("script-src https://a;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("https://a")));

p = Parser.parse("script-src ws://a;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("https://a")));

p = Parser.parse("script-src wss://a;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("https://a")));

p = Parser.parse("script-src wss://a;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("ws://a")));

p = Parser.parse("script-src wss://a;", "http://example.com");
assertFalse(p.allowsScriptFromSource(URI.parse("http://a")));

p = Parser.parse("script-src ws://a;", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("https://a")));
}

@Test public void testAllowsUnsafeInline() {
Policy p;

Expand Down Expand Up @@ -250,7 +341,7 @@ public class PolicyQueryingTest extends CSPTest {
p = Parser.parse("default-src *:* 'unsafe-inline'; connect-src 'self' http://good.com/", "https://abc.com");
assertTrue("connect is allowed", p.allowsConnectTo(URI.parse("https://abc.com")));
assertTrue("connect is allowed", p.allowsConnectTo(URI.parse("http://good.com/")));
assertFalse("connect is not allowed", p.allowsConnectTo(URI.parse("https://good.com/")));
assertTrue("connect is allowed", p.allowsConnectTo(URI.parse("https://good.com/")));
assertFalse("connect is not allowed", p.allowsConnectTo(URI.parse("http://aaa.good.com/")));
assertFalse("connect is not allowed", p.allowsConnectTo(URI.parse("wss://abc.com/")));
assertFalse("connect is not allowed", p.allowsConnectTo(URI.parse("http://abc.com/")));
Expand Down Expand Up @@ -513,7 +604,7 @@ public class PolicyQueryingTest extends CSPTest {

p = Parser.parse("script-src http://*", "http://example.com");
assertTrue(p.allowsScriptFromSource(URI.parse("http://example.com")));
assertFalse(p.allowsScriptFromSource(URI.parse("https://example.com")));
assertTrue(p.allowsScriptFromSource(URI.parse("https://example.com")));
assertFalse(p.allowsScriptFromSource(URI.parse("http://example.com:81")));
assertFalse(p.allowsScriptFromSource(URI.parse("ftp://example.com")));
assertFalse(p.allowsScriptFromSource(URI.parse("ftp://example.com:80")));
Expand All @@ -538,12 +629,12 @@ public class PolicyQueryingTest extends CSPTest {
assertFalse(p.allowsStyleFromSource(new GUID("custom.scheme:")));

p = Parser.parse("style-src *:80", "https://example.com");
assertTrue(p.allowsStyleFromSource(URI.parse("http://example.com")));
assertFalse(p.allowsStyleFromSource(URI.parse("http://example.com")));
assertFalse(p.allowsStyleFromSource(URI.parse("https://example.com")));
assertFalse(p.allowsStyleFromSource(URI.parse("http://example.com:81")));
assertFalse(p.allowsStyleFromSource(URI.parse("ftp://example.com")));
assertFalse(p.allowsStyleFromSource(URI.parse("ftp://example.com:80")));
assertTrue(p.allowsStyleFromSource(URI.parse("http://example.com/path")));
assertFalse(p.allowsStyleFromSource(URI.parse("http://example.com/path")));
assertFalse(p.allowsStyleFromSource(URI.parse("ws://example.com/PATH")));
assertFalse(p.allowsStyleFromSource(URI.parse("wss://example.com/PATH")));
assertFalse(p.allowsStyleFromSource(new GUID("data:")));
Expand Down Expand Up @@ -575,7 +666,7 @@ public class PolicyQueryingTest extends CSPTest {

p = Parser.parse("style-src *:*", "http://example.com");
assertTrue(p.allowsStyleFromSource(URI.parse("http://example.com")));
assertFalse(p.allowsStyleFromSource(URI.parse("https://example.com")));
assertTrue(p.allowsStyleFromSource(URI.parse("https://example.com")));
assertTrue(p.allowsStyleFromSource(URI.parse("http://example.com:81")));
assertFalse(p.allowsStyleFromSource(URI.parse("ftp://example.com")));
assertFalse(p.allowsStyleFromSource(URI.parse("ftp://example.com:80")));
Expand All @@ -587,7 +678,7 @@ public class PolicyQueryingTest extends CSPTest {

p = Parser.parse("style-src http://*:*", "http://example.com");
assertTrue(p.allowsStyleFromSource(URI.parse("http://example.com")));
assertFalse(p.allowsStyleFromSource(URI.parse("https://example.com")));
assertTrue(p.allowsStyleFromSource(URI.parse("https://example.com")));
assertTrue(p.allowsStyleFromSource(URI.parse("http://example.com:81")));
assertFalse(p.allowsStyleFromSource(URI.parse("ftp://example.com")));
assertFalse(p.allowsStyleFromSource(URI.parse("ftp://example.com:80")));
Expand Down Expand Up @@ -647,7 +738,7 @@ public class PolicyQueryingTest extends CSPTest {

p = Parser.parse("font-src http://*", "http://example.com");
assertTrue(p.allowsFontFromSource(URI.parse("http://example.com")));
assertFalse(p.allowsFontFromSource(URI.parse("https://example.com")));
assertTrue(p.allowsFontFromSource(URI.parse("https://example.com")));
assertFalse(p.allowsFontFromSource(URI.parse("http://example.com:81")));
assertFalse(p.allowsFontFromSource(URI.parse("ftp://example.com")));
assertFalse(p.allowsFontFromSource(URI.parse("ftp://example.com:80")));
Expand Down Expand Up @@ -684,7 +775,7 @@ public class PolicyQueryingTest extends CSPTest {

p = Parser.parse("object-src http://*", "http://example.com");
assertTrue(p.allowsObjectFromSource(URI.parse("http://example.com")));
assertFalse(p.allowsObjectFromSource(URI.parse("https://example.com")));
assertTrue(p.allowsObjectFromSource(URI.parse("https://example.com")));
assertFalse(p.allowsObjectFromSource(URI.parse("http://example.com:81")));
assertFalse(p.allowsObjectFromSource(URI.parse("ftp://example.com")));
assertFalse(p.allowsObjectFromSource(URI.parse("ftp://example.com:80")));
Expand Down Expand Up @@ -721,7 +812,7 @@ public class PolicyQueryingTest extends CSPTest {

p = Parser.parse("media-src http://*", "http://example.com");
assertTrue(p.allowsMediaFromSource(URI.parse("http://example.com")));
assertFalse(p.allowsMediaFromSource(URI.parse("https://example.com")));
assertTrue(p.allowsMediaFromSource(URI.parse("https://example.com")));
assertFalse(p.allowsMediaFromSource(URI.parse("http://example.com:81")));
assertFalse(p.allowsMediaFromSource(URI.parse("ftp://example.com")));
assertFalse(p.allowsMediaFromSource(URI.parse("ftp://example.com:80")));
Expand Down Expand Up @@ -758,7 +849,7 @@ public class PolicyQueryingTest extends CSPTest {

p = Parser.parse("manifest-src http://*", "http://example.com");
assertTrue(p.allowsManifestFromSource(URI.parse("http://example.com")));
assertFalse(p.allowsManifestFromSource(URI.parse("https://example.com")));
assertTrue(p.allowsManifestFromSource(URI.parse("https://example.com")));
assertFalse(p.allowsManifestFromSource(URI.parse("http://example.com:81")));
assertFalse(p.allowsManifestFromSource(URI.parse("ftp://example.com")));
assertFalse(p.allowsManifestFromSource(URI.parse("ftp://example.com:80")));
Expand Down