Merge branch 'CB-7291'

This commit is contained in:
Ian Clelland 2014-08-26 15:25:41 -04:00
commit 2e3e4ec3b2
7 changed files with 96 additions and 38 deletions

View File

@ -30,7 +30,20 @@
Apache Cordova Team Apache Cordova Team
</author> </author>
<access origin="*"/> <!-- Allow access to arbitrary URLs in the Cordova WebView. This is a
development mode setting, and should be changed for production. -->
<access origin="http://*/*"/>
<access origin="https://*/*"/>
<!-- Grant certain URLs the ability to launch external applications. This
behaviour is set to match that of Cordova versions before 3.6.0, and
should be reviewed before launching an application in production. It
may be changed in the future. -->
<access origin="tel:*" launch-external="yes"/>
<access origin="geo:*" launch-external="yes"/>
<access origin="mailto:*" launch-external="yes"/>
<access origin="sms:*" launch-external="yes"/>
<access origin="market:*" launch-external="yes"/>
<!-- <content src="http://mysite.com/myapp.html" /> for external pages --> <!-- <content src="http://mysite.com/myapp.html" /> for external pages -->
<content src="index.html" /> <content src="index.html" />

View File

@ -58,7 +58,7 @@ public class Config {
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?"); Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
return; return;
} }
parser.getWhitelist().addWhiteListEntry(origin, subdomains); parser.getInternalWhitelist().addWhiteListEntry(origin, subdomains);
} }
/** /**
@ -72,7 +72,21 @@ public class Config {
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?"); Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
return false; return false;
} }
return parser.getWhitelist().isUrlWhiteListed(url); return parser.getInternalWhitelist().isUrlWhiteListed(url);
}
/**
* Determine if URL is in approved list of URLs to launch external applications.
*
* @param url
* @return true if whitelisted
*/
public static boolean isUrlExternallyWhiteListed(String url) {
if (parser == null) {
Log.e(TAG, "Config was not initialised. Did you forget to Config.init(this)?");
return false;
}
return parser.getExternalWhitelist().isUrlWhiteListed(url);
} }
public static String getStartUrl() { public static String getStartUrl() {
@ -87,7 +101,11 @@ public class Config {
} }
public static Whitelist getWhitelist() { public static Whitelist getWhitelist() {
return parser.getWhitelist(); return parser.getInternalWhitelist();
}
public static Whitelist getExternalWhitelist() {
return parser.getExternalWhitelist();
} }
public static List<PluginEntry> getPluginEntries() { public static List<PluginEntry> getPluginEntries() {

View File

@ -37,11 +37,16 @@ public class ConfigXmlParser {
private String launchUrl = "file:///android_asset/www/index.html"; private String launchUrl = "file:///android_asset/www/index.html";
private CordovaPreferences prefs = new CordovaPreferences(); private CordovaPreferences prefs = new CordovaPreferences();
private Whitelist whitelist = new Whitelist(); private Whitelist internalWhitelist = new Whitelist();
private Whitelist externalWhitelist = new Whitelist();
private ArrayList<PluginEntry> pluginEntries = new ArrayList<PluginEntry>(20); private ArrayList<PluginEntry> pluginEntries = new ArrayList<PluginEntry>(20);
public Whitelist getWhitelist() { public Whitelist getInternalWhitelist() {
return whitelist; return internalWhitelist;
}
public Whitelist getExternalWhitelist() {
return externalWhitelist;
} }
public CordovaPreferences getPreferences() { public CordovaPreferences getPreferences() {
@ -77,6 +82,11 @@ public class ConfigXmlParser {
boolean insideFeature = false; boolean insideFeature = false;
ArrayList<String> urlMap = null; ArrayList<String> urlMap = null;
// Add implicitly allowed URLs
internalWhitelist.addWhiteListEntry("file:///*", false);
internalWhitelist.addWhiteListEntry("content:///*", false);
internalWhitelist.addWhiteListEntry("data:*", false);
while (eventType != XmlResourceParser.END_DOCUMENT) { while (eventType != XmlResourceParser.END_DOCUMENT) {
if (eventType == XmlResourceParser.START_TAG) { if (eventType == XmlResourceParser.START_TAG) {
String strNode = xml.getName(); String strNode = xml.getName();
@ -104,8 +114,21 @@ public class ConfigXmlParser {
else if (strNode.equals("access")) { else if (strNode.equals("access")) {
String origin = xml.getAttributeValue(null, "origin"); String origin = xml.getAttributeValue(null, "origin");
String subdomains = xml.getAttributeValue(null, "subdomains"); String subdomains = xml.getAttributeValue(null, "subdomains");
boolean external = (xml.getAttributeValue(null, "launch-external") != null);
if (origin != null) { if (origin != null) {
whitelist.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); if (external) {
externalWhitelist.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
} else {
if ("*".equals(origin)) {
// Special-case * origin to mean http and https when used for internal
// whitelist. This prevents external urls like sms: and geo: from being
// handled internally.
internalWhitelist.addWhiteListEntry("http://*/*", false);
internalWhitelist.addWhiteListEntry("https://*/*", false);
} else {
internalWhitelist.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
}
}
} }
} }
else if (strNode.equals("preference")) { else if (strNode.equals("preference")) {

View File

@ -130,7 +130,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
// Read from config.xml: // Read from config.xml:
protected CordovaPreferences preferences; protected CordovaPreferences preferences;
protected Whitelist whitelist; protected Whitelist internalWhitelist;
protected Whitelist externalWhitelist;
protected String launchUrl; protected String launchUrl;
protected ArrayList<PluginEntry> pluginEntries; protected ArrayList<PluginEntry> pluginEntries;
@ -216,7 +217,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
preferences = parser.getPreferences(); preferences = parser.getPreferences();
preferences.setPreferencesBundle(getIntent().getExtras()); preferences.setPreferencesBundle(getIntent().getExtras());
preferences.copyIntoIntentExtras(this); preferences.copyIntoIntentExtras(this);
whitelist = parser.getWhitelist(); internalWhitelist = parser.getInternalWhitelist();
externalWhitelist = parser.getExternalWhitelist();
launchUrl = parser.getLaunchUrl(); launchUrl = parser.getLaunchUrl();
pluginEntries = parser.getPluginEntries(); pluginEntries = parser.getPluginEntries();
Config.parser = parser; Config.parser = parser;
@ -324,7 +326,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
if (appView.pluginManager == null) { if (appView.pluginManager == null) {
appView.init(this, webViewClient != null ? webViewClient : makeWebViewClient(appView), appView.init(this, webViewClient != null ? webViewClient : makeWebViewClient(appView),
webChromeClient != null ? webChromeClient : makeChromeClient(appView), webChromeClient != null ? webChromeClient : makeChromeClient(appView),
pluginEntries, whitelist, preferences); pluginEntries, internalWhitelist, externalWhitelist, preferences);
} }
// TODO: Have the views set this themselves. // TODO: Have the views set this themselves.
@ -789,7 +791,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
// If errorUrl specified, then load it // If errorUrl specified, then load it
final String errorUrl = preferences.getString("errorUrl", null); final String errorUrl = preferences.getString("errorUrl", null);
if ((errorUrl != null) && (errorUrl.startsWith("file://") || whitelist.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) { if ((errorUrl != null) && (errorUrl.startsWith("file://") || internalWhitelist.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
// Load URL on UI thread // Load URL on UI thread
me.runOnUiThread(new Runnable() { me.runOnUiThread(new Runnable() {
@ -849,7 +851,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
*/ */
@Deprecated // Use whitelist object directly. @Deprecated // Use whitelist object directly.
public boolean isUrlWhiteListed(String url) { public boolean isUrlWhiteListed(String url) {
return whitelist.isUrlWhiteListed(url); return internalWhitelist.isUrlWhiteListed(url);
} }
/* /*

View File

@ -48,18 +48,11 @@ class CordovaUriHelper {
*/ */
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
boolean shouldOverrideUrlLoading(WebView view, String url) { boolean shouldOverrideUrlLoading(WebView view, String url) {
// The WebView should support http and https when going on the Internet
if(url.startsWith("http:") || url.startsWith("https:"))
{
// We only need to whitelist sites on the Internet!
if(appView.getWhitelist().isUrlWhiteListed(url))
{
return false;
}
}
// Give plugins the chance to handle the url // Give plugins the chance to handle the url
else if (this.appView.pluginManager.onOverrideUrlLoading(url)) { if (this.appView.pluginManager.onOverrideUrlLoading(url)) {
// Do nothing other than what the plugins wanted.
// If any returned true, then the request was handled.
return true;
} }
else if(url.startsWith("file://") | url.startsWith("data:")) else if(url.startsWith("file://") | url.startsWith("data:"))
{ {
@ -67,7 +60,11 @@ class CordovaUriHelper {
//DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING! //DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
return url.contains("app_webview"); return url.contains("app_webview");
} }
else else if (appView.getWhitelist().isUrlWhiteListed(url)) {
// Allow internal navigation
return false;
}
else if (appView.getExternalWhitelist().isUrlWhiteListed(url))
{ {
try { try {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
@ -78,11 +75,12 @@ class CordovaUriHelper {
intent.setSelector(null); intent.setSelector(null);
} }
this.cordova.getActivity().startActivity(intent); this.cordova.getActivity().startActivity(intent);
return true;
} catch (android.content.ActivityNotFoundException e) { } catch (android.content.ActivityNotFoundException e) {
LOG.e(TAG, "Error loading url " + url, e); LOG.e(TAG, "Error loading url " + url, e);
} }
} }
//Default behaviour should be to load the default intent, let's see what happens! // Intercept the request and do nothing with it -- block it
return true; return true;
} }
} }

View File

@ -88,7 +88,9 @@ public class CordovaWebView extends WebView {
private WebChromeClient.CustomViewCallback mCustomViewCallback; private WebChromeClient.CustomViewCallback mCustomViewCallback;
private CordovaResourceApi resourceApi; private CordovaResourceApi resourceApi;
private Whitelist whitelist; private Whitelist internalWhitelist;
private Whitelist externalWhitelist;
// The URL passed to loadUrl(), not necessarily the URL of the current page. // The URL passed to loadUrl(), not necessarily the URL of the current page.
String loadedUrl; String loadedUrl;
private CordovaPreferences preferences; private CordovaPreferences preferences;
@ -135,14 +137,16 @@ public class CordovaWebView extends WebView {
// Use two-phase init so that the control will work with XML layouts. // Use two-phase init so that the control will work with XML layouts.
public void init(CordovaInterface cordova, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient, public void init(CordovaInterface cordova, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient,
List<PluginEntry> pluginEntries, Whitelist whitelist, CordovaPreferences preferences) { List<PluginEntry> pluginEntries, Whitelist internalWhitelist, Whitelist externalWhitelist,
CordovaPreferences preferences) {
if (this.cordova != null) { if (this.cordova != null) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
this.cordova = cordova; this.cordova = cordova;
this.viewClient = webViewClient; this.viewClient = webViewClient;
this.chromeClient = webChromeClient; this.chromeClient = webChromeClient;
this.whitelist = whitelist; this.internalWhitelist = internalWhitelist;
this.externalWhitelist = externalWhitelist;
this.preferences = preferences; this.preferences = preferences;
super.setWebChromeClient(webChromeClient); super.setWebChromeClient(webChromeClient);
super.setWebViewClient(webViewClient); super.setWebViewClient(webViewClient);
@ -165,7 +169,7 @@ public class CordovaWebView extends WebView {
if (!Config.isInitialized()) { if (!Config.isInitialized()) {
Config.init(cdv.getActivity()); Config.init(cdv.getActivity());
} }
init(cdv, makeWebViewClient(cdv), makeWebChromeClient(cdv), Config.getPluginEntries(), Config.getWhitelist(), Config.getPreferences()); init(cdv, makeWebViewClient(cdv), makeWebChromeClient(cdv), Config.getPluginEntries(), Config.getWhitelist(), Config.getExternalWhitelist(), Config.getPreferences());
} }
} }
@ -319,7 +323,11 @@ public class CordovaWebView extends WebView {
public Whitelist getWhitelist() { public Whitelist getWhitelist() {
return this.whitelist; return this.internalWhitelist;
}
public Whitelist getExternalWhitelist() {
return this.externalWhitelist;
} }
/** /**
@ -427,7 +435,7 @@ public class CordovaWebView extends WebView {
if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) { if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
LOG.d(TAG, ">>> loadUrlNow()"); LOG.d(TAG, ">>> loadUrlNow()");
} }
if (url.startsWith("file://") || url.startsWith("javascript:") || whitelist.isUrlWhiteListed(url)) { if (url.startsWith("file://") || url.startsWith("javascript:") || internalWhitelist.isUrlWhiteListed(url)) {
super.loadUrl(url); super.loadUrl(url);
} }
} }
@ -560,7 +568,7 @@ public class CordovaWebView extends WebView {
if (!openExternal) { if (!openExternal) {
// Make sure url is in whitelist // Make sure url is in whitelist
if (url.startsWith("file://") || whitelist.isUrlWhiteListed(url)) { if (url.startsWith("file://") || internalWhitelist.isUrlWhiteListed(url)) {
// TODO: What about params? // TODO: What about params?
// Load new URL // Load new URL
this.loadUrl(url); this.loadUrl(url);

View File

@ -98,10 +98,6 @@ public class Whitelist {
public Whitelist() { public Whitelist() {
this.whiteList = new ArrayList<URLPattern>(); this.whiteList = new ArrayList<URLPattern>();
// Add implicitly allowed URLs
addWhiteListEntry("file:///*", false);
addWhiteListEntry("content:///*", false);
addWhiteListEntry("data:*", false);
} }
/* Match patterns (from http://developer.chrome.com/extensions/match_patterns.html) /* Match patterns (from http://developer.chrome.com/extensions/match_patterns.html)