@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
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const AIAnalyzer = require('../../core/ai-analyzer');
3
+ const { PatternMatcher } = require('../../utils/pattern-matchers');
4
+ const { RuleHelper } = require('../../utils/rule-helpers');
4
5
 
5
6
  class C029Analyzer {
6
7
  constructor() {
@@ -10,31 +11,27 @@ class C029Analyzer {
10
11
  this.aiAnalyzer = null;
11
12
  }
12
13
 
13
- async analyze(files, language, config) {
14
+ async analyze(files, language, options = {}) {
14
15
  const violations = [];
15
-
16
- // Initialize AI analyzer if enabled
17
- if (config.ai && config.ai.enabled) {
18
- this.aiAnalyzer = new AIAnalyzer(config.ai);
19
- console.log('🤖 AI analysis enabled for C029');
20
- }
21
-
16
+
22
17
  for (const filePath of files) {
18
+ if (options.verbose) {
19
+ console.log(`🔍 Running pattern analysis on ${path.basename(filePath)}`);
20
+ }
21
+
23
22
  try {
24
- const fileContent = fs.readFileSync(filePath, 'utf8');
25
- const fileViolations = await this.analyzeFile(filePath, fileContent, language, config);
23
+ const content = fs.readFileSync(filePath, 'utf8');
24
+ const fileViolations = await this.analyzeFile(filePath, content, language, options);
26
25
  violations.push(...fileViolations);
27
26
  } catch (error) {
28
- console.error(`Error analyzing file ${filePath}:`, error.message);
27
+ console.warn(`⚠️ Failed to analyze ${filePath}: ${error.message}`);
29
28
  }
30
29
  }
31
-
30
+
32
31
  return violations;
33
32
  }
34
33
 
35
34
  async analyzeFile(filePath, content, language, config) {
36
- // Fallback to pattern-based analysis
37
- console.log(`🔍 Running pattern analysis on ${path.basename(filePath)}`);
38
35
  switch (language) {
39
36
  case 'typescript':
40
37
  case 'javascript':
@@ -259,6 +256,43 @@ class C029Analyzer {
259
256
  return true;
260
257
  }
261
258
 
259
+ // Check for test assertions - valid form of error handling
260
+ const testPatterns = [
261
+ 'expect(',
262
+ 'assert(',
263
+ 'tobeinstanceof',
264
+ 'toequal(',
265
+ 'tohavebeencalled',
266
+ 'toBe(',
267
+ 'toHaveBeenCalledWith'
268
+ ];
269
+
270
+ const hasTestAssertions = testPatterns.some(pattern =>
271
+ content.includes(pattern.toLowerCase()) || originalContent.toLowerCase().includes(pattern.toLowerCase())
272
+ );
273
+
274
+ if (hasTestAssertions) {
275
+ return true;
276
+ }
277
+
278
+ // Check for Redux/async thunk error handling
279
+ const reduxErrorHandlers = [
280
+ 'handleaxioserror',
281
+ 'rejectwithvalue',
282
+ 'dispatch(',
283
+ 'seterror(',
284
+ 'return value',
285
+ 'return rejectwithvalue'
286
+ ];
287
+
288
+ const hasReduxHandling = reduxErrorHandlers.some(pattern =>
289
+ content.includes(pattern.toLowerCase()) || originalContent.toLowerCase().includes(pattern.toLowerCase())
290
+ );
291
+
292
+ if (hasReduxHandling) {
293
+ return true;
294
+ }
295
+
262
296
  // Check for logging patterns (expanded from original)
263
297
  const loggingPatterns = [
264
298
  'console.error',
@@ -0,0 +1,292 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ class C041Analyzer {
5
+ constructor() {
6
+ this.ruleId = 'C041';
7
+ this.ruleName = 'No Hardcoded Sensitive Information';
8
+ this.description = 'Không hardcode hoặc push thông tin nhạy cảm vào repo';
9
+ }
10
+
11
+ async analyze(files, language, options = {}) {
12
+ const violations = [];
13
+
14
+ for (const filePath of files) {
15
+ if (options.verbose) {
16
+ console.log(`🔍 Running C041 analysis on ${path.basename(filePath)}`);
17
+ }
18
+
19
+ try {
20
+ const content = fs.readFileSync(filePath, 'utf8');
21
+ const fileViolations = await this.analyzeFile(filePath, content, language, options);
22
+ violations.push(...fileViolations);
23
+ } catch (error) {
24
+ console.warn(`⚠️ Failed to analyze ${filePath}: ${error.message}`);
25
+ }
26
+ }
27
+
28
+ return violations;
29
+ }
30
+
31
+ async analyzeFile(filePath, content, language, config) {
32
+ switch (language) {
33
+ case 'typescript':
34
+ case 'javascript':
35
+ return this.analyzeTypeScript(filePath, content, config);
36
+ default:
37
+ return [];
38
+ }
39
+ }
40
+
41
+ async analyzeTypeScript(filePath, content, config) {
42
+ const violations = [];
43
+ const lines = content.split('\n');
44
+
45
+ lines.forEach((line, index) => {
46
+ const lineNumber = index + 1;
47
+ const trimmedLine = line.trim();
48
+
49
+ // Skip comments and imports
50
+ if (this.isCommentOrImport(trimmedLine)) {
51
+ return;
52
+ }
53
+
54
+ // Find potential hardcoded sensitive values
55
+ const sensitiveMatches = this.findSensitiveHardcode(trimmedLine, line);
56
+
57
+ sensitiveMatches.forEach(match => {
58
+ violations.push({
59
+ ruleId: this.ruleId,
60
+ file: filePath,
61
+ line: lineNumber,
62
+ column: match.column,
63
+ message: match.message,
64
+ severity: 'error',
65
+ code: trimmedLine,
66
+ type: match.type,
67
+ confidence: match.confidence,
68
+ suggestion: match.suggestion
69
+ });
70
+ });
71
+ });
72
+
73
+ return violations;
74
+ }
75
+
76
+ isCommentOrImport(line) {
77
+ const trimmed = line.trim();
78
+ return trimmed.startsWith('//') ||
79
+ trimmed.startsWith('/*') ||
80
+ trimmed.startsWith('*') ||
81
+ trimmed.startsWith('import ') ||
82
+ trimmed.startsWith('export ');
83
+ }
84
+
85
+ findSensitiveHardcode(line, originalLine) {
86
+ const matches = [];
87
+
88
+ // Skip template literals with variables - they are dynamic, not hardcoded
89
+ if (line.includes('${') || line.includes('`')) {
90
+ return matches;
91
+ }
92
+
93
+ // Skip if line is clearly configuration, type definition, or UI-related
94
+ if (this.isConfigOrUIContext(line)) {
95
+ return matches;
96
+ }
97
+
98
+ // Look for suspicious patterns with better context awareness
99
+ const patterns = [
100
+ {
101
+ name: 'suspicious_password_variable',
102
+ regex: /(const|let|var)\s+\w*[Pp]ass[Ww]ord\w*\s*=\s*['"`]([^'"`]{4,})['"`]/g,
103
+ severity: 'error',
104
+ message: 'Potential hardcoded password in variable assignment',
105
+ suggestion: 'Move sensitive values to environment variables or secure config files'
106
+ },
107
+ {
108
+ name: 'suspicious_secret_variable',
109
+ regex: /(const|let|var)\s+\w*[Ss]ecret\w*\s*=\s*['"`]([^'"`]{6,})['"`]/g,
110
+ severity: 'error',
111
+ message: 'Potential hardcoded secret in variable assignment',
112
+ suggestion: 'Use environment variables for secrets'
113
+ },
114
+ {
115
+ name: 'suspicious_short_password',
116
+ regex: /(const|let|var)\s+(?!use)\w*([Pp]ass|[Dd]b[Pp]ass|[Aa]dmin)(?!word[A-Z])\w*\s*=\s*['"`]([^'"`]{4,})['"`]/g,
117
+ severity: 'error',
118
+ message: 'Potential hardcoded password or admin credential',
119
+ suggestion: 'Use environment variables for credentials'
120
+ },
121
+ {
122
+ name: 'api_key',
123
+ regex: /(const|let|var)\s+\w*[Aa]pi[Kk]ey\w*\s*=\s*['"`]([^'"`]{10,})['"`]/g,
124
+ severity: 'error',
125
+ message: 'Potential hardcoded API key detected',
126
+ suggestion: 'Use environment variables for API keys'
127
+ },
128
+ {
129
+ name: 'auth_token',
130
+ regex: /(const|let|var)\s+\w*[Tt]oken\w*\s*=\s*['"`]([^'"`]{16,})['"`]/g,
131
+ severity: 'error',
132
+ message: 'Potential hardcoded authentication token detected',
133
+ suggestion: 'Store tokens in secure storage, not in source code'
134
+ },
135
+ {
136
+ name: 'database_url',
137
+ regex: /['"`](mongodb|mysql|postgres|redis):\/\/[^'"`]+['"`]/gi,
138
+ severity: 'error',
139
+ message: 'Hardcoded database connection string detected',
140
+ suggestion: 'Use environment variables for database connections'
141
+ },
142
+ {
143
+ name: 'suspicious_url',
144
+ regex: /['"`]https?:\/\/(?!localhost|127\.0\.0\.1|example\.com|test\.com|www\.w3\.org|www\.google\.com|googleapis\.com)[^'"`]{20,}['"`]/gi,
145
+ severity: 'warning',
146
+ message: 'Hardcoded external URL detected (consider configuration)',
147
+ suggestion: 'Consider moving URLs to configuration files'
148
+ }
149
+ ];
150
+
151
+ // Additional context-aware checks
152
+ patterns.forEach(pattern => {
153
+ let match;
154
+ while ((match = pattern.regex.exec(line)) !== null) {
155
+ // Skip false positives
156
+ if (this.isFalsePositive(line, match[0], pattern.name)) {
157
+ continue;
158
+ }
159
+
160
+ matches.push({
161
+ type: pattern.name,
162
+ column: match.index + 1,
163
+ message: pattern.message,
164
+ confidence: this.calculateConfidence(line, match[0], pattern.name),
165
+ suggestion: pattern.suggestion
166
+ });
167
+ }
168
+ });
169
+
170
+ return matches;
171
+ }
172
+
173
+ isConfigOrUIContext(line) {
174
+ const lowerLine = line.toLowerCase();
175
+
176
+ // UI/Component contexts - likely false positives
177
+ const uiContexts = [
178
+ 'inputtype', 'type:', 'type =', 'type:', 'inputtype=',
179
+ 'routes =', 'route:', 'path:', 'routes:',
180
+ 'import {', 'export {', 'from ', 'import ',
181
+ 'interface', 'type ', 'enum ',
182
+ 'props:', 'defaultprops',
183
+ 'schema', 'validator',
184
+ 'hook', 'use', 'const use', 'import.*use',
185
+ // React/UI specific
186
+ 'textinput', 'input ', 'field ', 'form',
187
+ 'component', 'page', 'screen', 'modal',
188
+ // Route/navigation specific
189
+ 'navigation', 'route', 'path', 'url:', 'route:',
190
+ 'setuppassword', 'resetpassword', 'forgotpassword',
191
+ 'changepassword', 'confirmpassword'
192
+ ];
193
+
194
+ return uiContexts.some(context => lowerLine.includes(context));
195
+ }
196
+
197
+ isFalsePositive(line, matchedText, patternName) {
198
+ const lowerLine = line.toLowerCase();
199
+ const lowerMatch = matchedText.toLowerCase();
200
+
201
+ // Global false positive indicators
202
+ const globalFalsePositives = [
203
+ 'test', 'mock', 'example', 'demo', 'sample', 'placeholder', 'dummy', 'fake',
204
+ 'xmlns', 'namespace', 'schema', 'w3.org', 'google.com', 'googleapis.com',
205
+ 'error', 'message', 'missing', 'invalid', 'failed'
206
+ ];
207
+
208
+ // Check if the line contains any global false positive indicators
209
+ const hasGlobalFalsePositive = globalFalsePositives.some(pattern =>
210
+ lowerLine.includes(pattern) || lowerMatch.includes(pattern)
211
+ );
212
+
213
+ if (hasGlobalFalsePositive) {
214
+ return true;
215
+ }
216
+
217
+ // Common false positive patterns
218
+ const falsePositivePatterns = {
219
+ 'suspicious_password_variable': [
220
+ 'inputtype', 'type:', 'type =', 'activation', 'forgot_password', 'reset_password',
221
+ 'setup_password', 'route', 'path', 'hook', 'use', 'change', 'confirm',
222
+ 'validation', 'component', 'page', 'screen', 'textinput', 'input',
223
+ 'trigger', 'useeffect', 'password.*trigger', 'renewpassword'
224
+ ],
225
+ 'suspicious_short_password': [
226
+ 'inputtype', 'type:', 'type =', 'activation', 'forgot_password', 'reset_password',
227
+ 'setup_password', 'route', 'path', 'hook', 'use', 'change', 'confirm',
228
+ 'validation', 'component', 'page', 'screen', 'textinput'
229
+ ],
230
+ 'suspicious_secret_variable': [
231
+ 'component', 'props', 'state', 'hook', 'use'
232
+ ],
233
+ 'suspicious_url': [
234
+ 'localhost', '127.0.0.1', 'example.com', 'test.com', 'placeholder',
235
+ 'mock', 'w3.org', 'google.com', 'recaptcha', 'googleapis.com'
236
+ ],
237
+ 'api_key': [
238
+ 'test-', 'mock-', 'example-', 'demo-', 'missing', 'error', 'message'
239
+ ]
240
+ };
241
+
242
+ const patterns = falsePositivePatterns[patternName] || [];
243
+
244
+ // Check if line contains any pattern-specific false positive indicators
245
+ const hasPatternFalsePositive = patterns.some(pattern =>
246
+ lowerLine.includes(pattern) || lowerMatch.includes(pattern)
247
+ );
248
+
249
+ // Special handling for password-related patterns
250
+ if (patternName === 'hardcoded_password') {
251
+ // Allow if it's clearly UI/component related
252
+ if (lowerLine.includes('input') ||
253
+ lowerLine.includes('field') ||
254
+ lowerLine.includes('form') ||
255
+ lowerLine.includes('component') ||
256
+ lowerLine.includes('type') ||
257
+ lowerLine.includes('route') ||
258
+ lowerLine.includes('path') ||
259
+ lowerMatch.includes('activation') ||
260
+ lowerMatch.includes('forgot_password') ||
261
+ lowerMatch.includes('reset_password') ||
262
+ lowerMatch.includes('setup_password')) {
263
+ return true;
264
+ }
265
+ }
266
+
267
+ return hasPatternFalsePositive;
268
+ }
269
+
270
+ calculateConfidence(line, match, patternName) {
271
+ let confidence = 0.8; // Base confidence
272
+
273
+ // Reduce confidence for potential false positives
274
+ const lowerLine = line.toLowerCase();
275
+
276
+ if (lowerLine.includes('test') || lowerLine.includes('mock') || lowerLine.includes('example')) {
277
+ confidence -= 0.3;
278
+ }
279
+
280
+ if (lowerLine.includes('const') || lowerLine.includes('let') || lowerLine.includes('var')) {
281
+ confidence += 0.1; // Variable assignments more likely to be hardcode
282
+ }
283
+
284
+ if (lowerLine.includes('type') || lowerLine.includes('component') || lowerLine.includes('props')) {
285
+ confidence -= 0.2; // UI-related less likely to be sensitive
286
+ }
287
+
288
+ return Math.max(0.3, Math.min(1.0, confidence));
289
+ }
290
+ }
291
+
292
+ module.exports = new C041Analyzer();
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Heuristic analyzer for: C042 – Boolean variable names should start with proper prefixes
3
+ * Purpose: Detect boolean variables that don't follow naming conventions
4
+ */
5
+
6
+ class C042Analyzer {
7
+ constructor() {
8
+ this.ruleId = 'C042';
9
+ this.ruleName = 'Boolean Variable Naming';
10
+ this.description = 'Boolean variable names should start with is, has, should, can, will, must, may, or check';
11
+
12
+ // User requested to add "check" prefix
13
+ this.booleanPrefixes = [
14
+ 'is', 'has', 'should', 'can', 'will', 'must', 'may', 'check',
15
+ 'are', 'were', 'was', 'could', 'might', 'shall', 'need', 'want'
16
+ ];
17
+
18
+ // Common non-boolean patterns to ignore (user feedback)
19
+ this.ignoredPatterns = [
20
+ // Fallback/default patterns: var = value || fallback
21
+ /\w+\s*=\s*\w+\s*\|\|\s*[^|]/,
22
+ // Assignment patterns that are clearly not boolean
23
+ /\w+\s*=\s*['"`][^'"`]*['"`]/, // String assignments
24
+ /\w+\s*=\s*\d+/, // Number assignments
25
+ /\w+\s*=\s*\{/, // Object assignments
26
+ /\w+\s*=\s*\[/, // Array assignments
27
+ ];
28
+
29
+ // Variables that commonly aren't boolean but might look like it
30
+ this.commonNonBooleans = [
31
+ 'value', 'result', 'data', 'config', 'name', 'id', 'key',
32
+ 'path', 'url', 'src', 'href', 'text', 'message', 'error',
33
+ 'response', 'request', 'params', 'options', 'settings'
34
+ ];
35
+ }
36
+
37
+ async analyze(files, language, options = {}) {
38
+ const violations = [];
39
+
40
+ for (const filePath of files) {
41
+ if (options.verbose) {
42
+ console.log(`🔍 Running C042 analysis on ${require('path').basename(filePath)}`);
43
+ }
44
+
45
+ try {
46
+ const content = require('fs').readFileSync(filePath, 'utf8');
47
+ const fileViolations = this.analyzeFile(content, filePath);
48
+ violations.push(...fileViolations);
49
+ } catch (error) {
50
+ console.warn(`⚠️ Failed to analyze ${filePath}: ${error.message}`);
51
+ }
52
+ }
53
+
54
+ return violations;
55
+ }
56
+
57
+ analyzeFile(content, filePath) {
58
+ const violations = [];
59
+ const lines = content.split('\n');
60
+
61
+ for (let i = 0; i < lines.length; i++) {
62
+ const line = lines[i].trim();
63
+
64
+ // Skip empty lines, comments, imports, and type declarations
65
+ if (!line || line.startsWith('//') || line.startsWith('/*') ||
66
+ line.startsWith('import') || line.startsWith('export') ||
67
+ line.startsWith('declare') || line.startsWith('*')) {
68
+ continue;
69
+ }
70
+
71
+ const lineViolations = this.analyzeDeclaration(line, i + 1);
72
+ lineViolations.forEach(violation => {
73
+ violations.push({
74
+ ...violation,
75
+ file: filePath
76
+ });
77
+ });
78
+ }
79
+
80
+ return violations;
81
+ }
82
+
83
+ analyzeDeclaration(line, lineNumber) {
84
+ const violations = [];
85
+
86
+ // Match variable declarations with boolean values
87
+ const booleanAssignments = this.findBooleanAssignments(line);
88
+
89
+ for (const assignment of booleanAssignments) {
90
+ const { varName, isBooleanValue, hasPrefix, actualValue } = assignment;
91
+
92
+ // Case 1: Variable is assigned a boolean value but doesn't have proper prefix
93
+ if (isBooleanValue && !hasPrefix) {
94
+ // Skip if this matches user feedback patterns (false positives)
95
+ if (this.shouldSkipBooleanVariableCheck(varName, line, actualValue)) {
96
+ continue;
97
+ }
98
+
99
+ violations.push({
100
+ line: lineNumber,
101
+ column: line.indexOf(varName) + 1,
102
+ message: `Boolean variable '${varName}' should start with a descriptive prefix like 'is', 'has', 'should', 'can', or 'check'. Consider: ${this.generateSuggestions(varName).join(', ')}.`,
103
+ severity: 'warning',
104
+ ruleId: this.ruleId
105
+ });
106
+ }
107
+
108
+ // Case 2: Variable has boolean prefix but is assigned a non-boolean value
109
+ else if (hasPrefix && !isBooleanValue && actualValue && this.isDefinitelyNotBoolean(actualValue)) {
110
+ // Only skip very basic cases for prefix misuse
111
+ if (varName.length <= 2) {
112
+ continue;
113
+ }
114
+
115
+ const prefix = this.extractPrefix(varName);
116
+ violations.push({
117
+ line: lineNumber,
118
+ column: line.indexOf(varName) + 1,
119
+ message: `Variable '${varName}' uses boolean prefix '${prefix}' but is assigned a non-boolean value. Consider renaming or changing the value.`,
120
+ severity: 'warning',
121
+ ruleId: this.ruleId
122
+ });
123
+ }
124
+ }
125
+
126
+ return violations;
127
+ }
128
+
129
+ findBooleanAssignments(line) {
130
+ const assignments = [];
131
+
132
+ // Match declaration patterns only (avoid duplicates)
133
+ const patterns = [
134
+ // let/const/var varName = value
135
+ /(?:let|const|var)\s+(\w+)\s*(?::\s*\w+\s*)?=\s*(.+?)(?:;|$)/g,
136
+ ];
137
+
138
+ for (const pattern of patterns) {
139
+ let match;
140
+ const seenVariables = new Set(); // Avoid duplicates
141
+
142
+ while ((match = pattern.exec(line)) !== null) {
143
+ const varName = match[1];
144
+ const value = match[2].trim();
145
+
146
+ // Skip if already processed
147
+ if (seenVariables.has(varName)) {
148
+ continue;
149
+ }
150
+ seenVariables.add(varName);
151
+
152
+ // Skip destructuring and complex patterns
153
+ if (varName.includes('[') || varName.includes('{') || value.includes('{') || value.includes('[')) {
154
+ continue;
155
+ }
156
+
157
+ const isBooleanValue = this.isBooleanValue(value);
158
+ const hasPrefix = this.hasBooleanPrefix(varName);
159
+
160
+ assignments.push({
161
+ varName,
162
+ isBooleanValue,
163
+ hasPrefix,
164
+ actualValue: value
165
+ });
166
+ }
167
+ }
168
+
169
+ return assignments;
170
+ }
171
+
172
+ isBooleanValue(value) {
173
+ const trimmedValue = value.trim();
174
+
175
+ // Direct boolean literals
176
+ if (trimmedValue === 'true' || trimmedValue === 'false') {
177
+ return true;
178
+ }
179
+
180
+ // Boolean expressions that clearly result in boolean
181
+ const booleanExpressions = [
182
+ /\w+\s*[<>!=]=/, // Comparisons
183
+ /\w+\s*(&&|\|\|)/, // Logical operations (but not fallback patterns)
184
+ /^\!\w+/, // Negation
185
+ /instanceof\s+/, // instanceof
186
+ /\.test\(/, // regex.test()
187
+ /\.includes\(/, // array.includes()
188
+ /\.hasOwnProperty\(/, // hasOwnProperty
189
+ /\.some\(/, // array.some()
190
+ /\.every\(/, // array.every()
191
+ /typeof\s+.*\s*===/, // typeof checks
192
+ /Math\.random\(\)\s*[<>]/, // Math.random() comparisons
193
+ /\.length\s*[<>!=]=/, // Length comparisons
194
+ ];
195
+
196
+ // Exclude fallback patterns (user feedback - these are NOT boolean)
197
+ if (trimmedValue.includes('||')) {
198
+ // Check if it's a boolean expression or just a fallback
199
+ // If the || is followed by a non-boolean value, it's likely a fallback
200
+ const parts = trimmedValue.split('||');
201
+ if (parts.length === 2) {
202
+ const fallback = parts[1].trim();
203
+ // If fallback is clearly not boolean, this is not a boolean assignment
204
+ if (this.isDefinitelyNotBoolean(fallback) || /^\d+$/.test(fallback) || /^['"`]/.test(fallback)) {
205
+ return false;
206
+ }
207
+ }
208
+ }
209
+
210
+ return booleanExpressions.some(pattern => pattern.test(trimmedValue));
211
+ }
212
+
213
+ hasBooleanPrefix(varName) {
214
+ const lowerName = varName.toLowerCase();
215
+ return this.booleanPrefixes.some(prefix =>
216
+ lowerName.startsWith(prefix.toLowerCase()) &&
217
+ lowerName.length > prefix.length
218
+ );
219
+ }
220
+
221
+ extractPrefix(varName) {
222
+ const lowerName = varName.toLowerCase();
223
+ for (const prefix of this.booleanPrefixes) {
224
+ if (lowerName.startsWith(prefix.toLowerCase())) {
225
+ return prefix;
226
+ }
227
+ }
228
+ return '';
229
+ }
230
+
231
+ shouldSkipBooleanVariableCheck(varName, line, value) {
232
+ // Skip very short names
233
+ if (varName.length <= 2) {
234
+ return true;
235
+ }
236
+
237
+ // Skip common non-boolean variable names
238
+ if (this.commonNonBooleans.includes(varName.toLowerCase())) {
239
+ return true;
240
+ }
241
+
242
+ // Skip user feedback patterns (fallback/default patterns)
243
+ if (this.ignoredPatterns.some(pattern => pattern.test(line))) {
244
+ return true;
245
+ }
246
+
247
+ // Skip function parameters and loop variables
248
+ if (line.includes('function') || line.includes('for') || line.includes('=>')) {
249
+ return true;
250
+ }
251
+
252
+ return false;
253
+ }
254
+
255
+ isDefinitelyNotBoolean(value) {
256
+ const trimmedValue = value.trim();
257
+
258
+ // String literals (including single quotes)
259
+ if (trimmedValue.match(/^['"`][^'"`]*['"`]$/)) {
260
+ return true;
261
+ }
262
+
263
+ // Number literals
264
+ if (trimmedValue.match(/^\d+(\.\d+)?$/)) {
265
+ return true;
266
+ }
267
+
268
+ // Object/array literals
269
+ if (trimmedValue.startsWith('{') || trimmedValue.startsWith('[')) {
270
+ return true;
271
+ }
272
+
273
+ // null, undefined
274
+ if (trimmedValue === 'null' || trimmedValue === 'undefined') {
275
+ return true;
276
+ }
277
+
278
+ // Common non-boolean patterns
279
+ if (trimmedValue.includes('new ') || trimmedValue.includes('function')) {
280
+ return true;
281
+ }
282
+
283
+ return false;
284
+ }
285
+
286
+ generateSuggestions(varName) {
287
+ const suggestions = [];
288
+ const baseName = varName.replace(/^(is|has|should|can|will|must|may|check)/i, '');
289
+ const capitalizedBase = baseName.charAt(0).toUpperCase() + baseName.slice(1);
290
+
291
+ // Generate a few reasonable suggestions
292
+ suggestions.push(`is${capitalizedBase}`);
293
+ suggestions.push(`has${capitalizedBase}`);
294
+ suggestions.push(`should${capitalizedBase}`);
295
+
296
+ return suggestions.slice(0, 3); // Limit to 3 suggestions
297
+ }
298
+ }
299
+
300
+ module.exports = C042Analyzer;