@aicqtools/guardrail 1.0.0-alpha.2

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 (269) 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 +12 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +7 -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 +31 -0
  27. package/dist/matcher/yaml-rule.d.ts.map +1 -0
  28. package/dist/matcher/yaml-rule.js +30 -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 +19 -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 +9 -0
  131. package/dist/rules-default/no-direct-openai.yaml +9 -0
  132. package/dist/rules-default/no-empty-catch.d.ts +7 -0
  133. package/dist/rules-default/no-empty-catch.d.ts.map +1 -0
  134. package/dist/rules-default/no-empty-catch.js +30 -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 +7 -0
  141. package/dist/rules-default/no-id-overwrite.d.ts.map +1 -0
  142. package/dist/rules-default/no-id-overwrite.js +26 -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 +42 -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 +34 -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/context.d.ts +12 -0
  233. package/dist/runner/context.d.ts.map +1 -0
  234. package/dist/runner/context.js +34 -0
  235. package/dist/runner/context.js.map +1 -0
  236. package/dist/runner/index.d.ts +7 -0
  237. package/dist/runner/index.d.ts.map +1 -0
  238. package/dist/runner/index.js +5 -0
  239. package/dist/runner/index.js.map +1 -0
  240. package/dist/runner/ruleset-signature.d.ts +4 -0
  241. package/dist/runner/ruleset-signature.d.ts.map +1 -0
  242. package/dist/runner/ruleset-signature.js +11 -0
  243. package/dist/runner/ruleset-signature.js.map +1 -0
  244. package/dist/runner/run-file.d.ts +10 -0
  245. package/dist/runner/run-file.d.ts.map +1 -0
  246. package/dist/runner/run-file.js +20 -0
  247. package/dist/runner/run-file.js.map +1 -0
  248. package/dist/runner/run-project.d.ts +12 -0
  249. package/dist/runner/run-project.d.ts.map +1 -0
  250. package/dist/runner/run-project.js +55 -0
  251. package/dist/runner/run-project.js.map +1 -0
  252. package/dist/runner/run-rule.d.ts +5 -0
  253. package/dist/runner/run-rule.d.ts.map +1 -0
  254. package/dist/runner/run-rule.js +37 -0
  255. package/dist/runner/run-rule.js.map +1 -0
  256. package/dist/sync/index.d.ts +5 -0
  257. package/dist/sync/index.d.ts.map +1 -0
  258. package/dist/sync/index.js +3 -0
  259. package/dist/sync/index.js.map +1 -0
  260. package/dist/sync/render.d.ts +9 -0
  261. package/dist/sync/render.d.ts.map +1 -0
  262. package/dist/sync/render.js +70 -0
  263. package/dist/sync/render.js.map +1 -0
  264. package/dist/sync/sync-files.d.ts +12 -0
  265. package/dist/sync/sync-files.d.ts.map +1 -0
  266. package/dist/sync/sync-files.js +29 -0
  267. package/dist/sync/sync-files.js.map +1 -0
  268. package/package.json +62 -0
  269. package/scripts/copy-yaml-rules.mjs +19 -0
