parent
76fbb1cfd5
commit
b17e1774b2
@ -1,9 +1,9 @@ |
|||||||
FROM python:alpine |
FROM python:alpine |
||||||
|
|
||||||
ADD requirements.txt / |
COPY setup.py / |
||||||
ADD hugo-encryptor.py / |
COPY hugo-enc / |
||||||
|
|
||||||
RUN apk add --update libxslt-dev libxml2-dev gcc musl-dev && \ |
RUN apk add --update libxslt-dev libxml2-dev gcc musl-dev && \ |
||||||
rm -rf /var/cache/apk/* |
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) |
[![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)) |
|
@ -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) |
@ -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 |
|
@ -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 new issue