001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.shell;
017:
018: import com.google.gwt.core.ext.TreeLogger;
019:
020: import java.util.HashSet;
021: import java.util.Iterator;
022: import java.util.Set;
023: import java.util.regex.Pattern;
024: import java.util.regex.PatternSyntaxException;
025:
026: /**
027: * This class contains utility functions to do whitelist/blacklist handling.
028: */
029: public class BrowserWidgetHostChecker {
030:
031: /**
032: * The set of always allowed URLs, which are immune to blacklisting.
033: */
034: private static final Set<String> alwaysValidHttpHosts = new HashSet<String>();
035:
036: /**
037: * The set of blacklisted URLs.
038: */
039: private static final Set<String> invalidHttpHosts = new HashSet<String>();
040:
041: private static String oldBlackList = null;
042:
043: private static String oldWhiteList = null;
044:
045: /**
046: * The set of whitelisted URLs.
047: */
048: private static final Set<String> validHttpHosts = new HashSet<String>();
049:
050: static {
051: alwaysValidHttpHosts.add("^https?://localhost");
052: alwaysValidHttpHosts.add("^file:");
053: alwaysValidHttpHosts.add("^about:");
054: alwaysValidHttpHosts.add("^res:");
055: alwaysValidHttpHosts.add("^javascript:");
056: alwaysValidHttpHosts.add("^([a-zA-Z][:])[/\\\\]");
057: // matches c:\ and c:/
058: alwaysValidHttpHosts.add("^https?://localhost/");
059: alwaysValidHttpHosts.add("^https?://localhost[.]localdomain/");
060: alwaysValidHttpHosts.add("^https?://127[.]0[.]0[.]1/");
061: alwaysValidHttpHosts.add("^https?://localhost$");
062: alwaysValidHttpHosts.add("^https?://localhost[.]localdomain$");
063: alwaysValidHttpHosts.add("^https?://127[.]0[.]0[.]1$");
064: }
065:
066: /**
067: * This method blacklists the supplied regexes, separated by comma or space.
068: *
069: * @param regexes the regexes to be forbidden
070: */
071: public static boolean blacklistRegexes(String regexes) {
072: return addRegex(regexes, false);
073: }
074:
075: /**
076: * This method blacklists the supplied URL, and any that share the same host.
077: *
078: * @param url the host to be forbidden
079: */
080: public static void blacklistURL(String url) {
081: String hostRegex = computeHostRegex(url);
082: blacklistRegexes(hostRegex);
083: }
084:
085: /**
086: * This method checks the host to see if it is in the supplied set of regexes.
087: *
088: * @param hostUnderConsideration the host to be checked
089: * @param hosts the set of regexes to be checked against
090: * @return true if the host matches
091: */
092: public static String checkHost(String hostUnderConsideration,
093: Set<String> hosts) {
094: hostUnderConsideration = hostUnderConsideration.toLowerCase();
095: for (Iterator<String> i = hosts.iterator(); i.hasNext();) {
096: String rule = i.next().toString().toLowerCase();
097: // match on lowercased regex
098: if (hostUnderConsideration.matches(".*" + rule + ".*")) {
099: return rule;
100: }
101: }
102: return null;
103: }
104:
105: /**
106: * This method computes the host regular expression for the given url.
107: *
108: * @param url the url to be allowed or disallowed
109: * @return the regex that matches the host in the url
110: */
111: public static String computeHostRegex(String url) {
112: // the entire URL up to the first slash not prefixed by a slash or colon.
113: String raw = url.split("(?<![:/])/")[0];
114: // escape the dots and put a begin line specifier on the result
115: return "^" + escapeString(raw);
116: }
117:
118: /**
119: * This method formats the blacklist for display in the treeLogger.
120: *
121: * @return the list of regexes as a String
122: */
123: public static String formatBlackList() {
124: return formatRules(invalidHttpHosts);
125: }
126:
127: /**
128: * This method formats the list of rules for display in the treeLogger.
129: *
130: * @param hosts the set of regexes that match hosts
131: * @return the list of regexes as a String
132: */
133: public static String formatRules(Set<String> hosts) {
134: StringBuffer out = new StringBuffer();
135: for (Iterator<String> i = hosts.iterator(); i.hasNext();) {
136: String rule = i.next();
137: out.append(rule);
138: out.append(" ");
139: }
140: return out.toString();
141: }
142:
143: /**
144: * This method formats the whitelist for display in the treeLogger.
145: *
146: * @return the list of regexes as a String
147: */
148: public static String formatWhiteList() {
149: return formatRules(validHttpHosts);
150: }
151:
152: /**
153: * This method returns true if the host is always admissable, regardless of
154: * the blacklist.
155: *
156: * @param url the URL to be verified
157: * @return returns true if the host is always admissable
158: */
159: public static boolean isAlwaysWhitelisted(String url) {
160: String whitelistRuleFound;
161: whitelistRuleFound = checkHost(url, alwaysValidHttpHosts);
162: return whitelistRuleFound != null;
163: }
164:
165: /**
166: * This method returns true if the host is forbidden.
167: *
168: * @param url the URL to be verified
169: * @return returns the regex that specified the host matches the blacklist
170: */
171: public static String matchBlacklisted(String url) {
172: oldBlackList = formatBlackList();
173: return checkHost(url, invalidHttpHosts);
174: }
175:
176: /**
177: * This method returns true if the host is admissable, provided it is not on
178: * the blacklist.
179: *
180: * @param url the URL to be verified
181: * @return returns the regex that specified the host matches the whitelist
182: */
183: public static String matchWhitelisted(String url) {
184: oldWhiteList = formatWhiteList();
185: return checkHost(url, validHttpHosts);
186: }
187:
188: /**
189: * This method formats a message, and logs it to the treelogger, stating that
190: * the url was blocked.
191: *
192: * @param url the URL that was disallowed
193: * @param header the treelogger under which these messages will be put
194: * @param msgType either a caution or an error
195: */
196: public static void notifyBlacklistedHost(String blacklistRuleFound,
197: String url, TreeLogger header, TreeLogger.Type msgType) {
198: TreeLogger reason = header.branch(msgType, "reason: " + url
199: + " is blacklisted", null);
200: reason.log(msgType, "To fix: remove \"" + blacklistRuleFound
201: + "\" from system property gwt.hosts.blacklist", null);
202: }
203:
204: /**
205: * This method formats a message, and logs it to the treelogger, stating that
206: * the url was not trusted.
207: *
208: * @param url the URL that provoked the dialog box
209: * @param header the treelogger under which these messages will be put
210: * @param msgType either a caution or an error
211: */
212: public static void notifyUntrustedHost(String url,
213: TreeLogger header, TreeLogger.Type msgType) {
214: String whiteListStr = oldWhiteList;
215: String blackListStr = oldBlackList;
216: String hostRegex = computeHostRegex(url);
217: TreeLogger reason = header.branch(msgType, "reason: " + url
218: + " is not in the whitelist", null);
219: reason.log(msgType, "whitelist: " + whiteListStr, null);
220: reason.log(msgType, "blacklist: " + blackListStr, null);
221: TreeLogger fix = header.branch(msgType,
222: "To fix: add regex matching "
223: + "URL to -whitelist command line argument",
224: null);
225: fix.log(msgType, "Example: -whitelist=\"" + whiteListStr + " "
226: + hostRegex + "\"", null);
227: TreeLogger reject = header.branch(msgType,
228: "To reject automatically: add regex matching "
229: + "URL to -blacklist command line argument",
230: null);
231: reject.log(msgType, "Example: -blacklist=\"" + blackListStr
232: + " " + hostRegex + "\"", null);
233: }
234:
235: /**
236: * This method whitelists the supplied String of regexes, separated by comma
237: * or space.
238: *
239: * @param regexes the regexes to be allowed
240: */
241: public static boolean whitelistRegexes(String regexes) {
242: return addRegex(regexes, true);
243: }
244:
245: /**
246: * This method whitelists the supplied URL, and any that share the same host.
247: *
248: * @param url the host to be allowed
249: */
250: public static void whitelistURL(String url) {
251: String hostRegex = computeHostRegex(url);
252: whitelistRegexes(hostRegex);
253: }
254:
255: /**
256: * This method blacklists or whitelists the supplied regexes, and any that
257: * share the same host.
258: *
259: * @param whitelist if <code>true</code> the host will be whitelisted
260: * @param regexes the regular expressions to be forbidden, seperated by comma
261: * or space
262: */
263: private static boolean addRegex(String regexes, boolean whitelist) {
264: if (regexes.equals("")) {
265: return true; // adding empty string is harmless and happens by default
266: }
267: String[] items = regexes.split("[ ,]");
268: for (int i = 0; i < items.length; i++) {
269: try {
270: Pattern.compile(items[i]);
271: } catch (PatternSyntaxException e) {
272: System.err.println("The regex '" + items[i]
273: + " has syntax errors.");
274: System.err.println(e.toString());
275: return false;
276: }
277: if (whitelist) {
278: validHttpHosts.add(items[i]);
279: } else {
280: invalidHttpHosts.add(items[i]);
281: }
282: }
283: return true;
284: }
285:
286: private static String escapeString(String raw) {
287: StringBuffer out = new StringBuffer();
288: for (int i = 0; i < raw.length(); i++) {
289: char c = raw.charAt(i);
290: if (Character.isLetterOrDigit(c) || c == '-' || c == '_') {
291: out.append(c);
292: } else if (c == '\\') {
293: out.append("[\\\\]");
294: } else if (c == ']') {
295: out.append("[\\]]");
296: } else if (c == '^') {
297: out.append("[\\^]");
298: } else if (c == '[') {
299: out.append("[\\[]");
300: } else {
301: out.append("[");
302: out.append(c);
303: out.append("]");
304: }
305: }
306: return out.toString();
307: }
308:
309: }
|