@@ -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,9 @@
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
+ query: |
7
+ (new_expression
8
+ constructor: (identifier) @ctor
9
+ (#eq? @ctor "Anthropic"))
@@ -0,0 +1,9 @@
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
+ query: |
7
+ (new_expression
8
+ constructor: (identifier) @ctor
9
+ (#eq? @ctor "OpenAI"))
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Forbids empty catch blocks. At minimum, log or rethrow.
3
+ * Catches the silent-failure anti-pattern that masks bugs.
4
+ */
5
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
6
+ export default _default;
7
+ //# 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":"AAEA;;;GAGG;;AACH,wBAqBG"}
@@ -0,0 +1,30 @@
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
+ export default defineRule({
7
+ id: 'no-empty-catch',
8
+ language: ['typescript', 'javascript', 'tsx'],
9
+ severity: 'error',
10
+ message: 'catch block must not be empty — log or rethrow.',
11
+ messageKo: 'catch 블록은 비어 있을 수 없습니다 — log하거나 rethrow하세요.',
12
+ visitors: {
13
+ catch_clause(node, ctx) {
14
+ const body = node.childForFieldName('body');
15
+ if (!body)
16
+ return;
17
+ let hasStmt = false;
18
+ for (let i = 0; i < body.namedChildCount; i++) {
19
+ const child = body.namedChild(i);
20
+ if (child && child.type !== 'comment') {
21
+ hasStmt = true;
22
+ break;
23
+ }
24
+ }
25
+ if (!hasStmt)
26
+ ctx.report({ node });
27
+ },
28
+ },
29
+ });
30
+ //# 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;;;GAGG;AACH,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,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,7 @@
1
+ /**
2
+ * Forbid assignment to `<obj>.id`. IDs should be immutable after creation.
3
+ * Catches the TalkUp class of bugs where conv_* IDs get overwritten with resp_*.
4
+ */
5
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
6
+ export default _default;
7
+ //# 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;;;GAGG;;AACH,wBAiBG"}
@@ -0,0 +1,26 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ /**
3
+ * Forbid assignment to `<obj>.id`. IDs should be immutable after creation.
4
+ * Catches the TalkUp class of bugs where conv_* IDs get overwritten with resp_*.
5
+ */
6
+ export default defineRule({
7
+ id: 'no-id-overwrite',
8
+ language: ['typescript', 'javascript', 'tsx'],
9
+ severity: 'error',
10
+ message: 'Do not reassign `.id`. IDs are immutable after creation.',
11
+ messageKo: '`.id` 필드는 생성 후 재할당하지 마세요. ID는 불변이어야 합니다.',
12
+ visitors: {
13
+ assignment_expression(node, ctx) {
14
+ const left = node.childForFieldName('left');
15
+ if (!left || left.type !== 'member_expression')
16
+ return;
17
+ const property = left.childForFieldName('property');
18
+ if (!property)
19
+ return;
20
+ if (ctx.textOf(property) === 'id') {
21
+ ctx.report({ node });
22
+ }
23
+ },
24
+ },
25
+ });
26
+ //# 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;;;GAGG;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":";AAqBA,wBAkBG"}
@@ -0,0 +1,42 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ const ALLOWED_NUMBERS = new Set(['0', '1', '-1', '2', '10', '100', '1000']);
3
+ function isInTestFile(filePath) {
4
+ return /(\.test\.|\.spec\.|__tests__|fixtures)/.test(filePath);
5
+ }
6
+ function isInTimeoutMsContext(node, textOf) {
7
+ // Allow magic numbers in obvious time/duration contexts where the unit is in the variable name
8
+ let parent = node.parent;
9
+ while (parent) {
10
+ const text = textOf(parent);
11
+ if (/timeout|interval|duration|delay|ms|sec|hour|day/i.test(text.slice(0, 40)))
12
+ return true;
13
+ if (parent.type === 'function_declaration' || parent.type === 'method_definition')
14
+ break;
15
+ parent = parent.parent;
16
+ }
17
+ return false;
18
+ }
19
+ export default defineRule({
20
+ id: 'no-magic-number',
21
+ language: ['typescript', 'javascript', 'tsx'],
22
+ severity: 'info',
23
+ message: 'Magic number — extract to a named constant for clarity.',
24
+ messageKo: '매직 넘버 — 명명된 상수로 추출해 의미를 명확히 하세요.',
25
+ visitors: {
26
+ number(node, ctx) {
27
+ if (isInTestFile(ctx.filePath))
28
+ return;
29
+ const text = ctx.textOf(node);
30
+ if (ALLOWED_NUMBERS.has(text))
31
+ return;
32
+ if (isInTimeoutMsContext(node, ctx.textOf))
33
+ return;
34
+ // Skip numbers inside variable_declarator (the const definition itself)
35
+ const parent = node.parent;
36
+ if (parent && parent.type === 'variable_declarator')
37
+ return;
38
+ ctx.report({ node });
39
+ },
40
+ },
41
+ });
42
+ //# 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":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAE5E,SAAS,YAAY,CAAC,QAAgB;IACpC,OAAO,wCAAwC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAuB,EAAE,MAAwC;IAC7F,+FAA+F;IAC/F,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACzB,OAAO,MAAM,EAAE,CAAC;QACd,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAI,kDAAkD,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5F,IAAI,MAAM,CAAC,IAAI,KAAK,sBAAsB,IAAI,MAAM,CAAC,IAAI,KAAK,mBAAmB;YAAE,MAAM;QACzF,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IACzB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,iBAAiB;IACrB,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC;IAC7C,QAAQ,EAAE,MAAM;IAChB,OAAO,EAAE,yDAAyD;IAClE,SAAS,EAAE,kCAAkC;IAC7C,QAAQ,EAAE;QACR,MAAM,CAAC,IAAI,EAAE,GAAG;YACd,IAAI,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,OAAO;YACvC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,OAAO;YACtC,IAAI,oBAAoB,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC;gBAAE,OAAO;YACnD,wEAAwE;YACxE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC3B,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,qBAAqB;gBAAE,OAAO;YAC5D,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvB,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-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,wBA2BG"}
@@ -0,0 +1,34 @@
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/, *.config.ts files
26
+ if (/(^|[/\\])(config|env)([/\\]|\.|$)/.test(ctx.filePath))
27
+ return;
28
+ ctx.report({ node });
29
+ }
30
+ }
31
+ },
32
+ },
33
+ });
34
+ //# 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,8CAA8C;oBAC9C,IAAI,mCAAmC,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;wBAAE,OAAO;oBACnE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACvB,CAAC;YACH,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-shell-true.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-shell-true.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-shell-true.ts"],"names":[],"mappings":";AAgBA,wBAoBG"}
@@ -0,0 +1,42 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ const SUBPROCESS_FNS = new Set(['run', 'call', 'check_call', 'check_output', 'Popen']);
3
+ function hasShellTrue(args, textOf) {
4
+ for (let i = 0; i < args.namedChildCount; i++) {
5
+ const arg = args.namedChild(i);
6
+ if (!arg || arg.type !== 'keyword_argument')
7
+ continue;
8
+ const name = arg.childForFieldName('name');
9
+ const value = arg.childForFieldName('value');
10
+ if (name && value && textOf(name) === 'shell' && textOf(value) === 'True')
11
+ return true;
12
+ }
13
+ return false;
14
+ }
15
+ export default defineRule({
16
+ id: 'no-shell-true',
17
+ language: 'python',
18
+ severity: 'error',
19
+ message: 'subprocess with shell=True is a command injection vector — pass a list of args instead.',
20
+ messageKo: 'subprocess의 shell=True는 명령어 주입 위험 — 인자를 리스트로 전달하세요.',
21
+ visitors: {
22
+ call(node, ctx) {
23
+ const fn = node.childForFieldName('function');
24
+ if (!fn || fn.type !== 'attribute')
25
+ return;
26
+ const obj = fn.childForFieldName('object');
27
+ const attr = fn.childForFieldName('attribute');
28
+ if (!obj || !attr)
29
+ return;
30
+ if (ctx.textOf(obj) !== 'subprocess')
31
+ return;
32
+ if (!SUBPROCESS_FNS.has(ctx.textOf(attr)))
33
+ return;
34
+ const args = node.childForFieldName('arguments');
35
+ if (!args)
36
+ return;
37
+ if (hasShellTrue(args, ctx.textOf))
38
+ ctx.report({ node });
39
+ },
40
+ },
41
+ });
42
+ //# sourceMappingURL=no-shell-true.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-shell-true.js","sourceRoot":"","sources":["../../src/rules-default/no-shell-true.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;AAEvF,SAAS,YAAY,CAAC,IAAuB,EAAE,MAAwC;IACrF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,kBAAkB;YAAE,SAAS;QACtD,MAAM,IAAI,GAAG,GAAG,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;IACzF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,eAAe;IACnB,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,yFAAyF;IAClG,SAAS,EAAE,qDAAqD;IAChE,QAAQ,EAAE;QACR,IAAI,CAAC,IAAI,EAAE,GAAG;YACZ,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAC9C,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,KAAK,WAAW;gBAAE,OAAO;YAC3C,MAAM,GAAG,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,IAAI,GAAG,EAAE,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;YAC/C,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI;gBAAE,OAAO;YAC1B,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,YAAY;gBAAE,OAAO;YAC7C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAAE,OAAO;YAClD,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;YACjD,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,IAAI,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC;gBAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * If a `let` declares an array literal (`let xs = []` or `let xs: T[] = []`)
3
+ * and is initialized once, prefer `const`. This is a heuristic — false positives
4
+ * exist when the variable is reassigned (not just mutated) later. Limited scope:
5
+ * only flags top-level `let xs = [...]` in functions.
6
+ */
7
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
8
+ export default _default;
9
+ //# sourceMappingURL=prefer-const-array.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefer-const-array.d.ts","sourceRoot":"","sources":["../../src/rules-default/prefer-const-array.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;;AACH,wBAqBG"}
@@ -0,0 +1,34 @@
1
+ import { defineRule } from '@aicqtools/rule-sdk';
2
+ /**
3
+ * If a `let` declares an array literal (`let xs = []` or `let xs: T[] = []`)
4
+ * and is initialized once, prefer `const`. This is a heuristic — false positives
5
+ * exist when the variable is reassigned (not just mutated) later. Limited scope:
6
+ * only flags top-level `let xs = [...]` in functions.
7
+ */
8
+ export default defineRule({
9
+ id: 'prefer-const-array',
10
+ language: ['typescript', 'javascript', 'tsx'],
11
+ severity: 'info',
12
+ message: 'Use `const` for array variables that are not reassigned (mutation via push/splice is allowed).',
13
+ messageKo: '재할당하지 않는 배열은 `const`를 사용하세요 (push/splice 같은 mutation은 const에서도 가능).',
14
+ visitors: {
15
+ lexical_declaration(node, ctx) {
16
+ const text = ctx.textOf(node);
17
+ if (!text.startsWith('let '))
18
+ return;
19
+ // Look for `= [` initialization
20
+ let initIsArray = false;
21
+ for (let i = 0; i < node.namedChildCount; i++) {
22
+ const decl = node.namedChild(i);
23
+ if (!decl || decl.type !== 'variable_declarator')
24
+ continue;
25
+ const value = decl.childForFieldName('value');
26
+ if (value && value.type === 'array')
27
+ initIsArray = true;
28
+ }
29
+ if (initIsArray)
30
+ ctx.report({ node });
31
+ },
32
+ },
33
+ });
34
+ //# sourceMappingURL=prefer-const-array.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefer-const-array.js","sourceRoot":"","sources":["../../src/rules-default/prefer-const-array.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;;GAKG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,oBAAoB;IACxB,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC;IAC7C,QAAQ,EAAE,MAAM;IAChB,OAAO,EAAE,gGAAgG;IACzG,SAAS,EAAE,qEAAqE;IAChF,QAAQ,EAAE;QACR,mBAAmB,CAAC,IAAI,EAAE,GAAG;YAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO;YACrC,gCAAgC;YAChC,IAAI,WAAW,GAAG,KAAK,CAAC;YACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBAChC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,qBAAqB;oBAAE,SAAS;gBAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;gBAC9C,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;oBAAE,WAAW,GAAG,IAAI,CAAC;YAC1D,CAAC;YACD,IAAI,WAAW;gBAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,CAAC;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `import * as X from 'module'` namespace imports prevent tree-shaking and
3
+ * obscure dependency analysis. Prefer named imports unless the module
4
+ * genuinely exports many disparate symbols (rare).
5
+ */
6
+ declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
7
+ export default _default;
8
+ //# sourceMappingURL=prefer-named-imports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefer-named-imports.d.ts","sourceRoot":"","sources":["../../src/rules-default/prefer-named-imports.ts"],"names":[],"mappings":"AAEA;;;;GAIG;;AACH,wBAgBG"}