tree-sitter-analyzer 0.8.2__py3-none-any.whl → 0.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tree-sitter-analyzer might be problematic. Click here for more details.
- tree_sitter_analyzer/__init__.py +1 -1
- tree_sitter_analyzer/mcp/__init__.py +17 -3
- tree_sitter_analyzer/mcp/server.py +185 -43
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +4 -4
- tree_sitter_analyzer/project_detector.py +317 -317
- tree_sitter_analyzer/security/__init__.py +22 -22
- tree_sitter_analyzer/security/boundary_manager.py +237 -237
- tree_sitter_analyzer/security/regex_checker.py +292 -292
- tree_sitter_analyzer/security/validator.py +241 -241
- {tree_sitter_analyzer-0.8.2.dist-info → tree_sitter_analyzer-0.9.1.dist-info}/METADATA +23 -13
- {tree_sitter_analyzer-0.8.2.dist-info → tree_sitter_analyzer-0.9.1.dist-info}/RECORD +13 -13
- {tree_sitter_analyzer-0.8.2.dist-info → tree_sitter_analyzer-0.9.1.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.8.2.dist-info → tree_sitter_analyzer-0.9.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,241 +1,241 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Security Validator for Tree-sitter Analyzer
|
|
4
|
-
|
|
5
|
-
Provides unified security validation framework inspired by code-index-mcp's
|
|
6
|
-
ValidationHelper but enhanced for tree-sitter analyzer's requirements.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import os
|
|
10
|
-
import re
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
from typing import Optional, Tuple
|
|
13
|
-
|
|
14
|
-
from ..exceptions import SecurityError
|
|
15
|
-
from ..utils import log_debug, log_warning
|
|
16
|
-
from .boundary_manager import ProjectBoundaryManager
|
|
17
|
-
from .regex_checker import RegexSafetyChecker
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class SecurityValidator:
|
|
21
|
-
"""
|
|
22
|
-
Unified security validation framework.
|
|
23
|
-
|
|
24
|
-
This class provides comprehensive security validation for file paths,
|
|
25
|
-
regex patterns, and other user inputs to prevent security vulnerabilities.
|
|
26
|
-
|
|
27
|
-
Features:
|
|
28
|
-
- Multi-layer path traversal protection
|
|
29
|
-
- Project boundary enforcement
|
|
30
|
-
- ReDoS attack prevention
|
|
31
|
-
- Input sanitization
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
def __init__(self, project_root: Optional[str] = None) -> None:
|
|
35
|
-
"""
|
|
36
|
-
Initialize security validator.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
project_root: Optional project root directory for boundary checks
|
|
40
|
-
"""
|
|
41
|
-
self.boundary_manager = (
|
|
42
|
-
ProjectBoundaryManager(project_root) if project_root else None
|
|
43
|
-
)
|
|
44
|
-
self.regex_checker = RegexSafetyChecker()
|
|
45
|
-
|
|
46
|
-
log_debug(f"SecurityValidator initialized with project_root: {project_root}")
|
|
47
|
-
|
|
48
|
-
def validate_file_path(
|
|
49
|
-
self, file_path: str, base_path: Optional[str] = None
|
|
50
|
-
) -> Tuple[bool, str]:
|
|
51
|
-
"""
|
|
52
|
-
Validate file path with comprehensive security checks.
|
|
53
|
-
|
|
54
|
-
Implements multi-layer defense against path traversal attacks
|
|
55
|
-
and ensures file access stays within project boundaries.
|
|
56
|
-
|
|
57
|
-
Args:
|
|
58
|
-
file_path: File path to validate
|
|
59
|
-
base_path: Optional base path for relative path validation
|
|
60
|
-
|
|
61
|
-
Returns:
|
|
62
|
-
Tuple of (is_valid, error_message)
|
|
63
|
-
|
|
64
|
-
Example:
|
|
65
|
-
>>> validator = SecurityValidator("/project/root")
|
|
66
|
-
>>> is_valid, error = validator.validate_file_path("src/main.py")
|
|
67
|
-
>>> assert is_valid
|
|
68
|
-
"""
|
|
69
|
-
try:
|
|
70
|
-
# Layer 1: Basic input validation
|
|
71
|
-
if not file_path or not isinstance(file_path, str):
|
|
72
|
-
return False, "File path must be a non-empty string"
|
|
73
|
-
|
|
74
|
-
# Layer 2: Null byte injection check
|
|
75
|
-
if "\x00" in file_path:
|
|
76
|
-
log_warning(f"Null byte detected in file path: {file_path}")
|
|
77
|
-
return False, "File path contains null bytes"
|
|
78
|
-
|
|
79
|
-
# Layer 3: Windows drive letter check (only on non-Windows systems)
|
|
80
|
-
if len(file_path) > 1 and file_path[1] == ":" and os.name != 'nt':
|
|
81
|
-
return False, "Windows drive letters are not allowed on this system"
|
|
82
|
-
|
|
83
|
-
# Layer 4: Absolute path check
|
|
84
|
-
if os.path.isabs(file_path):
|
|
85
|
-
# If we have a project root, check if the absolute path is within it
|
|
86
|
-
if self.boundary_manager and self.boundary_manager.project_root:
|
|
87
|
-
if not self.boundary_manager.is_within_project(file_path):
|
|
88
|
-
return False, "Absolute path must be within project directory"
|
|
89
|
-
else:
|
|
90
|
-
# In test environments (temp directories), allow absolute paths
|
|
91
|
-
import tempfile
|
|
92
|
-
temp_dir = tempfile.gettempdir()
|
|
93
|
-
if file_path.startswith(temp_dir):
|
|
94
|
-
return True, ""
|
|
95
|
-
# No project root defined, reject all other absolute paths
|
|
96
|
-
return False, "Absolute file paths are not allowed"
|
|
97
|
-
|
|
98
|
-
# Layer 5: Path normalization and traversal check
|
|
99
|
-
norm_path = os.path.normpath(file_path)
|
|
100
|
-
if "..\\" in norm_path or "../" in norm_path or norm_path.startswith(".."):
|
|
101
|
-
log_warning(f"Path traversal attempt detected: {file_path}")
|
|
102
|
-
return False, "Directory traversal not allowed"
|
|
103
|
-
|
|
104
|
-
# Layer 6: Project boundary validation
|
|
105
|
-
if self.boundary_manager and base_path:
|
|
106
|
-
if not self.boundary_manager.is_within_project(
|
|
107
|
-
os.path.join(base_path, norm_path)
|
|
108
|
-
):
|
|
109
|
-
return False, "Access denied. File path must be within project directory"
|
|
110
|
-
|
|
111
|
-
# Layer 7: Symbolic link check (if file exists)
|
|
112
|
-
if base_path:
|
|
113
|
-
full_path = os.path.join(base_path, norm_path)
|
|
114
|
-
if os.path.exists(full_path) and os.path.islink(full_path):
|
|
115
|
-
log_warning(f"Symbolic link detected: {full_path}")
|
|
116
|
-
return False, "Symbolic links are not allowed"
|
|
117
|
-
|
|
118
|
-
log_debug(f"File path validation passed: {file_path}")
|
|
119
|
-
return True, ""
|
|
120
|
-
|
|
121
|
-
except Exception as e:
|
|
122
|
-
log_warning(f"File path validation error: {e}")
|
|
123
|
-
return False, f"Validation error: {str(e)}"
|
|
124
|
-
|
|
125
|
-
def validate_directory_path(
|
|
126
|
-
self, dir_path: str, must_exist: bool = True
|
|
127
|
-
) -> Tuple[bool, str]:
|
|
128
|
-
"""
|
|
129
|
-
Validate directory path for security and existence.
|
|
130
|
-
|
|
131
|
-
Args:
|
|
132
|
-
dir_path: Directory path to validate
|
|
133
|
-
must_exist: Whether directory must exist
|
|
134
|
-
|
|
135
|
-
Returns:
|
|
136
|
-
Tuple of (is_valid, error_message)
|
|
137
|
-
"""
|
|
138
|
-
try:
|
|
139
|
-
# Basic validation using file path validator
|
|
140
|
-
is_valid, error = self.validate_file_path(dir_path)
|
|
141
|
-
if not is_valid:
|
|
142
|
-
return False, error
|
|
143
|
-
|
|
144
|
-
# Check if path exists and is directory
|
|
145
|
-
if must_exist:
|
|
146
|
-
if not os.path.exists(dir_path):
|
|
147
|
-
return False, f"Directory does not exist: {dir_path}"
|
|
148
|
-
|
|
149
|
-
if not os.path.isdir(dir_path):
|
|
150
|
-
return False, f"Path is not a directory: {dir_path}"
|
|
151
|
-
|
|
152
|
-
log_debug(f"Directory path validation passed: {dir_path}")
|
|
153
|
-
return True, ""
|
|
154
|
-
|
|
155
|
-
except Exception as e:
|
|
156
|
-
log_warning(f"Directory path validation error: {e}")
|
|
157
|
-
return False, f"Validation error: {str(e)}"
|
|
158
|
-
|
|
159
|
-
def validate_regex_pattern(self, pattern: str) -> Tuple[bool, str]:
|
|
160
|
-
"""
|
|
161
|
-
Validate regex pattern for ReDoS attack prevention.
|
|
162
|
-
|
|
163
|
-
Args:
|
|
164
|
-
pattern: Regex pattern to validate
|
|
165
|
-
|
|
166
|
-
Returns:
|
|
167
|
-
Tuple of (is_valid, error_message)
|
|
168
|
-
"""
|
|
169
|
-
return self.regex_checker.validate_pattern(pattern)
|
|
170
|
-
|
|
171
|
-
def sanitize_input(self, user_input: str, max_length: int = 1000) -> str:
|
|
172
|
-
"""
|
|
173
|
-
Sanitize user input by removing dangerous characters.
|
|
174
|
-
|
|
175
|
-
Args:
|
|
176
|
-
user_input: Input string to sanitize
|
|
177
|
-
max_length: Maximum allowed length
|
|
178
|
-
|
|
179
|
-
Returns:
|
|
180
|
-
Sanitized input string
|
|
181
|
-
|
|
182
|
-
Raises:
|
|
183
|
-
SecurityError: If input is too long or contains dangerous content
|
|
184
|
-
"""
|
|
185
|
-
if not isinstance(user_input, str):
|
|
186
|
-
raise SecurityError("Input must be a string")
|
|
187
|
-
|
|
188
|
-
if len(user_input) > max_length:
|
|
189
|
-
raise SecurityError(f"Input too long: {len(user_input)} > {max_length}")
|
|
190
|
-
|
|
191
|
-
# Remove null bytes and control characters
|
|
192
|
-
sanitized = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', user_input)
|
|
193
|
-
|
|
194
|
-
# Remove HTML/XML tags for XSS prevention
|
|
195
|
-
sanitized = re.sub(r'<[^>]*>', '', sanitized)
|
|
196
|
-
|
|
197
|
-
# Remove potentially dangerous characters
|
|
198
|
-
sanitized = re.sub(r'[<>"\']', '', sanitized)
|
|
199
|
-
|
|
200
|
-
# Log if sanitization occurred
|
|
201
|
-
if sanitized != user_input:
|
|
202
|
-
log_warning("Input sanitization performed")
|
|
203
|
-
|
|
204
|
-
return sanitized
|
|
205
|
-
|
|
206
|
-
def validate_glob_pattern(self, pattern: str) -> Tuple[bool, str]:
|
|
207
|
-
"""
|
|
208
|
-
Validate glob pattern for safe file matching.
|
|
209
|
-
|
|
210
|
-
Args:
|
|
211
|
-
pattern: Glob pattern to validate
|
|
212
|
-
|
|
213
|
-
Returns:
|
|
214
|
-
Tuple of (is_valid, error_message)
|
|
215
|
-
"""
|
|
216
|
-
try:
|
|
217
|
-
# Basic input validation
|
|
218
|
-
if not pattern or not isinstance(pattern, str):
|
|
219
|
-
return False, "Pattern must be a non-empty string"
|
|
220
|
-
|
|
221
|
-
# Check for dangerous patterns
|
|
222
|
-
dangerous_patterns = [
|
|
223
|
-
"..", # Path traversal
|
|
224
|
-
"//", # Double slashes
|
|
225
|
-
"\\\\", # Double backslashes
|
|
226
|
-
]
|
|
227
|
-
|
|
228
|
-
for dangerous in dangerous_patterns:
|
|
229
|
-
if dangerous in pattern:
|
|
230
|
-
return False, f"Dangerous pattern detected: {dangerous}"
|
|
231
|
-
|
|
232
|
-
# Validate length
|
|
233
|
-
if len(pattern) > 500:
|
|
234
|
-
return False, "Pattern too long"
|
|
235
|
-
|
|
236
|
-
log_debug(f"Glob pattern validation passed: {pattern}")
|
|
237
|
-
return True, ""
|
|
238
|
-
|
|
239
|
-
except Exception as e:
|
|
240
|
-
log_warning(f"Glob pattern validation error: {e}")
|
|
241
|
-
return False, f"Validation error: {str(e)}"
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Security Validator for Tree-sitter Analyzer
|
|
4
|
+
|
|
5
|
+
Provides unified security validation framework inspired by code-index-mcp's
|
|
6
|
+
ValidationHelper but enhanced for tree-sitter analyzer's requirements.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Optional, Tuple
|
|
13
|
+
|
|
14
|
+
from ..exceptions import SecurityError
|
|
15
|
+
from ..utils import log_debug, log_warning
|
|
16
|
+
from .boundary_manager import ProjectBoundaryManager
|
|
17
|
+
from .regex_checker import RegexSafetyChecker
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SecurityValidator:
|
|
21
|
+
"""
|
|
22
|
+
Unified security validation framework.
|
|
23
|
+
|
|
24
|
+
This class provides comprehensive security validation for file paths,
|
|
25
|
+
regex patterns, and other user inputs to prevent security vulnerabilities.
|
|
26
|
+
|
|
27
|
+
Features:
|
|
28
|
+
- Multi-layer path traversal protection
|
|
29
|
+
- Project boundary enforcement
|
|
30
|
+
- ReDoS attack prevention
|
|
31
|
+
- Input sanitization
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, project_root: Optional[str] = None) -> None:
|
|
35
|
+
"""
|
|
36
|
+
Initialize security validator.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
project_root: Optional project root directory for boundary checks
|
|
40
|
+
"""
|
|
41
|
+
self.boundary_manager = (
|
|
42
|
+
ProjectBoundaryManager(project_root) if project_root else None
|
|
43
|
+
)
|
|
44
|
+
self.regex_checker = RegexSafetyChecker()
|
|
45
|
+
|
|
46
|
+
log_debug(f"SecurityValidator initialized with project_root: {project_root}")
|
|
47
|
+
|
|
48
|
+
def validate_file_path(
|
|
49
|
+
self, file_path: str, base_path: Optional[str] = None
|
|
50
|
+
) -> Tuple[bool, str]:
|
|
51
|
+
"""
|
|
52
|
+
Validate file path with comprehensive security checks.
|
|
53
|
+
|
|
54
|
+
Implements multi-layer defense against path traversal attacks
|
|
55
|
+
and ensures file access stays within project boundaries.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
file_path: File path to validate
|
|
59
|
+
base_path: Optional base path for relative path validation
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Tuple of (is_valid, error_message)
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
>>> validator = SecurityValidator("/project/root")
|
|
66
|
+
>>> is_valid, error = validator.validate_file_path("src/main.py")
|
|
67
|
+
>>> assert is_valid
|
|
68
|
+
"""
|
|
69
|
+
try:
|
|
70
|
+
# Layer 1: Basic input validation
|
|
71
|
+
if not file_path or not isinstance(file_path, str):
|
|
72
|
+
return False, "File path must be a non-empty string"
|
|
73
|
+
|
|
74
|
+
# Layer 2: Null byte injection check
|
|
75
|
+
if "\x00" in file_path:
|
|
76
|
+
log_warning(f"Null byte detected in file path: {file_path}")
|
|
77
|
+
return False, "File path contains null bytes"
|
|
78
|
+
|
|
79
|
+
# Layer 3: Windows drive letter check (only on non-Windows systems)
|
|
80
|
+
if len(file_path) > 1 and file_path[1] == ":" and os.name != 'nt':
|
|
81
|
+
return False, "Windows drive letters are not allowed on this system"
|
|
82
|
+
|
|
83
|
+
# Layer 4: Absolute path check
|
|
84
|
+
if os.path.isabs(file_path):
|
|
85
|
+
# If we have a project root, check if the absolute path is within it
|
|
86
|
+
if self.boundary_manager and self.boundary_manager.project_root:
|
|
87
|
+
if not self.boundary_manager.is_within_project(file_path):
|
|
88
|
+
return False, "Absolute path must be within project directory"
|
|
89
|
+
else:
|
|
90
|
+
# In test environments (temp directories), allow absolute paths
|
|
91
|
+
import tempfile
|
|
92
|
+
temp_dir = tempfile.gettempdir()
|
|
93
|
+
if file_path.startswith(temp_dir):
|
|
94
|
+
return True, ""
|
|
95
|
+
# No project root defined, reject all other absolute paths
|
|
96
|
+
return False, "Absolute file paths are not allowed"
|
|
97
|
+
|
|
98
|
+
# Layer 5: Path normalization and traversal check
|
|
99
|
+
norm_path = os.path.normpath(file_path)
|
|
100
|
+
if "..\\" in norm_path or "../" in norm_path or norm_path.startswith(".."):
|
|
101
|
+
log_warning(f"Path traversal attempt detected: {file_path}")
|
|
102
|
+
return False, "Directory traversal not allowed"
|
|
103
|
+
|
|
104
|
+
# Layer 6: Project boundary validation
|
|
105
|
+
if self.boundary_manager and base_path:
|
|
106
|
+
if not self.boundary_manager.is_within_project(
|
|
107
|
+
os.path.join(base_path, norm_path)
|
|
108
|
+
):
|
|
109
|
+
return False, "Access denied. File path must be within project directory"
|
|
110
|
+
|
|
111
|
+
# Layer 7: Symbolic link check (if file exists)
|
|
112
|
+
if base_path:
|
|
113
|
+
full_path = os.path.join(base_path, norm_path)
|
|
114
|
+
if os.path.exists(full_path) and os.path.islink(full_path):
|
|
115
|
+
log_warning(f"Symbolic link detected: {full_path}")
|
|
116
|
+
return False, "Symbolic links are not allowed"
|
|
117
|
+
|
|
118
|
+
log_debug(f"File path validation passed: {file_path}")
|
|
119
|
+
return True, ""
|
|
120
|
+
|
|
121
|
+
except Exception as e:
|
|
122
|
+
log_warning(f"File path validation error: {e}")
|
|
123
|
+
return False, f"Validation error: {str(e)}"
|
|
124
|
+
|
|
125
|
+
def validate_directory_path(
|
|
126
|
+
self, dir_path: str, must_exist: bool = True
|
|
127
|
+
) -> Tuple[bool, str]:
|
|
128
|
+
"""
|
|
129
|
+
Validate directory path for security and existence.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
dir_path: Directory path to validate
|
|
133
|
+
must_exist: Whether directory must exist
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Tuple of (is_valid, error_message)
|
|
137
|
+
"""
|
|
138
|
+
try:
|
|
139
|
+
# Basic validation using file path validator
|
|
140
|
+
is_valid, error = self.validate_file_path(dir_path)
|
|
141
|
+
if not is_valid:
|
|
142
|
+
return False, error
|
|
143
|
+
|
|
144
|
+
# Check if path exists and is directory
|
|
145
|
+
if must_exist:
|
|
146
|
+
if not os.path.exists(dir_path):
|
|
147
|
+
return False, f"Directory does not exist: {dir_path}"
|
|
148
|
+
|
|
149
|
+
if not os.path.isdir(dir_path):
|
|
150
|
+
return False, f"Path is not a directory: {dir_path}"
|
|
151
|
+
|
|
152
|
+
log_debug(f"Directory path validation passed: {dir_path}")
|
|
153
|
+
return True, ""
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
log_warning(f"Directory path validation error: {e}")
|
|
157
|
+
return False, f"Validation error: {str(e)}"
|
|
158
|
+
|
|
159
|
+
def validate_regex_pattern(self, pattern: str) -> Tuple[bool, str]:
|
|
160
|
+
"""
|
|
161
|
+
Validate regex pattern for ReDoS attack prevention.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
pattern: Regex pattern to validate
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Tuple of (is_valid, error_message)
|
|
168
|
+
"""
|
|
169
|
+
return self.regex_checker.validate_pattern(pattern)
|
|
170
|
+
|
|
171
|
+
def sanitize_input(self, user_input: str, max_length: int = 1000) -> str:
|
|
172
|
+
"""
|
|
173
|
+
Sanitize user input by removing dangerous characters.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
user_input: Input string to sanitize
|
|
177
|
+
max_length: Maximum allowed length
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Sanitized input string
|
|
181
|
+
|
|
182
|
+
Raises:
|
|
183
|
+
SecurityError: If input is too long or contains dangerous content
|
|
184
|
+
"""
|
|
185
|
+
if not isinstance(user_input, str):
|
|
186
|
+
raise SecurityError("Input must be a string")
|
|
187
|
+
|
|
188
|
+
if len(user_input) > max_length:
|
|
189
|
+
raise SecurityError(f"Input too long: {len(user_input)} > {max_length}")
|
|
190
|
+
|
|
191
|
+
# Remove null bytes and control characters
|
|
192
|
+
sanitized = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', user_input)
|
|
193
|
+
|
|
194
|
+
# Remove HTML/XML tags for XSS prevention
|
|
195
|
+
sanitized = re.sub(r'<[^>]*>', '', sanitized)
|
|
196
|
+
|
|
197
|
+
# Remove potentially dangerous characters
|
|
198
|
+
sanitized = re.sub(r'[<>"\']', '', sanitized)
|
|
199
|
+
|
|
200
|
+
# Log if sanitization occurred
|
|
201
|
+
if sanitized != user_input:
|
|
202
|
+
log_warning("Input sanitization performed")
|
|
203
|
+
|
|
204
|
+
return sanitized
|
|
205
|
+
|
|
206
|
+
def validate_glob_pattern(self, pattern: str) -> Tuple[bool, str]:
|
|
207
|
+
"""
|
|
208
|
+
Validate glob pattern for safe file matching.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
pattern: Glob pattern to validate
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Tuple of (is_valid, error_message)
|
|
215
|
+
"""
|
|
216
|
+
try:
|
|
217
|
+
# Basic input validation
|
|
218
|
+
if not pattern or not isinstance(pattern, str):
|
|
219
|
+
return False, "Pattern must be a non-empty string"
|
|
220
|
+
|
|
221
|
+
# Check for dangerous patterns
|
|
222
|
+
dangerous_patterns = [
|
|
223
|
+
"..", # Path traversal
|
|
224
|
+
"//", # Double slashes
|
|
225
|
+
"\\\\", # Double backslashes
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
for dangerous in dangerous_patterns:
|
|
229
|
+
if dangerous in pattern:
|
|
230
|
+
return False, f"Dangerous pattern detected: {dangerous}"
|
|
231
|
+
|
|
232
|
+
# Validate length
|
|
233
|
+
if len(pattern) > 500:
|
|
234
|
+
return False, "Pattern too long"
|
|
235
|
+
|
|
236
|
+
log_debug(f"Glob pattern validation passed: {pattern}")
|
|
237
|
+
return True, ""
|
|
238
|
+
|
|
239
|
+
except Exception as e:
|
|
240
|
+
log_warning(f"Glob pattern validation error: {e}")
|
|
241
|
+
return False, f"Validation error: {str(e)}"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tree-sitter-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: Extensible multi-language code analyzer framework using Tree-sitter with dynamic plugin architecture
|
|
5
5
|
Project-URL: Homepage, https://github.com/aimasteracc/tree-sitter-analyzer
|
|
6
6
|
Project-URL: Documentation, https://github.com/aimasteracc/tree-sitter-analyzer#readme
|
|
@@ -137,7 +137,7 @@ Description-Content-Type: text/markdown
|
|
|
137
137
|
|
|
138
138
|
[](https://python.org)
|
|
139
139
|
[](LICENSE)
|
|
140
|
-
[](#testing)
|
|
141
141
|
[](#testing)
|
|
142
142
|
[](#quality)
|
|
143
143
|
|
|
@@ -228,11 +228,10 @@ Extract specific code sections efficiently:
|
|
|
228
228
|
- Content length information
|
|
229
229
|
|
|
230
230
|
### 3. AI Assistant Integration
|
|
231
|
-
|
|
232
|
-
- `
|
|
233
|
-
- `analyze_code_structure` - Generate detailed structure tables
|
|
234
|
-
- `
|
|
235
|
-
- `analyze_code_universal` - Universal analysis with auto-detection
|
|
231
|
+
Three-step workflow MCP tools for AI assistants:
|
|
232
|
+
- `check_code_scale` - **Step 1:** Get code metrics and complexity
|
|
233
|
+
- `analyze_code_structure` - **Step 2:** Generate detailed structure tables with line positions
|
|
234
|
+
- `extract_code_section` - **Step 3:** Extract specific code sections by line range
|
|
236
235
|
|
|
237
236
|
### 4. Multi-Language Support
|
|
238
237
|
- **Java** - Full support with advanced analysis
|
|
@@ -244,14 +243,25 @@ Four powerful MCP tools for AI assistants:
|
|
|
244
243
|
|
|
245
244
|
### AI Assistant Usage (via Claude Desktop)
|
|
246
245
|
|
|
247
|
-
**Step 1:
|
|
248
|
-
|
|
246
|
+
**Step 1: Check code scale**
|
|
247
|
+
```
|
|
248
|
+
Use tool: check_code_scale
|
|
249
|
+
Parameters: {"file_path": "examples/Sample.java"}
|
|
250
|
+
```
|
|
249
251
|
|
|
250
|
-
**Step 2: Analyze
|
|
251
|
-
|
|
252
|
+
**Step 2: Analyze structure (for large files >100 lines)**
|
|
253
|
+
```
|
|
254
|
+
Use tool: analyze_code_structure
|
|
255
|
+
Parameters: {"file_path": "examples/Sample.java", "format_type": "full"}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Step 3: Extract specific code (using line positions from step 2)**
|
|
259
|
+
```
|
|
260
|
+
Use tool: extract_code_section
|
|
261
|
+
Parameters: {"file_path": "examples/Sample.java", "start_line": 84, "end_line": 86}
|
|
262
|
+
```
|
|
252
263
|
|
|
253
|
-
**
|
|
254
|
-
> "Show me lines 84-86 from examples/Sample.java"
|
|
264
|
+
> **Note:** Always use snake_case parameter names: `file_path`, `start_line`, `end_line`, `format_type`
|
|
255
265
|
|
|
256
266
|
### CLI Usage
|
|
257
267
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
tree_sitter_analyzer/__init__.py,sha256
|
|
1
|
+
tree_sitter_analyzer/__init__.py,sha256=ToixgdoKI2Cp-Uj4bCiJIrMRcMptWOv6olr1C6iNKnE,3199
|
|
2
2
|
tree_sitter_analyzer/__main__.py,sha256=ilhMPpn_ar28oelzxLfQcX6WH_UbQ2euxiSoV3z_yCg,239
|
|
3
3
|
tree_sitter_analyzer/api.py,sha256=_94HoE1LKGELSE6FpZ6pEqm2R7qfoPokyfpGSjawliQ,17487
|
|
4
4
|
tree_sitter_analyzer/cli_main.py,sha256=ses68m5tLoYMP6Co3Fk2vqBACuFd38MqF85uEoa0mbw,9714
|
|
@@ -9,7 +9,7 @@ tree_sitter_analyzer/language_detector.py,sha256=IjkYF1E7_TtWlwYjz780ZUJAyPltL2a
|
|
|
9
9
|
tree_sitter_analyzer/language_loader.py,sha256=gdLxkSoajm-q7c1vcvFONtBf5XJRgasUVI4L0wMzra0,8124
|
|
10
10
|
tree_sitter_analyzer/models.py,sha256=z0aqdZOVA8rYWF0143TSAUoCvncVRLZ1O70eAjV87gU,16564
|
|
11
11
|
tree_sitter_analyzer/output_manager.py,sha256=eiBOSL2vUUQi1ghYBr4gwT7aOYC2WTgIoISBZlXkzPo,8399
|
|
12
|
-
tree_sitter_analyzer/project_detector.py,sha256=
|
|
12
|
+
tree_sitter_analyzer/project_detector.py,sha256=FWk9bgnonxhCBRBGST-iSSxI6zHC3Ea-VTnP8iM_S10,9882
|
|
13
13
|
tree_sitter_analyzer/query_loader.py,sha256=NilC2XmmhYrBL6ONlzRGlehGa23C_4V6nDVap6YG8v0,10120
|
|
14
14
|
tree_sitter_analyzer/table_formatter.py,sha256=BfrAouAr3r6MD9xY9yhHw_PwD0aJ4BQo5p1UFhorT5k,27284
|
|
15
15
|
tree_sitter_analyzer/utils.py,sha256=Pq_2vlDPul8jean0PwlQ_XC-RDjkuaUbwoXp2ls7dV8,8268
|
|
@@ -45,8 +45,8 @@ tree_sitter_analyzer/languages/__init__.py,sha256=D-b1wReVcu3XRBvbeWgO80i29naGJ6
|
|
|
45
45
|
tree_sitter_analyzer/languages/java_plugin.py,sha256=o_9F_anKCemnUDV6hq28RatRmBm3e0nchcQZ-v0nDv4,48454
|
|
46
46
|
tree_sitter_analyzer/languages/javascript_plugin.py,sha256=9al0ScXmM5Y8Xl82oNp7cUaU9P59eNCJCPXSlfea4u8,16290
|
|
47
47
|
tree_sitter_analyzer/languages/python_plugin.py,sha256=nlVxDx6thOB5o6QfQzGbD7gph3_YuM32YYzqYZoHlMw,29899
|
|
48
|
-
tree_sitter_analyzer/mcp/__init__.py,sha256=
|
|
49
|
-
tree_sitter_analyzer/mcp/server.py,sha256=
|
|
48
|
+
tree_sitter_analyzer/mcp/__init__.py,sha256=udPIWx_nP4UoCjX0xVybElPQgWycAFYd3x7TrQS18Bs,1475
|
|
49
|
+
tree_sitter_analyzer/mcp/server.py,sha256=6WoRAV4ro_2OdwpT9Yj3D_GiUMhnvH8CmQ6D2AvmUzo,23688
|
|
50
50
|
tree_sitter_analyzer/mcp/resources/__init__.py,sha256=PHDvZyHZawoToDQVqrepsmcTk00ZlaTsu6uxwVjoa4A,1433
|
|
51
51
|
tree_sitter_analyzer/mcp/resources/code_file_resource.py,sha256=MDHvJl6akElHtcxlN6eCcY5WYSjQEQFCyhAVGiPGk9s,6462
|
|
52
52
|
tree_sitter_analyzer/mcp/resources/project_stats_resource.py,sha256=lZF9TGxjKvTwPyuWE_o3I3V4LK0zEj3lab4L0Iq-hho,19758
|
|
@@ -54,7 +54,7 @@ tree_sitter_analyzer/mcp/tools/__init__.py,sha256=RMvJOzfZMVe24WUNWJJ-pdygc1RbEV
|
|
|
54
54
|
tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py,sha256=yI33yev1W-MztyjiPlSX4uwCcFigRpzdHloXNCXAQz8,27938
|
|
55
55
|
tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py,sha256=Ie1yeGTFNxuEeTLgXVnKEdKktoMEV27ychIMVkStRY8,9244
|
|
56
56
|
tree_sitter_analyzer/mcp/tools/base_tool.py,sha256=szW84sSYejzRyBlFbskOARQbsfc2JLwHmjZ6rJZ8SQA,1264
|
|
57
|
-
tree_sitter_analyzer/mcp/tools/read_partial_tool.py,sha256=
|
|
57
|
+
tree_sitter_analyzer/mcp/tools/read_partial_tool.py,sha256=O8zfxZ6FGKfVXeuQfjoCNul02-2Jn7mOg_Hoxy8cxxM,11457
|
|
58
58
|
tree_sitter_analyzer/mcp/tools/table_format_tool.py,sha256=JUfkB32ZXf-RQ5O2nKC2jFTVR1AxD8lks5vjjDFEoNw,15502
|
|
59
59
|
tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py,sha256=MbJEzWa0b2KtHLIgmy5WVcCN89YL4tB1drujoHt9axs,22173
|
|
60
60
|
tree_sitter_analyzer/mcp/utils/__init__.py,sha256=F_qFFC2gvGNdgRWGLxIh4Amd0dPhZv0Ni1ZbCbaYLlI,3063
|
|
@@ -67,11 +67,11 @@ tree_sitter_analyzer/queries/java.py,sha256=guHrEQD2PvzUvY7dVxIXpul2KmIJMLefq7G7
|
|
|
67
67
|
tree_sitter_analyzer/queries/javascript.py,sha256=wIv5k-l48e1_sW_vqNUBkdNKO97CBs-L8IZcQIQeZh0,4110
|
|
68
68
|
tree_sitter_analyzer/queries/python.py,sha256=tl72D3JTOSNIG-1lXww0YKQ41AgMKB5iBUkNBaqfkiI,7855
|
|
69
69
|
tree_sitter_analyzer/queries/typescript.py,sha256=I1ndwPjAMGOIa1frSK3ewLqEkeDAJuAE9qzD9seEGf4,6937
|
|
70
|
-
tree_sitter_analyzer/security/__init__.py,sha256=
|
|
71
|
-
tree_sitter_analyzer/security/boundary_manager.py,sha256=
|
|
72
|
-
tree_sitter_analyzer/security/regex_checker.py,sha256=
|
|
73
|
-
tree_sitter_analyzer/security/validator.py,sha256=
|
|
74
|
-
tree_sitter_analyzer-0.
|
|
75
|
-
tree_sitter_analyzer-0.
|
|
76
|
-
tree_sitter_analyzer-0.
|
|
77
|
-
tree_sitter_analyzer-0.
|
|
70
|
+
tree_sitter_analyzer/security/__init__.py,sha256=AlBGtSpDqVxlfM4K7JD-dJsDE8cPcuzJvN7OOsNOhm8,646
|
|
71
|
+
tree_sitter_analyzer/security/boundary_manager.py,sha256=jj7iHiZiMxC590aiw7oUlTjztGxY8WqauG8Rwu8n6Ig,8288
|
|
72
|
+
tree_sitter_analyzer/security/regex_checker.py,sha256=glKsja9zbrP_kOkaXxPkzkkUjLZpO1EBCi3LS3fobmQ,10317
|
|
73
|
+
tree_sitter_analyzer/security/validator.py,sha256=6rbf2fWOxwgMqFqrRqhblcPUsXDOiaij9gd6l1owhXw,9359
|
|
74
|
+
tree_sitter_analyzer-0.9.1.dist-info/METADATA,sha256=xGxnZmp2tpuCVELZpkChnwH6TRTvjRqXvj6pbyj_5m4,15893
|
|
75
|
+
tree_sitter_analyzer-0.9.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
76
|
+
tree_sitter_analyzer-0.9.1.dist-info/entry_points.txt,sha256=EA0Ow27x2SqNt2300sv70RTWxKRIxJzOhNPIVlez4NM,417
|
|
77
|
+
tree_sitter_analyzer-0.9.1.dist-info/RECORD,,
|
|
File without changes
|
{tree_sitter_analyzer-0.8.2.dist-info → tree_sitter_analyzer-0.9.1.dist-info}/entry_points.txt
RENAMED
|
File without changes
|