@sun-asterisk/sunlint 1.3.0 → 1.3.2

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 (124) hide show
  1. package/CHANGELOG.md +115 -1
  2. package/CONTRIBUTING.md +249 -605
  3. package/README.md +3 -4
  4. package/config/ci-cd.json +54 -0
  5. package/config/development.json +56 -0
  6. package/config/large-project.json +143 -0
  7. package/config/presets/all.json +0 -1
  8. package/config/release.json +70 -0
  9. package/config/rule-analysis-strategies.js +38 -3
  10. package/config/rules/enhanced-rules-registry.json +474 -1179
  11. package/config/rules/rules-registry-generated.json +3 -3
  12. package/core/cli-action-handler.js +24 -30
  13. package/core/cli-program.js +11 -3
  14. package/core/config-merger.js +29 -2
  15. package/core/enhanced-rules-registry.js +3 -2
  16. package/core/semantic-engine.js +129 -19
  17. package/core/semantic-rule-base.js +4 -2
  18. package/core/unified-rule-registry.js +1 -1
  19. package/docs/COMMAND-EXAMPLES.md +134 -0
  20. package/docs/LARGE-PROJECT-GUIDE.md +324 -0
  21. package/engines/heuristic-engine.js +135 -16
  22. package/integrations/eslint/plugin/index.js +0 -2
  23. package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
  24. package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
  25. package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
  26. package/origin-rules/common-en.md +19 -15
  27. package/package.json +1 -1
  28. package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
  29. package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
  30. package/rules/common/C006_function_naming/analyzer.js +29 -3
  31. package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
  32. package/rules/common/C010_limit_block_nesting/config.json +64 -0
  33. package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
  34. package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
  35. package/rules/common/C013_no_dead_code/analyzer.js +75 -177
  36. package/rules/common/C013_no_dead_code/config.json +61 -0
  37. package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
  38. package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
  39. package/rules/common/C014_dependency_injection/analyzer.js +48 -313
  40. package/rules/common/C014_dependency_injection/config.json +26 -0
  41. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
  42. package/rules/common/C017_constructor_logic/analyzer.js +254 -17
  43. package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
  44. package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
  45. package/rules/common/C018_no_throw_generic_error/config.json +50 -0
  46. package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
  47. package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
  48. package/rules/common/C019_log_level_usage/analyzer.js +110 -317
  49. package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
  50. package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
  51. package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
  52. package/rules/common/C023_no_duplicate_variable/config.json +50 -0
  53. package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
  54. package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
  55. package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
  56. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
  57. package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
  58. package/rules/common/C033_separate_service_repository/README.md +78 -0
  59. package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
  60. package/rules/common/C033_separate_service_repository/config.json +50 -0
  61. package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
  62. package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
  63. package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
  64. package/rules/common/C035_error_logging_context/analyzer.js +232 -0
  65. package/rules/common/C035_error_logging_context/config.json +54 -0
  66. package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
  67. package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
  68. package/rules/common/C040_centralized_validation/analyzer.js +165 -0
  69. package/rules/common/C040_centralized_validation/config.json +46 -0
  70. package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
  71. package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
  72. package/rules/common/{C076_single_test_behavior → C072_single_test_behavior}/analyzer.js +6 -6
  73. package/rules/common/C076_explicit_function_types/README.md +30 -0
  74. package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
  75. package/rules/common/C076_explicit_function_types/config.json +15 -0
  76. package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
  77. package/rules/index.js +6 -1
  78. package/rules/parser/rule-parser.js +13 -2
  79. package/rules/security/S005_no_origin_auth/README.md +226 -0
  80. package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
  81. package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
  82. package/rules/security/S005_no_origin_auth/config.json +85 -0
  83. package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
  84. package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
  85. package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
  86. package/rules/security/S007_no_plaintext_otp/README.md +198 -0
  87. package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
  88. package/rules/security/S007_no_plaintext_otp/config.json +79 -0
  89. package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
  90. package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
  91. package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
  92. package/rules/security/S009_no_insecure_encryption/README.md +158 -0
  93. package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
  94. package/rules/security/S009_no_insecure_encryption/config.json +55 -0
  95. package/rules/security/S010_no_insecure_encryption/README.md +224 -0
  96. package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
  97. package/rules/security/S010_no_insecure_encryption/config.json +48 -0
  98. package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
  99. package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
  100. package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
  101. package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
  102. package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
  103. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
  104. package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
  105. package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
  106. package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
  107. package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
  108. package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
  109. package/rules/security/S055_content_type_validation/README.md +176 -0
  110. package/rules/security/S055_content_type_validation/analyzer.js +312 -0
  111. package/rules/security/S055_content_type_validation/config.json +48 -0
  112. package/rules/utils/rule-helpers.js +140 -1
  113. package/scripts/consolidate-config.js +116 -0
  114. package/scripts/prepare-release.sh +1 -1
  115. package/config/rules/rules-registry.json +0 -765
  116. package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
  117. package/docs/FUTURE_PACKAGES.md +0 -83
  118. package/docs/HEURISTIC_VS_AI.md +0 -113
  119. package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
  120. package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
  121. package/docs/RELEASE_GUIDE.md +0 -230
  122. package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
  123. package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
  124. package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
