@sun-asterisk/sunlint 1.0.6 → 1.1.3

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 (314) hide show
  1. package/.sunlint.json +35 -0
  2. package/CHANGELOG.md +135 -169
  3. package/CONTRIBUTING.md +235 -0
  4. package/PROJECT_STRUCTURE.md +60 -0
  5. package/README.md +77 -50
  6. package/cli.js +1 -0
  7. package/config/README.md +88 -0
  8. package/config/defaults/ai-rules-context.json +231 -0
  9. package/config/engines/engines.json +49 -0
  10. package/config/engines/eslint-rule-mapping.json +74 -0
  11. package/config/eslint-rule-mapping.json +126 -0
  12. package/config/{typescript/eslint.config.js → integrations/eslint/typescript.config.js} +4 -0
  13. package/config/presets/beginner.json +1 -1
  14. package/config/presets/ci.json +3 -2
  15. package/config/presets/recommended.json +1 -1
  16. package/config/presets/strict.json +2 -2
  17. package/config/rule-analysis-strategies.js +74 -0
  18. package/config/{rules-registry.json → rules/rules-registry.json} +82 -0
  19. package/core/analysis-orchestrator.js +383 -591
  20. package/core/ast-modules/README.md +103 -0
  21. package/core/ast-modules/base-parser.js +90 -0
  22. package/core/ast-modules/index.js +97 -0
  23. package/core/ast-modules/package.json +37 -0
  24. package/core/ast-modules/parsers/eslint-js-parser.js +147 -0
  25. package/core/ast-modules/parsers/eslint-ts-parser.js +106 -0
  26. package/core/ast-modules/parsers/javascript-parser.js +187 -0
  27. package/core/ast-modules/parsers/typescript-parser.js +187 -0
  28. package/core/cli-action-handler.js +271 -255
  29. package/core/cli-program.js +18 -4
  30. package/core/config-manager.js +18 -11
  31. package/core/config-merger.js +52 -1
  32. package/core/config-validator.js +2 -2
  33. package/core/enhanced-rules-registry.js +331 -0
  34. package/core/file-targeting-service.js +93 -29
  35. package/core/interfaces/analysis-engine.interface.js +100 -0
  36. package/core/multi-rule-runner.js +0 -221
  37. package/core/output-service.js +1 -1
  38. package/core/rule-mapping-service.js +9 -1
  39. package/core/rule-selection-service.js +10 -2
  40. package/docs/CONFIGURATION.md +414 -0
  41. package/docs/DEPLOYMENT-STRATEGIES.md +270 -0
  42. package/engines/eslint-engine.js +601 -0
  43. package/engines/heuristic-engine.js +860 -0
  44. package/engines/openai-engine.js +374 -0
  45. package/integrations/eslint/README.md +99 -0
  46. package/{eslint-integration → integrations/eslint/configs}/.eslintrc.js +1 -1
  47. package/integrations/eslint/configs/eslint.config.js +133 -0
  48. package/integrations/eslint/configs/eslint.config.simple.js +24 -0
  49. package/integrations/eslint/plugin/index.js +164 -0
  50. package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c006-function-name-verb-noun.js +11 -2
  51. package/integrations/eslint/plugin/rules/common/c013-no-dead-code.js +78 -0
  52. package/integrations/eslint/plugin/rules/common/c017-limit-constructor-logic.js +146 -0
  53. package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c029-catch-block-logging.js +35 -0
  54. package/integrations/eslint/plugin/rules/common/c035-no-empty-catch.js +162 -0
  55. package/integrations/eslint/plugin/rules/common/c041-no-config-inline.js +122 -0
  56. package/integrations/eslint/plugin/rules/common/c072-one-assert-per-test.js +184 -0
  57. package/integrations/eslint/plugin/rules/common/c075-explicit-function-return-types.js +168 -0
  58. package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +254 -0
  59. package/integrations/eslint/plugin/rules/security/s001-fail-securely.js +381 -0
  60. package/integrations/eslint/plugin/rules/security/s002-idor-check.js +945 -0
  61. package/integrations/eslint/plugin/rules/security/s007-no-plaintext-otp.js +74 -0
  62. package/integrations/eslint/plugin/rules/security/s013-verify-tls-connection.js +47 -0
  63. package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/typescript}/t003-ts-ignore-reason.js +3 -3
  64. package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/typescript}/t007-no-fn-in-constructor.js +1 -1
  65. package/integrations/eslint/plugin/rules/typescript/t019-no-this-assign.js +81 -0
  66. package/integrations/eslint/plugin/rules/typescript/t020-no-default-multi-export.js +127 -0
  67. package/integrations/eslint/plugin/rules/typescript/t021-limit-nested-generics.js +150 -0
  68. package/integrations/eslint/test-c041-rule.js +87 -0
  69. package/package.json +29 -19
  70. package/rules/README.md +252 -0
  71. package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
  72. package/rules/common/C002_no_duplicate_code/config.json +23 -0
  73. package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
  74. package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
  75. package/rules/{C006_function_naming → common/C006_function_naming}/analyzer.js +13 -2
  76. package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
  77. package/rules/common/C013_no_dead_code/analyzer.js +206 -0
  78. package/rules/common/C014_dependency_injection/analyzer.js +338 -0
  79. package/rules/common/C017_constructor_logic/analyzer.js +314 -0
  80. package/rules/{C019_log_level_usage → common/C019_log_level_usage}/analyzer.js +5 -2
  81. package/rules/{C029_catch_block_logging → common/C029_catch_block_logging}/analyzer.js +49 -15
  82. package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
  83. package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
  84. package/rules/common/C043_no_console_or_print/analyzer.js +304 -0
  85. package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +351 -0
  86. package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
  87. package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
  88. package/rules/docs/C002_no_duplicate_code.md +57 -0
  89. package/rules/index.js +149 -0
  90. package/rules/migration/converter.js +385 -0
  91. package/rules/migration/mapping.json +164 -0
  92. package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
  93. package/rules/security/S026_json_schema_validation/config.json +27 -0
  94. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +263 -0
  95. package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
  96. package/rules/security/S029_csrf_protection/analyzer.js +264 -0
  97. package/rules/tests/C002_no_duplicate_code.test.js +50 -0
  98. package/rules/utils/ast-utils.js +191 -0
  99. package/rules/utils/base-analyzer.js +98 -0
  100. package/rules/utils/pattern-matchers.js +239 -0
  101. package/rules/utils/rule-helpers.js +264 -0
  102. package/rules/utils/severity-constants.js +93 -0
  103. package/scripts/build-release.sh +117 -0
  104. package/scripts/ci-report.js +179 -0
  105. package/scripts/install.sh +196 -0
  106. package/scripts/manual-release.sh +338 -0
  107. package/scripts/merge-reports.js +424 -0
  108. package/scripts/pre-release-test.sh +175 -0
  109. package/scripts/prepare-release.sh +202 -0
  110. package/scripts/setup-github-registry.sh +42 -0
  111. package/scripts/test-scripts/README.md +22 -0
  112. package/scripts/test-scripts/test-c041-comparison.js +114 -0
  113. package/scripts/test-scripts/test-c041-eslint.js +67 -0
  114. package/scripts/test-scripts/test-eslint-rules.js +146 -0
  115. package/scripts/test-scripts/test-real-world.js +44 -0
  116. package/scripts/test-scripts/test-rules-on-real-projects.js +86 -0
  117. package/scripts/trigger-release.sh +285 -0
  118. package/scripts/validate-rule-structure.js +148 -0
  119. package/scripts/verify-install.sh +82 -0
  120. package/cli-legacy.js +0 -355
  121. package/config/sunlint-schema.json +0 -166
  122. package/config/typescript/custom-rules-new.js +0 -0
  123. package/config/typescript/custom-rules.js +0 -9
  124. package/config/typescript/package-lock.json +0 -1585
  125. package/config/typescript/package.json +0 -13
  126. package/config/typescript/security-rules/index.js +0 -90
  127. package/config/typescript/security-rules/s005-no-origin-auth.js +0 -95
  128. package/config/typescript/security-rules/s006-activation-recovery-secret-not-plaintext.js +0 -69
  129. package/config/typescript/security-rules/s008-crypto-agility.js +0 -62
  130. package/config/typescript/security-rules/s009-no-insecure-crypto.js +0 -103
  131. package/config/typescript/security-rules/s010-no-insecure-random-in-sensitive-context.js +0 -123
  132. package/config/typescript/security-rules/s011-no-insecure-uuid.js +0 -66
  133. package/config/typescript/security-rules/s012-hardcode-secret.js +0 -71
  134. package/config/typescript/security-rules/s014-insecure-tls-version.js +0 -50
  135. package/config/typescript/security-rules/s015-insecure-tls-certificate.js +0 -43
  136. package/config/typescript/security-rules/s016-sensitive-query-parameter.js +0 -59
  137. package/config/typescript/security-rules/s017-no-sql-injection.js +0 -193
  138. package/config/typescript/security-rules/s018-positive-input-validation.js +0 -56
  139. package/config/typescript/security-rules/s019-no-raw-user-input-in-email.js +0 -113
  140. package/config/typescript/security-rules/s020-no-eval-dynamic-execution.js +0 -89
  141. package/config/typescript/security-rules/s022-output-encoding.js +0 -78
  142. package/config/typescript/security-rules/s023-no-json-injection.js +0 -300
  143. package/config/typescript/security-rules/s025-server-side-input-validation.js +0 -217
  144. package/config/typescript/security-rules/s026-json-schema-validation.js +0 -68
  145. package/config/typescript/security-rules/s027-no-hardcoded-secrets.js +0 -80
  146. package/config/typescript/security-rules/s029-require-csrf-protection.js +0 -79
  147. package/config/typescript/security-rules/s030-no-directory-browsing.js +0 -78
  148. package/config/typescript/security-rules/s033-require-samesite-cookie.js +0 -80
  149. package/config/typescript/security-rules/s034-require-host-cookie-prefix.js +0 -77
  150. package/config/typescript/security-rules/s035-cookie-specific-path.js +0 -74
  151. package/config/typescript/security-rules/s036-no-unsafe-file-include.js +0 -68
  152. package/config/typescript/security-rules/s037-require-anti-cache-headers.js +0 -70
  153. package/config/typescript/security-rules/s038-no-version-disclosure.js +0 -74
  154. package/config/typescript/security-rules/s039-no-session-token-in-url.js +0 -63
  155. package/config/typescript/security-rules/s041-require-session-invalidate-on-logout.js +0 -211
  156. package/config/typescript/security-rules/s042-require-periodic-reauthentication.js +0 -294
  157. package/config/typescript/security-rules/s043-terminate-sessions-on-password-change.js +0 -254
  158. package/config/typescript/security-rules/s044-require-full-session-for-sensitive-operations.js +0 -292
  159. package/config/typescript/security-rules/s045-anti-automation-controls.js +0 -46
  160. package/config/typescript/security-rules/s046-secure-notification-on-auth-change.js +0 -44
  161. package/config/typescript/security-rules/s048-password-credential-recovery.js +0 -54
  162. package/config/typescript/security-rules/s050-session-token-weak-hash.js +0 -94
  163. package/config/typescript/security-rules/s052-secure-random-authentication-code.js +0 -66
  164. package/config/typescript/security-rules/s054-verification-default-account.js +0 -109
  165. package/config/typescript/security-rules/s057-utc-logging.js +0 -54
  166. package/config/typescript/security-rules/s058-no-ssrf.js +0 -73
  167. package/config/typescript/tsconfig.json +0 -29
  168. package/core/ai-analyzer.js +0 -169
  169. package/core/eslint-engine-service.js +0 -312
  170. package/core/eslint-instance-manager.js +0 -104
  171. package/core/eslint-integration-service.js +0 -363
  172. package/core/sunlint-engine-service.js +0 -23
  173. package/core/typescript-analyzer.js +0 -262
  174. package/core/typescript-engine.js +0 -313
  175. package/docs/ENHANCED_FILE_TARGETING.md +0 -0
  176. package/docs/FILE_TARGETING_COMPARISON.md +0 -0
  177. package/docs/RULE-RESPONSIBILITY-MATRIX.md +0 -204
  178. package/eslint-integration/cli.js +0 -35
  179. package/eslint-integration/eslint-plugin-custom/c013-no-dead-code.js +0 -43
  180. package/eslint-integration/eslint-plugin-custom/c017-limit-constructor-logic.js +0 -39
  181. package/eslint-integration/eslint-plugin-custom/c027-limit-function-nesting.js +0 -50
  182. package/eslint-integration/eslint-plugin-custom/c034-no-implicit-return.js +0 -34
  183. package/eslint-integration/eslint-plugin-custom/c035-no-empty-catch.js +0 -32
  184. package/eslint-integration/eslint-plugin-custom/c041-no-config-inline.js +0 -64
  185. package/eslint-integration/eslint-plugin-custom/c048-no-var-declaration.js +0 -31
  186. package/eslint-integration/eslint-plugin-custom/index.js +0 -155
  187. package/eslint-integration/eslint-plugin-custom/package.json.bak +0 -9
  188. package/eslint-integration/eslint-plugin-custom/t004-interface-public-only.js +0 -160
  189. package/eslint-integration/eslint-plugin-custom/t011-no-real-time-dependency.js +0 -175
  190. package/eslint-integration/eslint-plugin-custom/t026-limit-nested-generics.js +0 -377
  191. package/eslint-integration/sample.ts +0 -53
  192. package/eslint-integration/test-s003.js +0 -5
  193. package/examples/.github/workflows/code-quality.yml +0 -111
  194. package/examples/README.md +0 -69
  195. package/examples/basic-typescript-demo/.eslintrc.json +0 -18
  196. package/examples/basic-typescript-demo/.next/cache/eslint/.cache_1othrmo +0 -1
  197. package/examples/basic-typescript-demo/.sunlint.json +0 -29
  198. package/examples/basic-typescript-demo/eslint.config.mjs +0 -37
  199. package/examples/basic-typescript-demo/next-env.d.ts +0 -5
  200. package/examples/basic-typescript-demo/next.config.mjs +0 -4
  201. package/examples/basic-typescript-demo/package-lock.json +0 -5656
  202. package/examples/basic-typescript-demo/package.json +0 -34
  203. package/examples/basic-typescript-demo/src/app/layout.tsx +0 -18
  204. package/examples/basic-typescript-demo/src/app/page.tsx +0 -48
  205. package/examples/basic-typescript-demo/src/config.ts +0 -14
  206. package/examples/basic-typescript-demo/src/good-practices.ts +0 -58
  207. package/examples/basic-typescript-demo/src/types.generated.ts +0 -13
  208. package/examples/basic-typescript-demo/src/user.test.ts +0 -19
  209. package/examples/basic-typescript-demo/src/violations.ts +0 -61
  210. package/examples/basic-typescript-demo/tsconfig.json +0 -27
  211. package/examples/eslint-integration-demo/.eslintrc.js +0 -38
  212. package/examples/eslint-integration-demo/.sunlint.json +0 -42
  213. package/examples/eslint-integration-demo/next-env.d.ts +0 -5
  214. package/examples/eslint-integration-demo/next.config.js +0 -8
  215. package/examples/eslint-integration-demo/package-lock.json +0 -5740
  216. package/examples/eslint-integration-demo/package.json +0 -37
  217. package/examples/eslint-integration-demo/src/api.test.ts +0 -20
  218. package/examples/eslint-integration-demo/src/conflict-test.tsx +0 -44
  219. package/examples/eslint-integration-demo/src/naming-conflicts.ts +0 -50
  220. package/examples/eslint-integration-demo/tsconfig.json +0 -26
  221. package/examples/file-targeting-demo/global.d.ts +0 -11
  222. package/examples/file-targeting-demo/jest.config.js +0 -8
  223. package/examples/file-targeting-demo/sample.ts +0 -53
  224. package/examples/file-targeting-demo/src/server.js +0 -11
  225. package/examples/file-targeting-demo/src/server.test.js +0 -11
  226. package/examples/file-targeting-demo/src/types.d.ts +0 -4
  227. package/examples/file-targeting-demo/src/types.generated.ts +0 -10
  228. package/examples/file-targeting-demo/user-service.test.ts +0 -15
  229. package/examples/file-targeting-demo/user-service.ts +0 -13
  230. package/examples/file-targeting-demo/utils.js +0 -15
  231. package/examples/multi-language-project/.eslintrc.json +0 -38
  232. package/examples/multi-language-project/package.json +0 -37
  233. package/examples/multi-language-project/src/sample.ts +0 -39
  234. package/examples/rule-test-fixtures/README.md +0 -67
  235. package/examples/rule-test-fixtures/rules/C006_function_naming/clean/typescript-clean.ts +0 -64
  236. package/examples/rule-test-fixtures/rules/C006_function_naming/violations/dart-violations.dart +0 -56
  237. package/examples/rule-test-fixtures/rules/C006_function_naming/violations/typescript-violations.ts +0 -47
  238. package/examples/rule-test-fixtures/rules/C019_log_level_usage/clean/typescript-clean.ts +0 -93
  239. package/examples/rule-test-fixtures/rules/C019_log_level_usage/violations/dart-violations.dart +0 -75
  240. package/examples/rule-test-fixtures/rules/C019_log_level_usage/violations/typescript-violations.ts +0 -84
  241. package/examples/rule-test-fixtures/rules/C029_catch_block_logging/violations/typescript-violations.ts +0 -37
  242. /package/config/{default.json → defaults/default.json} +0 -0
  243. /package/{eslint-integration/eslint.config.js → config/integrations/eslint/base.config.js} +0 -0
  244. /package/{eslint-integration/eslint.config.simple.js → config/integrations/eslint/simple.config.js} +0 -0
  245. /package/{examples/rule-test-fixtures/rules/C029_catch_block_logging/clean/typescript-clean.ts → config/schemas/sunlint-schema.json} +0 -0
  246. /package/config/{typescript → testing}/test-s005-working.ts +0 -0
  247. /package/{examples/eslint-integration-demo/test-file-targeting.sh → engines/tree-sitter-parser.js} +0 -0
  248. /package/{examples/enhanced-config.json → engines/universal-ast-engine.js} +0 -0
  249. /package/{eslint-integration → integrations/eslint}/package.json +0 -0
  250. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin}/package.json +0 -0
  251. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c002-no-duplicate-code.js +0 -0
  252. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c003-no-vague-abbreviations.js +0 -0
  253. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c010-limit-block-nesting.js +0 -0
  254. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c014-abstract-dependency-preferred.js +0 -0
  255. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c018-no-generic-throw.js +0 -0
  256. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c023-no-duplicate-variable-name-in-scope.js +0 -0
  257. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c030-use-custom-error-classes.js +0 -0
  258. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c042-boolean-name-prefix.js +0 -0
  259. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c043-no-console-or-print.js +0 -0
  260. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c047-no-duplicate-retry-logic.js +0 -0
  261. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s003-no-unvalidated-redirect.js +0 -0
  262. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s005-no-origin-auth.js +0 -0
  263. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s006-activation-recovery-secret-not-plaintext.js +0 -0
  264. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s008-crypto-agility.js +0 -0
  265. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s009-no-insecure-crypto.js +0 -0
  266. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s010-no-insecure-random-in-sensitive-context.js +0 -0
  267. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s011-no-insecure-uuid.js +0 -0
  268. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s012-hardcode-secret.js +0 -0
  269. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s014-insecure-tls-version.js +0 -0
  270. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s015-insecure-tls-certificate.js +0 -0
  271. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s016-sensitive-query-parameter.js +0 -0
  272. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s017-no-sql-injection.js +0 -0
  273. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s018-positive-input-validation.js +0 -0
  274. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s019-no-raw-user-input-in-email.js +0 -0
  275. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s020-no-eval-dynamic-execution.js +0 -0
  276. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s022-output-encoding.js +0 -0
  277. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s023-no-json-injection.js +0 -0
  278. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s025-server-side-input-validation.js +0 -0
  279. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s026-json-schema-validation.js +0 -0
  280. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s027-no-hardcoded-secrets.js +0 -0
  281. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s029-require-csrf-protection.js +0 -0
  282. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s030-no-directory-browsing.js +0 -0
  283. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s033-require-samesite-cookie.js +0 -0
  284. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s034-require-host-cookie-prefix.js +0 -0
  285. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s035-cookie-specific-path.js +0 -0
  286. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s036-no-unsafe-file-include.js +0 -0
  287. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s037-require-anti-cache-headers.js +0 -0
  288. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s038-no-version-disclosure.js +0 -0
  289. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s039-no-session-token-in-url.js +0 -0
  290. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s041-require-session-invalidate-on-logout.js +0 -0
  291. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s042-require-periodic-reauthentication.js +0 -0
  292. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s043-terminate-sessions-on-password-change.js +0 -0
  293. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s044-require-full-session-for-sensitive-operations.js +0 -0
  294. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s045-anti-automation-controls.js +0 -0
  295. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s046-secure-notification-on-auth-change.js +0 -0
  296. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s047-secure-random-passwords.js +0 -0
  297. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s048-password-credential-recovery.js +0 -0
  298. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s050-session-token-weak-hash.js +0 -0
  299. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s052-secure-random-authentication-code.js +0 -0
  300. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s054-verification-default-account.js +0 -0
  301. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s055-verification-rest-check-the-incoming-content-type.js +0 -0
  302. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s057-utc-logging.js +0 -0
  303. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s058-no-ssrf.js +0 -0
  304. /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/typescript}/t002-interface-prefix-i.js +0 -0
  305. /package/{eslint-integration/eslint-plugin-custom/t019-no-empty-type.js → integrations/eslint/plugin/rules/typescript/t004-no-empty-type.js} +0 -0
  306. /package/{eslint-integration/eslint-plugin-custom/t025-no-nested-union-tuple.js → integrations/eslint/plugin/rules/typescript/t010-no-nested-union-tuple.js} +0 -0
  307. /package/{eslint-integration → integrations/eslint}/tsconfig.json +0 -0
  308. /package/rules/{C006_function_naming → common/C006_function_naming}/config.json +0 -0
  309. /package/rules/{C019_log_level_usage → common/C019_log_level_usage}/config.json +0 -0
  310. /package/rules/{C029_catch_block_logging → common/C029_catch_block_logging}/config.json +0 -0
  311. /package/rules/{C031_validation_separation → common/C031_validation_separation}/analyzer.js +0 -0
  312. /package/rules/{C031_validation_separation/README.md → docs/C031_validation_separation.md} +0 -0
  313. /package/{examples/basic-typescript-demo/test-file-targeting.sh → rules/universal/C010/generic.js} +0 -0
  314. /package/{examples/basic-typescript-demo/test-config-priority.sh → rules/universal/C010/tree-sitter-analyzer.js} +0 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * ESLint rule: S007 – Plaintext OTP Check
