@aicqtools/guardrail 1.0.0-alpha.10

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 (297) hide show
  1. package/LICENSE +21 -0
  2. package/dist/docs/build.d.ts +12 -0
  3. package/dist/docs/build.d.ts.map +1 -0
  4. package/dist/docs/build.js +23 -0
  5. package/dist/docs/build.js.map +1 -0
  6. package/dist/docs/index.d.ts +4 -0
  7. package/dist/docs/index.d.ts.map +1 -0
  8. package/dist/docs/index.js +3 -0
  9. package/dist/docs/index.js.map +1 -0
  10. package/dist/docs/render-rule-md.d.ts +4 -0
  11. package/dist/docs/render-rule-md.d.ts.map +1 -0
  12. package/dist/docs/render-rule-md.js +117 -0
  13. package/dist/docs/render-rule-md.js.map +1 -0
  14. package/dist/index.d.ts +14 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +8 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/matcher/index.d.ts +4 -0
  19. package/dist/matcher/index.d.ts.map +1 -0
  20. package/dist/matcher/index.js +3 -0
  21. package/dist/matcher/index.js.map +1 -0
  22. package/dist/matcher/traverse.d.ts +3 -0
  23. package/dist/matcher/traverse.d.ts.map +1 -0
  24. package/dist/matcher/traverse.js +9 -0
  25. package/dist/matcher/traverse.js.map +1 -0
  26. package/dist/matcher/yaml-rule.d.ts +34 -0
  27. package/dist/matcher/yaml-rule.d.ts.map +1 -0
  28. package/dist/matcher/yaml-rule.js +32 -0
  29. package/dist/matcher/yaml-rule.js.map +1 -0
  30. package/dist/mcp/handlers.d.ts +36 -0
  31. package/dist/mcp/handlers.d.ts.map +1 -0
  32. package/dist/mcp/handlers.js +32 -0
  33. package/dist/mcp/handlers.js.map +1 -0
  34. package/dist/mcp/index.d.ts +6 -0
  35. package/dist/mcp/index.d.ts.map +1 -0
  36. package/dist/mcp/index.js +4 -0
  37. package/dist/mcp/index.js.map +1 -0
  38. package/dist/mcp/server.d.ts +9 -0
  39. package/dist/mcp/server.d.ts.map +1 -0
  40. package/dist/mcp/server.js +53 -0
  41. package/dist/mcp/server.js.map +1 -0
  42. package/dist/mcp/stdio.d.ts +3 -0
  43. package/dist/mcp/stdio.d.ts.map +1 -0
  44. package/dist/mcp/stdio.js +8 -0
  45. package/dist/mcp/stdio.js.map +1 -0
  46. package/dist/rules-default/ai-explainability-metadata.d.ts +3 -0
  47. package/dist/rules-default/ai-explainability-metadata.d.ts.map +1 -0
  48. package/dist/rules-default/ai-explainability-metadata.js +41 -0
  49. package/dist/rules-default/ai-explainability-metadata.js.map +1 -0
  50. package/dist/rules-default/api-response-shape.d.ts +3 -0
  51. package/dist/rules-default/api-response-shape.d.ts.map +1 -0
  52. package/dist/rules-default/api-response-shape.js +52 -0
  53. package/dist/rules-default/api-response-shape.js.map +1 -0
  54. package/dist/rules-default/async-await-consistency.d.ts +3 -0
  55. package/dist/rules-default/async-await-consistency.d.ts.map +1 -0
  56. package/dist/rules-default/async-await-consistency.js +38 -0
  57. package/dist/rules-default/async-await-consistency.js.map +1 -0
  58. package/dist/rules-default/audit-log-ai-decision.d.ts +3 -0
  59. package/dist/rules-default/audit-log-ai-decision.d.ts.map +1 -0
  60. package/dist/rules-default/audit-log-ai-decision.js +33 -0
  61. package/dist/rules-default/audit-log-ai-decision.js.map +1 -0
  62. package/dist/rules-default/camelcase-migration-column.d.ts +3 -0
  63. package/dist/rules-default/camelcase-migration-column.d.ts.map +1 -0
  64. package/dist/rules-default/camelcase-migration-column.js +56 -0
  65. package/dist/rules-default/camelcase-migration-column.js.map +1 -0
  66. package/dist/rules-default/controller-needs-async-wrapper.d.ts +3 -0
  67. package/dist/rules-default/controller-needs-async-wrapper.d.ts.map +1 -0
  68. package/dist/rules-default/controller-needs-async-wrapper.js +56 -0
  69. package/dist/rules-default/controller-needs-async-wrapper.js.map +1 -0
  70. package/dist/rules-default/enforce-utf8-encoding.d.ts +10 -0
  71. package/dist/rules-default/enforce-utf8-encoding.d.ts.map +1 -0
  72. package/dist/rules-default/enforce-utf8-encoding.js +28 -0
  73. package/dist/rules-default/enforce-utf8-encoding.js.map +1 -0
  74. package/dist/rules-default/explicit-kst-timezone.d.ts +3 -0
  75. package/dist/rules-default/explicit-kst-timezone.d.ts.map +1 -0
  76. package/dist/rules-default/explicit-kst-timezone.js +49 -0
  77. package/dist/rules-default/explicit-kst-timezone.js.map +1 -0
  78. package/dist/rules-default/fk-needs-on-delete.d.ts +3 -0
  79. package/dist/rules-default/fk-needs-on-delete.d.ts.map +1 -0
  80. package/dist/rules-default/fk-needs-on-delete.js +54 -0
  81. package/dist/rules-default/fk-needs-on-delete.js.map +1 -0
  82. package/dist/rules-default/human-oversight-checkpoint.d.ts +3 -0
  83. package/dist/rules-default/human-oversight-checkpoint.d.ts.map +1 -0
  84. package/dist/rules-default/human-oversight-checkpoint.js +37 -0
  85. package/dist/rules-default/human-oversight-checkpoint.js.map +1 -0
  86. package/dist/rules-default/index.d.ts +6 -0
  87. package/dist/rules-default/index.d.ts.map +1 -0
  88. package/dist/rules-default/index.js +138 -0
  89. package/dist/rules-default/index.js.map +1 -0
  90. package/dist/rules-default/korean-comment-encoding.d.ts +3 -0
  91. package/dist/rules-default/korean-comment-encoding.d.ts.map +1 -0
  92. package/dist/rules-default/korean-comment-encoding.js +28 -0
  93. package/dist/rules-default/korean-comment-encoding.js.map +1 -0
  94. package/dist/rules-default/mask-card-number.d.ts +3 -0
  95. package/dist/rules-default/mask-card-number.d.ts.map +1 -0
  96. package/dist/rules-default/mask-card-number.js +45 -0
  97. package/dist/rules-default/mask-card-number.js.map +1 -0
  98. package/dist/rules-default/mask-pii-in-ai-prompt.d.ts +3 -0
  99. package/dist/rules-default/mask-pii-in-ai-prompt.d.ts.map +1 -0
  100. package/dist/rules-default/mask-pii-in-ai-prompt.js +41 -0
  101. package/dist/rules-default/mask-pii-in-ai-prompt.js.map +1 -0
  102. package/dist/rules-default/naver-kakao-oauth-webview.d.ts +3 -0
  103. package/dist/rules-default/naver-kakao-oauth-webview.d.ts.map +1 -0
  104. package/dist/rules-default/naver-kakao-oauth-webview.js +40 -0
  105. package/dist/rules-default/naver-kakao-oauth-webview.js.map +1 -0
  106. package/dist/rules-default/no-bare-except.d.ts +7 -0
  107. package/dist/rules-default/no-bare-except.d.ts.map +1 -0
  108. package/dist/rules-default/no-bare-except.js +23 -0
  109. package/dist/rules-default/no-bare-except.js.map +1 -0
  110. package/dist/rules-default/no-bare-throw.d.ts +7 -0
  111. package/dist/rules-default/no-bare-throw.d.ts.map +1 -0
  112. package/dist/rules-default/no-bare-throw.js +32 -0
  113. package/dist/rules-default/no-bare-throw.js.map +1 -0
  114. package/dist/rules-default/no-boolean-trap.d.ts +8 -0
  115. package/dist/rules-default/no-boolean-trap.d.ts.map +1 -0
  116. package/dist/rules-default/no-boolean-trap.js +33 -0
  117. package/dist/rules-default/no-boolean-trap.js.map +1 -0
  118. package/dist/rules-default/no-console-log.d.ts +3 -0
  119. package/dist/rules-default/no-console-log.d.ts.map +1 -0
  120. package/dist/rules-default/no-console-log.js +27 -0
  121. package/dist/rules-default/no-console-log.js.map +1 -0
  122. package/dist/rules-default/no-cvv-logging.d.ts +3 -0
  123. package/dist/rules-default/no-cvv-logging.d.ts.map +1 -0
  124. package/dist/rules-default/no-cvv-logging.js +36 -0
  125. package/dist/rules-default/no-cvv-logging.js.map +1 -0
  126. package/dist/rules-default/no-default-export-from-libs.d.ts +7 -0
  127. package/dist/rules-default/no-default-export-from-libs.d.ts.map +1 -0
  128. package/dist/rules-default/no-default-export-from-libs.js +24 -0
  129. package/dist/rules-default/no-default-export-from-libs.js.map +1 -0
  130. package/dist/rules-default/no-direct-anthropic.yaml +15 -0
  131. package/dist/rules-default/no-direct-openai.yaml +17 -0
  132. package/dist/rules-default/no-empty-catch.d.ts +3 -0
  133. package/dist/rules-default/no-empty-catch.d.ts.map +1 -0
  134. package/dist/rules-default/no-empty-catch.js +37 -0
  135. package/dist/rules-default/no-empty-catch.js.map +1 -0
  136. package/dist/rules-default/no-fstring-sql.d.ts +3 -0
  137. package/dist/rules-default/no-fstring-sql.d.ts.map +1 -0
  138. package/dist/rules-default/no-fstring-sql.js +24 -0
  139. package/dist/rules-default/no-fstring-sql.js.map +1 -0
  140. package/dist/rules-default/no-id-overwrite.d.ts +8 -0
  141. package/dist/rules-default/no-id-overwrite.d.ts.map +1 -0
  142. package/dist/rules-default/no-id-overwrite.js +27 -0
  143. package/dist/rules-default/no-id-overwrite.js.map +1 -0
  144. package/dist/rules-default/no-inline-date.yaml +9 -0
  145. package/dist/rules-default/no-inline-math-round.yaml +12 -0
  146. package/dist/rules-default/no-jsonb-circular.d.ts +3 -0
  147. package/dist/rules-default/no-jsonb-circular.d.ts.map +1 -0
  148. package/dist/rules-default/no-jsonb-circular.js +33 -0
  149. package/dist/rules-default/no-jsonb-circular.js.map +1 -0
  150. package/dist/rules-default/no-magic-number.d.ts +3 -0
  151. package/dist/rules-default/no-magic-number.d.ts.map +1 -0
  152. package/dist/rules-default/no-magic-number.js +145 -0
  153. package/dist/rules-default/no-magic-number.js.map +1 -0
  154. package/dist/rules-default/no-mutable-default-arg.d.ts +3 -0
  155. package/dist/rules-default/no-mutable-default-arg.d.ts.map +1 -0
  156. package/dist/rules-default/no-mutable-default-arg.js +30 -0
  157. package/dist/rules-default/no-mutable-default-arg.js.map +1 -0
  158. package/dist/rules-default/no-pickle.yaml +12 -0
  159. package/dist/rules-default/no-plain-card-number.d.ts +3 -0
  160. package/dist/rules-default/no-plain-card-number.d.ts.map +1 -0
  161. package/dist/rules-default/no-plain-card-number.js +51 -0
  162. package/dist/rules-default/no-plain-card-number.js.map +1 -0
  163. package/dist/rules-default/no-print-in-prod.yaml +9 -0
  164. package/dist/rules-default/no-process-env-leak.d.ts +7 -0
  165. package/dist/rules-default/no-process-env-leak.d.ts.map +1 -0
  166. package/dist/rules-default/no-process-env-leak.js +37 -0
  167. package/dist/rules-default/no-process-env-leak.js.map +1 -0
  168. package/dist/rules-default/no-shell-true.d.ts +3 -0
  169. package/dist/rules-default/no-shell-true.d.ts.map +1 -0
  170. package/dist/rules-default/no-shell-true.js +42 -0
  171. package/dist/rules-default/no-shell-true.js.map +1 -0
  172. package/dist/rules-default/prefer-const-array.d.ts +9 -0
  173. package/dist/rules-default/prefer-const-array.d.ts.map +1 -0
  174. package/dist/rules-default/prefer-const-array.js +34 -0
  175. package/dist/rules-default/prefer-const-array.js.map +1 -0
  176. package/dist/rules-default/prefer-named-imports.d.ts +8 -0
  177. package/dist/rules-default/prefer-named-imports.d.ts.map +1 -0
  178. package/dist/rules-default/prefer-named-imports.js +25 -0
  179. package/dist/rules-default/prefer-named-imports.js.map +1 -0
  180. package/dist/rules-default/preserve-transaction-log.d.ts +3 -0
  181. package/dist/rules-default/preserve-transaction-log.d.ts.map +1 -0
  182. package/dist/rules-default/preserve-transaction-log.js +33 -0
  183. package/dist/rules-default/preserve-transaction-log.js.map +1 -0
  184. package/dist/rules-default/pytest-fixture-naming.d.ts +3 -0
  185. package/dist/rules-default/pytest-fixture-naming.d.ts.map +1 -0
  186. package/dist/rules-default/pytest-fixture-naming.js +36 -0
  187. package/dist/rules-default/pytest-fixture-naming.js.map +1 -0
  188. package/dist/rules-default/requests-needs-timeout.d.ts +3 -0
  189. package/dist/rules-default/requests-needs-timeout.d.ts.map +1 -0
  190. package/dist/rules-default/requests-needs-timeout.js +43 -0
  191. package/dist/rules-default/requests-needs-timeout.js.map +1 -0
  192. package/dist/rules-default/require-idempotency-key.d.ts +3 -0
  193. package/dist/rules-default/require-idempotency-key.d.ts.map +1 -0
  194. package/dist/rules-default/require-idempotency-key.js +47 -0
  195. package/dist/rules-default/require-idempotency-key.js.map +1 -0
  196. package/dist/rules-default/require-tls-1-2-plus.d.ts +3 -0
  197. package/dist/rules-default/require-tls-1-2-plus.d.ts.map +1 -0
  198. package/dist/rules-default/require-tls-1-2-plus.js +30 -0
  199. package/dist/rules-default/require-tls-1-2-plus.js.map +1 -0
  200. package/dist/rules-default/rfc5987-korean-filename.d.ts +3 -0
  201. package/dist/rules-default/rfc5987-korean-filename.d.ts.map +1 -0
  202. package/dist/rules-default/rfc5987-korean-filename.js +32 -0
  203. package/dist/rules-default/rfc5987-korean-filename.js.map +1 -0
  204. package/dist/rules-default/route-needs-auth.d.ts +3 -0
  205. package/dist/rules-default/route-needs-auth.d.ts.map +1 -0
  206. package/dist/rules-default/route-needs-auth.js +48 -0
  207. package/dist/rules-default/route-needs-auth.js.map +1 -0
  208. package/dist/rules-default/route-needs-rate-limit.d.ts +3 -0
  209. package/dist/rules-default/route-needs-rate-limit.d.ts.map +1 -0
  210. package/dist/rules-default/route-needs-rate-limit.js +47 -0
  211. package/dist/rules-default/route-needs-rate-limit.js.map +1 -0
  212. package/dist/rules-default/separate-refund-permission.d.ts +3 -0
  213. package/dist/rules-default/separate-refund-permission.d.ts.map +1 -0
  214. package/dist/rules-default/separate-refund-permission.js +33 -0
  215. package/dist/rules-default/separate-refund-permission.js.map +1 -0
  216. package/dist/rules-default/track-ai-model-version.d.ts +3 -0
  217. package/dist/rules-default/track-ai-model-version.d.ts.map +1 -0
  218. package/dist/rules-default/track-ai-model-version.js +37 -0
  219. package/dist/rules-default/track-ai-model-version.js.map +1 -0
  220. package/dist/rules-default/type-hint-required-public.d.ts +10 -0
  221. package/dist/rules-default/type-hint-required-public.d.ts.map +1 -0
  222. package/dist/rules-default/type-hint-required-public.js +29 -0
  223. package/dist/rules-default/type-hint-required-public.js.map +1 -0
  224. package/dist/rules-default/verify-pg-response.d.ts +3 -0
  225. package/dist/rules-default/verify-pg-response.d.ts.map +1 -0
  226. package/dist/rules-default/verify-pg-response.js +37 -0
  227. package/dist/rules-default/verify-pg-response.js.map +1 -0
  228. package/dist/rules-default/won-format-thousands.d.ts +3 -0
  229. package/dist/rules-default/won-format-thousands.d.ts.map +1 -0
  230. package/dist/rules-default/won-format-thousands.js +24 -0
  231. package/dist/rules-default/won-format-thousands.js.map +1 -0
  232. package/dist/runner/apply-rule-config.d.ts +69 -0
  233. package/dist/runner/apply-rule-config.d.ts.map +1 -0
  234. package/dist/runner/apply-rule-config.js +164 -0
  235. package/dist/runner/apply-rule-config.js.map +1 -0
  236. package/dist/runner/context.d.ts +12 -0
  237. package/dist/runner/context.d.ts.map +1 -0
  238. package/dist/runner/context.js +34 -0
  239. package/dist/runner/context.js.map +1 -0
  240. package/dist/runner/index.d.ts +9 -0
  241. package/dist/runner/index.d.ts.map +1 -0
  242. package/dist/runner/index.js +6 -0
  243. package/dist/runner/index.js.map +1 -0
  244. package/dist/runner/ruleset-signature.d.ts +4 -0
  245. package/dist/runner/ruleset-signature.d.ts.map +1 -0
  246. package/dist/runner/ruleset-signature.js +11 -0
  247. package/dist/runner/ruleset-signature.js.map +1 -0
  248. package/dist/runner/run-file.d.ts +10 -0
  249. package/dist/runner/run-file.d.ts.map +1 -0
  250. package/dist/runner/run-file.js +48 -0
  251. package/dist/runner/run-file.js.map +1 -0
  252. package/dist/runner/run-project.d.ts +30 -0
  253. package/dist/runner/run-project.d.ts.map +1 -0
  254. package/dist/runner/run-project.js +118 -0
  255. package/dist/runner/run-project.js.map +1 -0
  256. package/dist/runner/run-rule.d.ts +5 -0
  257. package/dist/runner/run-rule.d.ts.map +1 -0
  258. package/dist/runner/run-rule.js +53 -0
  259. package/dist/runner/run-rule.js.map +1 -0
  260. package/dist/runner/suppressions.d.ts +37 -0
  261. package/dist/runner/suppressions.d.ts.map +1 -0
  262. package/dist/runner/suppressions.js +127 -0
  263. package/dist/runner/suppressions.js.map +1 -0
  264. package/dist/suggest/analyze.d.ts +10 -0
  265. package/dist/suggest/analyze.d.ts.map +1 -0
  266. package/dist/suggest/analyze.js +193 -0
  267. package/dist/suggest/analyze.js.map +1 -0
  268. package/dist/suggest/format.d.ts +13 -0
  269. package/dist/suggest/format.d.ts.map +1 -0
  270. package/dist/suggest/format.js +120 -0
  271. package/dist/suggest/format.js.map +1 -0
  272. package/dist/suggest/index.d.ts +5 -0
  273. package/dist/suggest/index.d.ts.map +1 -0
  274. package/dist/suggest/index.js +4 -0
  275. package/dist/suggest/index.js.map +1 -0
  276. package/dist/suggest/mine.d.ts +11 -0
  277. package/dist/suggest/mine.d.ts.map +1 -0
  278. package/dist/suggest/mine.js +207 -0
  279. package/dist/suggest/mine.js.map +1 -0
  280. package/dist/suggest/types.d.ts +74 -0
  281. package/dist/suggest/types.d.ts.map +1 -0
  282. package/dist/suggest/types.js +2 -0
  283. package/dist/suggest/types.js.map +1 -0
  284. package/dist/sync/index.d.ts +5 -0
  285. package/dist/sync/index.d.ts.map +1 -0
  286. package/dist/sync/index.js +3 -0
  287. package/dist/sync/index.js.map +1 -0
  288. package/dist/sync/render.d.ts +9 -0
  289. package/dist/sync/render.d.ts.map +1 -0
  290. package/dist/sync/render.js +70 -0
  291. package/dist/sync/render.js.map +1 -0
  292. package/dist/sync/sync-files.d.ts +12 -0
  293. package/dist/sync/sync-files.d.ts.map +1 -0
  294. package/dist/sync/sync-files.js +29 -0
  295. package/dist/sync/sync-files.js.map +1 -0
  296. package/package.json +71 -0
  297. package/scripts/copy-yaml-rules.mjs +19 -0
