tree-sitter-analyzer 0.8.3__py3-none-any.whl → 0.9.2__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 +132 -132
- tree_sitter_analyzer/__main__.py +11 -11
- tree_sitter_analyzer/api.py +533 -533
- tree_sitter_analyzer/cli/__init__.py +39 -39
- tree_sitter_analyzer/cli/__main__.py +12 -12
- tree_sitter_analyzer/cli/commands/__init__.py +26 -26
- tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
- tree_sitter_analyzer/cli/commands/base_command.py +182 -180
- tree_sitter_analyzer/cli/commands/structure_command.py +138 -138
- tree_sitter_analyzer/cli/commands/summary_command.py +101 -101
- tree_sitter_analyzer/core/__init__.py +15 -15
- tree_sitter_analyzer/core/analysis_engine.py +74 -78
- tree_sitter_analyzer/core/cache_service.py +320 -320
- tree_sitter_analyzer/core/engine.py +566 -566
- tree_sitter_analyzer/core/parser.py +293 -293
- tree_sitter_analyzer/encoding_utils.py +459 -459
- tree_sitter_analyzer/file_handler.py +210 -210
- tree_sitter_analyzer/formatters/__init__.py +1 -1
- tree_sitter_analyzer/formatters/base_formatter.py +167 -167
- tree_sitter_analyzer/formatters/formatter_factory.py +78 -78
- tree_sitter_analyzer/formatters/java_formatter.py +18 -18
- tree_sitter_analyzer/formatters/python_formatter.py +19 -19
- tree_sitter_analyzer/interfaces/__init__.py +9 -9
- tree_sitter_analyzer/interfaces/cli.py +528 -528
- tree_sitter_analyzer/interfaces/cli_adapter.py +344 -343
- tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -206
- tree_sitter_analyzer/language_detector.py +53 -53
- tree_sitter_analyzer/languages/__init__.py +10 -10
- tree_sitter_analyzer/languages/java_plugin.py +1 -1
- tree_sitter_analyzer/languages/javascript_plugin.py +446 -446
- tree_sitter_analyzer/languages/python_plugin.py +755 -755
- tree_sitter_analyzer/mcp/__init__.py +34 -31
- tree_sitter_analyzer/mcp/resources/__init__.py +44 -44
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +209 -209
- tree_sitter_analyzer/mcp/server.py +623 -436
- tree_sitter_analyzer/mcp/tools/__init__.py +30 -30
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +10 -6
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +247 -242
- tree_sitter_analyzer/mcp/tools/base_tool.py +54 -54
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +310 -308
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +386 -379
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +563 -559
- tree_sitter_analyzer/mcp/utils/__init__.py +107 -107
- tree_sitter_analyzer/models.py +10 -10
- tree_sitter_analyzer/output_manager.py +253 -253
- tree_sitter_analyzer/plugins/__init__.py +280 -280
- tree_sitter_analyzer/plugins/base.py +529 -529
- tree_sitter_analyzer/plugins/manager.py +379 -379
- tree_sitter_analyzer/queries/__init__.py +26 -26
- tree_sitter_analyzer/queries/java.py +391 -391
- tree_sitter_analyzer/queries/javascript.py +148 -148
- tree_sitter_analyzer/queries/python.py +285 -285
- tree_sitter_analyzer/queries/typescript.py +229 -229
- tree_sitter_analyzer/query_loader.py +257 -257
- tree_sitter_analyzer/security/boundary_manager.py +237 -279
- tree_sitter_analyzer/security/validator.py +60 -58
- tree_sitter_analyzer/utils.py +294 -277
- {tree_sitter_analyzer-0.8.3.dist-info → tree_sitter_analyzer-0.9.2.dist-info}/METADATA +28 -19
- tree_sitter_analyzer-0.9.2.dist-info/RECORD +77 -0
- {tree_sitter_analyzer-0.8.3.dist-info → tree_sitter_analyzer-0.9.2.dist-info}/entry_points.txt +1 -0
- tree_sitter_analyzer-0.8.3.dist-info/RECORD +0 -77
- {tree_sitter_analyzer-0.8.3.dist-info → tree_sitter_analyzer-0.9.2.dist-info}/WHEEL +0 -0
|
@@ -8,8 +8,6 @@ ValidationHelper but enhanced for tree-sitter analyzer's requirements.
|
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
10
|
import re
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
from typing import Optional, Tuple
|
|
13
11
|
|
|
14
12
|
from ..exceptions import SecurityError
|
|
15
13
|
from ..utils import log_debug, log_warning
|
|
@@ -20,10 +18,10 @@ from .regex_checker import RegexSafetyChecker
|
|
|
20
18
|
class SecurityValidator:
|
|
21
19
|
"""
|
|
22
20
|
Unified security validation framework.
|
|
23
|
-
|
|
21
|
+
|
|
24
22
|
This class provides comprehensive security validation for file paths,
|
|
25
23
|
regex patterns, and other user inputs to prevent security vulnerabilities.
|
|
26
|
-
|
|
24
|
+
|
|
27
25
|
Features:
|
|
28
26
|
- Multi-layer path traversal protection
|
|
29
27
|
- Project boundary enforcement
|
|
@@ -31,10 +29,10 @@ class SecurityValidator:
|
|
|
31
29
|
- Input sanitization
|
|
32
30
|
"""
|
|
33
31
|
|
|
34
|
-
def __init__(self, project_root:
|
|
32
|
+
def __init__(self, project_root: str | None = None) -> None:
|
|
35
33
|
"""
|
|
36
34
|
Initialize security validator.
|
|
37
|
-
|
|
35
|
+
|
|
38
36
|
Args:
|
|
39
37
|
project_root: Optional project root directory for boundary checks
|
|
40
38
|
"""
|
|
@@ -42,25 +40,25 @@ class SecurityValidator:
|
|
|
42
40
|
ProjectBoundaryManager(project_root) if project_root else None
|
|
43
41
|
)
|
|
44
42
|
self.regex_checker = RegexSafetyChecker()
|
|
45
|
-
|
|
43
|
+
|
|
46
44
|
log_debug(f"SecurityValidator initialized with project_root: {project_root}")
|
|
47
45
|
|
|
48
46
|
def validate_file_path(
|
|
49
|
-
self, file_path: str, base_path:
|
|
50
|
-
) ->
|
|
47
|
+
self, file_path: str, base_path: str | None = None
|
|
48
|
+
) -> tuple[bool, str]:
|
|
51
49
|
"""
|
|
52
50
|
Validate file path with comprehensive security checks.
|
|
53
|
-
|
|
51
|
+
|
|
54
52
|
Implements multi-layer defense against path traversal attacks
|
|
55
53
|
and ensures file access stays within project boundaries.
|
|
56
|
-
|
|
54
|
+
|
|
57
55
|
Args:
|
|
58
56
|
file_path: File path to validate
|
|
59
57
|
base_path: Optional base path for relative path validation
|
|
60
|
-
|
|
58
|
+
|
|
61
59
|
Returns:
|
|
62
60
|
Tuple of (is_valid, error_message)
|
|
63
|
-
|
|
61
|
+
|
|
64
62
|
Example:
|
|
65
63
|
>>> validator = SecurityValidator("/project/root")
|
|
66
64
|
>>> is_valid, error = validator.validate_file_path("src/main.py")
|
|
@@ -70,71 +68,75 @@ class SecurityValidator:
|
|
|
70
68
|
# Layer 1: Basic input validation
|
|
71
69
|
if not file_path or not isinstance(file_path, str):
|
|
72
70
|
return False, "File path must be a non-empty string"
|
|
73
|
-
|
|
71
|
+
|
|
74
72
|
# Layer 2: Null byte injection check
|
|
75
73
|
if "\x00" in file_path:
|
|
76
74
|
log_warning(f"Null byte detected in file path: {file_path}")
|
|
77
75
|
return False, "File path contains null bytes"
|
|
78
|
-
|
|
76
|
+
|
|
79
77
|
# Layer 3: Windows drive letter check (only on non-Windows systems)
|
|
80
|
-
if len(file_path) > 1 and file_path[1] == ":" and os.name !=
|
|
78
|
+
if len(file_path) > 1 and file_path[1] == ":" and os.name != "nt":
|
|
81
79
|
return False, "Windows drive letters are not allowed on this system"
|
|
82
80
|
|
|
83
|
-
# Layer 4: Absolute path check (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
87
|
-
if is_abs:
|
|
88
|
-
# If we have a project root, check if the absolute path is within it
|
|
81
|
+
# Layer 4: Absolute path check (cross-platform)
|
|
82
|
+
if os.path.isabs(file_path) or file_path.startswith(("/", "\\")):
|
|
83
|
+
# If project boundaries are configured, enforce them strictly
|
|
89
84
|
if self.boundary_manager and self.boundary_manager.project_root:
|
|
90
85
|
if not self.boundary_manager.is_within_project(file_path):
|
|
91
86
|
return False, "Absolute path must be within project directory"
|
|
87
|
+
# Within project
|
|
88
|
+
return True, ""
|
|
92
89
|
else:
|
|
93
|
-
# In test
|
|
90
|
+
# In test/dev contexts without project boundaries, allow absolute
|
|
91
|
+
# paths under system temp folder only (safe sandbox)
|
|
94
92
|
import tempfile
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
|
|
94
|
+
temp_dir = os.path.realpath(tempfile.gettempdir())
|
|
95
|
+
real_path = os.path.realpath(file_path)
|
|
96
|
+
if real_path.startswith(temp_dir + os.sep) or real_path == temp_dir:
|
|
97
97
|
return True, ""
|
|
98
|
-
# No project root defined, reject all other absolute paths
|
|
99
98
|
return False, "Absolute file paths are not allowed"
|
|
100
|
-
|
|
99
|
+
|
|
101
100
|
# Layer 5: Path normalization and traversal check
|
|
102
101
|
norm_path = os.path.normpath(file_path)
|
|
103
102
|
if "..\\" in norm_path or "../" in norm_path or norm_path.startswith(".."):
|
|
104
103
|
log_warning(f"Path traversal attempt detected: {file_path}")
|
|
105
104
|
return False, "Directory traversal not allowed"
|
|
106
|
-
|
|
105
|
+
|
|
107
106
|
# Layer 6: Project boundary validation
|
|
108
107
|
if self.boundary_manager and base_path:
|
|
109
108
|
if not self.boundary_manager.is_within_project(
|
|
110
109
|
os.path.join(base_path, norm_path)
|
|
111
110
|
):
|
|
112
|
-
return
|
|
113
|
-
|
|
111
|
+
return (
|
|
112
|
+
False,
|
|
113
|
+
"Access denied. File path must be within project directory",
|
|
114
|
+
)
|
|
115
|
+
|
|
114
116
|
# Layer 7: Symbolic link check (if file exists)
|
|
115
117
|
if base_path:
|
|
116
118
|
full_path = os.path.join(base_path, norm_path)
|
|
117
119
|
if os.path.exists(full_path) and os.path.islink(full_path):
|
|
118
120
|
log_warning(f"Symbolic link detected: {full_path}")
|
|
119
121
|
return False, "Symbolic links are not allowed"
|
|
120
|
-
|
|
122
|
+
|
|
121
123
|
log_debug(f"File path validation passed: {file_path}")
|
|
122
124
|
return True, ""
|
|
123
|
-
|
|
125
|
+
|
|
124
126
|
except Exception as e:
|
|
125
127
|
log_warning(f"File path validation error: {e}")
|
|
126
128
|
return False, f"Validation error: {str(e)}"
|
|
127
129
|
|
|
128
130
|
def validate_directory_path(
|
|
129
131
|
self, dir_path: str, must_exist: bool = True
|
|
130
|
-
) ->
|
|
132
|
+
) -> tuple[bool, str]:
|
|
131
133
|
"""
|
|
132
134
|
Validate directory path for security and existence.
|
|
133
|
-
|
|
135
|
+
|
|
134
136
|
Args:
|
|
135
137
|
dir_path: Directory path to validate
|
|
136
138
|
must_exist: Whether directory must exist
|
|
137
|
-
|
|
139
|
+
|
|
138
140
|
Returns:
|
|
139
141
|
Tuple of (is_valid, error_message)
|
|
140
142
|
"""
|
|
@@ -143,29 +145,29 @@ class SecurityValidator:
|
|
|
143
145
|
is_valid, error = self.validate_file_path(dir_path)
|
|
144
146
|
if not is_valid:
|
|
145
147
|
return False, error
|
|
146
|
-
|
|
148
|
+
|
|
147
149
|
# Check if path exists and is directory
|
|
148
150
|
if must_exist:
|
|
149
151
|
if not os.path.exists(dir_path):
|
|
150
152
|
return False, f"Directory does not exist: {dir_path}"
|
|
151
|
-
|
|
153
|
+
|
|
152
154
|
if not os.path.isdir(dir_path):
|
|
153
155
|
return False, f"Path is not a directory: {dir_path}"
|
|
154
|
-
|
|
156
|
+
|
|
155
157
|
log_debug(f"Directory path validation passed: {dir_path}")
|
|
156
158
|
return True, ""
|
|
157
|
-
|
|
159
|
+
|
|
158
160
|
except Exception as e:
|
|
159
161
|
log_warning(f"Directory path validation error: {e}")
|
|
160
162
|
return False, f"Validation error: {str(e)}"
|
|
161
163
|
|
|
162
|
-
def validate_regex_pattern(self, pattern: str) ->
|
|
164
|
+
def validate_regex_pattern(self, pattern: str) -> tuple[bool, str]:
|
|
163
165
|
"""
|
|
164
166
|
Validate regex pattern for ReDoS attack prevention.
|
|
165
|
-
|
|
167
|
+
|
|
166
168
|
Args:
|
|
167
169
|
pattern: Regex pattern to validate
|
|
168
|
-
|
|
170
|
+
|
|
169
171
|
Returns:
|
|
170
172
|
Tuple of (is_valid, error_message)
|
|
171
173
|
"""
|
|
@@ -174,31 +176,31 @@ class SecurityValidator:
|
|
|
174
176
|
def sanitize_input(self, user_input: str, max_length: int = 1000) -> str:
|
|
175
177
|
"""
|
|
176
178
|
Sanitize user input by removing dangerous characters.
|
|
177
|
-
|
|
179
|
+
|
|
178
180
|
Args:
|
|
179
181
|
user_input: Input string to sanitize
|
|
180
182
|
max_length: Maximum allowed length
|
|
181
|
-
|
|
183
|
+
|
|
182
184
|
Returns:
|
|
183
185
|
Sanitized input string
|
|
184
|
-
|
|
186
|
+
|
|
185
187
|
Raises:
|
|
186
188
|
SecurityError: If input is too long or contains dangerous content
|
|
187
189
|
"""
|
|
188
190
|
if not isinstance(user_input, str):
|
|
189
191
|
raise SecurityError("Input must be a string")
|
|
190
|
-
|
|
192
|
+
|
|
191
193
|
if len(user_input) > max_length:
|
|
192
194
|
raise SecurityError(f"Input too long: {len(user_input)} > {max_length}")
|
|
193
|
-
|
|
195
|
+
|
|
194
196
|
# Remove null bytes and control characters
|
|
195
|
-
sanitized = re.sub(r
|
|
197
|
+
sanitized = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]", "", user_input)
|
|
196
198
|
|
|
197
199
|
# Remove HTML/XML tags for XSS prevention
|
|
198
|
-
sanitized = re.sub(r
|
|
200
|
+
sanitized = re.sub(r"<[^>]*>", "", sanitized)
|
|
199
201
|
|
|
200
202
|
# Remove potentially dangerous characters
|
|
201
|
-
sanitized = re.sub(r'[<>"\']',
|
|
203
|
+
sanitized = re.sub(r'[<>"\']', "", sanitized)
|
|
202
204
|
|
|
203
205
|
# Log if sanitization occurred
|
|
204
206
|
if sanitized != user_input:
|
|
@@ -206,13 +208,13 @@ class SecurityValidator:
|
|
|
206
208
|
|
|
207
209
|
return sanitized
|
|
208
210
|
|
|
209
|
-
def validate_glob_pattern(self, pattern: str) ->
|
|
211
|
+
def validate_glob_pattern(self, pattern: str) -> tuple[bool, str]:
|
|
210
212
|
"""
|
|
211
213
|
Validate glob pattern for safe file matching.
|
|
212
|
-
|
|
214
|
+
|
|
213
215
|
Args:
|
|
214
216
|
pattern: Glob pattern to validate
|
|
215
|
-
|
|
217
|
+
|
|
216
218
|
Returns:
|
|
217
219
|
Tuple of (is_valid, error_message)
|
|
218
220
|
"""
|
|
@@ -220,25 +222,25 @@ class SecurityValidator:
|
|
|
220
222
|
# Basic input validation
|
|
221
223
|
if not pattern or not isinstance(pattern, str):
|
|
222
224
|
return False, "Pattern must be a non-empty string"
|
|
223
|
-
|
|
225
|
+
|
|
224
226
|
# Check for dangerous patterns
|
|
225
227
|
dangerous_patterns = [
|
|
226
228
|
"..", # Path traversal
|
|
227
229
|
"//", # Double slashes
|
|
228
230
|
"\\\\", # Double backslashes
|
|
229
231
|
]
|
|
230
|
-
|
|
232
|
+
|
|
231
233
|
for dangerous in dangerous_patterns:
|
|
232
234
|
if dangerous in pattern:
|
|
233
235
|
return False, f"Dangerous pattern detected: {dangerous}"
|
|
234
|
-
|
|
236
|
+
|
|
235
237
|
# Validate length
|
|
236
238
|
if len(pattern) > 500:
|
|
237
239
|
return False, "Pattern too long"
|
|
238
|
-
|
|
240
|
+
|
|
239
241
|
log_debug(f"Glob pattern validation passed: {pattern}")
|
|
240
242
|
return True, ""
|
|
241
|
-
|
|
243
|
+
|
|
242
244
|
except Exception as e:
|
|
243
245
|
log_warning(f"Glob pattern validation error: {e}")
|
|
244
246
|
return False, f"Validation error: {str(e)}"
|