@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.
Files changed (137) hide show
  1. package/dart_analyzer/README.md +226 -0
  2. package/dart_analyzer/analysis_options.yaml +66 -0
  3. package/dart_analyzer/bin/sunlint-dart-macos +0 -0
  4. package/dart_analyzer/bin/sunlint_dart_analyzer.dart +124 -0
  5. package/dart_analyzer/lib/analyzer_service.dart +625 -0
  6. package/dart_analyzer/lib/json_rpc_server.dart +275 -0
  7. package/dart_analyzer/lib/models/rule.dart +67 -0
  8. package/dart_analyzer/lib/models/symbol_table.dart +607 -0
  9. package/dart_analyzer/lib/models/violation.dart +69 -0
  10. package/dart_analyzer/lib/rules/base_analyzer.dart +52 -0
  11. package/dart_analyzer/lib/rules/common/C002_no_duplicate_code.dart +344 -0
  12. package/dart_analyzer/lib/rules/common/C003_no_vague_abbreviations.dart +318 -0
  13. package/dart_analyzer/lib/rules/common/C006_function_naming.dart +219 -0
  14. package/dart_analyzer/lib/rules/common/C008_variable_declaration_locality.dart +205 -0
  15. package/dart_analyzer/lib/rules/common/C010_limit_block_nesting.dart +162 -0
  16. package/dart_analyzer/lib/rules/common/C012_command_query_separation.dart +214 -0
  17. package/dart_analyzer/lib/rules/common/C013_no_dead_code.dart +225 -0
  18. package/dart_analyzer/lib/rules/common/C014_dependency_injection.dart +249 -0
  19. package/dart_analyzer/lib/rules/common/C017_constructor_logic.dart +158 -0
  20. package/dart_analyzer/lib/rules/common/C018_no_throw_generic_error.dart +141 -0
  21. package/dart_analyzer/lib/rules/common/C019_log_level_usage.dart +165 -0
  22. package/dart_analyzer/lib/rules/common/C020_unused_imports.dart +128 -0
  23. package/dart_analyzer/lib/rules/common/C021_import_organization.dart +86 -0
  24. package/dart_analyzer/lib/rules/common/C023_no_duplicate_variable.dart +112 -0
  25. package/dart_analyzer/lib/rules/common/C024_no_scatter_hardcoded_constants.dart +79 -0
  26. package/dart_analyzer/lib/rules/common/C029_catch_block_logging.dart +81 -0
  27. package/dart_analyzer/lib/rules/common/C030_use_custom_error_classes.dart +77 -0
  28. package/dart_analyzer/lib/rules/common/C031_validation_separation.dart +90 -0
  29. package/dart_analyzer/lib/rules/common/C033_separate_service_repository.dart +80 -0
  30. package/dart_analyzer/lib/rules/common/C035_error_logging_context.dart +148 -0
  31. package/dart_analyzer/lib/rules/common/C040_centralized_validation.dart +84 -0
  32. package/dart_analyzer/lib/rules/common/C041_no_sensitive_hardcode.dart +103 -0
  33. package/dart_analyzer/lib/rules/common/C042_boolean_name_prefix.dart +105 -0
  34. package/dart_analyzer/lib/rules/common/C043_no_console_or_print.dart +101 -0
  35. package/dart_analyzer/lib/rules/common/C047_no_duplicate_retry_logic.dart +94 -0
  36. package/dart_analyzer/lib/rules/common/C048_no_bypass_architectural_layers.dart +132 -0
  37. package/dart_analyzer/lib/rules/common/C052_parsing_or_data_transformation.dart +95 -0
  38. package/dart_analyzer/lib/rules/common/C060_no_override_superclass.dart +81 -0
  39. package/dart_analyzer/lib/rules/common/C065_one_behavior_per_test.dart +83 -0
  40. package/dart_analyzer/lib/rules/common/C067_no_hardcoded_config.dart +89 -0
  41. package/dart_analyzer/lib/rules/common/C070_no_real_time_tests.dart +99 -0
  42. package/dart_analyzer/lib/rules/common/C072_single_test_behavior.dart +78 -0
  43. package/dart_analyzer/lib/rules/common/C073_validate_required_config_on_startup.dart +82 -0
  44. package/dart_analyzer/lib/rules/common/C075_explicit_return_types.dart +85 -0
  45. package/dart_analyzer/lib/rules/common/C076_explicit_function_types.dart +104 -0
  46. package/dart_analyzer/lib/rules/dart/D001_recommended_lint_rules.dart +309 -0
  47. package/dart_analyzer/lib/rules/dart/D002_dispose_resources.dart +338 -0
  48. package/dart_analyzer/lib/rules/dart/D003_prefer_widgets_over_methods.dart +273 -0
  49. package/dart_analyzer/lib/rules/dart/D004_avoid_shrinkwrap_listview.dart +154 -0
  50. package/dart_analyzer/lib/rules/dart/D005_limit_widget_nesting.dart +265 -0
  51. package/dart_analyzer/lib/rules/dart/D006_prefer_extracting_large_callbacks.dart +135 -0
  52. package/dart_analyzer/lib/rules/dart/D007_prefer_init_first_dispose_last.dart +150 -0
  53. package/dart_analyzer/lib/rules/dart/D008_avoid_long_functions.dart +394 -0
  54. package/dart_analyzer/lib/rules/dart/D009_limit_function_parameters.dart +179 -0
  55. package/dart_analyzer/lib/rules/dart/D010_limit_cyclomatic_complexity.dart +257 -0
  56. package/dart_analyzer/lib/rules/dart/D011_prefer_named_parameters.dart +152 -0
  57. package/dart_analyzer/lib/rules/dart/D012_prefer_named_boolean_parameters.dart +156 -0
  58. package/dart_analyzer/lib/rules/dart/D013_single_public_class.dart +246 -0
  59. package/dart_analyzer/lib/rules/dart/D014_unsafe_collection_access.dart +202 -0
  60. package/dart_analyzer/lib/rules/dart/D015_copywith_all_parameters.dart +125 -0
  61. package/dart_analyzer/lib/rules/dart/D016_project_should_have_tests.dart +134 -0
  62. package/dart_analyzer/lib/rules/dart/D017_pubspec_dependencies_review.dart +187 -0
  63. package/dart_analyzer/lib/rules/dart/D018_remove_commented_code.dart +196 -0
  64. package/dart_analyzer/lib/rules/dart/D019_avoid_single_child_multi_child_widget.dart +161 -0
  65. package/dart_analyzer/lib/rules/dart/D020_limit_if_else_branches.dart +125 -0
  66. package/dart_analyzer/lib/rules/dart/D021_avoid_negated_boolean_checks.dart +227 -0
  67. package/dart_analyzer/lib/rules/dart/D022_use_setstate_correctly.dart +269 -0
  68. package/dart_analyzer/lib/rules/dart/D023_avoid_unnecessary_method_overrides.dart +191 -0
  69. package/dart_analyzer/lib/rules/dart/D024_avoid_unnecessary_stateful_widget.dart +194 -0
  70. package/dart_analyzer/lib/rules/dart/D025_avoid_nested_conditional_expressions.dart +90 -0
  71. package/dart_analyzer/lib/rules/security/S001_backend_auth_communications.dart +155 -0
  72. package/dart_analyzer/lib/rules/security/S002_os_command_injection.dart +159 -0
  73. package/dart_analyzer/lib/rules/security/S003_open_redirect_protection.dart +208 -0
  74. package/dart_analyzer/lib/rules/security/S004_sensitive_data_logging.dart +391 -0
  75. package/dart_analyzer/lib/rules/security/S005_trusted_service_authorization.dart +182 -0
  76. package/dart_analyzer/lib/rules/security/S006_no_default_credentials.dart +208 -0
  77. package/dart_analyzer/lib/rules/security/S007_output_encoding.dart +224 -0
  78. package/dart_analyzer/lib/rules/security/S008_svg_content_sanitization.dart +211 -0
  79. package/dart_analyzer/lib/rules/security/S009_no_insecure_encryption.dart +160 -0
  80. package/dart_analyzer/lib/rules/security/S010_use_csprng.dart +184 -0
  81. package/dart_analyzer/lib/rules/security/S011_ech_tls_config.dart +175 -0
  82. package/dart_analyzer/lib/rules/security/S012_hardcoded_secrets.dart +255 -0
  83. package/dart_analyzer/lib/rules/security/S013_tls_enforcement.dart +148 -0
  84. package/dart_analyzer/lib/rules/security/S014_tls_version_enforcement.dart +117 -0
  85. package/dart_analyzer/lib/rules/security/S015_insecure_tls_certificate.dart +315 -0
  86. package/dart_analyzer/lib/rules/security/S016_no_sensitive_querystring.dart +244 -0
  87. package/dart_analyzer/lib/rules/security/S017_use_parameterized_queries.dart +191 -0
  88. package/dart_analyzer/lib/rules/security/S018_no_sensitive_browser_storage.dart +175 -0
  89. package/dart_analyzer/lib/rules/security/S019_smtp_injection_protection.dart +166 -0
  90. package/dart_analyzer/lib/rules/security/S020_no_eval_dynamic_code.dart +149 -0
  91. package/dart_analyzer/lib/rules/security/S021_referrer_policy.dart +146 -0
  92. package/dart_analyzer/lib/rules/security/S022_escape_output_context.dart +111 -0
  93. package/dart_analyzer/lib/rules/security/S023_no_json_injection.dart +550 -0
  94. package/dart_analyzer/lib/rules/security/S024_xpath_xxe_protection.dart +299 -0
  95. package/dart_analyzer/lib/rules/security/S025_server_side_validation.dart +140 -0
  96. package/dart_analyzer/lib/rules/security/S026_tls_all_connections.dart +196 -0
  97. package/dart_analyzer/lib/rules/security/S027_mtls_certificate_validation.dart +195 -0
  98. package/dart_analyzer/lib/rules/security/S028_file_upload_size_limits.dart +186 -0
  99. package/dart_analyzer/lib/rules/security/S029_csrf_protection.dart +171 -0
  100. package/dart_analyzer/lib/rules/security/S030_directory_browsing_protection.dart +144 -0
  101. package/dart_analyzer/lib/rules/security/S031_secure_session_cookies.dart +118 -0
  102. package/dart_analyzer/lib/rules/security/S032_httponly_session_cookies.dart +114 -0
  103. package/dart_analyzer/lib/rules/security/S033_samesite_session_cookies.dart +120 -0
  104. package/dart_analyzer/lib/rules/security/S034_host_prefix_session_cookies.dart +160 -0
  105. package/dart_analyzer/lib/rules/security/S035_separate_app_hostnames.dart +117 -0
  106. package/dart_analyzer/lib/rules/security/S036_lfi_rfi_protection.dart +188 -0
  107. package/dart_analyzer/lib/rules/security/S037_cache_headers.dart +113 -0
  108. package/dart_analyzer/lib/rules/security/S038_no_version_headers.dart +114 -0
  109. package/dart_analyzer/lib/rules/security/S039_tls_certificate_validation.dart +131 -0
  110. package/dart_analyzer/lib/rules/security/S040_session_fixation_protection.dart +155 -0
  111. package/dart_analyzer/lib/rules/security/S041_session_token_invalidation.dart +201 -0
  112. package/dart_analyzer/lib/rules/security/S042_require_re_authentication_for_long_lived.dart +158 -0
  113. package/dart_analyzer/lib/rules/security/S043_password_changes_invalidate_all_sessions.dart +88 -0
  114. package/dart_analyzer/lib/rules/security/S044_re_authentication_required.dart +119 -0
  115. package/dart_analyzer/lib/rules/security/S045_brute_force_protection.dart +253 -0
  116. package/dart_analyzer/lib/rules/security/S046_jwt_algorithm_allowlist.dart +113 -0
  117. package/dart_analyzer/lib/rules/security/S047_oauth_pkce_protection.dart +124 -0
  118. package/dart_analyzer/lib/rules/security/S048_oauth_redirect_uri_validation.dart +134 -0
  119. package/dart_analyzer/lib/rules/security/S049_short_validity_tokens.dart +145 -0
  120. package/dart_analyzer/lib/rules/security/S050_reference_tokens_entropy.dart +234 -0
  121. package/dart_analyzer/lib/rules/security/S051_password_length_policy.dart +171 -0
  122. package/dart_analyzer/lib/rules/security/S052_weak_otp_entropy.dart +107 -0
  123. package/dart_analyzer/lib/rules/security/S053_generic_error_messages.dart +159 -0
  124. package/dart_analyzer/lib/rules/security/S054_no_default_accounts.dart +141 -0
  125. package/dart_analyzer/lib/rules/security/S055_content_type_validation.dart +324 -0
  126. package/dart_analyzer/lib/rules/security/S056_log_injection_protection.dart +119 -0
  127. package/dart_analyzer/lib/rules/security/S057_utc_logging.dart +114 -0
  128. package/dart_analyzer/lib/rules/security/S058_no_ssrf.dart +175 -0
  129. package/dart_analyzer/lib/rules/security/S059_disable_debug_mode.dart +172 -0
  130. package/dart_analyzer/lib/rules/security/S060_password_minimum_length.dart +170 -0
  131. package/dart_analyzer/lib/symbol_table_extractor.dart +510 -0
  132. package/dart_analyzer/lib/utils/common_utils.dart +26 -0
  133. package/dart_analyzer/pubspec.lock +557 -0
  134. package/dart_analyzer/pubspec.yaml +39 -0
  135. package/dart_analyzer/test/fixtures/complex_code.dart +95 -0
  136. package/docs/GENERATED_FILE_HANDLING_SUMMARY.md +2 -2
  137. 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
+ }