@sun-asterisk/sunlint 1.2.1 → 1.3.0

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 (109) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/CONTRIBUTING.md +533 -70
  3. package/README.md +16 -2
  4. package/config/engines/engines-enhanced.json +86 -0
  5. package/config/engines/semantic-config.json +114 -0
  6. package/config/eslint-rule-mapping.json +50 -38
  7. package/config/rule-analysis-strategies.js +18 -2
  8. package/config/rules/enhanced-rules-registry.json +2503 -0
  9. package/config/rules/rules-registry-generated.json +785 -837
  10. package/core/adapters/sunlint-rule-adapter.js +25 -30
  11. package/core/analysis-orchestrator.js +42 -2
  12. package/core/categories.js +52 -0
  13. package/core/category-constants.js +39 -0
  14. package/core/cli-action-handler.js +32 -5
  15. package/core/config-manager.js +111 -0
  16. package/core/config-merger.js +61 -0
  17. package/core/constants/categories.js +168 -0
  18. package/core/constants/defaults.js +165 -0
  19. package/core/constants/engines.js +185 -0
  20. package/core/constants/index.js +30 -0
  21. package/core/constants/rules.js +215 -0
  22. package/core/file-targeting-service.js +128 -7
  23. package/core/interfaces/rule-plugin.interface.js +207 -0
  24. package/core/plugin-manager.js +448 -0
  25. package/core/rule-selection-service.js +42 -15
  26. package/core/semantic-engine.js +560 -0
  27. package/core/semantic-rule-base.js +433 -0
  28. package/core/unified-rule-registry.js +484 -0
  29. package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
  30. package/engines/core/base-engine.js +249 -0
  31. package/engines/engine-factory.js +275 -0
  32. package/engines/eslint-engine.js +180 -30
  33. package/engines/heuristic-engine.js +513 -56
  34. package/integrations/eslint/plugin/index.js +27 -27
  35. package/package.json +11 -6
  36. package/rules/README.md +252 -0
  37. package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
  38. package/rules/common/C002_no_duplicate_code/config.json +23 -0
  39. package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
  40. package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
  41. package/rules/common/C006_function_naming/analyzer.js +504 -0
  42. package/rules/common/C006_function_naming/config.json +86 -0
  43. package/rules/common/C006_function_naming/smart-analyzer.js +503 -0
  44. package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
  45. package/rules/common/C012_command_query_separation/analyzer.js +481 -0
  46. package/rules/common/C012_command_query_separation/ast-analyzer.js +495 -0
  47. package/rules/common/C013_no_dead_code/analyzer.js +206 -0
  48. package/rules/common/C014_dependency_injection/analyzer.js +338 -0
  49. package/rules/common/C017_constructor_logic/analyzer.js +314 -0
  50. package/rules/common/C019_log_level_usage/analyzer.js +362 -0
  51. package/rules/common/C019_log_level_usage/config.json +121 -0
  52. package/rules/common/C029_catch_block_logging/analyzer-smart-pipeline.js +755 -0
  53. package/rules/common/C029_catch_block_logging/analyzer.js +141 -0
  54. package/rules/common/C029_catch_block_logging/config.json +59 -0
  55. package/rules/common/C031_validation_separation/analyzer.js +186 -0
  56. package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
  57. package/rules/common/C041_no_sensitive_hardcode/ast-analyzer.js +296 -0
  58. package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
  59. package/rules/common/C043_no_console_or_print/analyzer.js +431 -0
  60. package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +590 -0
  61. package/rules/common/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
  62. package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
  63. package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -0
  64. package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
  65. package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
  66. package/rules/docs/C002_no_duplicate_code.md +57 -0
  67. package/rules/docs/C031_validation_separation.md +72 -0
  68. package/rules/index.js +162 -0
  69. package/rules/migration/converter.js +385 -0
  70. package/rules/migration/mapping.json +164 -0
  71. package/rules/parser/constants.js +31 -0
  72. package/rules/parser/file-config.js +80 -0
  73. package/rules/parser/rule-parser-simple.js +305 -0
  74. package/rules/parser/rule-parser.js +527 -0
  75. package/rules/security/S015_insecure_tls_certificate/analyzer.js +150 -0
  76. package/rules/security/S015_insecure_tls_certificate/ast-analyzer.js +237 -0
  77. package/rules/security/S023_no_json_injection/analyzer.js +278 -0
  78. package/rules/security/S023_no_json_injection/ast-analyzer.js +359 -0
  79. package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
  80. package/rules/security/S026_json_schema_validation/config.json +27 -0
  81. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +436 -0
  82. package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
  83. package/rules/security/S029_csrf_protection/analyzer.js +330 -0
  84. package/rules/tests/C002_no_duplicate_code.test.js +50 -0
  85. package/rules/utils/ast-utils.js +191 -0
  86. package/rules/utils/base-analyzer.js +98 -0
  87. package/rules/utils/pattern-matchers.js +239 -0
  88. package/rules/utils/rule-helpers.js +264 -0
  89. package/rules/utils/severity-constants.js +93 -0
  90. package/scripts/category-manager.js +150 -0
  91. package/scripts/generate-rules-registry.js +88 -0
  92. package/scripts/generate_insights.js +188 -0
  93. package/scripts/migrate-rule-registry.js +157 -0
  94. package/scripts/validate-system.js +48 -0
  95. package/.sunlint.json +0 -35
  96. package/config/README.md +0 -88
  97. package/config/engines/eslint-rule-mapping.json +0 -74
  98. package/config/testing/test-s005-working.ts +0 -22
  99. package/engines/tree-sitter-parser.js +0 -0
  100. package/engines/universal-ast-engine.js +0 -0
  101. package/scripts/merge-reports.js +0 -424
  102. package/scripts/test-scripts/README.md +0 -22
  103. package/scripts/test-scripts/test-c041-comparison.js +0 -114
  104. package/scripts/test-scripts/test-c041-eslint.js +0 -67
  105. package/scripts/test-scripts/test-eslint-rules.js +0 -146
  106. package/scripts/test-scripts/test-real-world.js +0 -44
  107. package/scripts/test-scripts/test-rules-on-real-projects.js +0 -86
  108. /package/{config/schemas/sunlint-schema.json → rules/universal/C010/generic.js} +0 -0
  109. /package/{core/multi-rule-runner.js → rules/universal/C010/tree-sitter-analyzer.js} +0 -0
