Cross-site Scripting (XSS) - Reflected in janeczku/calibre-web
Description
Cross-site Scripting (XSS) - Reflected in Pypi calibreweb prior to 0.6.16.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Reflected XSS in calibreweb prior to 0.6.16 allows arbitrary script execution via crafted input.
Vulnerability
A reflected cross-site scripting (XSS) vulnerability exists in calibreweb prior to version 0.6.16 [2]. The flaw occurs in template rendering where user-controlled data (e.g., author name, book title) is output without proper sanitization via the |safe filter [4]. Affected versions are all releases before 0.6.16.
Exploitation
An attacker can craft a malicious URL containing an XSS payload. No authentication is required, but the victim must click the crafted link. The payload is reflected in the server response and executed in the victim's browser [2].
Impact
Successful exploitation allows arbitrary JavaScript execution in the victim's browser, potentially leading to session hijacking, content defacement, or redirection to malicious sites. The attack impacts the user's session context [2].
Mitigation
The vulnerability is fixed in calibreweb version 0.6.16, released on 2022-01-28 [1][3][4]. Users should upgrade immediately. No known workarounds exist for unpatched versions.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
calibrewebPyPI | < 0.6.16 | 0.6.16 |
Affected products
2- janeczku/janeczku/calibre-webv5Range: unspecified
Patches
16bf075397880Prevent wrong use of safe statement
2 files changed · +95 −109
cps/templates/author.html+4 −4 modified@@ -5,11 +5,11 @@ <h2>{{title}}</h2> {% if author is not none %} <section class="author-bio"> {%if author.image_url is not none %} - <img title="{{author.name|safe}}" src="{{author.image_url}}" alt="{{author.name|safe}}" class="author-photo pull-left"> + <img title="{{author.name}}" src="{{author.image_url}}" alt="{{author.name}}" class="author-photo pull-left"> {% endif %} {%if author.about is not none %} - <p>{{author.about|safe}}</p> + <p>{{author.about}}</p> {% endif %} - {{_("via")}} <a href="{{author.link}}" class="author-link" target="_blank" rel="noopener">Goodreads</a> @@ -36,7 +36,7 @@ <h3>{{_("In Library")}}</h3> <div id="books" class="col-sm-3 col-lg-2 col-xs-6 book"> <div class="cover"> <a href="{{ url_for('web.show_book', book_id=entry.id) }}"> - <span class="img" title="{{entry.title|safe}}"> + <span class="img" title="{{entry.title}}"> <img src="{{ url_for('web.get_cover', book_id=entry.id) }}" /> {% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %} </span> @@ -98,7 +98,7 @@ <h3>{{_("In Library")}}</h3> {% if other_books and author is not none %} <div class="discover"> - <h3>{{_("More by")}} {{ author.name.replace('|',',')|safe }}</h3> + <h3>{{_("More by")}} {{ author.name.replace('|',',') }}</h3> <div class="row"> {% for entry in other_books %} <div class="col-sm-3 col-lg-2 col-xs-6 book">
test/Calibre-Web TestSummary_Linux.html+91 −105 modified@@ -37,20 +37,20 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <div class="row"> <div class="col-xs-6 col-md-6 col-sm-offset-3" style="margin-top:50px;"> - <p class='text-justify attribute'><strong>Start Time: </strong>2022-01-16 21:58:58</p> + <p class='text-justify attribute'><strong>Start Time: </strong>2022-01-17 18:51:33</p> </div> </div> <div class="row"> <div class="col-xs-6 col-md-6 col-sm-offset-3"> - <p class='text-justify attribute'><strong>Stop Time: </strong>2022-01-17 01:49:28</p> + <p class='text-justify attribute'><strong>Stop Time: </strong>2022-01-18 07:49:35</p> </div> </div> <div class="row"> <div class="col-xs-6 col-md-6 col-sm-offset-3"> - <p class='text-justify attribute'><strong>Duration: </strong>3h 10 min</p> + <p class='text-justify attribute'><strong>Duration: </strong>12h 17 min</p> </div> </div> </div> @@ -411,11 +411,11 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> - <tr id="su" class="failClass"> + <tr id="su" class="passClass"> <td>TestEbookConvertCalibre</td> <td class="text-center">15</td> - <td class="text-center">14</td> - <td class="text-center">1</td> + <td class="text-center">15</td> + <td class="text-center">0</td> <td class="text-center">0</td> <td class="text-center">0</td> <td class="text-center"> @@ -479,33 +479,11 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> - <tr id="ft6.7" class="none bg-danger"> + <tr id='pt6.7' class='hiddenRow bg-success'> <td> <div class='testcase'>TestEbookConvertCalibre - test_convert_parameter</div> </td> - <td colspan='6'> - <div class="text-center"> - <a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft6.7')">FAIL</a> - </div> - <!--css div popup start--> - <div id="div_ft6.7" class="popup_window test_output" style="display:block;"> - <div class='close_button pull-right'> - <button type="button" class="close" aria-label="Close" onfocus="this.blur();" - onclick="document.getElementById('div_ft6.7').style.display='none'"><span - aria-hidden="true">×</span></button> - </div> - <div class="text-left pull-left"> - <pre class="text-left">Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert.py", line 164, in test_convert_parameter - self.assertEqual(ret[-1]['result'], 'Finished') -AssertionError: 'Failed' != 'Finished' -- Failed -+ Finished</pre> - </div> - <div class="clearfix"></div> - </div> - <!--css div popup end--> - </td> + <td colspan='6' align='center'>PASS</td> </tr> @@ -738,13 +716,13 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <tr id="su" class="skipClass"> <td>TestEditAdditionalBooks</td> - <td class="text-center">16</td> - <td class="text-center">14</td> + <td class="text-center">17</td> + <td class="text-center">15</td> <td class="text-center">0</td> <td class="text-center">0</td> <td class="text-center">2</td> <td class="text-center"> - <a onclick="showClassDetail('c10', 16)">Detail</a> + <a onclick="showClassDetail('c10', 17)">Detail</a> </td> </tr> @@ -779,7 +757,7 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <tr id='pt10.4' class='hiddenRow bg-success'> <td> - <div class='testcase'>TestEditAdditionalBooks - test_edit_book_identifier</div> + <div class='testcase'>TestEditAdditionalBooks - test_details_popup</div> </td> <td colspan='6' align='center'>PASS</td> </tr> @@ -788,7 +766,7 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <tr id='pt10.5' class='hiddenRow bg-success'> <td> - <div class='testcase'>TestEditAdditionalBooks - test_edit_book_identifier_capital</div> + <div class='testcase'>TestEditAdditionalBooks - test_edit_book_identifier</div> </td> <td colspan='6' align='center'>PASS</td> </tr> @@ -797,7 +775,7 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <tr id='pt10.6' class='hiddenRow bg-success'> <td> - <div class='testcase'>TestEditAdditionalBooks - test_edit_book_identifier_standard</div> + <div class='testcase'>TestEditAdditionalBooks - test_edit_book_identifier_capital</div> </td> <td colspan='6' align='center'>PASS</td> </tr> @@ -806,7 +784,7 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <tr id='pt10.7' class='hiddenRow bg-success'> <td> - <div class='testcase'>TestEditAdditionalBooks - test_edit_special_book_identifier</div> + <div class='testcase'>TestEditAdditionalBooks - test_edit_book_identifier_standard</div> </td> <td colspan='6' align='center'>PASS</td> </tr> @@ -815,7 +793,7 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <tr id='pt10.8' class='hiddenRow bg-success'> <td> - <div class='testcase'>TestEditAdditionalBooks - test_title_sort</div> + <div class='testcase'>TestEditAdditionalBooks - test_edit_special_book_identifier</div> </td> <td colspan='6' align='center'>PASS</td> </tr> @@ -824,7 +802,7 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <tr id='pt10.9' class='hiddenRow bg-success'> <td> - <div class='testcase'>TestEditAdditionalBooks - test_upload_edit_role</div> + <div class='testcase'>TestEditAdditionalBooks - test_title_sort</div> </td> <td colspan='6' align='center'>PASS</td> </tr> @@ -833,14 +811,23 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <tr id='pt10.10' class='hiddenRow bg-success'> <td> - <div class='testcase'>TestEditAdditionalBooks - test_upload_metadata_cbr</div> + <div class='testcase'>TestEditAdditionalBooks - test_upload_edit_role</div> </td> <td colspan='6' align='center'>PASS</td> </tr> <tr id='pt10.11' class='hiddenRow bg-success'> + <td> + <div class='testcase'>TestEditAdditionalBooks - test_upload_metadata_cbr</div> + </td> + <td colspan='6' align='center'>PASS</td> + </tr> + + + + <tr id='pt10.12' class='hiddenRow bg-success'> <td> <div class='testcase'>TestEditAdditionalBooks - test_upload_metadata_cbt</div> </td> @@ -849,19 +836,19 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> - <tr id="st10.12" class="none bg-warning"> + <tr id="st10.13" class="none bg-warning"> <td> <div class='testcase'>TestEditAdditionalBooks - test_writeonly_calibre_database</div> </td> <td colspan='6'> <div class="text-center"> - <a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_st10.12')">SKIP</a> + <a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_st10.13')">SKIP</a> </div> <!--css div popup start--> - <div id="div_st10.12" class="popup_window test_output" style="display:none;"> + <div id="div_st10.13" class="popup_window test_output" style="display:none;"> <div class='close_button pull-right'> <button type="button" class="close" aria-label="Close" onfocus="this.blur();" - onclick="document.getElementById('div_st10.12').style.display='none'"><span + onclick="document.getElementById('div_st10.13').style.display='none'"><span aria-hidden="true">×</span></button> </div> <div class="text-left pull-left"> @@ -875,7 +862,7 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> - <tr id='pt10.13' class='hiddenRow bg-success'> + <tr id='pt10.14' class='hiddenRow bg-success'> <td> <div class='testcase'>TestEditAdditionalBooks - test_writeonly_path</div> </td> @@ -884,7 +871,7 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> - <tr id='st10.14' class='none bg-warning'> + <tr id='st10.15' class='none bg-warning'> <td> <div class='testcase'>TestEditAdditionalBooks - test_xss_author_edit</div> </td> @@ -893,7 +880,7 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> - <tr id='pt10.15' class='hiddenRow bg-success'> + <tr id='pt10.16' class='hiddenRow bg-success'> <td> <div class='testcase'>TestEditAdditionalBooks - test_xss_comment_edit</div> </td> @@ -902,7 +889,7 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> - <tr id='pt10.16' class='hiddenRow bg-success'> + <tr id='pt10.17' class='hiddenRow bg-success'> <td> <div class='testcase'>TestEditAdditionalBooks - test_xss_custom_comment_edit</div> </td> @@ -1436,56 +1423,36 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> - <tr id="su" class="failClass"> + <tr id="su" class="passClass"> <td>TestLoadMetadata</td> <td class="text-center">1</td> - <td class="text-center">0</td> <td class="text-center">1</td> <td class="text-center">0</td> <td class="text-center">0</td> + <td class="text-center">0</td> <td class="text-center"> <a onclick="showClassDetail('c13', 1)">Detail</a> </td> </tr> - <tr id="ft13.1" class="none bg-danger"> + <tr id='pt13.1' class='hiddenRow bg-success'> <td> <div class='testcase'>TestLoadMetadata - test_load_metadata</div> </td> - <td colspan='6'> - <div class="text-center"> - <a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft13.1')">FAIL</a> - </div> - <!--css div popup start--> - <div id="div_ft13.1" class="popup_window test_output" style="display:block;"> - <div class='close_button pull-right'> - <button type="button" class="close" aria-label="Close" onfocus="this.blur();" - onclick="document.getElementById('div_ft13.1').style.display='none'"><span - aria-hidden="true">×</span></button> - </div> - <div class="text-left pull-left"> - <pre class="text-left">Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_edit_books_metadata.py", line 136, in test_load_metadata - self.assertGreaterEqual(diff(BytesIO(cover), BytesIO(original_cover), delete_diff_file=True), 0.05) -AssertionError: 0.0 not greater than or equal to 0.05</pre> - </div> - <div class="clearfix"></div> - </div> - <!--css div popup end--> - </td> + <td colspan='6' align='center'>PASS</td> </tr> - <tr id="su" class="failClass"> + <tr id="su" class="errorClass"> <td>TestEditBooksOnGdrive</td> <td class="text-center">20</td> <td class="text-center">19</td> - <td class="text-center">1</td> <td class="text-center">0</td> + <td class="text-center">1</td> <td class="text-center">0</td> <td class="text-center"> <a onclick="showClassDetail('c14', 20)">Detail</a> @@ -1629,11 +1596,41 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> - <tr id='pt14.16' class='hiddenRow bg-success'> + <tr id="et14.16" class="none bg-info"> <td> <div class='testcase'>TestEditBooksOnGdrive - test_edit_title</div> </td> - <td colspan='6' align='center'>PASS</td> + <td colspan='6'> + <div class="text-center"> + <a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_et14.16')">ERROR</a> + </div> + <!--css div popup start--> + <div id="div_et14.16" class="popup_window test_output" style="display:block;"> + <div class='close_button pull-right'> + <button type="button" class="close" aria-label="Close" onfocus="this.blur();" + onclick="document.getElementById('div_et14.16').style.display='none'"><span + aria-hidden="true">×</span></button> + </div> + <div class="text-left pull-left"> + <pre class="text-left">Traceback (most recent call last): + File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 250, in test_edit_title + self.fill_basic_config({"config_unicode_filename": 0}) + File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 358, in fill_basic_config + cls._fill_basic_config(elements) + File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 268, in _fill_basic_config + WebDriverWait(cls.driver, 5).until(EC.presence_of_element_located((By.ID, "config_port"))) + File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/support/wait.py", line 89, in until + raise TimeoutException(message, screen, stacktrace) +selenium.common.exceptions.TimeoutException: Message: +Stacktrace: +WebDriverError@chrome://remote/content/shared/webdriver/Errors.jsm:183:5 +NoSuchElementError@chrome://remote/content/shared/webdriver/Errors.jsm:395:5 +element.find/</<@chrome://remote/content/marionette/element.js:300:16</pre> + </div> + <div class="clearfix"></div> + </div> + <!--css div popup end--> + </td> </tr> @@ -1665,31 +1662,11 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> - <tr id="ft14.20" class="none bg-danger"> + <tr id='pt14.20' class='hiddenRow bg-success'> <td> <div class='testcase'>TestEditBooksOnGdrive - test_watch_metadata</div> </td> - <td colspan='6'> - <div class="text-center"> - <a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft14.20')">FAIL</a> - </div> - <!--css div popup start--> - <div id="div_ft14.20" class="popup_window test_output" style="display:block;"> - <div class='close_button pull-right'> - <button type="button" class="close" aria-label="Close" onfocus="this.blur();" - onclick="document.getElementById('div_ft14.20').style.display='none'"><span - aria-hidden="true">×</span></button> - </div> - <div class="text-left pull-left"> - <pre class="text-left">Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py", line 940, in test_watch_metadata - self.assertNotIn('series', book) -AssertionError: 'series' unexpectedly found in {'id': 5, 'reader': [], 'title': 'testbook', 'author': ['John Döe'], 'rating': 0, 'languages': ['English'], 'identifier': [], 'cover': '/cover/5?edit=4e213e83-fe57-4648-9e75-8e2176fe7773', 'tag': [], 'publisher': ['Randomhäus'], 'pubdate': 'Jan 19, 2017', 'comment': 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.Aenean commodo ligula eget dolor.Aenean massa.Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.Nulla consequat massa quis enim.Donec pede justo, fringilla vel, aliquet nec, vulputate', 'add_shelf': [], 'del_shelf': [], 'edit_enable': True, 'kindle': None, 'kindlebtn': None, 'download': ['EPUB (6.7 kB)'], 'read': False, 'archived': False, 'series_all': 'Book 1 of test', 'series_index': '1', 'series': 'test', 'cust_columns': []}</pre> - </div> - <div class="clearfix"></div> - </div> - <!--css div popup end--> - </td> + <td colspan='6' align='center'>PASS</td> </tr> @@ -1865,13 +1842,13 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <tr id="su" class="passClass"> <td>TestErrorReadColumn</td> - <td class="text-center">1</td> - <td class="text-center">1</td> + <td class="text-center">2</td> + <td class="text-center">2</td> <td class="text-center">0</td> <td class="text-center">0</td> <td class="text-center">0</td> <td class="text-center"> - <a onclick="showClassDetail('c19', 1)">Detail</a> + <a onclick="showClassDetail('c19', 2)">Detail</a> </td> </tr> @@ -1885,6 +1862,15 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> </tr> + + <tr id='pt19.2' class='hiddenRow bg-success'> + <td> + <div class='testcase'>TestErrorReadColumn - test_invalid_custom_read_column</div> + </td> + <td colspan='6' align='center'>PASS</td> + </tr> + + <tr id="su" class="skipClass"> @@ -4376,10 +4362,10 @@ <h1 id='report_title' class="text-center">Calibre-Web Tests</h1> <tr id='total_row' class="text-center bg-grey"> <td>Total</td> - <td>383</td> - <td>372</td> - <td>3</td> + <td>385</td> + <td>376</td> <td>0</td> + <td>1</td> <td>8</td> <td> </td> </tr> @@ -4768,7 +4754,7 @@ <h4 class="panel-title"> </div> <script> - drawCircle(372, 3, 0, 8); + drawCircle(376, 0, 1, 8); showCase(5); </script>
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-h56g-v4vp-q9q6ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-0352ghsaADVISORY
- github.com/janeczku/calibre-web/commit/6bf07539788004513c3692c074ebc7ba4ce005e1ghsax_refsource_MISCWEB
- github.com/pypa/advisory-database/tree/main/vulns/calibreweb/PYSEC-2022-18.yamlghsaWEB
- huntr.dev/bounties/a577ff17-2ded-4c41-84ae-6ac02440f717ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.