Your message dated Sat, 16 May 2026 11:07:42 +0000
with message-id <[email protected]>
and subject line Released with 12.14
has caused the Debian Bug report #1127429,
regarding bookworm-pu: package calibre/6.13.0+repack-2+deb12u6
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1127429: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1127429
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:calibre
User: [email protected]
Usertags: pu

[ Reason ]
Fix these CVEs.
* CVE-2026-25635
* CVE-2026-25636
* CVE-2026-25731

[ Impact ]
* CVE-2026-25635: path traversal vulnerability
* CVE-2026-25636: path traversal vulnerability
* CVE-2026-25731: Server-Side Template Injection (SSTI) vulnerability

[ Tests ]
Automated build-time test was successful.

[ Risks ]
Not well tested on real machine.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
Backport and apply upstream fixes.

[ Other info ]
Fixes are examine from online:
  https://github.com/debian-
calibre/calibre/compare/debian/6.13.0+repack-2+deb12u5...bookworm-update
diff -Nru calibre-6.13.0+repack/debian/calibre.install 
calibre-6.13.0+repack/debian/calibre.install
--- calibre-6.13.0+repack/debian/calibre.install        2025-09-22 
00:51:57.000000000 +0900
+++ calibre-6.13.0+repack/debian/calibre.install        2026-02-09 
00:14:55.000000000 +0900
@@ -7,7 +7,6 @@
 #
 usr/lib/calibre/odf
 usr/lib/calibre/qt
-usr/lib/calibre/templite
 usr/lib/calibre/tinycss
 usr/lib/calibre/css_selectors
 usr/lib/calibre/polyglot
diff -Nru calibre-6.13.0+repack/debian/changelog 
calibre-6.13.0+repack/debian/changelog
--- calibre-6.13.0+repack/debian/changelog      2025-11-09 18:15:24.000000000 
+0900
+++ calibre-6.13.0+repack/debian/changelog      2026-02-09 00:25:09.000000000 
+0900
@@ -1,3 +1,16 @@
+calibre (6.13.0+repack-2+deb12u6) bookworm; urgency=medium
+
+  * CVE-2026-25635: CHM Input: Ignore internal files that have paths that
+    end up outside the container
+  * CVE-2026-25636: DRYer
+  * CVE-2026-25731: ZIP Output: Change the template engine used for HTML
+    templating from templite to Mustache, for greater safety and
+    performance. Note that this is a breaking change if you use custom
+    templates with ZIP output.
+  * Use pystache instead of templite to fix CVE-2026-25731
+
+ -- YOKOTA Hiroshi <[email protected]>  Mon, 09 Feb 2026 00:25:09 +0900
+
 calibre (6.13.0+repack-2+deb12u5) bookworm; urgency=medium
 
   * Fix CVE-2025-64486
diff -Nru calibre-6.13.0+repack/debian/control 
calibre-6.13.0+repack/debian/control
--- calibre-6.13.0+repack/debian/control        2025-11-09 18:15:24.000000000 
+0900
+++ calibre-6.13.0+repack/debian/control        2026-02-09 00:14:55.000000000 
+0900
@@ -63,6 +63,7 @@
  python3-pyqt6.qtsvg,
  python3-pyqt6.qtwebengine,
  python3-pyqtbuild,
+ python3-pystache,
  python3-regex,
  python3-routes,
  python3-setuptools,
diff -Nru 
calibre-6.13.0+repack/debian/patches/0037-CVE-2026-25635-CHM-Input-Ignore-internal-files-that-.patch
 
calibre-6.13.0+repack/debian/patches/0037-CVE-2026-25635-CHM-Input-Ignore-internal-files-that-.patch
--- 
calibre-6.13.0+repack/debian/patches/0037-CVE-2026-25635-CHM-Input-Ignore-internal-files-that-.patch
        1970-01-01 09:00:00.000000000 +0900
+++ 
calibre-6.13.0+repack/debian/patches/0037-CVE-2026-25635-CHM-Input-Ignore-internal-files-that-.patch
        2026-02-09 00:15:48.000000000 +0900
