From: Kovid Goyal <kovid@kovidgoyal.net>
Date: Fri, 6 Mar 2026 07:39:44 +0530
Subject: CVE-2026-30853: RB Input: Ensure files are extracted within
 container dir

Forwarded: not-needed
Bug: https://github.com/kovidgoyal/calibre/security/advisories/GHSA-7mp7-rfrg-542x
Origin: https://github.com/kovidgoyal/calibre/commit/0f8dc639337d9ace67201e15ca12d5906d05f4c8

Signed-off-by: YOKOTA Hiroshi <yokota.hgml@gmail.com>
---
 src/calibre/ebooks/rb/reader.py | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/src/calibre/ebooks/rb/reader.py b/src/calibre/ebooks/rb/reader.py
index 58a5ecb..8eb3b97 100644
--- a/src/calibre/ebooks/rb/reader.py
+++ b/src/calibre/ebooks/rb/reader.py
@@ -68,6 +68,15 @@ class Reader:
 
         return toc
 
+    def get_safe_path(self, output_dir, name):
+        base = os.path.abspath(output_dir)
+        if not base.endswith(os.sep):
+            base += os.sep
+        ans = os.path.abspath(os.path.join(base, name))
+        if os.path.commonprefix([ans, base]) != base:
+            ans = ''
+        return ans
+
     def get_text(self, toc_item, output_dir):
         if toc_item.flags in (1, 2):
             return
@@ -88,8 +97,9 @@ class Reader:
         else:
             output += self.stream.read(toc_item.size).decode('cp1252' if self.encoding is None else self.encoding, 'replace')
 
-        with open(os.path.join(output_dir, toc_item.name.decode('utf-8')), 'wb') as html:
-            html.write(output.replace('<TITLE>', '<TITLE> ').encode('utf-8'))
+        if path := self.get_safe_path(output_dir, toc_item.name.decode('utf-8')):
+            with open(path, 'wb') as html:
+                html.write(output.replace('<TITLE>', '<TITLE> ').encode('utf-8'))
 
     def get_image(self, toc_item, output_dir):
         if toc_item.flags != 0:
@@ -98,8 +108,9 @@ class Reader:
         self.stream.seek(toc_item.offset)
         data = self.stream.read(toc_item.size)
 
-        with open(os.path.join(output_dir, toc_item.name.decode('utf-8')), 'wb') as img:
-            img.write(data)
+        if path := self.get_safe_path(output_dir, toc_item.name.decode('utf-8')):
+            with open(path, 'wb') as img:
+                img.write(data)
 
     def extract_content(self, output_dir):
         self.log.debug('Extracting content from file...')
