Repository: astropy/astropy

Issue:
Invalid LZMA files are not properly checked on utils.data
### Description

While working on #17960 I've noticed the test case for invalid LZMA files is not being done properly. I will provide a PR for this

Discussion hints:
github-actions[bot]: Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.

  - [ ] Do the proposed changes actually accomplish desired goals?
  - [ ] Do the proposed changes follow the [Astropy coding guidelines](https://docs.astropy.org/en/latest/development/codeguide.html)?
  - [ ] Are tests added/updated as required? If so, do they follow the [Astropy testing guidelines](https://docs.astropy.org/en/latest/development/testguide.html)?
  - [ ] Are docs added/updated as required? If so, do they follow the [Astropy documentation guidelines](https://docs.astropy.org/en/latest/development/docguide.html)?
  - [ ] Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for [rebase](https://docs.astropy.org/en/latest/development/development_details.html#rebase-if-necessary) and [squash](https://docs.astropy.org/en/latest/development/development_details.html#squash-if-necessary).
  - [ ] Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the [bot](https://docs.astropy.org/en/latest/development/development_details.html#pre-commit).
  - [ ] Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  - [ ] Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  - [ ] At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) *before* merge.
kYwzor: Huh, pretty sure the failed checks are unrelated. Every test was passing (except Check PR milestone / milestone_checker) before I added the towncrier file and there's no way that's the source of these failures. What's the best way to trigger CI again?
pllim: @kYwzor , please make sure you are not branched from some outdated code. Try rebase? I do not see these failures on `main`.

@dhomeier , looks like you wrote the original support in https://github.com/astropy/astropy/pull/3667 . Are you able to review?

Thanks, all!

Solution patch:
diff --git a/astropy/utils/data.py b/astropy/utils/data.py
index 7473a7157d..8ca7fce2c7 100644
--- a/astropy/utils/data.py
+++ b/astropy/utils/data.py
@@ -384,7 +384,7 @@ def get_readable_fileobj(
             fileobj = io.BytesIO(fileobj.read())
 
     # Now read enough bytes to look at signature
-    signature = fileobj.read(4)
+    signature = fileobj.read(6)
     fileobj.seek(0)
 
     if signature[:3] == b"\x1f\x8b\x08":  # gzip
@@ -426,7 +426,7 @@ def get_readable_fileobj(
             fileobj_new.seek(0)
             close_fds.append(fileobj_new)
             fileobj = fileobj_new
-    elif signature[:3] == b"\xfd7z":  # xz
+    elif signature[:6] == b"\xfd7zXZ\x00":  # xz
         if not HAS_LZMA:
             for fd in close_fds:
                 fd.close()
@@ -438,7 +438,7 @@ def get_readable_fileobj(
         try:
             fileobj_new = lzma.LZMAFile(fileobj, mode="rb")
             fileobj_new.read(1)  # need to check that the file is really xz
-        except (OSError, EOFError):  # invalid xz file
+        except lzma.LZMAError:  # invalid xz file
             fileobj.seek(0)
             fileobj_new.close()
             # should we propagate this to the caller to signal bad content?
diff --git a/docs/changes/utils/17984.bugfix.rst b/docs/changes/utils/17984.bugfix.rst
new file mode 100644
index 0000000000..df606dbeaa
--- /dev/null
+++ b/docs/changes/utils/17984.bugfix.rst
@@ -0,0 +1,1 @@
+Properly detect invalid LZMA files in ``utils.data``.


Test patch:
diff --git a/astropy/utils/tests/test_data.py b/astropy/utils/tests/test_data.py
index b10a392dc4..3809282697 100644
--- a/astropy/utils/tests/test_data.py
+++ b/astropy/utils/tests/test_data.py
@@ -971,12 +971,13 @@ def test_local_data_obj(filename):
             assert f.read().rstrip() == b"CONTENT"
 
 
-@pytest.fixture(params=["invalid.dat.bz2", "invalid.dat.gz"])
+@pytest.fixture(params=["invalid.dat.bz2", "invalid.dat.xz", "invalid.dat.gz"])
 def bad_compressed(request, tmp_path):
     # These contents have valid headers for their respective file formats, but
     # are otherwise malformed and invalid.
     bz_content = b"BZhinvalid"
     gz_content = b"\x1f\x8b\x08invalid"
+    xz_content = b"\xfd7zXZ\x00invalid"
 
     datafile = tmp_path / request.param
     filename = str(datafile)
@@ -985,6 +986,8 @@ def bad_compressed(request, tmp_path):
         contents = bz_content
     elif filename.endswith(".gz"):
         contents = gz_content
+    elif filename.endswith(".xz"):
+        contents = xz_content
     else:
         contents = "invalid"