pomera-ai-commander 0.1.0 → 1.2.1
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.
- package/LICENSE +21 -21
- package/README.md +105 -680
- package/bin/pomera-ai-commander.js +62 -62
- package/core/__init__.py +65 -65
- package/core/app_context.py +482 -482
- package/core/async_text_processor.py +421 -421
- package/core/backup_manager.py +655 -655
- package/core/backup_recovery_manager.py +1033 -1033
- package/core/content_hash_cache.py +508 -508
- package/core/context_menu.py +313 -313
- package/core/data_validator.py +1066 -1066
- package/core/database_connection_manager.py +744 -744
- package/core/database_curl_settings_manager.py +608 -608
- package/core/database_promera_ai_settings_manager.py +446 -446
- package/core/database_schema.py +411 -411
- package/core/database_schema_manager.py +395 -395
- package/core/database_settings_manager.py +1507 -1507
- package/core/database_settings_manager_interface.py +456 -456
- package/core/dialog_manager.py +734 -734
- package/core/efficient_line_numbers.py +510 -510
- package/core/error_handler.py +746 -746
- package/core/error_service.py +431 -431
- package/core/event_consolidator.py +511 -511
- package/core/mcp/__init__.py +43 -43
- package/core/mcp/protocol.py +288 -288
- package/core/mcp/schema.py +251 -251
- package/core/mcp/server_stdio.py +299 -299
- package/core/mcp/tool_registry.py +2372 -2345
- package/core/memory_efficient_text_widget.py +711 -711
- package/core/migration_manager.py +914 -914
- package/core/migration_test_suite.py +1085 -1085
- package/core/migration_validator.py +1143 -1143
- package/core/optimized_find_replace.py +714 -714
- package/core/optimized_pattern_engine.py +424 -424
- package/core/optimized_search_highlighter.py +552 -552
- package/core/performance_monitor.py +674 -674
- package/core/persistence_manager.py +712 -712
- package/core/progressive_stats_calculator.py +632 -632
- package/core/regex_pattern_cache.py +529 -529
- package/core/regex_pattern_library.py +350 -350
- package/core/search_operation_manager.py +434 -434
- package/core/settings_defaults_registry.py +1087 -1087
- package/core/settings_integrity_validator.py +1111 -1111
- package/core/settings_serializer.py +557 -557
- package/core/settings_validator.py +1823 -1823
- package/core/smart_stats_calculator.py +709 -709
- package/core/statistics_update_manager.py +619 -619
- package/core/stats_config_manager.py +858 -858
- package/core/streaming_text_handler.py +723 -723
- package/core/task_scheduler.py +596 -596
- package/core/update_pattern_library.py +168 -168
- package/core/visibility_monitor.py +596 -596
- package/core/widget_cache.py +498 -498
- package/mcp.json +51 -61
- package/package.json +61 -57
- package/pomera.py +7482 -7482
- package/pomera_mcp_server.py +183 -144
- package/requirements.txt +32 -0
- package/tools/__init__.py +4 -4
- package/tools/ai_tools.py +2891 -2891
- package/tools/ascii_art_generator.py +352 -352
- package/tools/base64_tools.py +183 -183
- package/tools/base_tool.py +511 -511
- package/tools/case_tool.py +308 -308
- package/tools/column_tools.py +395 -395
- package/tools/cron_tool.py +884 -884
- package/tools/curl_history.py +600 -600
- package/tools/curl_processor.py +1207 -1207
- package/tools/curl_settings.py +502 -502
- package/tools/curl_tool.py +5467 -5467
- package/tools/diff_viewer.py +1071 -1071
- package/tools/email_extraction_tool.py +248 -248
- package/tools/email_header_analyzer.py +425 -425
- package/tools/extraction_tools.py +250 -250
- package/tools/find_replace.py +1750 -1750
- package/tools/folder_file_reporter.py +1463 -1463
- package/tools/folder_file_reporter_adapter.py +480 -480
- package/tools/generator_tools.py +1216 -1216
- package/tools/hash_generator.py +255 -255
- package/tools/html_tool.py +656 -656
- package/tools/jsonxml_tool.py +729 -729
- package/tools/line_tools.py +419 -419
- package/tools/markdown_tools.py +561 -561
- package/tools/mcp_widget.py +1417 -1417
- package/tools/notes_widget.py +973 -973
- package/tools/number_base_converter.py +372 -372
- package/tools/regex_extractor.py +571 -571
- package/tools/slug_generator.py +310 -310
- package/tools/sorter_tools.py +458 -458
- package/tools/string_escape_tool.py +392 -392
- package/tools/text_statistics_tool.py +365 -365
- package/tools/text_wrapper.py +430 -430
- package/tools/timestamp_converter.py +421 -421
- package/tools/tool_loader.py +710 -710
- package/tools/translator_tools.py +522 -522
- package/tools/url_link_extractor.py +261 -261
- package/tools/url_parser.py +204 -204
- package/tools/whitespace_tools.py +355 -355
- package/tools/word_frequency_counter.py +146 -146
- package/core/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/__pycache__/app_context.cpython-313.pyc +0 -0
- package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
- package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
- package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
- package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/error_service.cpython-313.pyc +0 -0
- package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
- package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
- package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
- package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
- package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
- package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
- package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
- package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
- package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
- package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
- package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
- package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
- package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
|
@@ -1,1824 +1,1824 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Settings Validation and Integrity Checking System
|
|
3
|
-
|
|
4
|
-
This module provides comprehensive validation for settings data before storage,
|
|
5
|
-
including type validation, data integrity checks for critical settings,
|
|
6
|
-
validation rules for encrypted data, and consistency checks across related settings.
|
|
7
|
-
|
|
8
|
-
Based on production codebase analysis of 45 Python files with complex settings patterns.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import re
|
|
12
|
-
import json
|
|
13
|
-
import logging
|
|
14
|
-
from typing import Any, Dict, List, Tuple, Optional, Set, Union, Callable, TYPE_CHECKING
|
|
15
|
-
from datetime import datetime
|
|
16
|
-
from pathlib import Path
|
|
17
|
-
|
|
18
|
-
if TYPE_CHECKING:
|
|
19
|
-
from typing import Self
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class SettingsValidator:
|
|
23
|
-
"""
|
|
24
|
-
Comprehensive validator for settings data with integrity checking.
|
|
25
|
-
|
|
26
|
-
Features:
|
|
27
|
-
- Type validation for all setting values before storage
|
|
28
|
-
- Data integrity checks for critical settings (tab counts, required fields)
|
|
29
|
-
- Validation rules for encrypted data and sensitive information
|
|
30
|
-
- Consistency checks across related settings
|
|
31
|
-
- Custom validation rules for specific tools and configurations
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
def __init__(self):
|
|
35
|
-
"""Initialize the settings validator."""
|
|
36
|
-
self.logger = logging.getLogger(__name__)
|
|
37
|
-
|
|
38
|
-
# Validation rules registry
|
|
39
|
-
self.validation_rules = {}
|
|
40
|
-
self.integrity_rules = {}
|
|
41
|
-
self.consistency_rules = {}
|
|
42
|
-
|
|
43
|
-
# Initialize built-in validation rules
|
|
44
|
-
self._initialize_core_validation_rules()
|
|
45
|
-
self._initialize_tool_validation_rules()
|
|
46
|
-
self._initialize_integrity_rules()
|
|
47
|
-
self._initialize_consistency_rules()
|
|
48
|
-
|
|
49
|
-
# Encryption validation patterns
|
|
50
|
-
self.encryption_pattern = re.compile(r'^ENC:[A-Za-z0-9+/=]+$')
|
|
51
|
-
|
|
52
|
-
# Critical settings that must always be valid
|
|
53
|
-
self.critical_settings = {
|
|
54
|
-
'export_path',
|
|
55
|
-
'debug_level',
|
|
56
|
-
'selected_tool',
|
|
57
|
-
'active_input_tab',
|
|
58
|
-
'active_output_tab'
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
# Required tool settings
|
|
62
|
-
self.required_tool_settings = {
|
|
63
|
-
'cURL Tool': {'default_timeout', 'follow_redirects', 'verify_ssl'},
|
|
64
|
-
'JSON/XML Tool': {'operation', 'json_indent', 'xml_indent'},
|
|
65
|
-
'Case Tool': {'mode'},
|
|
66
|
-
'Find & Replace': {'case_sensitive', 'use_regex'}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
def validate_setting_value(self, key: str, value: Any, context: Optional[Dict[str, Any]] = None) -> 'ValidationResult':
|
|
70
|
-
"""
|
|
71
|
-
Validate a single setting value with comprehensive checks.
|
|
72
|
-
|
|
73
|
-
Args:
|
|
74
|
-
key: Setting key/path
|
|
75
|
-
value: Setting value to validate
|
|
76
|
-
context: Additional context for validation (tool name, related settings, etc.)
|
|
77
|
-
|
|
78
|
-
Returns:
|
|
79
|
-
ValidationResult with validation status and details
|
|
80
|
-
"""
|
|
81
|
-
result = ValidationResult(key, value)
|
|
82
|
-
|
|
83
|
-
try:
|
|
84
|
-
# Basic type validation
|
|
85
|
-
type_result = self._validate_type(key, value, context)
|
|
86
|
-
result.add_check('type_validation', type_result)
|
|
87
|
-
|
|
88
|
-
# Value range/format validation
|
|
89
|
-
format_result = self._validate_format(key, value, context)
|
|
90
|
-
result.add_check('format_validation', format_result)
|
|
91
|
-
|
|
92
|
-
# Security validation (for sensitive data)
|
|
93
|
-
security_result = self._validate_security(key, value, context)
|
|
94
|
-
result.add_check('security_validation', security_result)
|
|
95
|
-
|
|
96
|
-
# Custom validation rules
|
|
97
|
-
custom_result = self._validate_custom_rules(key, value, context)
|
|
98
|
-
result.add_check('custom_validation', custom_result)
|
|
99
|
-
|
|
100
|
-
# Mark as critical if this is a critical setting
|
|
101
|
-
if key in self.critical_settings:
|
|
102
|
-
result.is_critical = True
|
|
103
|
-
|
|
104
|
-
self.logger.debug(f"Validated setting '{key}': {result.is_valid}")
|
|
105
|
-
return result
|
|
106
|
-
|
|
107
|
-
except Exception as e:
|
|
108
|
-
self.logger.error(f"Validation failed for setting '{key}': {e}")
|
|
109
|
-
result.add_error(f"Validation exception: {e}")
|
|
110
|
-
return result
|
|
111
|
-
|
|
112
|
-
def validate_tool_settings(self, tool_name: str, settings: Dict[str, Any]) -> 'ToolValidationResult':
|
|
113
|
-
"""
|
|
114
|
-
Validate all settings for a specific tool.
|
|
115
|
-
|
|
116
|
-
Args:
|
|
117
|
-
tool_name: Name of the tool
|
|
118
|
-
settings: Tool settings dictionary
|
|
119
|
-
|
|
120
|
-
Returns:
|
|
121
|
-
ToolValidationResult with comprehensive validation status
|
|
122
|
-
"""
|
|
123
|
-
result = ToolValidationResult(tool_name, settings)
|
|
124
|
-
|
|
125
|
-
try:
|
|
126
|
-
# Check required settings
|
|
127
|
-
required_result = self._check_required_settings(tool_name, settings)
|
|
128
|
-
result.add_check('required_settings', required_result)
|
|
129
|
-
|
|
130
|
-
# Validate individual settings
|
|
131
|
-
for key, value in settings.items():
|
|
132
|
-
context = {'tool_name': tool_name, 'all_settings': settings}
|
|
133
|
-
setting_result = self.validate_setting_value(f"{tool_name}.{key}", value, context)
|
|
134
|
-
result.add_setting_result(key, setting_result)
|
|
135
|
-
|
|
136
|
-
# Tool-specific integrity checks
|
|
137
|
-
integrity_result = self._check_tool_integrity(tool_name, settings)
|
|
138
|
-
result.add_check('integrity_check', integrity_result)
|
|
139
|
-
|
|
140
|
-
self.logger.debug(f"Validated tool '{tool_name}': {result.is_valid}")
|
|
141
|
-
return result
|
|
142
|
-
|
|
143
|
-
except Exception as e:
|
|
144
|
-
self.logger.error(f"Tool validation failed for '{tool_name}': {e}")
|
|
145
|
-
result.add_error(f"Tool validation exception: {e}")
|
|
146
|
-
return result
|
|
147
|
-
|
|
148
|
-
def validate_settings_consistency(self, all_settings: Dict[str, Any]) -> 'ConsistencyValidationResult':
|
|
149
|
-
"""
|
|
150
|
-
Validate consistency across all settings.
|
|
151
|
-
|
|
152
|
-
Args:
|
|
153
|
-
all_settings: Complete settings dictionary
|
|
154
|
-
|
|
155
|
-
Returns:
|
|
156
|
-
ConsistencyValidationResult with cross-setting validation status
|
|
157
|
-
"""
|
|
158
|
-
result = ConsistencyValidationResult(all_settings)
|
|
159
|
-
|
|
160
|
-
try:
|
|
161
|
-
# Tab consistency checks
|
|
162
|
-
tab_result = self._validate_tab_consistency(all_settings)
|
|
163
|
-
result.add_check('tab_consistency', tab_result)
|
|
164
|
-
|
|
165
|
-
# Tool selection consistency
|
|
166
|
-
tool_result = self._validate_tool_selection_consistency(all_settings)
|
|
167
|
-
result.add_check('tool_selection', tool_result)
|
|
168
|
-
|
|
169
|
-
# Performance settings consistency
|
|
170
|
-
perf_result = self._validate_performance_consistency(all_settings)
|
|
171
|
-
result.add_check('performance_consistency', perf_result)
|
|
172
|
-
|
|
173
|
-
# Font settings consistency
|
|
174
|
-
font_result = self._validate_font_consistency(all_settings)
|
|
175
|
-
result.add_check('font_consistency', font_result)
|
|
176
|
-
|
|
177
|
-
# Dialog settings consistency
|
|
178
|
-
dialog_result = self._validate_dialog_consistency(all_settings)
|
|
179
|
-
result.add_check('dialog_consistency', dialog_result)
|
|
180
|
-
|
|
181
|
-
# Integrity checks
|
|
182
|
-
for rule_name, rule_func in self.integrity_rules.items():
|
|
183
|
-
integrity_result = rule_func(all_settings)
|
|
184
|
-
result.add_check(rule_name, integrity_result)
|
|
185
|
-
|
|
186
|
-
# Custom consistency rules
|
|
187
|
-
for rule_name, rule_func in self.consistency_rules.items():
|
|
188
|
-
custom_result = rule_func(all_settings)
|
|
189
|
-
result.add_check(rule_name, custom_result)
|
|
190
|
-
|
|
191
|
-
self.logger.debug(f"Settings consistency validation: {result.is_valid}")
|
|
192
|
-
return result
|
|
193
|
-
|
|
194
|
-
except Exception as e:
|
|
195
|
-
self.logger.error(f"Consistency validation failed: {e}")
|
|
196
|
-
result.add_error(f"Consistency validation exception: {e}")
|
|
197
|
-
return result
|
|
198
|
-
|
|
199
|
-
def validate_complete_settings(self, settings: Dict[str, Any]) -> 'CompleteValidationResult':
|
|
200
|
-
"""
|
|
201
|
-
Perform complete validation of all settings including type validation,
|
|
202
|
-
integrity checks, and consistency validation.
|
|
203
|
-
|
|
204
|
-
Args:
|
|
205
|
-
settings: Complete settings dictionary
|
|
206
|
-
|
|
207
|
-
Returns:
|
|
208
|
-
CompleteValidationResult with comprehensive validation status
|
|
209
|
-
"""
|
|
210
|
-
result = CompleteValidationResult(settings)
|
|
211
|
-
|
|
212
|
-
try:
|
|
213
|
-
# 1. Validate core settings
|
|
214
|
-
core_settings = {k: v for k, v in settings.items()
|
|
215
|
-
if k not in ('tool_settings', 'performance_settings', 'font_settings', 'dialog_settings')}
|
|
216
|
-
|
|
217
|
-
for key, value in core_settings.items():
|
|
218
|
-
setting_result = self.validate_setting_value(key, value)
|
|
219
|
-
result.add_core_setting_result(key, setting_result)
|
|
220
|
-
|
|
221
|
-
# 2. Validate tool settings
|
|
222
|
-
tool_settings = settings.get('tool_settings', {})
|
|
223
|
-
if isinstance(tool_settings, dict):
|
|
224
|
-
for tool_name, tool_config in tool_settings.items():
|
|
225
|
-
if isinstance(tool_config, dict):
|
|
226
|
-
tool_result = self.validate_tool_settings(tool_name, tool_config)
|
|
227
|
-
result.add_tool_result(tool_name, tool_result)
|
|
228
|
-
|
|
229
|
-
# 3. Validate consistency across all settings
|
|
230
|
-
consistency_result = self.validate_settings_consistency(settings)
|
|
231
|
-
result.add_consistency_result(consistency_result)
|
|
232
|
-
|
|
233
|
-
self.logger.info(f"Complete settings validation: {result.is_valid} ({len(result.errors)} errors, {len(result.warnings)} warnings)")
|
|
234
|
-
return result
|
|
235
|
-
|
|
236
|
-
except Exception as e:
|
|
237
|
-
self.logger.error(f"Complete validation failed: {e}")
|
|
238
|
-
result.add_error(f"Complete validation exception: {e}")
|
|
239
|
-
return result
|
|
240
|
-
|
|
241
|
-
def _validate_type(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
|
|
242
|
-
"""Validate the type of a setting value."""
|
|
243
|
-
result = CheckResult('type_validation')
|
|
244
|
-
|
|
245
|
-
try:
|
|
246
|
-
# Get expected type for this key
|
|
247
|
-
expected_type = self._get_expected_type(key, context)
|
|
248
|
-
|
|
249
|
-
if expected_type is None:
|
|
250
|
-
# No specific type requirement
|
|
251
|
-
result.passed = True
|
|
252
|
-
result.message = "No type constraint"
|
|
253
|
-
return result
|
|
254
|
-
|
|
255
|
-
# Check type compatibility
|
|
256
|
-
if isinstance(expected_type, tuple):
|
|
257
|
-
# Multiple allowed types
|
|
258
|
-
if any(isinstance(value, t) for t in expected_type):
|
|
259
|
-
result.passed = True
|
|
260
|
-
result.message = f"Type {type(value).__name__} is allowed"
|
|
261
|
-
else:
|
|
262
|
-
result.passed = False
|
|
263
|
-
result.message = f"Type {type(value).__name__} not in allowed types {expected_type}"
|
|
264
|
-
else:
|
|
265
|
-
# Single expected type
|
|
266
|
-
if isinstance(value, expected_type):
|
|
267
|
-
result.passed = True
|
|
268
|
-
result.message = f"Type {type(value).__name__} matches expected {expected_type.__name__}"
|
|
269
|
-
else:
|
|
270
|
-
result.passed = False
|
|
271
|
-
result.message = f"Type {type(value).__name__} does not match expected {expected_type.__name__}"
|
|
272
|
-
|
|
273
|
-
return result
|
|
274
|
-
|
|
275
|
-
except Exception as e:
|
|
276
|
-
result.passed = False
|
|
277
|
-
result.message = f"Type validation error: {e}"
|
|
278
|
-
return result
|
|
279
|
-
|
|
280
|
-
def _validate_format(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
|
|
281
|
-
"""Validate the format/range of a setting value."""
|
|
282
|
-
result = CheckResult('format_validation')
|
|
283
|
-
|
|
284
|
-
try:
|
|
285
|
-
# Get validation rule for this key
|
|
286
|
-
rule = self.validation_rules.get(key)
|
|
287
|
-
|
|
288
|
-
if rule is None:
|
|
289
|
-
# Check for pattern-based rules
|
|
290
|
-
rule = self._get_pattern_rule(key, context)
|
|
291
|
-
|
|
292
|
-
if rule is None:
|
|
293
|
-
result.passed = True
|
|
294
|
-
result.message = "No format constraint"
|
|
295
|
-
return result
|
|
296
|
-
|
|
297
|
-
# Apply validation rule
|
|
298
|
-
if callable(rule):
|
|
299
|
-
rule_result = rule(value, context)
|
|
300
|
-
if isinstance(rule_result, bool):
|
|
301
|
-
result.passed = rule_result
|
|
302
|
-
result.message = "Custom rule validation"
|
|
303
|
-
elif isinstance(rule_result, dict):
|
|
304
|
-
result.passed = rule_result.get('valid', False)
|
|
305
|
-
result.message = rule_result.get('message', 'Custom rule validation')
|
|
306
|
-
else:
|
|
307
|
-
result.passed = bool(rule_result)
|
|
308
|
-
result.message = "Custom rule validation"
|
|
309
|
-
else:
|
|
310
|
-
# Pattern-based rule
|
|
311
|
-
if isinstance(value, str) and hasattr(rule, 'match'):
|
|
312
|
-
result.passed = bool(rule.match(value))
|
|
313
|
-
result.message = f"Pattern match: {rule.pattern}"
|
|
314
|
-
else:
|
|
315
|
-
result.passed = False
|
|
316
|
-
result.message = f"Cannot apply pattern rule to {type(value).__name__}"
|
|
317
|
-
|
|
318
|
-
return result
|
|
319
|
-
|
|
320
|
-
except Exception as e:
|
|
321
|
-
result.passed = False
|
|
322
|
-
result.message = f"Format validation error: {e}"
|
|
323
|
-
return result
|
|
324
|
-
|
|
325
|
-
def _validate_security(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
|
|
326
|
-
"""Validate security aspects of a setting value."""
|
|
327
|
-
result = CheckResult('security_validation')
|
|
328
|
-
|
|
329
|
-
try:
|
|
330
|
-
# Check for sensitive data patterns
|
|
331
|
-
if self._is_sensitive_key(key):
|
|
332
|
-
if isinstance(value, str):
|
|
333
|
-
# Check if encrypted properly
|
|
334
|
-
if key.upper().endswith('KEY') or key.upper().endswith('TOKEN'):
|
|
335
|
-
if not self._is_encrypted_or_placeholder(value):
|
|
336
|
-
result.passed = False
|
|
337
|
-
result.message = "Sensitive data should be encrypted or placeholder"
|
|
338
|
-
result.severity = 'warning' # Not critical, but recommended
|
|
339
|
-
return result
|
|
340
|
-
|
|
341
|
-
# Check for obvious secrets in plain text
|
|
342
|
-
if self._contains_obvious_secret(value):
|
|
343
|
-
result.passed = False
|
|
344
|
-
result.message = "Possible plain text secret detected"
|
|
345
|
-
result.severity = 'warning'
|
|
346
|
-
return result
|
|
347
|
-
|
|
348
|
-
result.passed = True
|
|
349
|
-
result.message = "Security validation passed"
|
|
350
|
-
return result
|
|
351
|
-
|
|
352
|
-
except Exception as e:
|
|
353
|
-
result.passed = False
|
|
354
|
-
result.message = f"Security validation error: {e}"
|
|
355
|
-
return result
|
|
356
|
-
|
|
357
|
-
def _validate_custom_rules(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
|
|
358
|
-
"""Apply custom validation rules for specific settings."""
|
|
359
|
-
result = CheckResult('custom_validation')
|
|
360
|
-
|
|
361
|
-
try:
|
|
362
|
-
# Tab index validation
|
|
363
|
-
if key in ('active_input_tab', 'active_output_tab'):
|
|
364
|
-
if not isinstance(value, int) or not (0 <= value < 7):
|
|
365
|
-
result.passed = False
|
|
366
|
-
result.message = "Tab index must be integer between 0 and 6"
|
|
367
|
-
return result
|
|
368
|
-
|
|
369
|
-
# Debug level validation
|
|
370
|
-
elif key == 'debug_level':
|
|
371
|
-
valid_levels = {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'}
|
|
372
|
-
if value not in valid_levels:
|
|
373
|
-
result.passed = False
|
|
374
|
-
result.message = f"Debug level must be one of {valid_levels}"
|
|
375
|
-
return result
|
|
376
|
-
|
|
377
|
-
# Export path validation
|
|
378
|
-
elif key == 'export_path':
|
|
379
|
-
if not isinstance(value, str) or not value.strip():
|
|
380
|
-
result.passed = False
|
|
381
|
-
result.message = "Export path must be non-empty string"
|
|
382
|
-
return result
|
|
383
|
-
|
|
384
|
-
# Check if path is reasonable (not validating existence)
|
|
385
|
-
try:
|
|
386
|
-
Path(value) # This will raise if path is invalid
|
|
387
|
-
except Exception:
|
|
388
|
-
result.passed = False
|
|
389
|
-
result.message = "Export path format is invalid"
|
|
390
|
-
return result
|
|
391
|
-
|
|
392
|
-
# Tool name validation
|
|
393
|
-
elif key == 'selected_tool':
|
|
394
|
-
if not isinstance(value, str) or not value.strip():
|
|
395
|
-
result.passed = False
|
|
396
|
-
result.message = "Selected tool must be non-empty string"
|
|
397
|
-
return result
|
|
398
|
-
|
|
399
|
-
result.passed = True
|
|
400
|
-
result.message = "Custom validation passed"
|
|
401
|
-
return result
|
|
402
|
-
|
|
403
|
-
except Exception as e:
|
|
404
|
-
result.passed = False
|
|
405
|
-
result.message = f"Custom validation error: {e}"
|
|
406
|
-
return result
|
|
407
|
-
|
|
408
|
-
def _check_required_settings(self, tool_name: str, settings: Dict[str, Any]) -> 'CheckResult':
|
|
409
|
-
"""Check that all required settings are present for a tool."""
|
|
410
|
-
result = CheckResult('required_settings')
|
|
411
|
-
|
|
412
|
-
try:
|
|
413
|
-
required = self.required_tool_settings.get(tool_name, set())
|
|
414
|
-
missing = required - set(settings.keys())
|
|
415
|
-
|
|
416
|
-
if missing:
|
|
417
|
-
result.passed = False
|
|
418
|
-
result.message = f"Missing required settings: {missing}"
|
|
419
|
-
else:
|
|
420
|
-
result.passed = True
|
|
421
|
-
result.message = "All required settings present"
|
|
422
|
-
|
|
423
|
-
return result
|
|
424
|
-
|
|
425
|
-
except Exception as e:
|
|
426
|
-
result.passed = False
|
|
427
|
-
result.message = f"Required settings check error: {e}"
|
|
428
|
-
return result
|
|
429
|
-
|
|
430
|
-
def _check_tool_integrity(self, tool_name: str, settings: Dict[str, Any]) -> 'CheckResult':
|
|
431
|
-
"""Check tool-specific integrity constraints."""
|
|
432
|
-
result = CheckResult('tool_integrity')
|
|
433
|
-
|
|
434
|
-
try:
|
|
435
|
-
# cURL Tool specific checks
|
|
436
|
-
if tool_name == 'cURL Tool':
|
|
437
|
-
return self._validate_curl_tool_integrity(settings)
|
|
438
|
-
|
|
439
|
-
# AI Tools specific checks
|
|
440
|
-
elif tool_name in ('Google AI', 'OpenAI', 'Anthropic'):
|
|
441
|
-
return self._validate_ai_tool_integrity(settings)
|
|
442
|
-
|
|
443
|
-
# JSON/XML Tool specific checks
|
|
444
|
-
elif tool_name == 'JSON/XML Tool':
|
|
445
|
-
return self._validate_json_xml_tool_integrity(settings)
|
|
446
|
-
|
|
447
|
-
# Default: basic integrity check
|
|
448
|
-
result.passed = True
|
|
449
|
-
result.message = "Basic integrity check passed"
|
|
450
|
-
return result
|
|
451
|
-
|
|
452
|
-
except Exception as e:
|
|
453
|
-
result.passed = False
|
|
454
|
-
result.message = f"Tool integrity check error: {e}"
|
|
455
|
-
return result
|
|
456
|
-
|
|
457
|
-
def _validate_curl_tool_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
458
|
-
"""Validate cURL tool specific integrity."""
|
|
459
|
-
result = CheckResult('curl_integrity')
|
|
460
|
-
|
|
461
|
-
try:
|
|
462
|
-
# Check timeout is reasonable
|
|
463
|
-
timeout = settings.get('default_timeout', 90)
|
|
464
|
-
if not isinstance(timeout, (int, float)) or timeout <= 0 or timeout > 3600:
|
|
465
|
-
result.passed = False
|
|
466
|
-
result.message = "Default timeout must be between 1 and 3600 seconds"
|
|
467
|
-
return result
|
|
468
|
-
|
|
469
|
-
# Check max_redirects is reasonable
|
|
470
|
-
max_redirects = settings.get('max_redirects', 10)
|
|
471
|
-
if not isinstance(max_redirects, int) or max_redirects < 0 or max_redirects > 50:
|
|
472
|
-
result.passed = False
|
|
473
|
-
result.message = "Max redirects must be between 0 and 50"
|
|
474
|
-
return result
|
|
475
|
-
|
|
476
|
-
# Check history settings
|
|
477
|
-
max_history = settings.get('max_history_items', 100)
|
|
478
|
-
if not isinstance(max_history, int) or max_history < 0 or max_history > 10000:
|
|
479
|
-
result.passed = False
|
|
480
|
-
result.message = "Max history items must be between 0 and 10000"
|
|
481
|
-
return result
|
|
482
|
-
|
|
483
|
-
# Validate history structure if present
|
|
484
|
-
history = settings.get('history', [])
|
|
485
|
-
if not isinstance(history, list):
|
|
486
|
-
result.passed = False
|
|
487
|
-
result.message = "History must be a list"
|
|
488
|
-
return result
|
|
489
|
-
|
|
490
|
-
# Check history entries structure
|
|
491
|
-
for i, entry in enumerate(history[:5]): # Check first 5 entries
|
|
492
|
-
if not isinstance(entry, dict):
|
|
493
|
-
result.passed = False
|
|
494
|
-
result.message = f"History entry {i} must be a dictionary"
|
|
495
|
-
return result
|
|
496
|
-
|
|
497
|
-
required_fields = {'timestamp', 'method', 'url', 'status_code'}
|
|
498
|
-
missing_fields = required_fields - set(entry.keys())
|
|
499
|
-
if missing_fields:
|
|
500
|
-
result.passed = False
|
|
501
|
-
result.message = f"History entry {i} missing fields: {missing_fields}"
|
|
502
|
-
return result
|
|
503
|
-
|
|
504
|
-
result.passed = True
|
|
505
|
-
result.message = "cURL tool integrity validated"
|
|
506
|
-
return result
|
|
507
|
-
|
|
508
|
-
except Exception as e:
|
|
509
|
-
result.passed = False
|
|
510
|
-
result.message = f"cURL integrity validation error: {e}"
|
|
511
|
-
return result
|
|
512
|
-
|
|
513
|
-
def _validate_ai_tool_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
514
|
-
"""Validate AI tool specific integrity."""
|
|
515
|
-
result = CheckResult('ai_tool_integrity')
|
|
516
|
-
|
|
517
|
-
try:
|
|
518
|
-
# Check temperature range
|
|
519
|
-
temperature = settings.get('temperature', 0.7)
|
|
520
|
-
if not isinstance(temperature, (int, float)) or not (0.0 <= temperature <= 2.0):
|
|
521
|
-
result.passed = False
|
|
522
|
-
result.message = "Temperature must be between 0.0 and 2.0"
|
|
523
|
-
return result
|
|
524
|
-
|
|
525
|
-
# Check token limits
|
|
526
|
-
max_tokens = settings.get('maxOutputTokens', 8192)
|
|
527
|
-
if not isinstance(max_tokens, int) or max_tokens <= 0 or max_tokens > 100000:
|
|
528
|
-
result.passed = False
|
|
529
|
-
result.message = "Max output tokens must be between 1 and 100000"
|
|
530
|
-
return result
|
|
531
|
-
|
|
532
|
-
# Check model list
|
|
533
|
-
models_list = settings.get('MODELS_LIST', [])
|
|
534
|
-
if not isinstance(models_list, list) or not models_list:
|
|
535
|
-
result.passed = False
|
|
536
|
-
result.message = "Models list must be non-empty list"
|
|
537
|
-
return result
|
|
538
|
-
|
|
539
|
-
# Check current model is in list
|
|
540
|
-
current_model = settings.get('MODEL')
|
|
541
|
-
if current_model and current_model not in models_list:
|
|
542
|
-
result.passed = False
|
|
543
|
-
result.message = "Current model must be in models list"
|
|
544
|
-
return result
|
|
545
|
-
|
|
546
|
-
result.passed = True
|
|
547
|
-
result.message = "AI tool integrity validated"
|
|
548
|
-
return result
|
|
549
|
-
|
|
550
|
-
except Exception as e:
|
|
551
|
-
result.passed = False
|
|
552
|
-
result.message = f"AI tool integrity validation error: {e}"
|
|
553
|
-
return result
|
|
554
|
-
|
|
555
|
-
def _validate_json_xml_tool_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
556
|
-
"""Validate JSON/XML tool specific integrity."""
|
|
557
|
-
result = CheckResult('json_xml_integrity')
|
|
558
|
-
|
|
559
|
-
try:
|
|
560
|
-
# Check indent values
|
|
561
|
-
for indent_key in ('json_indent', 'xml_indent'):
|
|
562
|
-
indent = settings.get(indent_key, 2)
|
|
563
|
-
if not isinstance(indent, int) or indent < 0 or indent > 10:
|
|
564
|
-
result.passed = False
|
|
565
|
-
result.message = f"{indent_key} must be between 0 and 10"
|
|
566
|
-
return result
|
|
567
|
-
|
|
568
|
-
# Check operation is valid
|
|
569
|
-
operation = settings.get('operation')
|
|
570
|
-
valid_operations = {
|
|
571
|
-
'json_to_xml', 'xml_to_json', 'format_json', 'format_xml',
|
|
572
|
-
'minify_json', 'validate_json', 'validate_xml'
|
|
573
|
-
}
|
|
574
|
-
if operation and operation not in valid_operations:
|
|
575
|
-
result.passed = False
|
|
576
|
-
result.message = f"Operation must be one of {valid_operations}"
|
|
577
|
-
return result
|
|
578
|
-
|
|
579
|
-
result.passed = True
|
|
580
|
-
result.message = "JSON/XML tool integrity validated"
|
|
581
|
-
return result
|
|
582
|
-
|
|
583
|
-
except Exception as e:
|
|
584
|
-
result.passed = False
|
|
585
|
-
result.message = f"JSON/XML integrity validation error: {e}"
|
|
586
|
-
return result
|
|
587
|
-
|
|
588
|
-
def _validate_tab_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
589
|
-
"""Validate tab-related consistency."""
|
|
590
|
-
result = CheckResult('tab_consistency')
|
|
591
|
-
|
|
592
|
-
try:
|
|
593
|
-
# Check tab arrays exist and have correct length
|
|
594
|
-
input_tabs = settings.get('input_tabs', [])
|
|
595
|
-
output_tabs = settings.get('output_tabs', [])
|
|
596
|
-
|
|
597
|
-
if not isinstance(input_tabs, list) or len(input_tabs) != 7:
|
|
598
|
-
result.passed = False
|
|
599
|
-
result.message = "input_tabs must be list of length 7"
|
|
600
|
-
return result
|
|
601
|
-
|
|
602
|
-
if not isinstance(output_tabs, list) or len(output_tabs) != 7:
|
|
603
|
-
result.passed = False
|
|
604
|
-
result.message = "output_tabs must be list of length 7"
|
|
605
|
-
return result
|
|
606
|
-
|
|
607
|
-
# Check active tab indices are valid
|
|
608
|
-
active_input = settings.get('active_input_tab', 0)
|
|
609
|
-
active_output = settings.get('active_output_tab', 0)
|
|
610
|
-
|
|
611
|
-
if not isinstance(active_input, int) or not (0 <= active_input < 7):
|
|
612
|
-
result.passed = False
|
|
613
|
-
result.message = "active_input_tab must be integer between 0 and 6"
|
|
614
|
-
return result
|
|
615
|
-
|
|
616
|
-
if not isinstance(active_output, int) or not (0 <= active_output < 7):
|
|
617
|
-
result.passed = False
|
|
618
|
-
result.message = "active_output_tab must be integer between 0 and 6"
|
|
619
|
-
return result
|
|
620
|
-
|
|
621
|
-
result.passed = True
|
|
622
|
-
result.message = "Tab consistency validated"
|
|
623
|
-
return result
|
|
624
|
-
|
|
625
|
-
except Exception as e:
|
|
626
|
-
result.passed = False
|
|
627
|
-
result.message = f"Tab consistency validation error: {e}"
|
|
628
|
-
return result
|
|
629
|
-
|
|
630
|
-
def _validate_tool_selection_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
631
|
-
"""Validate tool selection consistency."""
|
|
632
|
-
result = CheckResult('tool_selection_consistency')
|
|
633
|
-
|
|
634
|
-
try:
|
|
635
|
-
selected_tool = settings.get('selected_tool')
|
|
636
|
-
tool_settings = settings.get('tool_settings', {})
|
|
637
|
-
|
|
638
|
-
if selected_tool and selected_tool not in tool_settings:
|
|
639
|
-
result.passed = False
|
|
640
|
-
result.message = f"Selected tool '{selected_tool}' not found in tool_settings"
|
|
641
|
-
return result
|
|
642
|
-
|
|
643
|
-
result.passed = True
|
|
644
|
-
result.message = "Tool selection consistency validated"
|
|
645
|
-
return result
|
|
646
|
-
|
|
647
|
-
except Exception as e:
|
|
648
|
-
result.passed = False
|
|
649
|
-
result.message = f"Tool selection consistency validation error: {e}"
|
|
650
|
-
return result
|
|
651
|
-
|
|
652
|
-
def _validate_performance_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
653
|
-
"""Validate performance settings consistency."""
|
|
654
|
-
result = CheckResult('performance_consistency')
|
|
655
|
-
|
|
656
|
-
try:
|
|
657
|
-
perf_settings = settings.get('performance_settings', {})
|
|
658
|
-
|
|
659
|
-
if not isinstance(perf_settings, dict):
|
|
660
|
-
result.passed = True # Optional settings
|
|
661
|
-
result.message = "Performance settings not configured"
|
|
662
|
-
return result
|
|
663
|
-
|
|
664
|
-
# Check mode consistency
|
|
665
|
-
mode = perf_settings.get('mode', 'automatic')
|
|
666
|
-
if mode not in ('automatic', 'manual', 'disabled'):
|
|
667
|
-
result.passed = False
|
|
668
|
-
result.message = "Performance mode must be 'automatic', 'manual', or 'disabled'"
|
|
669
|
-
return result
|
|
670
|
-
|
|
671
|
-
# If disabled, other settings should be disabled too
|
|
672
|
-
if mode == 'disabled':
|
|
673
|
-
for category in ('async_processing', 'caching', 'memory_management', 'ui_optimizations'):
|
|
674
|
-
category_settings = perf_settings.get(category, {})
|
|
675
|
-
if isinstance(category_settings, dict) and category_settings.get('enabled', False):
|
|
676
|
-
result.passed = False
|
|
677
|
-
result.message = f"Performance mode is disabled but {category} is enabled"
|
|
678
|
-
return result
|
|
679
|
-
|
|
680
|
-
result.passed = True
|
|
681
|
-
result.message = "Performance consistency validated"
|
|
682
|
-
return result
|
|
683
|
-
|
|
684
|
-
except Exception as e:
|
|
685
|
-
result.passed = False
|
|
686
|
-
result.message = f"Performance consistency validation error: {e}"
|
|
687
|
-
return result
|
|
688
|
-
|
|
689
|
-
def _validate_font_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
690
|
-
"""Validate font settings consistency."""
|
|
691
|
-
result = CheckResult('font_consistency')
|
|
692
|
-
|
|
693
|
-
try:
|
|
694
|
-
font_settings = settings.get('font_settings', {})
|
|
695
|
-
|
|
696
|
-
if not isinstance(font_settings, dict):
|
|
697
|
-
result.passed = True # Optional settings
|
|
698
|
-
result.message = "Font settings not configured"
|
|
699
|
-
return result
|
|
700
|
-
|
|
701
|
-
# Check required font types
|
|
702
|
-
for font_type in ('text_font', 'interface_font'):
|
|
703
|
-
font_config = font_settings.get(font_type, {})
|
|
704
|
-
if not isinstance(font_config, dict):
|
|
705
|
-
continue
|
|
706
|
-
|
|
707
|
-
# Check required properties
|
|
708
|
-
if 'family' in font_config and 'size' in font_config:
|
|
709
|
-
size = font_config['size']
|
|
710
|
-
if not isinstance(size, (int, float)) or size <= 0 or size > 72:
|
|
711
|
-
result.passed = False
|
|
712
|
-
result.message = f"{font_type} size must be between 1 and 72"
|
|
713
|
-
return result
|
|
714
|
-
|
|
715
|
-
result.passed = True
|
|
716
|
-
result.message = "Font consistency validated"
|
|
717
|
-
return result
|
|
718
|
-
|
|
719
|
-
except Exception as e:
|
|
720
|
-
result.passed = False
|
|
721
|
-
result.message = f"Font consistency validation error: {e}"
|
|
722
|
-
return result
|
|
723
|
-
|
|
724
|
-
def _validate_dialog_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
725
|
-
"""Validate dialog settings consistency."""
|
|
726
|
-
result = CheckResult('dialog_consistency')
|
|
727
|
-
|
|
728
|
-
try:
|
|
729
|
-
dialog_settings = settings.get('dialog_settings', {})
|
|
730
|
-
|
|
731
|
-
if not isinstance(dialog_settings, dict):
|
|
732
|
-
result.passed = True # Optional settings
|
|
733
|
-
result.message = "Dialog settings not configured"
|
|
734
|
-
return result
|
|
735
|
-
|
|
736
|
-
# Error dialogs must always be enabled and locked
|
|
737
|
-
error_config = dialog_settings.get('error', {})
|
|
738
|
-
if isinstance(error_config, dict):
|
|
739
|
-
if not error_config.get('enabled', True):
|
|
740
|
-
result.passed = False
|
|
741
|
-
result.message = "Error dialogs must be enabled"
|
|
742
|
-
return result
|
|
743
|
-
|
|
744
|
-
if not error_config.get('locked', True):
|
|
745
|
-
result.passed = False
|
|
746
|
-
result.message = "Error dialogs must be locked"
|
|
747
|
-
return result
|
|
748
|
-
|
|
749
|
-
result.passed = True
|
|
750
|
-
result.message = "Dialog consistency validated"
|
|
751
|
-
return result
|
|
752
|
-
|
|
753
|
-
except Exception as e:
|
|
754
|
-
result.passed = False
|
|
755
|
-
result.message = f"Dialog consistency validation error: {e}"
|
|
756
|
-
return result
|
|
757
|
-
|
|
758
|
-
def _initialize_core_validation_rules(self):
|
|
759
|
-
"""Initialize validation rules for core settings."""
|
|
760
|
-
self.validation_rules.update({
|
|
761
|
-
'debug_level': lambda v, c: v in {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'},
|
|
762
|
-
'active_input_tab': lambda v, c: isinstance(v, int) and 0 <= v < 7,
|
|
763
|
-
'active_output_tab': lambda v, c: isinstance(v, int) and 0 <= v < 7,
|
|
764
|
-
'export_path': lambda v, c: isinstance(v, str) and v.strip(),
|
|
765
|
-
'selected_tool': lambda v, c: isinstance(v, str) and v.strip()
|
|
766
|
-
})
|
|
767
|
-
|
|
768
|
-
def _initialize_tool_validation_rules(self):
|
|
769
|
-
"""Initialize validation rules for tool-specific settings."""
|
|
770
|
-
# cURL Tool validation rules
|
|
771
|
-
self.validation_rules.update({
|
|
772
|
-
'cURL Tool.default_timeout': lambda v, c: isinstance(v, (int, float)) and 1 <= v <= 3600,
|
|
773
|
-
'cURL Tool.max_redirects': lambda v, c: isinstance(v, int) and 0 <= v <= 50,
|
|
774
|
-
'cURL Tool.max_history_items': lambda v, c: isinstance(v, int) and 0 <= v <= 10000,
|
|
775
|
-
'cURL Tool.history_retention_days': lambda v, c: isinstance(v, int) and 1 <= v <= 365,
|
|
776
|
-
'cURL Tool.auth_timeout_minutes': lambda v, c: isinstance(v, int) and 1 <= v <= 1440,
|
|
777
|
-
'cURL Tool.download_chunk_size': lambda v, c: isinstance(v, int) and 1024 <= v <= 1048576,
|
|
778
|
-
'cURL Tool.max_log_size_mb': lambda v, c: isinstance(v, (int, float)) and 1 <= v <= 100,
|
|
779
|
-
'cURL Tool.connection_pool_size': lambda v, c: isinstance(v, int) and 1 <= v <= 100,
|
|
780
|
-
'cURL Tool.retry_attempts': lambda v, c: isinstance(v, int) and 0 <= v <= 10,
|
|
781
|
-
'cURL Tool.retry_delay_seconds': lambda v, c: isinstance(v, (int, float)) and 0 <= v <= 60,
|
|
782
|
-
})
|
|
783
|
-
|
|
784
|
-
# AI Tools validation rules
|
|
785
|
-
ai_tools = ['Google AI', 'Anthropic AI', 'OpenAI', 'Cohere AI', 'HuggingFace AI', 'Groq AI', 'OpenRouterAI', 'AWS Bedrock', 'LM Studio']
|
|
786
|
-
for tool in ai_tools:
|
|
787
|
-
self.validation_rules.update({
|
|
788
|
-
f'{tool}.temperature': lambda v, c: isinstance(v, (int, float)) and 0.0 <= v <= 2.0,
|
|
789
|
-
f'{tool}.max_tokens': lambda v, c: isinstance(v, int) and 1 <= v <= 100000,
|
|
790
|
-
f'{tool}.maxOutputTokens': lambda v, c: isinstance(v, int) and 1 <= v <= 100000,
|
|
791
|
-
f'{tool}.MAX_TOKENS': lambda v, c: isinstance(v, str) and v.isdigit() and 1 <= int(v) <= 100000,
|
|
792
|
-
f'{tool}.CONTEXT_WINDOW': lambda v, c: isinstance(v, str) and v.isdigit() and 1 <= int(v) <= 1000000,
|
|
793
|
-
f'{tool}.topK': lambda v, c: isinstance(v, int) and 1 <= v <= 100,
|
|
794
|
-
f'{tool}.top_k': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
|
|
795
|
-
f'{tool}.topP': lambda v, c: isinstance(v, (int, float)) and 0.0 <= v <= 1.0,
|
|
796
|
-
f'{tool}.top_p': lambda v, c: isinstance(v, (int, float)) and 0.0 <= v <= 1.0,
|
|
797
|
-
f'{tool}.candidateCount': lambda v, c: isinstance(v, int) and 1 <= v <= 10,
|
|
798
|
-
f'{tool}.frequency_penalty': lambda v, c: isinstance(v, (int, float)) and -2.0 <= v <= 2.0,
|
|
799
|
-
f'{tool}.presence_penalty': lambda v, c: isinstance(v, (int, float)) and -2.0 <= v <= 2.0,
|
|
800
|
-
f'{tool}.repetition_penalty': lambda v, c: isinstance(v, (int, float)) and 0.1 <= v <= 2.0,
|
|
801
|
-
})
|
|
802
|
-
|
|
803
|
-
# JSON/XML Tool validation rules
|
|
804
|
-
self.validation_rules.update({
|
|
805
|
-
'JSON/XML Tool.json_indent': lambda v, c: isinstance(v, int) and 0 <= v <= 10,
|
|
806
|
-
'JSON/XML Tool.xml_indent': lambda v, c: isinstance(v, int) and 0 <= v <= 10,
|
|
807
|
-
'JSON/XML Tool.operation': lambda v, c: v in {
|
|
808
|
-
'json_to_xml', 'xml_to_json', 'format_json', 'format_xml',
|
|
809
|
-
'minify_json', 'validate_json', 'validate_xml'
|
|
810
|
-
},
|
|
811
|
-
})
|
|
812
|
-
|
|
813
|
-
# Generator Tools validation rules
|
|
814
|
-
self.validation_rules.update({
|
|
815
|
-
'Generator Tools.Strong Password Generator.length': lambda v, c: isinstance(v, int) and 4 <= v <= 128,
|
|
816
|
-
'Generator Tools.Strong Password Generator.letters_percent': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
|
|
817
|
-
'Generator Tools.Strong Password Generator.numbers_percent': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
|
|
818
|
-
'Generator Tools.Strong Password Generator.symbols_percent': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
|
|
819
|
-
'Generator Tools.Repeating Text Generator.times': lambda v, c: isinstance(v, int) and 1 <= v <= 10000,
|
|
820
|
-
'Generator Tools.Lorem Ipsum Generator.count': lambda v, c: isinstance(v, int) and 1 <= v <= 1000,
|
|
821
|
-
'Generator Tools.UUID/GUID Generator.version': lambda v, c: v in {1, 3, 4, 5},
|
|
822
|
-
'Generator Tools.UUID/GUID Generator.count': lambda v, c: isinstance(v, int) and 1 <= v <= 1000,
|
|
823
|
-
})
|
|
824
|
-
|
|
825
|
-
# Folder File Reporter validation rules
|
|
826
|
-
self.validation_rules.update({
|
|
827
|
-
'Folder File Reporter.recursion_depth': lambda v, c: isinstance(v, int) and 0 <= v <= 20,
|
|
828
|
-
'Folder File Reporter.recursion_mode': lambda v, c: v in {'none', 'single', 'full'},
|
|
829
|
-
'Folder File Reporter.size_format': lambda v, c: v in {'bytes', 'human', 'kb', 'mb', 'gb'},
|
|
830
|
-
})
|
|
831
|
-
|
|
832
|
-
# Cron Tool validation rules
|
|
833
|
-
self.validation_rules.update({
|
|
834
|
-
'Cron Tool.next_runs_count': lambda v, c: isinstance(v, int) and 1 <= v <= 100,
|
|
835
|
-
'Cron Tool.action': lambda v, c: v in {'parse_explain', 'generate', 'validate'},
|
|
836
|
-
})
|
|
837
|
-
|
|
838
|
-
def _initialize_integrity_rules(self):
|
|
839
|
-
"""Initialize integrity checking rules."""
|
|
840
|
-
# Critical settings that must always be present and valid
|
|
841
|
-
self.integrity_rules.update({
|
|
842
|
-
'tab_arrays_length': self._check_tab_arrays_integrity,
|
|
843
|
-
'active_tab_indices': self._check_active_tab_integrity,
|
|
844
|
-
'tool_settings_structure': self._check_tool_settings_structure,
|
|
845
|
-
'performance_settings_hierarchy': self._check_performance_hierarchy,
|
|
846
|
-
'font_settings_completeness': self._check_font_settings_completeness,
|
|
847
|
-
'dialog_settings_locked_fields': self._check_dialog_locked_fields,
|
|
848
|
-
'ai_model_consistency': self._check_ai_model_consistency,
|
|
849
|
-
'curl_history_structure': self._check_curl_history_structure,
|
|
850
|
-
'encrypted_data_format': self._check_encrypted_data_format,
|
|
851
|
-
'generator_tools_percentages': self._check_generator_percentages,
|
|
852
|
-
})
|
|
853
|
-
|
|
854
|
-
def _check_tab_arrays_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
855
|
-
"""Check that tab arrays have correct structure and length."""
|
|
856
|
-
result = CheckResult('tab_arrays_integrity')
|
|
857
|
-
|
|
858
|
-
try:
|
|
859
|
-
input_tabs = settings.get('input_tabs', [])
|
|
860
|
-
output_tabs = settings.get('output_tabs', [])
|
|
861
|
-
|
|
862
|
-
# Check arrays exist and are lists
|
|
863
|
-
if not isinstance(input_tabs, list):
|
|
864
|
-
result.passed = False
|
|
865
|
-
result.message = "input_tabs must be a list"
|
|
866
|
-
return result
|
|
867
|
-
|
|
868
|
-
if not isinstance(output_tabs, list):
|
|
869
|
-
result.passed = False
|
|
870
|
-
result.message = "output_tabs must be a list"
|
|
871
|
-
return result
|
|
872
|
-
|
|
873
|
-
# Check correct length (must be exactly 7)
|
|
874
|
-
if len(input_tabs) != 7:
|
|
875
|
-
result.passed = False
|
|
876
|
-
result.message = f"input_tabs must have exactly 7 elements, found {len(input_tabs)}"
|
|
877
|
-
return result
|
|
878
|
-
|
|
879
|
-
if len(output_tabs) != 7:
|
|
880
|
-
result.passed = False
|
|
881
|
-
result.message = f"output_tabs must have exactly 7 elements, found {len(output_tabs)}"
|
|
882
|
-
return result
|
|
883
|
-
|
|
884
|
-
# Check all elements are strings
|
|
885
|
-
for i, tab in enumerate(input_tabs):
|
|
886
|
-
if not isinstance(tab, str):
|
|
887
|
-
result.passed = False
|
|
888
|
-
result.message = f"input_tabs[{i}] must be string, found {type(tab).__name__}"
|
|
889
|
-
return result
|
|
890
|
-
|
|
891
|
-
for i, tab in enumerate(output_tabs):
|
|
892
|
-
if not isinstance(tab, str):
|
|
893
|
-
result.passed = False
|
|
894
|
-
result.message = f"output_tabs[{i}] must be string, found {type(tab).__name__}"
|
|
895
|
-
return result
|
|
896
|
-
|
|
897
|
-
result.passed = True
|
|
898
|
-
result.message = "Tab arrays integrity validated"
|
|
899
|
-
return result
|
|
900
|
-
|
|
901
|
-
except Exception as e:
|
|
902
|
-
result.passed = False
|
|
903
|
-
result.message = f"Tab arrays integrity check error: {e}"
|
|
904
|
-
return result
|
|
905
|
-
|
|
906
|
-
def _check_active_tab_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
907
|
-
"""Check that active tab indices are valid."""
|
|
908
|
-
result = CheckResult('active_tab_integrity')
|
|
909
|
-
|
|
910
|
-
try:
|
|
911
|
-
active_input = settings.get('active_input_tab', 0)
|
|
912
|
-
active_output = settings.get('active_output_tab', 0)
|
|
913
|
-
|
|
914
|
-
# Check types
|
|
915
|
-
if not isinstance(active_input, int):
|
|
916
|
-
result.passed = False
|
|
917
|
-
result.message = f"active_input_tab must be integer, found {type(active_input).__name__}"
|
|
918
|
-
return result
|
|
919
|
-
|
|
920
|
-
if not isinstance(active_output, int):
|
|
921
|
-
result.passed = False
|
|
922
|
-
result.message = f"active_output_tab must be integer, found {type(active_output).__name__}"
|
|
923
|
-
return result
|
|
924
|
-
|
|
925
|
-
# Check ranges (0-6 for 7 tabs)
|
|
926
|
-
if not (0 <= active_input < 7):
|
|
927
|
-
result.passed = False
|
|
928
|
-
result.message = f"active_input_tab must be 0-6, found {active_input}"
|
|
929
|
-
return result
|
|
930
|
-
|
|
931
|
-
if not (0 <= active_output < 7):
|
|
932
|
-
result.passed = False
|
|
933
|
-
result.message = f"active_output_tab must be 0-6, found {active_output}"
|
|
934
|
-
return result
|
|
935
|
-
|
|
936
|
-
result.passed = True
|
|
937
|
-
result.message = "Active tab indices validated"
|
|
938
|
-
return result
|
|
939
|
-
|
|
940
|
-
except Exception as e:
|
|
941
|
-
result.passed = False
|
|
942
|
-
result.message = f"Active tab integrity check error: {e}"
|
|
943
|
-
return result
|
|
944
|
-
|
|
945
|
-
def _check_tool_settings_structure(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
946
|
-
"""Check that tool_settings has proper structure."""
|
|
947
|
-
result = CheckResult('tool_settings_structure')
|
|
948
|
-
|
|
949
|
-
try:
|
|
950
|
-
tool_settings = settings.get('tool_settings', {})
|
|
951
|
-
|
|
952
|
-
if not isinstance(tool_settings, dict):
|
|
953
|
-
result.passed = False
|
|
954
|
-
result.message = "tool_settings must be a dictionary"
|
|
955
|
-
return result
|
|
956
|
-
|
|
957
|
-
# Check that each tool's settings is a dictionary
|
|
958
|
-
for tool_name, tool_config in tool_settings.items():
|
|
959
|
-
if not isinstance(tool_name, str):
|
|
960
|
-
result.passed = False
|
|
961
|
-
result.message = f"Tool name must be string, found {type(tool_name).__name__}"
|
|
962
|
-
return result
|
|
963
|
-
|
|
964
|
-
if not isinstance(tool_config, dict):
|
|
965
|
-
result.passed = False
|
|
966
|
-
result.message = f"Tool '{tool_name}' settings must be dictionary, found {type(tool_config).__name__}"
|
|
967
|
-
return result
|
|
968
|
-
|
|
969
|
-
result.passed = True
|
|
970
|
-
result.message = "Tool settings structure validated"
|
|
971
|
-
return result
|
|
972
|
-
|
|
973
|
-
except Exception as e:
|
|
974
|
-
result.passed = False
|
|
975
|
-
result.message = f"Tool settings structure check error: {e}"
|
|
976
|
-
return result
|
|
977
|
-
|
|
978
|
-
def _check_performance_hierarchy(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
979
|
-
"""Check performance settings hierarchy integrity."""
|
|
980
|
-
result = CheckResult('performance_hierarchy')
|
|
981
|
-
|
|
982
|
-
try:
|
|
983
|
-
perf_settings = settings.get('performance_settings', {})
|
|
984
|
-
|
|
985
|
-
if not isinstance(perf_settings, dict):
|
|
986
|
-
result.passed = True # Optional settings
|
|
987
|
-
result.message = "Performance settings not configured"
|
|
988
|
-
return result
|
|
989
|
-
|
|
990
|
-
# Check required categories exist and are dictionaries
|
|
991
|
-
required_categories = ['async_processing', 'caching', 'memory_management', 'ui_optimizations']
|
|
992
|
-
for category in required_categories:
|
|
993
|
-
if category in perf_settings:
|
|
994
|
-
category_settings = perf_settings[category]
|
|
995
|
-
if not isinstance(category_settings, dict):
|
|
996
|
-
result.passed = False
|
|
997
|
-
result.message = f"Performance category '{category}' must be dictionary"
|
|
998
|
-
return result
|
|
999
|
-
|
|
1000
|
-
# Check 'enabled' field exists and is boolean
|
|
1001
|
-
if 'enabled' in category_settings:
|
|
1002
|
-
enabled = category_settings['enabled']
|
|
1003
|
-
if not isinstance(enabled, bool):
|
|
1004
|
-
result.passed = False
|
|
1005
|
-
result.message = f"Performance category '{category}.enabled' must be boolean"
|
|
1006
|
-
return result
|
|
1007
|
-
|
|
1008
|
-
result.passed = True
|
|
1009
|
-
result.message = "Performance hierarchy validated"
|
|
1010
|
-
return result
|
|
1011
|
-
|
|
1012
|
-
except Exception as e:
|
|
1013
|
-
result.passed = False
|
|
1014
|
-
result.message = f"Performance hierarchy check error: {e}"
|
|
1015
|
-
return result
|
|
1016
|
-
|
|
1017
|
-
def _check_font_settings_completeness(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1018
|
-
"""Check font settings completeness."""
|
|
1019
|
-
result = CheckResult('font_settings_completeness')
|
|
1020
|
-
|
|
1021
|
-
try:
|
|
1022
|
-
font_settings = settings.get('font_settings', {})
|
|
1023
|
-
|
|
1024
|
-
if not isinstance(font_settings, dict):
|
|
1025
|
-
result.passed = True # Optional settings
|
|
1026
|
-
result.message = "Font settings not configured"
|
|
1027
|
-
return result
|
|
1028
|
-
|
|
1029
|
-
# Check required font types
|
|
1030
|
-
required_fonts = ['text_font', 'interface_font']
|
|
1031
|
-
for font_type in required_fonts:
|
|
1032
|
-
if font_type in font_settings:
|
|
1033
|
-
font_config = font_settings[font_type]
|
|
1034
|
-
if not isinstance(font_config, dict):
|
|
1035
|
-
result.passed = False
|
|
1036
|
-
result.message = f"Font type '{font_type}' must be dictionary"
|
|
1037
|
-
return result
|
|
1038
|
-
|
|
1039
|
-
# Check required properties
|
|
1040
|
-
required_props = ['family', 'size']
|
|
1041
|
-
for prop in required_props:
|
|
1042
|
-
if prop not in font_config:
|
|
1043
|
-
result.passed = False
|
|
1044
|
-
result.message = f"Font '{font_type}' missing required property '{prop}'"
|
|
1045
|
-
return result
|
|
1046
|
-
|
|
1047
|
-
if prop == 'size':
|
|
1048
|
-
size = font_config[prop]
|
|
1049
|
-
if not isinstance(size, (int, float)) or size <= 0 or size > 72:
|
|
1050
|
-
result.passed = False
|
|
1051
|
-
result.message = f"Font '{font_type}' size must be 1-72, found {size}"
|
|
1052
|
-
return result
|
|
1053
|
-
|
|
1054
|
-
result.passed = True
|
|
1055
|
-
result.message = "Font settings completeness validated"
|
|
1056
|
-
return result
|
|
1057
|
-
|
|
1058
|
-
except Exception as e:
|
|
1059
|
-
result.passed = False
|
|
1060
|
-
result.message = f"Font settings completeness check error: {e}"
|
|
1061
|
-
return result
|
|
1062
|
-
|
|
1063
|
-
def _check_dialog_locked_fields(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1064
|
-
"""Check that dialog settings locked fields are properly configured."""
|
|
1065
|
-
result = CheckResult('dialog_locked_fields')
|
|
1066
|
-
|
|
1067
|
-
try:
|
|
1068
|
-
dialog_settings = settings.get('dialog_settings', {})
|
|
1069
|
-
|
|
1070
|
-
if not isinstance(dialog_settings, dict):
|
|
1071
|
-
result.passed = True # Optional settings
|
|
1072
|
-
result.message = "Dialog settings not configured"
|
|
1073
|
-
return result
|
|
1074
|
-
|
|
1075
|
-
# Error dialogs must be enabled and locked
|
|
1076
|
-
if 'error' in dialog_settings:
|
|
1077
|
-
error_config = dialog_settings['error']
|
|
1078
|
-
if isinstance(error_config, dict):
|
|
1079
|
-
# Must be enabled
|
|
1080
|
-
if not error_config.get('enabled', True):
|
|
1081
|
-
result.passed = False
|
|
1082
|
-
result.message = "Error dialogs must be enabled"
|
|
1083
|
-
return result
|
|
1084
|
-
|
|
1085
|
-
# Must be locked
|
|
1086
|
-
if not error_config.get('locked', True):
|
|
1087
|
-
result.passed = False
|
|
1088
|
-
result.message = "Error dialogs must be locked"
|
|
1089
|
-
return result
|
|
1090
|
-
|
|
1091
|
-
result.passed = True
|
|
1092
|
-
result.message = "Dialog locked fields validated"
|
|
1093
|
-
return result
|
|
1094
|
-
|
|
1095
|
-
except Exception as e:
|
|
1096
|
-
result.passed = False
|
|
1097
|
-
result.message = f"Dialog locked fields check error: {e}"
|
|
1098
|
-
return result
|
|
1099
|
-
|
|
1100
|
-
def _check_ai_model_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1101
|
-
"""Check AI model consistency across tools."""
|
|
1102
|
-
result = CheckResult('ai_model_consistency')
|
|
1103
|
-
|
|
1104
|
-
try:
|
|
1105
|
-
tool_settings = settings.get('tool_settings', {})
|
|
1106
|
-
ai_tools = ['Google AI', 'Anthropic AI', 'OpenAI', 'Cohere AI', 'HuggingFace AI', 'Groq AI', 'OpenRouterAI', 'AWS Bedrock', 'LM Studio']
|
|
1107
|
-
|
|
1108
|
-
for tool_name in ai_tools:
|
|
1109
|
-
if tool_name in tool_settings:
|
|
1110
|
-
tool_config = tool_settings[tool_name]
|
|
1111
|
-
if isinstance(tool_config, dict):
|
|
1112
|
-
# Check model is in models list
|
|
1113
|
-
current_model = tool_config.get('MODEL')
|
|
1114
|
-
models_list = tool_config.get('MODELS_LIST', [])
|
|
1115
|
-
|
|
1116
|
-
if current_model and isinstance(models_list, list) and models_list:
|
|
1117
|
-
if current_model not in models_list:
|
|
1118
|
-
result.passed = False
|
|
1119
|
-
result.message = f"AI tool '{tool_name}' current model '{current_model}' not in models list"
|
|
1120
|
-
return result
|
|
1121
|
-
|
|
1122
|
-
# Check models list is not empty
|
|
1123
|
-
if 'MODELS_LIST' in tool_config:
|
|
1124
|
-
if not isinstance(models_list, list) or not models_list:
|
|
1125
|
-
result.passed = False
|
|
1126
|
-
result.message = f"AI tool '{tool_name}' models list must be non-empty list"
|
|
1127
|
-
return result
|
|
1128
|
-
|
|
1129
|
-
result.passed = True
|
|
1130
|
-
result.message = "AI model consistency validated"
|
|
1131
|
-
return result
|
|
1132
|
-
|
|
1133
|
-
except Exception as e:
|
|
1134
|
-
result.passed = False
|
|
1135
|
-
result.message = f"AI model consistency check error: {e}"
|
|
1136
|
-
return result
|
|
1137
|
-
|
|
1138
|
-
def _check_curl_history_structure(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1139
|
-
"""Check cURL history structure integrity."""
|
|
1140
|
-
result = CheckResult('curl_history_structure')
|
|
1141
|
-
|
|
1142
|
-
try:
|
|
1143
|
-
tool_settings = settings.get('tool_settings', {})
|
|
1144
|
-
curl_settings = tool_settings.get('cURL Tool', {})
|
|
1145
|
-
|
|
1146
|
-
if not isinstance(curl_settings, dict):
|
|
1147
|
-
result.passed = True # Tool not configured
|
|
1148
|
-
result.message = "cURL Tool not configured"
|
|
1149
|
-
return result
|
|
1150
|
-
|
|
1151
|
-
history = curl_settings.get('history', [])
|
|
1152
|
-
if not isinstance(history, list):
|
|
1153
|
-
result.passed = False
|
|
1154
|
-
result.message = "cURL history must be a list"
|
|
1155
|
-
return result
|
|
1156
|
-
|
|
1157
|
-
# Check history entries structure (first 5 entries)
|
|
1158
|
-
required_fields = {'timestamp', 'method', 'url', 'status_code', 'success'}
|
|
1159
|
-
for i, entry in enumerate(history[:5]):
|
|
1160
|
-
if not isinstance(entry, dict):
|
|
1161
|
-
result.passed = False
|
|
1162
|
-
result.message = f"cURL history entry {i} must be dictionary"
|
|
1163
|
-
return result
|
|
1164
|
-
|
|
1165
|
-
missing_fields = required_fields - set(entry.keys())
|
|
1166
|
-
if missing_fields:
|
|
1167
|
-
result.passed = False
|
|
1168
|
-
result.message = f"cURL history entry {i} missing fields: {missing_fields}"
|
|
1169
|
-
return result
|
|
1170
|
-
|
|
1171
|
-
# Validate timestamp format
|
|
1172
|
-
timestamp = entry.get('timestamp')
|
|
1173
|
-
if isinstance(timestamp, str):
|
|
1174
|
-
try:
|
|
1175
|
-
datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
|
1176
|
-
except ValueError:
|
|
1177
|
-
result.passed = False
|
|
1178
|
-
result.message = f"cURL history entry {i} has invalid timestamp format"
|
|
1179
|
-
return result
|
|
1180
|
-
|
|
1181
|
-
result.passed = True
|
|
1182
|
-
result.message = "cURL history structure validated"
|
|
1183
|
-
return result
|
|
1184
|
-
|
|
1185
|
-
except Exception as e:
|
|
1186
|
-
result.passed = False
|
|
1187
|
-
result.message = f"cURL history structure check error: {e}"
|
|
1188
|
-
return result
|
|
1189
|
-
|
|
1190
|
-
def _check_encrypted_data_format(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1191
|
-
"""Check encrypted data format integrity."""
|
|
1192
|
-
result = CheckResult('encrypted_data_format')
|
|
1193
|
-
|
|
1194
|
-
try:
|
|
1195
|
-
# Recursively check for encrypted values
|
|
1196
|
-
encrypted_count = 0
|
|
1197
|
-
invalid_encrypted = []
|
|
1198
|
-
|
|
1199
|
-
def check_encrypted_recursive(obj, path=""):
|
|
1200
|
-
nonlocal encrypted_count, invalid_encrypted
|
|
1201
|
-
|
|
1202
|
-
if isinstance(obj, dict):
|
|
1203
|
-
for key, value in obj.items():
|
|
1204
|
-
current_path = f"{path}.{key}" if path else key
|
|
1205
|
-
if isinstance(value, str) and value.startswith('ENC:'):
|
|
1206
|
-
encrypted_count += 1
|
|
1207
|
-
# Validate base64 format after ENC: prefix
|
|
1208
|
-
encrypted_data = value[4:] # Remove 'ENC:' prefix
|
|
1209
|
-
try:
|
|
1210
|
-
import base64
|
|
1211
|
-
base64.b64decode(encrypted_data, validate=True)
|
|
1212
|
-
except Exception:
|
|
1213
|
-
invalid_encrypted.append(current_path)
|
|
1214
|
-
elif isinstance(value, (dict, list)):
|
|
1215
|
-
check_encrypted_recursive(value, current_path)
|
|
1216
|
-
elif isinstance(obj, list):
|
|
1217
|
-
for i, item in enumerate(obj):
|
|
1218
|
-
current_path = f"{path}[{i}]" if path else f"[{i}]"
|
|
1219
|
-
check_encrypted_recursive(item, current_path)
|
|
1220
|
-
|
|
1221
|
-
check_encrypted_recursive(settings)
|
|
1222
|
-
|
|
1223
|
-
if invalid_encrypted:
|
|
1224
|
-
result.passed = False
|
|
1225
|
-
result.message = f"Invalid encrypted data format in: {invalid_encrypted}"
|
|
1226
|
-
return result
|
|
1227
|
-
|
|
1228
|
-
result.passed = True
|
|
1229
|
-
result.message = f"Encrypted data format validated ({encrypted_count} encrypted values found)"
|
|
1230
|
-
return result
|
|
1231
|
-
|
|
1232
|
-
except Exception as e:
|
|
1233
|
-
result.passed = False
|
|
1234
|
-
result.message = f"Encrypted data format check error: {e}"
|
|
1235
|
-
return result
|
|
1236
|
-
|
|
1237
|
-
def _check_generator_percentages(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1238
|
-
"""Check generator tools percentage consistency."""
|
|
1239
|
-
result = CheckResult('generator_percentages')
|
|
1240
|
-
|
|
1241
|
-
try:
|
|
1242
|
-
tool_settings = settings.get('tool_settings', {})
|
|
1243
|
-
generator_tools = tool_settings.get('Generator Tools', {})
|
|
1244
|
-
|
|
1245
|
-
if not isinstance(generator_tools, dict):
|
|
1246
|
-
result.passed = True # Tool not configured
|
|
1247
|
-
result.message = "Generator Tools not configured"
|
|
1248
|
-
return result
|
|
1249
|
-
|
|
1250
|
-
password_gen = generator_tools.get('Strong Password Generator', {})
|
|
1251
|
-
if isinstance(password_gen, dict):
|
|
1252
|
-
letters_pct = password_gen.get('letters_percent', 0)
|
|
1253
|
-
numbers_pct = password_gen.get('numbers_percent', 0)
|
|
1254
|
-
symbols_pct = password_gen.get('symbols_percent', 0)
|
|
1255
|
-
|
|
1256
|
-
# Check percentages are valid
|
|
1257
|
-
if not all(isinstance(pct, int) and 0 <= pct <= 100 for pct in [letters_pct, numbers_pct, symbols_pct]):
|
|
1258
|
-
result.passed = False
|
|
1259
|
-
result.message = "Password generator percentages must be integers 0-100"
|
|
1260
|
-
return result
|
|
1261
|
-
|
|
1262
|
-
# Check percentages sum to 100
|
|
1263
|
-
total = letters_pct + numbers_pct + symbols_pct
|
|
1264
|
-
if total != 100:
|
|
1265
|
-
result.passed = False
|
|
1266
|
-
result.message = f"Password generator percentages must sum to 100, found {total}"
|
|
1267
|
-
return result
|
|
1268
|
-
|
|
1269
|
-
result.passed = True
|
|
1270
|
-
result.message = "Generator percentages validated"
|
|
1271
|
-
return result
|
|
1272
|
-
|
|
1273
|
-
except Exception as e:
|
|
1274
|
-
result.passed = False
|
|
1275
|
-
result.message = f"Generator percentages check error: {e}"
|
|
1276
|
-
return result
|
|
1277
|
-
|
|
1278
|
-
def _initialize_consistency_rules(self):
|
|
1279
|
-
"""Initialize consistency checking rules."""
|
|
1280
|
-
# Cross-setting consistency rules
|
|
1281
|
-
self.consistency_rules.update({
|
|
1282
|
-
'selected_tool_exists': self._check_selected_tool_exists,
|
|
1283
|
-
'performance_mode_consistency': self._check_performance_mode_consistency,
|
|
1284
|
-
'ai_tools_api_keys': self._check_ai_tools_api_keys,
|
|
1285
|
-
'curl_timeout_consistency': self._check_curl_timeout_consistency,
|
|
1286
|
-
'font_fallback_consistency': self._check_font_fallback_consistency,
|
|
1287
|
-
'dialog_hierarchy_consistency': self._check_dialog_hierarchy_consistency,
|
|
1288
|
-
'generator_tools_completeness': self._check_generator_tools_completeness,
|
|
1289
|
-
'folder_reporter_path_consistency': self._check_folder_reporter_paths,
|
|
1290
|
-
})
|
|
1291
|
-
|
|
1292
|
-
def _check_selected_tool_exists(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1293
|
-
"""Check that selected tool exists in tool_settings."""
|
|
1294
|
-
result = CheckResult('selected_tool_exists')
|
|
1295
|
-
|
|
1296
|
-
try:
|
|
1297
|
-
selected_tool = settings.get('selected_tool')
|
|
1298
|
-
tool_settings = settings.get('tool_settings', {})
|
|
1299
|
-
|
|
1300
|
-
if selected_tool:
|
|
1301
|
-
if not isinstance(selected_tool, str):
|
|
1302
|
-
result.passed = False
|
|
1303
|
-
result.message = "selected_tool must be string"
|
|
1304
|
-
return result
|
|
1305
|
-
|
|
1306
|
-
if selected_tool not in tool_settings:
|
|
1307
|
-
result.passed = False
|
|
1308
|
-
result.message = f"Selected tool '{selected_tool}' not found in tool_settings"
|
|
1309
|
-
return result
|
|
1310
|
-
|
|
1311
|
-
result.passed = True
|
|
1312
|
-
result.message = "Selected tool consistency validated"
|
|
1313
|
-
return result
|
|
1314
|
-
|
|
1315
|
-
except Exception as e:
|
|
1316
|
-
result.passed = False
|
|
1317
|
-
result.message = f"Selected tool consistency check error: {e}"
|
|
1318
|
-
return result
|
|
1319
|
-
|
|
1320
|
-
def _check_performance_mode_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1321
|
-
"""Check performance mode consistency across categories."""
|
|
1322
|
-
result = CheckResult('performance_mode_consistency')
|
|
1323
|
-
|
|
1324
|
-
try:
|
|
1325
|
-
perf_settings = settings.get('performance_settings', {})
|
|
1326
|
-
|
|
1327
|
-
if not isinstance(perf_settings, dict):
|
|
1328
|
-
result.passed = True
|
|
1329
|
-
result.message = "Performance settings not configured"
|
|
1330
|
-
return result
|
|
1331
|
-
|
|
1332
|
-
mode = perf_settings.get('mode', 'automatic')
|
|
1333
|
-
|
|
1334
|
-
# If mode is disabled, all categories should be disabled
|
|
1335
|
-
if mode == 'disabled':
|
|
1336
|
-
categories = ['async_processing', 'caching', 'memory_management', 'ui_optimizations']
|
|
1337
|
-
for category in categories:
|
|
1338
|
-
category_settings = perf_settings.get(category, {})
|
|
1339
|
-
if isinstance(category_settings, dict) and category_settings.get('enabled', False):
|
|
1340
|
-
result.passed = False
|
|
1341
|
-
result.message = f"Performance mode is disabled but {category} is enabled"
|
|
1342
|
-
return result
|
|
1343
|
-
|
|
1344
|
-
result.passed = True
|
|
1345
|
-
result.message = "Performance mode consistency validated"
|
|
1346
|
-
return result
|
|
1347
|
-
|
|
1348
|
-
except Exception as e:
|
|
1349
|
-
result.passed = False
|
|
1350
|
-
result.message = f"Performance mode consistency check error: {e}"
|
|
1351
|
-
return result
|
|
1352
|
-
|
|
1353
|
-
def _check_ai_tools_api_keys(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1354
|
-
"""Check AI tools have proper API key configuration."""
|
|
1355
|
-
result = CheckResult('ai_tools_api_keys')
|
|
1356
|
-
|
|
1357
|
-
try:
|
|
1358
|
-
tool_settings = settings.get('tool_settings', {})
|
|
1359
|
-
ai_tools = ['Google AI', 'Anthropic AI', 'OpenAI', 'Cohere AI', 'HuggingFace AI', 'Groq AI', 'OpenRouterAI']
|
|
1360
|
-
|
|
1361
|
-
missing_keys = []
|
|
1362
|
-
placeholder_keys = []
|
|
1363
|
-
|
|
1364
|
-
for tool_name in ai_tools:
|
|
1365
|
-
if tool_name in tool_settings:
|
|
1366
|
-
tool_config = tool_settings[tool_name]
|
|
1367
|
-
if isinstance(tool_config, dict):
|
|
1368
|
-
api_key = tool_config.get('API_KEY', '')
|
|
1369
|
-
|
|
1370
|
-
if not api_key:
|
|
1371
|
-
missing_keys.append(tool_name)
|
|
1372
|
-
elif api_key == 'putinyourkey':
|
|
1373
|
-
placeholder_keys.append(tool_name)
|
|
1374
|
-
|
|
1375
|
-
# This is a warning, not an error - users might not have all API keys
|
|
1376
|
-
if missing_keys or placeholder_keys:
|
|
1377
|
-
result.passed = True # Don't fail validation
|
|
1378
|
-
result.severity = 'warning'
|
|
1379
|
-
warnings = []
|
|
1380
|
-
if missing_keys:
|
|
1381
|
-
warnings.append(f"Missing API keys: {missing_keys}")
|
|
1382
|
-
if placeholder_keys:
|
|
1383
|
-
warnings.append(f"Placeholder API keys: {placeholder_keys}")
|
|
1384
|
-
result.message = "; ".join(warnings)
|
|
1385
|
-
else:
|
|
1386
|
-
result.passed = True
|
|
1387
|
-
result.message = "AI tools API keys validated"
|
|
1388
|
-
|
|
1389
|
-
return result
|
|
1390
|
-
|
|
1391
|
-
except Exception as e:
|
|
1392
|
-
result.passed = False
|
|
1393
|
-
result.message = f"AI tools API keys check error: {e}"
|
|
1394
|
-
return result
|
|
1395
|
-
|
|
1396
|
-
def _check_curl_timeout_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1397
|
-
"""Check cURL timeout settings consistency."""
|
|
1398
|
-
result = CheckResult('curl_timeout_consistency')
|
|
1399
|
-
|
|
1400
|
-
try:
|
|
1401
|
-
tool_settings = settings.get('tool_settings', {})
|
|
1402
|
-
curl_settings = tool_settings.get('cURL Tool', {})
|
|
1403
|
-
|
|
1404
|
-
if not isinstance(curl_settings, dict):
|
|
1405
|
-
result.passed = True
|
|
1406
|
-
result.message = "cURL Tool not configured"
|
|
1407
|
-
return result
|
|
1408
|
-
|
|
1409
|
-
default_timeout = curl_settings.get('default_timeout', 90)
|
|
1410
|
-
auth_timeout_minutes = curl_settings.get('auth_timeout_minutes', 60)
|
|
1411
|
-
|
|
1412
|
-
# Convert auth timeout to seconds for comparison
|
|
1413
|
-
auth_timeout_seconds = auth_timeout_minutes * 60
|
|
1414
|
-
|
|
1415
|
-
# Auth timeout should be reasonable compared to default timeout
|
|
1416
|
-
if auth_timeout_seconds < default_timeout:
|
|
1417
|
-
result.passed = False
|
|
1418
|
-
result.message = f"Auth timeout ({auth_timeout_seconds}s) should not be less than default timeout ({default_timeout}s)"
|
|
1419
|
-
return result
|
|
1420
|
-
|
|
1421
|
-
result.passed = True
|
|
1422
|
-
result.message = "cURL timeout consistency validated"
|
|
1423
|
-
return result
|
|
1424
|
-
|
|
1425
|
-
except Exception as e:
|
|
1426
|
-
result.passed = False
|
|
1427
|
-
result.message = f"cURL timeout consistency check error: {e}"
|
|
1428
|
-
return result
|
|
1429
|
-
|
|
1430
|
-
def _check_font_fallback_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1431
|
-
"""Check font fallback consistency across platforms."""
|
|
1432
|
-
result = CheckResult('font_fallback_consistency')
|
|
1433
|
-
|
|
1434
|
-
try:
|
|
1435
|
-
font_settings = settings.get('font_settings', {})
|
|
1436
|
-
|
|
1437
|
-
if not isinstance(font_settings, dict):
|
|
1438
|
-
result.passed = True
|
|
1439
|
-
result.message = "Font settings not configured"
|
|
1440
|
-
return result
|
|
1441
|
-
|
|
1442
|
-
for font_type in ['text_font', 'interface_font']:
|
|
1443
|
-
if font_type in font_settings:
|
|
1444
|
-
font_config = font_settings[font_type]
|
|
1445
|
-
if isinstance(font_config, dict):
|
|
1446
|
-
# Check that if fallback_family exists, platform-specific fallbacks also exist
|
|
1447
|
-
has_fallback = 'fallback_family' in font_config
|
|
1448
|
-
has_mac_fallback = 'fallback_family_mac' in font_config
|
|
1449
|
-
has_linux_fallback = 'fallback_family_linux' in font_config
|
|
1450
|
-
|
|
1451
|
-
if has_fallback and not (has_mac_fallback and has_linux_fallback):
|
|
1452
|
-
result.passed = False
|
|
1453
|
-
result.message = f"Font '{font_type}' has fallback but missing platform-specific fallbacks"
|
|
1454
|
-
return result
|
|
1455
|
-
|
|
1456
|
-
result.passed = True
|
|
1457
|
-
result.message = "Font fallback consistency validated"
|
|
1458
|
-
return result
|
|
1459
|
-
|
|
1460
|
-
except Exception as e:
|
|
1461
|
-
result.passed = False
|
|
1462
|
-
result.message = f"Font fallback consistency check error: {e}"
|
|
1463
|
-
return result
|
|
1464
|
-
|
|
1465
|
-
def _check_dialog_hierarchy_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1466
|
-
"""Check dialog settings hierarchy consistency."""
|
|
1467
|
-
result = CheckResult('dialog_hierarchy_consistency')
|
|
1468
|
-
|
|
1469
|
-
try:
|
|
1470
|
-
dialog_settings = settings.get('dialog_settings', {})
|
|
1471
|
-
|
|
1472
|
-
if not isinstance(dialog_settings, dict):
|
|
1473
|
-
result.passed = True
|
|
1474
|
-
result.message = "Dialog settings not configured"
|
|
1475
|
-
return result
|
|
1476
|
-
|
|
1477
|
-
# Check that error dialogs cannot be disabled
|
|
1478
|
-
if 'error' in dialog_settings:
|
|
1479
|
-
error_config = dialog_settings['error']
|
|
1480
|
-
if isinstance(error_config, dict):
|
|
1481
|
-
if not error_config.get('enabled', True):
|
|
1482
|
-
result.passed = False
|
|
1483
|
-
result.message = "Error dialogs cannot be disabled"
|
|
1484
|
-
return result
|
|
1485
|
-
|
|
1486
|
-
if not error_config.get('locked', True):
|
|
1487
|
-
result.passed = False
|
|
1488
|
-
result.message = "Error dialogs must be locked"
|
|
1489
|
-
return result
|
|
1490
|
-
|
|
1491
|
-
# Check that confirmation dialogs have default_action if enabled
|
|
1492
|
-
if 'confirmation' in dialog_settings:
|
|
1493
|
-
confirm_config = dialog_settings['confirmation']
|
|
1494
|
-
if isinstance(confirm_config, dict) and confirm_config.get('enabled', False):
|
|
1495
|
-
if 'default_action' not in confirm_config:
|
|
1496
|
-
result.passed = False
|
|
1497
|
-
result.message = "Enabled confirmation dialogs must have default_action"
|
|
1498
|
-
return result
|
|
1499
|
-
|
|
1500
|
-
result.passed = True
|
|
1501
|
-
result.message = "Dialog hierarchy consistency validated"
|
|
1502
|
-
return result
|
|
1503
|
-
|
|
1504
|
-
except Exception as e:
|
|
1505
|
-
result.passed = False
|
|
1506
|
-
result.message = f"Dialog hierarchy consistency check error: {e}"
|
|
1507
|
-
return result
|
|
1508
|
-
|
|
1509
|
-
def _check_generator_tools_completeness(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1510
|
-
"""Check generator tools completeness and consistency."""
|
|
1511
|
-
result = CheckResult('generator_tools_completeness')
|
|
1512
|
-
|
|
1513
|
-
try:
|
|
1514
|
-
tool_settings = settings.get('tool_settings', {})
|
|
1515
|
-
generator_tools = tool_settings.get('Generator Tools', {})
|
|
1516
|
-
|
|
1517
|
-
if not isinstance(generator_tools, dict):
|
|
1518
|
-
result.passed = True
|
|
1519
|
-
result.message = "Generator Tools not configured"
|
|
1520
|
-
return result
|
|
1521
|
-
|
|
1522
|
-
# Check UUID generator consistency
|
|
1523
|
-
if 'UUID/GUID Generator' in generator_tools:
|
|
1524
|
-
uuid_config = generator_tools['UUID/GUID Generator']
|
|
1525
|
-
if isinstance(uuid_config, dict):
|
|
1526
|
-
version = uuid_config.get('version', 4)
|
|
1527
|
-
namespace = uuid_config.get('namespace', '')
|
|
1528
|
-
name = uuid_config.get('name', '')
|
|
1529
|
-
|
|
1530
|
-
# Version 3 and 5 require namespace and name
|
|
1531
|
-
if version in (3, 5):
|
|
1532
|
-
if not namespace or not name:
|
|
1533
|
-
result.passed = False
|
|
1534
|
-
result.message = f"UUID version {version} requires namespace and name"
|
|
1535
|
-
return result
|
|
1536
|
-
|
|
1537
|
-
result.passed = True
|
|
1538
|
-
result.message = "Generator tools completeness validated"
|
|
1539
|
-
return result
|
|
1540
|
-
|
|
1541
|
-
except Exception as e:
|
|
1542
|
-
result.passed = False
|
|
1543
|
-
result.message = f"Generator tools completeness check error: {e}"
|
|
1544
|
-
return result
|
|
1545
|
-
|
|
1546
|
-
def _check_folder_reporter_paths(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1547
|
-
"""Check folder reporter path consistency."""
|
|
1548
|
-
result = CheckResult('folder_reporter_paths')
|
|
1549
|
-
|
|
1550
|
-
try:
|
|
1551
|
-
tool_settings = settings.get('tool_settings', {})
|
|
1552
|
-
folder_reporter = tool_settings.get('Folder File Reporter', {})
|
|
1553
|
-
|
|
1554
|
-
if not isinstance(folder_reporter, dict):
|
|
1555
|
-
result.passed = True
|
|
1556
|
-
result.message = "Folder File Reporter not configured"
|
|
1557
|
-
return result
|
|
1558
|
-
|
|
1559
|
-
last_input = folder_reporter.get('last_input_folder', '')
|
|
1560
|
-
last_output = folder_reporter.get('last_output_folder', '')
|
|
1561
|
-
|
|
1562
|
-
# Check paths are valid format (if provided)
|
|
1563
|
-
for path_name, path_value in [('last_input_folder', last_input), ('last_output_folder', last_output)]:
|
|
1564
|
-
if path_value and isinstance(path_value, str):
|
|
1565
|
-
try:
|
|
1566
|
-
Path(path_value) # Basic path validation
|
|
1567
|
-
except Exception:
|
|
1568
|
-
result.passed = False
|
|
1569
|
-
result.message = f"Invalid path format in {path_name}: {path_value}"
|
|
1570
|
-
return result
|
|
1571
|
-
|
|
1572
|
-
result.passed = True
|
|
1573
|
-
result.message = "Folder reporter paths validated"
|
|
1574
|
-
return result
|
|
1575
|
-
|
|
1576
|
-
except Exception as e:
|
|
1577
|
-
result.passed = False
|
|
1578
|
-
result.message = f"Folder reporter paths check error: {e}"
|
|
1579
|
-
return result
|
|
1580
|
-
|
|
1581
|
-
def _get_expected_type(self, key: str, context: Optional[Dict[str, Any]]) -> Optional[type]:
|
|
1582
|
-
"""Get expected type for a setting key."""
|
|
1583
|
-
type_map = {
|
|
1584
|
-
'debug_level': str,
|
|
1585
|
-
'active_input_tab': int,
|
|
1586
|
-
'active_output_tab': int,
|
|
1587
|
-
'export_path': str,
|
|
1588
|
-
'selected_tool': str,
|
|
1589
|
-
'input_tabs': list,
|
|
1590
|
-
'output_tabs': list,
|
|
1591
|
-
'tool_settings': dict,
|
|
1592
|
-
'performance_settings': dict,
|
|
1593
|
-
'font_settings': dict,
|
|
1594
|
-
'dialog_settings': dict
|
|
1595
|
-
}
|
|
1596
|
-
return type_map.get(key)
|
|
1597
|
-
|
|
1598
|
-
def _get_pattern_rule(self, key: str, context: Optional[Dict[str, Any]]):
|
|
1599
|
-
"""Get pattern-based validation rule for a key."""
|
|
1600
|
-
# Add pattern-based rules as needed
|
|
1601
|
-
return None
|
|
1602
|
-
|
|
1603
|
-
def _is_sensitive_key(self, key: str) -> bool:
|
|
1604
|
-
"""Check if a key represents sensitive data."""
|
|
1605
|
-
sensitive_patterns = {
|
|
1606
|
-
'key', 'token', 'password', 'secret', 'auth', 'credential'
|
|
1607
|
-
}
|
|
1608
|
-
key_lower = key.lower()
|
|
1609
|
-
return any(pattern in key_lower for pattern in sensitive_patterns)
|
|
1610
|
-
|
|
1611
|
-
def _is_encrypted_or_placeholder(self, value: str) -> bool:
|
|
1612
|
-
"""Check if a value is encrypted or a placeholder."""
|
|
1613
|
-
if self.encryption_pattern.match(value):
|
|
1614
|
-
return True
|
|
1615
|
-
|
|
1616
|
-
placeholder_patterns = {
|
|
1617
|
-
'putinyourkey', 'your_key_here', 'enter_key', 'api_key_here',
|
|
1618
|
-
'your_token', 'enter_token', 'placeholder'
|
|
1619
|
-
}
|
|
1620
|
-
return value.lower() in placeholder_patterns
|
|
1621
|
-
|
|
1622
|
-
def _contains_obvious_secret(self, value: Any) -> bool:
|
|
1623
|
-
"""Check if value contains obvious secrets."""
|
|
1624
|
-
if not isinstance(value, str) or len(value) < 10:
|
|
1625
|
-
return False
|
|
1626
|
-
|
|
1627
|
-
# Look for patterns that suggest real API keys/tokens
|
|
1628
|
-
# This is a basic heuristic - real implementation might be more sophisticated
|
|
1629
|
-
if re.match(r'^[A-Za-z0-9]{20,}$', value): # Long alphanumeric string
|
|
1630
|
-
return True
|
|
1631
|
-
|
|
1632
|
-
if re.match(r'^sk-[A-Za-z0-9]{40,}$', value): # OpenAI-style key
|
|
1633
|
-
return True
|
|
1634
|
-
|
|
1635
|
-
return False
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
# Result classes for validation
|
|
1639
|
-
class CheckResult:
|
|
1640
|
-
"""Result of a single validation check."""
|
|
1641
|
-
|
|
1642
|
-
def __init__(self, check_name: str):
|
|
1643
|
-
self.check_name = check_name
|
|
1644
|
-
self.passed = False
|
|
1645
|
-
self.message = ""
|
|
1646
|
-
self.severity = 'error' # 'error', 'warning', 'info'
|
|
1647
|
-
|
|
1648
|
-
def __bool__(self):
|
|
1649
|
-
return self.passed
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
class ValidationResult:
|
|
1653
|
-
"""Result of validating a single setting."""
|
|
1654
|
-
|
|
1655
|
-
def __init__(self, key: str, value: Any):
|
|
1656
|
-
self.key = key
|
|
1657
|
-
self.value = value
|
|
1658
|
-
self.checks = {}
|
|
1659
|
-
self.errors = []
|
|
1660
|
-
self.warnings = []
|
|
1661
|
-
self.is_critical = False
|
|
1662
|
-
|
|
1663
|
-
def add_check(self, check_name: str, result: CheckResult):
|
|
1664
|
-
"""Add a check result."""
|
|
1665
|
-
self.checks[check_name] = result
|
|
1666
|
-
|
|
1667
|
-
if not result.passed:
|
|
1668
|
-
if result.severity == 'error':
|
|
1669
|
-
self.errors.append(f"{check_name}: {result.message}")
|
|
1670
|
-
elif result.severity == 'warning':
|
|
1671
|
-
self.warnings.append(f"{check_name}: {result.message}")
|
|
1672
|
-
|
|
1673
|
-
def add_error(self, message: str):
|
|
1674
|
-
"""Add an error message."""
|
|
1675
|
-
self.errors.append(message)
|
|
1676
|
-
|
|
1677
|
-
def add_warning(self, message: str):
|
|
1678
|
-
"""Add a warning message."""
|
|
1679
|
-
self.warnings.append(message)
|
|
1680
|
-
|
|
1681
|
-
@property
|
|
1682
|
-
def is_valid(self) -> bool:
|
|
1683
|
-
"""Check if validation passed (no errors)."""
|
|
1684
|
-
return len(self.errors) == 0
|
|
1685
|
-
|
|
1686
|
-
@property
|
|
1687
|
-
def has_warnings(self) -> bool:
|
|
1688
|
-
"""Check if there are warnings."""
|
|
1689
|
-
return len(self.warnings) > 0
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
class ToolValidationResult:
|
|
1693
|
-
"""Result of validating all settings for a tool."""
|
|
1694
|
-
|
|
1695
|
-
def __init__(self, tool_name: str, settings: Dict[str, Any]):
|
|
1696
|
-
self.tool_name = tool_name
|
|
1697
|
-
self.settings = settings
|
|
1698
|
-
self.checks = {}
|
|
1699
|
-
self.setting_results = {}
|
|
1700
|
-
self.errors = []
|
|
1701
|
-
self.warnings = []
|
|
1702
|
-
|
|
1703
|
-
def add_check(self, check_name: str, result: CheckResult):
|
|
1704
|
-
"""Add a tool-level check result."""
|
|
1705
|
-
self.checks[check_name] = result
|
|
1706
|
-
|
|
1707
|
-
if not result.passed:
|
|
1708
|
-
if result.severity == 'error':
|
|
1709
|
-
self.errors.append(f"{check_name}: {result.message}")
|
|
1710
|
-
elif result.severity == 'warning':
|
|
1711
|
-
self.warnings.append(f"{check_name}: {result.message}")
|
|
1712
|
-
|
|
1713
|
-
def add_setting_result(self, setting_key: str, result: ValidationResult):
|
|
1714
|
-
"""Add a setting validation result."""
|
|
1715
|
-
self.setting_results[setting_key] = result
|
|
1716
|
-
|
|
1717
|
-
# Aggregate errors and warnings
|
|
1718
|
-
self.errors.extend([f"{setting_key}: {err}" for err in result.errors])
|
|
1719
|
-
self.warnings.extend([f"{setting_key}: {warn}" for warn in result.warnings])
|
|
1720
|
-
|
|
1721
|
-
def add_error(self, message: str):
|
|
1722
|
-
"""Add an error message."""
|
|
1723
|
-
self.errors.append(message)
|
|
1724
|
-
|
|
1725
|
-
@property
|
|
1726
|
-
def is_valid(self) -> bool:
|
|
1727
|
-
"""Check if all validation passed."""
|
|
1728
|
-
return len(self.errors) == 0
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
class ConsistencyValidationResult:
|
|
1732
|
-
"""Result of validating consistency across all settings."""
|
|
1733
|
-
|
|
1734
|
-
def __init__(self, settings: Dict[str, Any]):
|
|
1735
|
-
self.settings = settings
|
|
1736
|
-
self.checks = {}
|
|
1737
|
-
self.errors = []
|
|
1738
|
-
self.warnings = []
|
|
1739
|
-
|
|
1740
|
-
def add_check(self, check_name: str, result: CheckResult):
|
|
1741
|
-
"""Add a consistency check result."""
|
|
1742
|
-
self.checks[check_name] = result
|
|
1743
|
-
|
|
1744
|
-
if not result.passed:
|
|
1745
|
-
if result.severity == 'error':
|
|
1746
|
-
self.errors.append(f"{check_name}: {result.message}")
|
|
1747
|
-
elif result.severity == 'warning':
|
|
1748
|
-
self.warnings.append(f"{check_name}: {result.message}")
|
|
1749
|
-
|
|
1750
|
-
def add_error(self, message: str):
|
|
1751
|
-
"""Add an error message."""
|
|
1752
|
-
self.errors.append(message)
|
|
1753
|
-
|
|
1754
|
-
@property
|
|
1755
|
-
def is_valid(self) -> bool:
|
|
1756
|
-
"""Check if all consistency checks passed."""
|
|
1757
|
-
return len(self.errors) == 0
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
class CompleteValidationResult:
|
|
1761
|
-
"""Result of complete settings validation including all checks."""
|
|
1762
|
-
|
|
1763
|
-
def __init__(self, settings: Dict[str, Any]):
|
|
1764
|
-
self.settings = settings
|
|
1765
|
-
self.core_setting_results = {}
|
|
1766
|
-
self.tool_results = {}
|
|
1767
|
-
self.consistency_result = None
|
|
1768
|
-
self.errors = []
|
|
1769
|
-
self.warnings = []
|
|
1770
|
-
|
|
1771
|
-
def add_core_setting_result(self, setting_key: str, result: ValidationResult):
|
|
1772
|
-
"""Add a core setting validation result."""
|
|
1773
|
-
self.core_setting_results[setting_key] = result
|
|
1774
|
-
|
|
1775
|
-
# Aggregate errors and warnings
|
|
1776
|
-
self.errors.extend([f"core.{setting_key}: {err}" for err in result.errors])
|
|
1777
|
-
self.warnings.extend([f"core.{setting_key}: {warn}" for warn in result.warnings])
|
|
1778
|
-
|
|
1779
|
-
def add_tool_result(self, tool_name: str, result: ToolValidationResult):
|
|
1780
|
-
"""Add a tool validation result."""
|
|
1781
|
-
self.tool_results[tool_name] = result
|
|
1782
|
-
|
|
1783
|
-
# Aggregate errors and warnings
|
|
1784
|
-
self.errors.extend([f"tool.{tool_name}: {err}" for err in result.errors])
|
|
1785
|
-
self.warnings.extend([f"tool.{tool_name}: {warn}" for warn in result.warnings])
|
|
1786
|
-
|
|
1787
|
-
def add_consistency_result(self, result: ConsistencyValidationResult):
|
|
1788
|
-
"""Add consistency validation result."""
|
|
1789
|
-
self.consistency_result = result
|
|
1790
|
-
|
|
1791
|
-
# Aggregate errors and warnings
|
|
1792
|
-
self.errors.extend([f"consistency: {err}" for err in result.errors])
|
|
1793
|
-
self.warnings.extend([f"consistency: {warn}" for warn in result.warnings])
|
|
1794
|
-
|
|
1795
|
-
def add_error(self, message: str):
|
|
1796
|
-
"""Add an error message."""
|
|
1797
|
-
self.errors.append(message)
|
|
1798
|
-
|
|
1799
|
-
def add_warning(self, message: str):
|
|
1800
|
-
"""Add a warning message."""
|
|
1801
|
-
self.warnings.append(message)
|
|
1802
|
-
|
|
1803
|
-
@property
|
|
1804
|
-
def is_valid(self) -> bool:
|
|
1805
|
-
"""Check if all validation passed (no errors)."""
|
|
1806
|
-
return len(self.errors) == 0
|
|
1807
|
-
|
|
1808
|
-
@property
|
|
1809
|
-
def has_warnings(self) -> bool:
|
|
1810
|
-
"""Check if there are warnings."""
|
|
1811
|
-
return len(self.warnings) > 0
|
|
1812
|
-
|
|
1813
|
-
def get_summary(self) -> Dict[str, Any]:
|
|
1814
|
-
"""Get validation summary."""
|
|
1815
|
-
return {
|
|
1816
|
-
'is_valid': self.is_valid,
|
|
1817
|
-
'error_count': len(self.errors),
|
|
1818
|
-
'warning_count': len(self.warnings),
|
|
1819
|
-
'core_settings_validated': len(self.core_setting_results),
|
|
1820
|
-
'tools_validated': len(self.tool_results),
|
|
1821
|
-
'consistency_checks': len(self.consistency_result.checks) if self.consistency_result else 0,
|
|
1822
|
-
'errors': self.errors,
|
|
1823
|
-
'warnings': self.warnings
|
|
1
|
+
"""
|
|
2
|
+
Settings Validation and Integrity Checking System
|
|
3
|
+
|
|
4
|
+
This module provides comprehensive validation for settings data before storage,
|
|
5
|
+
including type validation, data integrity checks for critical settings,
|
|
6
|
+
validation rules for encrypted data, and consistency checks across related settings.
|
|
7
|
+
|
|
8
|
+
Based on production codebase analysis of 45 Python files with complex settings patterns.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
from typing import Any, Dict, List, Tuple, Optional, Set, Union, Callable, TYPE_CHECKING
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from typing import Self
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SettingsValidator:
|
|
23
|
+
"""
|
|
24
|
+
Comprehensive validator for settings data with integrity checking.
|
|
25
|
+
|
|
26
|
+
Features:
|
|
27
|
+
- Type validation for all setting values before storage
|
|
28
|
+
- Data integrity checks for critical settings (tab counts, required fields)
|
|
29
|
+
- Validation rules for encrypted data and sensitive information
|
|
30
|
+
- Consistency checks across related settings
|
|
31
|
+
- Custom validation rules for specific tools and configurations
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self):
|
|
35
|
+
"""Initialize the settings validator."""
|
|
36
|
+
self.logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
# Validation rules registry
|
|
39
|
+
self.validation_rules = {}
|
|
40
|
+
self.integrity_rules = {}
|
|
41
|
+
self.consistency_rules = {}
|
|
42
|
+
|
|
43
|
+
# Initialize built-in validation rules
|
|
44
|
+
self._initialize_core_validation_rules()
|
|
45
|
+
self._initialize_tool_validation_rules()
|
|
46
|
+
self._initialize_integrity_rules()
|
|
47
|
+
self._initialize_consistency_rules()
|
|
48
|
+
|
|
49
|
+
# Encryption validation patterns
|
|
50
|
+
self.encryption_pattern = re.compile(r'^ENC:[A-Za-z0-9+/=]+$')
|
|
51
|
+
|
|
52
|
+
# Critical settings that must always be valid
|
|
53
|
+
self.critical_settings = {
|
|
54
|
+
'export_path',
|
|
55
|
+
'debug_level',
|
|
56
|
+
'selected_tool',
|
|
57
|
+
'active_input_tab',
|
|
58
|
+
'active_output_tab'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Required tool settings
|
|
62
|
+
self.required_tool_settings = {
|
|
63
|
+
'cURL Tool': {'default_timeout', 'follow_redirects', 'verify_ssl'},
|
|
64
|
+
'JSON/XML Tool': {'operation', 'json_indent', 'xml_indent'},
|
|
65
|
+
'Case Tool': {'mode'},
|
|
66
|
+
'Find & Replace': {'case_sensitive', 'use_regex'}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
def validate_setting_value(self, key: str, value: Any, context: Optional[Dict[str, Any]] = None) -> 'ValidationResult':
|
|
70
|
+
"""
|
|
71
|
+
Validate a single setting value with comprehensive checks.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
key: Setting key/path
|
|
75
|
+
value: Setting value to validate
|
|
76
|
+
context: Additional context for validation (tool name, related settings, etc.)
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
ValidationResult with validation status and details
|
|
80
|
+
"""
|
|
81
|
+
result = ValidationResult(key, value)
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
# Basic type validation
|
|
85
|
+
type_result = self._validate_type(key, value, context)
|
|
86
|
+
result.add_check('type_validation', type_result)
|
|
87
|
+
|
|
88
|
+
# Value range/format validation
|
|
89
|
+
format_result = self._validate_format(key, value, context)
|
|
90
|
+
result.add_check('format_validation', format_result)
|
|
91
|
+
|
|
92
|
+
# Security validation (for sensitive data)
|
|
93
|
+
security_result = self._validate_security(key, value, context)
|
|
94
|
+
result.add_check('security_validation', security_result)
|
|
95
|
+
|
|
96
|
+
# Custom validation rules
|
|
97
|
+
custom_result = self._validate_custom_rules(key, value, context)
|
|
98
|
+
result.add_check('custom_validation', custom_result)
|
|
99
|
+
|
|
100
|
+
# Mark as critical if this is a critical setting
|
|
101
|
+
if key in self.critical_settings:
|
|
102
|
+
result.is_critical = True
|
|
103
|
+
|
|
104
|
+
self.logger.debug(f"Validated setting '{key}': {result.is_valid}")
|
|
105
|
+
return result
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
self.logger.error(f"Validation failed for setting '{key}': {e}")
|
|
109
|
+
result.add_error(f"Validation exception: {e}")
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
def validate_tool_settings(self, tool_name: str, settings: Dict[str, Any]) -> 'ToolValidationResult':
|
|
113
|
+
"""
|
|
114
|
+
Validate all settings for a specific tool.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
tool_name: Name of the tool
|
|
118
|
+
settings: Tool settings dictionary
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
ToolValidationResult with comprehensive validation status
|
|
122
|
+
"""
|
|
123
|
+
result = ToolValidationResult(tool_name, settings)
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
# Check required settings
|
|
127
|
+
required_result = self._check_required_settings(tool_name, settings)
|
|
128
|
+
result.add_check('required_settings', required_result)
|
|
129
|
+
|
|
130
|
+
# Validate individual settings
|
|
131
|
+
for key, value in settings.items():
|
|
132
|
+
context = {'tool_name': tool_name, 'all_settings': settings}
|
|
133
|
+
setting_result = self.validate_setting_value(f"{tool_name}.{key}", value, context)
|
|
134
|
+
result.add_setting_result(key, setting_result)
|
|
135
|
+
|
|
136
|
+
# Tool-specific integrity checks
|
|
137
|
+
integrity_result = self._check_tool_integrity(tool_name, settings)
|
|
138
|
+
result.add_check('integrity_check', integrity_result)
|
|
139
|
+
|
|
140
|
+
self.logger.debug(f"Validated tool '{tool_name}': {result.is_valid}")
|
|
141
|
+
return result
|
|
142
|
+
|
|
143
|
+
except Exception as e:
|
|
144
|
+
self.logger.error(f"Tool validation failed for '{tool_name}': {e}")
|
|
145
|
+
result.add_error(f"Tool validation exception: {e}")
|
|
146
|
+
return result
|
|
147
|
+
|
|
148
|
+
def validate_settings_consistency(self, all_settings: Dict[str, Any]) -> 'ConsistencyValidationResult':
|
|
149
|
+
"""
|
|
150
|
+
Validate consistency across all settings.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
all_settings: Complete settings dictionary
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
ConsistencyValidationResult with cross-setting validation status
|
|
157
|
+
"""
|
|
158
|
+
result = ConsistencyValidationResult(all_settings)
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
# Tab consistency checks
|
|
162
|
+
tab_result = self._validate_tab_consistency(all_settings)
|
|
163
|
+
result.add_check('tab_consistency', tab_result)
|
|
164
|
+
|
|
165
|
+
# Tool selection consistency
|
|
166
|
+
tool_result = self._validate_tool_selection_consistency(all_settings)
|
|
167
|
+
result.add_check('tool_selection', tool_result)
|
|
168
|
+
|
|
169
|
+
# Performance settings consistency
|
|
170
|
+
perf_result = self._validate_performance_consistency(all_settings)
|
|
171
|
+
result.add_check('performance_consistency', perf_result)
|
|
172
|
+
|
|
173
|
+
# Font settings consistency
|
|
174
|
+
font_result = self._validate_font_consistency(all_settings)
|
|
175
|
+
result.add_check('font_consistency', font_result)
|
|
176
|
+
|
|
177
|
+
# Dialog settings consistency
|
|
178
|
+
dialog_result = self._validate_dialog_consistency(all_settings)
|
|
179
|
+
result.add_check('dialog_consistency', dialog_result)
|
|
180
|
+
|
|
181
|
+
# Integrity checks
|
|
182
|
+
for rule_name, rule_func in self.integrity_rules.items():
|
|
183
|
+
integrity_result = rule_func(all_settings)
|
|
184
|
+
result.add_check(rule_name, integrity_result)
|
|
185
|
+
|
|
186
|
+
# Custom consistency rules
|
|
187
|
+
for rule_name, rule_func in self.consistency_rules.items():
|
|
188
|
+
custom_result = rule_func(all_settings)
|
|
189
|
+
result.add_check(rule_name, custom_result)
|
|
190
|
+
|
|
191
|
+
self.logger.debug(f"Settings consistency validation: {result.is_valid}")
|
|
192
|
+
return result
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
self.logger.error(f"Consistency validation failed: {e}")
|
|
196
|
+
result.add_error(f"Consistency validation exception: {e}")
|
|
197
|
+
return result
|
|
198
|
+
|
|
199
|
+
def validate_complete_settings(self, settings: Dict[str, Any]) -> 'CompleteValidationResult':
|
|
200
|
+
"""
|
|
201
|
+
Perform complete validation of all settings including type validation,
|
|
202
|
+
integrity checks, and consistency validation.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
settings: Complete settings dictionary
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
CompleteValidationResult with comprehensive validation status
|
|
209
|
+
"""
|
|
210
|
+
result = CompleteValidationResult(settings)
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
# 1. Validate core settings
|
|
214
|
+
core_settings = {k: v for k, v in settings.items()
|
|
215
|
+
if k not in ('tool_settings', 'performance_settings', 'font_settings', 'dialog_settings')}
|
|
216
|
+
|
|
217
|
+
for key, value in core_settings.items():
|
|
218
|
+
setting_result = self.validate_setting_value(key, value)
|
|
219
|
+
result.add_core_setting_result(key, setting_result)
|
|
220
|
+
|
|
221
|
+
# 2. Validate tool settings
|
|
222
|
+
tool_settings = settings.get('tool_settings', {})
|
|
223
|
+
if isinstance(tool_settings, dict):
|
|
224
|
+
for tool_name, tool_config in tool_settings.items():
|
|
225
|
+
if isinstance(tool_config, dict):
|
|
226
|
+
tool_result = self.validate_tool_settings(tool_name, tool_config)
|
|
227
|
+
result.add_tool_result(tool_name, tool_result)
|
|
228
|
+
|
|
229
|
+
# 3. Validate consistency across all settings
|
|
230
|
+
consistency_result = self.validate_settings_consistency(settings)
|
|
231
|
+
result.add_consistency_result(consistency_result)
|
|
232
|
+
|
|
233
|
+
self.logger.info(f"Complete settings validation: {result.is_valid} ({len(result.errors)} errors, {len(result.warnings)} warnings)")
|
|
234
|
+
return result
|
|
235
|
+
|
|
236
|
+
except Exception as e:
|
|
237
|
+
self.logger.error(f"Complete validation failed: {e}")
|
|
238
|
+
result.add_error(f"Complete validation exception: {e}")
|
|
239
|
+
return result
|
|
240
|
+
|
|
241
|
+
def _validate_type(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
|
|
242
|
+
"""Validate the type of a setting value."""
|
|
243
|
+
result = CheckResult('type_validation')
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
# Get expected type for this key
|
|
247
|
+
expected_type = self._get_expected_type(key, context)
|
|
248
|
+
|
|
249
|
+
if expected_type is None:
|
|
250
|
+
# No specific type requirement
|
|
251
|
+
result.passed = True
|
|
252
|
+
result.message = "No type constraint"
|
|
253
|
+
return result
|
|
254
|
+
|
|
255
|
+
# Check type compatibility
|
|
256
|
+
if isinstance(expected_type, tuple):
|
|
257
|
+
# Multiple allowed types
|
|
258
|
+
if any(isinstance(value, t) for t in expected_type):
|
|
259
|
+
result.passed = True
|
|
260
|
+
result.message = f"Type {type(value).__name__} is allowed"
|
|
261
|
+
else:
|
|
262
|
+
result.passed = False
|
|
263
|
+
result.message = f"Type {type(value).__name__} not in allowed types {expected_type}"
|
|
264
|
+
else:
|
|
265
|
+
# Single expected type
|
|
266
|
+
if isinstance(value, expected_type):
|
|
267
|
+
result.passed = True
|
|
268
|
+
result.message = f"Type {type(value).__name__} matches expected {expected_type.__name__}"
|
|
269
|
+
else:
|
|
270
|
+
result.passed = False
|
|
271
|
+
result.message = f"Type {type(value).__name__} does not match expected {expected_type.__name__}"
|
|
272
|
+
|
|
273
|
+
return result
|
|
274
|
+
|
|
275
|
+
except Exception as e:
|
|
276
|
+
result.passed = False
|
|
277
|
+
result.message = f"Type validation error: {e}"
|
|
278
|
+
return result
|
|
279
|
+
|
|
280
|
+
def _validate_format(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
|
|
281
|
+
"""Validate the format/range of a setting value."""
|
|
282
|
+
result = CheckResult('format_validation')
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
# Get validation rule for this key
|
|
286
|
+
rule = self.validation_rules.get(key)
|
|
287
|
+
|
|
288
|
+
if rule is None:
|
|
289
|
+
# Check for pattern-based rules
|
|
290
|
+
rule = self._get_pattern_rule(key, context)
|
|
291
|
+
|
|
292
|
+
if rule is None:
|
|
293
|
+
result.passed = True
|
|
294
|
+
result.message = "No format constraint"
|
|
295
|
+
return result
|
|
296
|
+
|
|
297
|
+
# Apply validation rule
|
|
298
|
+
if callable(rule):
|
|
299
|
+
rule_result = rule(value, context)
|
|
300
|
+
if isinstance(rule_result, bool):
|
|
301
|
+
result.passed = rule_result
|
|
302
|
+
result.message = "Custom rule validation"
|
|
303
|
+
elif isinstance(rule_result, dict):
|
|
304
|
+
result.passed = rule_result.get('valid', False)
|
|
305
|
+
result.message = rule_result.get('message', 'Custom rule validation')
|
|
306
|
+
else:
|
|
307
|
+
result.passed = bool(rule_result)
|
|
308
|
+
result.message = "Custom rule validation"
|
|
309
|
+
else:
|
|
310
|
+
# Pattern-based rule
|
|
311
|
+
if isinstance(value, str) and hasattr(rule, 'match'):
|
|
312
|
+
result.passed = bool(rule.match(value))
|
|
313
|
+
result.message = f"Pattern match: {rule.pattern}"
|
|
314
|
+
else:
|
|
315
|
+
result.passed = False
|
|
316
|
+
result.message = f"Cannot apply pattern rule to {type(value).__name__}"
|
|
317
|
+
|
|
318
|
+
return result
|
|
319
|
+
|
|
320
|
+
except Exception as e:
|
|
321
|
+
result.passed = False
|
|
322
|
+
result.message = f"Format validation error: {e}"
|
|
323
|
+
return result
|
|
324
|
+
|
|
325
|
+
def _validate_security(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
|
|
326
|
+
"""Validate security aspects of a setting value."""
|
|
327
|
+
result = CheckResult('security_validation')
|
|
328
|
+
|
|
329
|
+
try:
|
|
330
|
+
# Check for sensitive data patterns
|
|
331
|
+
if self._is_sensitive_key(key):
|
|
332
|
+
if isinstance(value, str):
|
|
333
|
+
# Check if encrypted properly
|
|
334
|
+
if key.upper().endswith('KEY') or key.upper().endswith('TOKEN'):
|
|
335
|
+
if not self._is_encrypted_or_placeholder(value):
|
|
336
|
+
result.passed = False
|
|
337
|
+
result.message = "Sensitive data should be encrypted or placeholder"
|
|
338
|
+
result.severity = 'warning' # Not critical, but recommended
|
|
339
|
+
return result
|
|
340
|
+
|
|
341
|
+
# Check for obvious secrets in plain text
|
|
342
|
+
if self._contains_obvious_secret(value):
|
|
343
|
+
result.passed = False
|
|
344
|
+
result.message = "Possible plain text secret detected"
|
|
345
|
+
result.severity = 'warning'
|
|
346
|
+
return result
|
|
347
|
+
|
|
348
|
+
result.passed = True
|
|
349
|
+
result.message = "Security validation passed"
|
|
350
|
+
return result
|
|
351
|
+
|
|
352
|
+
except Exception as e:
|
|
353
|
+
result.passed = False
|
|
354
|
+
result.message = f"Security validation error: {e}"
|
|
355
|
+
return result
|
|
356
|
+
|
|
357
|
+
def _validate_custom_rules(self, key: str, value: Any, context: Optional[Dict[str, Any]]) -> 'CheckResult':
|
|
358
|
+
"""Apply custom validation rules for specific settings."""
|
|
359
|
+
result = CheckResult('custom_validation')
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
# Tab index validation
|
|
363
|
+
if key in ('active_input_tab', 'active_output_tab'):
|
|
364
|
+
if not isinstance(value, int) or not (0 <= value < 7):
|
|
365
|
+
result.passed = False
|
|
366
|
+
result.message = "Tab index must be integer between 0 and 6"
|
|
367
|
+
return result
|
|
368
|
+
|
|
369
|
+
# Debug level validation
|
|
370
|
+
elif key == 'debug_level':
|
|
371
|
+
valid_levels = {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'}
|
|
372
|
+
if value not in valid_levels:
|
|
373
|
+
result.passed = False
|
|
374
|
+
result.message = f"Debug level must be one of {valid_levels}"
|
|
375
|
+
return result
|
|
376
|
+
|
|
377
|
+
# Export path validation
|
|
378
|
+
elif key == 'export_path':
|
|
379
|
+
if not isinstance(value, str) or not value.strip():
|
|
380
|
+
result.passed = False
|
|
381
|
+
result.message = "Export path must be non-empty string"
|
|
382
|
+
return result
|
|
383
|
+
|
|
384
|
+
# Check if path is reasonable (not validating existence)
|
|
385
|
+
try:
|
|
386
|
+
Path(value) # This will raise if path is invalid
|
|
387
|
+
except Exception:
|
|
388
|
+
result.passed = False
|
|
389
|
+
result.message = "Export path format is invalid"
|
|
390
|
+
return result
|
|
391
|
+
|
|
392
|
+
# Tool name validation
|
|
393
|
+
elif key == 'selected_tool':
|
|
394
|
+
if not isinstance(value, str) or not value.strip():
|
|
395
|
+
result.passed = False
|
|
396
|
+
result.message = "Selected tool must be non-empty string"
|
|
397
|
+
return result
|
|
398
|
+
|
|
399
|
+
result.passed = True
|
|
400
|
+
result.message = "Custom validation passed"
|
|
401
|
+
return result
|
|
402
|
+
|
|
403
|
+
except Exception as e:
|
|
404
|
+
result.passed = False
|
|
405
|
+
result.message = f"Custom validation error: {e}"
|
|
406
|
+
return result
|
|
407
|
+
|
|
408
|
+
def _check_required_settings(self, tool_name: str, settings: Dict[str, Any]) -> 'CheckResult':
|
|
409
|
+
"""Check that all required settings are present for a tool."""
|
|
410
|
+
result = CheckResult('required_settings')
|
|
411
|
+
|
|
412
|
+
try:
|
|
413
|
+
required = self.required_tool_settings.get(tool_name, set())
|
|
414
|
+
missing = required - set(settings.keys())
|
|
415
|
+
|
|
416
|
+
if missing:
|
|
417
|
+
result.passed = False
|
|
418
|
+
result.message = f"Missing required settings: {missing}"
|
|
419
|
+
else:
|
|
420
|
+
result.passed = True
|
|
421
|
+
result.message = "All required settings present"
|
|
422
|
+
|
|
423
|
+
return result
|
|
424
|
+
|
|
425
|
+
except Exception as e:
|
|
426
|
+
result.passed = False
|
|
427
|
+
result.message = f"Required settings check error: {e}"
|
|
428
|
+
return result
|
|
429
|
+
|
|
430
|
+
def _check_tool_integrity(self, tool_name: str, settings: Dict[str, Any]) -> 'CheckResult':
|
|
431
|
+
"""Check tool-specific integrity constraints."""
|
|
432
|
+
result = CheckResult('tool_integrity')
|
|
433
|
+
|
|
434
|
+
try:
|
|
435
|
+
# cURL Tool specific checks
|
|
436
|
+
if tool_name == 'cURL Tool':
|
|
437
|
+
return self._validate_curl_tool_integrity(settings)
|
|
438
|
+
|
|
439
|
+
# AI Tools specific checks
|
|
440
|
+
elif tool_name in ('Google AI', 'OpenAI', 'Anthropic'):
|
|
441
|
+
return self._validate_ai_tool_integrity(settings)
|
|
442
|
+
|
|
443
|
+
# JSON/XML Tool specific checks
|
|
444
|
+
elif tool_name == 'JSON/XML Tool':
|
|
445
|
+
return self._validate_json_xml_tool_integrity(settings)
|
|
446
|
+
|
|
447
|
+
# Default: basic integrity check
|
|
448
|
+
result.passed = True
|
|
449
|
+
result.message = "Basic integrity check passed"
|
|
450
|
+
return result
|
|
451
|
+
|
|
452
|
+
except Exception as e:
|
|
453
|
+
result.passed = False
|
|
454
|
+
result.message = f"Tool integrity check error: {e}"
|
|
455
|
+
return result
|
|
456
|
+
|
|
457
|
+
def _validate_curl_tool_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
458
|
+
"""Validate cURL tool specific integrity."""
|
|
459
|
+
result = CheckResult('curl_integrity')
|
|
460
|
+
|
|
461
|
+
try:
|
|
462
|
+
# Check timeout is reasonable
|
|
463
|
+
timeout = settings.get('default_timeout', 90)
|
|
464
|
+
if not isinstance(timeout, (int, float)) or timeout <= 0 or timeout > 3600:
|
|
465
|
+
result.passed = False
|
|
466
|
+
result.message = "Default timeout must be between 1 and 3600 seconds"
|
|
467
|
+
return result
|
|
468
|
+
|
|
469
|
+
# Check max_redirects is reasonable
|
|
470
|
+
max_redirects = settings.get('max_redirects', 10)
|
|
471
|
+
if not isinstance(max_redirects, int) or max_redirects < 0 or max_redirects > 50:
|
|
472
|
+
result.passed = False
|
|
473
|
+
result.message = "Max redirects must be between 0 and 50"
|
|
474
|
+
return result
|
|
475
|
+
|
|
476
|
+
# Check history settings
|
|
477
|
+
max_history = settings.get('max_history_items', 100)
|
|
478
|
+
if not isinstance(max_history, int) or max_history < 0 or max_history > 10000:
|
|
479
|
+
result.passed = False
|
|
480
|
+
result.message = "Max history items must be between 0 and 10000"
|
|
481
|
+
return result
|
|
482
|
+
|
|
483
|
+
# Validate history structure if present
|
|
484
|
+
history = settings.get('history', [])
|
|
485
|
+
if not isinstance(history, list):
|
|
486
|
+
result.passed = False
|
|
487
|
+
result.message = "History must be a list"
|
|
488
|
+
return result
|
|
489
|
+
|
|
490
|
+
# Check history entries structure
|
|
491
|
+
for i, entry in enumerate(history[:5]): # Check first 5 entries
|
|
492
|
+
if not isinstance(entry, dict):
|
|
493
|
+
result.passed = False
|
|
494
|
+
result.message = f"History entry {i} must be a dictionary"
|
|
495
|
+
return result
|
|
496
|
+
|
|
497
|
+
required_fields = {'timestamp', 'method', 'url', 'status_code'}
|
|
498
|
+
missing_fields = required_fields - set(entry.keys())
|
|
499
|
+
if missing_fields:
|
|
500
|
+
result.passed = False
|
|
501
|
+
result.message = f"History entry {i} missing fields: {missing_fields}"
|
|
502
|
+
return result
|
|
503
|
+
|
|
504
|
+
result.passed = True
|
|
505
|
+
result.message = "cURL tool integrity validated"
|
|
506
|
+
return result
|
|
507
|
+
|
|
508
|
+
except Exception as e:
|
|
509
|
+
result.passed = False
|
|
510
|
+
result.message = f"cURL integrity validation error: {e}"
|
|
511
|
+
return result
|
|
512
|
+
|
|
513
|
+
def _validate_ai_tool_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
514
|
+
"""Validate AI tool specific integrity."""
|
|
515
|
+
result = CheckResult('ai_tool_integrity')
|
|
516
|
+
|
|
517
|
+
try:
|
|
518
|
+
# Check temperature range
|
|
519
|
+
temperature = settings.get('temperature', 0.7)
|
|
520
|
+
if not isinstance(temperature, (int, float)) or not (0.0 <= temperature <= 2.0):
|
|
521
|
+
result.passed = False
|
|
522
|
+
result.message = "Temperature must be between 0.0 and 2.0"
|
|
523
|
+
return result
|
|
524
|
+
|
|
525
|
+
# Check token limits
|
|
526
|
+
max_tokens = settings.get('maxOutputTokens', 8192)
|
|
527
|
+
if not isinstance(max_tokens, int) or max_tokens <= 0 or max_tokens > 100000:
|
|
528
|
+
result.passed = False
|
|
529
|
+
result.message = "Max output tokens must be between 1 and 100000"
|
|
530
|
+
return result
|
|
531
|
+
|
|
532
|
+
# Check model list
|
|
533
|
+
models_list = settings.get('MODELS_LIST', [])
|
|
534
|
+
if not isinstance(models_list, list) or not models_list:
|
|
535
|
+
result.passed = False
|
|
536
|
+
result.message = "Models list must be non-empty list"
|
|
537
|
+
return result
|
|
538
|
+
|
|
539
|
+
# Check current model is in list
|
|
540
|
+
current_model = settings.get('MODEL')
|
|
541
|
+
if current_model and current_model not in models_list:
|
|
542
|
+
result.passed = False
|
|
543
|
+
result.message = "Current model must be in models list"
|
|
544
|
+
return result
|
|
545
|
+
|
|
546
|
+
result.passed = True
|
|
547
|
+
result.message = "AI tool integrity validated"
|
|
548
|
+
return result
|
|
549
|
+
|
|
550
|
+
except Exception as e:
|
|
551
|
+
result.passed = False
|
|
552
|
+
result.message = f"AI tool integrity validation error: {e}"
|
|
553
|
+
return result
|
|
554
|
+
|
|
555
|
+
def _validate_json_xml_tool_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
556
|
+
"""Validate JSON/XML tool specific integrity."""
|
|
557
|
+
result = CheckResult('json_xml_integrity')
|
|
558
|
+
|
|
559
|
+
try:
|
|
560
|
+
# Check indent values
|
|
561
|
+
for indent_key in ('json_indent', 'xml_indent'):
|
|
562
|
+
indent = settings.get(indent_key, 2)
|
|
563
|
+
if not isinstance(indent, int) or indent < 0 or indent > 10:
|
|
564
|
+
result.passed = False
|
|
565
|
+
result.message = f"{indent_key} must be between 0 and 10"
|
|
566
|
+
return result
|
|
567
|
+
|
|
568
|
+
# Check operation is valid
|
|
569
|
+
operation = settings.get('operation')
|
|
570
|
+
valid_operations = {
|
|
571
|
+
'json_to_xml', 'xml_to_json', 'format_json', 'format_xml',
|
|
572
|
+
'minify_json', 'validate_json', 'validate_xml'
|
|
573
|
+
}
|
|
574
|
+
if operation and operation not in valid_operations:
|
|
575
|
+
result.passed = False
|
|
576
|
+
result.message = f"Operation must be one of {valid_operations}"
|
|
577
|
+
return result
|
|
578
|
+
|
|
579
|
+
result.passed = True
|
|
580
|
+
result.message = "JSON/XML tool integrity validated"
|
|
581
|
+
return result
|
|
582
|
+
|
|
583
|
+
except Exception as e:
|
|
584
|
+
result.passed = False
|
|
585
|
+
result.message = f"JSON/XML integrity validation error: {e}"
|
|
586
|
+
return result
|
|
587
|
+
|
|
588
|
+
def _validate_tab_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
589
|
+
"""Validate tab-related consistency."""
|
|
590
|
+
result = CheckResult('tab_consistency')
|
|
591
|
+
|
|
592
|
+
try:
|
|
593
|
+
# Check tab arrays exist and have correct length
|
|
594
|
+
input_tabs = settings.get('input_tabs', [])
|
|
595
|
+
output_tabs = settings.get('output_tabs', [])
|
|
596
|
+
|
|
597
|
+
if not isinstance(input_tabs, list) or len(input_tabs) != 7:
|
|
598
|
+
result.passed = False
|
|
599
|
+
result.message = "input_tabs must be list of length 7"
|
|
600
|
+
return result
|
|
601
|
+
|
|
602
|
+
if not isinstance(output_tabs, list) or len(output_tabs) != 7:
|
|
603
|
+
result.passed = False
|
|
604
|
+
result.message = "output_tabs must be list of length 7"
|
|
605
|
+
return result
|
|
606
|
+
|
|
607
|
+
# Check active tab indices are valid
|
|
608
|
+
active_input = settings.get('active_input_tab', 0)
|
|
609
|
+
active_output = settings.get('active_output_tab', 0)
|
|
610
|
+
|
|
611
|
+
if not isinstance(active_input, int) or not (0 <= active_input < 7):
|
|
612
|
+
result.passed = False
|
|
613
|
+
result.message = "active_input_tab must be integer between 0 and 6"
|
|
614
|
+
return result
|
|
615
|
+
|
|
616
|
+
if not isinstance(active_output, int) or not (0 <= active_output < 7):
|
|
617
|
+
result.passed = False
|
|
618
|
+
result.message = "active_output_tab must be integer between 0 and 6"
|
|
619
|
+
return result
|
|
620
|
+
|
|
621
|
+
result.passed = True
|
|
622
|
+
result.message = "Tab consistency validated"
|
|
623
|
+
return result
|
|
624
|
+
|
|
625
|
+
except Exception as e:
|
|
626
|
+
result.passed = False
|
|
627
|
+
result.message = f"Tab consistency validation error: {e}"
|
|
628
|
+
return result
|
|
629
|
+
|
|
630
|
+
def _validate_tool_selection_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
631
|
+
"""Validate tool selection consistency."""
|
|
632
|
+
result = CheckResult('tool_selection_consistency')
|
|
633
|
+
|
|
634
|
+
try:
|
|
635
|
+
selected_tool = settings.get('selected_tool')
|
|
636
|
+
tool_settings = settings.get('tool_settings', {})
|
|
637
|
+
|
|
638
|
+
if selected_tool and selected_tool not in tool_settings:
|
|
639
|
+
result.passed = False
|
|
640
|
+
result.message = f"Selected tool '{selected_tool}' not found in tool_settings"
|
|
641
|
+
return result
|
|
642
|
+
|
|
643
|
+
result.passed = True
|
|
644
|
+
result.message = "Tool selection consistency validated"
|
|
645
|
+
return result
|
|
646
|
+
|
|
647
|
+
except Exception as e:
|
|
648
|
+
result.passed = False
|
|
649
|
+
result.message = f"Tool selection consistency validation error: {e}"
|
|
650
|
+
return result
|
|
651
|
+
|
|
652
|
+
def _validate_performance_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
653
|
+
"""Validate performance settings consistency."""
|
|
654
|
+
result = CheckResult('performance_consistency')
|
|
655
|
+
|
|
656
|
+
try:
|
|
657
|
+
perf_settings = settings.get('performance_settings', {})
|
|
658
|
+
|
|
659
|
+
if not isinstance(perf_settings, dict):
|
|
660
|
+
result.passed = True # Optional settings
|
|
661
|
+
result.message = "Performance settings not configured"
|
|
662
|
+
return result
|
|
663
|
+
|
|
664
|
+
# Check mode consistency
|
|
665
|
+
mode = perf_settings.get('mode', 'automatic')
|
|
666
|
+
if mode not in ('automatic', 'manual', 'disabled'):
|
|
667
|
+
result.passed = False
|
|
668
|
+
result.message = "Performance mode must be 'automatic', 'manual', or 'disabled'"
|
|
669
|
+
return result
|
|
670
|
+
|
|
671
|
+
# If disabled, other settings should be disabled too
|
|
672
|
+
if mode == 'disabled':
|
|
673
|
+
for category in ('async_processing', 'caching', 'memory_management', 'ui_optimizations'):
|
|
674
|
+
category_settings = perf_settings.get(category, {})
|
|
675
|
+
if isinstance(category_settings, dict) and category_settings.get('enabled', False):
|
|
676
|
+
result.passed = False
|
|
677
|
+
result.message = f"Performance mode is disabled but {category} is enabled"
|
|
678
|
+
return result
|
|
679
|
+
|
|
680
|
+
result.passed = True
|
|
681
|
+
result.message = "Performance consistency validated"
|
|
682
|
+
return result
|
|
683
|
+
|
|
684
|
+
except Exception as e:
|
|
685
|
+
result.passed = False
|
|
686
|
+
result.message = f"Performance consistency validation error: {e}"
|
|
687
|
+
return result
|
|
688
|
+
|
|
689
|
+
def _validate_font_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
690
|
+
"""Validate font settings consistency."""
|
|
691
|
+
result = CheckResult('font_consistency')
|
|
692
|
+
|
|
693
|
+
try:
|
|
694
|
+
font_settings = settings.get('font_settings', {})
|
|
695
|
+
|
|
696
|
+
if not isinstance(font_settings, dict):
|
|
697
|
+
result.passed = True # Optional settings
|
|
698
|
+
result.message = "Font settings not configured"
|
|
699
|
+
return result
|
|
700
|
+
|
|
701
|
+
# Check required font types
|
|
702
|
+
for font_type in ('text_font', 'interface_font'):
|
|
703
|
+
font_config = font_settings.get(font_type, {})
|
|
704
|
+
if not isinstance(font_config, dict):
|
|
705
|
+
continue
|
|
706
|
+
|
|
707
|
+
# Check required properties
|
|
708
|
+
if 'family' in font_config and 'size' in font_config:
|
|
709
|
+
size = font_config['size']
|
|
710
|
+
if not isinstance(size, (int, float)) or size <= 0 or size > 72:
|
|
711
|
+
result.passed = False
|
|
712
|
+
result.message = f"{font_type} size must be between 1 and 72"
|
|
713
|
+
return result
|
|
714
|
+
|
|
715
|
+
result.passed = True
|
|
716
|
+
result.message = "Font consistency validated"
|
|
717
|
+
return result
|
|
718
|
+
|
|
719
|
+
except Exception as e:
|
|
720
|
+
result.passed = False
|
|
721
|
+
result.message = f"Font consistency validation error: {e}"
|
|
722
|
+
return result
|
|
723
|
+
|
|
724
|
+
def _validate_dialog_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
725
|
+
"""Validate dialog settings consistency."""
|
|
726
|
+
result = CheckResult('dialog_consistency')
|
|
727
|
+
|
|
728
|
+
try:
|
|
729
|
+
dialog_settings = settings.get('dialog_settings', {})
|
|
730
|
+
|
|
731
|
+
if not isinstance(dialog_settings, dict):
|
|
732
|
+
result.passed = True # Optional settings
|
|
733
|
+
result.message = "Dialog settings not configured"
|
|
734
|
+
return result
|
|
735
|
+
|
|
736
|
+
# Error dialogs must always be enabled and locked
|
|
737
|
+
error_config = dialog_settings.get('error', {})
|
|
738
|
+
if isinstance(error_config, dict):
|
|
739
|
+
if not error_config.get('enabled', True):
|
|
740
|
+
result.passed = False
|
|
741
|
+
result.message = "Error dialogs must be enabled"
|
|
742
|
+
return result
|
|
743
|
+
|
|
744
|
+
if not error_config.get('locked', True):
|
|
745
|
+
result.passed = False
|
|
746
|
+
result.message = "Error dialogs must be locked"
|
|
747
|
+
return result
|
|
748
|
+
|
|
749
|
+
result.passed = True
|
|
750
|
+
result.message = "Dialog consistency validated"
|
|
751
|
+
return result
|
|
752
|
+
|
|
753
|
+
except Exception as e:
|
|
754
|
+
result.passed = False
|
|
755
|
+
result.message = f"Dialog consistency validation error: {e}"
|
|
756
|
+
return result
|
|
757
|
+
|
|
758
|
+
def _initialize_core_validation_rules(self):
|
|
759
|
+
"""Initialize validation rules for core settings."""
|
|
760
|
+
self.validation_rules.update({
|
|
761
|
+
'debug_level': lambda v, c: v in {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'},
|
|
762
|
+
'active_input_tab': lambda v, c: isinstance(v, int) and 0 <= v < 7,
|
|
763
|
+
'active_output_tab': lambda v, c: isinstance(v, int) and 0 <= v < 7,
|
|
764
|
+
'export_path': lambda v, c: isinstance(v, str) and v.strip(),
|
|
765
|
+
'selected_tool': lambda v, c: isinstance(v, str) and v.strip()
|
|
766
|
+
})
|
|
767
|
+
|
|
768
|
+
def _initialize_tool_validation_rules(self):
|
|
769
|
+
"""Initialize validation rules for tool-specific settings."""
|
|
770
|
+
# cURL Tool validation rules
|
|
771
|
+
self.validation_rules.update({
|
|
772
|
+
'cURL Tool.default_timeout': lambda v, c: isinstance(v, (int, float)) and 1 <= v <= 3600,
|
|
773
|
+
'cURL Tool.max_redirects': lambda v, c: isinstance(v, int) and 0 <= v <= 50,
|
|
774
|
+
'cURL Tool.max_history_items': lambda v, c: isinstance(v, int) and 0 <= v <= 10000,
|
|
775
|
+
'cURL Tool.history_retention_days': lambda v, c: isinstance(v, int) and 1 <= v <= 365,
|
|
776
|
+
'cURL Tool.auth_timeout_minutes': lambda v, c: isinstance(v, int) and 1 <= v <= 1440,
|
|
777
|
+
'cURL Tool.download_chunk_size': lambda v, c: isinstance(v, int) and 1024 <= v <= 1048576,
|
|
778
|
+
'cURL Tool.max_log_size_mb': lambda v, c: isinstance(v, (int, float)) and 1 <= v <= 100,
|
|
779
|
+
'cURL Tool.connection_pool_size': lambda v, c: isinstance(v, int) and 1 <= v <= 100,
|
|
780
|
+
'cURL Tool.retry_attempts': lambda v, c: isinstance(v, int) and 0 <= v <= 10,
|
|
781
|
+
'cURL Tool.retry_delay_seconds': lambda v, c: isinstance(v, (int, float)) and 0 <= v <= 60,
|
|
782
|
+
})
|
|
783
|
+
|
|
784
|
+
# AI Tools validation rules
|
|
785
|
+
ai_tools = ['Google AI', 'Anthropic AI', 'OpenAI', 'Cohere AI', 'HuggingFace AI', 'Groq AI', 'OpenRouterAI', 'AWS Bedrock', 'LM Studio']
|
|
786
|
+
for tool in ai_tools:
|
|
787
|
+
self.validation_rules.update({
|
|
788
|
+
f'{tool}.temperature': lambda v, c: isinstance(v, (int, float)) and 0.0 <= v <= 2.0,
|
|
789
|
+
f'{tool}.max_tokens': lambda v, c: isinstance(v, int) and 1 <= v <= 100000,
|
|
790
|
+
f'{tool}.maxOutputTokens': lambda v, c: isinstance(v, int) and 1 <= v <= 100000,
|
|
791
|
+
f'{tool}.MAX_TOKENS': lambda v, c: isinstance(v, str) and v.isdigit() and 1 <= int(v) <= 100000,
|
|
792
|
+
f'{tool}.CONTEXT_WINDOW': lambda v, c: isinstance(v, str) and v.isdigit() and 1 <= int(v) <= 1000000,
|
|
793
|
+
f'{tool}.topK': lambda v, c: isinstance(v, int) and 1 <= v <= 100,
|
|
794
|
+
f'{tool}.top_k': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
|
|
795
|
+
f'{tool}.topP': lambda v, c: isinstance(v, (int, float)) and 0.0 <= v <= 1.0,
|
|
796
|
+
f'{tool}.top_p': lambda v, c: isinstance(v, (int, float)) and 0.0 <= v <= 1.0,
|
|
797
|
+
f'{tool}.candidateCount': lambda v, c: isinstance(v, int) and 1 <= v <= 10,
|
|
798
|
+
f'{tool}.frequency_penalty': lambda v, c: isinstance(v, (int, float)) and -2.0 <= v <= 2.0,
|
|
799
|
+
f'{tool}.presence_penalty': lambda v, c: isinstance(v, (int, float)) and -2.0 <= v <= 2.0,
|
|
800
|
+
f'{tool}.repetition_penalty': lambda v, c: isinstance(v, (int, float)) and 0.1 <= v <= 2.0,
|
|
801
|
+
})
|
|
802
|
+
|
|
803
|
+
# JSON/XML Tool validation rules
|
|
804
|
+
self.validation_rules.update({
|
|
805
|
+
'JSON/XML Tool.json_indent': lambda v, c: isinstance(v, int) and 0 <= v <= 10,
|
|
806
|
+
'JSON/XML Tool.xml_indent': lambda v, c: isinstance(v, int) and 0 <= v <= 10,
|
|
807
|
+
'JSON/XML Tool.operation': lambda v, c: v in {
|
|
808
|
+
'json_to_xml', 'xml_to_json', 'format_json', 'format_xml',
|
|
809
|
+
'minify_json', 'validate_json', 'validate_xml'
|
|
810
|
+
},
|
|
811
|
+
})
|
|
812
|
+
|
|
813
|
+
# Generator Tools validation rules
|
|
814
|
+
self.validation_rules.update({
|
|
815
|
+
'Generator Tools.Strong Password Generator.length': lambda v, c: isinstance(v, int) and 4 <= v <= 128,
|
|
816
|
+
'Generator Tools.Strong Password Generator.letters_percent': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
|
|
817
|
+
'Generator Tools.Strong Password Generator.numbers_percent': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
|
|
818
|
+
'Generator Tools.Strong Password Generator.symbols_percent': lambda v, c: isinstance(v, int) and 0 <= v <= 100,
|
|
819
|
+
'Generator Tools.Repeating Text Generator.times': lambda v, c: isinstance(v, int) and 1 <= v <= 10000,
|
|
820
|
+
'Generator Tools.Lorem Ipsum Generator.count': lambda v, c: isinstance(v, int) and 1 <= v <= 1000,
|
|
821
|
+
'Generator Tools.UUID/GUID Generator.version': lambda v, c: v in {1, 3, 4, 5},
|
|
822
|
+
'Generator Tools.UUID/GUID Generator.count': lambda v, c: isinstance(v, int) and 1 <= v <= 1000,
|
|
823
|
+
})
|
|
824
|
+
|
|
825
|
+
# Folder File Reporter validation rules
|
|
826
|
+
self.validation_rules.update({
|
|
827
|
+
'Folder File Reporter.recursion_depth': lambda v, c: isinstance(v, int) and 0 <= v <= 20,
|
|
828
|
+
'Folder File Reporter.recursion_mode': lambda v, c: v in {'none', 'single', 'full'},
|
|
829
|
+
'Folder File Reporter.size_format': lambda v, c: v in {'bytes', 'human', 'kb', 'mb', 'gb'},
|
|
830
|
+
})
|
|
831
|
+
|
|
832
|
+
# Cron Tool validation rules
|
|
833
|
+
self.validation_rules.update({
|
|
834
|
+
'Cron Tool.next_runs_count': lambda v, c: isinstance(v, int) and 1 <= v <= 100,
|
|
835
|
+
'Cron Tool.action': lambda v, c: v in {'parse_explain', 'generate', 'validate'},
|
|
836
|
+
})
|
|
837
|
+
|
|
838
|
+
def _initialize_integrity_rules(self):
|
|
839
|
+
"""Initialize integrity checking rules."""
|
|
840
|
+
# Critical settings that must always be present and valid
|
|
841
|
+
self.integrity_rules.update({
|
|
842
|
+
'tab_arrays_length': self._check_tab_arrays_integrity,
|
|
843
|
+
'active_tab_indices': self._check_active_tab_integrity,
|
|
844
|
+
'tool_settings_structure': self._check_tool_settings_structure,
|
|
845
|
+
'performance_settings_hierarchy': self._check_performance_hierarchy,
|
|
846
|
+
'font_settings_completeness': self._check_font_settings_completeness,
|
|
847
|
+
'dialog_settings_locked_fields': self._check_dialog_locked_fields,
|
|
848
|
+
'ai_model_consistency': self._check_ai_model_consistency,
|
|
849
|
+
'curl_history_structure': self._check_curl_history_structure,
|
|
850
|
+
'encrypted_data_format': self._check_encrypted_data_format,
|
|
851
|
+
'generator_tools_percentages': self._check_generator_percentages,
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
def _check_tab_arrays_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
855
|
+
"""Check that tab arrays have correct structure and length."""
|
|
856
|
+
result = CheckResult('tab_arrays_integrity')
|
|
857
|
+
|
|
858
|
+
try:
|
|
859
|
+
input_tabs = settings.get('input_tabs', [])
|
|
860
|
+
output_tabs = settings.get('output_tabs', [])
|
|
861
|
+
|
|
862
|
+
# Check arrays exist and are lists
|
|
863
|
+
if not isinstance(input_tabs, list):
|
|
864
|
+
result.passed = False
|
|
865
|
+
result.message = "input_tabs must be a list"
|
|
866
|
+
return result
|
|
867
|
+
|
|
868
|
+
if not isinstance(output_tabs, list):
|
|
869
|
+
result.passed = False
|
|
870
|
+
result.message = "output_tabs must be a list"
|
|
871
|
+
return result
|
|
872
|
+
|
|
873
|
+
# Check correct length (must be exactly 7)
|
|
874
|
+
if len(input_tabs) != 7:
|
|
875
|
+
result.passed = False
|
|
876
|
+
result.message = f"input_tabs must have exactly 7 elements, found {len(input_tabs)}"
|
|
877
|
+
return result
|
|
878
|
+
|
|
879
|
+
if len(output_tabs) != 7:
|
|
880
|
+
result.passed = False
|
|
881
|
+
result.message = f"output_tabs must have exactly 7 elements, found {len(output_tabs)}"
|
|
882
|
+
return result
|
|
883
|
+
|
|
884
|
+
# Check all elements are strings
|
|
885
|
+
for i, tab in enumerate(input_tabs):
|
|
886
|
+
if not isinstance(tab, str):
|
|
887
|
+
result.passed = False
|
|
888
|
+
result.message = f"input_tabs[{i}] must be string, found {type(tab).__name__}"
|
|
889
|
+
return result
|
|
890
|
+
|
|
891
|
+
for i, tab in enumerate(output_tabs):
|
|
892
|
+
if not isinstance(tab, str):
|
|
893
|
+
result.passed = False
|
|
894
|
+
result.message = f"output_tabs[{i}] must be string, found {type(tab).__name__}"
|
|
895
|
+
return result
|
|
896
|
+
|
|
897
|
+
result.passed = True
|
|
898
|
+
result.message = "Tab arrays integrity validated"
|
|
899
|
+
return result
|
|
900
|
+
|
|
901
|
+
except Exception as e:
|
|
902
|
+
result.passed = False
|
|
903
|
+
result.message = f"Tab arrays integrity check error: {e}"
|
|
904
|
+
return result
|
|
905
|
+
|
|
906
|
+
def _check_active_tab_integrity(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
907
|
+
"""Check that active tab indices are valid."""
|
|
908
|
+
result = CheckResult('active_tab_integrity')
|
|
909
|
+
|
|
910
|
+
try:
|
|
911
|
+
active_input = settings.get('active_input_tab', 0)
|
|
912
|
+
active_output = settings.get('active_output_tab', 0)
|
|
913
|
+
|
|
914
|
+
# Check types
|
|
915
|
+
if not isinstance(active_input, int):
|
|
916
|
+
result.passed = False
|
|
917
|
+
result.message = f"active_input_tab must be integer, found {type(active_input).__name__}"
|
|
918
|
+
return result
|
|
919
|
+
|
|
920
|
+
if not isinstance(active_output, int):
|
|
921
|
+
result.passed = False
|
|
922
|
+
result.message = f"active_output_tab must be integer, found {type(active_output).__name__}"
|
|
923
|
+
return result
|
|
924
|
+
|
|
925
|
+
# Check ranges (0-6 for 7 tabs)
|
|
926
|
+
if not (0 <= active_input < 7):
|
|
927
|
+
result.passed = False
|
|
928
|
+
result.message = f"active_input_tab must be 0-6, found {active_input}"
|
|
929
|
+
return result
|
|
930
|
+
|
|
931
|
+
if not (0 <= active_output < 7):
|
|
932
|
+
result.passed = False
|
|
933
|
+
result.message = f"active_output_tab must be 0-6, found {active_output}"
|
|
934
|
+
return result
|
|
935
|
+
|
|
936
|
+
result.passed = True
|
|
937
|
+
result.message = "Active tab indices validated"
|
|
938
|
+
return result
|
|
939
|
+
|
|
940
|
+
except Exception as e:
|
|
941
|
+
result.passed = False
|
|
942
|
+
result.message = f"Active tab integrity check error: {e}"
|
|
943
|
+
return result
|
|
944
|
+
|
|
945
|
+
def _check_tool_settings_structure(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
946
|
+
"""Check that tool_settings has proper structure."""
|
|
947
|
+
result = CheckResult('tool_settings_structure')
|
|
948
|
+
|
|
949
|
+
try:
|
|
950
|
+
tool_settings = settings.get('tool_settings', {})
|
|
951
|
+
|
|
952
|
+
if not isinstance(tool_settings, dict):
|
|
953
|
+
result.passed = False
|
|
954
|
+
result.message = "tool_settings must be a dictionary"
|
|
955
|
+
return result
|
|
956
|
+
|
|
957
|
+
# Check that each tool's settings is a dictionary
|
|
958
|
+
for tool_name, tool_config in tool_settings.items():
|
|
959
|
+
if not isinstance(tool_name, str):
|
|
960
|
+
result.passed = False
|
|
961
|
+
result.message = f"Tool name must be string, found {type(tool_name).__name__}"
|
|
962
|
+
return result
|
|
963
|
+
|
|
964
|
+
if not isinstance(tool_config, dict):
|
|
965
|
+
result.passed = False
|
|
966
|
+
result.message = f"Tool '{tool_name}' settings must be dictionary, found {type(tool_config).__name__}"
|
|
967
|
+
return result
|
|
968
|
+
|
|
969
|
+
result.passed = True
|
|
970
|
+
result.message = "Tool settings structure validated"
|
|
971
|
+
return result
|
|
972
|
+
|
|
973
|
+
except Exception as e:
|
|
974
|
+
result.passed = False
|
|
975
|
+
result.message = f"Tool settings structure check error: {e}"
|
|
976
|
+
return result
|
|
977
|
+
|
|
978
|
+
def _check_performance_hierarchy(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
979
|
+
"""Check performance settings hierarchy integrity."""
|
|
980
|
+
result = CheckResult('performance_hierarchy')
|
|
981
|
+
|
|
982
|
+
try:
|
|
983
|
+
perf_settings = settings.get('performance_settings', {})
|
|
984
|
+
|
|
985
|
+
if not isinstance(perf_settings, dict):
|
|
986
|
+
result.passed = True # Optional settings
|
|
987
|
+
result.message = "Performance settings not configured"
|
|
988
|
+
return result
|
|
989
|
+
|
|
990
|
+
# Check required categories exist and are dictionaries
|
|
991
|
+
required_categories = ['async_processing', 'caching', 'memory_management', 'ui_optimizations']
|
|
992
|
+
for category in required_categories:
|
|
993
|
+
if category in perf_settings:
|
|
994
|
+
category_settings = perf_settings[category]
|
|
995
|
+
if not isinstance(category_settings, dict):
|
|
996
|
+
result.passed = False
|
|
997
|
+
result.message = f"Performance category '{category}' must be dictionary"
|
|
998
|
+
return result
|
|
999
|
+
|
|
1000
|
+
# Check 'enabled' field exists and is boolean
|
|
1001
|
+
if 'enabled' in category_settings:
|
|
1002
|
+
enabled = category_settings['enabled']
|
|
1003
|
+
if not isinstance(enabled, bool):
|
|
1004
|
+
result.passed = False
|
|
1005
|
+
result.message = f"Performance category '{category}.enabled' must be boolean"
|
|
1006
|
+
return result
|
|
1007
|
+
|
|
1008
|
+
result.passed = True
|
|
1009
|
+
result.message = "Performance hierarchy validated"
|
|
1010
|
+
return result
|
|
1011
|
+
|
|
1012
|
+
except Exception as e:
|
|
1013
|
+
result.passed = False
|
|
1014
|
+
result.message = f"Performance hierarchy check error: {e}"
|
|
1015
|
+
return result
|
|
1016
|
+
|
|
1017
|
+
def _check_font_settings_completeness(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1018
|
+
"""Check font settings completeness."""
|
|
1019
|
+
result = CheckResult('font_settings_completeness')
|
|
1020
|
+
|
|
1021
|
+
try:
|
|
1022
|
+
font_settings = settings.get('font_settings', {})
|
|
1023
|
+
|
|
1024
|
+
if not isinstance(font_settings, dict):
|
|
1025
|
+
result.passed = True # Optional settings
|
|
1026
|
+
result.message = "Font settings not configured"
|
|
1027
|
+
return result
|
|
1028
|
+
|
|
1029
|
+
# Check required font types
|
|
1030
|
+
required_fonts = ['text_font', 'interface_font']
|
|
1031
|
+
for font_type in required_fonts:
|
|
1032
|
+
if font_type in font_settings:
|
|
1033
|
+
font_config = font_settings[font_type]
|
|
1034
|
+
if not isinstance(font_config, dict):
|
|
1035
|
+
result.passed = False
|
|
1036
|
+
result.message = f"Font type '{font_type}' must be dictionary"
|
|
1037
|
+
return result
|
|
1038
|
+
|
|
1039
|
+
# Check required properties
|
|
1040
|
+
required_props = ['family', 'size']
|
|
1041
|
+
for prop in required_props:
|
|
1042
|
+
if prop not in font_config:
|
|
1043
|
+
result.passed = False
|
|
1044
|
+
result.message = f"Font '{font_type}' missing required property '{prop}'"
|
|
1045
|
+
return result
|
|
1046
|
+
|
|
1047
|
+
if prop == 'size':
|
|
1048
|
+
size = font_config[prop]
|
|
1049
|
+
if not isinstance(size, (int, float)) or size <= 0 or size > 72:
|
|
1050
|
+
result.passed = False
|
|
1051
|
+
result.message = f"Font '{font_type}' size must be 1-72, found {size}"
|
|
1052
|
+
return result
|
|
1053
|
+
|
|
1054
|
+
result.passed = True
|
|
1055
|
+
result.message = "Font settings completeness validated"
|
|
1056
|
+
return result
|
|
1057
|
+
|
|
1058
|
+
except Exception as e:
|
|
1059
|
+
result.passed = False
|
|
1060
|
+
result.message = f"Font settings completeness check error: {e}"
|
|
1061
|
+
return result
|
|
1062
|
+
|
|
1063
|
+
def _check_dialog_locked_fields(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1064
|
+
"""Check that dialog settings locked fields are properly configured."""
|
|
1065
|
+
result = CheckResult('dialog_locked_fields')
|
|
1066
|
+
|
|
1067
|
+
try:
|
|
1068
|
+
dialog_settings = settings.get('dialog_settings', {})
|
|
1069
|
+
|
|
1070
|
+
if not isinstance(dialog_settings, dict):
|
|
1071
|
+
result.passed = True # Optional settings
|
|
1072
|
+
result.message = "Dialog settings not configured"
|
|
1073
|
+
return result
|
|
1074
|
+
|
|
1075
|
+
# Error dialogs must be enabled and locked
|
|
1076
|
+
if 'error' in dialog_settings:
|
|
1077
|
+
error_config = dialog_settings['error']
|
|
1078
|
+
if isinstance(error_config, dict):
|
|
1079
|
+
# Must be enabled
|
|
1080
|
+
if not error_config.get('enabled', True):
|
|
1081
|
+
result.passed = False
|
|
1082
|
+
result.message = "Error dialogs must be enabled"
|
|
1083
|
+
return result
|
|
1084
|
+
|
|
1085
|
+
# Must be locked
|
|
1086
|
+
if not error_config.get('locked', True):
|
|
1087
|
+
result.passed = False
|
|
1088
|
+
result.message = "Error dialogs must be locked"
|
|
1089
|
+
return result
|
|
1090
|
+
|
|
1091
|
+
result.passed = True
|
|
1092
|
+
result.message = "Dialog locked fields validated"
|
|
1093
|
+
return result
|
|
1094
|
+
|
|
1095
|
+
except Exception as e:
|
|
1096
|
+
result.passed = False
|
|
1097
|
+
result.message = f"Dialog locked fields check error: {e}"
|
|
1098
|
+
return result
|
|
1099
|
+
|
|
1100
|
+
def _check_ai_model_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1101
|
+
"""Check AI model consistency across tools."""
|
|
1102
|
+
result = CheckResult('ai_model_consistency')
|
|
1103
|
+
|
|
1104
|
+
try:
|
|
1105
|
+
tool_settings = settings.get('tool_settings', {})
|
|
1106
|
+
ai_tools = ['Google AI', 'Anthropic AI', 'OpenAI', 'Cohere AI', 'HuggingFace AI', 'Groq AI', 'OpenRouterAI', 'AWS Bedrock', 'LM Studio']
|
|
1107
|
+
|
|
1108
|
+
for tool_name in ai_tools:
|
|
1109
|
+
if tool_name in tool_settings:
|
|
1110
|
+
tool_config = tool_settings[tool_name]
|
|
1111
|
+
if isinstance(tool_config, dict):
|
|
1112
|
+
# Check model is in models list
|
|
1113
|
+
current_model = tool_config.get('MODEL')
|
|
1114
|
+
models_list = tool_config.get('MODELS_LIST', [])
|
|
1115
|
+
|
|
1116
|
+
if current_model and isinstance(models_list, list) and models_list:
|
|
1117
|
+
if current_model not in models_list:
|
|
1118
|
+
result.passed = False
|
|
1119
|
+
result.message = f"AI tool '{tool_name}' current model '{current_model}' not in models list"
|
|
1120
|
+
return result
|
|
1121
|
+
|
|
1122
|
+
# Check models list is not empty
|
|
1123
|
+
if 'MODELS_LIST' in tool_config:
|
|
1124
|
+
if not isinstance(models_list, list) or not models_list:
|
|
1125
|
+
result.passed = False
|
|
1126
|
+
result.message = f"AI tool '{tool_name}' models list must be non-empty list"
|
|
1127
|
+
return result
|
|
1128
|
+
|
|
1129
|
+
result.passed = True
|
|
1130
|
+
result.message = "AI model consistency validated"
|
|
1131
|
+
return result
|
|
1132
|
+
|
|
1133
|
+
except Exception as e:
|
|
1134
|
+
result.passed = False
|
|
1135
|
+
result.message = f"AI model consistency check error: {e}"
|
|
1136
|
+
return result
|
|
1137
|
+
|
|
1138
|
+
def _check_curl_history_structure(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1139
|
+
"""Check cURL history structure integrity."""
|
|
1140
|
+
result = CheckResult('curl_history_structure')
|
|
1141
|
+
|
|
1142
|
+
try:
|
|
1143
|
+
tool_settings = settings.get('tool_settings', {})
|
|
1144
|
+
curl_settings = tool_settings.get('cURL Tool', {})
|
|
1145
|
+
|
|
1146
|
+
if not isinstance(curl_settings, dict):
|
|
1147
|
+
result.passed = True # Tool not configured
|
|
1148
|
+
result.message = "cURL Tool not configured"
|
|
1149
|
+
return result
|
|
1150
|
+
|
|
1151
|
+
history = curl_settings.get('history', [])
|
|
1152
|
+
if not isinstance(history, list):
|
|
1153
|
+
result.passed = False
|
|
1154
|
+
result.message = "cURL history must be a list"
|
|
1155
|
+
return result
|
|
1156
|
+
|
|
1157
|
+
# Check history entries structure (first 5 entries)
|
|
1158
|
+
required_fields = {'timestamp', 'method', 'url', 'status_code', 'success'}
|
|
1159
|
+
for i, entry in enumerate(history[:5]):
|
|
1160
|
+
if not isinstance(entry, dict):
|
|
1161
|
+
result.passed = False
|
|
1162
|
+
result.message = f"cURL history entry {i} must be dictionary"
|
|
1163
|
+
return result
|
|
1164
|
+
|
|
1165
|
+
missing_fields = required_fields - set(entry.keys())
|
|
1166
|
+
if missing_fields:
|
|
1167
|
+
result.passed = False
|
|
1168
|
+
result.message = f"cURL history entry {i} missing fields: {missing_fields}"
|
|
1169
|
+
return result
|
|
1170
|
+
|
|
1171
|
+
# Validate timestamp format
|
|
1172
|
+
timestamp = entry.get('timestamp')
|
|
1173
|
+
if isinstance(timestamp, str):
|
|
1174
|
+
try:
|
|
1175
|
+
datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
|
1176
|
+
except ValueError:
|
|
1177
|
+
result.passed = False
|
|
1178
|
+
result.message = f"cURL history entry {i} has invalid timestamp format"
|
|
1179
|
+
return result
|
|
1180
|
+
|
|
1181
|
+
result.passed = True
|
|
1182
|
+
result.message = "cURL history structure validated"
|
|
1183
|
+
return result
|
|
1184
|
+
|
|
1185
|
+
except Exception as e:
|
|
1186
|
+
result.passed = False
|
|
1187
|
+
result.message = f"cURL history structure check error: {e}"
|
|
1188
|
+
return result
|
|
1189
|
+
|
|
1190
|
+
def _check_encrypted_data_format(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1191
|
+
"""Check encrypted data format integrity."""
|
|
1192
|
+
result = CheckResult('encrypted_data_format')
|
|
1193
|
+
|
|
1194
|
+
try:
|
|
1195
|
+
# Recursively check for encrypted values
|
|
1196
|
+
encrypted_count = 0
|
|
1197
|
+
invalid_encrypted = []
|
|
1198
|
+
|
|
1199
|
+
def check_encrypted_recursive(obj, path=""):
|
|
1200
|
+
nonlocal encrypted_count, invalid_encrypted
|
|
1201
|
+
|
|
1202
|
+
if isinstance(obj, dict):
|
|
1203
|
+
for key, value in obj.items():
|
|
1204
|
+
current_path = f"{path}.{key}" if path else key
|
|
1205
|
+
if isinstance(value, str) and value.startswith('ENC:'):
|
|
1206
|
+
encrypted_count += 1
|
|
1207
|
+
# Validate base64 format after ENC: prefix
|
|
1208
|
+
encrypted_data = value[4:] # Remove 'ENC:' prefix
|
|
1209
|
+
try:
|
|
1210
|
+
import base64
|
|
1211
|
+
base64.b64decode(encrypted_data, validate=True)
|
|
1212
|
+
except Exception:
|
|
1213
|
+
invalid_encrypted.append(current_path)
|
|
1214
|
+
elif isinstance(value, (dict, list)):
|
|
1215
|
+
check_encrypted_recursive(value, current_path)
|
|
1216
|
+
elif isinstance(obj, list):
|
|
1217
|
+
for i, item in enumerate(obj):
|
|
1218
|
+
current_path = f"{path}[{i}]" if path else f"[{i}]"
|
|
1219
|
+
check_encrypted_recursive(item, current_path)
|
|
1220
|
+
|
|
1221
|
+
check_encrypted_recursive(settings)
|
|
1222
|
+
|
|
1223
|
+
if invalid_encrypted:
|
|
1224
|
+
result.passed = False
|
|
1225
|
+
result.message = f"Invalid encrypted data format in: {invalid_encrypted}"
|
|
1226
|
+
return result
|
|
1227
|
+
|
|
1228
|
+
result.passed = True
|
|
1229
|
+
result.message = f"Encrypted data format validated ({encrypted_count} encrypted values found)"
|
|
1230
|
+
return result
|
|
1231
|
+
|
|
1232
|
+
except Exception as e:
|
|
1233
|
+
result.passed = False
|
|
1234
|
+
result.message = f"Encrypted data format check error: {e}"
|
|
1235
|
+
return result
|
|
1236
|
+
|
|
1237
|
+
def _check_generator_percentages(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1238
|
+
"""Check generator tools percentage consistency."""
|
|
1239
|
+
result = CheckResult('generator_percentages')
|
|
1240
|
+
|
|
1241
|
+
try:
|
|
1242
|
+
tool_settings = settings.get('tool_settings', {})
|
|
1243
|
+
generator_tools = tool_settings.get('Generator Tools', {})
|
|
1244
|
+
|
|
1245
|
+
if not isinstance(generator_tools, dict):
|
|
1246
|
+
result.passed = True # Tool not configured
|
|
1247
|
+
result.message = "Generator Tools not configured"
|
|
1248
|
+
return result
|
|
1249
|
+
|
|
1250
|
+
password_gen = generator_tools.get('Strong Password Generator', {})
|
|
1251
|
+
if isinstance(password_gen, dict):
|
|
1252
|
+
letters_pct = password_gen.get('letters_percent', 0)
|
|
1253
|
+
numbers_pct = password_gen.get('numbers_percent', 0)
|
|
1254
|
+
symbols_pct = password_gen.get('symbols_percent', 0)
|
|
1255
|
+
|
|
1256
|
+
# Check percentages are valid
|
|
1257
|
+
if not all(isinstance(pct, int) and 0 <= pct <= 100 for pct in [letters_pct, numbers_pct, symbols_pct]):
|
|
1258
|
+
result.passed = False
|
|
1259
|
+
result.message = "Password generator percentages must be integers 0-100"
|
|
1260
|
+
return result
|
|
1261
|
+
|
|
1262
|
+
# Check percentages sum to 100
|
|
1263
|
+
total = letters_pct + numbers_pct + symbols_pct
|
|
1264
|
+
if total != 100:
|
|
1265
|
+
result.passed = False
|
|
1266
|
+
result.message = f"Password generator percentages must sum to 100, found {total}"
|
|
1267
|
+
return result
|
|
1268
|
+
|
|
1269
|
+
result.passed = True
|
|
1270
|
+
result.message = "Generator percentages validated"
|
|
1271
|
+
return result
|
|
1272
|
+
|
|
1273
|
+
except Exception as e:
|
|
1274
|
+
result.passed = False
|
|
1275
|
+
result.message = f"Generator percentages check error: {e}"
|
|
1276
|
+
return result
|
|
1277
|
+
|
|
1278
|
+
def _initialize_consistency_rules(self):
|
|
1279
|
+
"""Initialize consistency checking rules."""
|
|
1280
|
+
# Cross-setting consistency rules
|
|
1281
|
+
self.consistency_rules.update({
|
|
1282
|
+
'selected_tool_exists': self._check_selected_tool_exists,
|
|
1283
|
+
'performance_mode_consistency': self._check_performance_mode_consistency,
|
|
1284
|
+
'ai_tools_api_keys': self._check_ai_tools_api_keys,
|
|
1285
|
+
'curl_timeout_consistency': self._check_curl_timeout_consistency,
|
|
1286
|
+
'font_fallback_consistency': self._check_font_fallback_consistency,
|
|
1287
|
+
'dialog_hierarchy_consistency': self._check_dialog_hierarchy_consistency,
|
|
1288
|
+
'generator_tools_completeness': self._check_generator_tools_completeness,
|
|
1289
|
+
'folder_reporter_path_consistency': self._check_folder_reporter_paths,
|
|
1290
|
+
})
|
|
1291
|
+
|
|
1292
|
+
def _check_selected_tool_exists(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1293
|
+
"""Check that selected tool exists in tool_settings."""
|
|
1294
|
+
result = CheckResult('selected_tool_exists')
|
|
1295
|
+
|
|
1296
|
+
try:
|
|
1297
|
+
selected_tool = settings.get('selected_tool')
|
|
1298
|
+
tool_settings = settings.get('tool_settings', {})
|
|
1299
|
+
|
|
1300
|
+
if selected_tool:
|
|
1301
|
+
if not isinstance(selected_tool, str):
|
|
1302
|
+
result.passed = False
|
|
1303
|
+
result.message = "selected_tool must be string"
|
|
1304
|
+
return result
|
|
1305
|
+
|
|
1306
|
+
if selected_tool not in tool_settings:
|
|
1307
|
+
result.passed = False
|
|
1308
|
+
result.message = f"Selected tool '{selected_tool}' not found in tool_settings"
|
|
1309
|
+
return result
|
|
1310
|
+
|
|
1311
|
+
result.passed = True
|
|
1312
|
+
result.message = "Selected tool consistency validated"
|
|
1313
|
+
return result
|
|
1314
|
+
|
|
1315
|
+
except Exception as e:
|
|
1316
|
+
result.passed = False
|
|
1317
|
+
result.message = f"Selected tool consistency check error: {e}"
|
|
1318
|
+
return result
|
|
1319
|
+
|
|
1320
|
+
def _check_performance_mode_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1321
|
+
"""Check performance mode consistency across categories."""
|
|
1322
|
+
result = CheckResult('performance_mode_consistency')
|
|
1323
|
+
|
|
1324
|
+
try:
|
|
1325
|
+
perf_settings = settings.get('performance_settings', {})
|
|
1326
|
+
|
|
1327
|
+
if not isinstance(perf_settings, dict):
|
|
1328
|
+
result.passed = True
|
|
1329
|
+
result.message = "Performance settings not configured"
|
|
1330
|
+
return result
|
|
1331
|
+
|
|
1332
|
+
mode = perf_settings.get('mode', 'automatic')
|
|
1333
|
+
|
|
1334
|
+
# If mode is disabled, all categories should be disabled
|
|
1335
|
+
if mode == 'disabled':
|
|
1336
|
+
categories = ['async_processing', 'caching', 'memory_management', 'ui_optimizations']
|
|
1337
|
+
for category in categories:
|
|
1338
|
+
category_settings = perf_settings.get(category, {})
|
|
1339
|
+
if isinstance(category_settings, dict) and category_settings.get('enabled', False):
|
|
1340
|
+
result.passed = False
|
|
1341
|
+
result.message = f"Performance mode is disabled but {category} is enabled"
|
|
1342
|
+
return result
|
|
1343
|
+
|
|
1344
|
+
result.passed = True
|
|
1345
|
+
result.message = "Performance mode consistency validated"
|
|
1346
|
+
return result
|
|
1347
|
+
|
|
1348
|
+
except Exception as e:
|
|
1349
|
+
result.passed = False
|
|
1350
|
+
result.message = f"Performance mode consistency check error: {e}"
|
|
1351
|
+
return result
|
|
1352
|
+
|
|
1353
|
+
def _check_ai_tools_api_keys(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1354
|
+
"""Check AI tools have proper API key configuration."""
|
|
1355
|
+
result = CheckResult('ai_tools_api_keys')
|
|
1356
|
+
|
|
1357
|
+
try:
|
|
1358
|
+
tool_settings = settings.get('tool_settings', {})
|
|
1359
|
+
ai_tools = ['Google AI', 'Anthropic AI', 'OpenAI', 'Cohere AI', 'HuggingFace AI', 'Groq AI', 'OpenRouterAI']
|
|
1360
|
+
|
|
1361
|
+
missing_keys = []
|
|
1362
|
+
placeholder_keys = []
|
|
1363
|
+
|
|
1364
|
+
for tool_name in ai_tools:
|
|
1365
|
+
if tool_name in tool_settings:
|
|
1366
|
+
tool_config = tool_settings[tool_name]
|
|
1367
|
+
if isinstance(tool_config, dict):
|
|
1368
|
+
api_key = tool_config.get('API_KEY', '')
|
|
1369
|
+
|
|
1370
|
+
if not api_key:
|
|
1371
|
+
missing_keys.append(tool_name)
|
|
1372
|
+
elif api_key == 'putinyourkey':
|
|
1373
|
+
placeholder_keys.append(tool_name)
|
|
1374
|
+
|
|
1375
|
+
# This is a warning, not an error - users might not have all API keys
|
|
1376
|
+
if missing_keys or placeholder_keys:
|
|
1377
|
+
result.passed = True # Don't fail validation
|
|
1378
|
+
result.severity = 'warning'
|
|
1379
|
+
warnings = []
|
|
1380
|
+
if missing_keys:
|
|
1381
|
+
warnings.append(f"Missing API keys: {missing_keys}")
|
|
1382
|
+
if placeholder_keys:
|
|
1383
|
+
warnings.append(f"Placeholder API keys: {placeholder_keys}")
|
|
1384
|
+
result.message = "; ".join(warnings)
|
|
1385
|
+
else:
|
|
1386
|
+
result.passed = True
|
|
1387
|
+
result.message = "AI tools API keys validated"
|
|
1388
|
+
|
|
1389
|
+
return result
|
|
1390
|
+
|
|
1391
|
+
except Exception as e:
|
|
1392
|
+
result.passed = False
|
|
1393
|
+
result.message = f"AI tools API keys check error: {e}"
|
|
1394
|
+
return result
|
|
1395
|
+
|
|
1396
|
+
def _check_curl_timeout_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1397
|
+
"""Check cURL timeout settings consistency."""
|
|
1398
|
+
result = CheckResult('curl_timeout_consistency')
|
|
1399
|
+
|
|
1400
|
+
try:
|
|
1401
|
+
tool_settings = settings.get('tool_settings', {})
|
|
1402
|
+
curl_settings = tool_settings.get('cURL Tool', {})
|
|
1403
|
+
|
|
1404
|
+
if not isinstance(curl_settings, dict):
|
|
1405
|
+
result.passed = True
|
|
1406
|
+
result.message = "cURL Tool not configured"
|
|
1407
|
+
return result
|
|
1408
|
+
|
|
1409
|
+
default_timeout = curl_settings.get('default_timeout', 90)
|
|
1410
|
+
auth_timeout_minutes = curl_settings.get('auth_timeout_minutes', 60)
|
|
1411
|
+
|
|
1412
|
+
# Convert auth timeout to seconds for comparison
|
|
1413
|
+
auth_timeout_seconds = auth_timeout_minutes * 60
|
|
1414
|
+
|
|
1415
|
+
# Auth timeout should be reasonable compared to default timeout
|
|
1416
|
+
if auth_timeout_seconds < default_timeout:
|
|
1417
|
+
result.passed = False
|
|
1418
|
+
result.message = f"Auth timeout ({auth_timeout_seconds}s) should not be less than default timeout ({default_timeout}s)"
|
|
1419
|
+
return result
|
|
1420
|
+
|
|
1421
|
+
result.passed = True
|
|
1422
|
+
result.message = "cURL timeout consistency validated"
|
|
1423
|
+
return result
|
|
1424
|
+
|
|
1425
|
+
except Exception as e:
|
|
1426
|
+
result.passed = False
|
|
1427
|
+
result.message = f"cURL timeout consistency check error: {e}"
|
|
1428
|
+
return result
|
|
1429
|
+
|
|
1430
|
+
def _check_font_fallback_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1431
|
+
"""Check font fallback consistency across platforms."""
|
|
1432
|
+
result = CheckResult('font_fallback_consistency')
|
|
1433
|
+
|
|
1434
|
+
try:
|
|
1435
|
+
font_settings = settings.get('font_settings', {})
|
|
1436
|
+
|
|
1437
|
+
if not isinstance(font_settings, dict):
|
|
1438
|
+
result.passed = True
|
|
1439
|
+
result.message = "Font settings not configured"
|
|
1440
|
+
return result
|
|
1441
|
+
|
|
1442
|
+
for font_type in ['text_font', 'interface_font']:
|
|
1443
|
+
if font_type in font_settings:
|
|
1444
|
+
font_config = font_settings[font_type]
|
|
1445
|
+
if isinstance(font_config, dict):
|
|
1446
|
+
# Check that if fallback_family exists, platform-specific fallbacks also exist
|
|
1447
|
+
has_fallback = 'fallback_family' in font_config
|
|
1448
|
+
has_mac_fallback = 'fallback_family_mac' in font_config
|
|
1449
|
+
has_linux_fallback = 'fallback_family_linux' in font_config
|
|
1450
|
+
|
|
1451
|
+
if has_fallback and not (has_mac_fallback and has_linux_fallback):
|
|
1452
|
+
result.passed = False
|
|
1453
|
+
result.message = f"Font '{font_type}' has fallback but missing platform-specific fallbacks"
|
|
1454
|
+
return result
|
|
1455
|
+
|
|
1456
|
+
result.passed = True
|
|
1457
|
+
result.message = "Font fallback consistency validated"
|
|
1458
|
+
return result
|
|
1459
|
+
|
|
1460
|
+
except Exception as e:
|
|
1461
|
+
result.passed = False
|
|
1462
|
+
result.message = f"Font fallback consistency check error: {e}"
|
|
1463
|
+
return result
|
|
1464
|
+
|
|
1465
|
+
def _check_dialog_hierarchy_consistency(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1466
|
+
"""Check dialog settings hierarchy consistency."""
|
|
1467
|
+
result = CheckResult('dialog_hierarchy_consistency')
|
|
1468
|
+
|
|
1469
|
+
try:
|
|
1470
|
+
dialog_settings = settings.get('dialog_settings', {})
|
|
1471
|
+
|
|
1472
|
+
if not isinstance(dialog_settings, dict):
|
|
1473
|
+
result.passed = True
|
|
1474
|
+
result.message = "Dialog settings not configured"
|
|
1475
|
+
return result
|
|
1476
|
+
|
|
1477
|
+
# Check that error dialogs cannot be disabled
|
|
1478
|
+
if 'error' in dialog_settings:
|
|
1479
|
+
error_config = dialog_settings['error']
|
|
1480
|
+
if isinstance(error_config, dict):
|
|
1481
|
+
if not error_config.get('enabled', True):
|
|
1482
|
+
result.passed = False
|
|
1483
|
+
result.message = "Error dialogs cannot be disabled"
|
|
1484
|
+
return result
|
|
1485
|
+
|
|
1486
|
+
if not error_config.get('locked', True):
|
|
1487
|
+
result.passed = False
|
|
1488
|
+
result.message = "Error dialogs must be locked"
|
|
1489
|
+
return result
|
|
1490
|
+
|
|
1491
|
+
# Check that confirmation dialogs have default_action if enabled
|
|
1492
|
+
if 'confirmation' in dialog_settings:
|
|
1493
|
+
confirm_config = dialog_settings['confirmation']
|
|
1494
|
+
if isinstance(confirm_config, dict) and confirm_config.get('enabled', False):
|
|
1495
|
+
if 'default_action' not in confirm_config:
|
|
1496
|
+
result.passed = False
|
|
1497
|
+
result.message = "Enabled confirmation dialogs must have default_action"
|
|
1498
|
+
return result
|
|
1499
|
+
|
|
1500
|
+
result.passed = True
|
|
1501
|
+
result.message = "Dialog hierarchy consistency validated"
|
|
1502
|
+
return result
|
|
1503
|
+
|
|
1504
|
+
except Exception as e:
|
|
1505
|
+
result.passed = False
|
|
1506
|
+
result.message = f"Dialog hierarchy consistency check error: {e}"
|
|
1507
|
+
return result
|
|
1508
|
+
|
|
1509
|
+
def _check_generator_tools_completeness(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1510
|
+
"""Check generator tools completeness and consistency."""
|
|
1511
|
+
result = CheckResult('generator_tools_completeness')
|
|
1512
|
+
|
|
1513
|
+
try:
|
|
1514
|
+
tool_settings = settings.get('tool_settings', {})
|
|
1515
|
+
generator_tools = tool_settings.get('Generator Tools', {})
|
|
1516
|
+
|
|
1517
|
+
if not isinstance(generator_tools, dict):
|
|
1518
|
+
result.passed = True
|
|
1519
|
+
result.message = "Generator Tools not configured"
|
|
1520
|
+
return result
|
|
1521
|
+
|
|
1522
|
+
# Check UUID generator consistency
|
|
1523
|
+
if 'UUID/GUID Generator' in generator_tools:
|
|
1524
|
+
uuid_config = generator_tools['UUID/GUID Generator']
|
|
1525
|
+
if isinstance(uuid_config, dict):
|
|
1526
|
+
version = uuid_config.get('version', 4)
|
|
1527
|
+
namespace = uuid_config.get('namespace', '')
|
|
1528
|
+
name = uuid_config.get('name', '')
|
|
1529
|
+
|
|
1530
|
+
# Version 3 and 5 require namespace and name
|
|
1531
|
+
if version in (3, 5):
|
|
1532
|
+
if not namespace or not name:
|
|
1533
|
+
result.passed = False
|
|
1534
|
+
result.message = f"UUID version {version} requires namespace and name"
|
|
1535
|
+
return result
|
|
1536
|
+
|
|
1537
|
+
result.passed = True
|
|
1538
|
+
result.message = "Generator tools completeness validated"
|
|
1539
|
+
return result
|
|
1540
|
+
|
|
1541
|
+
except Exception as e:
|
|
1542
|
+
result.passed = False
|
|
1543
|
+
result.message = f"Generator tools completeness check error: {e}"
|
|
1544
|
+
return result
|
|
1545
|
+
|
|
1546
|
+
def _check_folder_reporter_paths(self, settings: Dict[str, Any]) -> 'CheckResult':
|
|
1547
|
+
"""Check folder reporter path consistency."""
|
|
1548
|
+
result = CheckResult('folder_reporter_paths')
|
|
1549
|
+
|
|
1550
|
+
try:
|
|
1551
|
+
tool_settings = settings.get('tool_settings', {})
|
|
1552
|
+
folder_reporter = tool_settings.get('Folder File Reporter', {})
|
|
1553
|
+
|
|
1554
|
+
if not isinstance(folder_reporter, dict):
|
|
1555
|
+
result.passed = True
|
|
1556
|
+
result.message = "Folder File Reporter not configured"
|
|
1557
|
+
return result
|
|
1558
|
+
|
|
1559
|
+
last_input = folder_reporter.get('last_input_folder', '')
|
|
1560
|
+
last_output = folder_reporter.get('last_output_folder', '')
|
|
1561
|
+
|
|
1562
|
+
# Check paths are valid format (if provided)
|
|
1563
|
+
for path_name, path_value in [('last_input_folder', last_input), ('last_output_folder', last_output)]:
|
|
1564
|
+
if path_value and isinstance(path_value, str):
|
|
1565
|
+
try:
|
|
1566
|
+
Path(path_value) # Basic path validation
|
|
1567
|
+
except Exception:
|
|
1568
|
+
result.passed = False
|
|
1569
|
+
result.message = f"Invalid path format in {path_name}: {path_value}"
|
|
1570
|
+
return result
|
|
1571
|
+
|
|
1572
|
+
result.passed = True
|
|
1573
|
+
result.message = "Folder reporter paths validated"
|
|
1574
|
+
return result
|
|
1575
|
+
|
|
1576
|
+
except Exception as e:
|
|
1577
|
+
result.passed = False
|
|
1578
|
+
result.message = f"Folder reporter paths check error: {e}"
|
|
1579
|
+
return result
|
|
1580
|
+
|
|
1581
|
+
def _get_expected_type(self, key: str, context: Optional[Dict[str, Any]]) -> Optional[type]:
|
|
1582
|
+
"""Get expected type for a setting key."""
|
|
1583
|
+
type_map = {
|
|
1584
|
+
'debug_level': str,
|
|
1585
|
+
'active_input_tab': int,
|
|
1586
|
+
'active_output_tab': int,
|
|
1587
|
+
'export_path': str,
|
|
1588
|
+
'selected_tool': str,
|
|
1589
|
+
'input_tabs': list,
|
|
1590
|
+
'output_tabs': list,
|
|
1591
|
+
'tool_settings': dict,
|
|
1592
|
+
'performance_settings': dict,
|
|
1593
|
+
'font_settings': dict,
|
|
1594
|
+
'dialog_settings': dict
|
|
1595
|
+
}
|
|
1596
|
+
return type_map.get(key)
|
|
1597
|
+
|
|
1598
|
+
def _get_pattern_rule(self, key: str, context: Optional[Dict[str, Any]]):
|
|
1599
|
+
"""Get pattern-based validation rule for a key."""
|
|
1600
|
+
# Add pattern-based rules as needed
|
|
1601
|
+
return None
|
|
1602
|
+
|
|
1603
|
+
def _is_sensitive_key(self, key: str) -> bool:
|
|
1604
|
+
"""Check if a key represents sensitive data."""
|
|
1605
|
+
sensitive_patterns = {
|
|
1606
|
+
'key', 'token', 'password', 'secret', 'auth', 'credential'
|
|
1607
|
+
}
|
|
1608
|
+
key_lower = key.lower()
|
|
1609
|
+
return any(pattern in key_lower for pattern in sensitive_patterns)
|
|
1610
|
+
|
|
1611
|
+
def _is_encrypted_or_placeholder(self, value: str) -> bool:
|
|
1612
|
+
"""Check if a value is encrypted or a placeholder."""
|
|
1613
|
+
if self.encryption_pattern.match(value):
|
|
1614
|
+
return True
|
|
1615
|
+
|
|
1616
|
+
placeholder_patterns = {
|
|
1617
|
+
'putinyourkey', 'your_key_here', 'enter_key', 'api_key_here',
|
|
1618
|
+
'your_token', 'enter_token', 'placeholder'
|
|
1619
|
+
}
|
|
1620
|
+
return value.lower() in placeholder_patterns
|
|
1621
|
+
|
|
1622
|
+
def _contains_obvious_secret(self, value: Any) -> bool:
|
|
1623
|
+
"""Check if value contains obvious secrets."""
|
|
1624
|
+
if not isinstance(value, str) or len(value) < 10:
|
|
1625
|
+
return False
|
|
1626
|
+
|
|
1627
|
+
# Look for patterns that suggest real API keys/tokens
|
|
1628
|
+
# This is a basic heuristic - real implementation might be more sophisticated
|
|
1629
|
+
if re.match(r'^[A-Za-z0-9]{20,}$', value): # Long alphanumeric string
|
|
1630
|
+
return True
|
|
1631
|
+
|
|
1632
|
+
if re.match(r'^sk-[A-Za-z0-9]{40,}$', value): # OpenAI-style key
|
|
1633
|
+
return True
|
|
1634
|
+
|
|
1635
|
+
return False
|
|
1636
|
+
|
|
1637
|
+
|
|
1638
|
+
# Result classes for validation
|
|
1639
|
+
class CheckResult:
|
|
1640
|
+
"""Result of a single validation check."""
|
|
1641
|
+
|
|
1642
|
+
def __init__(self, check_name: str):
|
|
1643
|
+
self.check_name = check_name
|
|
1644
|
+
self.passed = False
|
|
1645
|
+
self.message = ""
|
|
1646
|
+
self.severity = 'error' # 'error', 'warning', 'info'
|
|
1647
|
+
|
|
1648
|
+
def __bool__(self):
|
|
1649
|
+
return self.passed
|
|
1650
|
+
|
|
1651
|
+
|
|
1652
|
+
class ValidationResult:
|
|
1653
|
+
"""Result of validating a single setting."""
|
|
1654
|
+
|
|
1655
|
+
def __init__(self, key: str, value: Any):
|
|
1656
|
+
self.key = key
|
|
1657
|
+
self.value = value
|
|
1658
|
+
self.checks = {}
|
|
1659
|
+
self.errors = []
|
|
1660
|
+
self.warnings = []
|
|
1661
|
+
self.is_critical = False
|
|
1662
|
+
|
|
1663
|
+
def add_check(self, check_name: str, result: CheckResult):
|
|
1664
|
+
"""Add a check result."""
|
|
1665
|
+
self.checks[check_name] = result
|
|
1666
|
+
|
|
1667
|
+
if not result.passed:
|
|
1668
|
+
if result.severity == 'error':
|
|
1669
|
+
self.errors.append(f"{check_name}: {result.message}")
|
|
1670
|
+
elif result.severity == 'warning':
|
|
1671
|
+
self.warnings.append(f"{check_name}: {result.message}")
|
|
1672
|
+
|
|
1673
|
+
def add_error(self, message: str):
|
|
1674
|
+
"""Add an error message."""
|
|
1675
|
+
self.errors.append(message)
|
|
1676
|
+
|
|
1677
|
+
def add_warning(self, message: str):
|
|
1678
|
+
"""Add a warning message."""
|
|
1679
|
+
self.warnings.append(message)
|
|
1680
|
+
|
|
1681
|
+
@property
|
|
1682
|
+
def is_valid(self) -> bool:
|
|
1683
|
+
"""Check if validation passed (no errors)."""
|
|
1684
|
+
return len(self.errors) == 0
|
|
1685
|
+
|
|
1686
|
+
@property
|
|
1687
|
+
def has_warnings(self) -> bool:
|
|
1688
|
+
"""Check if there are warnings."""
|
|
1689
|
+
return len(self.warnings) > 0
|
|
1690
|
+
|
|
1691
|
+
|
|
1692
|
+
class ToolValidationResult:
|
|
1693
|
+
"""Result of validating all settings for a tool."""
|
|
1694
|
+
|
|
1695
|
+
def __init__(self, tool_name: str, settings: Dict[str, Any]):
|
|
1696
|
+
self.tool_name = tool_name
|
|
1697
|
+
self.settings = settings
|
|
1698
|
+
self.checks = {}
|
|
1699
|
+
self.setting_results = {}
|
|
1700
|
+
self.errors = []
|
|
1701
|
+
self.warnings = []
|
|
1702
|
+
|
|
1703
|
+
def add_check(self, check_name: str, result: CheckResult):
|
|
1704
|
+
"""Add a tool-level check result."""
|
|
1705
|
+
self.checks[check_name] = result
|
|
1706
|
+
|
|
1707
|
+
if not result.passed:
|
|
1708
|
+
if result.severity == 'error':
|
|
1709
|
+
self.errors.append(f"{check_name}: {result.message}")
|
|
1710
|
+
elif result.severity == 'warning':
|
|
1711
|
+
self.warnings.append(f"{check_name}: {result.message}")
|
|
1712
|
+
|
|
1713
|
+
def add_setting_result(self, setting_key: str, result: ValidationResult):
|
|
1714
|
+
"""Add a setting validation result."""
|
|
1715
|
+
self.setting_results[setting_key] = result
|
|
1716
|
+
|
|
1717
|
+
# Aggregate errors and warnings
|
|
1718
|
+
self.errors.extend([f"{setting_key}: {err}" for err in result.errors])
|
|
1719
|
+
self.warnings.extend([f"{setting_key}: {warn}" for warn in result.warnings])
|
|
1720
|
+
|
|
1721
|
+
def add_error(self, message: str):
|
|
1722
|
+
"""Add an error message."""
|
|
1723
|
+
self.errors.append(message)
|
|
1724
|
+
|
|
1725
|
+
@property
|
|
1726
|
+
def is_valid(self) -> bool:
|
|
1727
|
+
"""Check if all validation passed."""
|
|
1728
|
+
return len(self.errors) == 0
|
|
1729
|
+
|
|
1730
|
+
|
|
1731
|
+
class ConsistencyValidationResult:
|
|
1732
|
+
"""Result of validating consistency across all settings."""
|
|
1733
|
+
|
|
1734
|
+
def __init__(self, settings: Dict[str, Any]):
|
|
1735
|
+
self.settings = settings
|
|
1736
|
+
self.checks = {}
|
|
1737
|
+
self.errors = []
|
|
1738
|
+
self.warnings = []
|
|
1739
|
+
|
|
1740
|
+
def add_check(self, check_name: str, result: CheckResult):
|
|
1741
|
+
"""Add a consistency check result."""
|
|
1742
|
+
self.checks[check_name] = result
|
|
1743
|
+
|
|
1744
|
+
if not result.passed:
|
|
1745
|
+
if result.severity == 'error':
|
|
1746
|
+
self.errors.append(f"{check_name}: {result.message}")
|
|
1747
|
+
elif result.severity == 'warning':
|
|
1748
|
+
self.warnings.append(f"{check_name}: {result.message}")
|
|
1749
|
+
|
|
1750
|
+
def add_error(self, message: str):
|
|
1751
|
+
"""Add an error message."""
|
|
1752
|
+
self.errors.append(message)
|
|
1753
|
+
|
|
1754
|
+
@property
|
|
1755
|
+
def is_valid(self) -> bool:
|
|
1756
|
+
"""Check if all consistency checks passed."""
|
|
1757
|
+
return len(self.errors) == 0
|
|
1758
|
+
|
|
1759
|
+
|
|
1760
|
+
class CompleteValidationResult:
|
|
1761
|
+
"""Result of complete settings validation including all checks."""
|
|
1762
|
+
|
|
1763
|
+
def __init__(self, settings: Dict[str, Any]):
|
|
1764
|
+
self.settings = settings
|
|
1765
|
+
self.core_setting_results = {}
|
|
1766
|
+
self.tool_results = {}
|
|
1767
|
+
self.consistency_result = None
|
|
1768
|
+
self.errors = []
|
|
1769
|
+
self.warnings = []
|
|
1770
|
+
|
|
1771
|
+
def add_core_setting_result(self, setting_key: str, result: ValidationResult):
|
|
1772
|
+
"""Add a core setting validation result."""
|
|
1773
|
+
self.core_setting_results[setting_key] = result
|
|
1774
|
+
|
|
1775
|
+
# Aggregate errors and warnings
|
|
1776
|
+
self.errors.extend([f"core.{setting_key}: {err}" for err in result.errors])
|
|
1777
|
+
self.warnings.extend([f"core.{setting_key}: {warn}" for warn in result.warnings])
|
|
1778
|
+
|
|
1779
|
+
def add_tool_result(self, tool_name: str, result: ToolValidationResult):
|
|
1780
|
+
"""Add a tool validation result."""
|
|
1781
|
+
self.tool_results[tool_name] = result
|
|
1782
|
+
|
|
1783
|
+
# Aggregate errors and warnings
|
|
1784
|
+
self.errors.extend([f"tool.{tool_name}: {err}" for err in result.errors])
|
|
1785
|
+
self.warnings.extend([f"tool.{tool_name}: {warn}" for warn in result.warnings])
|
|
1786
|
+
|
|
1787
|
+
def add_consistency_result(self, result: ConsistencyValidationResult):
|
|
1788
|
+
"""Add consistency validation result."""
|
|
1789
|
+
self.consistency_result = result
|
|
1790
|
+
|
|
1791
|
+
# Aggregate errors and warnings
|
|
1792
|
+
self.errors.extend([f"consistency: {err}" for err in result.errors])
|
|
1793
|
+
self.warnings.extend([f"consistency: {warn}" for warn in result.warnings])
|
|
1794
|
+
|
|
1795
|
+
def add_error(self, message: str):
|
|
1796
|
+
"""Add an error message."""
|
|
1797
|
+
self.errors.append(message)
|
|
1798
|
+
|
|
1799
|
+
def add_warning(self, message: str):
|
|
1800
|
+
"""Add a warning message."""
|
|
1801
|
+
self.warnings.append(message)
|
|
1802
|
+
|
|
1803
|
+
@property
|
|
1804
|
+
def is_valid(self) -> bool:
|
|
1805
|
+
"""Check if all validation passed (no errors)."""
|
|
1806
|
+
return len(self.errors) == 0
|
|
1807
|
+
|
|
1808
|
+
@property
|
|
1809
|
+
def has_warnings(self) -> bool:
|
|
1810
|
+
"""Check if there are warnings."""
|
|
1811
|
+
return len(self.warnings) > 0
|
|
1812
|
+
|
|
1813
|
+
def get_summary(self) -> Dict[str, Any]:
|
|
1814
|
+
"""Get validation summary."""
|
|
1815
|
+
return {
|
|
1816
|
+
'is_valid': self.is_valid,
|
|
1817
|
+
'error_count': len(self.errors),
|
|
1818
|
+
'warning_count': len(self.warnings),
|
|
1819
|
+
'core_settings_validated': len(self.core_setting_results),
|
|
1820
|
+
'tools_validated': len(self.tool_results),
|
|
1821
|
+
'consistency_checks': len(self.consistency_result.checks) if self.consistency_result else 0,
|
|
1822
|
+
'errors': self.errors,
|
|
1823
|
+
'warnings': self.warnings
|
|
1824
1824
|
}
|