From b17e1774b24be47668984c79bf1cc0fdfe0af946 Mon Sep 17 00:00:00 2001 From: Carlos Mogas da Silva Date: Sun, 23 May 2021 16:39:30 +0100 Subject: [PATCH] change to adityatelange's hugo_enc --- Dockerfile | 6 +- README.md | 2 +- hugo-encryptor.py | 160 ------------------------------------- hugo_enc/__init__.py | 153 +++++++++++++++++++++++++++++++++++ hugo_enc/decoder_script.js | 83 +++++++++++++++++++ requirements.txt | 3 - setup.py | 22 +++++ 7 files changed, 262 insertions(+), 167 deletions(-) delete mode 100644 hugo-encryptor.py create mode 100755 hugo_enc/__init__.py create mode 100644 hugo_enc/decoder_script.js delete mode 100644 requirements.txt create mode 100644 setup.py diff --git a/Dockerfile b/Dockerfile index fad65f2..da62cc8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ FROM python:alpine -ADD requirements.txt / -ADD hugo-encryptor.py / +COPY setup.py / +COPY hugo-enc / RUN apk add --update libxslt-dev libxml2-dev gcc musl-dev && \ rm -rf /var/cache/apk/* -RUN pip install --no-cache-dir -r /requirements.txt +RUN pip install --no-cache-dir -r . diff --git a/README.md b/README.md index 0c331f4..bd1b79e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ [![Build Status](https://drone.r3pek.org/api/badges/r3pek/docker-hugo-encryptor/status.svg)](https://drone.r3pek.org/r3pek/docker-hugo-encryptor) -Just a simple image for [hugo_encryptor](https://github.com/Li4n0/hugo_encryptor) so that it can be used on CI \ No newline at end of file +Just a simple image for [hugo_enc](https://github.com/adityatelange/hugo_enc) so that it can be used on CI \ No newline at end of file diff --git a/hugo-encryptor.py b/hugo-encryptor.py deleted file mode 100644 index 1382788..0000000 --- a/hugo-encryptor.py +++ /dev/null @@ -1,160 +0,0 @@ -# coding=utf-8 -import os -import base64 -import hashlib - -from bs4 import BeautifulSoup -from Crypto.Cipher import AES - - -class AESCrypt(object): - LEN = 32 - def __init__(self, key: str): - self.key = key.encode() - self.mode = AES.MODE_CBC - - def encrypt(self, text: bytes): - cryptor = AES.new(self.key, self.mode, self.key[16:]) - padlen = AESCrypt.LEN - len(text) % AESCrypt.LEN - padlen = padlen if padlen != 0 else AESCrypt.LEN - text += (chr(padlen)*padlen).encode('utf8') - - return cryptor.encrypt(text) - - -if __name__ == '__main__': - for dirpath, dirnames, filenames in os.walk('public'): - for filename in filenames: - if not filename.lower().endswith('.html'): - continue - - fullpath = os.path.join(dirpath, filename) - - soup = BeautifulSoup(open(fullpath, 'rb'), 'lxml') - blocks = soup.findAll( - 'div', {'class': 'hugo-encryptor-cipher-text'}) - - if len(blocks): - print(fullpath) - - for block in blocks: - md5 = hashlib.md5() - md5.update(block['data-password'].encode('utf-8')) - key = md5.hexdigest() - cryptor = AESCrypt(key) - text = ''.join(map(str, block.contents)) - written = base64.b64encode(cryptor.encrypt(text.encode('utf8'))) - - del block['data-password'] - block.string = written.decode() - - if len(blocks): - soup.body.append(soup.new_tag("script", src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js")) - script_tag = soup.new_tag("script") - script_tag.string = """ -const _do_decrypt = function(encrypted, password) { - let key = CryptoJS.enc.Utf8.parse(password); - let iv = CryptoJS.enc.Utf8.parse(password.substr(16)); - - let decrypted_data = CryptoJS.AES.decrypt(encrypted, key, { - iv: iv, - mode: CryptoJS.mode.CBC, - padding: CryptoJS.pad.Pkcs7 - }); - return decrypted_data.toString(CryptoJS.enc.Utf8); -}; - -const _click_handler = function(element) { - let parent = element.parentNode.parentNode; - let encrypted = parent.querySelector( - ".hugo-encryptor-cipher-text").innerText; - let password = parent.querySelector( - ".hugo-encryptor-input").value; - password = CryptoJS.MD5(password).toString(); - - let index = -1; - let elements = document.querySelectorAll( - ".hugo-encryptor-container"); - for (index = 0; index < elements.length; ++index) { - if (elements[index].isSameNode(parent)) { - break; - } - } - - let decrypted = ""; - try { - decrypted = _do_decrypt(encrypted, password); - } catch (err) { - console.error(err); - alert("Failed to decrypt."); - return; - } - - if (!decrypted.includes("--- DON'T MODIFY THIS LINE ---")) { - alert("Incorrect password."); - return; - } - - let storage = localStorage; - - let key = location.pathname + ".password." + index; - storage.setItem(key, password); - parent.innerHTML = decrypted; -} - -window.onload = () => { - let index = -1; - let elements = document.querySelectorAll( - ".hugo-encryptor-container"); - - while (1) { - ++index; - - let key = location.pathname + ".password." + index; - let password = localStorage.getItem(key); - - if (!password) { - break; - - } else { - console.log("Found password for part " + index); - - let parent = elements[index]; - let encrypted = parent.querySelector(".hugo-encryptor-cipher-text").innerText; - let decrypted = _do_decrypt(encrypted, password); - elements[index].innerHTML = decrypted; - } - } -};""" - soup.body.append(script_tag) - - with open(fullpath, 'w', encoding='utf-8') as f: - html = str(soup) - f.write(str(soup)) - - for xmlpath in ['public/index.xml', 'public/rss.xml', 'public/feed.xml']: - try: - soup = BeautifulSoup(open(xmlpath, 'rb'), 'xml') - except FileNotFoundError: - continue - - print(xmlpath) - - descriptions = soup('description') - - for description in descriptions: - - if description.string is not None: - post = BeautifulSoup(description.string,'html.parser') - block = post.find('hugo-encryptor') - - if block is None: - pass - - else: - prompt = BeautifulSoup('

Part of this article is encrypted with password, please goto the original webpage to check it out.

', 'html.parser') - block.replace_with(prompt) - description.string.replace_with(str(post)) - - with open(xmlpath, 'w', encoding='utf-8') as f: - f.write(str(soup)) diff --git a/hugo_enc/__init__.py b/hugo_enc/__init__.py new file mode 100755 index 0000000..64d3d18 --- /dev/null +++ b/hugo_enc/__init__.py @@ -0,0 +1,153 @@ +# coding=utf-8 +import os +import sys +import base64 +import hashlib +import pkgutil +import getopt +import json + +from bs4 import BeautifulSoup +from Crypto.Cipher import AES + + +class AESCrypt(object): + LEN = 32 + + def __init__(self, key: str): + self.key = key.encode() + self.mode = AES.MODE_CBC + + def encrypt(self, text: bytes): + cryptor = AES.new(self.key, self.mode, self.key[16:]) + padlen = AESCrypt.LEN - len(text) % AESCrypt.LEN + padlen = padlen if padlen != 0 else AESCrypt.LEN + text += (chr(padlen)*padlen).encode('utf8') + + return cryptor.encrypt(text) + + +def main(): + scriptURL = "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.js" + placeholder = "This article is encrypted with a password, please goto the original webpage to check it out." + identifier = "--- DON'T MODIFY THIS LINE ---" + destination = "public" + + help_strings = """ + Usage: + hugo_enc [flags] + + Flags: + -h, --help - get help about hugo_enc + --scriptURL string override the default 'scriptURL' to load crypto-js + (default: {}) + --destination string set the output folder (default: 'public') + """.format(scriptURL) + + try: + opts, args = getopt.getopt(sys.argv[1:], "hi:o:", + ["help", "scriptURL=", "destination="]) + except getopt.GetoptError as err: + print(err) + print(help_strings) + sys.exit(2) + + for opt, arg in opts: + if opt in ('-h', '--help'): + print(help_strings) + sys.exit() + elif opt in ("--scriptURL"): + scriptURL = arg + print('=> scriptURL =', scriptURL) + elif opt in ("--destination"): + destination = arg + print('=> destination =', destination) + + if not os.path.exists(destination): + sys.exit("[!] No destination directory '{}' found!\n[!] No files processed.".format( + destination)) + + for dirpath, dirnames, filenames in os.walk(destination): + for filename in filenames: + fullpath = os.path.join(dirpath, filename) + if filename.lower().endswith('.html'): + soup = BeautifulSoup(open(fullpath, 'rb'), 'lxml') + blocks = soup.findAll( + 'div', {'class': 'hugo-enc-cipher-text'}) + + if blocks: + print("[+] Processing '{}'".format(fullpath)) + + for block in blocks: + md5 = hashlib.md5() + if block.find("span"): + try: + md5.update( + block['data-password'].encode('utf-8')) + key = md5.hexdigest() + cryptor = AESCrypt(key) + text = ''.join(map(str, block.contents)) + written = base64.b64encode( + cryptor.encrypt(text.encode('utf8'))) + + del block['data-password'] + block.string = written.decode() + except KeyError: + print("\tNo Password found") + else: + print("\tAlready Processed") + + # append decryption scripts + soup.body.append( + soup.new_tag("script", src=scriptURL)) + + soup.body.append("\n") + script_tag = soup.new_tag("script") + + decoder_script = pkgutil.get_data( + 'hugo_enc', 'decoder_script.js').decode('utf8') + + script_tag.string = "\n" + decoder_script + soup.body.append(script_tag) + soup.body.append("\n") + + with open(fullpath, 'w') as f: + html = str(soup) + f.write(str(soup)) + elif filename.lower().endswith('.xml'): + soup = BeautifulSoup(open(fullpath, 'rb'), 'xml') + print("[+] Processing '{}'".format(fullpath)) + items = soup('item') + + for item in items: + link = item('link')[0] + description = item('description')[0] + + if description.string: + if identifier in description.string: + description.string.replace_with(str(placeholder)) + print("\tProcessed for: {}".format(link.string)) + elif placeholder in description.string: + print("\tAlready Processed") + + with open(fullpath, 'w') as f: + f.write(str(soup)) + + elif filename.lower().endswith('.json'): + print("[+] Processing '{}'".format(fullpath)) + + data = None + try: + with open(fullpath) as f: + data = json.load(f) + + for x in data: + for y in x: + if identifier in x[y]: + x[y] = placeholder + except Exception as e: + print("\t[!] {}: {}".format(type(e).__name__, e)) + finally: + if data: + with open(fullpath, "w") as f: + json.dump(data, f, indent=4) diff --git a/hugo_enc/decoder_script.js b/hugo_enc/decoder_script.js new file mode 100644 index 0000000..571cac9 --- /dev/null +++ b/hugo_enc/decoder_script.js @@ -0,0 +1,83 @@ +const _do_decrypt = function (encrypted, password) { + let key = CryptoJS.enc.Utf8.parse(password); + let iv = CryptoJS.enc.Utf8.parse(password.substr(16)); + + let decrypted_data = CryptoJS.AES.decrypt(encrypted, key, { + iv: iv, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7 + }); + return decrypted_data.toString(CryptoJS.enc.Utf8); +}; + +const _click_handler = function (element) { + let parent = element.parentNode.parentNode; + let encrypted = parent.querySelector(".hugo-enc-cipher-text").innerText.trim(); + let password = parent.querySelector(".hugo-enc-input").value.trim(); + password = CryptoJS.MD5(password).toString(); + + let index = -1; + let elements = document.querySelectorAll(".hugo-enc-container"); + for (index = 0; index < elements.length; ++index) { + if (elements[index].isSameNode(parent)) { + break; + } + } + + let decrypted = ""; + try { + decrypted = _do_decrypt(encrypted, password); + } catch (err) { + console.error(err); + alert("Failed to decrypt."); + return; + } + + if (!decrypted.includes("--- DON'T MODIFY THIS LINE ---")) { + alert("Incorrect password."); + return; + } + + let storage = localStorage; + + let key = location.pathname + ".password." + index; + storage.setItem(key, password); + parent.innerHTML = decrypted; +} + +window.onload = () => { + let index = -1; + let elements = document.querySelectorAll(".hugo-enc-container"); + + while (1) { + ++index; + + let key = location.pathname + ".password." + index; + let password = localStorage.getItem(key); + + if (!password) { + break; + + } else { + console.log("Found password for part " + index); + + let parent = elements[index]; + let encrypted = parent.querySelector(".hugo-enc-cipher-text").innerText.trim(); + let decrypted = _do_decrypt(encrypted, password); + elements[index].innerHTML = decrypted; + } + } +}; + +// Get the input field +var input = document.getElementById("hugo-enc-input"); + +// Execute a function when the user releases a key on the keyboard +input.addEventListener("keyup", function (event) { + if (event.key === "Enter") { + // Cancel the default action, if needed + event.preventDefault(); + // Trigger the button element with a click + document.getElementById("hugo-enc-button").click(); + } +}); diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 911f918..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -pycrypto==2.6.1 -beautifulsoup4==4.7.1 -lxml==4.6.3 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..c47582f --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +from setuptools import setup + + +setup( + name='hugo_enc', + version="1.0", + packages=["hugo_enc"], + author="Li4n0, adityatelange", + install_requires=[ + "beautifulsoup4==4.9.3", + "pycryptodome==3.10.1", + "lxml==4.6.3" + ], + description="A python tool to encrypt hugo posts", + package_data={'hugo_enc': ['decoder_script.js']}, + url='https://github.com/adityatelange/hugo_enc', + entry_points={'console_scripts': [ + 'hugo_enc = hugo_enc:main']}, + classifiers=[ + "Programming Language :: Python", + ], +)