Skip to content
This repository has been archived by the owner on Nov 6, 2023. It is now read-only.

Commit

Permalink
Refactor and enhance trivialize-cookie-rules.js (#17438)
Browse files Browse the repository at this point in the history
* Refactor and enhance trivialize-cookie-rules.js
* With help from @pipboy96 on some syntax issues, re-open #17420

* Update comments and fix typo
* Avoid fs.writeFileSync (too many files opened error)
  • Loading branch information
Chan Chak Shing authored and zoracon committed Mar 11, 2019
1 parent 83642b8 commit 2a7a7e8
Showing 1 changed file with 101 additions and 75 deletions.
176 changes: 101 additions & 75 deletions utils/trivialize-rules/trivialize-cookie-rules.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'use strict';
"use strict";

/**
* For future contributors, this script was written to trivialize a special
Expand Down Expand Up @@ -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<securecookie host=".+"$3name=".+"/>\n`;
let secureCookieRegExp = new RegExp(
`\n([\t ]*)<securecookie\\s*host=\\s*"([^"]+)"(\\s*)name=\\s*"([^"]+)"\\s*?/>[\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 ]*)<securecookie\\s*host=\\s*"([^"]+)"(\\s*)name=\\s*"([^"]+)"\\s*?/>[\t ]*\n`;
let scRe = new RegExp(scReSrc);
let source = content.replace(scRe, '\n$1<securecookie host=".+"$3name="$4" />\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));
}
})();

0 comments on commit 2a7a7e8

Please sign in to comment.