Coverage for dataexcept/security_exceptions.py: 100%

23 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-09-03 20:46 +0000

1"""Custom exceptions for security-related operations.""" 

2 

3from __future__ import annotations 

4 

5from .base import DataExceptError 

6from .redaction import redact_secret, remove_secret 

7 

8 

9class SecurityError(DataExceptError): 

10 """Base exception for security errors.""" 

11 

12 pass 

13 

14 

15class EncryptionError(SecurityError): 

16 """Raised when data encryption fails.""" 

17 

18 def __init__(self, algorithm: str, message: str | None = None) -> None: 

19 """Initialize EncryptionError. 

20 

21 Args: 

22 algorithm: Name of the encryption algorithm. 

23 message: Optional custom error message. 

24 """ 

25 self.algorithm = algorithm 

26 default = f"Encryption failed using {algorithm}" 

27 super().__init__(message or default) 

28 

29 

30class DecryptionError(SecurityError): 

31 """Raised when data decryption fails.""" 

32 

33 def __init__(self, algorithm: str, message: str | None = None) -> None: 

34 """Initialize DecryptionError. 

35 

36 Args: 

37 algorithm: Name of the decryption algorithm. 

38 message: Optional custom error message. 

39 """ 

40 self.algorithm = algorithm 

41 default = f"Decryption failed using {algorithm}" 

42 super().__init__(message or default) 

43 

44 

45class InvalidTokenError(SecurityError): 

46 """Raised when an authentication token is invalid or expired.""" 

47 

48 def __init__( 

49 self, 

50 token: str | None = None, 

51 message: str | None = None, 

52 ) -> None: 

53 """Initialize InvalidTokenError. 

54 

55 Args: 

56 token: The problematic token. 

57 message: Optional custom error message. 

58 """ 

59 # The raw token is never stored or rendered: this exception is often 

60 # logged, and the caller already holds the value it passed in. 

61 self.token = redact_secret(token) 

62 default = "Invalid authentication token" 

63 if token: 

64 default += f": {self.token}" 

65 # The library was handed the secret, so it can be removed even from a 

66 # message the caller wrote themselves. 

67 super().__init__(remove_secret(message or default, token)) 

68 

69 

70__all__ = [ 

71 "SecurityError", 

72 "EncryptionError", 

73 "DecryptionError", 

74 "InvalidTokenError", 

75]