已开启
Fix CVE-2025-12060 in python-Keras for openEuler-24.03-LTS-SP1 #27
Fix CVE-2025-12060 in python-Keras for openEuler-24.03-LTS-SP1 #27
已开启
jixiang创建于 7月20日
共 2 个文件变更+253-1
@@ -0,0 +1,248 @@
1+From 609cb47691f188133935254a8f7a98c1486e1bd4 Mon Sep 17 00:00:00 2001
2+From: chenjianhu <chenjianhu@kylinos.cn>
3+Date: Tue, 2 Dec 2025 06:05:47 +0800
4+Subject: [PATCH] Fix CVE-2025-12060
5+ 
6+Use filter="data" option of TarFile.extractall. (#21760)
7+For Python versions between 3.12 (inclusive) and 3.14 (exclusive).
8+ 
9+The "data" filter performs a number of additional checks on links
10+and paths. The `filter` option was added in Python 3.12.
11+The `filter="data"` option became the default in Python 3.14.
12+ 
13+Also:
14+- added similar path filtering when extracting zip archives
15+- shared the extraction code between `file_utils` and `saving_lib`
16+ 
17+---
18+ keras/saving/saving_lib.py | 2 +-
19+ keras/utils/data_utils.py | 63 +++++++++++++++++++++-----
20+ keras/utils/data_utils_test.py | 82 ++++++++++++++++++++++++++++++++++
21+ 3 files changed, 134 insertions(+), 13 deletions(-)
22+ 
23+diff --git a/keras/saving/saving_lib.py b/keras/saving/saving_lib.py
24+index 9e52ecf..7def015 100644
25+--- a/keras/saving/saving_lib.py
26++++ b/keras/saving/saving_lib.py
27+@@ -535,7 +535,7 @@ class DiskIOStore:
28+ if self.archive:
29+ self.tmp_dir = _get_temp_dir()
30+ if self.mode == "r":
31+- self.archive.extractall(path=self.tmp_dir)
32++ keras.utils.data_utils.extract_open_archive(self.archive, self.tmp_dir)
33+ self.working_dir = tf.io.gfile.join(self.tmp_dir, self.root_path)
34+ if self.mode == "w":
35+ tf.io.gfile.makedirs(self.working_dir)
36+diff --git a/keras/utils/data_utils.py b/keras/utils/data_utils.py
37+index 3856d42..e082e2e 100644
38+--- a/keras/utils/data_utils.py
39++++ b/keras/utils/data_utils.py
40+@@ -23,6 +23,7 @@ import pathlib
41+ import queue
42+ import random
43+ import shutil
44++import sys
45+ import tarfile
46+ import threading
47+ import time
48+@@ -116,26 +117,71 @@ def _is_link_in_dir(info, base):
49+ return _is_path_in_dir(info.linkname, base_dir=tip)
50+ 
51+ 
52+-def _filter_safe_paths(members):
53++def filter_safe_zipinfos(members):
54+ base_dir = _resolve_path(".")
Y
Yyixiangzhike7月23日

此处base_dir = _resolve_path(".")涉及CVE-2026-11816问题,已在CVE-2026-11816补丁中变更,需要重新适配

likedislike
55+ for finfo in members:
56+ valid_path = False
57+- if finfo.issym() or finfo.islnk():
58+- if _is_path_in_dir(finfo.name, base_dir) and _is_link_in_dir(
59+- finfo, base_dir
60+- ):
61++ if _is_path_in_dir(finfo.filename, base_dir):
62+- valid_path = True
63+- yield finfo
64++ valid_path = True
65++ yield finfo
66+- elif _is_path_in_dir(finfo.name, base_dir):
67+- valid_path = True
68+- yield finfo
69++ if not valid_path:
70++ warnings.warn(
71++ "Skipping invalid path during archive extraction: "
72++ f"'{finfo.filename}'.",
73++ stacklevel=2,
74++ )
75++
76++
77++def filter_safe_tarinfos(members):
78++ base_dir = _resolve_path(".")
Y
Yyixiangzhike7月23日

此处base_dir = _resolve_path(".")可能涉及CVE-2026-11816问题,需要参考CVE-2026-11816补丁重新适配

likedislike
79++ for finfo in members:
80++ valid_path = False
81++ if finfo.issym() or finfo.islnk():
82++ if _is_path_in_dir(finfo.name, base_dir) and _is_link_in_dir(
83++ finfo, base_dir
84++ ):
85++ valid_path = True
86++ yield finfo
87++ elif _is_path_in_dir(finfo.name, base_dir):
88++ valid_path = True
89++ yield finfo
90+ if not valid_path:
91+ warnings.warn(
92+ "Skipping invalid path during archive extraction: "
93+- f"'{finfo.name}'."
94++ f"'{finfo.name}'.",
95++ stacklevel=2,
96+ )
97+ 
98+ 
99++def extract_open_archive(archive, path="."):
100++ """Extracts an open tar or zip archive to the provided directory.
101++
102++ This function filters unsafe paths during extraction.
103++
104++ Args:
105++ archive: The archive object, either a `TarFile` or a `ZipFile`.
106++ path: Where to extract the archive file.
107++ """
108++ if isinstance(archive, zipfile.ZipFile):
109++ # Zip archive.
110++ archive.extractall(
111++ path, members=filter_safe_zipinfos(archive.infolist())
112++ )
113++ else:
114++ # Tar archive.
115++ extractall_kwargs = {}
116++ # The `filter="data"` option was added in Python 3.12. It became the
117++ # default starting from Python 3.14. So we only specify it between
118++ # those two versions.
119++ if sys.version_info >= (3, 12) and sys.version_info < (3, 14):
120++ extractall_kwargs = {"filter": "data"}
121++ archive.extractall(
122++ path,
123++ members=filter_safe_tarinfos(archive),
124++ **extractall_kwargs,
125++ )
126++
127++
128+ def _extract_archive(file_path, path=".", archive_format="auto"):
129+ """Extracts an archive if it matches tar, tar.gz, tar.bz, or zip formats.
130+ 
131+@@ -171,14 +217,7 @@ def _extract_archive(file_path, path=".", archive_format="auto"):
132+ if is_match_fn(file_path):
133+ with open_fn(file_path) as archive:
134+ try:
135+- if zipfile.is_zipfile(file_path):
136+- # Zip archive.
137+- archive.extractall(path)
138+- else:
139+- # Tar archive, perhaps unsafe. Filter paths.
140+- archive.extractall(
141+- path, members=_filter_safe_paths(archive)
142+- )
143++ extract_open_archive(archive, path)
144+ except (tarfile.TarError, RuntimeError, KeyboardInterrupt):
145+ if os.path.exists(path):
146+ if os.path.isfile(path):
147+diff --git a/keras/utils/data_utils_test.py b/keras/utils/data_utils_test.py
148+index 093281c..c1f6717 100644
149+--- a/keras/utils/data_utils_test.py
150++++ b/keras/utils/data_utils_test.py
151+@@ -17,8 +17,10 @@
152+ import os
153+ import tarfile
154+ import urllib
155++import shutil
156+ import zipfile
157+ from itertools import cycle
158++from unittest.mock import patch
159+ 
160+ import numpy as np
161+ import tensorflow.compat.v2 as tf
162+@@ -205,6 +207,86 @@ class TestGetFile(tf.test.TestCase):
163+ )
164+ 
165+ 
166++class FilterSafePathsTest(tf.test.TestCase):
167++ def setUp(self):
168++ self.base_dir = os.path.join(os.getcwd(), "temp_dir")
169++ os.makedirs(self.base_dir, exist_ok=True)
170++ self.tar_path = os.path.join(self.base_dir, "test.tar")
171++
172++ def tearDown(self):
173++ os.remove(self.tar_path)
174++ shutil.rmtree(self.base_dir)
175++
176++ def test_member_within_base_dir(self):
177++ """Test a member within the base directory."""
178++ with tarfile.open(self.tar_path, "w") as tar:
179++ tar.add(__file__, arcname="safe_path.txt")
180++ with tarfile.open(self.tar_path, "r") as tar:
181++ members = list(data_utils.filter_safe_tarinfos(tar.getmembers()))
182++ self.assertEqual(len(members), 1)
183++ self.assertEqual(members[0].name, "safe_path.txt")
184++
185++ def test_symlink_within_base_dir(self):
186++ """Test a symlink pointing within the base directory."""
187++ symlink_path = os.path.join(self.base_dir, "symlink.txt")
188++ target_path = os.path.join(self.base_dir, "target.txt")
189++ with open(target_path, "w") as f:
190++ f.write("target")
191++ os.symlink(target_path, symlink_path)
192++ with tarfile.open(self.tar_path, "w") as tar:
193++ tar.add(symlink_path, arcname="symlink.txt")
194++ with tarfile.open(self.tar_path, "r") as tar:
195++ members = list(data_utils.filter_safe_tarinfos(tar.getmembers()))
196++ self.assertEqual(len(members), 1)
197++ self.assertEqual(members[0].name, "symlink.txt")
198++ os.remove(symlink_path)
199++ os.remove(target_path)
200++
201++ def test_invalid_path_warning(self):
202++ """Test warning for an invalid path during archive extraction."""
203++ invalid_path = os.path.join(os.getcwd(), "invalid.txt")
204++ with open(invalid_path, "w") as f:
205++ f.write("invalid")
206++ with tarfile.open(self.tar_path, "w") as tar:
207++ tar.add(
208++ invalid_path, arcname="../../invalid.txt"
209++ ) # Path intended to be outside of base dir
210++ with tarfile.open(self.tar_path, "r") as tar:
211++ with patch("warnings.warn") as mock_warn:
212++ _ = list(data_utils.filter_safe_tarinfos(tar.getmembers()))
213++ warning_msg = (
214++ "Skipping invalid path during archive extraction: "
215++ "'../../invalid.txt'."
216++ )
217++ mock_warn.assert_called_with(warning_msg, stacklevel=2)
218++ os.remove(invalid_path)
219++
220++ def test_symbolic_link_in_base_dir(self):
221++ """symbolic link within the base directory is correctly processed."""
222++ symlink_path = os.path.join(self.base_dir, "symlink.txt")
223++ target_path = os.path.join(self.base_dir, "target.txt")
224++
225++ # Create a target file and then a symbolic link pointing to it.
226++ with open(target_path, "w") as f:
227++ f.write("target")
228++ os.symlink(target_path, symlink_path)
229++
230++ # Add the symbolic link to the tar archive.
231++ with tarfile.open(self.tar_path, "w") as tar:
232++ tar.add(symlink_path, arcname="symlink.txt")
233++
234++ with tarfile.open(self.tar_path, "r") as tar:
235++ members = list(data_utils.filter_safe_tarinfos(tar.getmembers()))
236++ self.assertEqual(len(members), 1)
237++ self.assertEqual(members[0].name, "symlink.txt")
238++ self.assertTrue(
239++ members[0].issym()
240++ ) # Explicitly assert it's a symbolic link.
241++
242++ os.remove(symlink_path)
243++ os.remove(target_path)
244++
245++
246+ class TestSequence(keras.utils.data_utils.Sequence):
247+ def __init__(self, shape, value=1.0):
248+ self.shape = shape
@@ -1,7 +1,7 @@
1%global _empty_manifest_terminate_build 01%global _empty_manifest_terminate_build 0
2Name: python-Keras2Name: python-Keras
3Version: 2.12.03Version: 2.12.0
4-Release: 34+Release: 4
5Summary: Deep Learning for humans5Summary: Deep Learning for humans
6License: Apache-2.06License: Apache-2.0
7URL: https://github.com/keras-team/keras7URL: https://github.com/keras-team/keras
@@ -10,6 +10,7 @@ BuildArch: noarch
10 10 
11Patch0: backport-CVE-2025-12058.patch11Patch0: backport-CVE-2025-12058.patch
12Patch1: backport-CVE-2026-12482.patch12Patch1: backport-CVE-2026-12482.patch
13+Patch2: backport-Fix-CVE-2025-12060.patch
13 14 
14Requires: python3-numpy15Requires: python3-numpy
15Requires: python3-scipy16Requires: python3-scipy
@@ -99,6 +100,9 @@ mv %{buildroot}/doclist.lst .
99%{_docdir}/*100%{_docdir}/*
100 101 
101%changelog102%changelog
103+* Tue Jul 21 2026 jixiang <qfmy_250803@qq.com> - 2.12.0-4
104+- Fix CVE-2025-12060
105+ 
102* Tue Jul 14 2026 shaojiansong <shaojiansong@kylinos.cn> - 2.12.0-3106* Tue Jul 14 2026 shaojiansong <shaojiansong@kylinos.cn> - 2.12.0-3
103- Fix CVE-2026-12482107- Fix CVE-2026-12482
104 108