@@ -0,0 +1,71 @@
1
+ {
2
+ "knownRetryFunctions": [
3
+ "axios.get",
4
+ "axios.post",
5
+ "axios.put",
6
+ "axios.delete",
7
+ "axios.patch",
8
+ "axios.request",
9
+ "axios.head",
10
+ "axios.options",
11
+
12
+ "useQuery",
13
+ "useMutation",
14
+ "useInfiniteQuery",
15
+ "queryClient.fetchQuery",
16
+ "queryClient.prefetchQuery",
17
+
18
+ "apolloClient.query",
19
+ "apolloClient.mutate",
20
+ "apolloClient.watchQuery",
21
+ "useLazyQuery",
22
+
23
+ "apiService.get",
24
+ "apiService.post",
25
+ "apiService.put",
26
+ "apiService.delete",
27
+ "apiService.patch",
28
+ "httpClient.get",
29
+ "httpClient.post",
30
+ "httpClient.request",
31
+
32
+ "retryAsync",
33
+ "withRetry",
34
+ "retry",
35
+ "p-retry",
36
+ "exponentialBackoff",
37
+ "retryPromise",
38
+
39
+ "fetch",
40
+ "fetch-retry",
41
+ "node-fetch",
42
+ "got",
43
+ "superagent",
44
+ "request-promise"
45
+ ],
46
+
47
+ "layerPatterns": {
48
+ "ui": ["component", "view", "page", "modal", "form", "screen", "widget", "/ui/", "/components/"],
49
+ "usecase": ["usecase", "use-case", "usecases", "service", "business", "/usecases/", "/services/"],
50
+ "repository": ["repository", "repo", "dao", "store", "persistence", "/repositories/", "/data/"],
51
+ "api": ["api", "client", "adapter", "gateway", "connector", "/api/", "/clients/", "/gateways/"]
52
+ },
53
+
54
+ "retryDetectionPatterns": {
55
+ "exceptionRetry": {
56
+ "description": "Detect retry logic in try-catch blocks",
57
+ "enabled": true
58
+ },
59
+ "emptyDataRetry": {
60
+ "description": "Detect retry logic when data is empty/null",
61
+ "enabled": true
62
+ },
63
+ "knownRetryConflict": {
64
+ "description": "Detect manual retry conflicting with built-in retry",
65
+ "enabled": true
66
+ }
67
+ },
68
+
69
+ "_description": "Configuration for Symbol-Based Analysis of retry functions using ts-morph",
70
+ "_usage": "Add functions that have built-in retry mechanisms to avoid false positives"
71
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * C075 Rule: Functions must have explicit return type declarations
3
+ * Ensures type safety by requiring explicit return type annotations
4
+ * Severity: warning
5
+ * Category: Quality
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ class C075ExplicitReturnTypesAnalyzer {
12
+ constructor() {
13
+ this.ruleId = 'C075';
14
+ this.ruleName = 'Explicit Function Return Types';
15
+ this.description = 'Functions must have explicit return type declarations';
16
+ this.severity = 'warning';
17
+ }
18
+
19
+ async analyze(files, language, config) {
20
+ const violations = [];
21
+
22
+ for (const filePath of files) {
23
+ try {
24
+ const fileContent = fs.readFileSync(filePath, 'utf8');
25
+ const fileViolations = await this.analyzeFile(filePath, fileContent, language, config);
26
+ violations.push(...fileViolations);
27
+ } catch (error) {
28
+ console.warn(`C075 analysis error for ${filePath}:`, error.message);
29
+ }
30
+ }
31
+
32
+ return violations;
33
+ }
34
+
35
+ async analyzeFile(filePath, fileContent, language, config) {
36
+ const violations = [];
37
+
38
+ try {
39
+ // Skip non-TypeScript files
40
+ if (!this.isTypeScriptFile(filePath)) {
41
+ return violations;
42
+ }
43
+
44
+ // Simple regex-based analysis for now
45
+ const lines = fileContent.split('\n');
46
+
47
+ for (let i = 0; i < lines.length; i++) {
48
+ const line = lines[i];
49
+ const lineNumber = i + 1;
50
+
51
+ // Look for function declarations without return types
52
+ const functionPatterns = [
53
+ /^(\s*)(function\s+\w+\s*\([^)]*\))\s*\{/, // function name() {
54
+ /^(\s*)(export\s+function\s+\w+\s*\([^)]*\))\s*\{/, // export function name() {
55
+ /^(\s*)(\w+\s*=\s*function\s*\([^)]*\))\s*\{/, // name = function() {
56
+ /^(\s*)(\w+\s*=\s*\([^)]*\)\s*=>\s*)\{/, // name = () => {
57
+ /^(\s*)(\w+\([^)]*\))\s*\{/, // method() {
58
+ ];
59
+
60
+ for (const pattern of functionPatterns) {
61
+ const match = line.match(pattern);
62
+ if (match) {
63
+ const fullMatch = match[2];
64
+
65
+ // Skip if already has return type annotation
66
+ if (fullMatch.includes('):') || line.includes('):')) {
67
+ continue;
68
+ }
69
+
70
+ // Skip constructors
71
+ if (fullMatch.includes('constructor')) {
72
+ continue;
73
+ }
74
+
75
+ violations.push({
76
+ ruleId: this.ruleId,
77
+ severity: this.severity,
78
+ message: `Function is missing explicit return type annotation`,
79
+ filePath: filePath,
80
+ line: lineNumber,
81
+ column: match[1].length + 1,
82
+ source: line.trim(),
83
+ suggestion: 'Add explicit return type annotation (: ReturnType)'
84
+ });
85
+ }
86
+ }
87
+ }
88
+
89
+ } catch (error) {
90
+ console.warn(`C075 analysis error for ${filePath}:`, error.message);
91
+ }
92
+
93
+ return violations;
94
+ }
95
+
96
+ isTypeScriptFile(filePath) {
97
+ return /\.(ts|tsx)$/.test(filePath);
98
+ }
99
+ }
100
+
101
+ module.exports = C075ExplicitReturnTypesAnalyzer;
102
+
103
+ module.exports = C075ExplicitReturnTypesAnalyzer;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * C076 Rule: Each test should assert only one behavior
3
+ * Ensures test focus and maintainability by limiting assertions per test
4
+ * Severity: warning
5
+ * Category: Quality
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ class C076SingleTestBehaviorAnalyzer {
12
+ constructor() {
13
+ this.ruleId = 'C076';
14
+ this.ruleName = 'Single Test Behavior';
15
+ this.description = 'Each test should assert only one behavior';
16
+ this.severity = 'warning';
17
+ this.maxAssertions = 1;
18
+ }
19
+
20
+ async analyze(files, language, config) {
21
+ const violations = [];
22
+
23
+ for (const filePath of files) {
24
+ try {
25
+ const fileContent = fs.readFileSync(filePath, 'utf8');
26
+ const fileViolations = await this.analyzeFile(filePath, fileContent, language, config);
27
+ violations.push(...fileViolations);
28
+ } catch (error) {
29
+ console.warn(`C076 analysis error for ${filePath}:`, error.message);
30
+ }
31
+ }
32
+
33
+ return violations;
34
+ }
35
+
36
+ async analyzeFile(filePath, fileContent, language, config) {
37
+ const violations = [];
38
+
39
+ try {
40
+ // Skip non-test files
41
+ if (!this.isTestFile(filePath)) {
42
+ return violations;
43
+ }
44
+
45
+ const lines = fileContent.split('\n');
46
+ let inTestBlock = false;
47
+ let testStartLine = 0;
48
+ let testName = '';
49
+ let braceLevel = 0;
50
+ let expectCount = 0;
51
+
52
+ for (let i = 0; i < lines.length; i++) {
53
+ const line = lines[i];
54
+ const lineNumber = i + 1;
55
+
56
+ // Check for test function start
57
+ const testMatch = line.match(/^\s*(?:it|test)\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*(?:async\s+)?\(.*\)\s*=>\s*\{|^\s*(?:it|test)\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*(?:async\s+)?function/);
58
+ if (testMatch) {
59
+ inTestBlock = true;
60
+ testStartLine = lineNumber;
61
+ testName = testMatch[1] || testMatch[2] || 'unnamed test';
62
+ braceLevel = 1;
63
+ expectCount = 0;
64
+ continue;
65
+ }
66
+
67
+ if (inTestBlock) {
68
+ // Count braces to track test scope
69
+ const openBraces = (line.match(/\{/g) || []).length;
70
+ const closeBraces = (line.match(/\}/g) || []).length;
71
+ braceLevel = braceLevel + openBraces - closeBraces;
72
+
73
+ // Count expect/assert statements
74
+ const expectMatches = line.match(/\b(?:expect|assert|should)\s*\(/g);
75
+ if (expectMatches) {
76
+ expectCount += expectMatches.length;
77
+ }
78
+
79
+ // Check if test block ended
80
+ if (braceLevel <= 0) {
81
+ inTestBlock = false;
82
+
83
+ // Report violation if too many expectations
84
+ if (expectCount > this.maxAssertions) {
85
+ violations.push({
86
+ ruleId: this.ruleId,
87
+ severity: this.severity,
88
+ message: `Test '${testName}' has ${expectCount} assertions. Each test should focus on one behavior (max ${this.maxAssertions} assertions)`,
89
+ filePath: filePath,
90
+ line: testStartLine,
91
+ column: 1,
92
+ source: lines[testStartLine - 1]?.trim() || '',
93
+ suggestion: 'Consider splitting into separate test cases'
94
+ });
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ } catch (error) {
101
+ console.warn(`C076 analysis error for ${filePath}:`, error.message);
102
+ }
103
+
104
+ return violations;
105
+ }
106
+
107
+ isTestFile(filePath) {
108
+ const testPatterns = [
109
+ /\.test\.(js|ts|jsx|tsx)$/,
110
+ /\.spec\.(js|ts|jsx|tsx)$/,
111
+ /\/__tests__\//,
112
+ /\/tests?\//,
113
+ /\.e2e\./,
114
+ /\.integration\./
115
+ ];
116
+
117
+ return testPatterns.some(pattern => pattern.test(filePath));
118
+ }
119
+ }
120
+
121
+ module.exports = C076SingleTestBehaviorAnalyzer;
@@ -0,0 +1,57 @@
1
+ # C002_no_duplicate_code - CODING Rule
2
+
3
+ ## 📋 Overview
4
+
5
+ **Rule ID**: `C002_no_duplicate_code`
6
+ **Category**: coding
7
+ **Severity**: Error
8
+ **Status**: pending
9
+
10
+ ## 🎯 Description
11
+
12
+ TODO: Add rule description after migration from ESLint.
13
+
14
+
15
+ ## 🔄 Migration Info
16
+
17
+ **ESLint Rule**: `c002-no-duplicate-code`
18
+ **Compatibility**: partial
19
+ **Priority**: medium
20
+
21
+
22
+ ## ✅ Valid Code Examples
23
+
24
+ ```javascript
25
+ // TODO: Add valid code examples
26
+ ```
27
+
28
+ ## ❌ Invalid Code Examples
29
+
30
+ ```javascript
31
+ // TODO: Add invalid code examples that should trigger violations
32
+ ```
33
+
34
+ ## ⚙️ Configuration
35
+
36
+ ```json
37
+ {
38
+ "rules": {
39
+ "C002_no_duplicate_code": "error"
40
+ }
41
+ }
42
+ ```
43
+
44
+ ## 🧪 Testing
45
+
46
+ ```bash
47
+ # Run rule-specific tests
48
+ npm test -- c002_no_duplicate_code
49
+
50
+ # Test with SunLint CLI
51
+ sunlint --rules=C002_no_duplicate_code --input=examples/
52
+ ```
53
+
54
+ ---
55
+
56
+ **Migration Status**: pending
57
+ **Last Updated**: 2025-07-21
@@ -0,0 +1,72 @@
1
+ # Rule C031 - Validation Logic Separation
2
+
3
+ ## Description
4
+ Logic kiểm tra dữ liệu (validate) phải nằm riêng biệt khỏi business logic.
5
+
6
+ ## Rationale
7
+ Tách biệt validation logic giúp:
8
+ - Code dễ đọc và maintain
9
+ - Validation có thể reuse
10
+ - Testing dễ dàng hơn
11
+ - Tuân thủ Single Responsibility Principle
12
+
13
+ ## Examples
14
+
15
+ ### ❌ Bad - Validation mixed with business logic
16
+ ```javascript
17
+ function processOrder(order) {
18
+ // Validation mixed with business logic
19
+ if (!order.customerId) {
20
+ throw new Error('Customer ID is required');
21
+ }
22
+ if (!order.items || order.items.length === 0) {
23
+ throw new Error('Order must have items');
24
+ }
25
+ if (order.total < 0) {
26
+ throw new Error('Total cannot be negative');
27
+ }
28
+
29
+ // Business logic
30
+ const discount = calculateDiscount(order);
31
+ const tax = calculateTax(order);
32
+ return processPayment(order, discount, tax);
33
+ }
34
+ ```
35
+
36
+ ### ✅ Good - Separate validation
37
+ ```javascript
38
+ function validateOrder(order) {
39
+ if (!order.customerId) {
40
+ throw new Error('Customer ID is required');
41
+ }
42
+ if (!order.items || order.items.length === 0) {
43
+ throw new Error('Order must have items');
44
+ }
45
+ if (order.total < 0) {
46
+ throw new Error('Total cannot be negative');
47
+ }
48
+ }
49
+
50
+ function processOrder(order) {
51
+ validateOrder(order);
52
+
53
+ // Pure business logic
54
+ const discount = calculateDiscount(order);
55
+ const tax = calculateTax(order);
56
+ return processPayment(order, discount, tax);
57
+ }
58
+ ```
59
+
60
+ ## Configuration
61
+ ```json
62
+ {
63
+ "C031": {
64
+ "enabled": true,
65
+ "severity": "warning",
66
+ "options": {
67
+ "maxValidationStatementsInFunction": 3,
68
+ "requireSeparateValidationFunction": true
69
+ }
70
+ }
71
+ }
72
+ ```
package/rules/index.js ADDED
@@ -0,0 +1,162 @@
1
+ /**
2
+ * SunLint Heuristic Rules Registry
3
+ * Central reconst securityRules = {
4
+ S015: require('./security/S015_insecure_tls_certificate/analyzer'),
5
+ S023: require('./security/S023_no_json_injection/analyzer'),stry for all heuristic rules organized by category
6
+ */
7
+
8
+ const path = require('path');
9
+
10
+ /**
11
+ * Load rule analyzer from category folder
12
+ * @param {string} category - Rule category (common, security, typescript)
13
+ * @param {string} ruleId - Rule ID (e.g., C006_function_naming)
14
+ * @returns {Object} Rule analyzer module
15
+ */
16
+ function loadRule(category, ruleId) {
17
+ try {
18
+ // Special case for C047: Use semantic analyzer by default
19
+ if (ruleId === 'C047_no_duplicate_retry_logic') {
20
+ const semanticPath = path.join(__dirname, category, ruleId, 'c047-semantic-rule.js');
21
+ console.log(`🔬 Loading C047 semantic analyzer: ${semanticPath}`);
22
+ return require(semanticPath);
23
+ }
24
+
25
+ const rulePath = path.join(__dirname, category, ruleId, 'analyzer.js');
26
+ return require(rulePath);
27
+ } catch (error) {
28
+ console.warn(`Failed to load rule ${category}/${ruleId}:`, error.message);
29
+ return null;
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Load rule configuration
35
+ * @param {string} category - Rule category
36
+ * @param {string} ruleId - Rule ID
37
+ * @returns {Object} Rule configuration
38
+ */
39
+ function loadRuleConfig(category, ruleId) {
40
+ try {
41
+ const configPath = path.join(__dirname, category, ruleId, 'config.json');
42
+ return require(configPath);
43
+ } catch (error) {
44
+ console.warn(`Failed to load config for ${category}/${ruleId}:`, error.message);
45
+ return {};
46
+ }
47
+ }
48
+
49
+ // 🔹 Common Rules (C-series) - General coding standards
50
+ const commonRules = {
51
+ C006: loadRule('common', 'C006_function_naming'),
52
+ C012: loadRule('common', 'C012_command_query_separation'),
53
+ C013: loadRule('common', 'C013_no_dead_code'),
54
+ C014: loadRule('common', 'C014_dependency_injection'),
55
+ C019: loadRule('common', 'C019_log_level_usage'),
56
+ C029: loadRule('common', 'C029_catch_block_logging'),
57
+ C031: loadRule('common', 'C031_validation_separation'),
58
+ C041: loadRule('common', 'C041_no_sensitive_hardcode'),
59
+ C042: loadRule('common', 'C042_boolean_name_prefix'),
60
+ C047: loadRule('common', 'C047_no_duplicate_retry_logic'),
61
+ };
62
+
63
+ // 🔒 Security Rules (S-series) - Ready for migration
64
+ const securityRules = {
65
+ S015: loadRule('security', 'S015_insecure_tls_certificate'),
66
+ S023: loadRule('security', 'S023_no_json_injection'),
67
+ S026: loadRule('security', 'S026_json_schema_validation'),
68
+ S027: loadRule('security', 'S027_no_hardcoded_secrets'),
69
+ S029: loadRule('security', 'S029_csrf_protection'),
70
+ // S001: loadRule('security', 'S001_fail_securely'),
71
+ // S003: loadRule('security', 'S003_no_unvalidated_redirect'),
72
+ // S012: loadRule('security', 'S012_hardcode_secret'),
73
+ // ... 46 more security rules ready for migration
74
+ };
75
+
76
+ // 📘 TypeScript Rules (T-series) - Ready for migration
77
+ const typescriptRules = {
78
+ // T002: loadRule('typescript', 'T002_interface_prefix_i'),
79
+ // T003: loadRule('typescript', 'T003_ts_ignore_reason'),
80
+ // T004: loadRule('typescript', 'T004_interface_public_only'),
81
+ // ... 7 more TypeScript rules ready for migration
82
+ };
83
+
84
+ /**
85
+ * Get all available rules by category
86
+ * @returns {Object} Organized rules object
87
+ */
88
+ function getAllRules() {
89
+ return {
90
+ common: commonRules,
91
+ security: securityRules,
92
+ typescript: typescriptRules
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Get rule by ID (searches all categories)
98
+ * @param {string} ruleId - Rule ID (e.g., 'C006', 'S001', 'T002')
99
+ * @returns {Object|null} Rule analyzer or null if not found
100
+ */
101
+ function getRuleById(ruleId) {
102
+ // Check all categories for the rule
103
+ if (commonRules[ruleId]) return commonRules[ruleId];
104
+ if (securityRules[ruleId]) return securityRules[ruleId];
105
+ if (typescriptRules[ruleId]) return typescriptRules[ruleId];
106
+
107
+ return null;
108
+ }
109
+
110
+ /**
111
+ * Get active rule count by category
112
+ * @returns {Object} Rule counts
113
+ */
114
+ function getRuleCounts() {
115
+ const counts = {
116
+ common: Object.keys(commonRules).filter(id => commonRules[id]).length,
117
+ security: Object.keys(securityRules).filter(id => securityRules[id]).length,
118
+ typescript: Object.keys(typescriptRules).filter(id => typescriptRules[id]).length,
119
+ };
120
+
121
+ counts.total = counts.common + counts.security + counts.typescript;
122
+ return counts;
123
+ }
124
+
125
+ /**
126
+ * List all available rules with metadata
127
+ * @returns {Array} Array of rule information
128
+ */
129
+ function listRules() {
130
+ const rules = [];
131
+ const allRules = getAllRules();
132
+
133
+ for (const category in allRules) {
134
+ for (const ruleId in allRules[category]) {
135
+ if (allRules[category][ruleId]) {
136
+ const config = loadRuleConfig(category, ruleId);
137
+ rules.push({
138
+ id: ruleId,
139
+ category,
140
+ name: config.name || ruleId,
141
+ description: config.description || 'No description',
142
+ status: 'active'
143
+ });
144
+ }
145
+ }
146
+ }
147
+
148
+ return rules;
149
+ }
150
+
151
+ module.exports = {
152
+ // Main exports
153
+ getAllRules,
154
+ getRuleById,
155
+ getRuleCounts,
156
+ listRules,
157
+
158
+ // Category exports
159
+ common: commonRules,
160
+ security: securityRules,
161
+ typescript: typescriptRules
162
+ };