@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,305 @@
|
|
|
1
|
+
const { RuleParser } = require("./rule-parser")
|
|
2
|
+
const fs = require("fs")
|
|
3
|
+
const path = require("path")
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Simple Rule Parser - Lightweight wrapper around RuleParser
|
|
7
|
+
* Provides simplified interface for basic rule information extraction
|
|
8
|
+
* Focused on English rules (*-en.md) with filtering capabilities
|
|
9
|
+
*/
|
|
10
|
+
class SimpleRuleParser {
|
|
11
|
+
constructor() {
|
|
12
|
+
// Use existing RuleParser as core engine, but disable migrate mode
|
|
13
|
+
this.coreParser = new RuleParser(false)
|
|
14
|
+
this.rulesCache = new Map() // Cache parsed rules by file
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parse a specific markdown file and extract basic rule information
|
|
19
|
+
* @param {string} filePath - Path to the markdown file
|
|
20
|
+
* @returns {Array} Array of simplified rule objects
|
|
21
|
+
*/
|
|
22
|
+
parseRuleFile(filePath) {
|
|
23
|
+
try {
|
|
24
|
+
// Check cache first
|
|
25
|
+
if (this.rulesCache.has(filePath)) {
|
|
26
|
+
return this.rulesCache.get(filePath)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Use default config for parsing
|
|
30
|
+
const defaultConfig = {
|
|
31
|
+
category: "Common",
|
|
32
|
+
language: "All languages",
|
|
33
|
+
framework: "All"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Parse using core parser
|
|
37
|
+
const fullRules = this.coreParser.parseMarkdownFile(filePath, defaultConfig)
|
|
38
|
+
|
|
39
|
+
// Convert to simplified format
|
|
40
|
+
const simplifiedRules = fullRules.map(rule => this.simplifyRule(rule))
|
|
41
|
+
|
|
42
|
+
// Cache the result
|
|
43
|
+
this.rulesCache.set(filePath, simplifiedRules)
|
|
44
|
+
|
|
45
|
+
return simplifiedRules
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(`Error parsing file ${filePath}:`, error.message)
|
|
48
|
+
return []
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Parse all English rule files from rules directory
|
|
54
|
+
* @param {string} rulesDir - Path to rules directory (default: relative to this script)
|
|
55
|
+
* @returns {Array} Array of all simplified rule objects
|
|
56
|
+
*/
|
|
57
|
+
parseAllRules(rulesDir = null) {
|
|
58
|
+
try {
|
|
59
|
+
if (!rulesDir) {
|
|
60
|
+
rulesDir = path.join(__dirname, "..", "..", "origin-rules")
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!fs.existsSync(rulesDir)) {
|
|
64
|
+
throw new Error(`Rules directory not found: ${rulesDir}`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const allRules = []
|
|
68
|
+
const files = fs.readdirSync(rulesDir)
|
|
69
|
+
|
|
70
|
+
// Find all English rule files (*-en.md)
|
|
71
|
+
const englishRuleFiles = files.filter(file =>
|
|
72
|
+
file.endsWith('-en.md') && file !== 'README-en.md'
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
console.log(`Found ${englishRuleFiles.length} English rule files: ${englishRuleFiles.join(', ')}`)
|
|
76
|
+
|
|
77
|
+
for (const file of englishRuleFiles) {
|
|
78
|
+
const filePath = path.join(rulesDir, file)
|
|
79
|
+
const rules = this.parseRuleFile(filePath)
|
|
80
|
+
allRules.push(...rules)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return allRules
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error("Error parsing all rules:", error.message)
|
|
86
|
+
return []
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Filter rules by various criteria
|
|
92
|
+
* @param {Array} rules - Array of rules to filter
|
|
93
|
+
* @param {Object} filters - Filter criteria
|
|
94
|
+
* @param {string} filters.ruleId - Specific rule ID (e.g., "C001")
|
|
95
|
+
* @param {string|Array} filters.principles - Principle(s) to match (e.g., "Quality", ["Security", "Performance"])
|
|
96
|
+
* @param {string} filters.framework - Framework to match (e.g., "Android")
|
|
97
|
+
* @param {string} filters.language - Language to match (e.g., "Java")
|
|
98
|
+
* @param {string} filters.minVersion - Minimum version (e.g., "1.0")
|
|
99
|
+
* @param {string} filters.status - Status to match (default: "activated")
|
|
100
|
+
* @returns {Array} Filtered rules
|
|
101
|
+
*/
|
|
102
|
+
filterRules(rules, filters = {}) {
|
|
103
|
+
try {
|
|
104
|
+
let filteredRules = [...rules]
|
|
105
|
+
|
|
106
|
+
// Default filters: version >= 1.0 and status = activated
|
|
107
|
+
const {
|
|
108
|
+
ruleId,
|
|
109
|
+
principles,
|
|
110
|
+
framework,
|
|
111
|
+
language,
|
|
112
|
+
minVersion = "1.0",
|
|
113
|
+
status // No default for status when looking for specific ruleId
|
|
114
|
+
} = filters
|
|
115
|
+
|
|
116
|
+
// Filter by specific rule ID first (no status filter needed)
|
|
117
|
+
if (ruleId) {
|
|
118
|
+
filteredRules = filteredRules.filter(rule =>
|
|
119
|
+
rule.id && rule.id.toLowerCase() === ruleId.toLowerCase()
|
|
120
|
+
)
|
|
121
|
+
// If searching by ID, return early (no other filters needed)
|
|
122
|
+
return filteredRules
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Filter by status (default: activated) only when not searching by ID
|
|
126
|
+
const defaultStatus = status || "activated"
|
|
127
|
+
filteredRules = filteredRules.filter(rule =>
|
|
128
|
+
rule.status && rule.status.toLowerCase() === defaultStatus.toLowerCase()
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
// Filter by minimum version
|
|
132
|
+
if (minVersion) {
|
|
133
|
+
filteredRules = filteredRules.filter(rule =>
|
|
134
|
+
this.compareVersions(rule.version || "1.0", minVersion) >= 0
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Filter by principles
|
|
139
|
+
if (principles) {
|
|
140
|
+
const principlesArray = Array.isArray(principles) ? principles : [principles]
|
|
141
|
+
filteredRules = filteredRules.filter(rule => {
|
|
142
|
+
if (!rule.principles || rule.principles.length === 0) return false
|
|
143
|
+
return principlesArray.some(principle =>
|
|
144
|
+
rule.principles.some(rulePrinciple =>
|
|
145
|
+
rulePrinciple.toLowerCase().includes(principle.toLowerCase())
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Filter by framework
|
|
152
|
+
if (framework) {
|
|
153
|
+
filteredRules = filteredRules.filter(rule =>
|
|
154
|
+
rule.framework && rule.framework.toLowerCase().includes(framework.toLowerCase())
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Filter by language
|
|
159
|
+
if (language) {
|
|
160
|
+
filteredRules = filteredRules.filter(rule =>
|
|
161
|
+
rule.language && (
|
|
162
|
+
rule.language.toLowerCase().includes(language.toLowerCase()) ||
|
|
163
|
+
rule.language.toLowerCase().includes('all')
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return filteredRules
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error("Error filtering rules:", error.message)
|
|
171
|
+
return rules
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get rules by principles (common use case)
|
|
177
|
+
* @param {string|Array} principles - Principle(s) to search for
|
|
178
|
+
* @param {string} rulesDir - Rules directory path
|
|
179
|
+
* @returns {Array} Rules matching the principles
|
|
180
|
+
*/
|
|
181
|
+
getRulesByPrinciples(principles, rulesDir = null) {
|
|
182
|
+
const allRules = this.parseAllRules(rulesDir)
|
|
183
|
+
return this.filterRules(allRules, { principles })
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get rules for specific framework
|
|
188
|
+
* @param {string} framework - Framework name (e.g., "Android", "React")
|
|
189
|
+
* @param {string} rulesDir - Rules directory path
|
|
190
|
+
* @returns {Array} Rules for the framework
|
|
191
|
+
*/
|
|
192
|
+
getRulesByFramework(framework, rulesDir = null) {
|
|
193
|
+
const allRules = this.parseAllRules(rulesDir)
|
|
194
|
+
return this.filterRules(allRules, { framework })
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get single rule by ID
|
|
199
|
+
* @param {string} ruleId - Rule ID (e.g., "C001")
|
|
200
|
+
* @param {string} rulesDir - Rules directory path
|
|
201
|
+
* @returns {Object|null} Rule object or null if not found
|
|
202
|
+
*/
|
|
203
|
+
getRuleById(ruleId, rulesDir = null) {
|
|
204
|
+
try {
|
|
205
|
+
const allRules = this.parseAllRules(rulesDir)
|
|
206
|
+
const filtered = this.filterRules(allRules, { ruleId })
|
|
207
|
+
return filtered.length > 0 ? filtered[0] : null
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error("Error in getRuleById:", error.message)
|
|
210
|
+
return null
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Compare version strings (supports x.y format)
|
|
216
|
+
* @param {string} version1 - First version
|
|
217
|
+
* @param {string} version2 - Second version
|
|
218
|
+
* @returns {number} -1 if version1 < version2, 0 if equal, 1 if version1 > version2
|
|
219
|
+
*/
|
|
220
|
+
compareVersions(version1, version2) {
|
|
221
|
+
try {
|
|
222
|
+
const v1Parts = version1.split('.').map(Number)
|
|
223
|
+
const v2Parts = version2.split('.').map(Number)
|
|
224
|
+
|
|
225
|
+
const maxLength = Math.max(v1Parts.length, v2Parts.length)
|
|
226
|
+
|
|
227
|
+
for (let i = 0; i < maxLength; i++) {
|
|
228
|
+
const v1Part = v1Parts[i] || 0
|
|
229
|
+
const v2Part = v2Parts[i] || 0
|
|
230
|
+
|
|
231
|
+
if (v1Part < v2Part) return -1
|
|
232
|
+
if (v1Part > v2Part) return 1
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return 0
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.warn(`Error comparing versions ${version1} and ${version2}:`, error.message)
|
|
238
|
+
return 0
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Convert full rule object to simplified format
|
|
244
|
+
* @param {Object} fullRule - Full rule object from RuleParser
|
|
245
|
+
* @returns {Object} Simplified rule object
|
|
246
|
+
*/
|
|
247
|
+
simplifyRule(fullRule) {
|
|
248
|
+
return {
|
|
249
|
+
id: fullRule.id,
|
|
250
|
+
title: fullRule.title,
|
|
251
|
+
description: fullRule.description,
|
|
252
|
+
details: fullRule.details || [],
|
|
253
|
+
tools: fullRule.tools || [],
|
|
254
|
+
principles: fullRule.principles || [],
|
|
255
|
+
version: fullRule.version,
|
|
256
|
+
status: fullRule.status,
|
|
257
|
+
severity: fullRule.severity,
|
|
258
|
+
language: fullRule.language,
|
|
259
|
+
framework: fullRule.framework,
|
|
260
|
+
category: fullRule.category
|
|
261
|
+
// Exclude examples and configs for lightweight usage
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Factory functions for easy use (maintaining backward compatibility)
|
|
267
|
+
function parseRuleFile(filePath) {
|
|
268
|
+
const parser = new SimpleRuleParser()
|
|
269
|
+
return parser.parseRuleFile(filePath)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function parseAllRules(rulesDir = null) {
|
|
273
|
+
const parser = new SimpleRuleParser()
|
|
274
|
+
return parser.parseAllRules(rulesDir)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function filterRules(rules, filters = {}) {
|
|
278
|
+
const parser = new SimpleRuleParser()
|
|
279
|
+
return parser.filterRules(rules, filters)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function getRulesByPrinciples(principles, rulesDir = null) {
|
|
283
|
+
const parser = new SimpleRuleParser()
|
|
284
|
+
return parser.getRulesByPrinciples(principles, rulesDir)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function getRulesByFramework(framework, rulesDir = null) {
|
|
288
|
+
const parser = new SimpleRuleParser()
|
|
289
|
+
return parser.getRulesByFramework(framework, rulesDir)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function getRuleById(ruleId, rulesDir = null) {
|
|
293
|
+
const parser = new SimpleRuleParser()
|
|
294
|
+
return parser.getRuleById(ruleId, rulesDir)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
module.exports = {
|
|
298
|
+
SimpleRuleParser,
|
|
299
|
+
parseRuleFile,
|
|
300
|
+
parseAllRules,
|
|
301
|
+
filterRules,
|
|
302
|
+
getRulesByPrinciples,
|
|
303
|
+
getRulesByFramework,
|
|
304
|
+
getRuleById
|
|
305
|
+
}
|