@@ -0,0 +1,81 @@
+From: Kovid Goyal <[email protected]>
+Date: Wed, 4 Feb 2026 09:39:54 +0530
+Subject: CVE-2026-25635: CHM Input: Ignore internal files that have paths
+ that end up outside the container
+
+Forwarded: not-needed
+Bug: 
https://github.com/kovidgoyal/calibre/security/advisories/GHSA-32vh-whvh-9fxr
+Origin: backport, 
https://github.com/kovidgoyal/calibre/commit/9739232fcb029ac15dfe52ccd4fdb4a07ebb6ce9
+
+Also, allow extraction of long filenames
+
+Signed-off-by: YOKOTA Hiroshi <[email protected]>
+---
+ src/calibre/ebooks/chm/reader.py | 33 ++++++++++++++++-----------------
+ 1 file changed, 16 insertions(+), 17 deletions(-)
+
+diff --git a/src/calibre/ebooks/chm/reader.py 
b/src/calibre/ebooks/chm/reader.py
+index 102f439..de5deaf 100644
+--- a/src/calibre/ebooks/chm/reader.py
++++ b/src/calibre/ebooks/chm/reader.py
+@@ -12,6 +12,7 @@ from calibre.constants import filesystem_encoding, iswindows
+ from calibre.ebooks.BeautifulSoup import BeautifulSoup, NavigableString
+ from calibre.ebooks.chardet import xml_to_unicode
+ from calibre.ebooks.metadata.toc import TOC
++from calibre.utils.filenames import make_long_path_useable
+ from chm.chm import CHMFile, chmlib
+ from polyglot.builtins import as_unicode
+ 
+@@ -159,37 +160,35 @@ class CHMReader(CHMFile):
+ 
+     def ExtractFiles(self, output_dir=os.getcwd(), debug_dump=False):
+         html_files = set()
++        base = output_dir = os.path.abspath(output_dir)
++        if not base.endswith(os.sep):
++            base += os.sep
+         for path in self.Contents():
+-            fpath = path
+-            lpath = os.path.join(output_dir, fpath)
++            fpath = path.partition(';')[0]  # fix file names with ";<junk>" 
at the end, see _reformat()
++            fpath = fpath.replace('/', os.sep)
++            lpath = os.path.abspath(os.path.join(output_dir, fpath))
++            if os.path.commonprefix((lpath, base)) != base:
++                self.log.warn(f'{path!r} outside container, skipping')
++                continue
+             self._ensure_dir(lpath)
+             try:
+                 data = self.GetFile(path)
+             except:
+                 self.log.exception('Failed to extract %s from CHM, 
ignoring'%path)
+                 continue
+-            if lpath.find(';') != -1:
+-                # fix file names with ";<junk>" at the end, see _reformat()
+-                lpath = lpath.split(';')[0]
++            with open(make_long_path_useable(lpath), 'wb') as f:
++                f.write(data)
+             try:
+-                with open(lpath, 'wb') as f:
+-                    f.write(data)
+-                try:
+-                    if 'html' in guess_mimetype(path)[0]:
+-                        html_files.add(lpath)
+-                except:
+-                    pass
++                if 'html' in guess_mimetype(os.path.basename(lpath))[0]:
++                    html_files.add(lpath)
+             except:
+-                if iswindows and len(lpath) > 250:
+-                    self.log.warn('%r filename too long, skipping'%path)
+-                    continue
+-                raise
++                pass
+ 
+         if debug_dump:
+             import shutil
+             shutil.copytree(output_dir, os.path.join(debug_dump, 
'debug_dump'))
+         for lpath in html_files:
+-            with open(lpath, 'r+b') as f:
++            with open(make_long_path_useable(lpath), 'r+b') as f:
+                 data = f.read()
+                 data = self._reformat(data, lpath)
+                 if isinstance(data, str):
diff -Nru calibre-6.13.0+repack/debian/patches/0038-CVE-2026-25636-DRYer.patch 
calibre-6.13.0+repack/debian/patches/0038-CVE-2026-25636-DRYer.patch
--- calibre-6.13.0+repack/debian/patches/0038-CVE-2026-25636-DRYer.patch        
1970-01-01 09:00:00.000000000 +0900
+++ calibre-6.13.0+repack/debian/patches/0038-CVE-2026-25636-DRYer.patch        
2026-02-09 00:15:48.000000000 +0900
@@ -0,0 +1,37 @@
+From: Kovid Goyal <[email protected]>
+Date: Mon, 2 Feb 2026 11:25:09 +0530
+Subject: CVE-2026-25636: DRYer
+
+Forwarded: not-needed
+Bug: 
https://github.com/kovidgoyal/calibre/security/advisories/GHSA-8r26-m7j5-hm29
+Origin: backport, 
https://github.com/kovidgoyal/calibre/commit/9484ea82c6ab226c18e6ca5aa000fa16de598726
+
+Signed-off-by: YOKOTA Hiroshi <[email protected]>
+---
+ src/calibre/ebooks/conversion/plugins/epub_input.py | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/calibre/ebooks/conversion/plugins/epub_input.py 
b/src/calibre/ebooks/conversion/plugins/epub_input.py
+index 9915c2b..87c0e7d 100644
+--- a/src/calibre/ebooks/conversion/plugins/epub_input.py
++++ b/src/calibre/ebooks/conversion/plugins/epub_input.py
+@@ -62,15 +62,17 @@ class EPUBInput(InputFormatPlugin):
+ 
+         try:
+             root = etree.parse(encfile)
++            base = os.path.dirname(encfile)
++            container_base = os.path.dirname(base)
+             for em in root.xpath('descendant::*[contains(name(), 
"EncryptionMethod")]'):
+                 algorithm = em.get('Algorithm', '')
+                 if algorithm not in {ADOBE_OBFUSCATION, IDPF_OBFUSCATION}:
+                     return False
+                 cr = em.getparent().xpath('descendant::*[contains(name(), 
"CipherReference")]')[0]
+                 uri = cr.get('URI')
+-                path = os.path.abspath(os.path.join(os.path.dirname(encfile), 
'..', *uri.split('/')))
++                path = os.path.abspath(os.path.join(base, '..', 
*uri.split('/')))
+                 tkey = (key if algorithm == ADOBE_OBFUSCATION else idpf_key)
+-                if (tkey and os.path.exists(path)):
++                if (tkey and is_existing_subpath(path, container_base)):
+                     self._encrypted_font_uris.append(uri)
+                     decrypt_font(tkey, path, algorithm)
+             return True
diff -Nru 
calibre-6.13.0+repack/debian/patches/0039-CVE-2026-25731-ZIP-Output-Change-the-template-engine.patch
 
