Coverage for src/taipanstack/security/types.py: 100%

36 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-07-06 15:01 +0000

1""" 

2Pydantic-compatible security types. 

3 

4Provide ``Annotated`` type aliases that integrate TaipanStack's 

5runtime security guards with Pydantic v2 validation, enabling 

6declarative, type-safe input validation inside ``BaseModel`` 

7definitions. 

8""" 

9 

10import re 

11from typing import Annotated 

12 

13from pydantic.functional_validators import AfterValidator 

14 

15from taipanstack.core.result import Ok 

16from taipanstack.security.guards import ( 

17 SecurityError, 

18 guard_command_injection, 

19 guard_path_traversal, 

20 guard_ssrf, 

21) 

22from taipanstack.security.validators import ( 

23 validate_project_name, 

24 validate_url, 

25) 

26 

27 

28def _validate_safe_url(url: str) -> str: 

29 """Validate a URL is safe from SSRF attacks. 

30 

31 Args: 

32 url: The URL string to validate. 

33 

34 Returns: 

35 The validated URL. 

36 

37 Raises: 

38 ValueError: If the URL fails SSRF validation. 

39 

40 """ 

41 result = guard_ssrf(url) 

42 if isinstance(result, Ok): 

43 return result.ok_value 

44 raise ValueError(str(result.err_value)) from result.err_value 

45 

46 

47def _validate_safe_path(path: str) -> str: 

48 """Validate a path is safe from traversal attacks. 

49 

50 Args: 

51 path: The path string to validate. 

52 

53 Returns: 

54 The validated path string. 

55 

56 Raises: 

57 ValueError: If path traversal is detected. 

58 

59 """ 

60 try: 

61 guard_path_traversal(path) 

62 except SecurityError as exc: 

63 raise ValueError(str(exc)) from exc 

64 return path 

65 

66 

67def _validate_safe_command(command: str) -> str: 

68 """Validate a command string is safe from injection attacks. 

69 

70 Args: 

71 command: The command string to validate. 

72 

73 Returns: 

74 The validated command string. 

75 

76 Raises: 

77 ValueError: If command injection is detected. 

78 

79 """ 

80 try: 

81 guard_command_injection([command]) 

82 except SecurityError as exc: 

83 raise ValueError(str(exc)) from exc 

84 return command 

85 

86 

87def _validate_safe_project_name(name: str) -> str: 

88 """Validate a project name conforms to safe naming rules. 

89 

90 Args: 

91 name: The project name to validate. 

92 

93 Returns: 

94 The validated project name. 

95 

96 Raises: 

97 ValueError: If the name is invalid. 

98 

99 """ 

100 return validate_project_name(name) 

101 

102 

103def _validate_safe_url_format(url: str) -> str: 

104 """Validate a URL has a correct format and allowed scheme. 

105 

106 Args: 

107 url: The URL string to validate. 

108 

109 Returns: 

110 The validated URL. 

111 

112 Raises: 

113 ValueError: If the URL format is invalid. 

114 

115 """ 

116 return validate_url(url) 

117 

118 

119SafeUrl = Annotated[ 

120 str, 

121 AfterValidator(_validate_safe_url_format), 

122 AfterValidator(_validate_safe_url), 

123] 

124"""A URL validated for correct format and safe from SSRF attacks.""" 

125 

126SafePath = Annotated[str, AfterValidator(_validate_safe_path)] 

127"""A filesystem path validated against path-traversal attacks.""" 

128 

129SafeCommand = Annotated[str, AfterValidator(_validate_safe_command)] 

130"""A command string validated against shell-injection attacks.""" 

131 

132SafeProjectName = Annotated[str, AfterValidator(_validate_safe_project_name)] 

133"""A project name validated for safe naming conventions.""" 

134 

135 

136_SQL_IDENTIFIER_REGEX = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*\Z")