tree-sitter-analyzer 0.9.2__py3-none-any.whl → 0.9.4__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/cli/commands/base_command.py +2 -3
- tree_sitter_analyzer/cli/commands/default_command.py +18 -18
- tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -141
- tree_sitter_analyzer/cli/commands/query_command.py +92 -88
- tree_sitter_analyzer/cli/commands/table_command.py +235 -235
- tree_sitter_analyzer/cli/info_commands.py +121 -121
- tree_sitter_analyzer/cli_main.py +307 -303
- tree_sitter_analyzer/core/analysis_engine.py +584 -576
- tree_sitter_analyzer/core/cache_service.py +6 -5
- tree_sitter_analyzer/core/query.py +502 -502
- tree_sitter_analyzer/encoding_utils.py +6 -2
- tree_sitter_analyzer/exceptions.py +400 -406
- tree_sitter_analyzer/formatters/java_formatter.py +291 -291
- tree_sitter_analyzer/formatters/python_formatter.py +259 -259
- tree_sitter_analyzer/interfaces/cli.py +1 -1
- tree_sitter_analyzer/interfaces/cli_adapter.py +3 -3
- tree_sitter_analyzer/interfaces/mcp_server.py +426 -425
- tree_sitter_analyzer/language_detector.py +398 -398
- tree_sitter_analyzer/language_loader.py +224 -224
- tree_sitter_analyzer/languages/java_plugin.py +1202 -1202
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +559 -555
- tree_sitter_analyzer/mcp/server.py +30 -9
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +21 -4
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +22 -4
- tree_sitter_analyzer/mcp/utils/error_handler.py +569 -567
- tree_sitter_analyzer/models.py +470 -470
- tree_sitter_analyzer/project_detector.py +330 -317
- tree_sitter_analyzer/security/__init__.py +22 -22
- tree_sitter_analyzer/security/boundary_manager.py +243 -237
- tree_sitter_analyzer/security/regex_checker.py +297 -292
- tree_sitter_analyzer/table_formatter.py +703 -652
- tree_sitter_analyzer/utils.py +53 -22
- {tree_sitter_analyzer-0.9.2.dist-info → tree_sitter_analyzer-0.9.4.dist-info}/METADATA +13 -13
- {tree_sitter_analyzer-0.9.2.dist-info → tree_sitter_analyzer-0.9.4.dist-info}/RECORD +37 -37
- {tree_sitter_analyzer-0.9.2.dist-info → tree_sitter_analyzer-0.9.4.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.9.2.dist-info → tree_sitter_analyzer-0.9.4.dist-info}/entry_points.txt +0 -0
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Security module for Tree-sitter Analyzer
|
|
4
|
-
|
|
5
|
-
This module provides unified security validation and protection mechanisms
|
|
6
|
-
for file path validation, regex pattern safety, and project boundary control.
|
|
7
|
-
|
|
8
|
-
Architecture:
|
|
9
|
-
- SecurityValidator: Unified validation framework
|
|
10
|
-
- ProjectBoundaryManager: Project access control
|
|
11
|
-
- RegexSafetyChecker: ReDoS attack prevention
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
from .boundary_manager import ProjectBoundaryManager
|
|
15
|
-
from .regex_checker import RegexSafetyChecker
|
|
16
|
-
from .validator import SecurityValidator
|
|
17
|
-
|
|
18
|
-
__all__ = [
|
|
19
|
-
"SecurityValidator",
|
|
20
|
-
"ProjectBoundaryManager",
|
|
21
|
-
"RegexSafetyChecker",
|
|
22
|
-
]
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Security module for Tree-sitter Analyzer
|
|
4
|
+
|
|
5
|
+
This module provides unified security validation and protection mechanisms
|
|
6
|
+
for file path validation, regex pattern safety, and project boundary control.
|
|
7
|
+
|
|
8
|
+
Architecture:
|
|
9
|
+
- SecurityValidator: Unified validation framework
|
|
10
|
+
- ProjectBoundaryManager: Project access control
|
|
11
|
+
- RegexSafetyChecker: ReDoS attack prevention
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from .boundary_manager import ProjectBoundaryManager
|
|
15
|
+
from .regex_checker import RegexSafetyChecker
|
|
16
|
+
from .validator import SecurityValidator
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"SecurityValidator",
|
|
20
|
+
"ProjectBoundaryManager",
|
|
21
|
+
"RegexSafetyChecker",
|
|
22
|
+
]
|
|
@@ -1,237 +1,243 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Project Boundary Manager for Tree-sitter Analyzer
|
|
4
|
-
|
|
5
|
-
Provides strict project boundary control to prevent access to files
|
|
6
|
-
outside the designated project directory.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import os
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
from ..
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
self.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
full_path = file_path
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
"""
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
"
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Project Boundary Manager for Tree-sitter Analyzer
|
|
4
|
+
|
|
5
|
+
Provides strict project boundary control to prevent access to files
|
|
6
|
+
outside the designated project directory.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from ..exceptions import SecurityError
|
|
13
|
+
from ..utils import log_debug, log_info, log_warning
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ProjectBoundaryManager:
|
|
17
|
+
"""
|
|
18
|
+
Project boundary manager for access control.
|
|
19
|
+
|
|
20
|
+
This class enforces strict boundaries around project directories
|
|
21
|
+
to prevent unauthorized file access outside the project scope.
|
|
22
|
+
|
|
23
|
+
Features:
|
|
24
|
+
- Real path resolution for symlink protection
|
|
25
|
+
- Configurable allowed directories
|
|
26
|
+
- Comprehensive boundary checking
|
|
27
|
+
- Audit logging for security events
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, project_root: str) -> None:
|
|
31
|
+
"""
|
|
32
|
+
Initialize project boundary manager.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
project_root: Root directory of the project
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
SecurityError: If project root is invalid
|
|
39
|
+
"""
|
|
40
|
+
if not project_root:
|
|
41
|
+
raise SecurityError("Project root cannot be empty")
|
|
42
|
+
|
|
43
|
+
if not os.path.exists(project_root):
|
|
44
|
+
raise SecurityError(f"Project root does not exist: {project_root}")
|
|
45
|
+
|
|
46
|
+
if not os.path.isdir(project_root):
|
|
47
|
+
raise SecurityError(f"Project root is not a directory: {project_root}")
|
|
48
|
+
|
|
49
|
+
# Store real path to prevent symlink attacks
|
|
50
|
+
self.project_root = os.path.realpath(project_root)
|
|
51
|
+
self.allowed_directories: set[str] = {self.project_root}
|
|
52
|
+
|
|
53
|
+
log_debug(f"ProjectBoundaryManager initialized with root: {self.project_root}")
|
|
54
|
+
|
|
55
|
+
def add_allowed_directory(self, directory: str) -> None:
|
|
56
|
+
"""
|
|
57
|
+
Add an additional allowed directory.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
directory: Directory path to allow access to
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
SecurityError: If directory is invalid
|
|
64
|
+
"""
|
|
65
|
+
if not directory:
|
|
66
|
+
raise SecurityError("Directory cannot be empty")
|
|
67
|
+
|
|
68
|
+
if not os.path.exists(directory):
|
|
69
|
+
raise SecurityError(f"Directory does not exist: {directory}")
|
|
70
|
+
|
|
71
|
+
if not os.path.isdir(directory):
|
|
72
|
+
raise SecurityError(f"Path is not a directory: {directory}")
|
|
73
|
+
|
|
74
|
+
real_dir = os.path.realpath(directory)
|
|
75
|
+
self.allowed_directories.add(real_dir)
|
|
76
|
+
|
|
77
|
+
log_info(f"Added allowed directory: {real_dir}")
|
|
78
|
+
|
|
79
|
+
def is_within_project(self, file_path: str) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
Check if file path is within project boundaries.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
file_path: File path to check
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
True if path is within allowed boundaries
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
if not file_path:
|
|
91
|
+
log_warning("Empty file path provided to boundary check")
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
# Resolve real path to handle symlinks
|
|
95
|
+
real_path = os.path.realpath(file_path)
|
|
96
|
+
|
|
97
|
+
# Check against all allowed directories
|
|
98
|
+
for allowed_dir in self.allowed_directories:
|
|
99
|
+
if (
|
|
100
|
+
real_path.startswith(allowed_dir + os.sep)
|
|
101
|
+
or real_path == allowed_dir
|
|
102
|
+
):
|
|
103
|
+
log_debug(f"File path within boundaries: {file_path}")
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
log_warning(f"File path outside boundaries: {file_path} -> {real_path}")
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
except Exception as e:
|
|
110
|
+
log_warning(f"Boundary check error for {file_path}: {e}")
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
def get_relative_path(self, file_path: str) -> str | None:
|
|
114
|
+
"""
|
|
115
|
+
Get relative path from project root if within boundaries.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
file_path: File path to convert
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Relative path from project root, or None if outside boundaries
|
|
122
|
+
"""
|
|
123
|
+
if not self.is_within_project(file_path):
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
real_path = os.path.realpath(file_path)
|
|
128
|
+
rel_path = os.path.relpath(real_path, self.project_root)
|
|
129
|
+
|
|
130
|
+
# Ensure relative path doesn't start with ..
|
|
131
|
+
if rel_path.startswith(".."):
|
|
132
|
+
log_warning(f"Relative path calculation failed: {rel_path}")
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
return rel_path
|
|
136
|
+
|
|
137
|
+
except Exception as e:
|
|
138
|
+
log_warning(f"Relative path calculation error: {e}")
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
def validate_and_resolve_path(self, file_path: str) -> str | None:
|
|
142
|
+
"""
|
|
143
|
+
Validate path and return resolved absolute path if within boundaries.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
file_path: File path to validate and resolve
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Resolved absolute path if valid, None otherwise
|
|
150
|
+
"""
|
|
151
|
+
try:
|
|
152
|
+
# Handle relative paths from project root
|
|
153
|
+
if not os.path.isabs(file_path):
|
|
154
|
+
full_path = os.path.join(self.project_root, file_path)
|
|
155
|
+
else:
|
|
156
|
+
full_path = file_path
|
|
157
|
+
|
|
158
|
+
# Check boundaries
|
|
159
|
+
if not self.is_within_project(full_path):
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
# Return real path
|
|
163
|
+
return os.path.realpath(full_path)
|
|
164
|
+
|
|
165
|
+
except Exception as e:
|
|
166
|
+
log_warning(f"Path validation error: {e}")
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
def list_allowed_directories(self) -> set[str]:
|
|
170
|
+
"""
|
|
171
|
+
Get list of all allowed directories.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Set of allowed directory paths
|
|
175
|
+
"""
|
|
176
|
+
return self.allowed_directories.copy()
|
|
177
|
+
|
|
178
|
+
def is_symlink_safe(self, file_path: str) -> bool:
|
|
179
|
+
"""
|
|
180
|
+
Check if file path is safe from symlink attacks.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
file_path: File path to check
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
True if path is safe from symlink attacks
|
|
187
|
+
"""
|
|
188
|
+
try:
|
|
189
|
+
if not os.path.exists(file_path):
|
|
190
|
+
return True # Non-existent files are safe
|
|
191
|
+
|
|
192
|
+
# Check if any component in the path is a symlink
|
|
193
|
+
path_parts = Path(file_path).parts
|
|
194
|
+
current_path = ""
|
|
195
|
+
|
|
196
|
+
for part in path_parts:
|
|
197
|
+
current_path = (
|
|
198
|
+
os.path.join(current_path, part) if current_path else part
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
if os.path.islink(current_path):
|
|
202
|
+
# Check if symlink target is within boundaries
|
|
203
|
+
target = os.path.realpath(current_path)
|
|
204
|
+
if not self.is_within_project(target):
|
|
205
|
+
log_warning(
|
|
206
|
+
f"Unsafe symlink detected: {current_path} -> {target}"
|
|
207
|
+
)
|
|
208
|
+
return False
|
|
209
|
+
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
log_warning(f"Symlink safety check error: {e}")
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
def audit_access(self, file_path: str, operation: str) -> None:
|
|
217
|
+
"""
|
|
218
|
+
Log file access for security auditing.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
file_path: File path being accessed
|
|
222
|
+
operation: Type of operation (read, write, analyze, etc.)
|
|
223
|
+
"""
|
|
224
|
+
is_within = self.is_within_project(file_path)
|
|
225
|
+
status = "ALLOWED" if is_within else "DENIED"
|
|
226
|
+
|
|
227
|
+
log_info(f"AUDIT: {status} {operation} access to {file_path}")
|
|
228
|
+
|
|
229
|
+
if not is_within:
|
|
230
|
+
log_warning(f"SECURITY: Unauthorized access attempt to {file_path}")
|
|
231
|
+
|
|
232
|
+
def __str__(self) -> str:
|
|
233
|
+
"""String representation of boundary manager."""
|
|
234
|
+
return f"ProjectBoundaryManager(root={self.project_root}, allowed_dirs={len(self.allowed_directories)})"
|
|
235
|
+
|
|
236
|
+
def __repr__(self) -> str:
|
|
237
|
+
"""Detailed representation of boundary manager."""
|
|
238
|
+
return (
|
|
239
|
+
f"ProjectBoundaryManager("
|
|
240
|
+
f"project_root='{self.project_root}', "
|
|
241
|
+
f"allowed_directories={self.allowed_directories}"
|
|
242
|
+
f")"
|
|
243
|
+
)
|