diff --git a/cps.py b/cps.py
index 737b0d97..46eb1440 100755
--- a/cps.py
+++ b/cps.py
@@ -70,7 +70,6 @@ def main():
app.register_blueprint(shelf)
app.register_blueprint(admi)
app.register_blueprint(remotelogin)
- # if config.config_use_google_drive:
app.register_blueprint(gdrive)
app.register_blueprint(editbook)
if kobo_available:
diff --git a/cps/__init__.py b/cps/__init__.py
index 9b004640..3b100907 100644
--- a/cps/__init__.py
+++ b/cps/__init__.py
@@ -37,6 +37,11 @@ from . import config_sql, logger, cache_buster, cli, ub, db
from .reverseproxy import ReverseProxied
from .server import WebServer
+try:
+ import lxml
+ lxml_present = True
+except ImportError:
+ lxml_present = False
mimetypes.init()
mimetypes.add_type('application/xhtml+xml', '.xhtml')
@@ -90,6 +95,16 @@ db.CalibreDB.setup_db(config.config_calibre_dir, cli.settingspath)
calibre_db = db.CalibreDB()
def create_app():
+ if sys.version_info < (3, 0):
+ log.info(
+ '*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***')
+ print(
+ '*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***')
+ sys.exit(5)
+ if not lxml_present:
+ log.info('*** "lxml" is needed for calibre-web to run. Please install it using pip: "pip install lxml" ***')
+ print('*** "lxml" is needed for calibre-web to run. Please install it using pip: "pip install lxml" ***')
+ sys.exit(6)
app.wsgi_app = ReverseProxied(app.wsgi_app)
# For python2 convert path to unicode
if sys.version_info < (3, 0):
@@ -99,12 +114,8 @@ def create_app():
if os.environ.get('FLASK_DEBUG'):
cache_buster.init_cache_busting(app)
-
log.info('Starting Calibre Web...')
- if sys.version_info < (3, 0):
- log.info('*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***')
- print('*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***')
- sys.exit(5)
+
Principal(app)
lm.init_app(app)
app.secret_key = os.getenv('SECRET_KEY', config_sql.get_flask_session_key(ub.session))
diff --git a/cps/editbooks.py b/cps/editbooks.py
index fbfc4483..d8e16e4c 100644
--- a/cps/editbooks.py
+++ b/cps/editbooks.py
@@ -26,16 +26,19 @@ from datetime import datetime
import json
from shutil import copyfile
from uuid import uuid4
-
-# Improve this to check if scholarly is available in a global way, like other pythonic libraries
-have_scholar = True
try:
- from scholarly import scholarly
+ from lxml.html.clean import clean_html
except ImportError:
- have_scholar = False
pass
+# Improve this to check if scholarly is available in a global way, like other pythonic libraries
+try:
+ from scholarly import scholarly
+ have_scholar = True
+except ImportError:
+ have_scholar = False
+
from babel import Locale as LC
from babel.core import UnknownLocaleError
from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
@@ -57,6 +60,8 @@ except ImportError:
pass # We're not using Python 3
+
+
editbook = Blueprint('editbook', __name__)
log = logger.create()
@@ -459,9 +464,11 @@ def edit_book_series_index(series_index, book):
# Handle book comments/description
def edit_book_comments(comments, book):
modif_date = False
+ if comments:
+ comments = clean_html(comments)
if len(book.comments):
if book.comments[0].text != comments:
- book.comments[0].text = comments
+ book.comments[0].text = clean_html(comments)
modif_date = True
else:
if comments:
@@ -515,6 +522,8 @@ def edit_cc_data_value(book_id, book, c, to_save, cc_db_value, cc_string):
to_save[cc_string] = 1 if to_save[cc_string] == 'True' else 0
elif c.datatype == 'comments':
to_save[cc_string] = Markup(to_save[cc_string]).unescape()
+ if to_save[cc_string]:
+ to_save[cc_string] = clean_html(to_save[cc_string])
elif c.datatype == 'datetime':
try:
to_save[cc_string] = datetime.strptime(to_save[cc_string], "%Y-%m-%d")
diff --git a/cps/helper.py b/cps/helper.py
index b6ea6760..8c0f1656 100644
--- a/cps/helper.py
+++ b/cps/helper.py
@@ -38,6 +38,7 @@ from flask_login import current_user
from sqlalchemy.sql.expression import true, false, and_, text, func
from werkzeug.datastructures import Headers
from werkzeug.security import generate_password_hash
+from markupsafe import escape
try:
from urllib.parse import quote
@@ -97,10 +98,11 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format,
settings['body'] = _(u'This e-mail has been sent via Calibre-Web.')
else:
settings = dict()
- txt = (u"%s -> %s: %s" % (
+ link = '{}"'.format(url_for('web.show_book', book_id=book.id), escape(book.title)) # prevent xss
+ txt = u"{} -> {}: {}".format(
old_book_format,
new_book_format,
- "" + book.title + ""))
+ link)
settings['old_book_format'] = old_book_format
settings['new_book_format'] = new_book_format
WorkerThread.add(user_id, TaskConvert(file_path, book.id, txt, settings, kindle_mail, user_id))
@@ -778,7 +780,7 @@ def render_task_status(tasklist):
ret['taskMessage'] = "{}: {}".format(_(task.name), task.message)
ret['progress'] = "{} %".format(int(task.progress * 100))
- ret['user'] = user
+ ret['user'] = escape(user) # prevent xss
renderedtasklist.append(ret)
return renderedtasklist
diff --git a/cps/jinjia.py b/cps/jinjia.py
index 70a6090e..554bc791 100644
--- a/cps/jinjia.py
+++ b/cps/jinjia.py
@@ -31,7 +31,7 @@ from babel.dates import format_date
from flask import Blueprint, request, url_for
from flask_babel import get_locale
from flask_login import current_user
-
+from markupsafe import escape
from . import logger
@@ -129,6 +129,10 @@ def formatseriesindex_filter(series_index):
return series_index
return 0
+@jinjia.app_template_filter('escapedlink')
+def escapedlink_filter(url, text):
+ return "{}".format(url, escape(text))
+
@jinjia.app_template_filter('uuidfilter')
def uuidfilter(var):
return uuid4()
diff --git a/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js b/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js
index 16061ebd..a154f702 100644
--- a/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js
+++ b/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js
@@ -7,4 +7,4 @@
* @license MIT
*/
-!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).jQuery)}(this,(function(t){"use strict";function e(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=e(t);function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){for(var n=0;nt.length)&&(e=t.length);for(var n=0,r=new Array(e);n0?gt:bt)(t)},xt=Math.min,Et=function(t){return t>0?xt(mt(t),9007199254740991):0},wt=Math.max,Ot=Math.min,St=function(t){return function(e,n,r){var o,i=I(e),a=Et(i.length),u=function(t,e){var n=mt(t);return n<0?wt(n+e,0):Ot(n,e)}(r,a);if(t&&n!=n){for(;a>u;)if((o=i[u++])!=o)return!0}else for(;a>u;u++)if((t||u in i)&&i[u]===n)return t||u||0;return!t&&-1}},jt={includes:St(!0),indexOf:St(!1)},Tt=jt.indexOf,At=function(t,e){var n,r=I(t),o=0,i=[];for(n in r)!C(it,n)&&C(r,n)&&i.push(n);for(;e.length>o;)C(r,n=e[o++])&&(~Tt(i,n)||i.push(n));return i},It=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Pt=It.concat("length","prototype"),Rt={f:Object.getOwnPropertyNames||function(t){return At(t,Pt)}},_t={f:Object.getOwnPropertySymbols},Ct=yt("Reflect","ownKeys")||function(t){var e=Rt.f(N(t)),n=_t.f;return n?e.concat(n(t)):e},kt=function(t,e){for(var n=Ct(e),r=L.f,o=U.f,i=0;i0&&(!i.multiline||i.multiline&&"\n"!==t[i.lastIndex-1])&&(c="(?: "+c+")",l=" "+l,f++),n=new RegExp("^(?:"+c+")",u)),Jt&&(n=new RegExp("^"+c+"$(?!\\s)",u)),Qt&&(e=i.lastIndex),r=zt.call(a?n:i,l),a?r?(r.input=r.input.slice(f),r[0]=r[0].slice(f),r.index=i.lastIndex,i.lastIndex+=r[0].length):i.lastIndex=0:Qt&&r&&(i.lastIndex=i.global?r.index+r[0].length:e),Jt&&r&&r.length>1&&Xt.call(r[0],n,(function(){for(o=1;o=74)&&(ee=oe.match(/Chrome\/(\d+)/))&&(ne=ee[1]);var ce=ne&&+ne,fe=!!Object.getOwnPropertySymbols&&!b((function(){return!Symbol.sham&&(re?38===ce:ce>37&&ce<41)})),le=fe&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,se=J("wks"),de=y.Symbol,pe=le?de:de&&de.withoutSetter||nt,ve=function(t){return C(se,t)&&(fe||"string"==typeof se[t])||(fe&&C(de,t)?se[t]=de[t]:se[t]=pe("Symbol."+t)),se[t]},he=ve("species"),ye=!b((function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")})),be="$0"==="a".replace(/./,"$0"),ge=ve("replace"),me=!!/./[ge]&&""===/./[ge]("a","$0"),xe=!b((function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n="ab".split(t);return 2!==n.length||"a"!==n[0]||"b"!==n[1]})),Ee=function(t){return function(e,n){var r,o,i=String(A(e)),a=mt(n),u=i.length;return a<0||a>=u?t?"":void 0:(r=i.charCodeAt(a))<55296||r>56319||a+1===u||(o=i.charCodeAt(a+1))<56320||o>57343?t?i.charAt(a):r:t?i.slice(a,a+2):o-56320+(r-55296<<10)+65536}},we={codeAt:Ee(!1),charAt:Ee(!0)}.charAt,Oe=function(t,e,n){return e+(n?we(t,e).length:1)},Se=function(t){return Object(A(t))},je=Math.floor,Te="".replace,Ae=/\$([$&'`]|\d{1,2}|<[^>]*>)/g,Ie=/\$([$&'`]|\d{1,2})/g,Pe=function(t,e,n,r,o,i){var a=n+t.length,u=r.length,c=Ie;return void 0!==o&&(o=Se(o),c=Ae),Te.call(i,c,(function(i,c){var f;switch(c.charAt(0)){case"$":return"$";case"&":return t;case"`":return e.slice(0,n);case"'":return e.slice(a);case"<":f=o[c.slice(1,-1)];break;default:var l=+c;if(0===l)return i;if(l>u){var s=je(l/10);return 0===s?i:s<=u?void 0===r[s-1]?c.charAt(1):r[s-1]+c.charAt(1):i}f=r[l-1]}return void 0===f?"":f}))},Re=function(t,e){var n=t.exec;if("function"==typeof n){var r=n.call(t,e);if("object"!=typeof r)throw TypeError("RegExp exec method returned something other than an Object or null");return r}if("RegExp"!==S(t))throw TypeError("RegExp#exec called on incompatible receiver");return te.call(t,e)},_e=Math.max,Ce=Math.min;!function(t,e,n,r){var o=ve(t),i=!b((function(){var e={};return e[o]=function(){return 7},7!=""[t](e)})),a=i&&!b((function(){var e=!1,n=/a/;return"split"===t&&((n={}).constructor={},n.constructor[he]=function(){return n},n.flags="",n[o]=/./[o]),n.exec=function(){return e=!0,null},n[o](""),!e}));if(!i||!a||"replace"===t&&(!ye||!be||me)||"split"===t&&!xe){var u=/./[o],c=n(o,""[t],(function(t,e,n,r,o){return e.exec===te?i&&!o?{done:!0,value:u.call(e,n,r)}:{done:!0,value:t.call(n,e,r)}:{done:!1}}),{REPLACE_KEEPS_$0:be,REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE:me}),f=c[0],l=c[1];pt(String.prototype,t,f),pt(RegExp.prototype,o,2==e?function(t,e){return l.call(t,this,e)}:function(t){return l.call(t,this)})}r&&q(RegExp.prototype[o],"sham",!0)}("replace",2,(function(t,e,n,r){var o=r.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE,i=r.REPLACE_KEEPS_$0,a=o?"$":"$0";return[function(n,r){var o=A(this),i=null==n?void 0:n[t];return void 0!==i?i.call(n,o,r):e.call(String(o),n,r)},function(t,r){if(!o&&i||"string"==typeof r&&-1===r.indexOf(a)){var u=n(e,t,this,r);if(u.done)return u.value}var c=N(t),f=String(this),l="function"==typeof r;l||(r=String(r));var s=c.global;if(s){var d=c.unicode;c.lastIndex=0}for(var p=[];;){var v=Re(c,f);if(null===v)break;if(p.push(v),!s)break;""===String(v[0])&&(c.lastIndex=Oe(f,Et(c.lastIndex),d))}for(var h,y="",b=0,g=0;g=b&&(y+=f.slice(b,x)+j,b=x+m.length)}return y+f.slice(b)}]}));var ke=function(t,e){var n=[][t];return!!n&&b((function(){n.call(null,e||function(){throw 1},1)}))},De=jt.indexOf,Fe=[].indexOf,Me=!!Fe&&1/[1].indexOf(1,-0)<0,$e=ke("indexOf");qt({target:"Array",proto:!0,forced:Me||!$e},{indexOf:function(t){return Me?Fe.apply(this,arguments)||0:De(this,t,arguments.length>1?arguments[1]:void 0)}});var Ue,Ne=Array.isArray||function(t){return"Array"==S(t)},Be=function(t,e,n){var r=R(e);r in t?L.f(t,r,w(0,n)):t[r]=n},Le=ve("species"),qe=function(t,e){var n;return Ne(t)&&("function"!=typeof(n=t.constructor)||n!==Array&&!Ne(n.prototype)?P(n)&&null===(n=n[Le])&&(n=void 0):n=void 0),new(void 0===n?Array:n)(0===e?0:e)},Ve=ve("species"),Ke=ve("isConcatSpreadable"),Ge=9007199254740991,He="Maximum allowed index exceeded",We=ce>=51||!b((function(){var t=[];return t[Ke]=!1,t.concat()[0]!==t})),ze=(Ue="concat",ce>=51||!b((function(){var t=[];return(t.constructor={})[Ve]=function(){return{foo:1}},1!==t[Ue](Boolean).foo}))),Xe=function(t){if(!P(t))return!1;var e=t[Ke];return void 0!==e?!!e:Ne(t)};qt({target:"Array",proto:!0,forced:!We||!ze},{concat:function(t){var e,n,r,o,i,a=Se(this),u=qe(a,0),c=0;for(e=-1,r=arguments.length;eGe)throw TypeError(He);for(n=0;n=Ge)throw TypeError(He);Be(u,c++,i)}return u.length=c,u}});var Ye=[].join,Qe=T!=Object,Ze=ke("join",",");qt({target:"Array",proto:!0,forced:Qe||!Ze},{join:function(t){return Ye.call(I(this),void 0===t?",":t)}});var Je,tn=function(t,e,n){if(function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function")}(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}},en=[].push,nn=function(t){var e=1==t,n=2==t,r=3==t,o=4==t,i=6==t,a=7==t,u=5==t||i;return function(c,f,l,s){for(var d,p,v=Se(c),h=T(v),y=tn(f,l,3),b=Et(h.length),g=0,m=s||qe,x=e?m(c,b):n||a?m(c,0):void 0;b>g;g++)if((u||g in h)&&(p=y(d=h[g],g,v),t))if(e)x[g]=p;else if(p)switch(t){case 3:return!0;case 5:return d;case 6:return g;case 2:en.call(x,d)}else switch(t){case 4:return!1;case 7:en.call(x,d)}return i?-1:r||o?o:x}},rn={forEach:nn(0),map:nn(1),filter:nn(2),some:nn(3),every:nn(4),find:nn(5),findIndex:nn(6),filterOut:nn(7)},on=Object.keys||function(t){return At(t,It)},an=g?Object.defineProperties:function(t,e){N(t);for(var n,r=on(e),o=r.length,i=0;o>i;)L.f(t,n=r[i++],e[n]);return t},un=yt("document","documentElement"),cn=ot("IE_PROTO"),fn=function(){},ln=function(t){return"
-
+
+
{% endblock %}
diff --git a/cps/ub.py b/cps/ub.py
index 8750b405..63265f7f 100644
--- a/cps/ub.py
+++ b/cps/ub.py
@@ -73,9 +73,9 @@ def store_user_session():
user_session = User_Sessions(flask_session.get('_user_id', ""), flask_session.get('_id', ""))
session.add(user_session)
session.commit()
- log.info("Login and store session : " + flask_session.get('_id', ""))
+ log.debug("Login and store session : " + flask_session.get('_id', ""))
else:
- log.info("Found stored session : " + flask_session.get('_id', ""))
+ log.debug("Found stored session: " + flask_session.get('_id', ""))
except (exc.OperationalError, exc.InvalidRequestError) as e:
session.rollback()
log.exception(e)
@@ -84,7 +84,7 @@ def store_user_session():
def delete_user_session(user_id, session_key):
try:
- log.info("Deleted session_key : " + session_key)
+ log.debug("Deleted session_key: " + session_key)
session.query(User_Sessions).filter(User_Sessions.user_id==user_id,
User_Sessions.session_key==session_key).delete()
session.commit()
diff --git a/cps/web.py b/cps/web.py
index f7291e0a..14857965 100644
--- a/cps/web.py
+++ b/cps/web.py
@@ -84,7 +84,7 @@ except ImportError:
@app.after_request
def add_security_headers(resp):
- # resp.headers['Content-Security-Policy']= "script-src 'self' https://www.googleapis.com https://api.douban.com https://comicvine.gamespot.com;"
+ # resp.headers['Content-Security-Policy']= "script-src 'self'" https://www.googleapis.com https://api.douban.com https://comicvine.gamespot.com;"
resp.headers['X-Content-Type-Options'] = 'nosniff'
resp.headers['X-Frame-Options'] = 'SAMEORIGIN'
resp.headers['X-XSS-Protection'] = '1; mode=block'
diff --git a/optional-requirements.txt b/optional-requirements.txt
index 1fa84d50..af068a51 100644
--- a/optional-requirements.txt
+++ b/optional-requirements.txt
@@ -30,7 +30,6 @@ Flask-Dance>=2.0.0,<5.1.0
SQLAlchemy-Utils>=0.33.5,<0.38.0
# extracting metadata
-lxml>=3.8.0,<4.7.0
rarfile>=2.7
scholarly>=1.2.0, <1.3
diff --git a/requirements.txt b/requirements.txt
index b29d3b99..e7f67593 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,3 +12,4 @@ SQLAlchemy>=1.3.0,<1.5.0
tornado>=4.1,<6.2
Wand>=0.4.4,<0.7.0
unidecode>=0.04.19,<1.3.0
+lxml>=3.8.0,<4.7.0