Skip to content

Commit

Permalink
8254049: Update WebView to public suffix list 2020-04-24
Browse files Browse the repository at this point in the history
Reviewed-by: kcr, ghb
  • Loading branch information
arun-joseph committed Dec 3, 2020
1 parent bd51089 commit c8d9c94
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 5,276 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,17 @@ private void put(URI uri, Cookie cookie) {
}
host = canonicalize(host);

if (PublicSuffixes.isPublicSuffix(cookie.getDomain())) {
if (cookie.getDomain().equals(host)) {
cookie.setDomain("");
} else {
logger.finest("Domain is public suffix, "
+ "ignoring cookie");
return;
if (!PublicSuffixes.pslFileExists()) {
cookie.setDomain("");
} else {
if (PublicSuffixes.isPublicSuffix(cookie.getDomain())) {
if (cookie.getDomain().equals(host)) {
cookie.setDomain("");
} else {
logger.finest("Domain is public suffix, "
+ "ignoring cookie");
return;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,20 @@
import com.sun.javafx.logging.PlatformLogger.Level;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.IDN;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
* A collection of static utility methods dealing with "public suffixes".
Expand All @@ -57,10 +64,30 @@ private enum Rule {


/**
* The mapping from domain names to public suffix list rules.
* The mapping from top-level domain names to public suffix list rules.
*/
private static final Map<String,Rule> RULES =
loadRules("effective_tld_names.dat");
private static final Map<String, Rules> rulesCache = new ConcurrentHashMap<>();


/**
* The public suffix list file.
*/
private static final File pslFile = AccessController.doPrivileged((PrivilegedAction<File>)
() -> new File(System.getProperty("java.home"), "lib/security/public_suffix_list.dat"));


/*
* Determines whether the public suffix list file is available.
*/
private static final boolean pslFileExists = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) () -> {
if (!pslFile.exists()) {
logger.warning("Resource not found: " +
"lib/security/public_suffix_list.dat");
return false;
}
return true;
});


/**
Expand All @@ -71,101 +98,132 @@ private PublicSuffixes() {
}


/**
* Returns whether the public suffix list file is available.
*/
static boolean pslFileExists() {
return pslFileExists;
}


/**
* Determines if a domain is a public suffix.
*/
static boolean isPublicSuffix(String domain) {
if (domain.length() == 0) {
return false;
}
Rule rule = RULES.get(domain);
if (rule == Rule.EXCEPTION_RULE) {

if (!pslFileExists()) {
return false;
} else if (rule == Rule.SIMPLE_RULE || rule == Rule.WILDCARD_RULE) {
return true;
} else {
int pos = domain.indexOf('.') + 1;
if (pos == 0) {
pos = domain.length();
}
String parent = domain.substring(pos);
return RULES.get(parent) == Rule.WILDCARD_RULE;
}

Rules rules = Rules.getRules(domain);
return rules == null ? false : rules.match(domain);
}

/**
* Loads the public suffix list from a given resource.
*/
private static Map<String,Rule> loadRules(String resourceName) {
logger.finest("resourceName: [{0}]", resourceName);
Map<String,Rule> result = null;

InputStream is = PublicSuffixes.class.getResourceAsStream(resourceName);
if (is != null) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
result = loadRules(reader);
} catch (IOException ex) {
logger.warning("Unexpected error", ex);
} finally {
private static class Rules {

private final Map<String, Rule> rules = new HashMap<>();

private Rules(InputStream is) throws IOException {
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
BufferedReader reader = new BufferedReader(isr);

String line;
int type = reader.read();
while (type != -1 && (line = reader.readLine()) != null) {
Rule rule;
if (line.startsWith("!")) {
line = line.substring(1);
rule = Rule.EXCEPTION_RULE;
} else if (line.startsWith("*.")) {
line = line.substring(2);
rule = Rule.WILDCARD_RULE;
} else {
rule = Rule.SIMPLE_RULE;
}
try {
if (reader != null) {
reader.close();
}
} catch (IOException ex) {
logger.warning("Unexpected error", ex);
line = IDN.toASCII(line, IDN.ALLOW_UNASSIGNED);
} catch (Exception ex) {
logger.warning(String.format("Error parsing rule: [%s]", line), ex);
continue;
}
rules.put(line, rule);
type = reader.read();
}
if (logger.isLoggable(Level.FINEST)) {
logger.finest("rules: {0}", toLogString(rules));
}
} else {
logger.warning("Resource not found: [{0}]",
resourceName);
}

result = result != null
? Collections.unmodifiableMap(result)
: Collections.<String,Rule>emptyMap();
if (logger.isLoggable(Level.FINEST)) {
logger.finest("result: {0}", toLogString(result));
static Rules getRules(String domain) {
String tld = getTopLevelDomain(domain);
if (tld.isEmpty()) {
return null;
}
return rulesCache.computeIfAbsent(tld, k -> createRules(tld));
}
return result;
}

/**
* Loads the public suffix list from a given reader.
*/
private static Map<String,Rule> loadRules(BufferedReader reader)
throws IOException
{
Map<String,Rule> result = new LinkedHashMap<String, Rule>();
String line;
while ((line = reader.readLine()) != null) {
line = line.split("\\s+", 2)[0];
if (line.length() == 0) {
continue;
private static String getTopLevelDomain(String domain) {
domain = IDN.toUnicode(domain, IDN.ALLOW_UNASSIGNED);
int n = domain.lastIndexOf('.');
if (n == -1) {
return domain;
}
if (line.startsWith("//")) {
continue;
return domain.substring(n + 1);
}

private static Rules createRules(String tld) {
try (InputStream pubSuffixStream = getPubSuffixStream()) {
if (pubSuffixStream == null) {
return null;
}
ZipInputStream zis = new ZipInputStream(pubSuffixStream);
ZipEntry ze = zis.getNextEntry();
while (ze != null) {
if (ze.getName().equals(tld)) {
return new Rules(zis);
} else {
ze = zis.getNextEntry();
}
}
} catch (IOException ex) {
logger.warning("Unexpected error", ex);
}
Rule rule;
if (line.startsWith("!")) {
line = line.substring(1);
rule = Rule.EXCEPTION_RULE;
} else if (line.startsWith("*.")) {
line = line.substring(2);
rule = Rule.WILDCARD_RULE;
return null;
}

private static InputStream getPubSuffixStream() {
InputStream is = AccessController.doPrivileged(
(PrivilegedAction<InputStream>) () -> {
try {
return new FileInputStream(pslFile);
} catch (FileNotFoundException ex) {
logger.warning("Resource not found: " +
"lib/security/public_suffix_list.dat");
return null;
}
}
);
return is;
}

boolean match(String domain) {
Rule rule = rules.get(domain);
if (rule == Rule.EXCEPTION_RULE) {
return false;
} else if (rule == Rule.SIMPLE_RULE || rule == Rule.WILDCARD_RULE) {
return true;
} else {
rule = Rule.SIMPLE_RULE;
}
try {
line = IDN.toASCII(line, IDN.ALLOW_UNASSIGNED);
} catch (Exception ex) {
logger.warning(String.format("Error parsing rule: [%s]", line), ex);
continue;
int pos = domain.indexOf('.') + 1;
if (pos == 0) {
pos = domain.length();
}
String parent = domain.substring(pos);
return rules.get(parent) == Rule.WILDCARD_RULE;
}
result.put(line, rule);
}
return result;
}

/**
Expand Down
Loading

0 comments on commit c8d9c94

Please sign in to comment.