mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-20 23:56:20 +08:00
[CB-4096] Implemente new unified whitelist for android
This commit is contained in:
parent
7c7230dd35
commit
463c7b5027
@ -78,6 +78,11 @@ public class Config {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add implicitly allowed URLs
|
||||
whitelist.addWhiteListEntry("file:///*", false);
|
||||
whitelist.addWhiteListEntry("content:///*", false);
|
||||
whitelist.addWhiteListEntry("data:*", false);
|
||||
|
||||
XmlResourceParser xml = action.getResources().getXml(id);
|
||||
int eventType = -1;
|
||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||
|
@ -1,89 +1,128 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.cordova.LOG;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
public class Whitelist {
|
||||
private ArrayList<Pattern> whiteList;
|
||||
private HashMap<String, Boolean> whiteListCache;
|
||||
private static class URLPattern {
|
||||
public Pattern scheme;
|
||||
public Pattern host;
|
||||
public Integer port;
|
||||
public Pattern path;
|
||||
|
||||
private String regexFromPattern(String pattern, boolean allowWildcards) {
|
||||
final String toReplace = "\\.[]{}()^$?+|";
|
||||
StringBuilder regex = new StringBuilder();
|
||||
for (int i=0; i < pattern.length(); i++) {
|
||||
char c = pattern.charAt(i);
|
||||
if (c == '*' && allowWildcards) {
|
||||
regex.append(".");
|
||||
} else if (toReplace.indexOf(c) > -1) {
|
||||
regex.append('\\');
|
||||
}
|
||||
regex.append(c);
|
||||
}
|
||||
return regex.toString();
|
||||
}
|
||||
|
||||
public URLPattern(String scheme, String host, String port, String path) throws MalformedURLException {
|
||||
try {
|
||||
if (scheme == null || "*".equals(scheme)) {
|
||||
this.scheme = null;
|
||||
} else {
|
||||
this.scheme = Pattern.compile(regexFromPattern(scheme, false));
|
||||
}
|
||||
if ("*".equals(host)) {
|
||||
this.host = null;
|
||||
} else if (host.startsWith("*.")) {
|
||||
this.host = Pattern.compile("([a-z0-9.-]*\\.)?" + regexFromPattern(host.substring(2), false));
|
||||
} else {
|
||||
this.host = Pattern.compile(regexFromPattern(host, false));
|
||||
}
|
||||
if (port == null || "*".equals(port)) {
|
||||
this.port = null;
|
||||
} else {
|
||||
this.port = Integer.parseInt(port,10);
|
||||
}
|
||||
if (path == null || "/*".equals(path)) {
|
||||
this.path = null;
|
||||
} else {
|
||||
this.path = Pattern.compile(regexFromPattern(path, true));
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new MalformedURLException("Port must be a number");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches(Uri uri) {
|
||||
try {
|
||||
return ((scheme == null || scheme.matcher(uri.getScheme()).matches()) &&
|
||||
(host == null || host.matcher(uri.getHost()).matches()) &&
|
||||
(port == null || port.equals(uri.getPort())) &&
|
||||
(path == null || path.matcher(uri.getPath()).matches()));
|
||||
} catch (Exception e) {
|
||||
LOG.d(TAG, e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<URLPattern> whiteList;
|
||||
|
||||
public static final String TAG = "Whitelist";
|
||||
|
||||
public Whitelist() {
|
||||
this.whiteList = new ArrayList<Pattern>();
|
||||
this.whiteListCache = new HashMap<String, Boolean>();
|
||||
}
|
||||
|
||||
/*
|
||||
* Trying to figure out how to match * is a pain
|
||||
* So, we don't use a regex here
|
||||
*/
|
||||
|
||||
private boolean originHasWildcard(String origin){
|
||||
//First, check for a protocol, then split it if it has one.
|
||||
if(origin.contains("//"))
|
||||
{
|
||||
origin = origin.split("//")[1];
|
||||
}
|
||||
return origin.startsWith("*");
|
||||
public Whitelist() {
|
||||
this.whiteList = new ArrayList<URLPattern>();
|
||||
}
|
||||
|
||||
/* Match patterns (from http://developer.chrome.com/extensions/match_patterns.html)
|
||||
*
|
||||
* <url-pattern> := <scheme>://<host><path>
|
||||
* <scheme> := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome-extension'
|
||||
* <host> := '*' | '*.' <any char except '/' and '*'>+
|
||||
* <path> := '/' <any chars>
|
||||
*
|
||||
* We extend this to explicitly allow a port attached to the host, and we allow
|
||||
* the scheme to be omitted for backwards compatibility. (Also host is not required
|
||||
* to begin with a "*" or "*.".)
|
||||
*/
|
||||
public void addWhiteListEntry(String origin, boolean subdomains) {
|
||||
try {
|
||||
// Unlimited access to network resources
|
||||
if (origin.compareTo("*") == 0) {
|
||||
LOG.d(TAG, "Unlimited access to network resources");
|
||||
whiteList.add(Pattern.compile(".*"));
|
||||
}
|
||||
else { // specific access
|
||||
// check if subdomains should be included
|
||||
if(originHasWildcard(origin))
|
||||
{
|
||||
subdomains = true;
|
||||
//Remove the wildcard so this works properly
|
||||
origin = origin.replace("*.", "");
|
||||
if (whiteList != null) {
|
||||
try {
|
||||
// Unlimited access to network resources
|
||||
if (origin.compareTo("*") == 0) {
|
||||
LOG.d(TAG, "Unlimited access to network resources");
|
||||
whiteList = null;
|
||||
}
|
||||
|
||||
// TODO: we should not add more domains if * has already been added
|
||||
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
|
||||
Matcher matcher = schemeRegex.matcher(origin);
|
||||
if (subdomains) {
|
||||
// Check for http or https protocols
|
||||
if (origin.startsWith("http")) {
|
||||
whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?")));
|
||||
else { // specific access
|
||||
Pattern parts = Pattern.compile("^((\\*|[a-z-]+)://)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?");
|
||||
Matcher m = parts.matcher(origin);
|
||||
if (m.matches()) {
|
||||
String scheme = m.group(2);
|
||||
String host = m.group(3);
|
||||
// Special case for two urls which are allowed to have empty hosts
|
||||
if (("file".equals(scheme) || "content".equals(scheme)) && host == null) host = "*";
|
||||
String port = m.group(7);
|
||||
String path = m.group(8);
|
||||
if (scheme == null) {
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
whiteList.add(new URLPattern("http", host, port, path));
|
||||
whiteList.add(new URLPattern("https", host, port, path));
|
||||
} else {
|
||||
whiteList.add(new URLPattern(scheme, host, port, path));
|
||||
}
|
||||
}
|
||||
// Check for other protocols
|
||||
else if(matcher.find()){
|
||||
whiteList.add(Pattern.compile("^" + origin.replaceFirst("//", "//(.*\\.)?")));
|
||||
}
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
else {
|
||||
whiteList.add(Pattern.compile("^https?://(.*\\.)?" + origin));
|
||||
}
|
||||
LOG.d(TAG, "Origin to allow with subdomains: %s", origin);
|
||||
} else {
|
||||
// Check for http or https protocols
|
||||
if (origin.startsWith("http")) {
|
||||
whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://")));
|
||||
}
|
||||
// Check for other protocols
|
||||
else if(matcher.find()){
|
||||
whiteList.add(Pattern.compile("^" + origin));
|
||||
}
|
||||
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||
else {
|
||||
whiteList.add(Pattern.compile("^https?://" + origin));
|
||||
}
|
||||
LOG.d(TAG, "Origin to allow: %s", origin);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.d(TAG, "Failed to add origin %s", origin);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.d(TAG, "Failed to add origin %s", origin);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,25 +130,19 @@ public class Whitelist {
|
||||
/**
|
||||
* Determine if URL is in approved list of URLs to load.
|
||||
*
|
||||
* @param url
|
||||
* @param uri
|
||||
* @return
|
||||
*/
|
||||
public boolean isUrlWhiteListed(String url) {
|
||||
|
||||
// Check to see if we have matched url previously
|
||||
if (whiteListCache.get(url) != null) {
|
||||
return true;
|
||||
}
|
||||
public boolean isUrlWhiteListed(String uri) {
|
||||
// If there is no whitelist, then it's wide open
|
||||
if (whiteList == null) return true;
|
||||
|
||||
Uri parsedUri = Uri.parse(uri);
|
||||
// Look for match in white list
|
||||
Iterator<Pattern> pit = whiteList.iterator();
|
||||
Iterator<URLPattern> pit = whiteList.iterator();
|
||||
while (pit.hasNext()) {
|
||||
Pattern p = pit.next();
|
||||
Matcher m = p.matcher(url);
|
||||
|
||||
// If match found, then cache it to speed up subsequent comparisons
|
||||
if (m.find()) {
|
||||
whiteListCache.put(url, true);
|
||||
URLPattern p = pit.next();
|
||||
if (p.matches(parsedUri)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user