@@ -1,254 +0,0 @@
1
- /**
2
- * Custom ESLint rule for: C076 – Each test should assert only one behavior
3
- * Rule ID: custom/c076
4
- * Purpose: Ensure test functions focus on testing a single behavior/aspect
5
- * Note: Similar to C072 but with broader scope and different focus
6
- */
7
-
8
- module.exports = {
9
- meta: {
10
- type: "suggestion",
11
- docs: {
12
- description: "Each test should assert only one behavior to maintain focus and clarity",
13
- recommended: true,
14
- category: "Testing"
15
- },
16
- schema: [
17
- {
18
- type: "object",
19
- properties: {
20
- maxAssertions: {
21
- type: "integer",
22
- minimum: 1,
23
- default: 1
24
- },
25
- allowSetupAssertions: {
26
- type: "boolean",
27
- default: true
28
- },
29
- assertionPatterns: {
30
- type: "array",
31
- items: {
32
- type: "string"
33
- },
34
- default: ["expect", "assert", "should"]
35
- }
36
- },
37
- additionalProperties: false
38
- }
39
- ],
40
- messages: {
41
- tooManyAssertions: "Test '{{testName}}' has {{count}} assertions. Each test should focus on one behavior (max {{max}} assertions)",
42
- multipleBehaviors: "Test '{{testName}}' appears to test multiple behaviors. Consider splitting into separate test cases"
43
- }
44
- },
45
- create(context) {
46
- const options = context.options[0] || {};
47
- const maxAssertions = options.maxAssertions || 1;
48
- const allowSetupAssertions = options.allowSetupAssertions !== false;
49
- const assertionPatterns = options.assertionPatterns || ["expect", "assert", "should"];
50
-
51
- function isTestFunction(node) {
52
- if (!node || !node.callee) return false;
53
-
54
- if (node.type === "CallExpression") {
55
- // Direct test/it/describe calls
56
- if (node.callee.type === "Identifier") {
57
- return ["test", "it", "describe", "context"].includes(node.callee.name);
58
- }
59
-
60
- // Method calls like jest.test, mocha.it, etc.
61
- if (node.callee.type === "MemberExpression" &&
62
- node.callee.property &&
63
- ["test", "it", "describe", "context"].includes(node.callee.property.name)) {
64
- return true;
65
- }
66
- }
67
- return false;
68
- }
69
-
70
- function isSetupFunction(node) {
71
- if (!node || !node.callee) return false;
72
-
73
- const setupFunctions = ["beforeEach", "afterEach", "beforeAll", "afterAll", "before", "after"];
74
-
75
- if (node.type === "CallExpression") {
76
- if (node.callee.type === "Identifier") {
77
- return setupFunctions.includes(node.callee.name);
78
- }
79
-
80
- if (node.callee.type === "MemberExpression" &&
81
- node.callee.property &&
82
- setupFunctions.includes(node.callee.property.name)) {
83
- return true;
84
- }
85
- }
86
- return false;
87
- }
88
-
89
- function isAssertionCall(node) {
90
- if (!node || node.type !== "CallExpression" || !node.callee) {
91
- return false;
92
- }
93
-
94
- // Direct assertion calls
95
- if (node.callee.type === "Identifier") {
96
- return assertionPatterns.includes(node.callee.name);
97
- }
98
-
99
- // Method calls like chai.expect, jest.expect, etc.
100
- if (node.callee.type === "MemberExpression" && node.callee.property) {
101
- return assertionPatterns.includes(node.callee.property.name);
102
- }
103
-
104
- return false;
105
- }
106
-
107
- function countAssertions(testBody, testName) {
108
- let assertionCount = 0;
109
- let setupAssertionCount = 0;
110
- let hasMultipleBehaviorIndicators = false;
111
-
112
- function traverse(node) {
113
- if (!node || typeof node !== 'object') return;
114
-
115
- // Count assertions
116
- if (isAssertionCall(node)) {
117
- // Check if this assertion is in setup/teardown
118
- let parent = node;
119
- let inSetup = false;
120
- while (parent && parent.parent) {
121
- parent = parent.parent;
122
- if (parent.type === "CallExpression" && isSetupFunction(parent)) {
123
- inSetup = true;
124
- break;
125
- }
126
- }
127
-
128
- if (inSetup) {
129
- setupAssertionCount++;
130
- } else {
131
- assertionCount++;
132
- }
133
- }
134
-
135
- // Look for multiple behavior indicators
136
- if (node.type === "CallExpression") {
137
- // Multiple nested test functions indicate multiple behaviors
138
- if (isTestFunction(node) && node !== testBody.parent) {
139
- hasMultipleBehaviorIndicators = true;
140
- }
141
- }
142
-
143
- // Look for comment patterns that suggest multiple behaviors
144
- if (node.type === "ExpressionStatement" && node.leadingComments) {
145
- const comments = node.leadingComments.map(c => c.value.toLowerCase());
146
- const behaviorKeywords = ["and", "also", "then", "next", "additionally", "furthermore"];
147
- if (comments.some(comment =>
148
- behaviorKeywords.some(keyword => comment.includes(keyword)))) {
149
- hasMultipleBehaviorIndicators = true;
150
- }
151
- }
152
-
153
- // Recursively check child nodes, but don't go into nested test functions
154
- for (const key in node) {
155
- if (key === 'parent' || key === 'range' || key === 'loc') continue;
156
-
157
- const child = node[key];
158
- if (Array.isArray(child)) {
159
- child.forEach(item => {
160
- if (item && typeof item === 'object' && item.type) {
161
- if (!(item.type === "CallExpression" && isTestFunction(item))) {
162
- traverse(item);
163
- }
164
- }
165
- });
166
- } else if (child && typeof child === 'object' && child.type) {
167
- if (!(child.type === "CallExpression" && isTestFunction(child))) {
168
- traverse(child);
169
- }
170
- }
171
- }
172
- }
173
-
174
- traverse(testBody);
175
-
176
- return {
177
- assertions: assertionCount,
178
- setupAssertions: setupAssertionCount,
179
- hasMultipleBehaviors: hasMultipleBehaviorIndicators
180
- };
181
- }
182
-
183
- return {
184
- CallExpression(node) {
185
- // Only check test function calls, not describe blocks
186
- if (!isTestFunction(node) || (node.callee.type === "Identifier" && node.callee.name === "describe")) {
187
- return;
188
- }
189
-
190
- // Skip describe blocks
191
- if (node.callee.type === "Identifier" && ["describe", "context"].includes(node.callee.name)) {
192
- return;
193
- }
194
-
195
- // Must have test name and callback
196
- if (!node.arguments || node.arguments.length < 2) return;
197
-
198
- const testName = node.arguments[0];
199
- const testCallback = node.arguments[1];
200
-
201
- if (!testCallback ||
202
- (testCallback.type !== "FunctionExpression" &&
203
- testCallback.type !== "ArrowFunctionExpression")) {
204
- return;
205
- }
206
-
207
- const testNameStr = testName.type === "Literal" ? testName.value :
208
- testName.type === "TemplateLiteral" ? "template" : "unnamed";
209
-
210
- // Get function body
211
- const fnBody = testCallback.body;
212
- if (!fnBody) return;
213
-
214
- // Handle both block statements and expression bodies
215
- let bodyToCheck = fnBody;
216
- if (testCallback.type === "ArrowFunctionExpression" && fnBody.type !== "BlockStatement") {
217
- bodyToCheck = { type: "BlockStatement", body: [{ type: "ExpressionStatement", expression: fnBody }] };
218
- }
219
-
220
- // Count assertions and analyze behavior
221
- const analysis = countAssertions(bodyToCheck, testNameStr);
222
-
223
- // Calculate effective assertions (exclude setup if allowed)
224
- const effectiveAssertions = allowSetupAssertions ?
225
- analysis.assertions :
226
- analysis.assertions + analysis.setupAssertions;
227
-
228
- // Report if too many assertions
229
- if (effectiveAssertions > maxAssertions) {
230
- context.report({
231
- node,
232
- messageId: "tooManyAssertions",
233
- data: {
234
- testName: testNameStr,
235
- count: effectiveAssertions,
236
- max: maxAssertions
237
- }
238
- });
239
- }
240
-
241
- // Report if multiple behaviors detected
242
- if (analysis.hasMultipleBehaviors) {
243
- context.report({
244
- node,
245
- messageId: "multipleBehaviors",
246
- data: {
247
- testName: testNameStr
248
- }
249
- });
250
- }
251
- }
252
- };
253
- }
254
- };