Skip to content

Commit c32bc6c

Browse files
committed
Add script for HSTS conversion.
1 parent 308de41 commit c32bc6c

File tree

3 files changed

+92
-0
lines changed

3 files changed

+92
-0
lines changed

utils/convert-hsts/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
hsts.xml

utils/convert-hsts/index.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
'use strict';
2+
3+
const _ = require('highland');
4+
const fs = require('fs');
5+
const https = require('https');
6+
const JSONStream = require('JSONStream');
7+
const base64 = require('base64-stream');
8+
const xmlBuilder = require('xmlbuilder');
9+
10+
function hostToRegex(host) {
11+
return host.replace(/\./g, '\\.');
12+
}
13+
14+
_(push => {
15+
https.get('https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.json?format=TEXT', res => {
16+
push(null, res);
17+
push(null, _.nil);
18+
});
19+
})
20+
.flatMap(_)
21+
.pipe(_.pipeline(
22+
base64.decode(),
23+
_.split(),
24+
_.filter(line => !/^\s*\/\//.test(line)),
25+
JSONStream.parse('entries.*')
26+
))
27+
.reduce({
28+
xml: xmlBuilder.create('rulesetlibrary'),
29+
rulesets: new Map(),
30+
greedyInclusions: '(?!)',
31+
potentialExclusions: new Map()
32+
}, (acc, { name, mode = '', include_subdomains = false }) => {
33+
if (mode === 'force-https') {
34+
const ruleset = acc.xml.ele('ruleset', { name });
35+
acc.rulesets.set(name, ruleset);
36+
ruleset.ele('target', {
37+
host: name
38+
});
39+
if (include_subdomains) {
40+
acc.greedyInclusions += `|${hostToRegex(name)}`;
41+
ruleset.ele('target', {
42+
host: `*.${name}`
43+
});
44+
}
45+
ruleset.ele('rule', {
46+
from: '^http:',
47+
to: 'https:'
48+
});
49+
} else {
50+
acc.potentialExclusions.set(name, include_subdomains);
51+
}
52+
return acc;
53+
})
54+
.map(acc => {
55+
const regexp = new RegExp(`\.(${acc.greedyInclusions})$`);
56+
for (const [ name, include_subdomains ] of acc.potentialExclusions) {
57+
const match = name.match(regexp);
58+
if (match) {
59+
const ruleset = acc.rulesets.get(match[1]);
60+
ruleset.ele('exclusion', {
61+
pattern: `^http://${include_subdomains ? '(?:[\\w-]+\\.)*' : ''}${hostToRegex(name)}/`
62+
});
63+
ruleset.ele('test', {
64+
url: `http://${name}/`
65+
});
66+
if (include_subdomains) {
67+
ruleset.ele('test', {
68+
url: `http://host-part.${name}/`
69+
});
70+
}
71+
}
72+
}
73+
return acc.xml.end({ pretty: true });
74+
})
75+
.pipe(fs.createWriteStream(`${__dirname}/hsts.xml`));

utils/convert-hsts/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "convert-hsts",
3+
"version": "1.0.0",
4+
"dependencies": {
5+
"JSONStream": "^1.2.1",
6+
"base64-stream": "^0.1.3",
7+
"highland": "^2.10.0",
8+
"xmlbuilder": "^8.2.2"
9+
},
10+
"keywords": [],
11+
"author": "Ingvar Stepanyan",
12+
"description": "Script to generate HTTPS Everywhere ruleset library from HSTS list",
13+
"license": "MIT",
14+
"private": true
15+
}

0 commit comments

Comments
 (0)