Skip to content

Commit 5a3df7e

Browse files
committed
download self-hosted cryptojs just once (check hash of js files)
1 parent 00cdeaf commit 5a3df7e

File tree

2 files changed

+45
-24
lines changed

2 files changed

+45
-24
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ The content is encrypted with AES-256 in Python using PyCryptodome, and decrypte
3131
* ~~Add optional obfuscation of file names. F.ex. to make it impossible to guess image names.~~
3232
* Rework password handling or inventory of some sort
3333
* ~~As we are vulnerable to brute force: Review strenght of used passwords.~~
34-
* download self-hosted cryptojs just once (check hash of js files)
34+
* ~~download self-hosted cryptojs just once (check hash of js files)~~
3535
* ~~Add button press to decrypt without password (just to hide content from search engines)~~
3636
* ...to be defined
3737

@@ -585,15 +585,17 @@ It is also possible to reload a script id like `<script id="autoexec">console.lo
585585
### Self-host crypto-js
586586

587587
If you enable `selfhost` then you'll choose to upload crypto-js to your web server rather than using cloudflare CDN.
588+
The self-host location is "SITE_URL/assets/javascripts/cryptojs/".
588589

589-
Additionally if you set `selfhost_download` then the required files will be downloaded **every time** on `mkdocs serve` or `mkdocs build`.
590-
Be aware that this costs additional build time and it is better to copy the files from `site/assets/javascripts/cryptojs/*` to `docs/assets/javascripts/cryptojs/*` to speed things up.
590+
Additionally if you set `selfhost_download` then the required files will be automatically downloaded if needed.
591+
The files are checked for their MD5 hash and saved to `docs_dir` or `selfhost_dir` (relative to `mkdocs.yml`).
591592

592593
```yaml
593594
plugins:
594595
- encryptcontent:
595596
selfhost: true
596597
selfhost_download: false
598+
selfhost_dir: 'theme_overrides'
597599
```
598600

599601
### File name obfuscation

encryptcontent/plugin.py

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import json
88
import math
99
from pathlib import Path
10+
from os.path import exists
1011
from Crypto import Random
1112
from jinja2 import Template
1213
from Crypto.Cipher import AES
@@ -22,12 +23,12 @@
2223
string_types = str
2324

2425
JS_LIBRARIES = [
25-
'//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/core.js',
26-
'//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/enc-base64.js',
27-
'//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/cipher-core.js',
28-
'//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/pad-nopadding.js',
29-
'//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/md5.js',
30-
'//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/aes.js'
26+
['//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/core.js','b55ae8027253d4d54c4f1ef3b6254646'],
27+
['//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/enc-base64.js','f551ce1340a86e5edbfef4a6aefa798f'],
28+
['//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/cipher-core.js','dfddc0e33faf7a794e0c3c140544490e'],
29+
['//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/pad-nopadding.js','e288e14e2cd299c3247120114e1178e6'],
30+
['//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/md5.js','349498f298a6e6e6a85789d637e89109'],
31+
['//cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/aes.js','da81b91b1b57c279c29b3469649d9b86'],
3132
]
3233

3334
PLUGIN_DIR = os.path.dirname(os.path.realpath(__file__))
@@ -83,6 +84,7 @@ class encryptContentPlugin(BasePlugin):
8384
('inject', config_options.Type(dict, default={})),
8485
('selfhost', config_options.Type(bool, default=False)),
8586
('selfhost_download', config_options.Type(bool, default=True)),
87+
('selfhost_dir', config_options.Type(string_types, default='')),
8688
('translations', config_options.Type(dict, default={}, required=False)),
8789
('hash_filenames', config_options.Type(dict, default={}, required=False)),
8890
# legacy features, doesn't exist anymore
@@ -103,6 +105,21 @@ def __hash_md5_file__(self, fname):
103105
hash_md5.update(chunk)
104106
return hash_md5.hexdigest()
105107

108+
def __download_and_check__(self, filename, url, hash):
109+
hash_md5 = hashlib.md5()
110+
if not exists(filename):
111+
with urlopen(url) as response:
112+
filecontent = response.read()
113+
hash_md5.update(filecontent)
114+
hash_check = hash_md5.hexdigest()
115+
if hash == hash_check:
116+
with open(filename, 'wb') as file:
117+
file.write(filecontent)
118+
logger.info('Downloaded external asset "' + filename.name + '"')
119+
else:
120+
logger.error('Error downloading asset "' + filename.name + '" hash mismatch!')
121+
os._exit(1)
122+
106123
def __encrypt_text_aes__(self, text, password):
107124
""" Encrypts text with AES-256. """
108125
BLOCK_SIZE = 32
@@ -125,12 +142,12 @@ def __encrypt_content__(self, content, base_path, encryptcontent_path, encryptco
125142
ciphertext_bundle = self.__encrypt_text_aes__(content, encryptcontent['password'])
126143

127144
# optionally selfhost cryptojs
128-
if self.config["selfhost"]:
129-
js_libraries = []
130-
for jsurl in JS_LIBRARIES:
131-
js_libraries.append(base_path + 'assets/javascripts/cryptojs/' + jsurl.rsplit('/',1)[1])
132-
else:
133-
js_libraries = JS_LIBRARIES
145+
js_libraries = []
146+
for jsurl in JS_LIBRARIES:
147+
if self.config["selfhost"]:
148+
js_libraries.append(base_path + 'assets/javascripts/cryptojs/' + jsurl[0].rsplit('/',1)[1])
149+
else:
150+
js_libraries = jsurl[0]
134151

135152
obfuscate = encryptcontent.get('obfuscate')
136153
if obfuscate:
@@ -626,16 +643,18 @@ def on_post_build(self, config, **kwargs):
626643
logger.info('Modified search_index.')
627644

628645
# optionally download cryptojs
629-
if self.config["selfhost"] and self.config["selfhost_download"]:
630-
#TODO just download once and verify hash afterwards
631-
logger.info('Downloading cryptojs for self-hosting... Please consider copying "assets/javascripts/cryptojs/" to "doc/" and setting "selfhost_download: false" to decrease build time.')
632-
Path(config.data["site_dir"] + '/assets/javascripts/cryptojs/').mkdir(parents=True, exist_ok=True)
646+
if self.config['selfhost'] and self.config['selfhost_download']:
647+
logger.info('Downloading cryptojs for self-hosting (if needed)...')
648+
if self.config['selfhost_dir']:
649+
configpath = Path(config['config_file_path']).parents[0]
650+
dlpath = configpath.joinpath(self.config['selfhost_dir'] + '/assets/javascripts/cryptojs/')
651+
else:
652+
dlpath = Path(config.data['docs_dir'] + '/assets/javascripts/cryptojs/')
653+
dlpath.mkdir(parents=True, exist_ok=True)
633654
for jsurl in JS_LIBRARIES:
634-
dlurl = "https:" + jsurl
635-
filepath = Path(config.data["site_dir"] + '/assets/javascripts/cryptojs/' + jsurl.rsplit('/',1)[1])
636-
with urlopen(dlurl) as response:
637-
with open(filepath, 'wb') as file:
638-
file.write(response.read())
655+
dlurl = "https:" + jsurl[0]
656+
filepath = dlpath.joinpath(jsurl[0].rsplit('/',1)[1])
657+
self.__download_and_check__(filepath, dlurl, jsurl[1])
639658

640659
passwords = set() #get all unique passwords
641660
for location in self.setup['locations'].keys():

0 commit comments

Comments
 (0)