@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,275 @@
1
+ import 'dart:async';
2
+ import 'dart:convert';
3
+ import 'dart:io';
4
+
5
+ import 'analyzer_service.dart';
6
+
7
+ /// JSON-RPC 2.0 Server for SunLint Dart Analyzer
8
+ /// Communicates via STDIO with the Node.js parent process
9
+ class JsonRpcServer {
10
+ final AnalyzerService _analyzerService;
11
+ bool _running = false;
12
+ StreamSubscription? _subscription;
13
+
14
+ JsonRpcServer() : _analyzerService = AnalyzerService();
15
+
16
+ /// Start the JSON-RPC server
17
+ /// Listens on stdin for requests, writes responses to stdout
18
+ Future<void> start() async {
19
+ if (_running) {
20
+ _logError('Server already running');
21
+ return;
22
+ }
23
+
24
+ _running = true;
25
+ _log('SunLint Dart Analyzer started');
26
+ _log('Listening for JSON-RPC requests on stdin...');
27
+
28
+ // Listen on stdin for newline-delimited JSON
29
+ _subscription = stdin
30
+ .transform(utf8.decoder)
31
+ .transform(const LineSplitter())
32
+ .listen(
33
+ _handleLine,
34
+ onError: _handleError,
35
+ onDone: _handleDone,
36
+ );
37
+ }
38
+
39
+ /// Stop the server
40
+ Future<void> stop() async {
41
+ _running = false;
42
+ await _subscription?.cancel();
43
+ _subscription = null;
44
+ _log('Server stopped');
45
+ }
46
+
47
+ /// Handle a single line of input (one JSON-RPC request)
48
+ Future<void> _handleLine(String line) async {
49
+ if (line.trim().isEmpty) return;
50
+
51
+ try {
52
+ final request = jsonDecode(line) as Map<String, dynamic>;
53
+ final response = await _processRequest(request);
54
+ _sendResponse(response);
55
+ } catch (e, stackTrace) {
56
+ _logError('Failed to process request: $e');
57
+ _logError('Stack trace: $stackTrace');
58
+ _sendResponse(_createErrorResponse(
59
+ null,
60
+ -32700,
61
+ 'Parse error: $e',
62
+ ));
63
+ }
64
+ }
65
+
66
+ /// Process a JSON-RPC request and return a response
67
+ Future<Map<String, dynamic>> _processRequest(
68
+ Map<String, dynamic> request) async {
69
+ // Validate JSON-RPC 2.0 format
70
+ final jsonrpc = request['jsonrpc'];
71
+ if (jsonrpc != '2.0') {
72
+ return _createErrorResponse(
73
+ request['id'],
74
+ -32600,
75
+ 'Invalid Request: jsonrpc must be "2.0"',
76
+ );
77
+ }
78
+
79
+ final method = request['method'] as String?;
80
+ if (method == null) {
81
+ return _createErrorResponse(
82
+ request['id'],
83
+ -32600,
84
+ 'Invalid Request: method is required',
85
+ );
86
+ }
87
+
88
+ final id = request['id'];
89
+ final params = request['params'] as Map<String, dynamic>? ?? {};
90
+
91
+ try {
92
+ final result = await _invokeMethod(method, params);
93
+ return _createSuccessResponse(id, result);
94
+ } on MethodNotFoundException catch (e) {
95
+ return _createErrorResponse(id, -32601, e.message);
96
+ } on InvalidParamsException catch (e) {
97
+ return _createErrorResponse(id, -32602, e.message);
98
+ } catch (e, stackTrace) {
99
+ _logError('Error in method $method: $e');
100
+ _logError('Stack trace: $stackTrace');
101
+ return _createErrorResponse(id, -32603, 'Internal error: $e');
102
+ }
103
+ }
104
+
105
+ /// Invoke the requested method
106
+ Future<dynamic> _invokeMethod(
107
+ String method, Map<String, dynamic> params) async {
108
+ switch (method) {
109
+ case 'initialize':
110
+ return await _handleInitialize(params);
111
+ case 'analyze':
112
+ return await _handleAnalyze(params);
113
+ case 'getSymbolTable':
114
+ return await _handleGetSymbolTable(params);
115
+ case 'shutdown':
116
+ return await _handleShutdown(params);
117
+ case 'ping':
118
+ return _handlePing(params);
119
+ default:
120
+ throw MethodNotFoundException('Method not found: $method');
121
+ }
122
+ }
123
+
124
+ /// Handle initialize request
125
+ Future<Map<String, dynamic>> _handleInitialize(
126
+ Map<String, dynamic> params) async {
127
+ final projectPath = params['projectPath'] as String?;
128
+ if (projectPath == null) {
129
+ throw InvalidParamsException('projectPath is required');
130
+ }
131
+
132
+ final targetFiles = (params['targetFiles'] as List<dynamic>?)
133
+ ?.map((e) => e as String)
134
+ .toList();
135
+
136
+ final sdkPath = params['sdkPath'] as String?;
137
+
138
+ await _analyzerService.initialize(
139
+ projectPath: projectPath,
140
+ targetFiles: targetFiles,
141
+ sdkPath: sdkPath,
142
+ );
143
+
144
+ return {
145
+ 'success': true,
146
+ 'version': '1.0.0',
147
+ 'capabilities': {
148
+ 'analyze': true,
149
+ 'getSymbolTable': true,
150
+ 'rules': _analyzerService.supportedRules,
151
+ },
152
+ };
153
+ }
154
+
155
+ /// Handle analyze request
156
+ Future<Map<String, dynamic>> _handleAnalyze(
157
+ Map<String, dynamic> params) async {
158
+ final filePath = params['filePath'] as String?;
159
+ if (filePath == null) {
160
+ throw InvalidParamsException('filePath is required');
161
+ }
162
+
163
+ final rulesData = params['rules'] as List<dynamic>?;
164
+
165
+ final violations = await _analyzerService.analyzeFile(
166
+ filePath: filePath,
167
+ rulesData: rulesData,
168
+ );
169
+
170
+ return {
171
+ 'violations': violations.map((v) => v.toJson()).toList(),
172
+ 'fileAnalyzed': filePath,
173
+ };
174
+ }
175
+
176
+ /// Handle getSymbolTable request
177
+ Future<Map<String, dynamic>> _handleGetSymbolTable(
178
+ Map<String, dynamic> params) async {
179
+ final filePath = params['filePath'] as String?;
180
+ if (filePath == null) {
181
+ throw InvalidParamsException('filePath is required');
182
+ }
183
+
184
+ final symbolTable = await _analyzerService.getSymbolTable(filePath);
185
+
186
+ if (symbolTable == null) {
187
+ return {'error': 'Failed to parse file', 'filePath': filePath};
188
+ }
189
+
190
+ return symbolTable.toJson();
191
+ }
192
+
193
+ /// Handle shutdown request
194
+ Future<Map<String, dynamic>> _handleShutdown(
195
+ Map<String, dynamic> params) async {
196
+ _log('Shutdown requested');
197
+
198
+ // Schedule shutdown after response is sent
199
+ Future.delayed(const Duration(milliseconds: 100), () {
200
+ stop();
201
+ exit(0);
202
+ });
203
+
204
+ return {'success': true};
205
+ }
206
+
207
+ /// Handle ping request (for health checks)
208
+ Map<String, dynamic> _handlePing(Map<String, dynamic> params) {
209
+ return {
210
+ 'pong': true,
211
+ 'timestamp': DateTime.now().toIso8601String(),
212
+ };
213
+ }
214
+
215
+ /// Create a success response
216
+ Map<String, dynamic> _createSuccessResponse(dynamic id, dynamic result) {
217
+ return {
218
+ 'jsonrpc': '2.0',
219
+ 'id': id,
220
+ 'result': result,
221
+ };
222
+ }
223
+
224
+ /// Create an error response
225
+ Map<String, dynamic> _createErrorResponse(
226
+ dynamic id, int code, String message) {
227
+ return {
228
+ 'jsonrpc': '2.0',
229
+ 'id': id,
230
+ 'error': {
231
+ 'code': code,
232
+ 'message': message,
233
+ },
234
+ };
235
+ }
236
+
237
+ /// Send a response to stdout
238
+ void _sendResponse(Map<String, dynamic> response) {
239
+ final json = jsonEncode(response);
240
+ stdout.writeln(json);
241
+ }
242
+
243
+ /// Handle stream errors
244
+ void _handleError(dynamic error) {
245
+ _logError('Stream error: $error');
246
+ }
247
+
248
+ /// Handle stream done
249
+ void _handleDone() {
250
+ _log('Input stream closed');
251
+ _running = false;
252
+ }
253
+
254
+ /// Log to stderr (debug output)
255
+ void _log(String message) {
256
+ stderr.writeln('[SunLint Dart] $message');
257
+ }
258
+
259
+ /// Log error to stderr
260
+ void _logError(String message) {
261
+ stderr.writeln('[SunLint Dart ERROR] $message');
262
+ }
263
+ }
264
+
265
+ /// Exception for method not found errors
266
+ class MethodNotFoundException implements Exception {
267
+ final String message;
268
+ MethodNotFoundException(this.message);
269
+ }
270
+
271
+ /// Exception for invalid params errors
272
+ class InvalidParamsException implements Exception {
273
+ final String message;
274
+ InvalidParamsException(this.message);
275
+ }
@@ -0,0 +1,67 @@
1
+ /// Represents a lint rule configuration
2
+ class Rule {
3
+ /// Rule ID (e.g., 'C001', 'N001', 'S003')
4
+ final String id;
5
+
6
+ /// Rule name
7
+ final String name;
8
+
9
+ /// Rule description
10
+ final String description;
11
+
12
+ /// Severity level
13
+ final String severity;
14
+
15
+ /// Rule category
16
+ final String category;
17
+
18
+ /// Whether the rule is enabled
19
+ final bool enabled;
20
+
21
+ /// Rule-specific configuration
22
+ final Map<String, dynamic> config;
23
+
24
+ Rule({
25
+ required this.id,
26
+ this.name = '',
27
+ this.description = '',
28
+ this.severity = 'warning',
29
+ this.category = 'common',
30
+ this.enabled = true,
31
+ this.config = const {},
32
+ });
33
+
34
+ /// Get a config value with default
35
+ T getConfig<T>(String key, T defaultValue) {
36
+ final value = config[key];
37
+ if (value == null) return defaultValue;
38
+ if (value is T) return value;
39
+ return defaultValue;
40
+ }
41
+
42
+ /// Get threshold value (common for complexity rules)
43
+ int get threshold => getConfig('threshold', 10);
44
+
45
+ /// Get max depth (common for nesting rules)
46
+ int get maxDepth => getConfig('maxDepth', 4);
47
+
48
+ factory Rule.fromJson(Map<String, dynamic> json) => Rule(
49
+ id: json['id'] as String,
50
+ name: json['name'] as String? ?? '',
51
+ description: json['description'] as String? ?? '',
52
+ severity: json['severity'] as String? ?? 'warning',
53
+ category: json['category'] as String? ?? 'common',
54
+ enabled: json['enabled'] as bool? ?? true,
55
+ config: json['config'] as Map<String, dynamic>? ?? {},
56
+ );
57
+
58
+ Map<String, dynamic> toJson() => {
59
+ 'id': id,
60
+ 'name': name,
61
+ 'description': description,
62
+ 'severity': severity,
63
+ 'category': category,
64
+ 'enabled': enabled,
65
+ if (config.isNotEmpty) 'config': config,
66
+ };
67
+ }