@sun-asterisk/sunlint 1.3.43 → 1.3.44
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/dart_analyzer/README.md +226 -0
- package/dart_analyzer/analysis_options.yaml +66 -0
- package/dart_analyzer/bin/sunlint-dart-macos +0 -0
- package/dart_analyzer/bin/sunlint_dart_analyzer.dart +124 -0
- package/dart_analyzer/lib/analyzer_service.dart +625 -0
- package/dart_analyzer/lib/json_rpc_server.dart +275 -0
- package/dart_analyzer/lib/models/rule.dart +67 -0
- package/dart_analyzer/lib/models/symbol_table.dart +607 -0
- package/dart_analyzer/lib/models/violation.dart +69 -0
- package/dart_analyzer/lib/rules/base_analyzer.dart +52 -0
- package/dart_analyzer/lib/rules/common/C002_no_duplicate_code.dart +344 -0
- package/dart_analyzer/lib/rules/common/C003_no_vague_abbreviations.dart +318 -0
- package/dart_analyzer/lib/rules/common/C006_function_naming.dart +219 -0
- package/dart_analyzer/lib/rules/common/C008_variable_declaration_locality.dart +205 -0
- package/dart_analyzer/lib/rules/common/C010_limit_block_nesting.dart +162 -0
- package/dart_analyzer/lib/rules/common/C012_command_query_separation.dart +214 -0
- package/dart_analyzer/lib/rules/common/C013_no_dead_code.dart +225 -0
- package/dart_analyzer/lib/rules/common/C014_dependency_injection.dart +249 -0
- package/dart_analyzer/lib/rules/common/C017_constructor_logic.dart +158 -0
- package/dart_analyzer/lib/rules/common/C018_no_throw_generic_error.dart +141 -0
- package/dart_analyzer/lib/rules/common/C019_log_level_usage.dart +165 -0
- package/dart_analyzer/lib/rules/common/C020_unused_imports.dart +128 -0
- package/dart_analyzer/lib/rules/common/C021_import_organization.dart +86 -0
- package/dart_analyzer/lib/rules/common/C023_no_duplicate_variable.dart +112 -0
- package/dart_analyzer/lib/rules/common/C024_no_scatter_hardcoded_constants.dart +79 -0
- package/dart_analyzer/lib/rules/common/C029_catch_block_logging.dart +81 -0
- package/dart_analyzer/lib/rules/common/C030_use_custom_error_classes.dart +77 -0
- package/dart_analyzer/lib/rules/common/C031_validation_separation.dart +90 -0
- package/dart_analyzer/lib/rules/common/C033_separate_service_repository.dart +80 -0
- package/dart_analyzer/lib/rules/common/C035_error_logging_context.dart +148 -0
- package/dart_analyzer/lib/rules/common/C040_centralized_validation.dart +84 -0
- package/dart_analyzer/lib/rules/common/C041_no_sensitive_hardcode.dart +103 -0
- package/dart_analyzer/lib/rules/common/C042_boolean_name_prefix.dart +105 -0
- package/dart_analyzer/lib/rules/common/C043_no_console_or_print.dart +101 -0
- package/dart_analyzer/lib/rules/common/C047_no_duplicate_retry_logic.dart +94 -0
- package/dart_analyzer/lib/rules/common/C048_no_bypass_architectural_layers.dart +132 -0
- package/dart_analyzer/lib/rules/common/C052_parsing_or_data_transformation.dart +95 -0
- package/dart_analyzer/lib/rules/common/C060_no_override_superclass.dart +81 -0
- package/dart_analyzer/lib/rules/common/C065_one_behavior_per_test.dart +83 -0
- package/dart_analyzer/lib/rules/common/C067_no_hardcoded_config.dart +89 -0
- package/dart_analyzer/lib/rules/common/C070_no_real_time_tests.dart +99 -0
- package/dart_analyzer/lib/rules/common/C072_single_test_behavior.dart +78 -0
- package/dart_analyzer/lib/rules/common/C073_validate_required_config_on_startup.dart +82 -0
- package/dart_analyzer/lib/rules/common/C075_explicit_return_types.dart +85 -0
- package/dart_analyzer/lib/rules/common/C076_explicit_function_types.dart +104 -0
- package/dart_analyzer/lib/rules/dart/D001_recommended_lint_rules.dart +309 -0
- package/dart_analyzer/lib/rules/dart/D002_dispose_resources.dart +338 -0
- package/dart_analyzer/lib/rules/dart/D003_prefer_widgets_over_methods.dart +273 -0
- package/dart_analyzer/lib/rules/dart/D004_avoid_shrinkwrap_listview.dart +154 -0
- package/dart_analyzer/lib/rules/dart/D005_limit_widget_nesting.dart +265 -0
- package/dart_analyzer/lib/rules/dart/D006_prefer_extracting_large_callbacks.dart +135 -0
- package/dart_analyzer/lib/rules/dart/D007_prefer_init_first_dispose_last.dart +150 -0
- package/dart_analyzer/lib/rules/dart/D008_avoid_long_functions.dart +394 -0
- package/dart_analyzer/lib/rules/dart/D009_limit_function_parameters.dart +179 -0
- package/dart_analyzer/lib/rules/dart/D010_limit_cyclomatic_complexity.dart +257 -0
- package/dart_analyzer/lib/rules/dart/D011_prefer_named_parameters.dart +152 -0
- package/dart_analyzer/lib/rules/dart/D012_prefer_named_boolean_parameters.dart +156 -0
- package/dart_analyzer/lib/rules/dart/D013_single_public_class.dart +246 -0
- package/dart_analyzer/lib/rules/dart/D014_unsafe_collection_access.dart +202 -0
- package/dart_analyzer/lib/rules/dart/D015_copywith_all_parameters.dart +125 -0
- package/dart_analyzer/lib/rules/dart/D016_project_should_have_tests.dart +134 -0
- package/dart_analyzer/lib/rules/dart/D017_pubspec_dependencies_review.dart +187 -0
- package/dart_analyzer/lib/rules/dart/D018_remove_commented_code.dart +196 -0
- package/dart_analyzer/lib/rules/dart/D019_avoid_single_child_multi_child_widget.dart +161 -0
- package/dart_analyzer/lib/rules/dart/D020_limit_if_else_branches.dart +125 -0
- package/dart_analyzer/lib/rules/dart/D021_avoid_negated_boolean_checks.dart +227 -0
- package/dart_analyzer/lib/rules/dart/D022_use_setstate_correctly.dart +269 -0
- package/dart_analyzer/lib/rules/dart/D023_avoid_unnecessary_method_overrides.dart +191 -0
- package/dart_analyzer/lib/rules/dart/D024_avoid_unnecessary_stateful_widget.dart +194 -0
- package/dart_analyzer/lib/rules/dart/D025_avoid_nested_conditional_expressions.dart +90 -0
- package/dart_analyzer/lib/rules/security/S001_backend_auth_communications.dart +155 -0
- package/dart_analyzer/lib/rules/security/S002_os_command_injection.dart +159 -0
- package/dart_analyzer/lib/rules/security/S003_open_redirect_protection.dart +208 -0
- package/dart_analyzer/lib/rules/security/S004_sensitive_data_logging.dart +391 -0
- package/dart_analyzer/lib/rules/security/S005_trusted_service_authorization.dart +182 -0
- package/dart_analyzer/lib/rules/security/S006_no_default_credentials.dart +208 -0
- package/dart_analyzer/lib/rules/security/S007_output_encoding.dart +224 -0
- package/dart_analyzer/lib/rules/security/S008_svg_content_sanitization.dart +211 -0
- package/dart_analyzer/lib/rules/security/S009_no_insecure_encryption.dart +160 -0
- package/dart_analyzer/lib/rules/security/S010_use_csprng.dart +184 -0
- package/dart_analyzer/lib/rules/security/S011_ech_tls_config.dart +175 -0
- package/dart_analyzer/lib/rules/security/S012_hardcoded_secrets.dart +255 -0
- package/dart_analyzer/lib/rules/security/S013_tls_enforcement.dart +148 -0
- package/dart_analyzer/lib/rules/security/S014_tls_version_enforcement.dart +117 -0
- package/dart_analyzer/lib/rules/security/S015_insecure_tls_certificate.dart +315 -0
- package/dart_analyzer/lib/rules/security/S016_no_sensitive_querystring.dart +244 -0
- package/dart_analyzer/lib/rules/security/S017_use_parameterized_queries.dart +191 -0
- package/dart_analyzer/lib/rules/security/S018_no_sensitive_browser_storage.dart +175 -0
- package/dart_analyzer/lib/rules/security/S019_smtp_injection_protection.dart +166 -0
- package/dart_analyzer/lib/rules/security/S020_no_eval_dynamic_code.dart +149 -0
- package/dart_analyzer/lib/rules/security/S021_referrer_policy.dart +146 -0
- package/dart_analyzer/lib/rules/security/S022_escape_output_context.dart +111 -0
- package/dart_analyzer/lib/rules/security/S023_no_json_injection.dart +550 -0
- package/dart_analyzer/lib/rules/security/S024_xpath_xxe_protection.dart +299 -0
- package/dart_analyzer/lib/rules/security/S025_server_side_validation.dart +140 -0
- package/dart_analyzer/lib/rules/security/S026_tls_all_connections.dart +196 -0
- package/dart_analyzer/lib/rules/security/S027_mtls_certificate_validation.dart +195 -0
- package/dart_analyzer/lib/rules/security/S028_file_upload_size_limits.dart +186 -0
- package/dart_analyzer/lib/rules/security/S029_csrf_protection.dart +171 -0
- package/dart_analyzer/lib/rules/security/S030_directory_browsing_protection.dart +144 -0
- package/dart_analyzer/lib/rules/security/S031_secure_session_cookies.dart +118 -0
- package/dart_analyzer/lib/rules/security/S032_httponly_session_cookies.dart +114 -0
- package/dart_analyzer/lib/rules/security/S033_samesite_session_cookies.dart +120 -0
- package/dart_analyzer/lib/rules/security/S034_host_prefix_session_cookies.dart +160 -0
- package/dart_analyzer/lib/rules/security/S035_separate_app_hostnames.dart +117 -0
- package/dart_analyzer/lib/rules/security/S036_lfi_rfi_protection.dart +188 -0
- package/dart_analyzer/lib/rules/security/S037_cache_headers.dart +113 -0
- package/dart_analyzer/lib/rules/security/S038_no_version_headers.dart +114 -0
- package/dart_analyzer/lib/rules/security/S039_tls_certificate_validation.dart +131 -0
- package/dart_analyzer/lib/rules/security/S040_session_fixation_protection.dart +155 -0
- package/dart_analyzer/lib/rules/security/S041_session_token_invalidation.dart +201 -0
- package/dart_analyzer/lib/rules/security/S042_require_re_authentication_for_long_lived.dart +158 -0
- package/dart_analyzer/lib/rules/security/S043_password_changes_invalidate_all_sessions.dart +88 -0
- package/dart_analyzer/lib/rules/security/S044_re_authentication_required.dart +119 -0
- package/dart_analyzer/lib/rules/security/S045_brute_force_protection.dart +253 -0
- package/dart_analyzer/lib/rules/security/S046_jwt_algorithm_allowlist.dart +113 -0
- package/dart_analyzer/lib/rules/security/S047_oauth_pkce_protection.dart +124 -0
- package/dart_analyzer/lib/rules/security/S048_oauth_redirect_uri_validation.dart +134 -0
- package/dart_analyzer/lib/rules/security/S049_short_validity_tokens.dart +145 -0
- package/dart_analyzer/lib/rules/security/S050_reference_tokens_entropy.dart +234 -0
- package/dart_analyzer/lib/rules/security/S051_password_length_policy.dart +171 -0
- package/dart_analyzer/lib/rules/security/S052_weak_otp_entropy.dart +107 -0
- package/dart_analyzer/lib/rules/security/S053_generic_error_messages.dart +159 -0
- package/dart_analyzer/lib/rules/security/S054_no_default_accounts.dart +141 -0
- package/dart_analyzer/lib/rules/security/S055_content_type_validation.dart +324 -0
- package/dart_analyzer/lib/rules/security/S056_log_injection_protection.dart +119 -0
- package/dart_analyzer/lib/rules/security/S057_utc_logging.dart +114 -0
- package/dart_analyzer/lib/rules/security/S058_no_ssrf.dart +175 -0
- package/dart_analyzer/lib/rules/security/S059_disable_debug_mode.dart +172 -0
- package/dart_analyzer/lib/rules/security/S060_password_minimum_length.dart +170 -0
- package/dart_analyzer/lib/symbol_table_extractor.dart +510 -0
- package/dart_analyzer/lib/utils/common_utils.dart +26 -0
- package/dart_analyzer/pubspec.lock +557 -0
- package/dart_analyzer/pubspec.yaml +39 -0
- package/dart_analyzer/test/fixtures/complex_code.dart +95 -0
- package/docs/GENERATED_FILE_HANDLING_SUMMARY.md +2 -2
- package/package.json +3 -2
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
import 'dart:io';
|
|
2
|
+
|
|
3
|
+
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
|
|
4
|
+
import 'package:analyzer/dart/analysis/results.dart';
|
|
5
|
+
import 'package:analyzer/file_system/physical_file_system.dart';
|
|
6
|
+
import 'package:path/path.dart' as path;
|
|
7
|
+
|
|
8
|
+
import 'models/rule.dart';
|
|
9
|
+
import 'models/symbol_table.dart';
|
|
10
|
+
import 'models/violation.dart';
|
|
11
|
+
import 'symbol_table_extractor.dart';
|
|
12
|
+
// Import rules matching folder structure
|
|
13
|
+
import 'rules/base_analyzer.dart';
|
|
14
|
+
// Common rules (C-series)
|
|
15
|
+
import 'rules/common/C002_no_duplicate_code.dart';
|
|
16
|
+
import 'rules/common/C003_no_vague_abbreviations.dart';
|
|
17
|
+
import 'rules/common/C006_function_naming.dart';
|
|
18
|
+
import 'rules/common/C008_variable_declaration_locality.dart';
|
|
19
|
+
import 'rules/common/C010_limit_block_nesting.dart';
|
|
20
|
+
import 'rules/common/C012_command_query_separation.dart';
|
|
21
|
+
import 'rules/common/C013_no_dead_code.dart';
|
|
22
|
+
import 'rules/common/C014_dependency_injection.dart';
|
|
23
|
+
import 'rules/common/C017_constructor_logic.dart';
|
|
24
|
+
import 'rules/common/C018_no_throw_generic_error.dart';
|
|
25
|
+
import 'rules/common/C019_log_level_usage.dart';
|
|
26
|
+
import 'rules/common/C020_unused_imports.dart';
|
|
27
|
+
import 'rules/common/C021_import_organization.dart';
|
|
28
|
+
import 'rules/common/C023_no_duplicate_variable.dart';
|
|
29
|
+
import 'rules/common/C024_no_scatter_hardcoded_constants.dart';
|
|
30
|
+
import 'rules/common/C029_catch_block_logging.dart';
|
|
31
|
+
import 'rules/common/C030_use_custom_error_classes.dart';
|
|
32
|
+
import 'rules/common/C031_validation_separation.dart';
|
|
33
|
+
import 'rules/common/C033_separate_service_repository.dart';
|
|
34
|
+
import 'rules/common/C035_error_logging_context.dart';
|
|
35
|
+
import 'rules/common/C040_centralized_validation.dart';
|
|
36
|
+
import 'rules/common/C041_no_sensitive_hardcode.dart';
|
|
37
|
+
import 'rules/common/C042_boolean_name_prefix.dart';
|
|
38
|
+
import 'rules/common/C043_no_console_or_print.dart';
|
|
39
|
+
import 'rules/common/C047_no_duplicate_retry_logic.dart';
|
|
40
|
+
import 'rules/common/C048_no_bypass_architectural_layers.dart';
|
|
41
|
+
import 'rules/common/C052_parsing_or_data_transformation.dart';
|
|
42
|
+
import 'rules/common/C060_no_override_superclass.dart';
|
|
43
|
+
import 'rules/common/C065_one_behavior_per_test.dart';
|
|
44
|
+
import 'rules/common/C067_no_hardcoded_config.dart';
|
|
45
|
+
import 'rules/common/C070_no_real_time_tests.dart';
|
|
46
|
+
import 'rules/common/C072_single_test_behavior.dart';
|
|
47
|
+
import 'rules/common/C073_validate_required_config_on_startup.dart';
|
|
48
|
+
import 'rules/common/C075_explicit_return_types.dart';
|
|
49
|
+
import 'rules/common/C076_explicit_function_types.dart';
|
|
50
|
+
// Security rules (S-series)
|
|
51
|
+
import 'rules/security/S001_backend_auth_communications.dart';
|
|
52
|
+
import 'rules/security/S002_os_command_injection.dart';
|
|
53
|
+
import 'rules/security/S003_open_redirect_protection.dart';
|
|
54
|
+
import 'rules/security/S004_sensitive_data_logging.dart';
|
|
55
|
+
import 'rules/security/S005_trusted_service_authorization.dart';
|
|
56
|
+
import 'rules/security/S006_no_default_credentials.dart';
|
|
57
|
+
import 'rules/security/S007_output_encoding.dart';
|
|
58
|
+
import 'rules/security/S008_svg_content_sanitization.dart';
|
|
59
|
+
import 'rules/security/S009_no_insecure_encryption.dart';
|
|
60
|
+
import 'rules/security/S010_use_csprng.dart';
|
|
61
|
+
import 'rules/security/S011_ech_tls_config.dart';
|
|
62
|
+
import 'rules/security/S012_hardcoded_secrets.dart';
|
|
63
|
+
import 'rules/security/S013_tls_enforcement.dart';
|
|
64
|
+
import 'rules/security/S014_tls_version_enforcement.dart';
|
|
65
|
+
import 'rules/security/S015_insecure_tls_certificate.dart';
|
|
66
|
+
import 'rules/security/S016_no_sensitive_querystring.dart';
|
|
67
|
+
import 'rules/security/S017_use_parameterized_queries.dart';
|
|
68
|
+
import 'rules/security/S018_no_sensitive_browser_storage.dart';
|
|
69
|
+
import 'rules/security/S019_smtp_injection_protection.dart';
|
|
70
|
+
import 'rules/security/S020_no_eval_dynamic_code.dart';
|
|
71
|
+
import 'rules/security/S021_referrer_policy.dart';
|
|
72
|
+
import 'rules/security/S022_escape_output_context.dart';
|
|
73
|
+
import 'rules/security/S023_no_json_injection.dart';
|
|
74
|
+
import 'rules/security/S024_xpath_xxe_protection.dart';
|
|
75
|
+
import 'rules/security/S025_server_side_validation.dart';
|
|
76
|
+
import 'rules/security/S026_tls_all_connections.dart';
|
|
77
|
+
import 'rules/security/S027_mtls_certificate_validation.dart';
|
|
78
|
+
import 'rules/security/S028_file_upload_size_limits.dart';
|
|
79
|
+
import 'rules/security/S029_csrf_protection.dart';
|
|
80
|
+
import 'rules/security/S030_directory_browsing_protection.dart';
|
|
81
|
+
import 'rules/security/S031_secure_session_cookies.dart';
|
|
82
|
+
import 'rules/security/S032_httponly_session_cookies.dart';
|
|
83
|
+
import 'rules/security/S033_samesite_session_cookies.dart';
|
|
84
|
+
import 'rules/security/S034_host_prefix_session_cookies.dart';
|
|
85
|
+
import 'rules/security/S035_separate_app_hostnames.dart';
|
|
86
|
+
import 'rules/security/S036_lfi_rfi_protection.dart';
|
|
87
|
+
import 'rules/security/S037_cache_headers.dart';
|
|
88
|
+
import 'rules/security/S038_no_version_headers.dart';
|
|
89
|
+
import 'rules/security/S039_tls_certificate_validation.dart';
|
|
90
|
+
import 'rules/security/S040_session_fixation_protection.dart';
|
|
91
|
+
import 'rules/security/S041_session_token_invalidation.dart';
|
|
92
|
+
import 'rules/security/S042_require_re_authentication_for_long_lived.dart';
|
|
93
|
+
import 'rules/security/S043_password_changes_invalidate_all_sessions.dart';
|
|
94
|
+
import 'rules/security/S044_re_authentication_required.dart';
|
|
95
|
+
import 'rules/security/S045_brute_force_protection.dart';
|
|
96
|
+
import 'rules/security/S046_jwt_algorithm_allowlist.dart';
|
|
97
|
+
import 'rules/security/S047_oauth_pkce_protection.dart';
|
|
98
|
+
import 'rules/security/S048_oauth_redirect_uri_validation.dart';
|
|
99
|
+
import 'rules/security/S049_short_validity_tokens.dart';
|
|
100
|
+
import 'rules/security/S050_reference_tokens_entropy.dart';
|
|
101
|
+
import 'rules/security/S051_password_length_policy.dart';
|
|
102
|
+
import 'rules/security/S052_weak_otp_entropy.dart';
|
|
103
|
+
import 'rules/security/S053_generic_error_messages.dart';
|
|
104
|
+
import 'rules/security/S054_no_default_accounts.dart';
|
|
105
|
+
import 'rules/security/S055_content_type_validation.dart';
|
|
106
|
+
import 'rules/security/S056_log_injection_protection.dart';
|
|
107
|
+
import 'rules/security/S057_utc_logging.dart';
|
|
108
|
+
import 'rules/security/S058_no_ssrf.dart';
|
|
109
|
+
import 'rules/security/S059_disable_debug_mode.dart';
|
|
110
|
+
import 'rules/security/S060_password_minimum_length.dart';
|
|
111
|
+
// Dart-only rules (D-series)
|
|
112
|
+
import 'rules/dart/D001_recommended_lint_rules.dart';
|
|
113
|
+
import 'rules/dart/D002_dispose_resources.dart';
|
|
114
|
+
import 'rules/dart/D003_prefer_widgets_over_methods.dart';
|
|
115
|
+
import 'rules/dart/D004_avoid_shrinkwrap_listview.dart';
|
|
116
|
+
import 'rules/dart/D005_limit_widget_nesting.dart';
|
|
117
|
+
import 'rules/dart/D006_prefer_extracting_large_callbacks.dart';
|
|
118
|
+
import 'rules/dart/D007_prefer_init_first_dispose_last.dart';
|
|
119
|
+
import 'rules/dart/D008_avoid_long_functions.dart';
|
|
120
|
+
import 'rules/dart/D009_limit_function_parameters.dart';
|
|
121
|
+
import 'rules/dart/D010_limit_cyclomatic_complexity.dart';
|
|
122
|
+
import 'rules/dart/D011_prefer_named_parameters.dart';
|
|
123
|
+
import 'rules/dart/D012_prefer_named_boolean_parameters.dart';
|
|
124
|
+
import 'rules/dart/D013_single_public_class.dart';
|
|
125
|
+
import 'rules/dart/D014_unsafe_collection_access.dart';
|
|
126
|
+
import 'rules/dart/D015_copywith_all_parameters.dart';
|
|
127
|
+
import 'rules/dart/D016_project_should_have_tests.dart';
|
|
128
|
+
import 'rules/dart/D017_pubspec_dependencies_review.dart';
|
|
129
|
+
import 'rules/dart/D018_remove_commented_code.dart';
|
|
130
|
+
import 'rules/dart/D019_avoid_single_child_multi_child_widget.dart';
|
|
131
|
+
import 'rules/dart/D020_limit_if_else_branches.dart';
|
|
132
|
+
import 'rules/dart/D021_avoid_negated_boolean_checks.dart';
|
|
133
|
+
import 'rules/dart/D022_use_setstate_correctly.dart';
|
|
134
|
+
import 'rules/dart/D023_avoid_unnecessary_method_overrides.dart';
|
|
135
|
+
import 'rules/dart/D024_avoid_unnecessary_stateful_widget.dart';
|
|
136
|
+
import 'rules/dart/D025_avoid_nested_conditional_expressions.dart';
|
|
137
|
+
|
|
138
|
+
/// Main service for Dart code analysis
|
|
139
|
+
/// Manages the analysis context and rule execution
|
|
140
|
+
class AnalyzerService {
|
|
141
|
+
AnalysisContextCollection? _contextCollection;
|
|
142
|
+
String? _projectPath;
|
|
143
|
+
bool _initialized = false;
|
|
144
|
+
|
|
145
|
+
/// Map of rule ID to analyzer
|
|
146
|
+
final Map<String, BaseAnalyzer> _analyzers = {};
|
|
147
|
+
|
|
148
|
+
/// List of supported rule IDs
|
|
149
|
+
List<String> get supportedRules => _analyzers.keys.toList();
|
|
150
|
+
|
|
151
|
+
AnalyzerService() {
|
|
152
|
+
_registerAnalyzers();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// Register all available analyzers
|
|
156
|
+
/// Analyzer names match TypeScript rule folder names
|
|
157
|
+
void _registerAnalyzers() {
|
|
158
|
+
// Common rules (C-series)
|
|
159
|
+
_analyzers['C002'] = C002NoDuplicateCodeAnalyzer();
|
|
160
|
+
_analyzers['C003'] = C003NoVagueAbbreviationsAnalyzer();
|
|
161
|
+
_analyzers['C006'] = C006FunctionNamingAnalyzer();
|
|
162
|
+
_analyzers['C008'] = C008VariableDeclarationLocalityAnalyzer();
|
|
163
|
+
_analyzers['C010'] = C010LimitBlockNestingAnalyzer();
|
|
164
|
+
_analyzers['C012'] = C012CommandQuerySeparationAnalyzer();
|
|
165
|
+
_analyzers['C013'] = C013NoDeadCodeAnalyzer();
|
|
166
|
+
_analyzers['C014'] = C014DependencyInjectionAnalyzer();
|
|
167
|
+
_analyzers['C017'] = C017ConstructorLogicAnalyzer();
|
|
168
|
+
_analyzers['C018'] = C018NoThrowGenericErrorAnalyzer();
|
|
169
|
+
_analyzers['C019'] = C019LogLevelUsageAnalyzer();
|
|
170
|
+
_analyzers['C020'] = C020UnusedImportsAnalyzer();
|
|
171
|
+
_analyzers['C021'] = C021ImportOrganizationAnalyzer();
|
|
172
|
+
_analyzers['C023'] = C023NoDuplicateVariableAnalyzer();
|
|
173
|
+
_analyzers['C024'] = C024NoScatterHardcodedConstantsAnalyzer();
|
|
174
|
+
_analyzers['C029'] = C029CatchBlockLoggingAnalyzer();
|
|
175
|
+
_analyzers['C030'] = C030UseCustomErrorClassesAnalyzer();
|
|
176
|
+
_analyzers['C031'] = C031ValidationSeparationAnalyzer();
|
|
177
|
+
_analyzers['C033'] = C033SeparateServiceRepositoryAnalyzer();
|
|
178
|
+
_analyzers['C035'] = C035ErrorLoggingContextAnalyzer();
|
|
179
|
+
_analyzers['C040'] = C040CentralizedValidationAnalyzer();
|
|
180
|
+
_analyzers['C041'] = C041NoSensitiveHardcodeAnalyzer();
|
|
181
|
+
_analyzers['C042'] = C042BooleanNamePrefixAnalyzer();
|
|
182
|
+
_analyzers['C043'] = C043NoConsoleOrPrintAnalyzer();
|
|
183
|
+
_analyzers['C047'] = C047NoDuplicateRetryLogicAnalyzer();
|
|
184
|
+
_analyzers['C048'] = C048NoBypassArchitecturalLayersAnalyzer();
|
|
185
|
+
_analyzers['C052'] = C052ParsingOrDataTransformationAnalyzer();
|
|
186
|
+
_analyzers['C060'] = C060NoOverrideSuperclassAnalyzer();
|
|
187
|
+
_analyzers['C065'] = C065OneBehaviorPerTestAnalyzer();
|
|
188
|
+
_analyzers['C067'] = C067NoHardcodedConfigAnalyzer();
|
|
189
|
+
_analyzers['C070'] = C070NoRealTimeTestsAnalyzer();
|
|
190
|
+
_analyzers['C072'] = C072SingleTestBehaviorAnalyzer();
|
|
191
|
+
_analyzers['C073'] = C073ValidateRequiredConfigOnStartupAnalyzer();
|
|
192
|
+
_analyzers['C075'] = C075ExplicitReturnTypesAnalyzer();
|
|
193
|
+
_analyzers['C076'] = C076ExplicitFunctionTypesAnalyzer();
|
|
194
|
+
|
|
195
|
+
// Security rules (S-series)
|
|
196
|
+
_analyzers['S001'] = S001BackendAuthCommunicationsAnalyzer();
|
|
197
|
+
_analyzers['S002'] = S002OsCommandInjectionAnalyzer();
|
|
198
|
+
_analyzers['S003'] = S003OpenRedirectProtectionAnalyzer();
|
|
199
|
+
_analyzers['S004'] = S004SensitiveDataLoggingAnalyzer();
|
|
200
|
+
_analyzers['S005'] = S005TrustedServiceAuthorizationAnalyzer();
|
|
201
|
+
_analyzers['S006'] = S006NoDefaultCredentialsAnalyzer();
|
|
202
|
+
_analyzers['S007'] = S007OutputEncodingAnalyzer();
|
|
203
|
+
_analyzers['S008'] = S008SvgContentSanitizationAnalyzer();
|
|
204
|
+
_analyzers['S009'] = S009NoInsecureEncryptionAnalyzer();
|
|
205
|
+
_analyzers['S010'] = S010UseCsprngAnalyzer();
|
|
206
|
+
_analyzers['S011'] = S011EchTlsConfigAnalyzer();
|
|
207
|
+
_analyzers['S012'] = S012HardcodedSecretsAnalyzer();
|
|
208
|
+
_analyzers['S013'] = S013TlsEnforcementAnalyzer();
|
|
209
|
+
_analyzers['S014'] = S014TlsVersionEnforcementAnalyzer();
|
|
210
|
+
_analyzers['S015'] = S015InsecureTlsCertificateAnalyzer();
|
|
211
|
+
_analyzers['S016'] = S016NoSensitiveQuerystringAnalyzer();
|
|
212
|
+
_analyzers['S017'] = S017UseParameterizedQueriesAnalyzer();
|
|
213
|
+
_analyzers['S018'] = S018NoSensitiveBrowserStorageAnalyzer();
|
|
214
|
+
_analyzers['S019'] = S019SmtpInjectionProtectionAnalyzer();
|
|
215
|
+
_analyzers['S020'] = S020NoEvalDynamicCodeAnalyzer();
|
|
216
|
+
_analyzers['S021'] = S021ReferrerPolicyAnalyzer();
|
|
217
|
+
_analyzers['S022'] = S022EscapeOutputContextAnalyzer();
|
|
218
|
+
_analyzers['S023'] = S023NoJsonInjectionAnalyzer();
|
|
219
|
+
_analyzers['S024'] = S024XpathXxeProtectionAnalyzer();
|
|
220
|
+
_analyzers['S025'] = S025ServerSideValidationAnalyzer();
|
|
221
|
+
_analyzers['S026'] = S026TlsAllConnectionsAnalyzer();
|
|
222
|
+
_analyzers['S027'] = S027MtlsCertificateValidationAnalyzer();
|
|
223
|
+
_analyzers['S028'] = S028FileUploadSizeLimitsAnalyzer();
|
|
224
|
+
_analyzers['S029'] = S029CsrfProtectionAnalyzer();
|
|
225
|
+
_analyzers['S030'] = S030DirectoryBrowsingProtectionAnalyzer();
|
|
226
|
+
_analyzers['S031'] = S031SecureSessionCookiesAnalyzer();
|
|
227
|
+
_analyzers['S032'] = S032HttpOnlySessionCookiesAnalyzer();
|
|
228
|
+
_analyzers['S033'] = S033SameSiteSessionCookiesAnalyzer();
|
|
229
|
+
_analyzers['S034'] = S034HostPrefixSessionCookiesAnalyzer();
|
|
230
|
+
_analyzers['S035'] = S035SeparateAppHostnamesAnalyzer();
|
|
231
|
+
_analyzers['S036'] = S036LfiRfiProtectionAnalyzer();
|
|
232
|
+
_analyzers['S037'] = S037CacheHeadersAnalyzer();
|
|
233
|
+
_analyzers['S038'] = S038NoVersionHeadersAnalyzer();
|
|
234
|
+
_analyzers['S039'] = S039TlsCertificateValidationAnalyzer();
|
|
235
|
+
_analyzers['S040'] = S040SessionFixationProtectionAnalyzer();
|
|
236
|
+
_analyzers['S041'] = S041SessionTokenInvalidationAnalyzer();
|
|
237
|
+
_analyzers['S042'] = S042RequireReAuthenticationForLongLivedAnalyzer();
|
|
238
|
+
_analyzers['S043'] = S043PasswordChangesInvalidateAllSessionsAnalyzer();
|
|
239
|
+
_analyzers['S044'] = S044ReAuthenticationRequiredAnalyzer();
|
|
240
|
+
_analyzers['S045'] = S045BruteForceProtectionAnalyzer();
|
|
241
|
+
_analyzers['S046'] = S046JwtAlgorithmAllowlistAnalyzer();
|
|
242
|
+
_analyzers['S047'] = S047OAuthPkceProtectionAnalyzer();
|
|
243
|
+
_analyzers['S048'] = S048OAuthRedirectUriValidationAnalyzer();
|
|
244
|
+
_analyzers['S049'] = S049ShortValidityTokensAnalyzer();
|
|
245
|
+
_analyzers['S050'] = S050ReferenceTokensEntropyAnalyzer();
|
|
246
|
+
_analyzers['S051'] = S051PasswordLengthPolicyAnalyzer();
|
|
247
|
+
_analyzers['S052'] = S052WeakOtpEntropyAnalyzer();
|
|
248
|
+
_analyzers['S053'] = S053GenericErrorMessagesAnalyzer();
|
|
249
|
+
_analyzers['S054'] = S054NoDefaultAccountsAnalyzer();
|
|
250
|
+
_analyzers['S055'] = S055ContentTypeValidationAnalyzer();
|
|
251
|
+
_analyzers['S056'] = S056LogInjectionProtectionAnalyzer();
|
|
252
|
+
_analyzers['S057'] = S057UtcLoggingAnalyzer();
|
|
253
|
+
_analyzers['S058'] = S058NoSsrfAnalyzer();
|
|
254
|
+
_analyzers['S059'] = S059DisableDebugModeAnalyzer();
|
|
255
|
+
_analyzers['S060'] = S060PasswordMinimumLengthAnalyzer();
|
|
256
|
+
|
|
257
|
+
// Dart-only rules (D-series)
|
|
258
|
+
_analyzers['D001'] = D001RecommendedLintRulesAnalyzer();
|
|
259
|
+
_analyzers['D002'] = D002DisposeResourcesAnalyzer();
|
|
260
|
+
_analyzers['D003'] = D003PreferWidgetsOverMethodsAnalyzer();
|
|
261
|
+
_analyzers['D004'] = D004AvoidShrinkwrapListviewAnalyzer();
|
|
262
|
+
_analyzers['D005'] = D005LimitWidgetNestingAnalyzer();
|
|
263
|
+
_analyzers['D006'] = D006PreferExtractingLargeCallbacksAnalyzer();
|
|
264
|
+
_analyzers['D007'] = D007PreferInitFirstDisposeLastAnalyzer();
|
|
265
|
+
_analyzers['D008'] = D008AvoidLongFunctionsAnalyzer();
|
|
266
|
+
_analyzers['D009'] = D009LimitFunctionParametersAnalyzer();
|
|
267
|
+
_analyzers['D010'] = D010LimitCyclomaticComplexityAnalyzer();
|
|
268
|
+
_analyzers['D011'] = D011PreferNamedParametersAnalyzer();
|
|
269
|
+
_analyzers['D012'] = D012PreferNamedBooleanParametersAnalyzer();
|
|
270
|
+
_analyzers['D013'] = D013SinglePublicClassAnalyzer();
|
|
271
|
+
_analyzers['D014'] = D014UnsafeCollectionAccessAnalyzer();
|
|
272
|
+
_analyzers['D015'] = D015CopyWithAllParametersAnalyzer();
|
|
273
|
+
_analyzers['D016'] = D016ProjectShouldHaveTestsAnalyzer();
|
|
274
|
+
_analyzers['D017'] = D017PubspecDependenciesReviewAnalyzer();
|
|
275
|
+
_analyzers['D018'] = D018RemoveCommentedCodeAnalyzer();
|
|
276
|
+
_analyzers['D019'] = D019AvoidSingleChildMultiChildWidgetAnalyzer();
|
|
277
|
+
_analyzers['D020'] = D020LimitIfElseBranchesAnalyzer();
|
|
278
|
+
_analyzers['D021'] = D021AvoidNegatedBooleanChecksAnalyzer();
|
|
279
|
+
_analyzers['D022'] = D022UseSetStateCorrectlyAnalyzer();
|
|
280
|
+
_analyzers['D023'] = D023AvoidUnnecessaryMethodOverridesAnalyzer();
|
|
281
|
+
_analyzers['D024'] = D024AvoidUnnecessaryStatefulWidgetAnalyzer();
|
|
282
|
+
_analyzers['D025'] = D025AvoidNestedConditionalExpressionsAnalyzer();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/// Initialize the analyzer with a project path
|
|
286
|
+
Future<void> initialize({
|
|
287
|
+
required String projectPath,
|
|
288
|
+
List<String>? targetFiles,
|
|
289
|
+
String? sdkPath,
|
|
290
|
+
}) async {
|
|
291
|
+
_projectPath = projectPath;
|
|
292
|
+
|
|
293
|
+
// Create analysis context collection
|
|
294
|
+
final resourceProvider = PhysicalResourceProvider.INSTANCE;
|
|
295
|
+
|
|
296
|
+
// Determine included paths
|
|
297
|
+
final includedPaths = <String>[];
|
|
298
|
+
|
|
299
|
+
if (targetFiles != null && targetFiles.isNotEmpty) {
|
|
300
|
+
// Use specific files
|
|
301
|
+
for (final file in targetFiles) {
|
|
302
|
+
var absolutePath =
|
|
303
|
+
path.isAbsolute(file) ? file : path.join(projectPath, file);
|
|
304
|
+
// Normalize the path to remove .. and .
|
|
305
|
+
absolutePath = path.normalize(absolutePath);
|
|
306
|
+
if (File(absolutePath).existsSync()) {
|
|
307
|
+
includedPaths.add(absolutePath);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
// Use project path
|
|
312
|
+
includedPaths.add(path.normalize(projectPath));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (includedPaths.isEmpty) {
|
|
316
|
+
includedPaths.add(path.normalize(projectPath));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Resolve SDK path
|
|
320
|
+
final resolvedSdkPath = sdkPath ?? _findSdkPath();
|
|
321
|
+
if (resolvedSdkPath != null) {
|
|
322
|
+
_log('Using SDK path: $resolvedSdkPath');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
_contextCollection = AnalysisContextCollection(
|
|
326
|
+
includedPaths: includedPaths,
|
|
327
|
+
resourceProvider: resourceProvider,
|
|
328
|
+
sdkPath: resolvedSdkPath,
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
_initialized = true;
|
|
332
|
+
_log('Initialized with project: $projectPath');
|
|
333
|
+
_log('Included paths: $includedPaths');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/// Find the Dart SDK path
|
|
337
|
+
String? _findSdkPath() {
|
|
338
|
+
// Try DART_SDK environment variable
|
|
339
|
+
final dartSdk = Platform.environment['DART_SDK'];
|
|
340
|
+
if (dartSdk != null && _isValidSdkPath(dartSdk)) {
|
|
341
|
+
return dartSdk;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Try to find 'dart' command in PATH
|
|
345
|
+
final dartFromPath = _findDartInPath();
|
|
346
|
+
if (dartFromPath != null) {
|
|
347
|
+
return dartFromPath;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Try to find from resolved executable (works when running via 'dart run')
|
|
351
|
+
final dartExe = Platform.resolvedExecutable;
|
|
352
|
+
final exeName = path.basename(dartExe).toLowerCase();
|
|
353
|
+
// Only use this if we're running from 'dart' executable
|
|
354
|
+
if (exeName == 'dart' || exeName == 'dart.exe') {
|
|
355
|
+
// Dart SDK structure: <sdk>/bin/dart
|
|
356
|
+
final sdkPath = path.dirname(path.dirname(dartExe));
|
|
357
|
+
if (_isValidSdkPath(sdkPath)) {
|
|
358
|
+
return sdkPath;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Try common locations
|
|
363
|
+
final commonPaths = [
|
|
364
|
+
'/usr/local/opt/dart/libexec', // Homebrew on macOS
|
|
365
|
+
'/opt/homebrew/opt/dart/libexec', // Homebrew on Apple Silicon
|
|
366
|
+
'/usr/lib/dart', // Linux
|
|
367
|
+
'C:\\tools\\dart-sdk', // Windows
|
|
368
|
+
// FVM common paths
|
|
369
|
+
'${Platform.environment['HOME']}/fvm/default/bin/cache/dart-sdk',
|
|
370
|
+
];
|
|
371
|
+
|
|
372
|
+
for (final p in commonPaths) {
|
|
373
|
+
if (_isValidSdkPath(p)) {
|
|
374
|
+
return p;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Try to find FVM version
|
|
379
|
+
final fvmPath = _findFvmSdk();
|
|
380
|
+
if (fvmPath != null) {
|
|
381
|
+
return fvmPath;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/// Check if a path is a valid Dart SDK
|
|
388
|
+
bool _isValidSdkPath(String sdkPath) {
|
|
389
|
+
// A valid SDK has lib/core/core.dart
|
|
390
|
+
return Directory(sdkPath).existsSync() &&
|
|
391
|
+
File(path.join(sdkPath, 'lib', 'core', 'core.dart')).existsSync();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/// Find dart executable in PATH and return SDK path
|
|
395
|
+
String? _findDartInPath() {
|
|
396
|
+
try {
|
|
397
|
+
final result = Process.runSync('which', ['dart']);
|
|
398
|
+
if (result.exitCode == 0) {
|
|
399
|
+
final dartPath = (result.stdout as String).trim();
|
|
400
|
+
if (dartPath.isNotEmpty) {
|
|
401
|
+
// Resolve symlinks
|
|
402
|
+
final resolved = File(dartPath).resolveSymbolicLinksSync();
|
|
403
|
+
// <sdk>/bin/dart -> <sdk>
|
|
404
|
+
var sdkPath = path.dirname(path.dirname(resolved));
|
|
405
|
+
|
|
406
|
+
// Check if this is Flutter SDK, try the embedded Dart SDK
|
|
407
|
+
if (!_isValidSdkPath(sdkPath)) {
|
|
408
|
+
// Flutter SDK structure: <flutter>/bin/dart -> try <flutter>/bin/cache/dart-sdk
|
|
409
|
+
final flutterEmbeddedSdk =
|
|
410
|
+
path.join(sdkPath, 'bin', 'cache', 'dart-sdk');
|
|
411
|
+
if (_isValidSdkPath(flutterEmbeddedSdk)) {
|
|
412
|
+
return flutterEmbeddedSdk;
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
return sdkPath;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} catch (_) {
|
|
420
|
+
// Ignore errors
|
|
421
|
+
}
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/// Find Dart SDK from FVM
|
|
426
|
+
String? _findFvmSdk() {
|
|
427
|
+
final home = Platform.environment['HOME'];
|
|
428
|
+
if (home == null) return null;
|
|
429
|
+
|
|
430
|
+
final fvmVersionsDir = Directory(path.join(home, 'fvm', 'versions'));
|
|
431
|
+
if (!fvmVersionsDir.existsSync()) return null;
|
|
432
|
+
|
|
433
|
+
// Find any version and use its SDK
|
|
434
|
+
try {
|
|
435
|
+
final versions = fvmVersionsDir.listSync().whereType<Directory>();
|
|
436
|
+
for (final versionDir in versions) {
|
|
437
|
+
final sdkPath = path.join(versionDir.path, 'bin', 'cache', 'dart-sdk');
|
|
438
|
+
if (_isValidSdkPath(sdkPath)) {
|
|
439
|
+
return sdkPath;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
} catch (_) {
|
|
443
|
+
// Ignore errors
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Patterns for generated files that should be skipped
|
|
450
|
+
static final _generatedFilePatterns = [
|
|
451
|
+
// Code generation patterns
|
|
452
|
+
RegExp(r'\.g\.dart$'), // json_serializable, built_value
|
|
453
|
+
RegExp(r'\.gen\.dart$'), // flutter_gen (assets, localization)
|
|
454
|
+
RegExp(r'\.gql\.dart$'), // GraphQL code generation
|
|
455
|
+
RegExp(r'\.graphql\.dart$'), // GraphQL code generation (alternative)
|
|
456
|
+
RegExp(r'\.freezed\.dart$'), // freezed unions and sealed classes
|
|
457
|
+
RegExp(r'\.gr\.dart$'), // gRPC code generation
|
|
458
|
+
RegExp(r'\.pb\.dart$'), // Protocol buffers
|
|
459
|
+
RegExp(r'\.pbenum\.dart$'), // Protocol buffers enums
|
|
460
|
+
RegExp(r'\.pbserver\.dart$'), // Protocol buffers server
|
|
461
|
+
RegExp(r'\.pbjson\.dart$'), // Protocol buffers JSON
|
|
462
|
+
RegExp(r'\.mocks\.dart$'), // Mockito generated mocks
|
|
463
|
+
RegExp(r'\.config\.dart$'), // Injectable and other config generators
|
|
464
|
+
RegExp(r'\.iconfig\.dart$'), // Injectable config
|
|
465
|
+
RegExp(r'\.mapper\.dart$'), // Mapper generators (dart_mappable)
|
|
466
|
+
RegExp(r'\.drift\.dart$'), // Drift database
|
|
467
|
+
RegExp(r'\.chopper\.dart$'), // Chopper HTTP client
|
|
468
|
+
RegExp(r'\.reflectable\.dart$'), // Reflectable code generation
|
|
469
|
+
|
|
470
|
+
// Directory patterns
|
|
471
|
+
RegExp(r'__generated__'), // General generated directory
|
|
472
|
+
RegExp(r'/generated/'), // Generated directory
|
|
473
|
+
RegExp(r'\.generated\.'), // Files with .generated. in name
|
|
474
|
+
|
|
475
|
+
// Special cases
|
|
476
|
+
RegExp(r'pigeon\.dart$'), // Pigeon platform channels
|
|
477
|
+
RegExp(r'l10n\.dart$'), // Flutter intl localization
|
|
478
|
+
RegExp(r'_l10n\.dart$'), // Flutter intl localization (alternative)
|
|
479
|
+
];
|
|
480
|
+
|
|
481
|
+
/// Check if file is auto-generated
|
|
482
|
+
bool _isGeneratedFile(String filePath) {
|
|
483
|
+
final fileName = path.basename(filePath);
|
|
484
|
+
final fullPath = filePath.toLowerCase();
|
|
485
|
+
return _generatedFilePatterns.any((p) => p.hasMatch(fullPath) || p.hasMatch(fileName));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/// Analyze a single file with specified rules
|
|
489
|
+
Future<List<Violation>> analyzeFile({
|
|
490
|
+
required String filePath,
|
|
491
|
+
List<dynamic>? rulesData,
|
|
492
|
+
}) async {
|
|
493
|
+
if (!_initialized) {
|
|
494
|
+
throw StateError('AnalyzerService not initialized');
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
final absolutePath = _getAbsolutePath(filePath);
|
|
498
|
+
|
|
499
|
+
// Skip generated files - they are auto-generated and shouldn't be analyzed for security
|
|
500
|
+
if (_isGeneratedFile(absolutePath)) {
|
|
501
|
+
_log('Skipping generated file: $absolutePath');
|
|
502
|
+
return [];
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (!File(absolutePath).existsSync()) {
|
|
506
|
+
_log('File not found: $absolutePath');
|
|
507
|
+
return [];
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Parse rules from data
|
|
511
|
+
final rules = _parseRules(rulesData);
|
|
512
|
+
|
|
513
|
+
// Get the analysis context for this file
|
|
514
|
+
final context = _contextCollection!.contextFor(absolutePath);
|
|
515
|
+
final session = context.currentSession;
|
|
516
|
+
|
|
517
|
+
// Get parsed unit (more reliable for linting)
|
|
518
|
+
final result = session.getParsedUnit(absolutePath);
|
|
519
|
+
|
|
520
|
+
if (result is! ParsedUnitResult) {
|
|
521
|
+
_log('Failed to parse: $absolutePath');
|
|
522
|
+
return [];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
final unit = result.unit;
|
|
526
|
+
final violations = <Violation>[];
|
|
527
|
+
|
|
528
|
+
// Run each applicable analyzer
|
|
529
|
+
for (final rule in rules) {
|
|
530
|
+
final analyzer = _analyzers[rule.id];
|
|
531
|
+
if (analyzer == null) continue;
|
|
532
|
+
|
|
533
|
+
try {
|
|
534
|
+
final ruleViolations = analyzer.analyze(
|
|
535
|
+
unit: unit,
|
|
536
|
+
filePath: absolutePath,
|
|
537
|
+
rule: rule,
|
|
538
|
+
lineInfo: result.lineInfo,
|
|
539
|
+
);
|
|
540
|
+
violations.addAll(ruleViolations);
|
|
541
|
+
} catch (e) {
|
|
542
|
+
_logError('Analyzer ${rule.id} failed: $e');
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
_log('Analyzed $absolutePath: ${violations.length} violations');
|
|
547
|
+
return violations;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/// Get symbol table for a file
|
|
551
|
+
Future<SymbolTable?> getSymbolTable(String filePath) async {
|
|
552
|
+
if (!_initialized) {
|
|
553
|
+
throw StateError('AnalyzerService not initialized');
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
final absolutePath = _getAbsolutePath(filePath);
|
|
557
|
+
|
|
558
|
+
if (!File(absolutePath).existsSync()) {
|
|
559
|
+
_log('File not found: $absolutePath');
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Get the analysis context for this file
|
|
564
|
+
final context = _contextCollection!.contextFor(absolutePath);
|
|
565
|
+
final session = context.currentSession;
|
|
566
|
+
|
|
567
|
+
// Get parsed unit (faster than resolved for symbol extraction)
|
|
568
|
+
final result = session.getParsedUnit(absolutePath);
|
|
569
|
+
|
|
570
|
+
if (result is! ParsedUnitResult) {
|
|
571
|
+
_log('Failed to parse: $absolutePath');
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Extract symbol table
|
|
576
|
+
final extractor = SymbolTableExtractor(
|
|
577
|
+
filePath: absolutePath,
|
|
578
|
+
fileName: path.basename(absolutePath),
|
|
579
|
+
lineInfo: result.lineInfo,
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
return extractor.extract(result.unit);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/// Parse rules from JSON data
|
|
586
|
+
List<Rule> _parseRules(List<dynamic>? rulesData) {
|
|
587
|
+
if (rulesData == null || rulesData.isEmpty) {
|
|
588
|
+
// Return all supported rules with default config
|
|
589
|
+
return supportedRules.map((id) => Rule(id: id)).toList();
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return rulesData.map((data) {
|
|
593
|
+
if (data is String) {
|
|
594
|
+
return Rule(id: data);
|
|
595
|
+
} else if (data is Map<String, dynamic>) {
|
|
596
|
+
return Rule.fromJson(data);
|
|
597
|
+
} else {
|
|
598
|
+
return Rule(id: data.toString());
|
|
599
|
+
}
|
|
600
|
+
}).toList();
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/// Get absolute path for a file
|
|
604
|
+
String _getAbsolutePath(String filePath) {
|
|
605
|
+
if (path.isAbsolute(filePath)) {
|
|
606
|
+
return path.normalize(filePath);
|
|
607
|
+
}
|
|
608
|
+
return path
|
|
609
|
+
.normalize(path.join(_projectPath ?? Directory.current.path, filePath));
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/// Dispose resources
|
|
613
|
+
void dispose() {
|
|
614
|
+
_contextCollection = null;
|
|
615
|
+
_initialized = false;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
void _log(String message) {
|
|
619
|
+
stderr.writeln('[AnalyzerService] $message');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
void _logError(String message) {
|
|
623
|
+
stderr.writeln('[AnalyzerService ERROR] $message');
|
|
624
|
+
}
|
|
625
|
+
}
|