calibre-6.13.0+repack/debian/patches/0039-CVE-2026-25731-ZIP-Output-Change-the-template-engine.patch
--- 
calibre-6.13.0+repack/debian/patches/0039-CVE-2026-25731-ZIP-Output-Change-the-template-engine.patch
        1970-01-01 09:00:00.000000000 +0900
+++ 
calibre-6.13.0+repack/debian/patches/0039-CVE-2026-25731-ZIP-Output-Change-the-template-engine.patch
        2026-02-09 00:15:48.000000000 +0900
@@ -0,0 +1,541 @@
+From: Kovid Goyal <[email protected]>
+Date: Thu, 5 Feb 2026 14:21:25 +0530
+Subject: CVE-2026-25731: ZIP Output: Change the template engine used for HTML
+ templating from templite to Mustache,
+ for greater safety and performance. Note that this is a breaking change if
+ you use custom templates with ZIP output.
+
+Forwarded: not-needed
+Bug: 
https://github.com/kovidgoyal/calibre/security/advisories/GHSA-xrh9-w7qx-3gcc
+Origin: backport, 
https://github.com/kovidgoyal/calibre/commit/f0649b27512e987b95fcab2e1e0a3bcdafc23379
+
+Signed-off-by: YOKOTA Hiroshi <[email protected]>
+---
+ COPYRIGHT                                          |  6 --
+ resources/templates/html_export_default.mustache   | 70 +++++++++++++++++
+ resources/templates/html_export_default.tmpl       | 74 ------------------
+ .../templates/html_export_default_index.mustache   | 55 +++++++++++++
+ resources/templates/html_export_default_index.tmpl | 61 ---------------
+ .../ebooks/conversion/plugins/html_output.py       | 60 +++++++++------
+ src/templite/__init__.py                           | 89 ----------------------
+ 7 files changed, 163 insertions(+), 252 deletions(-)
+ create mode 100644 resources/templates/html_export_default.mustache
+ delete mode 100644 resources/templates/html_export_default.tmpl
+ create mode 100644 resources/templates/html_export_default_index.mustache
+ delete mode 100644 resources/templates/html_export_default_index.tmpl
+ delete mode 100644 src/templite/__init__.py
+
+diff --git a/COPYRIGHT b/COPYRIGHT
+index 3425d82..702ced2 100644
+--- a/COPYRIGHT
++++ b/COPYRIGHT
+@@ -12,12 +12,6 @@ Files: resources/rapydscript/*
+ Copyright: Various
+ License: BSD
+ 
+-Files: src/templite/*
+-Copyright: Copyright (c) 2009 joonis new media, Thimo Kraemer
+-License: GPL-2+
+- The full text of the GPL is distributed as in
+- /usr/share/common-licenses/GPL-2 on Debian systems.
+-
+ Files: src/calibre/devices/bambook/*
+ Copyright: 2010, Li Fanxi
+ License: GPL-3
+diff --git a/resources/templates/html_export_default.mustache 
b/resources/templates/html_export_default.mustache
+new file mode 100644
+index 0000000..1c8691a
+--- /dev/null
++++ b/resources/templates/html_export_default.mustache
+@@ -0,0 +1,70 @@
++<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
++<html xmlns="http://www.w3.org/1999/xhtml";>
++<head>
++{{{head_content}}}
++
++<link href="{{css_link}}" type="text/css" rel="stylesheet" />
++
++</head>
++<body>
++
++<div class="calibreMeta">
++  <div class="calibreMetaTitle">
++  {{#meta.titles}}
++      {{#is_first}}
++              <h1><a href="{{toc_url}}">{{title}}</a> </h1>
++      {{/is_first}}
++      {{^is_first}}
++              <div class="calibreMetaSubtitle">{{title}}</div>
++      {{/is_first}}
++  {{/meta.titles}}
++  </div>
++  <div class="calibreMetaAuthor">{{meta.creators}}</div>
++</div>
++
++<div class="calibreMain">
++
++  <div class="calibreEbookContent">
++    {{#has_link}}
++      <div class="calibreEbNavTop">
++          {{#prev_link}}
++          <a href="{{prev_link}}" class="calibreAPrev">{{prev_page}}</a>
++              {{/prev_link}}
++        {{^prev_link}}
++          <a href="{{toc_url}}" class="calibreAPrev">{{prev_page}}</a>
++              {{/prev_link}}
++              {{#next_link}}
++          <a href="{{next_link}}" class="calibreANext">{{next_page}}</a>
++              {{/next_link}}
++      </div>
++      {{/has_link}}
++
++      {{{ebook_content}}}
++  </div>
++
++  {{#has_toc}}
++  <div class="calibreToc">
++    <h2><a href="{{toc_url}}">{{table_of_contents}}</a></h2>
++      {{{toc}}}
++  </div>
++  {{/has_toc}}
++
++  <div class="calibreEbNav">
++      {{#prev_link}}
++              <a href="{{prev_link}}" class="calibreAPrev">{{prev_page}}</a>
++      {{/prev_link}}
++      {{^prev_link}}
++              <a href="{{toc_url}}" class="calibreAPrev">{{prev_page}}</a>
++      {{/prev_link}}
++
++    <a href="{{toc_url}}" class="calibreAHome">{{start}}</a>
++
++      {{#next_link}}
++              <a href="{{next_link}}" class="calibreANext">{{next_page}}</a>
++      {{/next_link}}
++  </div>
++
++</div>
++
++</body>
++</html>
+diff --git a/resources/templates/html_export_default.tmpl 
b/resources/templates/html_export_default.tmpl
+deleted file mode 100644
+index 7aac247..0000000
+--- a/resources/templates/html_export_default.tmpl
++++ /dev/null
+@@ -1,74 +0,0 @@
+-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+-<html xmlns="http://www.w3.org/1999/xhtml";>
+-<head>
+-${head_content}$
+-
+-<link href="${cssLink}$" type="text/css" rel="stylesheet" />
+-
+-</head>
+-<body>
+-
+-<div class="calibreMeta">
+-  <div class="calibreMetaTitle">
+-  ${pos1=1}$
+-  ${for title in meta.titles():}$
+-    ${if pos1:}$
+-    <h1>
+-      <a href="${tocUrl}$">${print(title)}$</a>
+-    </h1>
+-    ${:else:}$
+-    <div class="calibreMetaSubtitle">${print(title)}$</div>
+-    ${:endif}$
+-    ${pos1=0}$
+-  ${:endfor}$
+-  </div>
+-  <div class="calibreMetaAuthor">
+-    ${print(', '.join(meta.creators()))}$
+-  </div>
+-</div>
+-
+-<div class="calibreMain">
+-
+-  <div class="calibreEbookContent">
+-    ${if prevLink or nextLink:}$
+-      <div class="calibreEbNavTop">
+-        ${if prevLink:}$
+-          <a href="${prevLink}$" class="calibreAPrev">${print(_('previous 
page'))}$</a>
+-        ${:else:}$
+-          <a href="${tocUrl}$" class="calibreAPrev">${print(_('previous 
page'))}$</a>
+-        ${:endif}$
+-
+-        ${if nextLink:}$
+-          <a href="${nextLink}$" class="calibreANext">${print(_('next 
page'))}$</a>
+-        ${:endif}$
+-      </div>
+-    ${:endif}$
+-
+-    ${ebookContent}$
+-  </div>
+-
+-  ${if has_toc:}$
+-  <div class="calibreToc">
+-    <h2><a href="${tocUrl}$">${print( _('Table of contents'))}$</a></h2>
+-    ${print(toc())}$
+-  </div>
+-  ${:endif}$
+-
+-  <div class="calibreEbNav">
+-    ${if prevLink:}$
+-      <a href="${prevLink}$" class="calibreAPrev">${print(_('previous 
page'))}$</a>
+-    ${:else:}$
+-      <a href="${tocUrl}$" class="calibreAPrev">${print(_('previous 
page'))}$</a>
+-    ${:endif}$
+-
+-    <a href="${tocUrl}$" class="calibreAHome">${print(_('start'))}$</a>
+-
+-    ${if nextLink:}$
+-      <a href="${nextLink}$" class="calibreANext">${print(_('next 
page'))}$</a>
+-    ${:endif}$
+-  </div>
+-
+-</div>
+-
+-</body>
+-</html>
+diff --git a/resources/templates/html_export_default_index.mustache 
b/resources/templates/html_export_default_index.mustache
+new file mode 100644
+index 0000000..aa1bc4d
+--- /dev/null
++++ b/resources/templates/html_export_default_index.mustache
+@@ -0,0 +1,55 @@
++<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
++<html xmlns="http://www.w3.org/1999/xhtml";>
++<head>
++<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
++
++<link rel="schema.DC" href="http://purl.org/dc/elements/1.1/"; />
++<link rel="schema.DCTERMS" href="http://purl.org/dc/terms/"; />
++
++<title>{{meta.creators}} - {{meta.first_title}}</title>
++
++{{#meta.items}}
++  <meta name="DC.{{name}}" content="{{value}}" />
++{{/meta.items}}
++
++<link href="{{css_link}}" type="text/css" rel="stylesheet" />
++</head>
++<body>
++
++<div class="calibreMeta">
++  <div class="calibreMetaTitle">
++  {{#meta.titles}}
++      {{#is_first}}
++              <h1><a href="{{toc_url}}">{{title}}</a> </h1>
++      {{/is_first}}
++      {{^is_first}}
++              <div class="calibreMetaSubtitle">{{title}}</div>
++      {{/is_first}}
++  {{/meta.titles}}
++  </div>
++  <div class="calibreMetaAuthor">{{meta.creators}}</div>
++</div>
++
++<div class="calibreMain">
++  <div class="calibreEbookContent">
++    {{#has_toc}}
++      <div class="calibreTocIndex">
++        <h2>{{table_of_contents}}</h2>
++              {{{toc}}}
++      </div>
++    {{/has_toc}}
++    {{^has_toc}}
++        <h2>{{no_toc}}</h2>
++        <div><strong><a 
href="{{next_link}}">{{begin_to_read}}</a></strong></div>
++    {{/has_toc}}
++  </div>
++
++  <div class="calibreEbNav">
++    {{#next_link}}
++      <a href="{{next_link}}" class="calibreANext">{{next_page}}</a>
++    {{/next_link}}
++  </div>
++</div>
++
++</body>
++</html>
+diff --git a/resources/templates/html_export_default_index.tmpl 
b/resources/templates/html_export_default_index.tmpl
+deleted file mode 100644
+index f0665ad..0000000
+--- a/resources/templates/html_export_default_index.tmpl
++++ /dev/null
+@@ -1,61 +0,0 @@
+-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+-<html xmlns="http://www.w3.org/1999/xhtml";>
+-<head>
+-<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+-
+-<link rel="schema.DC" href="http://purl.org/dc/elements/1.1/"; />
+-<link rel="schema.DCTERMS" href="http://purl.org/dc/terms/"; />
+-
+-<title>${print(', '.join(meta.creators()))}$ - ${print(next(meta.titles())); 
print(meta.titles().close())}$</title>
+-
+-${for item in meta:}$
+-  <meta ${print('name="DC.'+item['name']+'"')}$ 
${print('content="'+item['value']+'"')}$ />
+-${:endfor}$
+-
+-<link href="${cssLink}$" type="text/css" rel="stylesheet" />
+-</head>
+-<body>
+-
+-<div class="calibreMeta">
+-  <div class="calibreMetaTitle">
+-  ${pos1=1}$
+-  ${for title in meta.titles():}$
+-    ${if pos1:}$
+-    <h1>
+-      <a href="${tocUrl}$">${print(title)}$</a>
+-    </h1>
+-    ${:else:}$
+-    <div class="calibreMetaSubtitle">${print(title)}$</div>
+-    ${:endif}$
+-    ${pos1=0}$
+-  ${:endfor}$
+-  </div>
+-  <div class="calibreMetaAuthor">
+-    ${print(', '.join(meta.creators()))}$
+-  </div>
+-</div>
+-
+-<div class="calibreMain">
+-  <div class="calibreEbookContent">
+-
+-    ${if has_toc:}$
+-      <div class="calibreTocIndex">
+-        <h2>${print(_('Table of contents'))}$</h2>
+-        ${toc}$
+-      </div>
+-    ${:else:}$
+-        <h2>${print(_('No table of contents present'))}$</h2>
+-        <div><strong><a href="${nextLink}$">${print(_('begin to 
read'))}$</a></strong></div>
+-    ${:endif}$
+-
+-  </div>
+-
+-  <div class="calibreEbNav">
+-    ${if nextLink:}$
+-      <a href="${nextLink}$" class="calibreANext">${print(_('next 
page'))}$</a>
+-    ${:endif}$
+-  </div>
+-</div>
+-
+-</body>
+-</html>
+diff --git a/src/calibre/ebooks/conversion/plugins/html_output.py 
b/src/calibre/ebooks/conversion/plugins/html_output.py
+index d8e2f18..2d311d1 100644
+--- a/src/calibre/ebooks/conversion/plugins/html_output.py
++++ b/src/calibre/ebooks/conversion/plugins/html_output.py
+@@ -26,13 +26,13 @@ class HTMLOutput(OutputFormatPlugin):
+ 
+     options = {
+         OptionRecommendation(name='template_css',
+-            help=_('CSS file used for the output instead of the default 
file')),
++            help=_('CSS file used for the output instead of the default 
CSS.')),
+ 
+         OptionRecommendation(name='template_html_index',
+-            help=_('Template used for generation of the HTML index file 
instead of the default file')),
++            help=_('Template used for generation of the HTML index file 
instead of the default template. In Mustache format.')),
+ 
+         OptionRecommendation(name='template_html',
+-            help=_('Template used for the generation of the HTML contents of 
the book instead of the default file')),
++            help=_('Template used for the generation of the HTML contents of 
the book instead of the default template. In Mustache format.')),
+ 
+         OptionRecommendation(name='extract_to',
+             help=_('Extract the contents of the generated ZIP file to the '
+@@ -84,6 +84,7 @@ class HTMLOutput(OutputFormatPlugin):
+                 xml_declaration=False)
+ 
+     def convert(self, oeb_book, output_path, input_plugin, opts, log):
++        import pystache
+         from lxml import etree
+ 
+         from calibre.ebooks.html.meta import EasyMeta
+@@ -96,7 +97,7 @@ class HTMLOutput(OutputFormatPlugin):
+             with open(opts.template_html_index, 'rb') as f:
+                 template_html_index_data = f.read()
+         else:
+-            template_html_index_data = 
P('templates/html_export_default_index.tmpl', data=True)
++            template_html_data = P('templates/html_export_default.mustache', 
data=True)
+ 
+         if opts.template_html is not None:
+             with open(opts.template_html, 'rb') as f:
+@@ -110,9 +111,10 @@ class HTMLOutput(OutputFormatPlugin):
+         else:
+             template_css_data = P('templates/html_export_default.css', 
data=True)
+ 
+-        template_html_index_data = template_html_index_data.decode('utf-8')
+-        template_html_data = template_html_data.decode('utf-8')
++        template_html_index = 
pystache.parse(template_html_index_data.decode('utf-8'))
++        template_html = pystache.parse(template_html_data.decode('utf-8'))
+         template_css_data = template_css_data.decode('utf-8')
++        has_toc = bool(oeb_book.toc.count())
+ 
+         self.log  = log
+         self.opts = opts
+@@ -129,18 +131,31 @@ class HTMLOutput(OutputFormatPlugin):
+         css_path = output_dir+os.sep+'calibreHtmlOutBasicCss.css'
+         with open(css_path, 'wb') as f:
+             f.write(template_css_data.encode('utf-8'))
++        meta_dict = {
++            'titles': [{'title': x, 'is_first': i == 0} for i, x in 
enumerate(meta.titles())],
++            'creators': authors_to_string(tuple(meta.creators())),
++            'items': list(meta),
++        }
++        meta_dict['first_title'] = meta_dict['titles'][0]['title'] if 
meta_dict['titles'] else ''
++        basic_template_vars = {
++                'meta': meta_dict, 'has_toc': has_toc,
++                'table_of_contents':  _('Table of contents'), 'no_toc': _('No 
table of contents present'),
++                'begin_to_read': _('begin to read'), 'start': _('start'),
++                'prev_page': _('previous page'), 'next_page': _('next page'),
++        }
+ 
+         with open(output_file, 'wb') as f:
+-            html_toc = self.generate_html_toc(oeb_book, output_file, 
output_dir)
+-            templite = Templite(template_html_index_data)
+             nextLink = oeb_book.spine[0].href
+             nextLink = relpath(output_dir+os.sep+nextLink, 
dirname(output_file))
+             cssLink = relpath(abspath(css_path), dirname(output_file))
+             tocUrl = relpath(output_file, dirname(output_file))
+-            t = templite.render(has_toc=bool(oeb_book.toc.count()),
+-                    toc=html_toc, meta=meta, nextLink=nextLink,
+-                    tocUrl=tocUrl, cssLink=cssLink,
+-                    firstContentPageLink=nextLink)
++            toc_as_html = self.generate_html_toc(oeb_book, output_file, 
output_dir) if has_toc else ''
++            v = basic_template_vars.copy()
++            v.update({
++                'toc': toc_as_html, 'css_link': cssLink, 'toc_url': tocUrl, 
'next_link': nextLink,
++                'first_content_page_link': nextLink,
++            })
++            t = pystache.render(template_html_index, v)
+             if isinstance(t, str):
+                 t = t.encode('utf-8')
+             f.write(t)
+@@ -196,17 +211,18 @@ class HTMLOutput(OutputFormatPlugin):
+                 firstContentPageLink = oeb_book.spine[0].href
+ 
+                 # render template
+-                templite = Templite(template_html_data)
+-
+                 def toc():
+-                    return self.generate_html_toc(oeb_book, path, output_dir)
+-                t = templite.render(ebookContent=ebook_content,
+-                        prevLink=prevLink, nextLink=nextLink,
+-                        has_toc=bool(oeb_book.toc.count()), toc=toc,
+-                        tocUrl=tocUrl, head_content=head_content,
+-                        meta=meta, cssLink=cssLink,
+-                        firstContentPageLink=firstContentPageLink)
+-
++                    return
++                toc_as_html = self.generate_html_toc(oeb_book, path, 
output_dir) if has_toc else ''
++                v = basic_template_vars.copy()
++                v.update({
++                    'has_link': prevLink or nextLink,
++                    'prev_link': prevLink, 'next_link': nextLink, 'toc_url': 
tocUrl,
++                    'head_content': head_content, 'ebook_content': 
ebook_content,
++                    'css_link': cssLink, 'toc': toc_as_html,
++                    'first_content_page_link': firstContentPageLink,
++                })
++                t = pystache.render(template_html, v)
+                 # write html to file
+                 with open(path, 'wb') as f:
+                     f.write(t.encode('utf-8'))
+diff --git a/src/templite/__init__.py b/src/templite/__init__.py
+deleted file mode 100644
+index f46e777..0000000
+--- a/src/templite/__init__.py
++++ /dev/null
+@@ -1,89 +0,0 @@
+-#!/usr/bin/env python
+-#
+-#       Templite+
+-#       A light-weight, fully functional, general purpose templating engine
+-#
+-#       Copyright (c) 2009 joonis new media
+-#       Author: Thimo Kraemer <[email protected]>
+-#
+-#       Based on Templite - Tomer Filiba
+-#       http://code.activestate.com/recipes/496702/
+-#
+-#       This program is free software; you can redistribute it and/or modify
+-#       it under the terms of the GNU General Public License as published by
+-#       the Free Software Foundation; either version 2 of the License, or
+-#       (at your option) any later version.
+-#
+-#       This program is distributed in the hope that it will be useful,
+-#       but WITHOUT ANY WARRANTY; without even the implied warranty of
+-#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-#       GNU General Public License for more details.
+-#
+-#       You should have received a copy of the GNU General Public License
+-#       along with this program; if not, write to the Free Software
+-#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+-#       MA 02110-1301, USA.
+-#
+-
+-import sys, re
+-
+-from polyglot.builtins import unicode_type
+-
+-class Templite:
+-    auto_emit = re.compile('(^[\'\"])|(^[a-zA-Z0-9_\[\]\'\"]+$)')
+-
+-    def __init__(self, template, start='${', end='}$'):
+-        if len(start) != 2 or len(end) != 2:
+-            raise ValueError('each delimiter must be two characters long')
+-        delimiter = re.compile('%s(.*?)%s' % (re.escape(start), 
re.escape(end)), re.DOTALL)
+-        offset = 0
+-        tokens = []
+-        for i, part in enumerate(delimiter.split(template)):
+-            part = part.replace('\\'.join(list(start)), start)
+-            part = part.replace('\\'.join(list(end)), end)
+-            if i % 2 == 0:
+-                if not part: continue
+-                part = part.replace('\\', '\\\\').replace('"', '\\"')
+-                part = '\t' * offset + 'emit("""%s""")' % part
+-            else:
+-                part = part.rstrip()
+-                if not part: continue
+-                if part.lstrip().startswith(':'):
+-                    if not offset:
+-                        raise SyntaxError('no block statement to terminate: 
${%s}$' % part)
+-                    offset -= 1
+-                    part = part.lstrip()[1:]
+-                    if not part.endswith(':'): continue
+-                elif self.auto_emit.match(part.lstrip()):
+-                    part = 'emit(%s)' % part.lstrip()
+-                lines = part.splitlines()
+-                margin = min(len(l) - len(l.lstrip()) for l in lines if 
l.strip())
+-                part = '\n'.join('\t' * offset + l[margin:] for l in lines)
+-                if part.endswith(':'):
+-                    offset += 1
+-            tokens.append(part)
+-        if offset:
+-            raise SyntaxError('%i block statement(s) not terminated' % offset)
+-        self.__code = compile('\n'.join(tokens), '<templite %r>' % 
template[:20], 'exec')
+-
+-    def render(self, __namespace=None, **kw):
+-        """
+-        renders the template according to the given namespace.
+-        __namespace - a dictionary serving as a namespace for evaluation
+-        **kw - keyword arguments which are added to the namespace
+-        """
+-        namespace = {}
+-        if __namespace: namespace.update(__namespace)
+-        if kw: namespace.update(kw)
+-        namespace['emit'] = self.write
+-
+-        __stdout = sys.stdout
+-        sys.stdout = self
+-        self.__output = []
+-        eval(self.__code, namespace)
+-        sys.stdout = __stdout
+-        return ''.join(self.__output)
+-
+-    def write(self, *args):
+-        for a in args:
+-            self.__output.append(unicode_type(a))
diff -Nru calibre-6.13.0+repack/debian/patches/series 
calibre-6.13.0+repack/debian/patches/series
--- calibre-6.13.0+repack/debian/patches/series 2025-11-09 18:15:24.000000000 
+0900
+++ calibre-6.13.0+repack/debian/patches/series 2026-02-09 00:15:48.000000000 
+0900
@@ -34,3 +34,6 @@
 0034-Fix-2075128-Private-bug-https-bugs.launchpad.net-cal.patch
 0035-Fix-2076515-calibredb-list-command-ignores-fields-op.patch
 0036-Fix-CVE-2025-64486.patch
+0037-CVE-2026-25635-CHM-Input-Ignore-internal-files-that-.patch
+0038-CVE-2026-25636-DRYer.patch
+0039-CVE-2026-25731-ZIP-Output-Change-the-template-engine.patch

--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 12.14

This update has been released as part of Debian 12.14.

--- End Message ---

Reply via email to