@sun-asterisk/sunlint 1.0.5

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 (192) hide show
  1. package/CHANGELOG.md +202 -0
  2. package/LICENSE +21 -0
  3. package/README.md +490 -0
  4. package/cli-legacy.js +355 -0
  5. package/cli.js +35 -0
  6. package/config/default.json +22 -0
  7. package/config/presets/beginner.json +36 -0
  8. package/config/presets/ci.json +46 -0
  9. package/config/presets/recommended.json +24 -0
  10. package/config/presets/strict.json +32 -0
  11. package/config/rules-registry.json +681 -0
  12. package/config/sunlint-schema.json +166 -0
  13. package/config/typescript/custom-rules-new.js +0 -0
  14. package/config/typescript/custom-rules.js +9 -0
  15. package/config/typescript/eslint.config.js +110 -0
  16. package/config/typescript/package-lock.json +1585 -0
  17. package/config/typescript/package.json +13 -0
  18. package/config/typescript/security-rules/index.js +90 -0
  19. package/config/typescript/security-rules/s005-no-origin-auth.js +95 -0
  20. package/config/typescript/security-rules/s006-activation-recovery-secret-not-plaintext.js +69 -0
  21. package/config/typescript/security-rules/s008-crypto-agility.js +62 -0
  22. package/config/typescript/security-rules/s009-no-insecure-crypto.js +103 -0
  23. package/config/typescript/security-rules/s010-no-insecure-random-in-sensitive-context.js +123 -0
  24. package/config/typescript/security-rules/s011-no-insecure-uuid.js +66 -0
  25. package/config/typescript/security-rules/s012-hardcode-secret.js +71 -0
  26. package/config/typescript/security-rules/s014-insecure-tls-version.js +50 -0
  27. package/config/typescript/security-rules/s015-insecure-tls-certificate.js +43 -0
  28. package/config/typescript/security-rules/s016-sensitive-query-parameter.js +59 -0
  29. package/config/typescript/security-rules/s017-no-sql-injection.js +193 -0
  30. package/config/typescript/security-rules/s018-positive-input-validation.js +56 -0
  31. package/config/typescript/security-rules/s019-no-raw-user-input-in-email.js +113 -0
  32. package/config/typescript/security-rules/s020-no-eval-dynamic-execution.js +89 -0
  33. package/config/typescript/security-rules/s022-output-encoding.js +78 -0
  34. package/config/typescript/security-rules/s023-no-json-injection.js +300 -0
  35. package/config/typescript/security-rules/s025-server-side-input-validation.js +217 -0
  36. package/config/typescript/security-rules/s026-json-schema-validation.js +68 -0
  37. package/config/typescript/security-rules/s027-no-hardcoded-secrets.js +80 -0
  38. package/config/typescript/security-rules/s029-require-csrf-protection.js +79 -0
  39. package/config/typescript/security-rules/s030-no-directory-browsing.js +78 -0
  40. package/config/typescript/security-rules/s033-require-samesite-cookie.js +80 -0
  41. package/config/typescript/security-rules/s034-require-host-cookie-prefix.js +77 -0
  42. package/config/typescript/security-rules/s035-cookie-specific-path.js +74 -0
  43. package/config/typescript/security-rules/s036-no-unsafe-file-include.js +68 -0
  44. package/config/typescript/security-rules/s037-require-anti-cache-headers.js +70 -0
  45. package/config/typescript/security-rules/s038-no-version-disclosure.js +74 -0
  46. package/config/typescript/security-rules/s039-no-session-token-in-url.js +63 -0
  47. package/config/typescript/security-rules/s041-require-session-invalidate-on-logout.js +211 -0
  48. package/config/typescript/security-rules/s042-require-periodic-reauthentication.js +294 -0
  49. package/config/typescript/security-rules/s043-terminate-sessions-on-password-change.js +254 -0
  50. package/config/typescript/security-rules/s044-require-full-session-for-sensitive-operations.js +292 -0
  51. package/config/typescript/security-rules/s045-anti-automation-controls.js +46 -0
  52. package/config/typescript/security-rules/s046-secure-notification-on-auth-change.js +44 -0
  53. package/config/typescript/security-rules/s048-password-credential-recovery.js +54 -0
  54. package/config/typescript/security-rules/s050-session-token-weak-hash.js +94 -0
  55. package/config/typescript/security-rules/s052-secure-random-authentication-code.js +66 -0
  56. package/config/typescript/security-rules/s054-verification-default-account.js +109 -0
  57. package/config/typescript/security-rules/s057-utc-logging.js +54 -0
  58. package/config/typescript/security-rules/s058-no-ssrf.js +73 -0
  59. package/config/typescript/test-s005-working.ts +22 -0
  60. package/config/typescript/tsconfig.json +29 -0
  61. package/core/ai-analyzer.js +169 -0
  62. package/core/analysis-orchestrator.js +705 -0
  63. package/core/cli-action-handler.js +230 -0
  64. package/core/cli-program.js +106 -0
  65. package/core/config-manager.js +396 -0
  66. package/core/config-merger.js +136 -0
  67. package/core/config-override-processor.js +74 -0
  68. package/core/config-preset-resolver.js +65 -0
  69. package/core/config-source-loader.js +152 -0
  70. package/core/config-validator.js +126 -0
  71. package/core/dependency-manager.js +105 -0
  72. package/core/eslint-engine-service.js +312 -0
  73. package/core/eslint-instance-manager.js +104 -0
  74. package/core/eslint-integration-service.js +363 -0
  75. package/core/git-utils.js +170 -0
  76. package/core/multi-rule-runner.js +239 -0
  77. package/core/output-service.js +250 -0
  78. package/core/report-generator.js +320 -0
  79. package/core/rule-mapping-service.js +309 -0
  80. package/core/rule-selection-service.js +121 -0
  81. package/core/sunlint-engine-service.js +23 -0
  82. package/core/typescript-analyzer.js +262 -0
  83. package/core/typescript-engine.js +313 -0
  84. package/docs/AI.md +163 -0
  85. package/docs/ARCHITECTURE.md +78 -0
  86. package/docs/CI-CD-GUIDE.md +315 -0
  87. package/docs/COMMAND-EXAMPLES.md +256 -0
  88. package/docs/DEBUG.md +86 -0
  89. package/docs/DISTRIBUTION.md +153 -0
  90. package/docs/ESLINT-INTEGRATION-STRATEGY.md +392 -0
  91. package/docs/ESLINT_INTEGRATION.md +238 -0
  92. package/docs/FOLDER_STRUCTURE.md +59 -0
  93. package/docs/HEURISTIC_VS_AI.md +113 -0
  94. package/docs/README.md +32 -0
  95. package/docs/RELEASE_GUIDE.md +230 -0
  96. package/docs/RULE-RESPONSIBILITY-MATRIX.md +204 -0
  97. package/eslint-integration/.eslintrc.js +98 -0
  98. package/eslint-integration/cli.js +35 -0
  99. package/eslint-integration/eslint-plugin-custom/c002-no-duplicate-code.js +204 -0
  100. package/eslint-integration/eslint-plugin-custom/c003-no-vague-abbreviations.js +246 -0
  101. package/eslint-integration/eslint-plugin-custom/c006-function-name-verb-noun.js +207 -0
  102. package/eslint-integration/eslint-plugin-custom/c010-limit-block-nesting.js +90 -0
  103. package/eslint-integration/eslint-plugin-custom/c013-no-dead-code.js +43 -0
  104. package/eslint-integration/eslint-plugin-custom/c014-abstract-dependency-preferred.js +38 -0
  105. package/eslint-integration/eslint-plugin-custom/c017-limit-constructor-logic.js +39 -0
  106. package/eslint-integration/eslint-plugin-custom/c018-no-generic-throw.js +335 -0
  107. package/eslint-integration/eslint-plugin-custom/c023-no-duplicate-variable-name-in-scope.js +142 -0
  108. package/eslint-integration/eslint-plugin-custom/c027-limit-function-nesting.js +50 -0
  109. package/eslint-integration/eslint-plugin-custom/c029-catch-block-logging.js +80 -0
  110. package/eslint-integration/eslint-plugin-custom/c030-use-custom-error-classes.js +294 -0
  111. package/eslint-integration/eslint-plugin-custom/c034-no-implicit-return.js +34 -0
  112. package/eslint-integration/eslint-plugin-custom/c035-no-empty-catch.js +32 -0
  113. package/eslint-integration/eslint-plugin-custom/c041-no-config-inline.js +64 -0
  114. package/eslint-integration/eslint-plugin-custom/c042-boolean-name-prefix.js +406 -0
  115. package/eslint-integration/eslint-plugin-custom/c043-no-console-or-print.js +300 -0
  116. package/eslint-integration/eslint-plugin-custom/c047-no-duplicate-retry-logic.js +239 -0
  117. package/eslint-integration/eslint-plugin-custom/c048-no-var-declaration.js +31 -0
  118. package/eslint-integration/eslint-plugin-custom/c076-one-assert-per-test.js +184 -0
  119. package/eslint-integration/eslint-plugin-custom/index.js +155 -0
  120. package/eslint-integration/eslint-plugin-custom/package.json +13 -0
  121. package/eslint-integration/eslint-plugin-custom/package.json.bak +9 -0
  122. package/eslint-integration/eslint-plugin-custom/s003-no-unvalidated-redirect.js +86 -0
  123. package/eslint-integration/eslint-plugin-custom/s005-no-origin-auth.js +95 -0
  124. package/eslint-integration/eslint-plugin-custom/s006-activation-recovery-secret-not-plaintext.js +69 -0
  125. package/eslint-integration/eslint-plugin-custom/s008-crypto-agility.js +62 -0
  126. package/eslint-integration/eslint-plugin-custom/s009-no-insecure-crypto.js +103 -0
  127. package/eslint-integration/eslint-plugin-custom/s010-no-insecure-random-in-sensitive-context.js +123 -0
  128. package/eslint-integration/eslint-plugin-custom/s011-no-insecure-uuid.js +66 -0
  129. package/eslint-integration/eslint-plugin-custom/s012-hardcode-secret.js +71 -0
  130. package/eslint-integration/eslint-plugin-custom/s014-insecure-tls-version.js +50 -0
  131. package/eslint-integration/eslint-plugin-custom/s015-insecure-tls-certificate.js +43 -0
  132. package/eslint-integration/eslint-plugin-custom/s016-sensitive-query-parameter.js +59 -0
  133. package/eslint-integration/eslint-plugin-custom/s017-no-sql-injection.js +193 -0
  134. package/eslint-integration/eslint-plugin-custom/s018-positive-input-validation.js +56 -0
  135. package/eslint-integration/eslint-plugin-custom/s019-no-raw-user-input-in-email.js +113 -0
  136. package/eslint-integration/eslint-plugin-custom/s020-no-eval-dynamic-execution.js +89 -0
  137. package/eslint-integration/eslint-plugin-custom/s022-output-encoding.js +78 -0
  138. package/eslint-integration/eslint-plugin-custom/s023-no-json-injection.js +300 -0
  139. package/eslint-integration/eslint-plugin-custom/s025-server-side-input-validation.js +217 -0
  140. package/eslint-integration/eslint-plugin-custom/s026-json-schema-validation.js +68 -0
  141. package/eslint-integration/eslint-plugin-custom/s027-no-hardcoded-secrets.js +80 -0
  142. package/eslint-integration/eslint-plugin-custom/s029-require-csrf-protection.js +79 -0
  143. package/eslint-integration/eslint-plugin-custom/s030-no-directory-browsing.js +78 -0
  144. package/eslint-integration/eslint-plugin-custom/s033-require-samesite-cookie.js +80 -0
  145. package/eslint-integration/eslint-plugin-custom/s034-require-host-cookie-prefix.js +77 -0
  146. package/eslint-integration/eslint-plugin-custom/s035-cookie-specific-path.js +74 -0
  147. package/eslint-integration/eslint-plugin-custom/s036-no-unsafe-file-include.js +68 -0
  148. package/eslint-integration/eslint-plugin-custom/s037-require-anti-cache-headers.js +70 -0
  149. package/eslint-integration/eslint-plugin-custom/s038-no-version-disclosure.js +74 -0
  150. package/eslint-integration/eslint-plugin-custom/s039-no-session-token-in-url.js +63 -0
  151. package/eslint-integration/eslint-plugin-custom/s041-require-session-invalidate-on-logout.js +211 -0
  152. package/eslint-integration/eslint-plugin-custom/s042-require-periodic-reauthentication.js +294 -0
  153. package/eslint-integration/eslint-plugin-custom/s043-terminate-sessions-on-password-change.js +254 -0
  154. package/eslint-integration/eslint-plugin-custom/s044-require-full-session-for-sensitive-operations.js +292 -0
  155. package/eslint-integration/eslint-plugin-custom/s045-anti-automation-controls.js +46 -0
  156. package/eslint-integration/eslint-plugin-custom/s046-secure-notification-on-auth-change.js +44 -0
  157. package/eslint-integration/eslint-plugin-custom/s047-secure-random-passwords.js +108 -0
  158. package/eslint-integration/eslint-plugin-custom/s048-password-credential-recovery.js +54 -0
  159. package/eslint-integration/eslint-plugin-custom/s050-session-token-weak-hash.js +94 -0
  160. package/eslint-integration/eslint-plugin-custom/s052-secure-random-authentication-code.js +66 -0
  161. package/eslint-integration/eslint-plugin-custom/s054-verification-default-account.js +109 -0
  162. package/eslint-integration/eslint-plugin-custom/s055-verification-rest-check-the-incoming-content-type.js +143 -0
  163. package/eslint-integration/eslint-plugin-custom/s057-utc-logging.js +54 -0
  164. package/eslint-integration/eslint-plugin-custom/s058-no-ssrf.js +73 -0
  165. package/eslint-integration/eslint-plugin-custom/t002-interface-prefix-i.js +42 -0
  166. package/eslint-integration/eslint-plugin-custom/t003-ts-ignore-reason.js +48 -0
  167. package/eslint-integration/eslint-plugin-custom/t004-interface-public-only.js +160 -0
  168. package/eslint-integration/eslint-plugin-custom/t007-no-fn-in-constructor.js +52 -0
  169. package/eslint-integration/eslint-plugin-custom/t011-no-real-time-dependency.js +175 -0
  170. package/eslint-integration/eslint-plugin-custom/t019-no-empty-type.js +95 -0
  171. package/eslint-integration/eslint-plugin-custom/t025-no-nested-union-tuple.js +48 -0
  172. package/eslint-integration/eslint-plugin-custom/t026-limit-nested-generics.js +377 -0
  173. package/eslint-integration/eslint.config.js +125 -0
  174. package/eslint-integration/eslint.config.simple.js +24 -0
  175. package/eslint-integration/node_modules/eslint-plugin-custom/package.json +0 -0
  176. package/eslint-integration/package.json +23 -0
  177. package/eslint-integration/sample.ts +53 -0
  178. package/eslint-integration/test-s003.js +5 -0
  179. package/eslint-integration/tsconfig.json +27 -0
  180. package/examples/.github/workflows/code-quality.yml +111 -0
  181. package/examples/.sunlint.json +42 -0
  182. package/examples/README.md +47 -0
  183. package/examples/package.json +33 -0
  184. package/package.json +100 -0
  185. package/rules/C006_function_naming/analyzer.js +338 -0
  186. package/rules/C006_function_naming/config.json +86 -0
  187. package/rules/C019_log_level_usage/analyzer.js +359 -0
  188. package/rules/C019_log_level_usage/config.json +121 -0
  189. package/rules/C029_catch_block_logging/analyzer.js +339 -0
  190. package/rules/C029_catch_block_logging/config.json +59 -0
  191. package/rules/C031_validation_separation/README.md +72 -0
  192. package/rules/C031_validation_separation/analyzer.js +186 -0
