change to adityatelange's hugo_enc
This commit is contained in:
parent
76fbb1cfd5
commit
b17e1774b2
7 changed files with 262 additions and 167 deletions
|
@ -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 .
|
||||
|
|
|
@ -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
|
||||
Just a simple image for [hugo_enc](https://github.com/adityatelange/hugo_enc) so that it can be used on CI
|
|
@ -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('<p><i>Part of this article is encrypted with password, please goto the original webpage to check it out.</i></p>', '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))
|
153
hugo_enc/__init__.py
Executable file
153
hugo_enc/__init__.py
Executable file
|
@ -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)
|
83
hugo_enc/decoder_script.js
Normal file
83
hugo_enc/decoder_script.js
Normal file
|
@ -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();
|
||||
}
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
pycrypto==2.6.1
|
||||
beautifulsoup4==4.7.1
|
||||
lxml==4.6.3
|
22
setup.py
Normal file
22
setup.py
Normal file
|
@ -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",
|
||||
],
|
||||
)
|
Loading…
Reference in a new issue