diff --git a/utils/trivialize-rules/trivialize-cookie-rules.js b/utils/trivialize-rules/trivialize-cookie-rules.js
index 6e45579f5cac..1f0dcf941d58 100644
--- a/utils/trivialize-rules/trivialize-cookie-rules.js
+++ b/utils/trivialize-rules/trivialize-cookie-rules.js
@@ -1,4 +1,4 @@
-'use strict';
+"use strict";
/**
* For future contributors, this script was written to trivialize a special
@@ -28,89 +28,115 @@
* tailing $
*/
-let util = require('util');
-let path = require('path');
-let xml2js = require('xml2js');
+let util = require("util");
+let path = require("path");
+let xml2js = require("xml2js");
-let fs = require('graceful-fs');
+let fs = require("graceful-fs");
let readdir = util.promisify(fs.readdir);
let readFile = util.promisify(fs.readFile);
let parseString = util.promisify(xml2js.parseString);
-const rulesDir = 'src/chrome/content/rules';
+let rulesDir = "src/chrome/content/rules";
-const isTrivial = (securecookie) => {
- return securecookie.host === '.+' && securecookie.name === '.+';
+let trivialSecureCookieLiteral = `\n$1\n`;
+let secureCookieRegExp = new RegExp(
+ `\n([\t ]*)[\t ]*\n`
+);
+
+let isTrivial = securecookie => {
+ return securecookie.host === ".+" && securecookie.name === ".+";
};
(async () => {
- let readFilePromises = null;
-
- await readdir(rulesDir)
- .then(filenames => {
- return filenames.filter(filename => filename.endsWith('.xml'));
- })
- .then(filenames => {
- readFilePromises = filenames.map(async (filename) => {
- let content = null;
-
- return readFile(path.join(rulesDir, filename), 'utf8')
- .then(body => {
- content = body;
- return parseString(content);
- })
- .then(ruleset => ruleset.ruleset)
- .then(ruleset => {
- let rules = ruleset.rule.map(rule => rule.$);
- let targets = ruleset.target.map(target => target.$.host);
- let securecookies = ruleset.securecookie ? ruleset.securecookie.map(sc => sc.$) : null;
-
- if (!(rules && rules.length === 1)) {
- return;
- }
-
- if (securecookies && securecookies.length === 1 && !isTrivial(securecookies[0])) {
- let securecookie = securecookies[0];
- if (!securecookie.host.endsWith('$')) {
- return;
- }
-
- if (!securecookie.host.startsWith('^.+') &&
- !securecookie.host.startsWith('^.*') &&
- !securecookie.host.startsWith('.+') &&
- !securecookie.host.startsWith('.*') &&
- !securecookie.host.startsWith('^(?:.*\\.)?') &&
- !securecookie.host.startsWith('^(?:.+\\.)?')) {
- return;
- }
-
- let hostRegex = new RegExp(securecookie.host);
- for (let target of targets) {
- if (target.includes('.*')) {
- return;
- }
-
- target = target.replace('*.', 'www.');
- if (!hostRegex.test(target)) {
- return;
- }
- }
-
- let scReSrc = `\n([\t ]*)[\t ]*\n`;
- let scRe = new RegExp(scReSrc);
- let source = content.replace(scRe, '\n$1\n');
-
- fs.writeFileSync(path.join(rulesDir, filename), source, 'utf8');
- }
- });
+ let filenames = (await readdir(rulesDir)).filter(fn => fn.endsWith(".xml"));
+ let filePromises = filenames.map(async filename => {
+ let content = await readFile(path.join(rulesDir, filename), "utf8");
+ let { ruleset } = await parseString(content);
+
+ let targets = ruleset.target.map(target => target.$.host);
+ let securecookies = ruleset.securecookie
+ ? ruleset.securecookie.map(sc => sc.$)
+ : [];
+
+ // make sure there is at least one non-trivial securecookie
+ if (!securecookies.length || securecookies.some(isTrivial)) {
+ return;
+ }
+
+ for (let securecookie of securecookies) {
+ if (!securecookie.name === ".+") {
+ return;
+ }
+
+ if (
+ !securecookie.host.startsWith("^.+") &&
+ !securecookie.host.startsWith("^.*") &&
+ !securecookie.host.startsWith(".+") &&
+ !securecookie.host.startsWith(".*") &&
+ !securecookie.host.startsWith("(?:.*\\.)?") &&
+ !securecookie.host.startsWith("(?:.+\\.)?") &&
+ !securecookie.host.startsWith("^(?:.*\\.)?") &&
+ !securecookie.host.startsWith("^(?:.+\\.)?")
+ ) {
+ return;
+ }
+ }
+
+ // make sure each domains and its subdomains are covered by at least
+ // one securecookie rule
+ let securedDomains = new Map();
+ for (let target of targets) {
+ // we cannot handle the right-wildcards based on the argument above
+ if (target.includes(".*")) {
+ return;
+ }
+ // replace left-wildcard with www is an implementation detail
+ // see https://github.com/EFForg/https-everywhere/blob/
+ // 260cd8d402fb8069c55cda311e1be7a60db7339d/chromium/background-scripts/background.js#L595
+ if (target.includes("*.")) {
+ target = target.replace("*.", "www.");
+ }
+ securedDomains.set(target, false);
+ securedDomains.set("." + target, false);
+ }
+
+ for (let securecookie of securecookies) {
+ let pattern = new RegExp(securecookie.host);
+ securedDomains.forEach((val, key, map) => {
+ if (pattern.test(key)) {
+ map.set(key, true);
+ }
+ });
+ }
+
+ // If any value of ${securedDomains} is false, return.
+ if (![...securedDomains.values()].every(secured => secured)) {
+ return;
+ }
+
+ // remove the securecookie tag except the last one
+ // replace the last securecookie tag with a trivial one
+ for (let occurrence = securecookies.length; occurrence; --occurrence) {
+ content = content.replace(
+ secureCookieRegExp,
+ occurrence == 1 ? trivialSecureCookieLiteral : ""
+ );
+ }
+
+ return new Promise((resolve, reject) => {
+ fs.writeFile(path.join(rulesDir, filename), content, "utf8", (err) => {
+ if (err) {
+ reject(err);
+ }
+ resolve();
});
})
- .catch(error => {
- console.log(error);
- });
-
- await Promise.all(readFilePromises)
- .catch(error => {
- console.log(error);
- });
+ });
+
+
+ // use for-loop to await too many file opened error
+ for (let fp of filePromises) {
+ await fp.catch(error => console.log(error));
+ }
})();