crackerjack 0.33.0__py3-none-any.whl → 0.33.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 crackerjack might be problematic. Click here for more details.
- crackerjack/__main__.py +1350 -34
- crackerjack/adapters/__init__.py +17 -0
- crackerjack/adapters/lsp_client.py +358 -0
- crackerjack/adapters/rust_tool_adapter.py +194 -0
- crackerjack/adapters/rust_tool_manager.py +193 -0
- crackerjack/adapters/skylos_adapter.py +231 -0
- crackerjack/adapters/zuban_adapter.py +560 -0
- crackerjack/agents/base.py +7 -3
- crackerjack/agents/coordinator.py +271 -33
- crackerjack/agents/documentation_agent.py +9 -15
- crackerjack/agents/dry_agent.py +3 -15
- crackerjack/agents/formatting_agent.py +1 -1
- crackerjack/agents/import_optimization_agent.py +36 -180
- crackerjack/agents/performance_agent.py +17 -98
- crackerjack/agents/performance_helpers.py +7 -31
- crackerjack/agents/proactive_agent.py +1 -3
- crackerjack/agents/refactoring_agent.py +16 -85
- crackerjack/agents/refactoring_helpers.py +7 -42
- crackerjack/agents/security_agent.py +9 -48
- crackerjack/agents/test_creation_agent.py +356 -513
- crackerjack/agents/test_specialist_agent.py +0 -4
- crackerjack/api.py +6 -25
- crackerjack/cli/cache_handlers.py +204 -0
- crackerjack/cli/cache_handlers_enhanced.py +683 -0
- crackerjack/cli/facade.py +100 -0
- crackerjack/cli/handlers.py +224 -9
- crackerjack/cli/interactive.py +6 -4
- crackerjack/cli/options.py +642 -55
- crackerjack/cli/utils.py +2 -1
- crackerjack/code_cleaner.py +58 -117
- crackerjack/config/global_lock_config.py +8 -48
- crackerjack/config/hooks.py +53 -62
- crackerjack/core/async_workflow_orchestrator.py +24 -34
- crackerjack/core/autofix_coordinator.py +3 -17
- crackerjack/core/enhanced_container.py +4 -13
- crackerjack/core/file_lifecycle.py +12 -89
- crackerjack/core/performance.py +2 -2
- crackerjack/core/performance_monitor.py +15 -55
- crackerjack/core/phase_coordinator.py +104 -204
- crackerjack/core/resource_manager.py +14 -90
- crackerjack/core/service_watchdog.py +62 -95
- crackerjack/core/session_coordinator.py +149 -0
- crackerjack/core/timeout_manager.py +14 -72
- crackerjack/core/websocket_lifecycle.py +13 -78
- crackerjack/core/workflow_orchestrator.py +171 -174
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +765 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +977 -0
- crackerjack/dynamic_config.py +55 -50
- crackerjack/executors/async_hook_executor.py +10 -15
- crackerjack/executors/cached_hook_executor.py +117 -43
- crackerjack/executors/hook_executor.py +8 -34
- crackerjack/executors/hook_lock_manager.py +26 -183
- crackerjack/executors/individual_hook_executor.py +13 -11
- crackerjack/executors/lsp_aware_hook_executor.py +270 -0
- crackerjack/executors/tool_proxy.py +417 -0
- crackerjack/hooks/lsp_hook.py +79 -0
- crackerjack/intelligence/adaptive_learning.py +25 -10
- crackerjack/intelligence/agent_orchestrator.py +2 -5
- crackerjack/intelligence/agent_registry.py +34 -24
- crackerjack/intelligence/agent_selector.py +5 -7
- crackerjack/interactive.py +17 -6
- crackerjack/managers/async_hook_manager.py +0 -1
- crackerjack/managers/hook_manager.py +79 -1
- crackerjack/managers/publish_manager.py +44 -8
- crackerjack/managers/test_command_builder.py +1 -15
- crackerjack/managers/test_executor.py +1 -3
- crackerjack/managers/test_manager.py +98 -7
- crackerjack/managers/test_manager_backup.py +10 -9
- crackerjack/mcp/cache.py +2 -2
- crackerjack/mcp/client_runner.py +1 -1
- crackerjack/mcp/context.py +191 -68
- crackerjack/mcp/dashboard.py +7 -5
- crackerjack/mcp/enhanced_progress_monitor.py +31 -28
- crackerjack/mcp/file_monitor.py +30 -23
- crackerjack/mcp/progress_components.py +31 -21
- crackerjack/mcp/progress_monitor.py +50 -53
- crackerjack/mcp/rate_limiter.py +6 -6
- crackerjack/mcp/server_core.py +17 -16
- crackerjack/mcp/service_watchdog.py +2 -1
- crackerjack/mcp/state.py +4 -7
- crackerjack/mcp/task_manager.py +11 -9
- crackerjack/mcp/tools/core_tools.py +173 -32
- crackerjack/mcp/tools/error_analyzer.py +3 -2
- crackerjack/mcp/tools/execution_tools.py +8 -10
- crackerjack/mcp/tools/execution_tools_backup.py +42 -30
- crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
- crackerjack/mcp/tools/intelligence_tools.py +5 -2
- crackerjack/mcp/tools/monitoring_tools.py +33 -70
- crackerjack/mcp/tools/proactive_tools.py +24 -11
- crackerjack/mcp/tools/progress_tools.py +5 -8
- crackerjack/mcp/tools/utility_tools.py +20 -14
- crackerjack/mcp/tools/workflow_executor.py +62 -40
- crackerjack/mcp/websocket/app.py +8 -0
- crackerjack/mcp/websocket/endpoints.py +352 -357
- crackerjack/mcp/websocket/jobs.py +40 -57
- crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
- crackerjack/mcp/websocket/server.py +7 -25
- crackerjack/mcp/websocket/websocket_handler.py +6 -17
- crackerjack/mixins/__init__.py +0 -2
- crackerjack/mixins/error_handling.py +1 -70
- crackerjack/models/config.py +12 -1
- crackerjack/models/config_adapter.py +49 -1
- crackerjack/models/protocols.py +122 -122
- crackerjack/models/resource_protocols.py +55 -210
- crackerjack/monitoring/ai_agent_watchdog.py +13 -13
- crackerjack/monitoring/metrics_collector.py +426 -0
- crackerjack/monitoring/regression_prevention.py +8 -8
- crackerjack/monitoring/websocket_server.py +643 -0
- crackerjack/orchestration/advanced_orchestrator.py +11 -6
- crackerjack/orchestration/coverage_improvement.py +3 -3
- crackerjack/orchestration/execution_strategies.py +26 -6
- crackerjack/orchestration/test_progress_streamer.py +8 -5
- crackerjack/plugins/base.py +2 -2
- crackerjack/plugins/hooks.py +7 -0
- crackerjack/plugins/managers.py +11 -8
- crackerjack/security/__init__.py +0 -1
- crackerjack/security/audit.py +6 -35
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +615 -0
- crackerjack/services/backup_service.py +2 -2
- crackerjack/services/bounded_status_operations.py +15 -152
- crackerjack/services/cache.py +127 -1
- crackerjack/services/changelog_automation.py +395 -0
- crackerjack/services/config.py +15 -9
- crackerjack/services/config_merge.py +19 -80
- crackerjack/services/config_template.py +506 -0
- crackerjack/services/contextual_ai_assistant.py +48 -22
- crackerjack/services/coverage_badge_service.py +171 -0
- crackerjack/services/coverage_ratchet.py +27 -25
- crackerjack/services/debug.py +3 -3
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +14 -11
- crackerjack/services/documentation_generator.py +491 -0
- crackerjack/services/documentation_service.py +675 -0
- crackerjack/services/enhanced_filesystem.py +6 -5
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/git.py +8 -25
- crackerjack/services/health_metrics.py +10 -8
- crackerjack/services/heatmap_generator.py +735 -0
- crackerjack/services/initialization.py +11 -30
- crackerjack/services/input_validator.py +5 -97
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +15 -12
- crackerjack/services/logging.py +4 -3
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +19 -87
- crackerjack/services/metrics.py +42 -33
- crackerjack/services/parallel_executor.py +9 -67
- crackerjack/services/pattern_cache.py +1 -1
- crackerjack/services/pattern_detector.py +6 -6
- crackerjack/services/performance_benchmarks.py +18 -59
- crackerjack/services/performance_cache.py +20 -81
- crackerjack/services/performance_monitor.py +27 -95
- crackerjack/services/predictive_analytics.py +510 -0
- crackerjack/services/quality_baseline.py +234 -0
- crackerjack/services/quality_baseline_enhanced.py +646 -0
- crackerjack/services/quality_intelligence.py +785 -0
- crackerjack/services/regex_patterns.py +618 -524
- crackerjack/services/regex_utils.py +43 -123
- crackerjack/services/secure_path_utils.py +5 -164
- crackerjack/services/secure_status_formatter.py +30 -141
- crackerjack/services/secure_subprocess.py +11 -92
- crackerjack/services/security.py +9 -41
- crackerjack/services/security_logger.py +12 -24
- crackerjack/services/server_manager.py +124 -16
- crackerjack/services/status_authentication.py +16 -159
- crackerjack/services/status_security_manager.py +4 -131
- crackerjack/services/thread_safe_status_collector.py +19 -125
- crackerjack/services/unified_config.py +21 -13
- crackerjack/services/validation_rate_limiter.py +5 -54
- crackerjack/services/version_analyzer.py +459 -0
- crackerjack/services/version_checker.py +1 -1
- crackerjack/services/websocket_resource_limiter.py +10 -144
- crackerjack/services/zuban_lsp_service.py +390 -0
- crackerjack/slash_commands/__init__.py +2 -7
- crackerjack/slash_commands/run.md +2 -2
- crackerjack/tools/validate_input_validator_patterns.py +14 -40
- crackerjack/tools/validate_regex_patterns.py +19 -48
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/METADATA +196 -25
- crackerjack-0.33.2.dist-info/RECORD +229 -0
- crackerjack/CLAUDE.md +0 -207
- crackerjack/RULES.md +0 -380
- crackerjack/py313.py +0 -234
- crackerjack-0.33.0.dist-info/RECORD +0 -187
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/WHEEL +0 -0
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,12 +10,9 @@ from .security_logger import SecurityEventLevel, SecurityEventType, get_security
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class SecurePathValidator:
|
|
13
|
-
"""Comprehensive path security validation to prevent directory traversal attacks."""
|
|
14
|
-
|
|
15
13
|
MAX_FILE_SIZE = 100 * 1024 * 1024
|
|
16
14
|
MAX_PATH_LENGTH = 4096
|
|
17
15
|
|
|
18
|
-
# Enhanced dangerous components including encoded variations
|
|
19
16
|
DANGEROUS_COMPONENTS = {
|
|
20
17
|
"..",
|
|
21
18
|
".",
|
|
@@ -51,32 +48,14 @@ class SecurePathValidator:
|
|
|
51
48
|
"LPT9",
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
# Pattern constants removed - now using centralized SAFE_PATTERNS for security validation
|
|
55
|
-
|
|
56
51
|
@classmethod
|
|
57
52
|
def validate_safe_path(
|
|
58
53
|
cls, path: str | Path, base_directory: Path | None = None
|
|
59
54
|
) -> Path:
|
|
60
|
-
"""
|
|
61
|
-
Comprehensive path validation to prevent directory traversal attacks.
|
|
62
|
-
|
|
63
|
-
Args:
|
|
64
|
-
path: Path to validate (string or Path object)
|
|
65
|
-
base_directory: Optional base directory to constrain path within
|
|
66
|
-
|
|
67
|
-
Returns:
|
|
68
|
-
Validated and normalized Path object
|
|
69
|
-
|
|
70
|
-
Raises:
|
|
71
|
-
ExecutionError: If path contains malicious patterns or is invalid
|
|
72
|
-
"""
|
|
73
|
-
# Convert to string for pattern checking
|
|
74
55
|
path_str = str(path)
|
|
75
56
|
|
|
76
|
-
# Check for null bytes and dangerous patterns
|
|
77
57
|
cls._check_malicious_patterns(path_str)
|
|
78
58
|
|
|
79
|
-
# Convert to Path and normalize
|
|
80
59
|
try:
|
|
81
60
|
path_obj = Path(path_str)
|
|
82
61
|
normalized = cls.normalize_path(path_obj)
|
|
@@ -86,17 +65,14 @@ class SecurePathValidator:
|
|
|
86
65
|
error_code=ErrorCode.VALIDATION_ERROR,
|
|
87
66
|
) from e
|
|
88
67
|
|
|
89
|
-
# Validate path length
|
|
90
68
|
if len(str(normalized)) > cls.MAX_PATH_LENGTH:
|
|
91
69
|
raise ExecutionError(
|
|
92
70
|
message=f"Path too long: {len(str(normalized))} > {cls.MAX_PATH_LENGTH}",
|
|
93
71
|
error_code=ErrorCode.VALIDATION_ERROR,
|
|
94
72
|
)
|
|
95
73
|
|
|
96
|
-
# Check dangerous components
|
|
97
74
|
cls._check_dangerous_components(normalized)
|
|
98
75
|
|
|
99
|
-
# Validate within base directory if specified
|
|
100
76
|
if base_directory:
|
|
101
77
|
if not cls.is_within_directory(normalized, base_directory):
|
|
102
78
|
raise ExecutionError(
|
|
@@ -110,41 +86,23 @@ class SecurePathValidator:
|
|
|
110
86
|
def validate_file_path(
|
|
111
87
|
cls, file_path: Path, base_directory: Path | None = None
|
|
112
88
|
) -> Path:
|
|
113
|
-
"""Legacy method - redirects to validate_safe_path for consistency."""
|
|
114
89
|
return cls.validate_safe_path(file_path, base_directory)
|
|
115
90
|
|
|
116
91
|
@classmethod
|
|
117
92
|
def secure_path_join(cls, base: Path, *parts: str) -> Path:
|
|
118
|
-
"""
|
|
119
|
-
Safe alternative to Path.joinpath() that prevents directory traversal.
|
|
120
|
-
|
|
121
|
-
Args:
|
|
122
|
-
base: Base directory path
|
|
123
|
-
*parts: Path components to join
|
|
124
|
-
|
|
125
|
-
Returns:
|
|
126
|
-
Safely joined path
|
|
127
|
-
|
|
128
|
-
Raises:
|
|
129
|
-
ExecutionError: If any part contains malicious patterns
|
|
130
|
-
"""
|
|
131
|
-
# Validate base path
|
|
132
93
|
validated_base = cls.validate_safe_path(base)
|
|
133
94
|
|
|
134
|
-
# Validate each part for malicious patterns
|
|
135
95
|
for part in parts:
|
|
136
96
|
cls._check_malicious_patterns(part)
|
|
137
|
-
|
|
97
|
+
|
|
138
98
|
if Path(part).is_absolute():
|
|
139
99
|
raise ExecutionError(
|
|
140
100
|
message=f"Absolute path not allowed in join: {part}",
|
|
141
101
|
error_code=ErrorCode.VALIDATION_ERROR,
|
|
142
102
|
)
|
|
143
103
|
|
|
144
|
-
# Join paths safely
|
|
145
104
|
result = validated_base.joinpath(*parts)
|
|
146
105
|
|
|
147
|
-
# Ensure result is still within base directory
|
|
148
106
|
if not cls.is_within_directory(result, validated_base):
|
|
149
107
|
raise ExecutionError(
|
|
150
108
|
message=f"Joined path escapes base directory: {result} not within {validated_base}",
|
|
@@ -155,23 +113,9 @@ class SecurePathValidator:
|
|
|
155
113
|
|
|
156
114
|
@classmethod
|
|
157
115
|
def normalize_path(cls, path: Path) -> Path:
|
|
158
|
-
"""
|
|
159
|
-
Canonical path resolution with security checks.
|
|
160
|
-
|
|
161
|
-
Args:
|
|
162
|
-
path: Path to normalize
|
|
163
|
-
|
|
164
|
-
Returns:
|
|
165
|
-
Normalized path with symlinks resolved
|
|
166
|
-
|
|
167
|
-
Raises:
|
|
168
|
-
ExecutionError: If path resolution fails or contains malicious patterns
|
|
169
|
-
"""
|
|
170
116
|
try:
|
|
171
|
-
# Resolve symlinks and normalize
|
|
172
117
|
resolved = path.resolve()
|
|
173
118
|
|
|
174
|
-
# Additional validation after resolution
|
|
175
119
|
cls._validate_resolved_path(resolved)
|
|
176
120
|
|
|
177
121
|
return resolved
|
|
@@ -184,22 +128,10 @@ class SecurePathValidator:
|
|
|
184
128
|
|
|
185
129
|
@classmethod
|
|
186
130
|
def is_within_directory(cls, path: Path, directory: Path) -> bool:
|
|
187
|
-
"""
|
|
188
|
-
Verify that a path is contained within a directory.
|
|
189
|
-
|
|
190
|
-
Args:
|
|
191
|
-
path: Path to check
|
|
192
|
-
directory: Directory that should contain the path
|
|
193
|
-
|
|
194
|
-
Returns:
|
|
195
|
-
True if path is within directory, False otherwise
|
|
196
|
-
"""
|
|
197
131
|
try:
|
|
198
|
-
# Resolve both paths to handle symlinks
|
|
199
132
|
resolved_path = path.resolve()
|
|
200
133
|
resolved_directory = directory.resolve()
|
|
201
134
|
|
|
202
|
-
# Check if path is relative to directory
|
|
203
135
|
resolved_path.relative_to(resolved_directory)
|
|
204
136
|
return True
|
|
205
137
|
|
|
@@ -208,26 +140,10 @@ class SecurePathValidator:
|
|
|
208
140
|
|
|
209
141
|
@classmethod
|
|
210
142
|
def safe_resolve(cls, path: Path, base_directory: Path | None = None) -> Path:
|
|
211
|
-
"""
|
|
212
|
-
Secure path resolution preventing symlink attacks.
|
|
213
|
-
|
|
214
|
-
Args:
|
|
215
|
-
path: Path to resolve
|
|
216
|
-
base_directory: Optional base directory constraint
|
|
217
|
-
|
|
218
|
-
Returns:
|
|
219
|
-
Safely resolved path
|
|
220
|
-
|
|
221
|
-
Raises:
|
|
222
|
-
ExecutionError: If resolution fails or path escapes constraints
|
|
223
|
-
"""
|
|
224
|
-
# First validate the input path
|
|
225
143
|
validated_path = cls.validate_safe_path(path, base_directory)
|
|
226
144
|
|
|
227
|
-
# Resolve with additional symlink attack prevention
|
|
228
145
|
resolved = cls.normalize_path(validated_path)
|
|
229
146
|
|
|
230
|
-
# Re-validate after resolution
|
|
231
147
|
if base_directory and not cls.is_within_directory(resolved, base_directory):
|
|
232
148
|
raise ExecutionError(
|
|
233
149
|
message=f"Resolved path escapes base directory: {resolved} not within {base_directory}",
|
|
@@ -238,25 +154,18 @@ class SecurePathValidator:
|
|
|
238
154
|
|
|
239
155
|
@classmethod
|
|
240
156
|
def _check_malicious_patterns(cls, path_str: str) -> None:
|
|
241
|
-
"""Check for directory traversal and null byte patterns using safe patterns."""
|
|
242
157
|
security_logger = get_security_logger()
|
|
243
158
|
|
|
244
|
-
# URL decode the path to catch encoded attacks
|
|
245
159
|
try:
|
|
246
160
|
decoded = urllib.parse.unquote(path_str, errors="strict")
|
|
247
161
|
except UnicodeDecodeError:
|
|
248
|
-
# If decoding fails, use original string but still check patterns
|
|
249
162
|
decoded = path_str
|
|
250
163
|
|
|
251
|
-
# Check both original and decoded versions using safe patterns
|
|
252
164
|
for check_str in (path_str, decoded):
|
|
253
165
|
validation_results = validate_path_security(check_str)
|
|
254
166
|
|
|
255
|
-
# Check for null byte patterns
|
|
256
167
|
if validation_results["null_bytes"]:
|
|
257
|
-
detected_pattern = validation_results["null_bytes"][
|
|
258
|
-
0
|
|
259
|
-
] # First detected pattern
|
|
168
|
+
detected_pattern = validation_results["null_bytes"][0]
|
|
260
169
|
security_logger.log_security_event(
|
|
261
170
|
SecurityEventType.PATH_TRAVERSAL_ATTEMPT,
|
|
262
171
|
SecurityEventLevel.CRITICAL,
|
|
@@ -270,11 +179,8 @@ class SecurePathValidator:
|
|
|
270
179
|
error_code=ErrorCode.VALIDATION_ERROR,
|
|
271
180
|
)
|
|
272
181
|
|
|
273
|
-
# Check for directory traversal patterns
|
|
274
182
|
if validation_results["traversal_patterns"]:
|
|
275
|
-
detected_pattern = validation_results["traversal_patterns"][
|
|
276
|
-
0
|
|
277
|
-
] # First detected pattern
|
|
183
|
+
detected_pattern = validation_results["traversal_patterns"][0]
|
|
278
184
|
security_logger.log_path_traversal_attempt(
|
|
279
185
|
attempted_path=path_str,
|
|
280
186
|
pattern_type="directory_traversal",
|
|
@@ -287,13 +193,10 @@ class SecurePathValidator:
|
|
|
287
193
|
|
|
288
194
|
@classmethod
|
|
289
195
|
def _validate_resolved_path(cls, path: Path) -> None:
|
|
290
|
-
"""Additional validation for resolved paths using safe patterns."""
|
|
291
196
|
path_str = str(path)
|
|
292
197
|
|
|
293
|
-
# Check for dangerous patterns that might appear after resolution using safe patterns
|
|
294
198
|
validation_results = validate_path_security(path_str)
|
|
295
199
|
|
|
296
|
-
# Check for parent directory references
|
|
297
200
|
if validation_results["suspicious_patterns"]:
|
|
298
201
|
if (
|
|
299
202
|
"detect_parent_directory_in_path"
|
|
@@ -304,7 +207,6 @@ class SecurePathValidator:
|
|
|
304
207
|
error_code=ErrorCode.VALIDATION_ERROR,
|
|
305
208
|
)
|
|
306
209
|
|
|
307
|
-
# Check for suspicious traversal patterns in system directories
|
|
308
210
|
suspicious_detected = [
|
|
309
211
|
pattern
|
|
310
212
|
for pattern in validation_results["suspicious_patterns"]
|
|
@@ -380,24 +282,8 @@ class SecurePathValidator:
|
|
|
380
282
|
directory: Path | None = None,
|
|
381
283
|
purpose: str = "general",
|
|
382
284
|
) -> t.Any:
|
|
383
|
-
"""
|
|
384
|
-
Create a secure temporary file with proper permissions.
|
|
385
|
-
|
|
386
|
-
Args:
|
|
387
|
-
suffix: File suffix
|
|
388
|
-
prefix: File prefix
|
|
389
|
-
directory: Directory to create temp file in (validated if provided)
|
|
390
|
-
purpose: Purpose description for security logging
|
|
391
|
-
|
|
392
|
-
Returns:
|
|
393
|
-
Secure temporary file handle
|
|
394
|
-
|
|
395
|
-
Raises:
|
|
396
|
-
ExecutionError: If temp file creation fails
|
|
397
|
-
"""
|
|
398
285
|
security_logger = get_security_logger()
|
|
399
286
|
|
|
400
|
-
# Validate directory if provided
|
|
401
287
|
if directory:
|
|
402
288
|
directory = cls.validate_safe_path(directory)
|
|
403
289
|
|
|
@@ -406,10 +292,8 @@ class SecurePathValidator:
|
|
|
406
292
|
mode="w+b", suffix=suffix, prefix=prefix, dir=directory, delete=False
|
|
407
293
|
)
|
|
408
294
|
|
|
409
|
-
# Set restrictive permissions (owner read/write only)
|
|
410
295
|
os.chmod(temp_file.name, 0o600)
|
|
411
296
|
|
|
412
|
-
# Log secure temp file creation
|
|
413
297
|
security_logger.log_temp_file_created(
|
|
414
298
|
temp_path=temp_file.name,
|
|
415
299
|
purpose=purpose,
|
|
@@ -455,7 +339,6 @@ class AtomicFileOperations:
|
|
|
455
339
|
temp_path = Path(temp_file.name)
|
|
456
340
|
temp_path.replace(validated_path)
|
|
457
341
|
|
|
458
|
-
# Log successful atomic operation
|
|
459
342
|
security_logger.log_atomic_operation(
|
|
460
343
|
operation="write",
|
|
461
344
|
file_path=str(validated_path),
|
|
@@ -468,7 +351,6 @@ class AtomicFileOperations:
|
|
|
468
351
|
if temp_path.exists():
|
|
469
352
|
temp_path.unlink()
|
|
470
353
|
|
|
471
|
-
# Log failed atomic operation
|
|
472
354
|
security_logger.log_atomic_operation(
|
|
473
355
|
operation="write",
|
|
474
356
|
file_path=str(validated_path),
|
|
@@ -514,7 +396,6 @@ class AtomicFileOperations:
|
|
|
514
396
|
validated_path, new_content, base_directory
|
|
515
397
|
)
|
|
516
398
|
|
|
517
|
-
# Log successful backup creation
|
|
518
399
|
security_logger.log_backup_created(
|
|
519
400
|
original_path=str(validated_path),
|
|
520
401
|
backup_path=str(backup_path),
|
|
@@ -526,7 +407,6 @@ class AtomicFileOperations:
|
|
|
526
407
|
if backup_path.exists():
|
|
527
408
|
backup_path.unlink()
|
|
528
409
|
|
|
529
|
-
# Log failed backup operation
|
|
530
410
|
security_logger.log_atomic_operation(
|
|
531
411
|
operation="backup_and_write",
|
|
532
412
|
file_path=str(validated_path),
|
|
@@ -541,9 +421,6 @@ class AtomicFileOperations:
|
|
|
541
421
|
|
|
542
422
|
|
|
543
423
|
class SubprocessPathValidator:
|
|
544
|
-
"""Specialized path validation for subprocess execution contexts."""
|
|
545
|
-
|
|
546
|
-
# Paths that should never be accessible via subprocess
|
|
547
424
|
FORBIDDEN_SUBPROCESS_PATHS = {
|
|
548
425
|
"/etc/passwd",
|
|
549
426
|
"/etc/shadow",
|
|
@@ -563,32 +440,15 @@ class SubprocessPathValidator:
|
|
|
563
440
|
"/var/spool/cron",
|
|
564
441
|
}
|
|
565
442
|
|
|
566
|
-
# Directory patterns removed - now using centralized SAFE_PATTERNS for security validation
|
|
567
|
-
|
|
568
443
|
@classmethod
|
|
569
444
|
def validate_subprocess_cwd(cls, cwd: Path | str | None) -> Path | None:
|
|
570
|
-
"""
|
|
571
|
-
Validate working directory for subprocess execution.
|
|
572
|
-
|
|
573
|
-
Args:
|
|
574
|
-
cwd: Working directory path
|
|
575
|
-
|
|
576
|
-
Returns:
|
|
577
|
-
Validated Path object or None
|
|
578
|
-
|
|
579
|
-
Raises:
|
|
580
|
-
ExecutionError: If path is dangerous for subprocess execution
|
|
581
|
-
"""
|
|
582
445
|
if cwd is None:
|
|
583
446
|
return None
|
|
584
447
|
|
|
585
|
-
# Use base path validation first
|
|
586
448
|
validated_cwd = SecurePathValidator.validate_safe_path(cwd)
|
|
587
449
|
|
|
588
|
-
# Additional subprocess-specific checks
|
|
589
450
|
cwd_str = str(validated_cwd)
|
|
590
451
|
|
|
591
|
-
# Check against forbidden paths
|
|
592
452
|
if cwd_str in cls.FORBIDDEN_SUBPROCESS_PATHS:
|
|
593
453
|
security_logger = get_security_logger()
|
|
594
454
|
security_logger.log_dangerous_path_detected(
|
|
@@ -601,17 +461,14 @@ class SubprocessPathValidator:
|
|
|
601
461
|
error_code=ErrorCode.VALIDATION_ERROR,
|
|
602
462
|
)
|
|
603
463
|
|
|
604
|
-
# Check against dangerous directory patterns using safe patterns
|
|
605
464
|
validation_results = validate_path_security(cwd_str)
|
|
606
465
|
|
|
607
466
|
if validation_results["dangerous_directories"]:
|
|
608
|
-
detected_pattern = validation_results["dangerous_directories"][
|
|
609
|
-
0
|
|
610
|
-
] # First detected pattern
|
|
467
|
+
detected_pattern = validation_results["dangerous_directories"][0]
|
|
611
468
|
security_logger = get_security_logger()
|
|
612
469
|
security_logger.log_dangerous_path_detected(
|
|
613
470
|
path=cwd_str,
|
|
614
|
-
dangerous_component=f"pattern:{detected_pattern}",
|
|
471
|
+
dangerous_component=f"pattern: {detected_pattern}",
|
|
615
472
|
context="subprocess_cwd_validation",
|
|
616
473
|
)
|
|
617
474
|
raise ExecutionError(
|
|
@@ -623,31 +480,15 @@ class SubprocessPathValidator:
|
|
|
623
480
|
|
|
624
481
|
@classmethod
|
|
625
482
|
def validate_executable_path(cls, executable: str | Path) -> Path:
|
|
626
|
-
"""
|
|
627
|
-
Validate executable path for subprocess execution.
|
|
628
|
-
|
|
629
|
-
Args:
|
|
630
|
-
executable: Executable path or name
|
|
631
|
-
|
|
632
|
-
Returns:
|
|
633
|
-
Validated Path object
|
|
634
|
-
|
|
635
|
-
Raises:
|
|
636
|
-
ExecutionError: If executable is dangerous or invalid
|
|
637
|
-
"""
|
|
638
483
|
exec_path = Path(executable)
|
|
639
484
|
|
|
640
|
-
# If it's just a command name, don't validate as full path
|
|
641
485
|
if not str(executable).startswith(("/", "./", "../")):
|
|
642
486
|
return exec_path
|
|
643
487
|
|
|
644
|
-
# For full paths, apply full validation
|
|
645
488
|
validated_exec = SecurePathValidator.validate_safe_path(exec_path)
|
|
646
489
|
|
|
647
|
-
# Additional checks for executable paths
|
|
648
490
|
exec_str = str(validated_exec)
|
|
649
491
|
|
|
650
|
-
# Check if trying to execute system-critical files
|
|
651
492
|
dangerous_executables = {
|
|
652
493
|
"/usr/bin/sudo",
|
|
653
494
|
"/bin/sudo",
|