3
+ * Rule ID: custom/s007
4
+ * Description: Verify that OTPs are not stored or transmitted in plaintext form.
5
+ */
6
+
7
+ "use strict";
8
+
9
+ module.exports = {
10
+ meta: {
11
+ type: 'problem',
12
+ docs: {
13
+ description: 'Verify that OTPs are not stored or transmitted in plaintext form.',
14
+ recommended: true,
15
+ url: 'https://owasp.org/www-community/vulnerabilities/Insecure_Storage',
16
+ },
17
+ messages: {
18
+ plaintextOtp:
19
+ 'OTP should not be stored or transmitted in plaintext. Consider hashing or encrypting it.',
20
+ },
21
+ schema: [],
22
+ },
23
+
24
+ create(context) {
25
+ return {
26
+ Property(node) {
27
+ if (
28
+ node.key &&
29
+ node.key.type === 'Identifier' &&
30
+ /otp/i.test(node.key.name) &&
31
+ node.value &&
32
+ node.value.type === 'Identifier' &&
33
+ !/hash|encrypt|bcrypt|sha/i.test(node.value.name)
34
+ ) {
35
+ context.report({
36
+ node,
37
+ messageId: 'plaintextOtp',
38
+ });
39
+ }
40
+ },
41
+
42
+ CallExpression(node) {
43
+ const calleeName = getCalleeName(node.callee);
44
+ if (!calleeName) return;
45
+
46
+ if (/(insert|update|save|query|create)/i.test(calleeName)) {
47
+ for (const arg of node.arguments) {
48
+ const text = context.getSourceCode().getText(arg);
49
+ if (/otp/i.test(text) && !/hash|encrypt|bcrypt|sha/i.test(text)) {
50
+ context.report({
51
+ node: arg,
52
+ messageId: 'plaintextOtp',
53
+ });
54
+ }
55
+ }
56
+ }
57
+ },
58
+ };
59
+ },
60
+ };
61
+
62
+ // ===== Helper function =====
63
+
64
+ function getCalleeName(callee) {
65
+ if (callee.type === 'Identifier') {
66
+ return callee.name;
67
+ } else if (
68
+ callee.type === 'MemberExpression' &&
69
+ callee.property.type === 'Identifier'
70
+ ) {
71
+ return callee.property.name;
72
+ }
73
+ return null;
74
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * ESLint rule: S013 – Enforce TLS Usage
3
+ * Rule ID: custom-tls/s013
4
+ */
5
+ "use strict";
6
+
7
+ const HTTP_REGEX = /^http:\/\//i;
8
+
9
+ module.exports = {
10
+ meta: {
11
+ type: "problem",
12
+ docs: {
13
+ description:
14
+ "Ensure all client connections use TLS (HTTPS) and prevent unencrypted (HTTP) connections.",
15
+ recommended: true,
16
+ },
17
+ messages: {
18
+ insecureUrl:
19
+ "Unencrypted connection detected (http://). Always use HTTPS (TLS).",
20
+ },
21
+ schema: [],
22
+ },
23
+
24
+ create(context) {
25
+ /** Báo lỗi nếu node là string chứa http:// */
26
+ function reportIfHttp(node, text) {
27
+ if (HTTP_REGEX.test(text)) {
28
+ context.report({ node, messageId: "insecureUrl" });
29
+ }
30
+ }
31
+
32
+ return {
33
+ // String literal
34
+ Literal(node) {
35
+ if (typeof node.value === "string") reportIfHttp(node, node.value);
36
+ },
37
+
38
+ // Template “thuần” (không có ${...})
39
+ TemplateLiteral(node) {
40
+ if (node.expressions.length === 0) {
41
+ const raw = node.quasis.map(q => q.value.raw).join("");
42
+ reportIfHttp(node, raw);
43
+ }
44
+ },
45
+ };
46
+ },
47
+ };
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Custom ESLint rule for: T003 – Avoid using @ts-ignore without a justification
3
- * Rule ID: custom/t003
4
- * Purpose: Ensure @ts-ignore comments include a reason to maintain code quality and documentation
2
+ * Custom ESLint rule for: T003 – Avoid using @ts-ignore without justification
3
+ * Rule ID: custom/t003
4
+ * Purpose: Require clear justification when using @ts-ignore comments
5
5
  */
6
6
 
7
7
  "use strict";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Custom ESLint rule for: T007 – Avoid declaring functions inside constructors or class bodies
3
3
  * Rule ID: custom/t007
4
- * Purpose: Prevent function declarations inside constructors and class bodies to maintain clean code structure
4
+ * Purpose: Discourage function declarations within constructors or class methods
5
5
  */
6
6
 
7
7
  module.exports = {
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @fileoverview Rule T019: Do not assign to this arbitrarily
3
+ * @description Maintain proper context and avoid this manipulation
4
+ */
5
+
6
+ module.exports = {
7
+ meta: {
8
+ type: 'problem',
9
+ docs: {
10
+ description: 'Do not assign to this arbitrarily',
11
+ category: 'Best Practices',
12
+ recommended: true
13
+ },
14
+ fixable: null,
15
+ schema: []
16
+ },
17
+
18
+ create(context) {
19
+ return {
20
+ // Detect direct assignment to 'this'
21
+ AssignmentExpression(node) {
22
+ if (node.left.type === 'ThisExpression') {
23
+ context.report({
24
+ node,
25
+ message: 'T019: Do not assign to "this" directly. Use proper binding, arrow functions, or explicit parameter passing.'
26
+ });
27
+ }
28
+ },
29
+
30
+ // Detect patterns like "const that = this" or "var self = this"
31
+ VariableDeclarator(node) {
32
+ if (node.init && node.init.type === 'ThisExpression') {
33
+ // Common patterns: that, self, _this, me
34
+ const variableName = node.id.name;
35
+ const suspiciousNames = ['that', 'self', '_this', 'me', '_self'];
36
+
37
+ if (suspiciousNames.includes(variableName.toLowerCase())) {
38
+ context.report({
39
+ node,
40
+ message: `T019: Avoid storing "this" in variable "${variableName}". Use arrow functions or proper binding instead.`
41
+ });
42
+ }
43
+ }
44
+ },
45
+
46
+ // Detect method calls that bind this arbitrarily
47
+ CallExpression(node) {
48
+ // Detect .bind(this) patterns that might be problematic
49
+ if (node.callee.type === 'MemberExpression' &&
50
+ node.callee.property.name === 'bind' &&
51
+ node.arguments.length > 0) {
52
+
53
+ const bindTarget = node.arguments[0];
54
+ if (bindTarget.type === 'ThisExpression') {
55
+ // Allow legitimate .bind(this) in constructors and specific patterns
56
+ const sourceCode = context.sourceCode || context.getSourceCode();
57
+ let currentScope = sourceCode.getScope ? sourceCode.getScope(node) : null;
58
+
59
+ if (!currentScope && context.getScope) {
60
+ currentScope = context.getScope();
61
+ }
62
+
63
+ const parent = currentScope ? currentScope.block : null;
64
+ const isInConstructor = parent &&
65
+ parent.type === 'FunctionExpression' &&
66
+ parent.parent &&
67
+ parent.parent.key &&
68
+ parent.parent.key.name === 'constructor';
69
+
70
+ if (!isInConstructor) {
71
+ context.report({
72
+ node,
73
+ message: 'T019: Consider using arrow functions instead of .bind(this) for cleaner context handling.'
74
+ });
75
+ }
76
+ }
77
+ }
78
+ }
79
+ };
80
+ }
81
+ };
@@ -0,0 +1,127 @@
1
+ /**
2
+ * @fileoverview Rule T020: Avoid export default for multi-responsibility modules
3
+ * @description Improve tree-shaking and module clarity
4
+ */
5
+
6
+ module.exports = {
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ description: 'Avoid export default for multi-responsibility modules',
11
+ category: 'Best Practices',
12
+ recommended: true
13
+ },
14
+ fixable: null,
15
+ schema: []
16
+ },
17
+
18
+ create(context) {
19
+ let hasDefaultExport = false;
20
+ let namedExports = [];
21
+ let exportedFunctions = [];
22
+ let exportedClasses = [];
23
+ let exportedConstants = [];
24
+
25
+ function analyzeExport(node, exportType) {
26
+ if (exportType === 'default') {
27
+ hasDefaultExport = true;
28
+ } else {
29
+ namedExports.push(node);
30
+ }
31
+ }
32
+
33
+ return {
34
+ // Track export default statements
35
+ ExportDefaultDeclaration(node) {
36
+ hasDefaultExport = true;
37
+
38
+ // Analyze what's being exported as default
39
+ if (node.declaration) {
40
+ if (node.declaration.type === 'FunctionDeclaration') {
41
+ exportedFunctions.push(node.declaration);
42
+ } else if (node.declaration.type === 'ClassDeclaration') {
43
+ exportedClasses.push(node.declaration);
44
+ }
45
+ }
46
+ },
47
+
48
+ // Track named exports
49
+ ExportNamedDeclaration(node) {
50
+ namedExports.push(node);
51
+
52
+ if (node.declaration) {
53
+ if (node.declaration.type === 'FunctionDeclaration') {
54
+ exportedFunctions.push(node.declaration);
55
+ } else if (node.declaration.type === 'ClassDeclaration') {
56
+ exportedClasses.push(node.declaration);
57
+ } else if (node.declaration.type === 'VariableDeclaration') {
58
+ exportedConstants.push(node.declaration);
59
+ }
60
+ }
61
+
62
+ if (node.specifiers) {
63
+ node.specifiers.forEach(spec => {
64
+ if (spec.type === 'ExportSpecifier') {
65
+ namedExports.push(spec);
66
+ }
67
+ });
68
+ }
69
+ },
70
+
71
+ // Check at the end of the program
72
+ 'Program:exit'() {
73
+ if (!hasDefaultExport) {
74
+ return; // No default export, rule doesn't apply
75
+ }
76
+
77
+ // Count total exports
78
+ const totalNamedExports = namedExports.length;
79
+ const totalFunctions = exportedFunctions.length;
80
+ const totalClasses = exportedClasses.length;
81
+ const totalConstants = exportedConstants.length;
82
+
83
+ // Multi-responsibility indicators:
84
+ // 1. Multiple named exports alongside default export
85
+ // 2. Multiple functions being exported
86
+ // 3. Multiple classes being exported
87
+ // 4. Mix of functions, classes, and constants
88
+
89
+ const isMultiResponsibility =
90
+ totalNamedExports > 0 || // Has both default and named exports
91
+ totalFunctions > 1 || // Multiple functions
92
+ totalClasses > 1 || // Multiple classes
93
+ (totalFunctions > 0 && totalClasses > 0) || // Mix of functions and classes
94
+ (totalConstants > 2); // Too many constants
95
+
96
+ if (isMultiResponsibility) {
97
+ // Find the default export node to report
98
+ const sourceCode = context.getSourceCode();
99
+ const defaultExportNode = sourceCode.ast.body.find(
100
+ node => node.type === 'ExportDefaultDeclaration'
101
+ );
102
+
103
+ if (defaultExportNode) {
104
+ let message = 'T020: Avoid export default for multi-responsibility modules. ';
105
+
106
+ if (totalNamedExports > 0) {
107
+ message += `Found ${totalNamedExports} named exports alongside default export. `;
108
+ }
109
+ if (totalFunctions > 1) {
110
+ message += `Found ${totalFunctions} exported functions. `;
111
+ }
112
+ if (totalClasses > 1) {
113
+ message += `Found ${totalClasses} exported classes. `;
114
+ }
115
+
116
+ message += 'Consider using only named exports for better tree-shaking and clarity.';
117
+
118
+ context.report({
119
+ node: defaultExportNode,
120
+ message
121
+ });
122
+ }
123
+ }
124
+ }
125
+ };
126
+ }
127
+ };
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @fileoverview Rule T021: Limit deeply nested generics
3
+ * @description Improve code readability and TypeScript performance
4
+ */
5
+
6
+ module.exports = {
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ description: 'Limit deeply nested generics',
11
+ category: 'Best Practices',
12
+ recommended: true
13
+ },
14
+ fixable: null,
15
+ schema: [
16
+ {
17
+ type: 'object',
18
+ properties: {
19
+ maxDepth: {
20
+ type: 'integer',
21
+ minimum: 1,
22
+ default: 3
23
+ }
24
+ },
25
+ additionalProperties: false
26
+ }
27
+ ]
28
+ },
29
+
30
+ create(context) {
31
+ const options = context.options[0] || {};
32
+ const maxDepth = options.maxDepth || 3;
33
+
34
+ function getGenericDepth(typeNode) {
35
+ if (!typeNode) return 0;
36
+
37
+ switch (typeNode.type) {
38
+ case 'TSTypeReference':
39
+ // Generic types like Array<T>, Promise<U>, etc.
40
+ // ESLint uses 'typeArguments' instead of 'typeParameters'
41
+ if (typeNode.typeArguments && typeNode.typeArguments.params) {
42
+ let maxChildDepth = 0;
43
+ for (const param of typeNode.typeArguments.params) {
44
+ const childDepth = getGenericDepth(param);
45
+ maxChildDepth = Math.max(maxChildDepth, childDepth);
46
+ }
47
+ return 1 + maxChildDepth;
48
+ }
49
+ return 0;
50
+
51
+ case 'TSArrayType':
52
+ // Array types like T[]
53
+ return 1 + getGenericDepth(typeNode.elementType);
54
+
55
+ case 'TSUnionType':
56
+ case 'TSIntersectionType':
57
+ // Union types like A | B or intersection types A & B
58
+ let maxDepth = 0;
59
+ for (const typeItem of typeNode.types) {
60
+ maxDepth = Math.max(maxDepth, getGenericDepth(typeItem));
61
+ }
62
+ return maxDepth;
63
+
64
+ case 'TSTupleType':
65
+ // Tuple types like [A, B, C]
66
+ let maxTupleDepth = 0;
67
+ for (const elementType of typeNode.elementTypes) {
68
+ maxTupleDepth = Math.max(maxTupleDepth, getGenericDepth(elementType));
69
+ }
70
+ return 1 + maxTupleDepth;
71
+
72
+ case 'TSMappedType':
73
+ // Mapped types like { [K in keyof T]: U }
74
+ return 1 + getGenericDepth(typeNode.typeAnnotation);
75
+
76
+ case 'TSConditionalType':
77
+ // Conditional types like T extends U ? A : B
78
+ return 1 + Math.max(
79
+ getGenericDepth(typeNode.trueType),
80
+ getGenericDepth(typeNode.falseType)
81
+ );
82
+
83
+ default:
84
+ return 0;
85
+ }
86
+ }
87
+
88
+ function checkTypeDepth(node, typeName) {
89
+ const depth = getGenericDepth(node);
90
+ if (depth > maxDepth) {
91
+ context.report({
92
+ node,
93
+ message: `T021: Generic nesting depth of ${depth} exceeds maximum of ${maxDepth}. Consider breaking down complex types into intermediate type aliases for better readability and TypeScript compiler performance.`
94
+ });
95
+ }
96
+ }
97
+
98
+ return {
99
+ // Check type aliases - this is the main entry point
100
+ TSTypeAliasDeclaration(node) {
101
+ checkTypeDepth(node.typeAnnotation, node.id.name);
102
+ },
103
+
104
+ // Check interface properties
105
+ TSPropertySignature(node) {
106
+ if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
107
+ checkTypeDepth(node.typeAnnotation.typeAnnotation, 'property');
108
+ }
109
+ },
110
+
111
+ // Check function parameters
112
+ TSParameterProperty(node) {
113
+ if (node.parameter && node.parameter.typeAnnotation) {
114
+ checkTypeDepth(node.parameter.typeAnnotation.typeAnnotation, 'parameter');
115
+ }
116
+ },
117
+
118
+ // Check function return types
119
+ TSFunctionType(node) {
120
+ if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
121
+ checkTypeDepth(node.typeAnnotation.typeAnnotation, 'return type');
122
+ }
123
+ },
124
+
125
+ // Check variable declarations
126
+ VariableDeclarator(node) {
127
+ if (node.id && node.id.typeAnnotation && node.id.typeAnnotation.typeAnnotation) {
128
+ checkTypeDepth(node.id.typeAnnotation.typeAnnotation, 'variable');
129
+ }
130
+ },
131
+
132
+ // Check generic constraints
133
+ TSTypeParameter(node) {
134
+ if (node.constraint) {
135
+ checkTypeDepth(node.constraint, 'generic constraint');
136
+ }
137
+ if (node.default) {
138
+ checkTypeDepth(node.default, 'generic default');
139
+ }
140
+ },
141
+
142
+ // Check method signatures
143
+ TSMethodSignature(node) {
144
+ if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
145
+ checkTypeDepth(node.typeAnnotation.typeAnnotation, 'method return type');
146
+ }
147
+ }
148
+ };
149
+ }
150
+ };
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ const rule = require('./plugin/rules/common/c041-no-config-inline.js');
4
+
5
+ // Simple test cases
6
+ const testCases = [
7
+ // Should NOT flag (false positives to avoid)
8
+ { code: 'const inputType = "password";', shouldError: false, desc: 'InputType definition' },
9
+ { code: 'const fieldType = "password";', shouldError: false, desc: 'Field type' },
10
+ { code: 'const usePassword = "password_validation";', shouldError: false, desc: 'Hook usage' },
11
+ { code: 'const testPassword = "test123";', shouldError: false, desc: 'Test data' },
12
+ { code: 'const mockSecret = "mock_secret_key";', shouldError: false, desc: 'Mock data' },
13
+ { code: 'const route = "/reset_password";', shouldError: false, desc: 'Route path' },
14
+
15
+ // SHOULD flag (real sensitive data)
16
+ { code: 'const dbPassword = "MySecretP@ssw0rd123";', shouldError: true, desc: 'Real hardcoded password' },
17
+ { code: 'const apiKey = "sk-1234567890abcdef1234567890abcdef";', shouldError: true, desc: 'Real API key' },
18
+ { code: 'const authToken = "bearer_token_12345678901234567";', shouldError: true, desc: 'Real auth token' }
19
+ ];
20
+
21
+ console.log('🧪 Testing ESLint C041 rule manually...\n');
22
+
23
+ let passed = 0;
24
+ let failed = 0;
25
+
26
+ // Mock context for testing
27
+ function createMockContext(code) {
28
+ return {
29
+ getSourceCode: () => ({
30
+ lines: code.split('\n')
31
+ }),
32
+ report: ({ node, messageId }) => {
33
+ return { reported: true, messageId };
34
+ }
35
+ };
36
+ }
37
+
38
+ testCases.forEach((testCase, index) => {
39
+ try {
40
+ let reported = false;
41
+
42
+ // Parse the code to extract literal nodes (simplified)
43
+ const literalMatch = testCase.code.match(/"([^"]+)"/);
44
+ if (literalMatch) {
45
+ const mockNode = {
46
+ value: literalMatch[1],
47
+ loc: { start: { line: 1 } }
48
+ };
49
+
50
+ const mockContext = {
51
+ getSourceCode: () => ({
52
+ lines: [testCase.code]
53
+ }),
54
+ report: ({ node, messageId }) => {
55
+ reported = true;
56
+ }
57
+ };
58
+
59
+ const ruleImplementation = rule.create(mockContext);
60
+ ruleImplementation.Literal(mockNode);
61
+ }
62
+
63
+ const testPassed = reported === testCase.shouldError;
64
+
65
+ if (testPassed) {
66
+ console.log(`✅ Test ${index + 1}: ${testCase.desc}`);
67
+ passed++;
68
+ } else {
69
+ console.log(`❌ Test ${index + 1}: ${testCase.desc}`);
70
+ console.log(` Expected: ${testCase.shouldError ? 'Should flag' : 'Should NOT flag'}`);
71
+ console.log(` Actual: ${reported ? 'Flagged' : 'Not flagged'}`);
72
+ failed++;
73
+ }
74
+ } catch (error) {
75
+ console.log(`💥 Test ${index + 1}: ${testCase.desc} - Error: ${error.message}`);
76
+ failed++;
77
+ }
78
+ });
79
+
80
+ console.log(`\n📊 Test Results: ${passed} passed, ${failed} failed`);
81
+ console.log(`Success rate: ${Math.round((passed / (passed + failed)) * 100)}%`);
82
+
83
+ if (failed === 0) {
84
+ console.log('\n🎉 All ESLint rule tests passed!');
85
+ } else {
86
+ console.log('\n⚠️ Some tests failed. Review the rule logic.');
87
+ }