@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,111 @@
1
+ name: Code Quality & Security Check
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main, develop]
6
+ push:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ # Parallel execution for faster CI
11
+ eslint:
12
+ name: ESLint Check
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ with:
17
+ fetch-depth: 0
18
+
19
+ - name: Setup Node.js
20
+ uses: actions/setup-node@v4
21
+ with:
22
+ node-version: '18'
23
+ cache: 'npm'
24
+
25
+ - name: Install dependencies
26
+ run: npm ci
27
+
28
+ - name: Run ESLint on changed files
29
+ if: github.event_name == 'pull_request'
30
+ run: |
31
+ CHANGED_FILES=$(git diff --name-only --diff-filter=AM origin/${{ github.base_ref }} | grep -E '\.(js|ts|tsx)$' | tr '\n' ' ')
32
+ if [ ! -z "$CHANGED_FILES" ]; then
33
+ npx eslint $CHANGED_FILES
34
+ else
35
+ echo "No JS/TS files changed"
36
+ fi
37
+
38
+ - name: Run ESLint full scan
39
+ if: github.event_name == 'push'
40
+ run: npm run lint:eslint
41
+
42
+ sunlint:
43
+ name: SunLint Security & Quality Check
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+ with:
48
+ fetch-depth: 0
49
+
50
+ - name: Setup Node.js
51
+ uses: actions/setup-node@v4
52
+ with:
53
+ node-version: '18'
54
+ cache: 'npm'
55
+
56
+ - name: Install dependencies
57
+ run: npm ci
58
+
59
+ - name: Install SunLint
60
+ run: npm install -g @sun/sunlint
61
+
62
+ - name: Run SunLint on changed files (PR)
63
+ if: github.event_name == 'pull_request'
64
+ run: |
65
+ sunlint --all --changed-files --diff-base=origin/${{ github.base_ref }} \
66
+ --format=github --fail-on-new-violations
67
+
68
+ - name: Run SunLint full scan (Main)
69
+ if: github.event_name == 'push'
70
+ run: |
71
+ sunlint --all --input=. --format=json \
72
+ --save-baseline=sunlint-baseline.json --output=sunlint-report.json
73
+
74
+ - name: Upload SunLint Baseline
75
+ if: github.event_name == 'push'
76
+ uses: actions/upload-artifact@v4
77
+ with:
78
+ name: sunlint-baseline
79
+ path: sunlint-baseline.json
80
+
81
+ - name: Upload SunLint Report
82
+ if: always()
83
+ uses: actions/upload-artifact@v4
84
+ with:
85
+ name: sunlint-report
86
+ path: sunlint-report.json
87
+
88
+ # Combined summary job
89
+ quality-gate:
90
+ name: Quality Gate
91
+ runs-on: ubuntu-latest
92
+ needs: [eslint, sunlint]
93
+ if: always()
94
+ steps:
95
+ - name: Check ESLint Status
96
+ if: needs.eslint.result != 'success'
97
+ run: |
98
+ echo "❌ ESLint check failed"
99
+ exit 1
100
+
101
+ - name: Check SunLint Status
102
+ if: needs.sunlint.result != 'success'
103
+ run: |
104
+ echo "❌ SunLint check failed"
105
+ exit 1
106
+
107
+ - name: Quality Gate Passed
108
+ run: |
109
+ echo "✅ All quality checks passed!"
110
+ echo "📊 ESLint: ${{ needs.eslint.result }}"
111
+ echo "🔒 SunLint: ${{ needs.sunlint.result }}"
@@ -0,0 +1,42 @@
1
+ {
2
+ "extends": ["@sun/sunlint/recommended"],
3
+ "rules": {
4
+ // SunLint Security Rules
5
+ "S003": "warn", // URL Redirect Validation
6
+ "S047": "warn", // Secure Random Password Generation
7
+ "S055": "warn", // REST Content-Type Verification
8
+ "C006": "error", // Function Naming Convention
9
+ "C019": "error" // Log Level Usage
10
+ },
11
+ "eslint": {
12
+ "enabled": true,
13
+ "runBefore": true,
14
+ "configPath": ".eslintrc.js",
15
+ "rules": {
16
+ // Keep existing ESLint rules
17
+ "no-unused-vars": "error",
18
+ "prefer-const": "error",
19
+ "no-var": "error",
20
+ "@typescript-eslint/no-explicit-any": "warn"
21
+ }
22
+ },
23
+ "ignorePatterns": [
24
+ "**/node_modules/**",
25
+ "**/dist/**",
26
+ "**/build/**",
27
+ "**/coverage/**",
28
+ "**/*.d.ts"
29
+ ],
30
+ "languages": ["typescript", "javascript"],
31
+ "include": ["src/**", "tests/**"],
32
+ "exclude": ["**/node_modules/**"],
33
+ "output": {
34
+ "format": "eslint",
35
+ "console": true,
36
+ "summary": true
37
+ },
38
+ "ai": {
39
+ "enabled": false,
40
+ "fallbackToPattern": true
41
+ }
42
+ }
@@ -0,0 +1,47 @@
1
+ # SunLint Configuration Examples
2
+
3
+ This directory contains example configurations and workflows for SunLint integration.
4
+
5
+ ## Configuration Examples
6
+
7
+ ### 📋 **Project Configuration**
8
+ - **`.sunlint.json`** - Complete project configuration example
9
+ - **`package.json`** - NPM scripts and dependencies setup
10
+
11
+ ### 🚀 **CI/CD Examples**
12
+ - **`.github/workflows/code-quality.yml`** - GitHub Actions workflow
13
+
14
+ ## Usage
15
+
16
+ ### Copy Configuration
17
+ ```bash
18
+ # Copy base configuration to your project
19
+ cp examples/.sunlint.json your-project/.sunlint.json
20
+
21
+ # Copy GitHub Actions workflow
22
+ cp examples/.github/workflows/code-quality.yml .github/workflows/
23
+ ```
24
+
25
+ ### Customize Rules
26
+ ```json
27
+ {
28
+ "extends": ["@sun/sunlint/recommended"],
29
+ "rules": {
30
+ "C006": "error", // Function naming
31
+ "C019": "error", // Log level usage
32
+ "S005": "warn", // Security rules
33
+ "S012": "error" // No hardcoded secrets
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## Integration Patterns
39
+
40
+ 1. **TypeScript Projects**: Use with ESLint integration
41
+ 2. **CI/CD**: GitHub Actions with changed files only
42
+ 3. **Pre-commit**: Staged files validation
43
+ 4. **Full scan**: Baseline creation for large projects
44
+
45
+ ---
46
+
47
+ **For test projects, see `test/` directory**
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "example-project",
3
+ "version": "1.0.0",
4
+ "scripts": {
5
+ "lint": "npm run lint:eslint && npm run lint:sunlint",
6
+ "lint:eslint": "eslint src/",
7
+ "lint:sunlint": "sunlint --all --input=src",
8
+ "lint:changed": "npm run lint:eslint:changed && npm run lint:sunlint:changed",
9
+ "lint:eslint:changed": "eslint $(git diff --name-only --diff-filter=AM origin/main | grep -E '\\.(js|ts|tsx)$' | tr '\\n' ' ')",
10
+ "lint:sunlint:changed": "sunlint --all --changed-files --diff-base=origin/main",
11
+ "lint:fix": "npm run lint:eslint:fix && npm run lint:sunlint:fix",
12
+ "lint:eslint:fix": "eslint src/ --fix",
13
+ "lint:sunlint:fix": "sunlint --all --input=src --fix"
14
+ },
15
+ "devDependencies": {
16
+ "@sun/sunlint": "^1.0.5",
17
+ "eslint": "^8.57.0"
18
+ },
19
+ "sunlint": {
20
+ "extends": ["@sun/sunlint/recommended"],
21
+ "rules": {
22
+ "S003": "warn",
23
+ "C006": "error",
24
+ "S047": "warn"
25
+ },
26
+ "ignorePatterns": ["**/test/**", "**/cypress/**"],
27
+ "eslint": {
28
+ "enabled": true,
29
+ "runBefore": true,
30
+ "configPath": ".eslintrc.js"
31
+ }
32
+ }
33
+ }
package/package.json ADDED
@@ -0,0 +1,100 @@
1
+ {
2
+ "name": "@sun-asterisk/sunlint",
3
+ "version": "1.0.5",
4
+ "description": "☀️ Sun Lint - Universal Coding Standards | Multi-rule Quality & Security Analysis with ESLint Integration",
5
+ "main": "cli.js",
6
+ "bin": {
7
+ "sunlint": "./cli.js"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/sun-asterisk/engineer-excellence.git",
12
+ "directory": "coding-quality/extensions/sunlint"
13
+ },
14
+ "publishConfig": {
15
+ "registry": "https://registry.npmjs.org",
16
+ "access": "public"
17
+ },
18
+ "scripts": {
19
+ "test": "npm run test:unit && npm run test:integration",
20
+ "test:unit": "node test/unit/test-runner.js",
21
+ "test:integration": "node test/integration/test-runner.js",
22
+ "test:c019": "node cli.js --rule=C019 --input=test/fixtures --format=eslint",
23
+ "test:c006": "node cli.js --rule=C006 --input=test/fixtures --format=eslint",
24
+ "test:c029": "node cli.js --rule=C029 --input=test/fixtures/c029 --format=eslint",
25
+ "test:multi": "node cli.js --rules=C019,C006,C029 --input=test/fixtures --format=eslint",
26
+ "test:all": "node cli.js --all --input=test/fixtures --format=eslint",
27
+ "test:quality": "node cli.js --category=quality --input=test/fixtures --format=eslint",
28
+ "test:security": "node cli.js --category=security --input=test/fixtures --format=eslint",
29
+ "demo": "./demo.sh",
30
+ "demo:single": "node cli.js --rule=C019 --input=test/fixtures/typescript --format=eslint",
31
+ "demo:multi": "node cli.js --rules=C019,C006 --input=test/fixtures --format=summary",
32
+ "demo:quality": "node cli.js --quality --input=test/fixtures --format=summary",
33
+ "demo:security": "node cli.js --security --input=test/fixtures --format=summary",
34
+ "demo:all": "node cli.js --all --input=test/fixtures --format=summary",
35
+ "demo:config": "node cli.js --config=.sunlint.json --input=test/fixtures",
36
+ "demo:eslint-integration": "./demo-eslint-integration.sh",
37
+ "lint": "node cli.js --config=.sunlint.json --input=.",
38
+ "lint:eslint-integration": "node cli.js --all --eslint-integration --input=.",
39
+ "build": "echo 'No build step required for Node.js CLI'",
40
+ "clean": "rm -rf coverage/ *.log reports/ *.tgz",
41
+ "start": "node cli.js --help",
42
+ "version": "node cli.js --version",
43
+ "pack": "npm pack",
44
+ "publish:github": "npm publish --registry=https://npm.pkg.github.com",
45
+ "publish:npmjs": "npm publish --registry=https://registry.npmjs.org",
46
+ "publish:test": "npm publish --dry-run --registry=https://registry.npmjs.org",
47
+ "prepublishOnly": "npm run clean"
48
+ },
49
+ "keywords": [
50
+ "linting",
51
+ "code-quality",
52
+ "security",
53
+ "typescript",
54
+ "eslint",
55
+ "eslint-integration",
56
+ "coding-standards",
57
+ "static-analysis",
58
+ "git-integration",
59
+ "ci-cd",
60
+ "sun-asterisk"
61
+ ],
62
+ "author": "Sun* Engineering Team",
63
+ "license": "MIT",
64
+ "files": [
65
+ "cli.js",
66
+ "cli-legacy.js",
67
+ "core/",
68
+ "rules/",
69
+ "config/",
70
+ "eslint-integration/",
71
+ "docs/",
72
+ "examples/",
73
+ "README.md",
74
+ "LICENSE",
75
+ "CHANGELOG.md"
76
+ ],
77
+ "engines": {
78
+ "node": ">=18.18.0"
79
+ },
80
+ "dependencies": {
81
+ "@typescript-eslint/eslint-plugin": "^8.36.0",
82
+ "@typescript-eslint/parser": "^8.36.0",
83
+ "chalk": "^4.1.2",
84
+ "commander": "^12.1.0",
85
+ "eslint": "^9.30.0",
86
+ "glob": "^11.0.0",
87
+ "minimatch": "^10.0.3",
88
+ "node-fetch": "^3.3.2",
89
+ "table": "^6.8.2",
90
+ "typescript": "^5.7.2"
91
+ },
92
+ "devDependencies": {
93
+ "@types/node": "^22.10.1",
94
+ "jest": "^29.7.0"
95
+ },
96
+ "bugs": {
97
+ "url": "https://github.com/sun-asterisk/engineer-excellence/issues"
98
+ },
99
+ "homepage": "https://github.com/sun-asterisk/engineer-excellence/tree/main/coding-quality/extensions/sunlint#readme"
100
+ }
@@ -0,0 +1,338 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const ts = require('typescript');
4
+
5
+ class C006Analyzer {
6
+ constructor() {
7
+ this.ruleId = 'C006';
8
+ this.ruleName = 'Function Naming Convention';
9
+ this.description = 'Tên hàm phải là động từ/verb-noun pattern';
10
+ }
11
+
12
+ async analyze(files, language, config) {
13
+ const violations = [];
14
+
15
+ for (const filePath of files) {
16
+ try {
17
+ const fileContent = fs.readFileSync(filePath, 'utf8');
18
+ const fileViolations = await this.analyzeFile(filePath, fileContent, language, config);
19
+ violations.push(...fileViolations);
20
+ } catch (error) {
21
+ console.error(`Error analyzing file ${filePath}:`, error.message);
22
+ }
23
+ }
24
+
25
+ return violations;
26
+ }
27
+
28
+ async analyzeFile(filePath, content, language, config) {
29
+ switch (language) {
30
+ case 'typescript':
31
+ case 'javascript':
32
+ return this.analyzeTypeScript(filePath, content, config);
33
+ case 'dart':
34
+ return this.analyzeDart(filePath, content, config);
35
+ case 'kotlin':
36
+ return this.analyzeKotlin(filePath, content, config);
37
+ default:
38
+ return [];
39
+ }
40
+ }
41
+
42
+ async analyzeTypeScript(filePath, content, config) {
43
+ const violations = [];
44
+ const lines = content.split('\n');
45
+
46
+ // Parse TypeScript/JavaScript code
47
+ const sourceFile = ts.createSourceFile(
48
+ filePath,
49
+ content,
50
+ ts.ScriptTarget.Latest,
51
+ true
52
+ );
53
+
54
+ const visit = (node) => {
55
+ // Check function declarations (skip declare statements without body)
56
+ if (ts.isFunctionDeclaration(node) && node.name && node.body) {
57
+ const functionName = node.name.text;
58
+ const analysis = this.analyzeFunctionName(functionName);
59
+
60
+ if (analysis.isViolation) {
61
+ const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
62
+ const column = sourceFile.getLineAndCharacterOfPosition(node.getStart()).character + 1;
63
+ const lineText = lines[line - 1]?.trim() || '';
64
+
65
+ violations.push({
66
+ ruleId: this.ruleId,
67
+ file: filePath,
68
+ line,
69
+ column,
70
+ message: analysis.reason,
71
+ severity: analysis.severity || 'warning',
72
+ code: lineText,
73
+ type: analysis.type,
74
+ confidence: analysis.confidence || 0.8,
75
+ suggestion: analysis.suggestion
76
+ });
77
+ }
78
+ }
79
+
80
+ // Check method declarations
81
+ if (ts.isMethodDeclaration(node) && node.name) {
82
+ const methodName = node.name.text;
83
+ const analysis = this.analyzeFunctionName(methodName);
84
+
85
+ if (analysis.isViolation) {
86
+ const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
87
+ const column = sourceFile.getLineAndCharacterOfPosition(node.getStart()).character + 1;
88
+ const lineText = lines[line - 1]?.trim() || '';
89
+
90
+ violations.push({
91
+ ruleId: this.ruleId,
92
+ file: filePath,
93
+ line,
94
+ column,
95
+ message: analysis.reason,
96
+ severity: analysis.severity || 'warning',
97
+ code: lineText,
98
+ type: analysis.type,
99
+ confidence: analysis.confidence || 0.8,
100
+ suggestion: analysis.suggestion
101
+ });
102
+ }
103
+ }
104
+
105
+ // Check arrow functions assigned to variables
106
+ if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name) &&
107
+ node.initializer && ts.isArrowFunction(node.initializer)) {
108
+ const functionName = node.name.text;
109
+ const analysis = this.analyzeFunctionName(functionName);
110
+
111
+ if (analysis.isViolation) {
112
+ const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
113
+ const column = sourceFile.getLineAndCharacterOfPosition(node.getStart()).character + 1;
114
+ const lineText = lines[line - 1]?.trim() || '';
115
+
116
+ violations.push({
117
+ ruleId: this.ruleId,
118
+ file: filePath,
119
+ line,
120
+ column,
121
+ message: analysis.reason,
122
+ severity: analysis.severity || 'warning',
123
+ code: lineText,
124
+ type: analysis.type,
125
+ confidence: analysis.confidence || 0.8,
126
+ suggestion: analysis.suggestion
127
+ });
128
+ }
129
+ }
130
+
131
+ ts.forEachChild(node, visit);
132
+ };
133
+
134
+ visit(sourceFile);
135
+ return violations;
136
+ }
137
+
138
+ async analyzeDart(filePath, content, config) {
139
+ const violations = [];
140
+ const lines = content.split('\n');
141
+
142
+ // Pattern-based analysis for Dart function declarations
143
+ const functionPattern = /^\s*(\w+\s+)?(\w+)\s*\([^)]*\)\s*\{?/;
144
+
145
+ lines.forEach((line, index) => {
146
+ const lineNumber = index + 1;
147
+ const match = line.match(functionPattern);
148
+
149
+ if (match) {
150
+ const functionName = match[2];
151
+
152
+ // Skip constructors, getters, setters, and built-in methods
153
+ if (this.isSpecialFunction(functionName)) {
154
+ return;
155
+ }
156
+
157
+ const analysis = this.analyzeFunctionName(functionName);
158
+
159
+ if (analysis.isViolation) {
160
+ violations.push({
161
+ ruleId: this.ruleId,
162
+ file: filePath,
163
+ line: lineNumber,
164
+ column: line.indexOf(functionName) + 1,
165
+ message: analysis.reason,
166
+ severity: analysis.severity || 'warning',
167
+ code: line.trim(),
168
+ type: analysis.type,
169
+ confidence: analysis.confidence || 0.7,
170
+ suggestion: analysis.suggestion
171
+ });
172
+ }
173
+ }
174
+ });
175
+
176
+ return violations;
177
+ }
178
+
179
+ async analyzeKotlin(filePath, content, config) {
180
+ const violations = [];
181
+ const lines = content.split('\n');
182
+
183
+ // Pattern-based analysis for Kotlin function declarations
184
+ const functionPattern = /^\s*(?:fun|private\s+fun|public\s+fun|internal\s+fun|override\s+fun)\s+(\w+)\s*\(/;
185
+
186
+ lines.forEach((line, index) => {
187
+ const lineNumber = index + 1;
188
+ const match = line.match(functionPattern);
189
+
190
+ if (match) {
191
+ const functionName = match[1];
192
+
193
+ // Skip constructors and built-in methods
194
+ if (this.isSpecialFunction(functionName)) {
195
+ return;
196
+ }
197
+
198
+ const analysis = this.analyzeFunctionName(functionName);
199
+
200
+ if (analysis.isViolation) {
201
+ violations.push({
202
+ ruleId: this.ruleId,
203
+ file: filePath,
204
+ line: lineNumber,
205
+ column: line.indexOf(functionName) + 1,
206
+ message: analysis.reason,
207
+ severity: analysis.severity || 'warning',
208
+ code: line.trim(),
209
+ type: analysis.type,
210
+ confidence: analysis.confidence || 0.7,
211
+ suggestion: analysis.suggestion
212
+ });
213
+ }
214
+ }
215
+ });
216
+
217
+ return violations;
218
+ }
219
+
220
+ analyzeFunctionName(functionName) {
221
+ // Skip special functions
222
+ if (this.isSpecialFunction(functionName)) {
223
+ return { isViolation: false };
224
+ }
225
+
226
+ // Common verbs for function names
227
+ const commonVerbs = [
228
+ 'get', 'set', 'is', 'has', 'can', 'should', 'will',
229
+ 'create', 'build', 'make', 'generate', 'construct',
230
+ 'update', 'modify', 'change', 'edit', 'alter',
231
+ 'delete', 'remove', 'destroy', 'clean', 'clear',
232
+ 'load', 'save', 'fetch', 'retrieve', 'find', 'search',
233
+ 'validate', 'verify', 'check', 'confirm', 'ensure',
234
+ 'calculate', 'compute', 'process', 'handle', 'manage',
235
+ 'send', 'receive', 'transmit', 'broadcast', 'emit',
236
+ 'parse', 'format', 'transform', 'convert', 'map',
237
+ 'filter', 'sort', 'group', 'merge', 'split',
238
+ 'connect', 'disconnect', 'open', 'close', 'start', 'stop',
239
+ 'show', 'hide', 'display', 'render', 'draw', 'paint',
240
+ 'add', 'append', 'insert', 'push', 'pop', 'shift',
241
+ 'test', 'debug', 'log', 'trace', 'monitor', 'watch',
242
+ 'count', 'execute', 'perform', 'run', 'invoke', 'call',
243
+ 'reset', 'initialize', 'setup', 'configure', 'prepare',
244
+ 'refresh', 'restore', 'reload', 'retry', 'resume', 'redirect',
245
+ 'select', 'toggle', 'switch', 'enable', 'disable', 'activate',
246
+ 'expand', 'collapse', 'scroll', 'navigate', 'submit', 'cancel'
247
+ ];
248
+
249
+ // Check if function name starts with a verb
250
+ const startsWithVerb = commonVerbs.some(verb => {
251
+ const verbPattern = new RegExp(`^${verb}[A-Z]?`, 'i');
252
+ return verbPattern.test(functionName);
253
+ });
254
+
255
+ if (startsWithVerb) {
256
+ return { isViolation: false };
257
+ }
258
+
259
+ // Check for camelCase pattern that might be verb-noun
260
+ const camelCasePattern = /^[a-z][a-zA-Z0-9]*$/;
261
+ const pascalCasePattern = /^[A-Z][a-zA-Z0-9]*$/;
262
+
263
+ // PascalCase functions (likely React components) - different rules
264
+ if (pascalCasePattern.test(functionName)) {
265
+ // For React components, we can be more lenient but still suggest verb patterns where appropriate
266
+ return {
267
+ isViolation: true,
268
+ reason: 'PascalCase function should be a React component or use verb-noun pattern',
269
+ severity: 'info',
270
+ type: 'react_component_naming',
271
+ confidence: 0.5,
272
+ suggestion: `If this is a React component, consider using descriptive names. If it's a function, use camelCase with verb-noun pattern.`
273
+ };
274
+ }
275
+
276
+ if (!camelCasePattern.test(functionName)) {
277
+ return {
278
+ isViolation: true,
279
+ reason: 'Function name should follow camelCase and verb-noun pattern',
280
+ severity: 'warning',
281
+ type: 'naming_convention',
282
+ confidence: 0.9,
283
+ suggestion: 'Use camelCase with verb-noun pattern (e.g., getUserData, validateInput)'
284
+ };
285
+ }
286
+
287
+ // Check for noun-only patterns (likely violations)
288
+ const nounOnlyPatterns = [
289
+ /^[a-z]+$/, // simple lowercase (e.g., 'user', 'data')
290
+ /^[a-z]+[A-Z][a-z]+$/, // simple camelCase noun (e.g., 'userData', 'userInfo')
291
+ ];
292
+
293
+ const isNounOnly = nounOnlyPatterns.some(pattern => pattern.test(functionName));
294
+
295
+ if (isNounOnly) {
296
+ return {
297
+ isViolation: true,
298
+ reason: 'Function name appears to be a noun - should start with a verb',
299
+ severity: 'warning',
300
+ type: 'missing_verb',
301
+ confidence: 0.8,
302
+ suggestion: `Consider renaming to use verb-noun pattern (e.g., get${functionName.charAt(0).toUpperCase() + functionName.slice(1)})`
303
+ };
304
+ }
305
+
306
+ // If it's a complex name without obvious verb, flag as potential violation
307
+ if (functionName.length > 3 && !functionName.match(/^[a-z]+[A-Z]/)) {
308
+ return {
309
+ isViolation: true,
310
+ reason: 'Function name should follow verb-noun pattern for clarity',
311
+ severity: 'info',
312
+ type: 'unclear_naming',
313
+ confidence: 0.6,
314
+ suggestion: 'Consider using verb-noun pattern for better readability'
315
+ };
316
+ }
317
+
318
+ return { isViolation: false };
319
+ }
320
+
321
+ isSpecialFunction(name) {
322
+ const specialFunctions = [
323
+ 'constructor', 'toString', 'valueOf', 'toJSON',
324
+ 'main', 'init', 'setup', 'teardown', 'build',
325
+ 'onCreate', 'onDestroy', 'onStart', 'onStop',
326
+ 'onPause', 'onResume', 'onSaveInstanceState',
327
+ 'equals', 'hashCode', 'compareTo', 'clone',
328
+ 'finalize', 'notify', 'notifyAll', 'wait'
329
+ ];
330
+
331
+ return specialFunctions.includes(name) ||
332
+ name.startsWith('_') ||
333
+ name.startsWith('$');
334
+ // Removed: || name.match(/^[A-Z]/); // Allow checking React components
335
+ }
336
+ }
337
+
338
+ module.exports = new C006Analyzer();