@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,320 @@
1
+ const chalk = require('chalk');
2
+ const { table } = require('table');
3
+ const path = require('path');
4
+
5
+ class ReportGenerator {
6
+ constructor(config, options) {
7
+ this.config = config;
8
+ this.options = options;
9
+ }
10
+
11
+ async generateReport(results, metadata) {
12
+ const report = {
13
+ metadata: {
14
+ ...metadata,
15
+ timestamp: new Date().toISOString(),
16
+ tool: '☀️ Sun Lint',
17
+ company: 'Sun*',
18
+ format: this.options?.format || 'eslint'
19
+ },
20
+ raw: results,
21
+ formatted: '',
22
+ summary: ''
23
+ };
24
+
25
+ // Generate formatted output based on format
26
+ switch (this.options?.format) {
27
+ case 'eslint':
28
+ report.formatted = this.generateESLintFormat(results);
29
+ break;
30
+ case 'json':
31
+ report.formatted = JSON.stringify(results, null, 2);
32
+ break;
33
+ case 'summary':
34
+ report.formatted = this.generateSummaryFormat(results);
35
+ break;
36
+ case 'table':
37
+ report.formatted = this.generateTableFormat(results);
38
+ break;
39
+ default:
40
+ report.formatted = this.generateESLintFormat(results);
41
+ }
42
+
43
+ // Generate summary
44
+ report.summary = this.generateSummary(results, metadata);
45
+
46
+ return report;
47
+ }
48
+
49
+ generateESLintFormat(results) {
50
+ const violations = this.flattenViolations(results);
51
+
52
+ if (violations.length === 0) {
53
+ return chalk.green('✨ No coding standard violations found!');
54
+ }
55
+
56
+ const groupedByFile = this.groupViolationsByFile(violations);
57
+ let output = '';
58
+
59
+ for (const [file, fileViolations] of Object.entries(groupedByFile)) {
60
+ const relativePath = path.relative(process.cwd(), file);
61
+ output += `\n${chalk.underline(relativePath)}\n`;
62
+
63
+ fileViolations.forEach(violation => {
64
+ const severityColor = this.getSeverityColor(violation.severity);
65
+ const line = violation.line || 1;
66
+ const column = violation.column || 1;
67
+
68
+ output += ` ${line}:${column} `;
69
+ output += `${severityColor(this.getSeveritySymbol(violation.severity))} `;
70
+ output += `${violation.message} `;
71
+ output += `${chalk.gray(violation.ruleId)}\n`;
72
+ });
73
+ }
74
+
75
+ // Add summary line
76
+ const errorCount = violations.filter(v => v.severity === 'error').length;
77
+ const warningCount = violations.filter(v => v.severity === 'warning').length;
78
+ const infoCount = violations.filter(v => v.severity === 'info').length;
79
+
80
+ output += '\n';
81
+ output += chalk.red(`✖ ${violations.length} problems `);
82
+ output += `(${errorCount} errors, ${warningCount} warnings, ${infoCount} infos)\n`;
83
+
84
+ return output;
85
+ }
86
+
87
+ generateSummaryFormat(results) {
88
+ const violations = this.flattenViolations(results);
89
+ let output = '';
90
+
91
+ // Header
92
+ output += chalk.blue.bold('🔍 Coding Standards Analysis Report\n');
93
+ output += chalk.gray('═'.repeat(50)) + '\n\n';
94
+
95
+ // Overview
96
+ output += chalk.white.bold('📊 Overview:\n');
97
+ output += ` Files analyzed: ${results.filesAnalyzed || 0}\n`;
98
+ output += ` Rules executed: ${results.rulesRun || 0}\n`;
99
+ output += ` Total violations: ${violations.length}\n\n`;
100
+
101
+ // Violations by severity
102
+ const bySeverity = results.violationsBySeverity || {};
103
+ output += chalk.white.bold('🚨 Violations by Severity:\n');
104
+ output += ` ${chalk.red('Errors')}: ${bySeverity.error || 0}\n`;
105
+ output += ` ${chalk.yellow('Warnings')}: ${bySeverity.warning || 0}\n`;
106
+ output += ` ${chalk.blue('Info')}: ${bySeverity.info || 0}\n\n`;
107
+
108
+ // Top violated rules
109
+ if (results.violationsByRule && Object.keys(results.violationsByRule).length > 0) {
110
+ output += chalk.white.bold('📏 Most Violated Rules:\n');
111
+ const sortedRules = Object.entries(results.violationsByRule)
112
+ .sort(([,a], [,b]) => b - a)
113
+ .slice(0, 5);
114
+
115
+ sortedRules.forEach(([ruleId, count]) => {
116
+ output += ` ${ruleId}: ${count} violations\n`;
117
+ });
118
+ output += '\n';
119
+ }
120
+
121
+ // Top problematic files
122
+ if (results.violationsByFile && Object.keys(results.violationsByFile).length > 0) {
123
+ output += chalk.white.bold('📁 Most Problematic Files:\n');
124
+ const sortedFiles = Object.entries(results.violationsByFile)
125
+ .sort(([,a], [,b]) => b - a)
126
+ .slice(0, 5);
127
+
128
+ sortedFiles.forEach(([file, count]) => {
129
+ const relativePath = path.relative(process.cwd(), file);
130
+ output += ` ${relativePath}: ${count} violations\n`;
131
+ });
132
+ output += '\n';
133
+ }
134
+
135
+ // Recent violations (first 10)
136
+ if (violations.length > 0) {
137
+ output += chalk.white.bold('🔍 Recent Violations:\n');
138
+ violations.slice(0, 10).forEach(violation => {
139
+ const relativePath = path.relative(process.cwd(), violation.file || '');
140
+ const severityColor = this.getSeverityColor(violation.severity);
141
+ output += ` ${severityColor(violation.severity.toUpperCase())}: ${violation.message}\n`;
142
+ output += ` ${chalk.gray(`at ${relativePath}:${violation.line || 1}:${violation.column || 1} (${violation.ruleId})`)}\n`;
143
+ });
144
+ }
145
+
146
+ return output;
147
+ }
148
+
149
+ generateTableFormat(results) {
150
+ const violations = this.flattenViolations(results);
151
+
152
+ if (violations.length === 0) {
153
+ return chalk.green('✨ No coding standard violations found!');
154
+ }
155
+
156
+ // Check if this is an integrated analysis with source information
157
+ const hasSourceInfo = violations.some(v => v.source);
158
+
159
+ const tableData = hasSourceInfo
160
+ ? [['File', 'Line', 'Severity', 'Source', 'Rule', 'Message']]
161
+ : [['File', 'Line', 'Severity', 'Rule', 'Message']];
162
+
163
+ violations.forEach(violation => {
164
+ const relativePath = path.relative(process.cwd(), violation.file || '');
165
+ const row = [
166
+ relativePath,
167
+ (violation.line || 1).toString(),
168
+ violation.severity,
169
+ ];
170
+
171
+ if (hasSourceInfo) {
172
+ // Add source information with color coding
173
+ const sourceDisplay = violation.source === 'sunlint'
174
+ ? chalk.yellow('SunLint')
175
+ : chalk.cyan('ESLint');
176
+ row.push(sourceDisplay);
177
+ }
178
+
179
+ row.push(
180
+ violation.ruleId || 'unknown',
181
+ violation.message.substring(0, 50) + (violation.message.length > 50 ? '...' : '')
182
+ );
183
+
184
+ // Add conflict info if available
185
+ if (violation.additionalInfo) {
186
+ row[row.length - 1] += chalk.gray(` (ESLint: ${violation.additionalInfo.eslintRule})`);
187
+ }
188
+
189
+ tableData.push(row);
190
+ });
191
+
192
+ const config = {
193
+ border: {
194
+ topBody: '─',
195
+ topJoin: '┬',
196
+ topLeft: '┌',
197
+ topRight: '┐',
198
+ bottomBody: '─',
199
+ bottomJoin: '┴',
200
+ bottomLeft: '└',
201
+ bottomRight: '┘',
202
+ bodyLeft: '│',
203
+ bodyRight: '│',
204
+ bodyJoin: '│',
205
+ joinBody: '─',
206
+ joinLeft: '├',
207
+ joinRight: '┤',
208
+ joinJoin: '┼'
209
+ }
210
+ };
211
+
212
+ return table(tableData, config);
213
+ }
214
+
215
+ generateSummary(results, metadata) {
216
+ const violations = this.flattenViolations(results);
217
+ const errorCount = violations.filter(v => v.severity === 'error').length;
218
+ const warningCount = violations.filter(v => v.severity === 'warning').length;
219
+ const infoCount = violations.filter(v => v.severity === 'info').length;
220
+
221
+ let summary = '';
222
+ summary += `Analysis completed in ${metadata.duration}ms\n`;
223
+ summary += `Files: ${results.filesAnalyzed || 0} | Rules: ${metadata.rulesRun} | Total: ${violations.length}\n`;
224
+
225
+ if (errorCount > 0) {
226
+ summary += chalk.red(`Errors: ${errorCount} `);
227
+ }
228
+ if (warningCount > 0) {
229
+ summary += chalk.yellow(`Warnings: ${warningCount} `);
230
+ }
231
+ if (infoCount > 0) {
232
+ summary += chalk.blue(`Info: ${infoCount} `);
233
+ }
234
+
235
+ return summary.trim();
236
+ }
237
+
238
+ flattenViolations(results) {
239
+ const violations = [];
240
+
241
+ if (results.results) {
242
+ results.results.forEach(result => {
243
+ // Handle SunLint format (violations array)
244
+ if (result.violations) {
245
+ result.violations.forEach(violation => {
246
+ violations.push({
247
+ ...violation,
248
+ ruleId: violation.ruleId || result.ruleId,
249
+ severity: violation.severity || result.severity || 'warning'
250
+ });
251
+ });
252
+ }
253
+
254
+ // Handle ESLint format (messages array)
255
+ if (result.messages) {
256
+ result.messages.forEach(message => {
257
+ violations.push({
258
+ file: result.filePath || message.file,
259
+ ruleId: message.ruleId,
260
+ severity: message.severity === 2 ? 'error' : 'warning',
261
+ message: message.message,
262
+ line: message.line,
263
+ column: message.column,
264
+ source: message.source || 'eslint'
265
+ });
266
+ });
267
+ }
268
+ });
269
+ }
270
+
271
+ return violations;
272
+ }
273
+
274
+ groupViolationsByFile(violations) {
275
+ const grouped = {};
276
+
277
+ violations.forEach(violation => {
278
+ const file = violation.file || violation.filePath || 'unknown';
279
+ if (!grouped[file]) {
280
+ grouped[file] = [];
281
+ }
282
+ grouped[file].push(violation);
283
+ });
284
+
285
+ // Sort violations within each file by line number
286
+ Object.keys(grouped).forEach(file => {
287
+ grouped[file].sort((a, b) => (a.line || 1) - (b.line || 1));
288
+ });
289
+
290
+ return grouped;
291
+ }
292
+
293
+ getSeverityColor(severity) {
294
+ switch (severity) {
295
+ case 'error':
296
+ return chalk.red;
297
+ case 'warning':
298
+ return chalk.yellow;
299
+ case 'info':
300
+ return chalk.blue;
301
+ default:
302
+ return chalk.gray;
303
+ }
304
+ }
305
+
306
+ getSeveritySymbol(severity) {
307
+ switch (severity) {
308
+ case 'error':
309
+ return '✖';
310
+ case 'warning':
311
+ return '⚠';
312
+ case 'info':
313
+ return 'ℹ';
314
+ default:
315
+ return '•';
316
+ }
317
+ }
318
+ }
319
+
320
+ module.exports = ReportGenerator;
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Rule Mapping Service
3
+ * Following Rule C005: Single responsibility - handle rule mapping logic
4
+ * Following Rule C015: Use domain language - clear naming for rule mappings
5
+ */
6
+
7
+ class RuleMappingService {
8
+ constructor() {
9
+ this.eslintToSunlintMapping = this.createEslintToSunlintMapping();
10
+ this.sunlintToEslintMapping = this.createSunlintToEslintMapping();
11
+ }
12
+
13
+ /**
14
+ * Create ESLint to SunLint rule mapping
15
+ * Following Rule C012: Query - pure function
16
+ */
17
+ createEslintToSunlintMapping() {
18
+ return {
19
+ // Custom rules mapping (using actual rule IDs from eslint-plugin-custom)
20
+ 'custom/c002': 'C002',
21
+ 'custom/c003': 'C003',
22
+ 'custom/c006': 'C006',
23
+ 'custom/c010': 'C010',
24
+ 'custom/c013': 'C013',
25
+ 'custom/c014': 'C014',
26
+ 'custom/c017': 'C017',
27
+ 'custom/c018': 'C018',
28
+ 'custom/c023': 'C023',
29
+ 'custom/c027': 'C027',
30
+ 'custom/c029': 'C029',
31
+ 'custom/c030': 'C030',
32
+ 'custom/c034': 'C034',
33
+ 'custom/c035': 'C035',
34
+ 'custom/c041': 'C041',
35
+ 'custom/c042': 'C042',
36
+ 'custom/c043': 'C043',
37
+ 'custom/c047': 'C047',
38
+ 'custom/c048': 'C048',
39
+ 'custom/c076': 'C076',
40
+ 'custom/t002': 'T002',
41
+ 'custom/t003': 'T003',
42
+ 'custom/t004': 'T004',
43
+ 'custom/t007': 'T007',
44
+ 'custom/t011': 'T011',
45
+ 'custom/t019': 'T019',
46
+ 'custom/t025': 'T025',
47
+ 'custom/t026': 'T026',
48
+
49
+ // Additional mappings for semantic rule names
50
+ 'custom/no-console-error': 'C019',
51
+
52
+ // Security rules mapping
53
+ 'custom/typescript_s003': 'S003',
54
+ 'custom/typescript_s005': 'S005',
55
+ 'custom/typescript_s006': 'S006',
56
+ 'custom/typescript_s008': 'S008',
57
+ 'custom/typescript_s009': 'S009',
58
+ 'custom/typescript_s010': 'S010',
59
+ 'custom/typescript_s011': 'S011',
60
+ 'custom/typescript_s012': 'S012',
61
+ 'custom/typescript_s014': 'S014',
62
+ 'custom/typescript_s015': 'S015',
63
+ 'custom/typescript_s016': 'S016',
64
+ 'custom/typescript_s017': 'S017',
65
+ 'custom/typescript_s018': 'S018',
66
+ 'custom/typescript_s019': 'S019',
67
+ 'custom/typescript_s020': 'S020',
68
+ 'custom/typescript_s022': 'S022',
69
+ 'custom/typescript_s023': 'S023',
70
+ 'custom/typescript_s025': 'S025',
71
+ 'custom/typescript_s026': 'S026',
72
+ 'custom/typescript_s027': 'S027',
73
+ 'custom/typescript_s029': 'S029',
74
+ 'custom/typescript_s030': 'S030',
75
+ 'custom/typescript_s033': 'S033',
76
+ 'custom/typescript_s034': 'S034',
77
+ 'custom/typescript_s035': 'S035',
78
+ 'custom/typescript_s036': 'S036',
79
+ 'custom/typescript_s037': 'S037',
80
+ 'custom/typescript_s038': 'S038',
81
+ 'custom/typescript_s039': 'S039',
82
+ 'custom/typescript_s041': 'S041',
83
+ 'custom/typescript_s042': 'S042',
84
+ 'custom/typescript_s043': 'S043',
85
+ 'custom/typescript_s044': 'S044',
86
+ 'custom/typescript_s045': 'S045',
87
+ 'custom/typescript_s046': 'S046',
88
+ 'custom/typescript_s047': 'S047',
89
+ 'custom/typescript_s048': 'S048',
90
+ 'custom/typescript_s050': 'S050',
91
+ 'custom/typescript_s052': 'S052',
92
+ 'custom/typescript_s054': 'S054',
93
+ 'custom/typescript_s055': 'S055',
94
+ 'custom/typescript_s057': 'S057',
95
+ 'custom/typescript_s058': 'S058',
96
+ 'custom/single-responsibility': 'C005',
97
+ 'custom/verb-noun-naming': 'C006',
98
+ 'custom/no-direct-new': 'C014',
99
+ 'custom/domain-language': 'C015',
100
+ 'custom/separate-validation': 'C031',
101
+ 'custom/no-constructor-api': 'C032',
102
+ 'custom/separate-logic-data': 'C033',
103
+ 'custom/no-global-state': 'C034',
104
+ 'custom/full-error-logging': 'C035',
105
+ 'custom/standard-response': 'C037',
106
+ 'custom/no-order-dependency': 'C038',
107
+ 'custom/centralized-validation': 'C040',
108
+
109
+ // Standard ESLint rules that map to SunLint rules
110
+ 'prefer-const': 'C_PREFER_CONST',
111
+ 'no-unused-vars': 'C_NO_UNUSED_VARS',
112
+ 'no-console': 'C019', // Maps to logging rule
113
+ 'no-undef': 'C_NO_UNDEF', // Undefined variables
114
+ 'consistent-return': 'C_CONSISTENT_RETURN',
115
+ 'no-var': 'C_NO_VAR',
116
+ 'eqeqeq': 'C_STRICT_EQUALITY',
117
+ 'no-eval': 'S_NO_EVAL',
118
+ 'no-implied-eval': 'S_NO_IMPLIED_EVAL',
119
+ 'no-new-func': 'S_NO_NEW_FUNC',
120
+ 'max-lines-per-function': 'C_MAX_LINES_PER_FUNCTION',
121
+ 'complexity': 'C_COMPLEXITY',
122
+ 'no-new': 'C_NO_NEW',
123
+ 'curly': 'C_CURLY_BRACES',
124
+ };
125
+ }
126
+
127
+ /**
128
+ * Create SunLint to ESLint rule mapping
129
+ * Following Rule C012: Query - pure function
130
+ */
131
+ createSunlintToEslintMapping() {
132
+ const mapping = {};
133
+
134
+ // Reverse the ESLint to SunLint mapping
135
+ for (const [eslintRule, sunlintRule] of Object.entries(this.eslintToSunlintMapping)) {
136
+ if (!mapping[sunlintRule]) {
137
+ mapping[sunlintRule] = [];
138
+ }
139
+ mapping[sunlintRule].push(eslintRule);
140
+ }
141
+
142
+ // Add additional direct mappings
143
+ mapping['C005'] = ['custom/single-responsibility']; // C005 doesn't have direct ESLint rule, fallback to basic
144
+ mapping['C006'] = ['custom/c006']; // Verb-noun naming
145
+ mapping['C014'] = ['custom/c014']; // Dependency injection
146
+ mapping['C015'] = ['custom/domain-language']; // Domain language - no direct ESLint rule
147
+ mapping['C019'] = ['custom/c043', 'no-console']; // Console/logging
148
+ mapping['C031'] = ['custom/separate-validation']; // Validation - no direct ESLint rule
149
+ mapping['C032'] = ['custom/no-constructor-api']; // Constructor API - no direct ESLint rule
150
+ mapping['C033'] = ['custom/separate-logic-data']; // Logic separation - no direct ESLint rule
151
+ mapping['C034'] = ['custom/c034']; // Implicit return
152
+ mapping['C035'] = ['custom/c035']; // Empty catch
153
+ mapping['C037'] = ['custom/standard-response']; // Standard response - no direct ESLint rule
154
+ mapping['C038'] = ['custom/no-order-dependency']; // Order dependency - no direct ESLint rule
155
+ mapping['C040'] = ['custom/centralized-validation']; // Centralized validation - no direct ESLint rule
156
+
157
+ // Additional mappings for available custom rules
158
+ mapping['C002'] = ['custom/c002']; // Duplicate code
159
+ mapping['C003'] = ['custom/c003']; // Vague abbreviations
160
+ mapping['C010'] = ['custom/c010']; // Block nesting
161
+ mapping['C013'] = ['custom/c013']; // Dead code
162
+ mapping['C017'] = ['custom/c017']; // Constructor logic
163
+ mapping['C018'] = ['custom/c018']; // Generic throw
164
+ mapping['C023'] = ['custom/c023']; // Duplicate variable name
165
+ mapping['C027'] = ['custom/c027']; // Function nesting
166
+ mapping['C029'] = ['custom/c029']; // Catch block logging
167
+ mapping['C030'] = ['custom/c030']; // Custom error classes
168
+ mapping['C041'] = ['custom/c041']; // Config inline
169
+ mapping['C042'] = ['custom/c042']; // Boolean naming
170
+ mapping['C043'] = ['custom/c043']; // Console/print
171
+ mapping['C047'] = ['custom/c047']; // Duplicate retry logic
172
+ mapping['C048'] = ['custom/c048']; // Var declaration
173
+ mapping['C076'] = ['custom/c076']; // One assert per test
174
+
175
+ // Security rules mapping
176
+ mapping['S005'] = ['custom/typescript_s005']; // No Origin header auth
177
+ mapping['S006'] = ['custom/typescript_s006']; // Activation recovery secret
178
+ mapping['S008'] = ['custom/typescript_s008']; // Crypto agility
179
+ mapping['S009'] = ['custom/typescript_s009']; // No insecure crypto
180
+ mapping['S010'] = ['custom/typescript_s010']; // No insecure random
181
+ mapping['S011'] = ['custom/typescript_s011']; // No insecure UUID
182
+ mapping['S012'] = ['custom/typescript_s012']; // No hardcoded secrets
183
+ mapping['S014'] = ['custom/typescript_s014']; // Insecure TLS version
184
+ mapping['S015'] = ['custom/typescript_s015']; // Insecure TLS certificate
185
+ mapping['S016'] = ['custom/typescript_s016']; // Sensitive query parameter
186
+ mapping['S017'] = ['custom/typescript_s017']; // No SQL injection
187
+ mapping['S018'] = ['custom/typescript_s018']; // Positive input validation
188
+ mapping['S019'] = ['custom/typescript_s019']; // No raw user input in email
189
+ mapping['S020'] = ['custom/typescript_s020']; // No eval dynamic execution
190
+ mapping['S022'] = ['custom/typescript_s022']; // Output encoding required
191
+ mapping['S023'] = ['custom/typescript_s023']; // No JSON injection
192
+ mapping['S025'] = ['custom/typescript_s025']; // Server side input validation
193
+ mapping['S026'] = ['custom/typescript_s026']; // JSON schema validation
194
+ mapping['S027'] = ['custom/typescript_s027']; // No hardcoded secrets advanced
195
+ mapping['S029'] = ['custom/typescript_s029']; // Require CSRF protection
196
+ mapping['S030'] = ['custom/typescript_s030']; // No directory browsing
197
+ mapping['S033'] = ['custom/typescript_s033']; // Require SameSite cookie
198
+ mapping['S034'] = ['custom/typescript_s034']; // Require Host cookie prefix
199
+ mapping['S035'] = ['custom/typescript_s035']; // Cookie specific path
200
+ mapping['S036'] = ['custom/typescript_s036']; // No unsafe file include
201
+ mapping['S037'] = ['custom/typescript_s037']; // Require anti cache headers
202
+ mapping['S038'] = ['custom/typescript_s038']; // No version disclosure
203
+ mapping['S039'] = ['custom/typescript_s039']; // No session token in URL
204
+ mapping['S041'] = ['custom/typescript_s041']; // Require session invalidate on logout
205
+ mapping['S042'] = ['custom/typescript_s042']; // Require periodic reauthentication
206
+ mapping['S043'] = ['custom/typescript_s043']; // Terminate sessions on password change
207
+ mapping['S044'] = ['custom/typescript_s044']; // Require full session for sensitive ops
208
+ mapping['S045'] = ['custom/typescript_s045']; // Anti automation controls
209
+ mapping['S046'] = ['custom/typescript_s046']; // Secure notification on auth change
210
+ mapping['S048'] = ['custom/typescript_s048']; // Password credential recovery
211
+ mapping['S050'] = ['custom/typescript_s050']; // Session token weak hash
212
+ mapping['S052'] = ['custom/typescript_s052']; // Secure random authentication code
213
+ mapping['S054'] = ['custom/typescript_s054']; // Verification default account
214
+ mapping['S057'] = ['custom/typescript_s057']; // UTC logging
215
+ mapping['S058'] = ['custom/typescript_s058']; // No SSRF
216
+
217
+ return mapping;
218
+ }
219
+
220
+ /**
221
+ * Get ESLint rules for SunLint rule ID
222
+ * Following Rule C006: Verb-noun naming
223
+ */
224
+ getEslintRulesForSunLintRule(sunlintRuleId) {
225
+ return this.sunlintToEslintMapping[sunlintRuleId] || [];
226
+ }
227
+
228
+ /**
229
+ * Get SunLint rule for ESLint rule ID
230
+ * Following Rule C006: Verb-noun naming
231
+ */
232
+ getSunLintRuleForEslintRule(eslintRuleId) {
233
+ return this.eslintToSunlintMapping[eslintRuleId] || eslintRuleId;
234
+ }
235
+
236
+ /**
237
+ * Get all supported SunLint rules
238
+ * Following Rule C006: Verb-noun naming
239
+ */
240
+ getSupportedSunLintRules() {
241
+ return Object.keys(this.sunlintToEslintMapping);
242
+ }
243
+
244
+ /**
245
+ * Get all supported ESLint rules
246
+ * Following Rule C006: Verb-noun naming
247
+ */
248
+ getSupportedEslintRules() {
249
+ return Object.keys(this.eslintToSunlintMapping);
250
+ }
251
+
252
+ /**
253
+ * Check if SunLint rule is supported by ESLint
254
+ * Following Rule C006: Verb-noun naming
255
+ */
256
+ isSunLintRuleSupportedByEslint(sunlintRuleId) {
257
+ return this.sunlintToEslintMapping.hasOwnProperty(sunlintRuleId);
258
+ }
259
+
260
+ /**
261
+ * Get rule description for SunLint rule
262
+ * Following Rule C015: Use domain language
263
+ */
264
+ getSunLintRuleDescription(sunlintRuleId) {
265
+ const descriptions = {
266
+ 'C005': 'Each function should do only one thing',
267
+ 'C006': 'Function names should be verb-noun',
268
+ 'C014': 'Use Dependency Injection instead of direct new',
269
+ 'C015': 'Use domain language in class/function names',
270
+ 'C019': 'Do not use error level logging for non-critical errors',
271
+ 'C031': 'Data validation logic should be separate',
272
+ 'C032': 'No external API calls in constructor or static block',
273
+ 'C033': 'Separate processing logic and data query in service layer',
274
+ 'C034': 'Limit direct access to global state in domain logic',
275
+ 'C035': 'When handling errors, log full relevant information',
276
+ 'C037': 'API handlers should return standard response objects',
277
+ 'C038': 'Avoid logic dependent on file/module loading order',
278
+ 'C040': 'Do not scatter validation logic across multiple classes',
279
+ };
280
+
281
+ return descriptions[sunlintRuleId] || 'No description available';
282
+ }
283
+
284
+ /**
285
+ * Get rule category for SunLint rule
286
+ * Following Rule C015: Use domain language
287
+ */
288
+ getSunLintRuleCategory(sunlintRuleId) {
289
+ const categories = {
290
+ 'C005': 'quality',
291
+ 'C006': 'naming',
292
+ 'C014': 'architecture',
293
+ 'C015': 'naming',
294
+ 'C019': 'logging',
295
+ 'C031': 'validation',
296
+ 'C032': 'architecture',
297
+ 'C033': 'architecture',
298
+ 'C034': 'architecture',
299
+ 'C035': 'logging',
300
+ 'C037': 'api',
301
+ 'C038': 'architecture',
302
+ 'C040': 'validation',
303
+ };
304
+
305
+ return categories[sunlintRuleId] || 'other';
306
+ }
307
+ }
308
+
309
+ module.exports = RuleMappingService;