@@ -0,0 +1,86 @@
1
+ {
2
+ "ruleId": "C006",
3
+ "name": "Function Naming Convention",
4
+ "description": "Tên hàm phải là động từ/verb-noun pattern",
5
+ "category": "naming",
6
+ "severity": "warning",
7
+ "languages": ["typescript", "dart", "kotlin"],
8
+ "version": "1.0.0",
9
+ "status": "activated",
10
+ "tags": ["naming", "convention", "readability"],
11
+ "config": {
12
+ "commonVerbs": [
13
+ "get", "set", "is", "has", "can", "should", "will",
14
+ "create", "build", "make", "generate", "construct",
15
+ "update", "modify", "change", "edit", "alter",
16
+ "delete", "remove", "destroy", "clean", "clear",
17
+ "load", "save", "fetch", "retrieve", "find", "search",
18
+ "validate", "verify", "check", "confirm", "ensure",
19
+ "calculate", "compute", "process", "handle", "manage",
20
+ "send", "receive", "transmit", "broadcast", "emit",
21
+ "parse", "format", "transform", "convert", "map",
22
+ "filter", "sort", "group", "merge", "split",
23
+ "connect", "disconnect", "open", "close", "start", "stop",
24
+ "show", "hide", "display", "render", "draw", "paint",
25
+ "add", "append", "insert", "push", "pop", "shift",
26
+ "test", "debug", "log", "trace", "monitor", "watch"
27
+ ],
28
+ "specialFunctions": [
29
+ "constructor", "toString", "valueOf", "toJSON",
30
+ "main", "init", "setup", "teardown", "build",
31
+ "onCreate", "onDestroy", "onStart", "onStop",
32
+ "onPause", "onResume", "onSaveInstanceState",
33
+ "equals", "hashCode", "compareTo", "clone",
34
+ "finalize", "notify", "notifyAll", "wait"
35
+ ],
36
+ "patterns": {
37
+ "camelCase": "^[a-z][a-zA-Z0-9]*$",
38
+ "verbNoun": "^(get|set|is|has|can|create|update|delete|load|save|find|validate|process|handle|calculate|send|receive|parse|format|transform|filter|sort|connect|show|hide|add|remove)[A-Z][a-zA-Z0-9]*$"
39
+ }
40
+ },
41
+ "examples": {
42
+ "violations": [
43
+ {
44
+ "language": "typescript",
45
+ "code": "function userData() { return user.data; }",
46
+ "reason": "Function name is a noun - should start with a verb like 'getUserData'"
47
+ },
48
+ {
49
+ "language": "typescript",
50
+ "code": "function calculation() { return x + y; }",
51
+ "reason": "Should use verb-noun pattern like 'calculateSum' or 'performCalculation'"
52
+ },
53
+ {
54
+ "language": "dart",
55
+ "code": "String userInfo() { return 'info'; }",
56
+ "reason": "Function name should start with verb like 'getUserInfo'"
57
+ }
58
+ ],
59
+ "valid": [
60
+ {
61
+ "language": "typescript",
62
+ "code": "function getUserData() { return user.data; }",
63
+ "reason": "Follows verb-noun pattern"
64
+ },
65
+ {
66
+ "language": "typescript",
67
+ "code": "function isValid() { return true; }",
68
+ "reason": "Starts with verb 'is'"
69
+ },
70
+ {
71
+ "language": "dart",
72
+ "code": "bool hasPermission() { return true; }",
73
+ "reason": "Starts with verb 'has'"
74
+ }
75
+ ]
76
+ },
77
+ "fixes": {
78
+ "autoFixable": false,
79
+ "suggestions": [
80
+ "Start function names with verbs (get, set, is, has, create, update, etc.)",
81
+ "Use verb-noun pattern for clarity (e.g., getUserData, validateInput)",
82
+ "Avoid noun-only function names",
83
+ "Use camelCase convention"
84
+ ]
85
+ }
86
+ }
@@ -0,0 +1,359 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const ts = require('typescript');
4
+ const AIAnalyzer = require('../../core/ai-analyzer');
5
+
6
+ class C019Analyzer {
7
+ constructor() {
8
+ this.ruleId = 'C019';
9
+ this.ruleName = 'Log Level Usage';
10
+ this.description = 'Không sử dụng log mức error cho lỗi không nghiêm trọng';
11
+ this.aiAnalyzer = null;
12
+ }
13
+
14
+ async analyze(files, language, config) {
15
+ const violations = [];
16
+
17
+ // Initialize AI analyzer if enabled
18
+ if (config.ai && config.ai.enabled) {
19
+ this.aiAnalyzer = new AIAnalyzer(config.ai);
20
+ console.log('🤖 AI analysis enabled for C019');
21
+ }
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.error(`Error analyzing file ${filePath}:`, error.message);
30
+ }
31
+ }
32
+
33
+ return violations;
34
+ }
35
+
36
+ async analyzeFile(filePath, content, language, config) {
37
+ let violations = [];
38
+
39
+ // Try AI analysis first if enabled
40
+ if (this.aiAnalyzer) {
41
+ try {
42
+ console.log(`🤖 Running AI analysis on ${path.basename(filePath)}`);
43
+ const aiViolations = await this.aiAnalyzer.analyzeWithAI(filePath, content, {
44
+ name: this.ruleName,
45
+ description: this.description,
46
+ ruleId: this.ruleId
47
+ });
48
+
49
+ if (aiViolations && aiViolations.length > 0) {
50
+ console.log(`🎯 AI found ${aiViolations.length} violations`);
51
+ return aiViolations;
52
+ }
53
+ } catch (error) {
54
+ console.warn('🤖 AI analysis failed, falling back to pattern analysis:', error.message);
55
+ }
56
+ }
57
+
58
+ // Fallback to pattern-based analysis
59
+ console.log(`🔍 Running pattern analysis on ${path.basename(filePath)}`);
60
+ switch (language) {
61
+ case 'typescript':
62
+ case 'javascript':
63
+ return this.analyzeTypeScript(filePath, content, config);
64
+ case 'dart':
65
+ return this.analyzeDart(filePath, content, config);
66
+ case 'kotlin':
67
+ return this.analyzeKotlin(filePath, content, config);
68
+ default:
69
+ return [];
70
+ }
71
+ }
72
+
73
+ async analyzeTypeScript(filePath, content, config) {
74
+ const violations = [];
75
+ const lines = content.split('\n');
76
+
77
+ // Parse TypeScript/JavaScript code
78
+ const sourceFile = ts.createSourceFile(
79
+ filePath,
80
+ content,
81
+ ts.ScriptTarget.Latest,
82
+ true
83
+ );
84
+
85
+ const visit = (node) => {
86
+ // Look for method calls that might be logging
87
+ if (ts.isCallExpression(node)) {
88
+ const callText = node.getText(sourceFile);
89
+
90
+ // Check if this is an error-level log call
91
+ const isErrorLog = this.isErrorLogCall(callText);
92
+
93
+ if (isErrorLog) {
94
+ const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
95
+ const column = sourceFile.getLineAndCharacterOfPosition(node.getStart()).character + 1;
96
+ const lineText = lines[line - 1]?.trim() || '';
97
+
98
+ // Analyze the log message for inappropriate error usage
99
+ const logMessage = this.extractLogMessage(node, sourceFile);
100
+ const analysis = this.analyzeLogMessage(logMessage, callText);
101
+
102
+ if (analysis.isViolation) {
103
+ violations.push({
104
+ ruleId: this.ruleId,
105
+ file: filePath,
106
+ line,
107
+ column,
108
+ message: analysis.reason,
109
+ severity: analysis.severity || 'warning',
110
+ code: lineText,
111
+ type: analysis.type,
112
+ confidence: analysis.confidence || 0.8,
113
+ suggestion: analysis.suggestion
114
+ });
115
+ }
116
+ }
117
+ }
118
+
119
+ ts.forEachChild(node, visit);
120
+ };
121
+
122
+ visit(sourceFile);
123
+ return violations;
124
+ }
125
+
126
+ async analyzeDart(filePath, content, config) {
127
+ const violations = [];
128
+ const lines = content.split('\n');
129
+
130
+ // Pattern-based analysis for Dart
131
+ const errorLogPatterns = [
132
+ /log\.error\(/g,
133
+ /logger\.error\(/g,
134
+ /Logger\.error\(/g,
135
+ /print\(.*error.*\)/gi,
136
+ /_logger\.error\(/g
137
+ ];
138
+
139
+ lines.forEach((line, index) => {
140
+ const lineNumber = index + 1;
141
+ const trimmedLine = line.trim();
142
+
143
+ errorLogPatterns.forEach(pattern => {
144
+ const matches = trimmedLine.match(pattern);
145
+ if (matches) {
146
+ const analysis = this.analyzeLogMessage(trimmedLine, trimmedLine);
147
+
148
+ if (analysis.isViolation) {
149
+ violations.push({
150
+ ruleId: this.ruleId,
151
+ file: filePath,
152
+ line: lineNumber,
153
+ column: line.indexOf(matches[0]) + 1,
154
+ message: analysis.reason,
155
+ severity: analysis.severity || 'warning',
156
+ code: trimmedLine,
157
+ type: analysis.type,
158
+ confidence: analysis.confidence || 0.7,
159
+ suggestion: analysis.suggestion
160
+ });
161
+ }
162
+ }
163
+ });
164
+ });
165
+
166
+ return violations;
167
+ }
168
+
169
+ async analyzeKotlin(filePath, content, config) {
170
+ const violations = [];
171
+ const lines = content.split('\n');
172
+
173
+ // Pattern-based analysis for Kotlin
174
+ const errorLogPatterns = [
175
+ /Log\.e\(/g,
176
+ /logger\.error\(/g,
177
+ /log\.error\(/g,
178
+ /Timber\.e\(/g
179
+ ];
180
+
181
+ lines.forEach((line, index) => {
182
+ const lineNumber = index + 1;
183
+ const trimmedLine = line.trim();
184
+
185
+ errorLogPatterns.forEach(pattern => {
186
+ const matches = trimmedLine.match(pattern);
187
+ if (matches) {
188
+ const analysis = this.analyzeLogMessage(trimmedLine, trimmedLine);
189
+
190
+ if (analysis.isViolation) {
191
+ violations.push({
192
+ ruleId: this.ruleId,
193
+ file: filePath,
194
+ line: lineNumber,
195
+ column: line.indexOf(matches[0]) + 1,
196
+ message: analysis.reason,
197
+ severity: analysis.severity || 'warning',
198
+ code: trimmedLine,
199
+ type: analysis.type,
200
+ confidence: analysis.confidence || 0.7,
201
+ suggestion: analysis.suggestion
202
+ });
203
+ }
204
+ }
205
+ });
206
+ });
207
+
208
+ return violations;
209
+ }
210
+
211
+ isErrorLogCall(callText) {
212
+ const errorLogPatterns = [
213
+ 'console.error(',
214
+ 'logger.error(',
215
+ 'log.error(',
216
+ '.error(',
217
+ 'Logger.error(',
218
+ 'winston.error(',
219
+ 'bunyan.error('
220
+ ];
221
+
222
+ return errorLogPatterns.some(pattern => callText.includes(pattern));
223
+ }
224
+
225
+ extractLogMessage(callNode, sourceFile) {
226
+ // Try to extract the log message from the call expression
227
+ if (callNode.arguments && callNode.arguments.length > 0) {
228
+ const firstArg = callNode.arguments[0];
229
+ if (ts.isStringLiteral(firstArg) || ts.isTemplateExpression(firstArg)) {
230
+ return firstArg.getText(sourceFile).replace(/['"`]/g, '');
231
+ }
232
+ }
233
+ return '';
234
+ }
235
+
236
+ analyzeLogMessage(message, fullCall) {
237
+ const config = {
238
+ errorKeywords: [
239
+ 'not found', 'invalid', 'unauthorized', 'forbidden',
240
+ 'validation failed', 'bad request', 'cache miss',
241
+ 'retry', 'fallback', 'user error', 'input error',
242
+ 'validation', 'invalid input', 'missing parameter'
243
+ ],
244
+ legitimateErrorKeywords: [
245
+ 'exception', 'crash', 'fatal', 'critical', 'emergency',
246
+ 'database', 'connection', 'timeout', 'security breach',
247
+ 'system error', 'memory', 'disk space', 'internal server error',
248
+ 'unhandled exception', 'stack overflow'
249
+ ]
250
+ };
251
+
252
+ const lowerMessage = message.toLowerCase();
253
+ const lowerCall = fullCall.toLowerCase();
254
+
255
+ // Skip if this is in a catch block (legitimate error logging)
256
+ const isCatchBlockContext = this.isCatchBlockContext(fullCall);
257
+ if (isCatchBlockContext) {
258
+ return { isViolation: false };
259
+ }
260
+
261
+ // Check for legitimate error usage
262
+ const hasLegitimateError = config.legitimateErrorKeywords.some(keyword =>
263
+ lowerMessage.includes(keyword) || lowerCall.includes(keyword)
264
+ );
265
+
266
+ if (hasLegitimateError) {
267
+ return { isViolation: false };
268
+ }
269
+
270
+ // Check for inappropriate error usage
271
+ const hasInappropriateError = config.errorKeywords.some(keyword =>
272
+ lowerMessage.includes(keyword)
273
+ );
274
+
275
+ if (hasInappropriateError) {
276
+ return {
277
+ isViolation: true,
278
+ reason: 'Error log level used for non-critical issue - should use warn/info level',
279
+ severity: 'warning',
280
+ type: 'inappropriate_error_level',
281
+ confidence: 0.9,
282
+ suggestion: 'Consider using logger.warn() or logger.info() instead'
283
+ };
284
+ }
285
+
286
+ // Validation patterns
287
+ if (lowerMessage.includes('validation') || lowerMessage.includes('invalid')) {
288
+ return {
289
+ isViolation: true,
290
+ reason: 'Validation errors should typically use warn level',
291
+ severity: 'warning',
292
+ type: 'validation_error_level',
293
+ confidence: 0.85,
294
+ suggestion: 'Use logger.warn() for validation failures'
295
+ };
296
+ }
297
+
298
+ // User input patterns
299
+ if (lowerMessage.includes('user') && (lowerMessage.includes('input') || lowerMessage.includes('request'))) {
300
+ return {
301
+ isViolation: true,
302
+ reason: 'User input errors should not use error level',
303
+ severity: 'warning',
304
+ type: 'user_input_error_level',
305
+ confidence: 0.8,
306
+ suggestion: 'Use logger.warn() or logger.info() for user input issues'
307
+ };
308
+ }
309
+
310
+ // Authorization patterns
311
+ if (lowerMessage.includes('unauthorized') || lowerMessage.includes('forbidden')) {
312
+ return {
313
+ isViolation: true,
314
+ reason: 'Authorization failures are typically business logic, not system errors',
315
+ severity: 'info',
316
+ type: 'auth_error_level',
317
+ confidence: 0.7,
318
+ suggestion: 'Consider logger.warn() for authorization failures'
319
+ };
320
+ }
321
+
322
+ // Generic error call without clear context
323
+ if (message === '' || message.length < 10) {
324
+ // Don't flag if it's clearly a system error with error object concatenation
325
+ if (fullCall.includes('+ error') || fullCall.includes('${error}') ||
326
+ fullCall.includes('+ e') || fullCall.includes('${e}') ||
327
+ fullCall.includes('error:') || fullCall.includes('failed:')) {
328
+ return { isViolation: false };
329
+ }
330
+
331
+ return {
332
+ isViolation: true,
333
+ reason: 'Generic error log without specific message - might be inappropriate',
334
+ severity: 'info',
335
+ type: 'generic_error_level',
336
+ confidence: 0.6,
337
+ suggestion: 'Add specific error message and verify if error level is appropriate'
338
+ };
339
+ }
340
+
341
+ return { isViolation: false };
342
+ }
343
+
344
+ isCatchBlockContext(callText) {
345
+ // Simple heuristic: if the call contains error/e parameters common in catch blocks
346
+ const catchPatterns = [
347
+ 'console.error(error)',
348
+ 'console.error(e)',
349
+ 'logger.error(error)',
350
+ 'logger.error(e)',
351
+ 'console.error("', // followed by message and error
352
+ 'logger.error("'
353
+ ];
354
+
355
+ return catchPatterns.some(pattern => callText.includes(pattern));
356
+ }
357
+ }
358
+
359
+ module.exports = new C019Analyzer();
@@ -0,0 +1,121 @@
1
+ {
2
+ "ruleId": "C019",
3
+ "name": "Log Level Usage",
4
+ "description": "Không sử dụng log mức error cho lỗi không nghiêm trọng",
5
+ "category": "logging",
6
+ "severity": "warning",
7
+ "languages": ["typescript", "dart", "kotlin"],
8
+ "version": "1.0.0",
9
+ "status": "stable",
10
+ "tags": ["logging", "error-handling", "severity"],
11
+ "config": {
12
+ "errorKeywords": [
13
+ "not found",
14
+ "invalid",
15
+ "unauthorized",
16
+ "forbidden",
17
+ "validation failed",
18
+ "bad request",
19
+ "cache miss",
20
+ "retry",
21
+ "fallback",
22
+ "user error",
23
+ "input error",
24
+ "validation",
25
+ "invalid input",
26
+ "missing parameter"
27
+ ],
28
+ "legitimateErrorKeywords": [
29
+ "exception",
30
+ "crash",
31
+ "fatal",
32
+ "critical",
33
+ "emergency",
34
+ "database",
35
+ "connection",
36
+ "timeout",
37
+ "security breach",
38
+ "system error",
39
+ "memory",
40
+ "disk space",
41
+ "internal server error",
42
+ "unhandled exception",
43
+ "stack overflow"
44
+ ],
45
+ "languageSpecific": {
46
+ "typescript": {
47
+ "loggerPatterns": [
48
+ "console.error(",
49
+ "logger.error(",
50
+ "log.error(",
51
+ ".error(",
52
+ "Logger.error(",
53
+ "winston.error(",
54
+ "bunyan.error("
55
+ ]
56
+ },
57
+ "dart": {
58
+ "loggerPatterns": [
59
+ "log.error(",
60
+ "logger.error(",
61
+ "Logger.error(",
62
+ "_logger.error(",
63
+ "print("
64
+ ]
65
+ },
66
+ "kotlin": {
67
+ "loggerPatterns": [
68
+ "Log.e(",
69
+ "logger.error(",
70
+ "log.error(",
71
+ "Timber.e("
72
+ ]
73
+ }
74
+ }
75
+ },
76
+ "examples": {
77
+ "violations": [
78
+ {
79
+ "language": "typescript",
80
+ "code": "logger.error('User not found');",
81
+ "reason": "User not found is a business logic issue, not a system error"
82
+ },
83
+ {
84
+ "language": "typescript",
85
+ "code": "console.error('Invalid input provided');",
86
+ "reason": "Input validation should use warn level"
87
+ },
88
+ {
89
+ "language": "dart",
90
+ "code": "logger.error('Validation failed for user input');",
91
+ "reason": "Validation failures are expected business logic"
92
+ }
93
+ ],
94
+ "valid": [
95
+ {
96
+ "language": "typescript",
97
+ "code": "logger.error('Database connection failed');",
98
+ "reason": "Database issues are legitimate system errors"
99
+ },
100
+ {
101
+ "language": "typescript",
102
+ "code": "logger.warn('User not found');",
103
+ "reason": "Appropriate warning level for business logic"
104
+ },
105
+ {
106
+ "language": "dart",
107
+ "code": "logger.error('Unhandled exception in payment processing');",
108
+ "reason": "Unhandled exceptions are legitimate errors"
109
+ }
110
+ ]
111
+ },
112
+ "fixes": {
113
+ "autoFixable": false,
114
+ "suggestions": [
115
+ "Use logger.warn() for business logic issues",
116
+ "Use logger.info() for informational messages",
117
+ "Reserve logger.error() for system-level problems",
118
+ "Add specific error context to help determine appropriate level"
119
+ ]
120
+ }
121
+ }