diff --git a/absl/testing/absltest.py b/absl/testing/absltest.py index 34f8141..5422f68 100644 --- a/absl/testing/absltest.py +++ b/absl/testing/absltest.py @@ -358,8 +358,24 @@ def _create( ) -> tuple['_TempFile', str]: """Module-private: create a tempfile instance.""" if file_path: - cleanup_path = os.path.join(base_path, _get_first_part(file_path)) - path = os.path.join(base_path, file_path) + normalized_file_path = os.path.normpath(file_path) + if os.path.isabs(normalized_file_path): + raise ValueError( + f'Invalid file_path {file_path!r}: absolute paths are not allowed' + ) + + base_path_real = os.path.realpath(base_path) + path = os.path.join(base_path, normalized_file_path) + path_real = os.path.realpath(path) + if os.path.commonpath([base_path_real, path_real]) != base_path_real: + raise ValueError( + f'Invalid file_path {file_path!r}: path must stay within the ' + 'base directory' + ) + + cleanup_path = os.path.join( + base_path, _get_first_part(normalized_file_path) + ) os.makedirs(os.path.dirname(path), exist_ok=True) # The file may already exist, in which case, ensure it's writable so that # it can be truncated. diff --git a/absl/testing/tests/absltest_test.py b/absl/testing/tests/absltest_test.py index 048ae65..d45d919 100644 --- a/absl/testing/tests/absltest_test.py +++ b/absl/testing/tests/absltest_test.py @@ -2751,6 +2751,27 @@ def test_tempdir_create_file(self): td = self.create_tempdir() td.create_file(content='text') + def test_tempdir_create_file_valid_relative_path(self): + td = self.create_tempdir() + tf = td.create_file('file.txt', content='ok') + self.assert_file_exists(tf, 'ok') + + def test_tempdir_create_file_valid_nested_relative_path(self): + td = self.create_tempdir() + tf = td.create_file('nested/path/file.txt', content='ok') + self.assert_file_exists(tf, 'ok') + + def test_tempdir_create_file_rejects_parent_traversal(self): + td = self.create_tempdir() + with self.assertRaisesRegex(ValueError, 'must stay within the base directory'): + td.create_file('../escape.txt', content='bad') + + def test_tempdir_create_file_rejects_absolute_path(self): + td = self.create_tempdir() + absolute_path = os.path.join(tempfile.gettempdir(), 'absltest-escape.txt') + with self.assertRaisesRegex(ValueError, 'absolute paths are not allowed'): + td.create_file(absolute_path, content='bad') + def test_tempfile_text(self): tf = self.create_tempfile(content='text') self.assert_file_exists(tf, 'text')