Replaced bleach with nh3

Fix for format check during conversion
This commit is contained in:
Ozzie Isaacs
2026-08-08 10:46:44 +02:00
parent ec521c07ba
commit 94eb288cf6
7 changed files with 1886 additions and 41415 deletions
+27 -14
View File
@@ -18,32 +18,45 @@
from . import logger
from lxml.etree import ParserError
import re
log = logger.create()
CONTROL_CHAR_RE = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]")
try:
# at least bleach 6.0 is needed -> incomplatible change from list arguments to set arguments
from bleach import clean as clean_html
from bleach.sanitizer import ALLOWED_TAGS
bleach = True
except ImportError:
import nh3
from nh3 import clean as clean_html
bleach = False
BASE_ALLOWED_TAGS = nh3.ALLOWED_TAGS
BASE_ALLOWED_ATTRIBUTES = nh3.ALLOWED_ATTRIBUTES
except ImportError:
# at least bleach 6.0 is needed -> incomplatible change from list arguments to set arguments
from bleach import clean as clean_html
from bleach.sanitizer import ALLOWED_ATTRIBUTES as BASE_ALLOWED_ATTRIBUTES
from bleach.sanitizer import ALLOWED_TAGS as BASE_ALLOWED_TAGS
bleach = True
ALLOWED_TAGS = set(BASE_ALLOWED_TAGS) | {"p", "span", "div", "pre", "br", "h1", "h2", "h3", "h4", "h5", "h6", "img"}
ALLOWED_ATTRIBUTES = {
key: set(value) for key, value in BASE_ALLOWED_ATTRIBUTES.items()
}
ALLOWED_ATTRIBUTES["*"] = set(ALLOWED_ATTRIBUTES.get("*", set())) | {"class", "style"}
ALLOWED_ATTRIBUTES["a"] = set(ALLOWED_ATTRIBUTES.get("a", set())) | {"href", "title", "rel"}
ALLOWED_ATTRIBUTES["img"] = set(ALLOWED_ATTRIBUTES.get("img", set())) | {"src", "alt", "title", "width", "height"}
def _normalize_html_input(unsafe_text):
return CONTROL_CHAR_RE.sub("", unsafe_text)
def clean_string(unsafe_text, book_id=0):
try:
unsafe_text = _normalize_html_input(unsafe_text)
if bleach:
allowed_tags = list(ALLOWED_TAGS)
allowed_tags.extend(["p", "span", "div", "pre", "br", "h1", "h2", "h3", "h4", "h5", "h6", "img"])
allowed_attributes = {
"*": ["class", "style"],
"a": ["href", "title", "rel"],
"img": ["src", "alt", "title", "width", "height"],
}
safe_text = clean_html(unsafe_text, tags=set(allowed_tags), attributes=allowed_attributes)
safe_text = clean_html(unsafe_text, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES)
else:
safe_text = clean_html(unsafe_text)
safe_text = clean_html(unsafe_text, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES, link_rel=None)
except ParserError as e:
log.error("Comments of book {} are corrupted: {}".format(book_id, e))
safe_text = ""
+9 -1
View File
@@ -189,7 +189,15 @@ def convert_bookformat(book_id):
flash(_("Source format for conversion not supported"), category="error")
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
if book_format_to.lower() not in constants.EXTENSIONS_CONVERT_TO:
allowed_destination_formats = set(constants.EXTENSIONS_CONVERT_TO)
if config.config_kepubifypath:
allowed_destination_formats.add('kepub')
if book_format_to.lower() == 'kepub' and book_format_from.lower() != 'epub':
flash(_("Source format for conversion not supported"), category="error")
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
if book_format_to.lower() not in allowed_destination_formats:
flash(_("Destination format for conversion not supported"), category="error")
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
+2 -1
View File
@@ -24,7 +24,7 @@ import mimetypes
import hashlib
from io import BytesIO
from . import logger, config
from . import logger
log = logger.create()
@@ -46,6 +46,7 @@ def get_mimetype(ext):
def get_temp_dir():
base_tmp_dir = os.path.join(gettempdir(), 'calibre_web')
from . import config
instance_value = "{}|{}".format(config.config_calibre_dir or "", config.config_port or "")
instance_key = hashlib.md5(instance_value.encode('utf-8')).hexdigest() # nosec
tmp_dir = os.path.join(base_tmp_dir, "instance_{}".format(instance_key[:12]))
+7 -1
View File
@@ -1262,7 +1262,13 @@ def download_link(book_id, book_format, anyname):
@login_required_if_no_ano
@download_required
def send_to_ereader(book_id, book_format, convert):
if book_format.lower() not in constants.EXTENSIONS_UPLOAD:
book = calibre_db.get_book(book_id)
allowed_formats = check_send_to_ereader(book) if book else []
is_allowed = any(
option.get('format', '').lower() == book_format.lower() and int(option.get('convert', -1)) == convert
for option in allowed_formats
)
if not is_allowed:
return make_response(jsonify(type="danger", message=_("Book format not supported")))
if not config.get_mail_server_configured():
return make_response(jsonify(type="danger", message=_("Please configure the SMTP mail settings first...")))
+1 -1
View File
@@ -49,7 +49,7 @@ dependencies = [
"urllib3>=1.22,<3.0",
"Flask-Limiter>=2.3.0,<4.2.0",
"regex>=2022.3.2,<2026.1.16",
"bleach>=6.0.0,<6.4.0",
"nh3>=0.3.0,<0.4.0",
"python-magic>=0.4.27,<0.5.0",
"python-magic-bin>=0.4.0,<0.5.0;sys_platform=='win32'",
"flask-httpAuth>=4.4.0,<5.0.0",
+1 -1
View File
@@ -19,7 +19,7 @@ netifaces-plus>=0.12.0,<0.13.0
urllib3>=1.22,<3.0
Flask-Limiter>=2.3.0,<4.2.0
regex>=2022.3.2,<2026.1.16
bleach>=6.0.0,<6.4.0
nh3>=0.3.0,<0.4.0
python-magic>=0.4.27,<0.5.0
python-magic-bin>=0.4.0,<0.5.0;sys_platform=='win32'
flask-httpAuth>=4.4.0,<5.0.0
File diff suppressed because it is too large Load Diff