1
0

Merge pull request #10026 from SomberNight/202507_jsonpatch_monkeypatch_exc

JsonDB: monkeypatch jsonpatch exceptions to avoid leaking secrets
This commit is contained in:
ThomasV
2025-07-15 12:06:39 +02:00
committed by GitHub
3 changed files with 127 additions and 1 deletions

View File

@@ -28,14 +28,30 @@ import json
from typing import TYPE_CHECKING, Optional, Sequence, List, Union
import jsonpatch
import jsonpointer
from . import util
from .util import WalletFileException, profiler
from .util import WalletFileException, profiler, sticky_property
from .logging import Logger
if TYPE_CHECKING:
from .storage import WalletStorage
# We monkeypatch exceptions in the jsonpatch package to ensure they do not contain secrets from the DB.
# We often log exceptions and offer to send them to the crash reporter, so they must not contain secrets.
jsonpointer.JsonPointerException.__str__ = lambda self: """(JPE) 'redacted'"""
jsonpointer.JsonPointerException.__repr__ = lambda self: """<JsonPointerException 'redacted'>"""
setattr(jsonpointer.JsonPointerException, '__cause__', sticky_property(None))
setattr(jsonpointer.JsonPointerException, '__context__', sticky_property(None))
setattr(jsonpointer.JsonPointerException, '__suppress_context__', sticky_property(True))
jsonpatch.JsonPatchException.__str__ = lambda self: """(JPE) 'redacted'"""
jsonpatch.JsonPatchException.__repr__ = lambda self: """<JsonPatchException 'redacted'>"""
setattr(jsonpatch.JsonPatchException, '__cause__', sticky_property(None))
setattr(jsonpatch.JsonPatchException, '__context__', sticky_property(None))
setattr(jsonpatch.JsonPatchException, '__suppress_context__', sticky_property(True))
def modifier(func):
def wrapper(self, *args, **kwargs):
with self.lock:

View File

@@ -2199,6 +2199,30 @@ class classproperty(property):
return self.fget(owner_cls)
def sticky_property(val):
"""Creates a 'property' whose value cannot be changed and that cannot be deleted.
Attempts to change the value are silently ignored.
>>> class C: pass
...
>>> setattr(C, 'x', sticky_property(3))
>>> c = C()
>>> c.x
3
>>> c.x = 2
>>> c.x
3
>>> del c.x
>>> c.x
3
"""
return property(
fget=lambda self: val,
fset=lambda *args, **kwargs: None,
fdel=lambda *args, **kwargs: None,
)
def get_running_loop() -> Optional[asyncio.AbstractEventLoop]:
"""Returns the asyncio event loop that is *running in this thread*, if any."""
try: