File unarchive-test-fix.patch of Package ansible-core.38786

From c158b3f85126dabb8107260839dbcdb80e356c70 Mon Sep 17 00:00:00 2001
From: Stefano Rivera <github@rivera.za.net>
Date: Thu, 5 Dec 2024 14:26:58 -0800
Subject: [PATCH] unarchive: Clamp zip timestamps on 32-bit time_t (#84409)

Clamp zip timestamps to representible values when unpacking zip files on
platforms that use 32-bit time_t (e.g.  Debian i386). This is a
non-issue in practice (in 2024), but should allow the test suite to pass
on Debian i386.

We use a round value of 2038-01-01 00:00:00 for simplicity, and to avoid
running into timezone offsets closer to the actual limit.

MR #81520 introduced sanity-checking tests that used dates not
representable with a 32-bit time_t.
---
 .../fragments/unarchive_timestamp_t32.yaml    |  3 +++
 lib/ansible/modules/unarchive.py              | 22 +++++++++++++++++++
 test/units/modules/test_unarchive.py          | 10 ++++++++-
 3 files changed, 34 insertions(+), 1 deletion(-)
 create mode 100644 changelogs/fragments/unarchive_timestamp_t32.yaml

diff --git a/changelogs/fragments/unarchive_timestamp_t32.yaml b/changelogs/fragments/unarchive_timestamp_t32.yaml
new file mode 100644
index 0000000000..969c85de05
--- /dev/null
+++ b/changelogs/fragments/unarchive_timestamp_t32.yaml
@@ -0,0 +1,3 @@
+---
+bugfixes:
+  - unarchive - Clamp timestamps from beyond y2038 to representible values when unpacking zip files on platforms that use 32-bit time_t (e.g. Debian i386).
diff --git a/lib/ansible/modules/unarchive.py b/lib/ansible/modules/unarchive.py
index 0d56da53a4..3663cebcb8 100644
--- a/lib/ansible/modules/unarchive.py
+++ b/lib/ansible/modules/unarchive.py
@@ -241,6 +241,7 @@ uid:
 
 import binascii
 import codecs
+import ctypes
 import fnmatch
 import grp
 import os
@@ -262,6 +263,13 @@ from ansible.module_utils.urls import fetch_file
 from shlex import quote
 from zipfile import BadZipFile
 
+try:
+    from functools import cache
+except ImportError:
+    # Python < 3.9
+    from functools import lru_cache
+    cache = lru_cache(maxsize=None)
+
 # String from tar that shows the tar contents are different from the
 # filesystem
 OWNER_DIFF_RE = re.compile(r': Uid differs$')
@@ -279,6 +287,18 @@ CONTENT_DIFF_RE = re.compile(r': Contents differ$')
 SIZE_DIFF_RE = re.compile(r': Size differs$')
 
 
+@cache
+def _y2038_impacted():
+    """Determine if the system has 64-bit time_t."""
+    if hasattr(ctypes, "c_time_t"):  # Python >= 3.12
+        return ctypes.sizeof(ctypes.c_time_t) < 8
+    try:
+        time.gmtime(2**31)
+    except OverflowError:
+        return True
+    return False
+
+
 def crc32(path, buffer_size):
     ''' Return a CRC32 checksum of a file '''
 
@@ -414,6 +434,8 @@ class ZipArchive(object):
             try:
                 if int(match.groups()[0]) < 1980:
                     date_time = epoch_date_time
+                elif int(match.groups()[0]) >= 2038 and _y2038_impacted():
+                    date_time = (2038, 1, 1, 0, 0, 0, 0, 0, 0)
                 elif int(match.groups()[0]) > 2107:
                     date_time = (2107, 12, 31, 23, 59, 59, 0, 0, 0)
                 else:
diff --git a/test/units/modules/test_unarchive.py b/test/units/modules/test_unarchive.py
index 6a2f0d9a67..b1885c2f1c 100644
--- a/test/units/modules/test_unarchive.py
+++ b/test/units/modules/test_unarchive.py
@@ -14,6 +14,14 @@ def fake_ansible_module():
     return FakeAnsibleModule()
 
 
+def max_zip_timestamp():
+    """Return the max clamp value that will be selected."""
+    try:
+        return time.mktime(time.struct_time((2107, 12, 31, 23, 59, 59, 0, 0, 0)))
+    except OverflowError:
+        return time.mktime(time.struct_time((2038, 1, 1, 0, 0, 0, 0, 0, 0)))
+
+
 class FakeAnsibleModule:
     def __init__(self):
         self.params = {}
@@ -68,7 +76,7 @@ class TestCaseZipArchive:
             ),
             pytest.param(
                 "21081231.000000",
-                time.mktime(time.struct_time((2107, 12, 31, 23, 59, 59, 0, 0, 0))),
+                max_zip_timestamp(),
                 id="invalid-year-2108",
             ),
             pytest.param(
-- 
2.48.1

openSUSE Build Service is sponsored by