e904b667c6
PaddleOCR PR Tests GPU / test-pr-gpu (push) Blocked by required conditions
PaddleOCR PR Tests / test-pr (push) Blocked by required conditions
PaddleOCR PR Tests / test-pr-python (3.8) (push) Waiting to run
Build/Publish Develop Docs / deploy (push) Failing after 1s
PaddleOCR Code Style Check / check-code-style (push) Failing after 1s
PaddleOCR PR Tests GPU / detect-changes (push) Failing after 1s
PaddleOCR PR Tests GPU / test-pr-gpu-impl (push) Waiting to run
PaddleOCR PR Tests / detect-changes (push) Failing after 1s
PaddleOCR PR Tests / test-pr-python (3.13) (push) Waiting to run
PaddleOCR PR Tests / test-pr-python (3.9) (push) Waiting to run
151 lines
4.8 KiB
Python
151 lines
4.8 KiB
Python
# Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import importlib.util
|
|
import os
|
|
import pickle
|
|
import sys
|
|
import types
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def _stub_module(monkeypatch, name, **attrs):
|
|
module = types.ModuleType(name)
|
|
for key, value in attrs.items():
|
|
setattr(module, key, value)
|
|
monkeypatch.setitem(sys.modules, name, module)
|
|
return module
|
|
|
|
|
|
def _load_lmdb_dataset_module(monkeypatch, lmdb_open):
|
|
_stub_module(monkeypatch, "cv2")
|
|
_stub_module(
|
|
monkeypatch, "numpy", random=types.SimpleNamespace(randint=lambda upper: 0)
|
|
)
|
|
_stub_module(monkeypatch, "lmdb", open=lmdb_open)
|
|
_stub_module(monkeypatch, "PIL")
|
|
_stub_module(monkeypatch, "PIL.Image")
|
|
|
|
paddle_module = _stub_module(monkeypatch, "paddle")
|
|
paddle_io_module = _stub_module(
|
|
monkeypatch, "paddle.io", Dataset=type("Dataset", (), {})
|
|
)
|
|
paddle_module.io = paddle_io_module
|
|
|
|
ppocr_module = _stub_module(monkeypatch, "ppocr")
|
|
ppocr_data_module = _stub_module(monkeypatch, "ppocr.data")
|
|
ppocr_data_module.__path__ = []
|
|
ppocr_module.data = ppocr_data_module
|
|
_stub_module(
|
|
monkeypatch,
|
|
"ppocr.data.imaug",
|
|
transform=lambda data, ops: data,
|
|
create_operators=lambda *args, **kwargs: [],
|
|
)
|
|
|
|
spec = importlib.util.spec_from_file_location(
|
|
"ppocr.data.lmdb_dataset", REPO_ROOT / "ppocr" / "data" / "lmdb_dataset.py"
|
|
)
|
|
module = importlib.util.module_from_spec(spec)
|
|
monkeypatch.setitem(sys.modules, "ppocr.data.lmdb_dataset", module)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
class _Exploit:
|
|
def __reduce__(self):
|
|
return (os.system, ("echo SHOULD_NOT_RUN > /tmp/paddleocr_lmdb_pickle_test",))
|
|
|
|
|
|
class _Txn:
|
|
def __init__(self, values):
|
|
self._values = values
|
|
|
|
def get(self, key):
|
|
return self._values.get(key)
|
|
|
|
|
|
class _Env:
|
|
def __init__(self, txn):
|
|
self._txn = txn
|
|
|
|
def begin(self, write=False):
|
|
return self._txn
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _cleanup_marker():
|
|
marker = Path("/tmp/paddleocr_lmdb_pickle_test")
|
|
marker.unlink(missing_ok=True)
|
|
yield
|
|
marker.unlink(missing_ok=True)
|
|
|
|
|
|
def test_tablemaster_sample_info_rejects_pickle_rce_payload(monkeypatch):
|
|
module = _load_lmdb_dataset_module(
|
|
monkeypatch, lmdb_open=lambda *args, **kwargs: None
|
|
)
|
|
payload = pickle.dumps(_Exploit())
|
|
txn = _Txn({b"1": payload})
|
|
dataset = module.LMDBDataSetTableMaster.__new__(module.LMDBDataSetTableMaster)
|
|
|
|
assert dataset.get_lmdb_sample_info(txn, 1) is None
|
|
assert not Path("/tmp/paddleocr_lmdb_pickle_test").exists()
|
|
|
|
|
|
def test_tablemaster_sample_info_accepts_expected_basic_pickle_data(monkeypatch):
|
|
module = _load_lmdb_dataset_module(
|
|
monkeypatch, lmdb_open=lambda *args, **kwargs: None
|
|
)
|
|
safe_payload = pickle.dumps(("sample.png", b"img-bytes", "raw-name\ntext\n1,2,3,4"))
|
|
txn = _Txn({b"1": safe_payload})
|
|
dataset = module.LMDBDataSetTableMaster.__new__(module.LMDBDataSetTableMaster)
|
|
|
|
sample = dataset.get_lmdb_sample_info(txn, 1)
|
|
|
|
assert sample["file_name"] == "sample.png"
|
|
assert sample["image"] == b"img-bytes"
|
|
assert sample["structure"] == ["text"]
|
|
assert sample["cells"] == [{"bbox": [1, 2, 3, 4], "tokens": ["1", "2"]}]
|
|
|
|
|
|
def test_tablemaster_length_metadata_rejects_pickle_rce_payload(monkeypatch):
|
|
payload = pickle.dumps(_Exploit())
|
|
txn = _Txn({b"__len__": payload})
|
|
module = _load_lmdb_dataset_module(
|
|
monkeypatch, lmdb_open=lambda *args, **kwargs: _Env(txn)
|
|
)
|
|
dataset = module.LMDBDataSetTableMaster.__new__(module.LMDBDataSetTableMaster)
|
|
|
|
with pytest.raises(pickle.UnpicklingError):
|
|
dataset.load_hierarchical_lmdb_dataset("/tmp/ignored")
|
|
|
|
assert not Path("/tmp/paddleocr_lmdb_pickle_test").exists()
|
|
|
|
|
|
def test_tablemaster_length_metadata_accepts_expected_integer_pickle(monkeypatch):
|
|
txn = _Txn({b"__len__": pickle.dumps(7)})
|
|
module = _load_lmdb_dataset_module(
|
|
monkeypatch, lmdb_open=lambda *args, **kwargs: _Env(txn)
|
|
)
|
|
dataset = module.LMDBDataSetTableMaster.__new__(module.LMDBDataSetTableMaster)
|
|
|
|
lmdb_sets = dataset.load_hierarchical_lmdb_dataset("/tmp/ignored")
|
|
|
|
assert lmdb_sets[0]["num_samples"] == 7
|