@@ -0,0 +1,24 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ /**
3
+ * Inside a `libs/` or `packages/` directory, prefer named exports for tree-shaking
4
+ * and clearer intent. `export default` makes refactoring/renaming harder across consumers.
5
+ */
6
+ export default defineRule({
7
+ id: 'no-default-export-from-libs',
8
+ language: ['typescript', 'javascript', 'tsx'],
9
+ severity: 'warning',
10
+ message: 'Use named exports from libraries (default exports hinder tree-shaking and renames).',
11
+ messageKo: '라이브러리는 named export를 사용하세요 (default export는 tree-shaking·이름 변경에 불리).',
12
+ visitors: {
13
+ export_statement(node, ctx) {
14
+ // Only flag `export default ...` patterns inside lib/package directories
15
+ if (!/(^|[/\\])(libs?|packages)[/\\]/.test(ctx.filePath))
16
+ return;
17
+ const text = ctx.textOf(node);
18
+ if (text.startsWith('export default ')) {
19
+ ctx.report({ node });
20
+ }
21
+ },
22
+ },
23
+ });
24
+ //# sourceMappingURL=no-default-export-from-libs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-default-export-from-libs.js","sourceRoot":"","sources":["../../src/rules-default/no-default-export-from-libs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;GAGG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,6BAA6B;IACjC,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC;IAC7C,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,qFAAqF;IAC9F,SAAS,EAAE,sEAAsE;IACjF,QAAQ,EAAE;QACR,gBAAgB,CAAC,IAAI,EAAE,GAAG;YACxB,yEAAyE;YACzE,IAAI,CAAC,gCAAgC,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,OAAO;YACjE,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACvC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ id: no-direct-anthropic
2
+ language: typescript
3
+ severity: error
4
+ message: Use the llmClient singleton. Direct Anthropic instantiation is forbidden.
5
+ messageKo: llmClient 싱글톤을 사용하세요. 직접 Anthropic 인스턴스화는 금지입니다.
6
+ # Allow the designated LLM wrapper module to construct the SDK once. Conventions only —
7
+ # add an inline `// aicq-disable-next-line no-direct-anthropic` outside these paths.
8
+ pathExclude:
9
+ - '**/llm/client.*'
10
+ - '**/llm/index.*'
11
+ - '**/ai/**/*client*.*'
12
+ query: |
13
+ (new_expression
14
+ constructor: (identifier) @ctor
15
+ (#eq? @ctor "Anthropic"))
@@ -0,0 +1,17 @@
1
+ id: no-direct-openai
2
+ language: typescript
3
+ severity: error
4
+ message: Use the llmClient singleton. Direct OpenAI instantiation is forbidden.
5
+ messageKo: llmClient 싱글톤을 사용하세요. 직접 OpenAI 인스턴스화는 금지입니다.
6
+ # Allow the *designated* LLM wrapper module to construct the SDK once. Conventions only —
7
+ # the project structures `src/**/llm/{client,index}.ts` or `src/**/ai/**/{*client*}.ts`
8
+ # as the single permitted call-site. Add an inline `// aicq-disable-next-line no-direct-openai`
9
+ # for one-off exceptions outside these paths.
10
+ pathExclude:
11
+ - '**/llm/client.*'
12
+ - '**/llm/index.*'
13
+ - '**/ai/**/*client*.*'
14
+ query: |
15
+ (new_expression
16
+ constructor: (identifier) @ctor
17
+ (#eq? @ctor "OpenAI"))
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
2
+ export default _default;
3
+ //# sourceMappingURL=no-empty-catch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-empty-catch.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-empty-catch.ts"],"names":[],"mappings":";AAYA,wBAsBG"}
@@ -0,0 +1,37 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ /**
3
+ * Forbids empty catch blocks. At minimum, log or rethrow.
4
+ * Catches the silent-failure anti-pattern that masks bugs.
5
+ *
6
+ * Skip: Capacitor's `native-bridge.{js,ts}` and PWA `service-worker.{js,ts}` regularly
7
+ * swallow exceptions on purpose (the bridge/SW must never crash the host). The rule still
8
+ * fires elsewhere on the same files via other lints.
9
+ */
10
+ const SKIP_FILE_RE = /[/\\](native-bridge|service-worker)\.[jt]sx?$/;
11
+ export default defineRule({
12
+ id: 'no-empty-catch',
13
+ language: ['typescript', 'javascript', 'tsx'],
14
+ severity: 'error',
15
+ message: 'catch block must not be empty — log or rethrow.',
16
+ messageKo: 'catch 블록은 비어 있을 수 없습니다 — log하거나 rethrow하세요.',
17
+ visitors: {
18
+ catch_clause(node, ctx) {
19
+ if (SKIP_FILE_RE.test(ctx.filePath))
20
+ return;
21
+ const body = node.childForFieldName('body');
22
+ if (!body)
23
+ return;
24
+ let hasStmt = false;
25
+ for (let i = 0; i < body.namedChildCount; i++) {
26
+ const child = body.namedChild(i);
27
+ if (child && child.type !== 'comment') {
28
+ hasStmt = true;
29
+ break;
30
+ }
31
+ }
32
+ if (!hasStmt)
33
+ ctx.report({ node });
34
+ },
35
+ },
36
+ });
37
+ //# sourceMappingURL=no-empty-catch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-empty-catch.js","sourceRoot":"","sources":["../../src/rules-default/no-empty-catch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,YAAY,GAAG,+CAA+C,CAAC;AAErE,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,gBAAgB;IACpB,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC;IAC7C,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,iDAAiD;IAC1D,SAAS,EAAE,6CAA6C;IACxD,QAAQ,EAAE;QACR,YAAY,CAAC,IAAI,EAAE,GAAG;YACpB,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,OAAO;YAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBACjC,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBACtC,OAAO,GAAG,IAAI,CAAC;oBACf,MAAM;gBACR,CAAC;YACH,CAAC;YACD,IAAI,CAAC,OAAO;gBAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACrC,CAAC;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
2
+ export default _default;
3
+ //# sourceMappingURL=no-fstring-sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-fstring-sql.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-fstring-sql.ts"],"names":[],"mappings":";AAIA,wBAiBG"}
@@ -0,0 +1,24 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ const SQL_KEYWORD_PATTERN = /\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN|VALUES)\b/i;
3
+ export default defineRule({
4
+ id: 'no-fstring-sql',
5
+ language: 'python',
6
+ severity: 'error',
7
+ message: 'SQL inside f-string is a SQL injection vector — use parameterized queries.',
8
+ messageKo: 'f-string으로 SQL 조합은 SQL 주입 위험 — 파라미터 바인딩을 사용하세요.',
9
+ visitors: {
10
+ string(node, ctx) {
11
+ const text = ctx.textOf(node);
12
+ // Python f-string starts with f" or f' (or rf", fr", etc.)
13
+ if (!/^[a-zA-Z]*[fF][a-zA-Z]*['"]/.test(text))
14
+ return;
15
+ if (!SQL_KEYWORD_PATTERN.test(text))
16
+ return;
17
+ // Has interpolation `{...}`?
18
+ if (!/\{[^{}]+\}/.test(text))
19
+ return;
20
+ ctx.report({ node });
21
+ },
22
+ },
23
+ });
24
+ //# sourceMappingURL=no-fstring-sql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-fstring-sql.js","sourceRoot":"","sources":["../../src/rules-default/no-fstring-sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,mBAAmB,GAAG,2DAA2D,CAAC;AAExF,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,gBAAgB;IACpB,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,4EAA4E;IACrF,SAAS,EAAE,iDAAiD;IAC5D,QAAQ,EAAE;QACR,MAAM,CAAC,IAAI,EAAE,GAAG;YACd,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,2DAA2D;YAC3D,IAAI,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO;YACtD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO;YAC5C,6BAA6B;YAC7B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO;YACrC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvB,CAAC;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Forbid assignment to `<obj>.id`. IDs should be immutable after creation.
3
+ * Catches a common class of bugs where IDs are overwritten after creation
4
+ * (e.g. a record's id field reassigned to a child entity's id).
5
+ */
6
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
7
+ export default _default;
8
+ //# sourceMappingURL=no-id-overwrite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-id-overwrite.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-id-overwrite.ts"],"names":[],"mappings":"AAEA;;;;GAIG;;AACH,wBAiBG"}
@@ -0,0 +1,27 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ /**
3
+ * Forbid assignment to `<obj>.id`. IDs should be immutable after creation.
4
+ * Catches a common class of bugs where IDs are overwritten after creation
5
+ * (e.g. a record's id field reassigned to a child entity's id).
6
+ */
7
+ export default defineRule({
8
+ id: 'no-id-overwrite',
9
+ language: ['typescript', 'javascript', 'tsx'],
10
+ severity: 'error',
11
+ message: 'Do not reassign `.id`. IDs are immutable after creation.',
12
+ messageKo: '`.id` 필드는 생성 후 재할당하지 마세요. ID는 불변이어야 합니다.',
13
+ visitors: {
14
+ assignment_expression(node, ctx) {
15
+ const left = node.childForFieldName('left');
16
+ if (!left || left.type !== 'member_expression')
17
+ return;
18
+ const property = left.childForFieldName('property');
19
+ if (!property)
20
+ return;
21
+ if (ctx.textOf(property) === 'id') {
22
+ ctx.report({ node });
23
+ }
24
+ },
25
+ },
26
+ });
27
+ //# sourceMappingURL=no-id-overwrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-id-overwrite.js","sourceRoot":"","sources":["../../src/rules-default/no-id-overwrite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;GAIG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,iBAAiB;IACrB,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC;IAC7C,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,0DAA0D;IACnE,SAAS,EAAE,0CAA0C;IACrD,QAAQ,EAAE;QACR,qBAAqB,CAAC,IAAI,EAAE,GAAG;YAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,mBAAmB;gBAAE,OAAO;YACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YACpD,IAAI,CAAC,QAAQ;gBAAE,OAAO;YACtB,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC;gBAClC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ id: no-inline-date
2
+ language: typescript
3
+ severity: warning
4
+ message: Avoid inline `new Date()` calls; use dateHelper for testable, timezone-aware dates.
5
+ messageKo: 인라인 `new Date()` 호출 금지. 테스트 가능·타임존 인식을 위해 dateHelper를 사용하세요.
6
+ query: |
7
+ (new_expression
8
+ constructor: (identifier) @ctor
9
+ (#eq? @ctor "Date")) @call
@@ -0,0 +1,12 @@
1
+ id: no-inline-math-round
2
+ language: typescript
3
+ severity: warning
4
+ message: Avoid inline Math.round; use mathHelper for consistent rounding rules.
5
+ messageKo: Math.round 인라인 사용 금지. 일관된 반올림 규칙을 위해 mathHelper를 사용하세요.
6
+ query: |
7
+ (call_expression
8
+ function: (member_expression
9
+ object: (identifier) @obj
10
+ property: (property_identifier) @prop)
11
+ (#eq? @obj "Math")
12
+ (#eq? @prop "round")) @call
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
2
+ export default _default;
3
+ //# sourceMappingURL=no-jsonb-circular.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-jsonb-circular.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-jsonb-circular.ts"],"names":[],"mappings":";AASA,wBAoBG"}
@@ -0,0 +1,33 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ /**
3
+ * Heuristic: when `JSON.stringify(...)` is called on a value whose name suggests
4
+ * a request/response/model object (likely to contain circular references via ORM
5
+ * relations), warn. Encourages the explicit `safeStringify` or `replacer` patterns.
6
+ */
7
+ const SUSPICIOUS_NAME_PATTERN = /^(req|res|request|response|model|instance|entity|record|row|user|order|product|item)$/i;
8
+ export default defineRule({
9
+ id: 'no-jsonb-circular',
10
+ language: ['typescript', 'javascript', 'tsx'],
11
+ severity: 'warning',
12
+ message: 'JSON.stringify on ORM/request/response objects may hit circular references — use safeStringify or pick fields explicitly.',
13
+ messageKo: 'ORM/요청/응답 객체에 JSON.stringify는 순환 참조 위험 — safeStringify 또는 명시적 필드 선택 권장.',
14
+ visitors: {
15
+ call_expression(node, ctx) {
16
+ const fn = node.childForFieldName('function');
17
+ if (!fn)
18
+ return;
19
+ if (ctx.textOf(fn) !== 'JSON.stringify')
20
+ return;
21
+ const args = node.childForFieldName('arguments');
22
+ if (!args || args.namedChildCount === 0)
23
+ return;
24
+ const first = args.namedChild(0);
25
+ if (!first || first.type !== 'identifier')
26
+ return;
27
+ if (SUSPICIOUS_NAME_PATTERN.test(ctx.textOf(first))) {
28
+ ctx.report({ node });
29
+ }
30
+ },
31
+ },
32
+ });
33
+ //# sourceMappingURL=no-jsonb-circular.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-jsonb-circular.js","sourceRoot":"","sources":["../../src/rules-default/no-jsonb-circular.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;GAIG;AACH,MAAM,uBAAuB,GAAG,wFAAwF,CAAC;AAEzH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,mBAAmB;IACvB,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC;IAC7C,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,2HAA2H;IACpI,SAAS,EAAE,yEAAyE;IACpF,QAAQ,EAAE;QACR,eAAe,CAAC,IAAI,EAAE,GAAG;YACvB,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAC9C,IAAI,CAAC,EAAE;gBAAE,OAAO;YAChB,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,gBAAgB;gBAAE,OAAO;YAChD,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;YACjD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,eAAe,KAAK,CAAC;gBAAE,OAAO;YAChD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACjC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;gBAAE,OAAO;YAClD,IAAI,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBACpD,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
2
+ export default _default;
3
+ //# sourceMappingURL=no-magic-number.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-magic-number.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-magic-number.ts"],"names":[],"mappings":";AA6HA,wBAiBG"}
@@ -0,0 +1,145 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ /**
3
+ * `no-magic-number` — info-severity rule that flags numeric literals appearing inline in
4
+ * application code (rather than being given a named constant). The hardest part is choosing
5
+ * a defensible default for "what's NOT a magic number." This rule errs on the side of fewer
6
+ * false positives because it ships at `info` severity and historically dominates noise budgets.
7
+ *
8
+ * Skipped contexts (all heuristic; project-specific tuning belongs in a future per-rule
9
+ * options seam):
10
+ * - Test fixture / spec files (filename heuristic)
11
+ * - `*.config.{ts,tsx,js,jsx}`, `*.polyfill.{ts,tsx,js,jsx}`, `**\/polyfills/**` (filename)
12
+ * - Database migrations / seeders / fixtures directories (alpha.8) — these are data files where
13
+ * numeric literals are the payload, not magic numbers. Other rules (e.g. camelcase-migration-column)
14
+ * keep firing on the same files because this skip is scoped to no-magic-number only.
15
+ * - Variable declarator RHS (`const X = 7;` — 7 is being NAMED, not used inline)
16
+ * - JSX attribute values (`<View width={24} />`)
17
+ * - Enum members (`enum E { A = 7 }`)
18
+ * - Array index subscripts (`arr[42]`)
19
+ * - `for` loop initializer/condition/update
20
+ * - Arguments to obvious numeric APIs (`parseInt(s, 16)`, `Math.*`, `Number(...)`, `.toFixed(2)`,
21
+ * `.padStart(8, '0')`, `setTimeout(f, 100)`, `setInterval(f, 1000)`)
22
+ * - "Timeout-ish" variable-name contexts (carried over from alpha.6)
23
+ *
24
+ * Allowed-numbers default extended to common-sense values: powers of two, common bases,
25
+ * time/clock constants, etc.
26
+ */
27
+ const ALLOWED_NUMBERS = new Set([
28
+ '0', '1', '-1', '2', '-2', '10', '16', '24', '60', '100', '1000', '1024',
29
+ ]);
30
+ const SKIP_FILE_RE = /(\.test\.|\.spec\.|__tests__|fixtures|\.config\.|\.polyfill\.|[/\\]polyfills[/\\]|[/\\]seeders[/\\]|[/\\]migrations[/\\]|[/\\](scripts|tools|bin)[/\\]|[/\\](native-bridge|service-worker)\.[jt]sx?$)/;
31
+ // Callee names whose numeric args are almost always intentional, not magic numbers.
32
+ // Matched on the leaf identifier (last name segment) — covers both `parseInt(...)` and
33
+ // `Number.parseInt(...)` / `globalThis.setTimeout(...)`.
34
+ const NUMERIC_API_CALLEES = new Set([
35
+ 'parseInt', 'parseFloat', 'Number',
36
+ 'setTimeout', 'setInterval', 'setImmediate',
37
+ 'toFixed', 'toPrecision', 'toExponential', 'toString',
38
+ 'padStart', 'padEnd', 'repeat', 'slice', 'substr', 'substring', 'charAt', 'codePointAt',
39
+ 'round', 'floor', 'ceil', 'trunc', 'abs', 'sign', 'min', 'max', 'pow', 'log',
40
+ ]);
41
+ function isInSkippedFile(filePath) {
42
+ return SKIP_FILE_RE.test(filePath);
43
+ }
44
+ function isInTimeoutMsContext(node, textOf) {
45
+ // Allow magic numbers in obvious time/duration contexts where the unit is in the variable name.
46
+ let parent = node.parent;
47
+ while (parent) {
48
+ const text = textOf(parent);
49
+ if (/timeout|interval|duration|delay|ms|sec|hour|day/i.test(text.slice(0, 40)))
50
+ return true;
51
+ if (parent.type === 'function_declaration' || parent.type === 'method_definition')
52
+ break;
53
+ parent = parent.parent;
54
+ }
55
+ return false;
56
+ }
57
+ const SKIP_ANCESTOR_TYPES = new Set([
58
+ // Variable declarator: `const RETRY = 5;` — the literal IS the named constant we want.
59
+ 'variable_declarator',
60
+ // Enum members: `enum E { A = 7 }`
61
+ 'enum_body',
62
+ 'enum_assignment',
63
+ // JSX attribute values: `<View width={24} />`
64
+ 'jsx_attribute',
65
+ 'jsx_expression',
66
+ // Array index subscripts: `arr[42]`
67
+ 'subscript_expression',
68
+ // For loop header: `for (let i = 0; i < 5; i++) {}`
69
+ 'for_statement',
70
+ 'for_in_statement',
71
+ 'for_of_statement',
72
+ ]);
73
+ function hasSkippableAncestor(node) {
74
+ let parent = node.parent;
75
+ let depth = 0;
76
+ while (parent && depth < 8) {
77
+ if (SKIP_ANCESTOR_TYPES.has(parent.type))
78
+ return true;
79
+ // Stop at clear statement / function boundaries so a number deep in a function body
80
+ // doesn't get silently allowed by a far-away for-loop ancestor.
81
+ if (parent.type === 'statement_block' ||
82
+ parent.type === 'function_declaration' ||
83
+ parent.type === 'method_definition' ||
84
+ parent.type === 'arrow_function' ||
85
+ parent.type === 'function_expression') {
86
+ return false;
87
+ }
88
+ parent = parent.parent;
89
+ depth += 1;
90
+ }
91
+ return false;
92
+ }
93
+ /**
94
+ * Returns true if the number is a positional argument to a call expression whose final
95
+ * identifier is a known numeric API (parseInt, parseFloat, Math.*, .toFixed, …).
96
+ */
97
+ function isArgOfNumericApi(node, textOf) {
98
+ const parent = node.parent;
99
+ if (!parent || parent.type !== 'arguments')
100
+ return false;
101
+ const callExpr = parent.parent;
102
+ if (!callExpr || callExpr.type !== 'call_expression')
103
+ return false;
104
+ const fn = callExpr.childForFieldName('function');
105
+ if (!fn)
106
+ return false;
107
+ // Pull out the *final* name segment whether the callee is `foo` or `Obj.foo` or `a.b.foo`.
108
+ let nameNode = fn;
109
+ while (nameNode && (nameNode.type === 'member_expression' || nameNode.type === 'parenthesized_expression')) {
110
+ const fromField = nameNode.childForFieldName('property');
111
+ const fallback = nameNode.namedChildren[nameNode.namedChildren.length - 1];
112
+ const prop = fromField ?? fallback ?? null;
113
+ if (!prop || prop === nameNode)
114
+ break;
115
+ nameNode = prop;
116
+ }
117
+ if (!nameNode)
118
+ return false;
119
+ const name = textOf(nameNode);
120
+ return NUMERIC_API_CALLEES.has(name);
121
+ }
122
+ export default defineRule({
123
+ id: 'no-magic-number',
124
+ language: ['typescript', 'javascript', 'tsx'],
125
+ severity: 'info',
126
+ message: 'Magic number — extract to a named constant for clarity.',
127
+ messageKo: '매직 넘버 — 명명된 상수로 추출해 의미를 명확히 하세요.',
128
+ visitors: {
129
+ number(node, ctx) {
130
+ if (isInSkippedFile(ctx.filePath))
131
+ return;
132
+ const text = ctx.textOf(node);
133
+ if (ALLOWED_NUMBERS.has(text))
134
+ return;
135
+ if (hasSkippableAncestor(node))
136
+ return;
137
+ if (isArgOfNumericApi(node, ctx.textOf))
138
+ return;
139
+ if (isInTimeoutMsContext(node, ctx.textOf))
140
+ return;
141
+ ctx.report({ node });
142
+ },
143
+ },
144
+ });
145
+ //# sourceMappingURL=no-magic-number.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-magic-number.js","sourceRoot":"","sources":["../../src/rules-default/no-magic-number.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
2
+ export default _default;
3
+ //# sourceMappingURL=no-mutable-default-arg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-mutable-default-arg.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-mutable-default-arg.ts"],"names":[],"mappings":";AAOA,wBAsBG"}
@@ -0,0 +1,30 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ function isMutableLiteral(node) {
3
+ return node.type === 'list' || node.type === 'dictionary' || node.type === 'set';
4
+ }
5
+ export default defineRule({
6
+ id: 'no-mutable-default-arg',
7
+ language: 'python',
8
+ severity: 'error',
9
+ message: 'Mutable default argument (list/dict/set) is shared across calls — use None and initialize inside.',
10
+ messageKo: '가변 기본 인자(list/dict/set)는 모든 호출에 공유됩니다 — None을 기본값으로 두고 함수 내부에서 초기화하세요.',
11
+ visitors: {
12
+ function_definition(node, ctx) {
13
+ const params = node.childForFieldName('parameters');
14
+ if (!params)
15
+ return;
16
+ for (let i = 0; i < params.namedChildCount; i++) {
17
+ const param = params.namedChild(i);
18
+ if (!param)
19
+ continue;
20
+ if (param.type === 'default_parameter' || param.type === 'typed_default_parameter') {
21
+ const value = param.childForFieldName('value');
22
+ if (value && isMutableLiteral(value)) {
23
+ ctx.report({ node: param });
24
+ }
25
+ }
26
+ }
27
+ },
28
+ },
29
+ });
30
+ //# sourceMappingURL=no-mutable-default-arg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-mutable-default-arg.js","sourceRoot":"","sources":["../../src/rules-default/no-mutable-default-arg.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,SAAS,gBAAgB,CAAC,IAAuB;IAC/C,OAAO,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC;AACnF,CAAC;AAED,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,wBAAwB;IAC5B,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,mGAAmG;IAC5G,SAAS,EAAE,wEAAwE;IACnF,QAAQ,EAAE;QACR,mBAAmB,CAAC,IAAI,EAAE,GAAG;YAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;YACpD,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC;gBAChD,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBACnC,IAAI,CAAC,KAAK;oBAAE,SAAS;gBACrB,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB,IAAI,KAAK,CAAC,IAAI,KAAK,yBAAyB,EAAE,CAAC;oBACnF,MAAM,KAAK,GAAG,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;oBAC/C,IAAI,KAAK,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;wBACrC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;oBAC9B,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ id: no-pickle
2
+ language: python
3
+ severity: error
4
+ message: pickle.loads/load is unsafe — never deserialize untrusted data. Use json or msgpack.
5
+ messageKo: pickle.loads/load는 안전하지 않습니다 — 신뢰할 수 없는 데이터 역직렬화 금지. json 또는 msgpack을 사용하세요.
6
+ query: |
7
+ (call
8
+ function: (attribute
9
+ object: (identifier) @mod
10
+ attribute: (identifier) @attr)
11
+ (#eq? @mod "pickle")
12
+ (#match? @attr "^(load|loads)$")) @call
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
2
+ export default _default;
3
+ //# sourceMappingURL=no-plain-card-number.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-plain-card-number.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-plain-card-number.ts"],"names":[],"mappings":";AAiBA,wBA6BG"}
@@ -0,0 +1,51 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ /**
3
+ * PCI DSS: card numbers (PAN) must never be stored in plaintext columns.
4
+ * Detect Sequelize/Prisma/TypeORM-style schema definitions where a column
5
+ * named `card_number` / `cardNumber` is declared with a plain string type
6
+ * and no encryption/hash suffix in the column name.
7
+ *
8
+ * Limitation: pattern-based — does not understand custom encryption
9
+ * wrappers. Severity stays at `error` because the cost of a false positive
10
+ * (rename column) is far smaller than the cost of a true positive.
11
+ */
12
+ const PAYMENT_NAME = /^(card[_-]?number|pan|primary[_-]?account[_-]?number)$/i;
13
+ const ENCRYPTED_HINT = /(encrypted|hashed|tokenized|masked|encr|hash)/i;
14
+ export default defineRule({
15
+ id: 'no-plain-card-number',
16
+ language: ['typescript', 'tsx'],
17
+ severity: 'error',
18
+ message: 'Card number column appears to be stored in plaintext (PCI DSS § 3.5.1 — render PAN unreadable).',
19
+ messageKo: '카드번호 컬럼이 평문으로 저장되는 것으로 보입니다 (PCI DSS § 3.5.1 — PAN 평문 저장 금지, 암호화/토큰화 필수).',
20
+ docs: 'https://github.com/aicqtools/aicqtools/blob/main/docs/rules/no-plain-card-number.md',
21
+ visitors: {
22
+ pair(node, ctx) {
23
+ const key = node.childForFieldName('key');
24
+ if (!key)
25
+ return;
26
+ const keyText = ctx.textOf(key).replace(/^['"`]|['"`]$/g, '');
27
+ if (!PAYMENT_NAME.test(keyText))
28
+ return;
29
+ const value = node.childForFieldName('value');
30
+ if (!value)
31
+ return;
32
+ const valueText = ctx.textOf(value);
33
+ if (ENCRYPTED_HINT.test(valueText))
34
+ return;
35
+ ctx.report({ node });
36
+ },
37
+ property_signature(node, ctx) {
38
+ const name = node.childForFieldName('name');
39
+ if (!name)
40
+ return;
41
+ const text = ctx.textOf(name);
42
+ if (!PAYMENT_NAME.test(text))
43
+ return;
44
+ const full = ctx.textOf(node);
45
+ if (ENCRYPTED_HINT.test(full))
46
+ return;
47
+ ctx.report({ node });
48
+ },
49
+ },
50
+ });
51
+ //# sourceMappingURL=no-plain-card-number.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-plain-card-number.js","sourceRoot":"","sources":["../../src/rules-default/no-plain-card-number.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD;;;;;;;;;GASG;AACH,MAAM,YAAY,GAAG,yDAAyD,CAAC;AAC/E,MAAM,cAAc,GAAG,gDAAgD,CAAC;AAExE,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,sBAAsB;IAC1B,QAAQ,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC;IAC/B,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,iGAAiG;IAC1G,SAAS,EAAE,2EAA2E;IACtF,IAAI,EAAE,qFAAqF;IAC3F,QAAQ,EAAE;QACR,IAAI,CAAC,IAAuB,EAAE,GAAgB;YAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC1C,IAAI,CAAC,GAAG;gBAAE,OAAO;YACjB,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;YAC9D,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,OAAO;YACxC,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;YAC9C,IAAI,CAAC,KAAK;gBAAE,OAAO;YACnB,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACpC,IAAI,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC;gBAAE,OAAO;YAC3C,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvB,CAAC;QACD,kBAAkB,CAAC,IAAuB,EAAE,GAAgB;YAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO;YACrC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO;YACtC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvB,CAAC;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ id: no-print-in-prod
2
+ language: python
3
+ severity: warning
4
+ message: Avoid bare print() in production code; use the standard `logging` module.
5
+ messageKo: 운영 코드에서 print() 사용 지양 — 표준 `logging` 모듈을 사용하세요.
6
+ query: |
7
+ (call
8
+ function: (identifier) @fn
9
+ (#eq? @fn "print")) @call
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Forbids direct `process.env.X` access outside of designated config modules.
3
+ * Centralize env reading in `config/`, `env/`, or `*.config.ts` for type-safety + validation.
4
+ */
5
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
6
+ export default _default;
7
+ //# sourceMappingURL=no-process-env-leak.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-process-env-leak.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-process-env-leak.ts"],"names":[],"mappings":"AAEA;;;GAGG;;AACH,wBA8BG"}
@@ -0,0 +1,37 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ /**
3
+ * Forbids direct `process.env.X` access outside of designated config modules.
4
+ * Centralize env reading in `config/`, `env/`, or `*.config.ts` for type-safety + validation.
5
+ */
6
+ export default defineRule({
7
+ id: 'no-process-env-leak',
8
+ language: ['typescript', 'javascript', 'tsx'],
9
+ severity: 'warning',
10
+ message: 'Read `process.env` only via the config module (config/env.ts).',
11
+ messageKo: '`process.env` 직접 접근 금지 — config 모듈(config/env.ts)을 통해서만 읽으세요.',
12
+ visitors: {
13
+ member_expression(node, ctx) {
14
+ const obj = node.childForFieldName('object');
15
+ const prop = node.childForFieldName('property');
16
+ if (!obj || !prop)
17
+ return;
18
+ if (obj.type === 'member_expression') {
19
+ const inner = obj.childForFieldName('object');
20
+ const innerProp = obj.childForFieldName('property');
21
+ if (inner &&
22
+ innerProp &&
23
+ ctx.textOf(inner) === 'process' &&
24
+ ctx.textOf(innerProp) === 'env') {
25
+ // Allowlist: `config/`, `env/`, and `*.config.*` / `*.env.*` files (e.g. `next.config.ts`,
26
+ // `vite.config.ts`, `jest.config.ts`). Includes a leading `.` in the prefix character
27
+ // class so dotted filenames where `config`/`env` is preceded by `.` (e.g. `next.config.ts`)
28
+ // are recognized.
29
+ if (/(^|[/\\.])(config|env)([/\\.]|$)/.test(ctx.filePath))
30
+ return;
31
+ ctx.report({ node });
32
+ }
33
+ }
34
+ },
35
+ },
36
+ });
37
+ //# sourceMappingURL=no-process-env-leak.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-process-env-leak.js","sourceRoot":"","sources":["../../src/rules-default/no-process-env-leak.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;GAGG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,qBAAqB;IACzB,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC;IAC7C,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,gEAAgE;IACzE,SAAS,EAAE,+DAA+D;IAC1E,QAAQ,EAAE;QACR,iBAAiB,CAAC,IAAI,EAAE,GAAG;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAChD,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI;gBAAE,OAAO;YAC1B,IAAI,GAAG,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;gBACrC,MAAM,KAAK,GAAG,GAAG,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;gBAC9C,MAAM,SAAS,GAAG,GAAG,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;gBACpD,IACE,KAAK;oBACL,SAAS;oBACT,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,SAAS;oBAC/B,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,KAAK,EAC/B,CAAC;oBACD,2FAA2F;oBAC3F,sFAAsF;oBACtF,4FAA4F;oBAC5F,kBAAkB;oBAClB,IAAI,kCAAkC,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;wBAAE,OAAO;oBAClE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC;KACF;CACF,CAAC,CAAC"}