summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnders Heimer <anders.heimer@est.tech>2026-05-18 16:59:08 +0200
committerPaul Barker <paul@pbarker.dev>2026-06-08 21:44:04 +0100
commita42a436300d336b7d196ce76a02fb472dbda9d94 (patch)
tree3751b441cfb6a91d5fe581099ba60970cce54e11
parentd4576e3c081a7ee7b57d48421c4c4f491cb4defe (diff)
downloadpoky-a42a436300d336b7d196ce76a02fb472dbda9d94.tar.gz
bitbake: fetch2: validate deb/ipk data member names
The deb/ipk unpack path selects a data archive member from 'ar -t' output and then passes that member name to a shell command. Previously, any member beginning with data.tar. was selected. Only select known deb/ipk data archive member names when datafile is created. Quote the package path used in the shell command as it can come from the local fetch path. Add local fetcher regression coverage for quoted package filenames, valid compressed data members, and unsupported or unsafe data member names. (Bitbake rev: a32064d0f10b9f5a163a25f410a4e39dccf9cb93) Signed-off-by: Anders Heimer <anders.heimer@est.tech> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org> (cherry picked from commit 73ae3a2447ec93df39bc66cf3d8f9b2ea1bfe3bf) Signed-off-by: Yoann Congal <yoann.congal@smile.fr> Signed-off-by: Paul Barker <paul@pbarker.dev>
-rw-r--r--bitbake/lib/bb/fetch2/__init__.py10
-rw-r--r--bitbake/lib/bb/tests/fetch.py53
2 files changed, 60 insertions, 3 deletions
diff --git a/bitbake/lib/bb/fetch2/__init__.py b/bitbake/lib/bb/fetch2/__init__.py
index 224408de0f..2f54cb86e6 100644
--- a/bitbake/lib/bb/fetch2/__init__.py
+++ b/bitbake/lib/bb/fetch2/__init__.py
@@ -23,6 +23,7 @@ import collections
23import subprocess 23import subprocess
24import pickle 24import pickle
25import errno 25import errno
26import shlex
26import bb.persist_data, bb.utils 27import bb.persist_data, bb.utils
27import bb.checksum 28import bb.checksum
28import bb.process 29import bb.process
@@ -1567,16 +1568,19 @@ class FetchMethod(object):
1567 elif file.endswith('.deb') or file.endswith('.ipk'): 1568 elif file.endswith('.deb') or file.endswith('.ipk'):
1568 output = subprocess.check_output(['ar', '-t', file], preexec_fn=subprocess_setup) 1569 output = subprocess.check_output(['ar', '-t', file], preexec_fn=subprocess_setup)
1569 datafile = None 1570 datafile = None
1571 valid_datafiles = ('data.tar', 'data.tar.gz', 'data.tar.xz',
1572 'data.tar.zst', 'data.tar.bz2', 'data.tar.lzma')
1570 if output: 1573 if output:
1571 for line in output.decode().splitlines(): 1574 for line in output.decode().splitlines():
1572 if line.startswith('data.tar.'): 1575 if line in valid_datafiles:
1573 datafile = line 1576 datafile = line
1574 break 1577 break
1575 else: 1578 else:
1576 raise UnpackError("Unable to unpack deb/ipk package - does not contain data.tar.* file", urldata.url) 1579 raise UnpackError("Unable to unpack deb/ipk package - does not contain supported data.tar* file", urldata.url)
1577 else: 1580 else:
1578 raise UnpackError("Unable to unpack deb/ipk package - could not list contents", urldata.url) 1581 raise UnpackError("Unable to unpack deb/ipk package - could not list contents", urldata.url)
1579 cmd = 'ar x %s %s && %s -p -f %s && rm %s' % (file, datafile, tar_cmd, datafile, datafile) 1582 quoted_datafile = shlex.quote(datafile)
1583 cmd = 'ar x %s %s && %s -p -f %s && rm %s' % (shlex.quote(file), quoted_datafile, tar_cmd, quoted_datafile, quoted_datafile)
1580 1584
1581 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd 1585 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd
1582 if 'subdir' in urldata.parm: 1586 if 'subdir' in urldata.parm:
diff --git a/bitbake/lib/bb/tests/fetch.py b/bitbake/lib/bb/tests/fetch.py
index 3775ab0f33..5735cf8f45 100644
--- a/bitbake/lib/bb/tests/fetch.py
+++ b/bitbake/lib/bb/tests/fetch.py
@@ -13,6 +13,7 @@ import tempfile
13import collections 13import collections
14import os 14import os
15import signal 15import signal
16import subprocess
16import tarfile 17import tarfile
17from bb.fetch2 import URI 18from bb.fetch2 import URI
18from bb.fetch2 import FetchMethod 19from bb.fetch2 import FetchMethod
@@ -731,6 +732,34 @@ class FetcherLocalTest(FetcherTest):
731 bb.process.run('tar cjf archive.tar.bz2 -C dir .', cwd=self.localsrcdir) 732 bb.process.run('tar cjf archive.tar.bz2 -C dir .', cwd=self.localsrcdir)
732 self.d.setVar("FILESPATH", self.localsrcdir) 733 self.d.setVar("FILESPATH", self.localsrcdir)
733 734
735 def make_ar_package(self, package_name, data_member="data.tar"):
736 if not shutil.which("ar"):
737 self.skipTest("ar not installed")
738
739 workdir = tempfile.mkdtemp(dir=self.tempdir)
740 payload = os.path.join(workdir, "payload")
741 with open(payload, "w") as f:
742 f.write("payload\n")
743
744 data_path = os.path.join(workdir, data_member)
745 mode = "w:gz" if data_member.endswith(".gz") else "w"
746 with tarfile.open(data_path, mode) as archive:
747 archive.add(payload, arcname="payload")
748
749 with open(os.path.join(workdir, "debian-binary"), "w") as f:
750 f.write("2.0\n")
751
752 control = os.path.join(workdir, "control")
753 with open(control, "w") as f:
754 f.write("Package: fetch-test\nVersion: 1\nArchitecture: all\n")
755 with tarfile.open(os.path.join(workdir, "control.tar"), "w") as archive:
756 archive.add(control, arcname="control")
757
758 package_path = os.path.join(self.localsrcdir, package_name)
759 subprocess.check_call(["ar", "r", package_path, "debian-binary", "control.tar", data_member],
760 cwd=workdir, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
761 return package_name
762
734 def fetchUnpack(self, uris): 763 def fetchUnpack(self, uris):
735 fetcher = bb.fetch.Fetch(uris, self.d) 764 fetcher = bb.fetch.Fetch(uris, self.d)
736 fetcher.download() 765 fetcher.download()
@@ -800,6 +829,30 @@ class FetcherLocalTest(FetcherTest):
800 tree = self.fetchUnpack(['file://archive.tar.bz2;subdir=bar;striplevel=1']) 829 tree = self.fetchUnpack(['file://archive.tar.bz2;subdir=bar;striplevel=1'])
801 self.assertEqual(tree, ['bar/c', 'bar/d', 'bar/subdir/e']) 830 self.assertEqual(tree, ['bar/c', 'bar/d', 'bar/subdir/e'])
802 831
832 def test_local_deb_quoted_filename(self):
833 package = self.make_ar_package("archive$(id).deb")
834 tree = self.fetchUnpack(['file://%s' % package])
835 self.assertEqual(tree, ['payload'])
836
837 def test_local_ipk_gz_data_member(self):
838 package = self.make_ar_package("archive.ipk", data_member="data.tar.gz")
839 tree = self.fetchUnpack(['file://%s' % package])
840 self.assertEqual(tree, ['payload'])
841
842 def test_local_deb_rejects_unknown_data_member_suffix(self):
843 package = self.make_ar_package("archive.deb", data_member="data.tar.foo")
844 with self.assertRaises(bb.fetch2.UnpackError) as context:
845 self.fetchUnpack(['file://%s' % package])
846
847 self.assertIn("does not contain supported data.tar* file", str(context.exception))
848
849 def test_local_deb_rejects_unsafe_data_member(self):
850 package = self.make_ar_package("archive.deb", data_member="data.tar.xz;id")
851 with self.assertRaises(bb.fetch2.UnpackError) as context:
852 self.fetchUnpack(['file://%s' % package])
853
854 self.assertIn("does not contain supported data.tar* file", str(context.exception))
855
803 def dummyGitTest(self, suffix): 856 def dummyGitTest(self, suffix):
804 # Create dummy local Git repo 857 # Create dummy local Git repo
805 src_dir = tempfile.mkdtemp(dir=self.tempdir, 858 src_dir = tempfile.mkdtemp(dir=self.tempdir,