@sun-asterisk/sunlint 1.2.1 → 1.2.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.
- package/config/rule-analysis-strategies.js +18 -2
- package/engines/eslint-engine.js +9 -11
- package/engines/heuristic-engine.js +55 -31
- package/package.json +2 -1
- package/rules/README.md +252 -0
- package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
- package/rules/common/C002_no_duplicate_code/config.json +23 -0
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
- package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
- package/rules/common/C006_function_naming/analyzer.js +504 -0
- package/rules/common/C006_function_naming/config.json +86 -0
- package/rules/common/C006_function_naming/smart-analyzer.js +503 -0
- package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
- package/rules/common/C012_command_query_separation/analyzer.js +481 -0
- package/rules/common/C012_command_query_separation/ast-analyzer.js +495 -0
- package/rules/common/C013_no_dead_code/analyzer.js +206 -0
- package/rules/common/C014_dependency_injection/analyzer.js +338 -0
- package/rules/common/C017_constructor_logic/analyzer.js +314 -0
- package/rules/common/C019_log_level_usage/analyzer.js +362 -0
- package/rules/common/C019_log_level_usage/config.json +121 -0
- package/rules/common/C029_catch_block_logging/analyzer-backup.js +426 -0
- package/rules/common/C029_catch_block_logging/analyzer-fixed.js +130 -0
- package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +487 -0
- package/rules/common/C029_catch_block_logging/analyzer-simple.js +110 -0
- package/rules/common/C029_catch_block_logging/analyzer-smart-pipeline.js +755 -0
- package/rules/common/C029_catch_block_logging/analyzer.js +129 -0
- package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +441 -0
- package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +127 -0
- package/rules/common/C029_catch_block_logging/ast-analyzer.js +133 -0
- package/rules/common/C029_catch_block_logging/cfg-analyzer.js +408 -0
- package/rules/common/C029_catch_block_logging/config.json +59 -0
- package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +454 -0
- package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +700 -0
- package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +568 -0
- package/rules/common/C029_catch_block_logging/semantic-analyzer.js +459 -0
- package/rules/common/C031_validation_separation/analyzer.js +186 -0
- package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
- package/rules/common/C041_no_sensitive_hardcode/ast-analyzer.js +296 -0
- package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
- package/rules/common/C043_no_console_or_print/analyzer.js +431 -0
- package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +590 -0
- package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
- package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
- package/rules/docs/C002_no_duplicate_code.md +57 -0
- package/rules/docs/C031_validation_separation.md +72 -0
- package/rules/index.js +155 -0
- package/rules/migration/converter.js +385 -0
- package/rules/migration/mapping.json +164 -0
- package/rules/parser/constants.js +31 -0
- package/rules/parser/file-config.js +80 -0
- package/rules/parser/rule-parser-simple.js +305 -0
- package/rules/parser/rule-parser.js +527 -0
- package/rules/security/S015_insecure_tls_certificate/analyzer.js +150 -0
- package/rules/security/S015_insecure_tls_certificate/ast-analyzer.js +237 -0
- package/rules/security/S023_no_json_injection/analyzer.js +278 -0
- package/rules/security/S023_no_json_injection/ast-analyzer.js +359 -0
- package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
- package/rules/security/S026_json_schema_validation/config.json +27 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +436 -0
- package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
- package/rules/security/S029_csrf_protection/analyzer.js +330 -0
- package/rules/tests/C002_no_duplicate_code.test.js +50 -0
- package/rules/universal/C010/generic.js +0 -0
- package/rules/universal/C010/tree-sitter-analyzer.js +0 -0
- package/rules/utils/ast-utils.js +191 -0
- package/rules/utils/base-analyzer.js +98 -0
- package/rules/utils/pattern-matchers.js +239 -0
- package/rules/utils/rule-helpers.js +264 -0
- package/rules/utils/severity-constants.js +93 -0
- package/scripts/generate_insights.js +188 -0
- package/scripts/merge-reports.js +0 -424
- package/scripts/test-scripts/README.md +0 -22
- package/scripts/test-scripts/test-c041-comparison.js +0 -114
- package/scripts/test-scripts/test-c041-eslint.js +0 -67
- package/scripts/test-scripts/test-eslint-rules.js +0 -146
- package/scripts/test-scripts/test-real-world.js +0 -44
- package/scripts/test-scripts/test-rules-on-real-projects.js +0 -86
|
@@ -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,155 @@
|
|
|
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
|
+
const rulePath = path.join(__dirname, category, ruleId, 'analyzer.js');
|
|
19
|
+
return require(rulePath);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.warn(`Failed to load rule ${category}/${ruleId}:`, error.message);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Load rule configuration
|
|
28
|
+
* @param {string} category - Rule category
|
|
29
|
+
* @param {string} ruleId - Rule ID
|
|
30
|
+
* @returns {Object} Rule configuration
|
|
31
|
+
*/
|
|
32
|
+
function loadRuleConfig(category, ruleId) {
|
|
33
|
+
try {
|
|
34
|
+
const configPath = path.join(__dirname, category, ruleId, 'config.json');
|
|
35
|
+
return require(configPath);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.warn(`Failed to load config for ${category}/${ruleId}:`, error.message);
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 🔹 Common Rules (C-series) - General coding standards
|
|
43
|
+
const commonRules = {
|
|
44
|
+
C006: loadRule('common', 'C006_function_naming'),
|
|
45
|
+
C012: loadRule('common', 'C012_command_query_separation'),
|
|
46
|
+
C013: loadRule('common', 'C013_no_dead_code'),
|
|
47
|
+
C014: loadRule('common', 'C014_dependency_injection'),
|
|
48
|
+
C019: loadRule('common', 'C019_log_level_usage'),
|
|
49
|
+
C029: loadRule('common', 'C029_catch_block_logging'),
|
|
50
|
+
C031: loadRule('common', 'C031_validation_separation'),
|
|
51
|
+
C041: loadRule('common', 'C041_no_sensitive_hardcode'),
|
|
52
|
+
C042: loadRule('common', 'C042_boolean_name_prefix'),
|
|
53
|
+
C047: loadRule('common', 'C047_no_duplicate_retry_logic'),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// 🔒 Security Rules (S-series) - Ready for migration
|
|
57
|
+
const securityRules = {
|
|
58
|
+
S015: loadRule('security', 'S015_insecure_tls_certificate'),
|
|
59
|
+
S023: loadRule('security', 'S023_no_json_injection'),
|
|
60
|
+
S026: loadRule('security', 'S026_json_schema_validation'),
|
|
61
|
+
S027: loadRule('security', 'S027_no_hardcoded_secrets'),
|
|
62
|
+
S029: loadRule('security', 'S029_csrf_protection'),
|
|
63
|
+
// S001: loadRule('security', 'S001_fail_securely'),
|
|
64
|
+
// S003: loadRule('security', 'S003_no_unvalidated_redirect'),
|
|
65
|
+
// S012: loadRule('security', 'S012_hardcode_secret'),
|
|
66
|
+
// ... 46 more security rules ready for migration
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// 📘 TypeScript Rules (T-series) - Ready for migration
|
|
70
|
+
const typescriptRules = {
|
|
71
|
+
// T002: loadRule('typescript', 'T002_interface_prefix_i'),
|
|
72
|
+
// T003: loadRule('typescript', 'T003_ts_ignore_reason'),
|
|
73
|
+
// T004: loadRule('typescript', 'T004_interface_public_only'),
|
|
74
|
+
// ... 7 more TypeScript rules ready for migration
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get all available rules by category
|
|
79
|
+
* @returns {Object} Organized rules object
|
|
80
|
+
*/
|
|
81
|
+
function getAllRules() {
|
|
82
|
+
return {
|
|
83
|
+
common: commonRules,
|
|
84
|
+
security: securityRules,
|
|
85
|
+
typescript: typescriptRules
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get rule by ID (searches all categories)
|
|
91
|
+
* @param {string} ruleId - Rule ID (e.g., 'C006', 'S001', 'T002')
|
|
92
|
+
* @returns {Object|null} Rule analyzer or null if not found
|
|
93
|
+
*/
|
|
94
|
+
function getRuleById(ruleId) {
|
|
95
|
+
// Check all categories for the rule
|
|
96
|
+
if (commonRules[ruleId]) return commonRules[ruleId];
|
|
97
|
+
if (securityRules[ruleId]) return securityRules[ruleId];
|
|
98
|
+
if (typescriptRules[ruleId]) return typescriptRules[ruleId];
|
|
99
|
+
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get active rule count by category
|
|
105
|
+
* @returns {Object} Rule counts
|
|
106
|
+
*/
|
|
107
|
+
function getRuleCounts() {
|
|
108
|
+
const counts = {
|
|
109
|
+
common: Object.keys(commonRules).filter(id => commonRules[id]).length,
|
|
110
|
+
security: Object.keys(securityRules).filter(id => securityRules[id]).length,
|
|
111
|
+
typescript: Object.keys(typescriptRules).filter(id => typescriptRules[id]).length,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
counts.total = counts.common + counts.security + counts.typescript;
|
|
115
|
+
return counts;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* List all available rules with metadata
|
|
120
|
+
* @returns {Array} Array of rule information
|
|
121
|
+
*/
|
|
122
|
+
function listRules() {
|
|
123
|
+
const rules = [];
|
|
124
|
+
const allRules = getAllRules();
|
|
125
|
+
|
|
126
|
+
for (const category in allRules) {
|
|
127
|
+
for (const ruleId in allRules[category]) {
|
|
128
|
+
if (allRules[category][ruleId]) {
|
|
129
|
+
const config = loadRuleConfig(category, ruleId);
|
|
130
|
+
rules.push({
|
|
131
|
+
id: ruleId,
|
|
132
|
+
category,
|
|
133
|
+
name: config.name || ruleId,
|
|
134
|
+
description: config.description || 'No description',
|
|
135
|
+
status: 'active'
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return rules;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
// Main exports
|
|
146
|
+
getAllRules,
|
|
147
|
+
getRuleById,
|
|
148
|
+
getRuleCounts,
|
|
149
|
+
listRules,
|
|
150
|
+
|
|
151
|
+
// Category exports
|
|
152
|
+
common: commonRules,
|
|
153
|
+
security: securityRules,
|
|
154
|
+
typescript: typescriptRules
|
|
155
|
+
};
|