thuban 0.4.3 → 0.4.6

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 (747) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +437 -187
  3. package/dist/LICENSE +21 -21
  4. package/dist/README.md +437 -187
  5. package/dist/cli.js +1 -1
  6. package/dist/package.json +64 -63
  7. package/dist/packages/crucible/.golden/golden-master.json +413 -413
  8. package/dist/packages/crucible/mutation-engine.js +1 -1
  9. package/dist/packages/crucible/pattern-learner.js +1 -1
  10. package/dist/packages/crucible/rule-loader.js +1 -1
  11. package/dist/packages/crucible/rules/code-scanner-core.json +281 -147
  12. package/dist/packages/crucible/rules/command-injection.json +411 -411
  13. package/dist/packages/crucible/rules/crypto-misuse.json +35 -35
  14. package/dist/packages/crucible/rules/deserialization.json +152 -152
  15. package/dist/packages/crucible/rules/env-injection.json +46 -46
  16. package/dist/packages/crucible/rules/eval-abuse.json +104 -104
  17. package/dist/packages/crucible/rules/file-upload.json +46 -46
  18. package/dist/packages/crucible/rules/hallucination.json +22 -22
  19. package/dist/packages/crucible/rules/hardcoded-secret.json +397 -397
  20. package/dist/packages/crucible/rules/hardcoded-url.json +13 -13
  21. package/dist/packages/crucible/rules/insecure-crypto.json +302 -302
  22. package/dist/packages/crucible/rules/integer-overflow.json +35 -35
  23. package/dist/packages/crucible/rules/log-injection.json +33 -33
  24. package/dist/packages/crucible/rules/mass-assignment.json +112 -112
  25. package/dist/packages/crucible/rules/open-redirect.json +196 -196
  26. package/dist/packages/crucible/rules/path-traversal.json +422 -422
  27. package/dist/packages/crucible/rules/prototype-pollution.json +22 -22
  28. package/dist/packages/crucible/rules/sql-injection.json +619 -619
  29. package/dist/packages/crucible/rules/ssrf.json +557 -557
  30. package/dist/packages/crucible/rules/timing-attack.json +55 -55
  31. package/dist/packages/crucible/rules/unsafe-block.json +24 -24
  32. package/dist/packages/crucible/rules/xss.json +663 -663
  33. package/dist/packages/crucible/rules/xxe.json +66 -66
  34. package/dist/packages/crucible/rules/yaml-deserialization.json +22 -22
  35. package/dist/packages/crucible/rules/yaml-injection.json +22 -22
  36. package/dist/packages/crucible/seeding-engine.js +1 -1
  37. package/dist/packages/crucible/seeds/csharp/seed-001.cs +28 -28
  38. package/dist/packages/crucible/seeds/csharp/seed-002.cs +24 -24
  39. package/dist/packages/crucible/seeds/csharp/seed-003.cs +30 -30
  40. package/dist/packages/crucible/seeds/csharp/seed-004.cs +31 -31
  41. package/dist/packages/crucible/seeds/csharp/seed-005.cs +28 -28
  42. package/dist/packages/crucible/seeds/csharp/seed-006.cs +25 -25
  43. package/dist/packages/crucible/seeds/csharp/seed-007.cs +34 -34
  44. package/dist/packages/crucible/seeds/csharp/seed-008.cs +23 -23
  45. package/dist/packages/crucible/seeds/csharp/seed-009.cs +27 -27
  46. package/dist/packages/crucible/seeds/csharp/seed-010.cs +29 -29
  47. package/dist/packages/crucible/seeds/csharp/seed-011.cs +32 -32
  48. package/dist/packages/crucible/seeds/csharp/seed-012.cs +27 -27
  49. package/dist/packages/crucible/seeds/csharp/seed-013.cs +27 -27
  50. package/dist/packages/crucible/seeds/csharp/seed-014.cs +26 -26
  51. package/dist/packages/crucible/seeds/csharp/seed-015.cs +33 -33
  52. package/dist/packages/crucible/seeds/csharp/seed-016.cs +25 -25
  53. package/dist/packages/crucible/seeds/csharp/seed-017.cs +22 -22
  54. package/dist/packages/crucible/seeds/csharp/seed-018.cs +26 -26
  55. package/dist/packages/crucible/seeds/csharp/seed-019.cs +25 -25
  56. package/dist/packages/crucible/seeds/csharp/seed-020.cs +22 -22
  57. package/dist/packages/crucible/seeds/csharp/seed-021.cs +28 -28
  58. package/dist/packages/crucible/seeds/csharp/seed-022.cs +25 -25
  59. package/dist/packages/crucible/seeds/csharp/seed-023.cs +33 -33
  60. package/dist/packages/crucible/seeds/csharp/seed-024.cs +31 -31
  61. package/dist/packages/crucible/seeds/csharp/seed-025.cs +31 -31
  62. package/dist/packages/crucible/seeds/csharp/seed-026.cs +25 -25
  63. package/dist/packages/crucible/seeds/csharp/seed-027.cs +29 -29
  64. package/dist/packages/crucible/seeds/csharp/seed-028.cs +25 -25
  65. package/dist/packages/crucible/seeds/csharp/seed-029.cs +27 -27
  66. package/dist/packages/crucible/seeds/csharp/seed-030.cs +27 -27
  67. package/dist/packages/crucible/seeds/csharp/seed-031.cs +28 -28
  68. package/dist/packages/crucible/seeds/csharp/seed-032.cs +30 -30
  69. package/dist/packages/crucible/seeds/csharp/seed-033.cs +27 -27
  70. package/dist/packages/crucible/seeds/csharp/seed-034.cs +25 -25
  71. package/dist/packages/crucible/seeds/csharp/seed-035.cs +32 -32
  72. package/dist/packages/crucible/seeds/csharp/seed-036.cs +28 -28
  73. package/dist/packages/crucible/seeds/csharp/seed-037.cs +29 -29
  74. package/dist/packages/crucible/seeds/csharp/seed-038.cs +28 -28
  75. package/dist/packages/crucible/seeds/csharp/seed-039.cs +29 -29
  76. package/dist/packages/crucible/seeds/csharp/seed-040.cs +28 -28
  77. package/dist/packages/crucible/seeds/csharp/seed-041.cs +33 -33
  78. package/dist/packages/crucible/seeds/csharp/seed-042.cs +27 -27
  79. package/dist/packages/crucible/seeds/csharp/seed-043.cs +27 -27
  80. package/dist/packages/crucible/seeds/csharp/seed-044.cs +25 -25
  81. package/dist/packages/crucible/seeds/csharp/seed-045.cs +30 -30
  82. package/dist/packages/crucible/seeds/csharp/seed-046.cs +29 -29
  83. package/dist/packages/crucible/seeds/csharp/seed-047.cs +26 -26
  84. package/dist/packages/crucible/seeds/csharp/seed-048.cs +31 -31
  85. package/dist/packages/crucible/seeds/csharp/seed-049.cs +27 -27
  86. package/dist/packages/crucible/seeds/csharp/seed-050.cs +30 -30
  87. package/dist/packages/crucible/seeds/csharp/seed-051.cs +34 -34
  88. package/dist/packages/crucible/seeds/csharp/seed-052.cs +36 -36
  89. package/dist/packages/crucible/seeds/go/seed-001.go +29 -29
  90. package/dist/packages/crucible/seeds/go/seed-002.go +27 -27
  91. package/dist/packages/crucible/seeds/go/seed-003.go +31 -31
  92. package/dist/packages/crucible/seeds/go/seed-004.go +27 -27
  93. package/dist/packages/crucible/seeds/go/seed-005.go +27 -27
  94. package/dist/packages/crucible/seeds/go/seed-006.go +29 -29
  95. package/dist/packages/crucible/seeds/go/seed-007.go +24 -24
  96. package/dist/packages/crucible/seeds/go/seed-008.go +25 -25
  97. package/dist/packages/crucible/seeds/go/seed-009.go +30 -30
  98. package/dist/packages/crucible/seeds/go/seed-010.go +35 -35
  99. package/dist/packages/crucible/seeds/go/seed-011.go +24 -24
  100. package/dist/packages/crucible/seeds/go/seed-012.go +24 -24
  101. package/dist/packages/crucible/seeds/go/seed-013.go +31 -31
  102. package/dist/packages/crucible/seeds/go/seed-014.go +35 -35
  103. package/dist/packages/crucible/seeds/go/seed-015.go +25 -25
  104. package/dist/packages/crucible/seeds/go/seed-016.go +19 -19
  105. package/dist/packages/crucible/seeds/go/seed-017.go +23 -23
  106. package/dist/packages/crucible/seeds/go/seed-018.go +23 -23
  107. package/dist/packages/crucible/seeds/go/seed-019.go +22 -22
  108. package/dist/packages/crucible/seeds/go/seed-020.go +31 -31
  109. package/dist/packages/crucible/seeds/go/seed-021.go +22 -22
  110. package/dist/packages/crucible/seeds/go/seed-022.go +29 -29
  111. package/dist/packages/crucible/seeds/go/seed-023.go +26 -26
  112. package/dist/packages/crucible/seeds/go/seed-024.go +27 -27
  113. package/dist/packages/crucible/seeds/go/seed-025.go +23 -23
  114. package/dist/packages/crucible/seeds/go/seed-026.go +29 -29
  115. package/dist/packages/crucible/seeds/go/seed-027.go +26 -26
  116. package/dist/packages/crucible/seeds/go/seed-028.go +23 -23
  117. package/dist/packages/crucible/seeds/go/seed-029.go +28 -28
  118. package/dist/packages/crucible/seeds/go/seed-030.go +27 -27
  119. package/dist/packages/crucible/seeds/go/seed-031.go +28 -28
  120. package/dist/packages/crucible/seeds/go/seed-032.go +27 -27
  121. package/dist/packages/crucible/seeds/go/seed-033.go +29 -29
  122. package/dist/packages/crucible/seeds/go/seed-034.go +22 -22
  123. package/dist/packages/crucible/seeds/go/seed-035.go +32 -32
  124. package/dist/packages/crucible/seeds/go/seed-036.go +27 -27
  125. package/dist/packages/crucible/seeds/go/seed-037.go +27 -27
  126. package/dist/packages/crucible/seeds/go/seed-038.go +27 -27
  127. package/dist/packages/crucible/seeds/go/seed-039.go +26 -26
  128. package/dist/packages/crucible/seeds/go/seed-040.go +37 -37
  129. package/dist/packages/crucible/seeds/go/seed-041.go +23 -23
  130. package/dist/packages/crucible/seeds/go/seed-042.go +32 -32
  131. package/dist/packages/crucible/seeds/go/seed-043.go +35 -35
  132. package/dist/packages/crucible/seeds/go/seed-044.go +25 -25
  133. package/dist/packages/crucible/seeds/go/seed-045.go +26 -26
  134. package/dist/packages/crucible/seeds/go/seed-046.go +34 -34
  135. package/dist/packages/crucible/seeds/go/seed-047.go +26 -26
  136. package/dist/packages/crucible/seeds/go/seed-048.go +29 -29
  137. package/dist/packages/crucible/seeds/go/seed-049.go +24 -24
  138. package/dist/packages/crucible/seeds/go/seed-050.go +27 -27
  139. package/dist/packages/crucible/seeds/go/seed-051.go +38 -38
  140. package/dist/packages/crucible/seeds/go/seed-052.go +27 -27
  141. package/dist/packages/crucible/seeds/java/seed-001.java +23 -23
  142. package/dist/packages/crucible/seeds/java/seed-002.java +22 -22
  143. package/dist/packages/crucible/seeds/java/seed-003.java +28 -28
  144. package/dist/packages/crucible/seeds/java/seed-004.java +26 -26
  145. package/dist/packages/crucible/seeds/java/seed-005.java +33 -33
  146. package/dist/packages/crucible/seeds/java/seed-006.java +22 -22
  147. package/dist/packages/crucible/seeds/java/seed-007.java +21 -21
  148. package/dist/packages/crucible/seeds/java/seed-008.java +29 -29
  149. package/dist/packages/crucible/seeds/java/seed-009.java +24 -24
  150. package/dist/packages/crucible/seeds/java/seed-010.java +30 -30
  151. package/dist/packages/crucible/seeds/java/seed-011.java +25 -25
  152. package/dist/packages/crucible/seeds/java/seed-012.java +20 -20
  153. package/dist/packages/crucible/seeds/java/seed-013.java +26 -26
  154. package/dist/packages/crucible/seeds/java/seed-014.java +23 -23
  155. package/dist/packages/crucible/seeds/java/seed-015.java +25 -25
  156. package/dist/packages/crucible/seeds/java/seed-016.java +22 -22
  157. package/dist/packages/crucible/seeds/java/seed-017.java +24 -24
  158. package/dist/packages/crucible/seeds/java/seed-018.java +24 -24
  159. package/dist/packages/crucible/seeds/java/seed-019.java +38 -38
  160. package/dist/packages/crucible/seeds/java/seed-020.java +24 -24
  161. package/dist/packages/crucible/seeds/java/seed-021.java +22 -22
  162. package/dist/packages/crucible/seeds/java/seed-022.java +22 -22
  163. package/dist/packages/crucible/seeds/java/seed-023.java +22 -22
  164. package/dist/packages/crucible/seeds/java/seed-024.java +25 -25
  165. package/dist/packages/crucible/seeds/java/seed-025.java +27 -27
  166. package/dist/packages/crucible/seeds/java/seed-026.java +25 -25
  167. package/dist/packages/crucible/seeds/java/seed-027.java +22 -22
  168. package/dist/packages/crucible/seeds/java/seed-028.java +21 -21
  169. package/dist/packages/crucible/seeds/java/seed-029.java +28 -28
  170. package/dist/packages/crucible/seeds/java/seed-030.java +27 -27
  171. package/dist/packages/crucible/seeds/java/seed-031.java +23 -23
  172. package/dist/packages/crucible/seeds/java/seed-032.java +22 -22
  173. package/dist/packages/crucible/seeds/java/seed-033.java +24 -24
  174. package/dist/packages/crucible/seeds/java/seed-034.java +24 -24
  175. package/dist/packages/crucible/seeds/java/seed-035.java +29 -29
  176. package/dist/packages/crucible/seeds/java/seed-036.java +25 -25
  177. package/dist/packages/crucible/seeds/java/seed-037.java +30 -30
  178. package/dist/packages/crucible/seeds/java/seed-038.java +29 -29
  179. package/dist/packages/crucible/seeds/java/seed-039.java +26 -26
  180. package/dist/packages/crucible/seeds/java/seed-040.java +31 -31
  181. package/dist/packages/crucible/seeds/java/seed-041.java +20 -20
  182. package/dist/packages/crucible/seeds/java/seed-042.java +26 -26
  183. package/dist/packages/crucible/seeds/java/seed-043.java +31 -31
  184. package/dist/packages/crucible/seeds/java/seed-044.java +22 -22
  185. package/dist/packages/crucible/seeds/java/seed-045.java +25 -25
  186. package/dist/packages/crucible/seeds/java/seed-046.java +22 -22
  187. package/dist/packages/crucible/seeds/java/seed-047.java +26 -26
  188. package/dist/packages/crucible/seeds/java/seed-048.java +24 -24
  189. package/dist/packages/crucible/seeds/java/seed-049.java +28 -28
  190. package/dist/packages/crucible/seeds/java/seed-050.java +29 -29
  191. package/dist/packages/crucible/seeds/java/seed-051.java +25 -25
  192. package/dist/packages/crucible/seeds/java/seed-052.java +32 -32
  193. package/dist/packages/crucible/seeds/js/seed-001.js +27 -27
  194. package/dist/packages/crucible/seeds/js/seed-002.js +28 -28
  195. package/dist/packages/crucible/seeds/js/seed-003.js +30 -30
  196. package/dist/packages/crucible/seeds/js/seed-004.js +28 -28
  197. package/dist/packages/crucible/seeds/js/seed-005.js +31 -31
  198. package/dist/packages/crucible/seeds/js/seed-006.js +28 -28
  199. package/dist/packages/crucible/seeds/js/seed-007.js +26 -26
  200. package/dist/packages/crucible/seeds/js/seed-008.js +30 -30
  201. package/dist/packages/crucible/seeds/js/seed-009.js +30 -30
  202. package/dist/packages/crucible/seeds/js/seed-010.js +29 -29
  203. package/dist/packages/crucible/seeds/js/seed-011.js +34 -34
  204. package/dist/packages/crucible/seeds/js/seed-012.js +32 -32
  205. package/dist/packages/crucible/seeds/js/seed-013.js +29 -29
  206. package/dist/packages/crucible/seeds/js/seed-014.js +29 -29
  207. package/dist/packages/crucible/seeds/js/seed-015.js +31 -31
  208. package/dist/packages/crucible/seeds/js/seed-016.js +31 -31
  209. package/dist/packages/crucible/seeds/js/seed-017.js +30 -30
  210. package/dist/packages/crucible/seeds/js/seed-018.js +29 -29
  211. package/dist/packages/crucible/seeds/js/seed-019.js +32 -32
  212. package/dist/packages/crucible/seeds/js/seed-020.js +29 -29
  213. package/dist/packages/crucible/seeds/js/seed-021.js +31 -31
  214. package/dist/packages/crucible/seeds/js/seed-022.js +31 -31
  215. package/dist/packages/crucible/seeds/js/seed-023.js +29 -29
  216. package/dist/packages/crucible/seeds/js/seed-024.js +29 -29
  217. package/dist/packages/crucible/seeds/js/seed-025.js +34 -34
  218. package/dist/packages/crucible/seeds/js/seed-026.js +31 -31
  219. package/dist/packages/crucible/seeds/js/seed-027.js +31 -31
  220. package/dist/packages/crucible/seeds/js/seed-028.js +33 -33
  221. package/dist/packages/crucible/seeds/js/seed-029.js +33 -33
  222. package/dist/packages/crucible/seeds/js/seed-030.js +30 -30
  223. package/dist/packages/crucible/seeds/js/seed-031.js +31 -31
  224. package/dist/packages/crucible/seeds/js/seed-032.js +34 -34
  225. package/dist/packages/crucible/seeds/js/seed-033.js +33 -33
  226. package/dist/packages/crucible/seeds/js/seed-034.js +33 -33
  227. package/dist/packages/crucible/seeds/js/seed-035.js +30 -30
  228. package/dist/packages/crucible/seeds/js/seed-036.js +30 -30
  229. package/dist/packages/crucible/seeds/js/seed-037.js +29 -29
  230. package/dist/packages/crucible/seeds/js/seed-038.js +31 -31
  231. package/dist/packages/crucible/seeds/js/seed-039.js +34 -34
  232. package/dist/packages/crucible/seeds/js/seed-040.js +35 -35
  233. package/dist/packages/crucible/seeds/js/seed-041.js +32 -32
  234. package/dist/packages/crucible/seeds/js/seed-042.js +32 -32
  235. package/dist/packages/crucible/seeds/js/seed-043.js +27 -27
  236. package/dist/packages/crucible/seeds/js/seed-044.js +29 -29
  237. package/dist/packages/crucible/seeds/js/seed-045.js +33 -33
  238. package/dist/packages/crucible/seeds/js/seed-046.js +33 -33
  239. package/dist/packages/crucible/seeds/js/seed-047.js +29 -29
  240. package/dist/packages/crucible/seeds/js/seed-048.js +29 -29
  241. package/dist/packages/crucible/seeds/js/seed-049.js +34 -34
  242. package/dist/packages/crucible/seeds/js/seed-050.js +34 -34
  243. package/dist/packages/crucible/seeds/js/seed-051.js +30 -30
  244. package/dist/packages/crucible/seeds/js/seed-052.js +30 -30
  245. package/dist/packages/crucible/seeds/js/seed-053.js +26 -26
  246. package/dist/packages/crucible/seeds/js/seed-054.js +35 -35
  247. package/dist/packages/crucible/seeds/js/seed-055.js +33 -33
  248. package/dist/packages/crucible/seeds/kotlin/seed-001.kt +24 -24
  249. package/dist/packages/crucible/seeds/kotlin/seed-002.kt +27 -27
  250. package/dist/packages/crucible/seeds/kotlin/seed-003.kt +29 -29
  251. package/dist/packages/crucible/seeds/kotlin/seed-004.kt +24 -24
  252. package/dist/packages/crucible/seeds/kotlin/seed-005.kt +29 -29
  253. package/dist/packages/crucible/seeds/kotlin/seed-006.kt +21 -21
  254. package/dist/packages/crucible/seeds/kotlin/seed-007.kt +23 -23
  255. package/dist/packages/crucible/seeds/kotlin/seed-008.kt +25 -25
  256. package/dist/packages/crucible/seeds/kotlin/seed-009.kt +21 -21
  257. package/dist/packages/crucible/seeds/kotlin/seed-010.kt +28 -28
  258. package/dist/packages/crucible/seeds/kotlin/seed-011.kt +22 -22
  259. package/dist/packages/crucible/seeds/kotlin/seed-012.kt +25 -25
  260. package/dist/packages/crucible/seeds/kotlin/seed-013.kt +30 -30
  261. package/dist/packages/crucible/seeds/kotlin/seed-014.kt +27 -27
  262. package/dist/packages/crucible/seeds/kotlin/seed-015.kt +24 -24
  263. package/dist/packages/crucible/seeds/kotlin/seed-016.kt +28 -28
  264. package/dist/packages/crucible/seeds/kotlin/seed-017.kt +33 -33
  265. package/dist/packages/crucible/seeds/kotlin/seed-018.kt +31 -31
  266. package/dist/packages/crucible/seeds/kotlin/seed-019.kt +28 -28
  267. package/dist/packages/crucible/seeds/kotlin/seed-020.kt +31 -31
  268. package/dist/packages/crucible/seeds/kotlin/seed-021.kt +34 -34
  269. package/dist/packages/crucible/seeds/kotlin/seed-022.kt +29 -29
  270. package/dist/packages/crucible/seeds/kotlin/seed-023.kt +20 -20
  271. package/dist/packages/crucible/seeds/kotlin/seed-024.kt +23 -23
  272. package/dist/packages/crucible/seeds/kotlin/seed-025.kt +23 -23
  273. package/dist/packages/crucible/seeds/kotlin/seed-026.kt +26 -26
  274. package/dist/packages/crucible/seeds/kotlin/seed-027.kt +29 -29
  275. package/dist/packages/crucible/seeds/kotlin/seed-028.kt +24 -24
  276. package/dist/packages/crucible/seeds/kotlin/seed-029.kt +27 -27
  277. package/dist/packages/crucible/seeds/kotlin/seed-030.kt +26 -26
  278. package/dist/packages/crucible/seeds/kotlin/seed-031.kt +28 -28
  279. package/dist/packages/crucible/seeds/kotlin/seed-032.kt +29 -29
  280. package/dist/packages/crucible/seeds/kotlin/seed-033.kt +30 -30
  281. package/dist/packages/crucible/seeds/kotlin/seed-034.kt +26 -26
  282. package/dist/packages/crucible/seeds/kotlin/seed-035.kt +33 -33
  283. package/dist/packages/crucible/seeds/kotlin/seed-036.kt +25 -25
  284. package/dist/packages/crucible/seeds/kotlin/seed-037.kt +26 -26
  285. package/dist/packages/crucible/seeds/kotlin/seed-038.kt +25 -25
  286. package/dist/packages/crucible/seeds/kotlin/seed-039.kt +29 -29
  287. package/dist/packages/crucible/seeds/kotlin/seed-040.kt +28 -28
  288. package/dist/packages/crucible/seeds/kotlin/seed-041.kt +31 -31
  289. package/dist/packages/crucible/seeds/kotlin/seed-042.kt +27 -27
  290. package/dist/packages/crucible/seeds/kotlin/seed-043.kt +26 -26
  291. package/dist/packages/crucible/seeds/kotlin/seed-044.kt +27 -27
  292. package/dist/packages/crucible/seeds/kotlin/seed-045.kt +27 -27
  293. package/dist/packages/crucible/seeds/kotlin/seed-046.kt +33 -33
  294. package/dist/packages/crucible/seeds/kotlin/seed-047.kt +32 -32
  295. package/dist/packages/crucible/seeds/kotlin/seed-048.kt +20 -20
  296. package/dist/packages/crucible/seeds/kotlin/seed-049.kt +23 -23
  297. package/dist/packages/crucible/seeds/kotlin/seed-050.kt +19 -19
  298. package/dist/packages/crucible/seeds/php/seed-001.php +23 -23
  299. package/dist/packages/crucible/seeds/php/seed-002.php +18 -18
  300. package/dist/packages/crucible/seeds/php/seed-003.php +25 -25
  301. package/dist/packages/crucible/seeds/php/seed-004.php +21 -21
  302. package/dist/packages/crucible/seeds/php/seed-005.php +21 -21
  303. package/dist/packages/crucible/seeds/php/seed-006.php +21 -21
  304. package/dist/packages/crucible/seeds/php/seed-007.php +18 -18
  305. package/dist/packages/crucible/seeds/php/seed-008.php +19 -19
  306. package/dist/packages/crucible/seeds/php/seed-009.php +24 -24
  307. package/dist/packages/crucible/seeds/php/seed-010.php +24 -24
  308. package/dist/packages/crucible/seeds/php/seed-011.php +18 -18
  309. package/dist/packages/crucible/seeds/php/seed-012.php +24 -24
  310. package/dist/packages/crucible/seeds/php/seed-013.php +23 -23
  311. package/dist/packages/crucible/seeds/php/seed-014.php +20 -20
  312. package/dist/packages/crucible/seeds/php/seed-015.php +18 -18
  313. package/dist/packages/crucible/seeds/php/seed-016.php +18 -18
  314. package/dist/packages/crucible/seeds/php/seed-017.php +18 -18
  315. package/dist/packages/crucible/seeds/php/seed-018.php +17 -17
  316. package/dist/packages/crucible/seeds/php/seed-019.php +24 -24
  317. package/dist/packages/crucible/seeds/php/seed-020.php +19 -19
  318. package/dist/packages/crucible/seeds/php/seed-021.php +22 -22
  319. package/dist/packages/crucible/seeds/php/seed-022.php +15 -15
  320. package/dist/packages/crucible/seeds/php/seed-023.php +24 -24
  321. package/dist/packages/crucible/seeds/php/seed-024.php +22 -22
  322. package/dist/packages/crucible/seeds/php/seed-025.php +20 -20
  323. package/dist/packages/crucible/seeds/php/seed-026.php +22 -22
  324. package/dist/packages/crucible/seeds/php/seed-027.php +15 -15
  325. package/dist/packages/crucible/seeds/php/seed-028.php +15 -15
  326. package/dist/packages/crucible/seeds/php/seed-029.php +21 -21
  327. package/dist/packages/crucible/seeds/php/seed-030.php +21 -21
  328. package/dist/packages/crucible/seeds/php/seed-031.php +23 -23
  329. package/dist/packages/crucible/seeds/php/seed-032.php +18 -18
  330. package/dist/packages/crucible/seeds/php/seed-033.php +18 -18
  331. package/dist/packages/crucible/seeds/php/seed-034.php +24 -24
  332. package/dist/packages/crucible/seeds/php/seed-035.php +22 -22
  333. package/dist/packages/crucible/seeds/php/seed-036.php +17 -17
  334. package/dist/packages/crucible/seeds/php/seed-037.php +19 -19
  335. package/dist/packages/crucible/seeds/php/seed-038.php +20 -20
  336. package/dist/packages/crucible/seeds/php/seed-039.php +15 -15
  337. package/dist/packages/crucible/seeds/php/seed-040.php +21 -21
  338. package/dist/packages/crucible/seeds/php/seed-041.php +20 -20
  339. package/dist/packages/crucible/seeds/php/seed-042.php +21 -21
  340. package/dist/packages/crucible/seeds/php/seed-043.php +21 -21
  341. package/dist/packages/crucible/seeds/php/seed-044.php +25 -25
  342. package/dist/packages/crucible/seeds/php/seed-045.php +25 -25
  343. package/dist/packages/crucible/seeds/php/seed-046.php +20 -20
  344. package/dist/packages/crucible/seeds/php/seed-047.php +21 -21
  345. package/dist/packages/crucible/seeds/php/seed-048.php +19 -19
  346. package/dist/packages/crucible/seeds/php/seed-049.php +18 -18
  347. package/dist/packages/crucible/seeds/php/seed-050.php +20 -20
  348. package/dist/packages/crucible/seeds/python/seed-001.py +33 -33
  349. package/dist/packages/crucible/seeds/python/seed-002.py +39 -39
  350. package/dist/packages/crucible/seeds/python/seed-003.py +31 -31
  351. package/dist/packages/crucible/seeds/python/seed-004.py +33 -33
  352. package/dist/packages/crucible/seeds/python/seed-005.py +33 -33
  353. package/dist/packages/crucible/seeds/python/seed-006.py +34 -34
  354. package/dist/packages/crucible/seeds/python/seed-007.py +33 -33
  355. package/dist/packages/crucible/seeds/python/seed-008.py +38 -38
  356. package/dist/packages/crucible/seeds/python/seed-009.py +38 -38
  357. package/dist/packages/crucible/seeds/python/seed-010.py +42 -42
  358. package/dist/packages/crucible/seeds/python/seed-011.py +30 -30
  359. package/dist/packages/crucible/seeds/python/seed-012.py +35 -35
  360. package/dist/packages/crucible/seeds/python/seed-013.py +33 -33
  361. package/dist/packages/crucible/seeds/python/seed-014.py +40 -40
  362. package/dist/packages/crucible/seeds/python/seed-015.py +33 -33
  363. package/dist/packages/crucible/seeds/python/seed-016.py +34 -34
  364. package/dist/packages/crucible/seeds/python/seed-017.py +34 -34
  365. package/dist/packages/crucible/seeds/python/seed-018.py +30 -30
  366. package/dist/packages/crucible/seeds/python/seed-019.py +33 -33
  367. package/dist/packages/crucible/seeds/python/seed-020.py +37 -37
  368. package/dist/packages/crucible/seeds/python/seed-021 2.py +37 -37
  369. package/dist/packages/crucible/seeds/python/seed-021.py +37 -37
  370. package/dist/packages/crucible/seeds/python/seed-022 2.py +34 -34
  371. package/dist/packages/crucible/seeds/python/seed-022.py +34 -34
  372. package/dist/packages/crucible/seeds/python/seed-023 2.py +37 -37
  373. package/dist/packages/crucible/seeds/python/seed-023.py +37 -37
  374. package/dist/packages/crucible/seeds/python/seed-024 2.py +38 -38
  375. package/dist/packages/crucible/seeds/python/seed-024.py +38 -38
  376. package/dist/packages/crucible/seeds/python/seed-025 2.py +40 -40
  377. package/dist/packages/crucible/seeds/python/seed-025.py +40 -40
  378. package/dist/packages/crucible/seeds/python/seed-026 2.py +35 -35
  379. package/dist/packages/crucible/seeds/python/seed-026.py +35 -35
  380. package/dist/packages/crucible/seeds/python/seed-027 2.py +35 -35
  381. package/dist/packages/crucible/seeds/python/seed-027.py +35 -35
  382. package/dist/packages/crucible/seeds/python/seed-028 2.py +42 -42
  383. package/dist/packages/crucible/seeds/python/seed-028.py +42 -42
  384. package/dist/packages/crucible/seeds/python/seed-029.py +42 -42
  385. package/dist/packages/crucible/seeds/python/seed-030 2.py +37 -37
  386. package/dist/packages/crucible/seeds/python/seed-030.py +37 -37
  387. package/dist/packages/crucible/seeds/python/seed-031 2.py +34 -34
  388. package/dist/packages/crucible/seeds/python/seed-031.py +34 -34
  389. package/dist/packages/crucible/seeds/python/seed-032.py +33 -33
  390. package/dist/packages/crucible/seeds/python/seed-033.py +32 -32
  391. package/dist/packages/crucible/seeds/python/seed-034.py +38 -38
  392. package/dist/packages/crucible/seeds/python/seed-035.py +35 -35
  393. package/dist/packages/crucible/seeds/python/seed-036 2.py +33 -33
  394. package/dist/packages/crucible/seeds/python/seed-036.py +33 -33
  395. package/dist/packages/crucible/seeds/python/seed-037 2.py +41 -41
  396. package/dist/packages/crucible/seeds/python/seed-037.py +41 -41
  397. package/dist/packages/crucible/seeds/python/seed-038 2.py +33 -33
  398. package/dist/packages/crucible/seeds/python/seed-038.py +33 -33
  399. package/dist/packages/crucible/seeds/python/seed-039 2.py +39 -39
  400. package/dist/packages/crucible/seeds/python/seed-039.py +39 -39
  401. package/dist/packages/crucible/seeds/python/seed-040 2.py +39 -39
  402. package/dist/packages/crucible/seeds/python/seed-040.py +39 -39
  403. package/dist/packages/crucible/seeds/python/seed-041 2.py +37 -37
  404. package/dist/packages/crucible/seeds/python/seed-041.py +37 -37
  405. package/dist/packages/crucible/seeds/python/seed-042 2.py +38 -38
  406. package/dist/packages/crucible/seeds/python/seed-042.py +38 -38
  407. package/dist/packages/crucible/seeds/python/seed-043 2.py +32 -32
  408. package/dist/packages/crucible/seeds/python/seed-043.py +32 -32
  409. package/dist/packages/crucible/seeds/python/seed-044 2.py +38 -38
  410. package/dist/packages/crucible/seeds/python/seed-044.py +38 -38
  411. package/dist/packages/crucible/seeds/python/seed-045 2.py +36 -36
  412. package/dist/packages/crucible/seeds/python/seed-045.py +36 -36
  413. package/dist/packages/crucible/seeds/python/seed-046 2.py +33 -33
  414. package/dist/packages/crucible/seeds/python/seed-046.py +33 -33
  415. package/dist/packages/crucible/seeds/python/seed-047 2.py +44 -44
  416. package/dist/packages/crucible/seeds/python/seed-047.py +44 -44
  417. package/dist/packages/crucible/seeds/python/seed-048 2.py +35 -35
  418. package/dist/packages/crucible/seeds/python/seed-048.py +35 -35
  419. package/dist/packages/crucible/seeds/python/seed-049 2.py +39 -39
  420. package/dist/packages/crucible/seeds/python/seed-049.py +39 -39
  421. package/dist/packages/crucible/seeds/python/seed-050 2.py +39 -39
  422. package/dist/packages/crucible/seeds/python/seed-050.py +39 -39
  423. package/dist/packages/crucible/seeds/python/seed-051 2.py +38 -38
  424. package/dist/packages/crucible/seeds/python/seed-051.py +38 -38
  425. package/dist/packages/crucible/seeds/python/seed-052 2.py +41 -41
  426. package/dist/packages/crucible/seeds/python/seed-052.py +41 -41
  427. package/dist/packages/crucible/seeds/ruby/seed-001 2.rb +15 -15
  428. package/dist/packages/crucible/seeds/ruby/seed-001.rb +15 -15
  429. package/dist/packages/crucible/seeds/ruby/seed-002 2.rb +22 -22
  430. package/dist/packages/crucible/seeds/ruby/seed-002.rb +22 -22
  431. package/dist/packages/crucible/seeds/ruby/seed-003 2.rb +25 -25
  432. package/dist/packages/crucible/seeds/ruby/seed-003.rb +25 -25
  433. package/dist/packages/crucible/seeds/ruby/seed-004 2.rb +17 -17
  434. package/dist/packages/crucible/seeds/ruby/seed-004.rb +17 -17
  435. package/dist/packages/crucible/seeds/ruby/seed-005 2.rb +21 -21
  436. package/dist/packages/crucible/seeds/ruby/seed-005.rb +21 -21
  437. package/dist/packages/crucible/seeds/ruby/seed-006 2.rb +17 -17
  438. package/dist/packages/crucible/seeds/ruby/seed-006.rb +17 -17
  439. package/dist/packages/crucible/seeds/ruby/seed-007 2.rb +16 -16
  440. package/dist/packages/crucible/seeds/ruby/seed-007.rb +16 -16
  441. package/dist/packages/crucible/seeds/ruby/seed-008 2.rb +18 -18
  442. package/dist/packages/crucible/seeds/ruby/seed-008.rb +18 -18
  443. package/dist/packages/crucible/seeds/ruby/seed-009 2.rb +20 -20
  444. package/dist/packages/crucible/seeds/ruby/seed-009.rb +20 -20
  445. package/dist/packages/crucible/seeds/ruby/seed-010 2.rb +24 -24
  446. package/dist/packages/crucible/seeds/ruby/seed-010.rb +24 -24
  447. package/dist/packages/crucible/seeds/ruby/seed-011 2.rb +21 -21
  448. package/dist/packages/crucible/seeds/ruby/seed-011.rb +21 -21
  449. package/dist/packages/crucible/seeds/ruby/seed-012 2.rb +22 -22
  450. package/dist/packages/crucible/seeds/ruby/seed-012.rb +22 -22
  451. package/dist/packages/crucible/seeds/ruby/seed-013 2.rb +21 -21
  452. package/dist/packages/crucible/seeds/ruby/seed-013.rb +21 -21
  453. package/dist/packages/crucible/seeds/ruby/seed-014 2.rb +16 -16
  454. package/dist/packages/crucible/seeds/ruby/seed-014.rb +16 -16
  455. package/dist/packages/crucible/seeds/ruby/seed-015 2.rb +18 -18
  456. package/dist/packages/crucible/seeds/ruby/seed-015.rb +18 -18
  457. package/dist/packages/crucible/seeds/ruby/seed-016 2.rb +17 -17
  458. package/dist/packages/crucible/seeds/ruby/seed-016.rb +17 -17
  459. package/dist/packages/crucible/seeds/ruby/seed-017 2.rb +25 -25
  460. package/dist/packages/crucible/seeds/ruby/seed-017.rb +25 -25
  461. package/dist/packages/crucible/seeds/ruby/seed-018 2.rb +23 -23
  462. package/dist/packages/crucible/seeds/ruby/seed-018.rb +23 -23
  463. package/dist/packages/crucible/seeds/ruby/seed-019 2.rb +20 -20
  464. package/dist/packages/crucible/seeds/ruby/seed-019.rb +20 -20
  465. package/dist/packages/crucible/seeds/ruby/seed-020 2.rb +17 -17
  466. package/dist/packages/crucible/seeds/ruby/seed-020.rb +17 -17
  467. package/dist/packages/crucible/seeds/ruby/seed-021 2.rb +20 -20
  468. package/dist/packages/crucible/seeds/ruby/seed-021.rb +20 -20
  469. package/dist/packages/crucible/seeds/ruby/seed-022 2.rb +21 -21
  470. package/dist/packages/crucible/seeds/ruby/seed-022.rb +21 -21
  471. package/dist/packages/crucible/seeds/ruby/seed-023 2.rb +19 -19
  472. package/dist/packages/crucible/seeds/ruby/seed-023.rb +19 -19
  473. package/dist/packages/crucible/seeds/ruby/seed-024 2.rb +17 -17
  474. package/dist/packages/crucible/seeds/ruby/seed-024.rb +17 -17
  475. package/dist/packages/crucible/seeds/ruby/seed-025 2.rb +17 -17
  476. package/dist/packages/crucible/seeds/ruby/seed-025.rb +17 -17
  477. package/dist/packages/crucible/seeds/ruby/seed-026 2.rb +18 -18
  478. package/dist/packages/crucible/seeds/ruby/seed-026.rb +18 -18
  479. package/dist/packages/crucible/seeds/ruby/seed-027 2.rb +21 -21
  480. package/dist/packages/crucible/seeds/ruby/seed-027.rb +21 -21
  481. package/dist/packages/crucible/seeds/ruby/seed-028 2.rb +22 -22
  482. package/dist/packages/crucible/seeds/ruby/seed-028.rb +22 -22
  483. package/dist/packages/crucible/seeds/ruby/seed-029 2.rb +19 -19
  484. package/dist/packages/crucible/seeds/ruby/seed-029.rb +19 -19
  485. package/dist/packages/crucible/seeds/ruby/seed-030 2.rb +20 -20
  486. package/dist/packages/crucible/seeds/ruby/seed-030.rb +20 -20
  487. package/dist/packages/crucible/seeds/ruby/seed-031 2.rb +17 -17
  488. package/dist/packages/crucible/seeds/ruby/seed-031.rb +17 -17
  489. package/dist/packages/crucible/seeds/ruby/seed-032 2.rb +22 -22
  490. package/dist/packages/crucible/seeds/ruby/seed-032.rb +22 -22
  491. package/dist/packages/crucible/seeds/ruby/seed-033 2.rb +18 -18
  492. package/dist/packages/crucible/seeds/ruby/seed-033.rb +18 -18
  493. package/dist/packages/crucible/seeds/ruby/seed-034 2.rb +19 -19
  494. package/dist/packages/crucible/seeds/ruby/seed-034.rb +19 -19
  495. package/dist/packages/crucible/seeds/ruby/seed-035 2.rb +19 -19
  496. package/dist/packages/crucible/seeds/ruby/seed-035.rb +19 -19
  497. package/dist/packages/crucible/seeds/ruby/seed-036 2.rb +18 -18
  498. package/dist/packages/crucible/seeds/ruby/seed-036.rb +18 -18
  499. package/dist/packages/crucible/seeds/ruby/seed-037 2.rb +21 -21
  500. package/dist/packages/crucible/seeds/ruby/seed-037.rb +21 -21
  501. package/dist/packages/crucible/seeds/ruby/seed-038 2.rb +24 -24
  502. package/dist/packages/crucible/seeds/ruby/seed-038.rb +24 -24
  503. package/dist/packages/crucible/seeds/ruby/seed-039 2.rb +24 -24
  504. package/dist/packages/crucible/seeds/ruby/seed-039.rb +24 -24
  505. package/dist/packages/crucible/seeds/ruby/seed-040 2.rb +22 -22
  506. package/dist/packages/crucible/seeds/ruby/seed-040.rb +22 -22
  507. package/dist/packages/crucible/seeds/ruby/seed-041 2.rb +23 -23
  508. package/dist/packages/crucible/seeds/ruby/seed-041.rb +23 -23
  509. package/dist/packages/crucible/seeds/ruby/seed-042 2.rb +25 -25
  510. package/dist/packages/crucible/seeds/ruby/seed-042.rb +25 -25
  511. package/dist/packages/crucible/seeds/ruby/seed-043 2.rb +23 -23
  512. package/dist/packages/crucible/seeds/ruby/seed-043.rb +23 -23
  513. package/dist/packages/crucible/seeds/ruby/seed-044 2.rb +16 -16
  514. package/dist/packages/crucible/seeds/ruby/seed-044.rb +16 -16
  515. package/dist/packages/crucible/seeds/ruby/seed-045 2.rb +22 -22
  516. package/dist/packages/crucible/seeds/ruby/seed-045.rb +22 -22
  517. package/dist/packages/crucible/seeds/ruby/seed-046 2.rb +27 -27
  518. package/dist/packages/crucible/seeds/ruby/seed-046.rb +27 -27
  519. package/dist/packages/crucible/seeds/ruby/seed-047 2.rb +26 -26
  520. package/dist/packages/crucible/seeds/ruby/seed-047.rb +26 -26
  521. package/dist/packages/crucible/seeds/ruby/seed-048 2.rb +24 -24
  522. package/dist/packages/crucible/seeds/ruby/seed-048.rb +24 -24
  523. package/dist/packages/crucible/seeds/ruby/seed-049 2.rb +20 -20
  524. package/dist/packages/crucible/seeds/ruby/seed-049.rb +20 -20
  525. package/dist/packages/crucible/seeds/ruby/seed-050 2.rb +27 -27
  526. package/dist/packages/crucible/seeds/ruby/seed-050.rb +27 -27
  527. package/dist/packages/crucible/seeds/rust/seed-001 2.rs +25 -25
  528. package/dist/packages/crucible/seeds/rust/seed-001.rs +25 -25
  529. package/dist/packages/crucible/seeds/rust/seed-002 2.rs +20 -20
  530. package/dist/packages/crucible/seeds/rust/seed-002.rs +20 -20
  531. package/dist/packages/crucible/seeds/rust/seed-003 2.rs +23 -23
  532. package/dist/packages/crucible/seeds/rust/seed-003.rs +23 -23
  533. package/dist/packages/crucible/seeds/rust/seed-004 2.rs +21 -21
  534. package/dist/packages/crucible/seeds/rust/seed-004.rs +21 -21
  535. package/dist/packages/crucible/seeds/rust/seed-005 2.rs +21 -21
  536. package/dist/packages/crucible/seeds/rust/seed-005.rs +21 -21
  537. package/dist/packages/crucible/seeds/rust/seed-006 2.rs +24 -24
  538. package/dist/packages/crucible/seeds/rust/seed-006.rs +24 -24
  539. package/dist/packages/crucible/seeds/rust/seed-007 2.rs +20 -20
  540. package/dist/packages/crucible/seeds/rust/seed-007.rs +20 -20
  541. package/dist/packages/crucible/seeds/rust/seed-008 2.rs +19 -19
  542. package/dist/packages/crucible/seeds/rust/seed-008.rs +19 -19
  543. package/dist/packages/crucible/seeds/rust/seed-009 2.rs +28 -28
  544. package/dist/packages/crucible/seeds/rust/seed-009.rs +28 -28
  545. package/dist/packages/crucible/seeds/rust/seed-010 2.rs +28 -28
  546. package/dist/packages/crucible/seeds/rust/seed-010.rs +28 -28
  547. package/dist/packages/crucible/seeds/rust/seed-011 2.rs +25 -25
  548. package/dist/packages/crucible/seeds/rust/seed-011.rs +25 -25
  549. package/dist/packages/crucible/seeds/rust/seed-012 2.rs +31 -31
  550. package/dist/packages/crucible/seeds/rust/seed-012.rs +31 -31
  551. package/dist/packages/crucible/seeds/rust/seed-013 2.rs +27 -27
  552. package/dist/packages/crucible/seeds/rust/seed-013.rs +27 -27
  553. package/dist/packages/crucible/seeds/rust/seed-014 2.rs +30 -30
  554. package/dist/packages/crucible/seeds/rust/seed-014.rs +30 -30
  555. package/dist/packages/crucible/seeds/rust/seed-015 2.rs +33 -33
  556. package/dist/packages/crucible/seeds/rust/seed-015.rs +33 -33
  557. package/dist/packages/crucible/seeds/rust/seed-016 2.rs +22 -22
  558. package/dist/packages/crucible/seeds/rust/seed-016.rs +22 -22
  559. package/dist/packages/crucible/seeds/rust/seed-017 2.rs +28 -28
  560. package/dist/packages/crucible/seeds/rust/seed-017.rs +28 -28
  561. package/dist/packages/crucible/seeds/rust/seed-018 2.rs +21 -21
  562. package/dist/packages/crucible/seeds/rust/seed-018.rs +21 -21
  563. package/dist/packages/crucible/seeds/rust/seed-019 2.rs +36 -36
  564. package/dist/packages/crucible/seeds/rust/seed-019.rs +36 -36
  565. package/dist/packages/crucible/seeds/rust/seed-020 2.rs +27 -27
  566. package/dist/packages/crucible/seeds/rust/seed-020.rs +27 -27
  567. package/dist/packages/crucible/seeds/rust/seed-021 2.rs +26 -26
  568. package/dist/packages/crucible/seeds/rust/seed-021.rs +26 -26
  569. package/dist/packages/crucible/seeds/rust/seed-022 2.rs +23 -23
  570. package/dist/packages/crucible/seeds/rust/seed-022.rs +23 -23
  571. package/dist/packages/crucible/seeds/rust/seed-023 2.rs +22 -22
  572. package/dist/packages/crucible/seeds/rust/seed-023.rs +22 -22
  573. package/dist/packages/crucible/seeds/rust/seed-024 2.rs +24 -24
  574. package/dist/packages/crucible/seeds/rust/seed-024.rs +24 -24
  575. package/dist/packages/crucible/seeds/rust/seed-025 2.rs +29 -29
  576. package/dist/packages/crucible/seeds/rust/seed-025.rs +29 -29
  577. package/dist/packages/crucible/seeds/rust/seed-026 2.rs +23 -23
  578. package/dist/packages/crucible/seeds/rust/seed-026.rs +23 -23
  579. package/dist/packages/crucible/seeds/rust/seed-027 2.rs +24 -24
  580. package/dist/packages/crucible/seeds/rust/seed-027.rs +24 -24
  581. package/dist/packages/crucible/seeds/rust/seed-028 2.rs +25 -25
  582. package/dist/packages/crucible/seeds/rust/seed-028.rs +25 -25
  583. package/dist/packages/crucible/seeds/rust/seed-029 2.rs +25 -25
  584. package/dist/packages/crucible/seeds/rust/seed-029.rs +25 -25
  585. package/dist/packages/crucible/seeds/rust/seed-030 2.rs +30 -30
  586. package/dist/packages/crucible/seeds/rust/seed-030.rs +30 -30
  587. package/dist/packages/crucible/seeds/rust/seed-031 2.rs +22 -22
  588. package/dist/packages/crucible/seeds/rust/seed-031.rs +22 -22
  589. package/dist/packages/crucible/seeds/rust/seed-032 2.rs +25 -25
  590. package/dist/packages/crucible/seeds/rust/seed-032.rs +25 -25
  591. package/dist/packages/crucible/seeds/rust/seed-033 2.rs +25 -25
  592. package/dist/packages/crucible/seeds/rust/seed-033.rs +25 -25
  593. package/dist/packages/crucible/seeds/rust/seed-034 2.rs +20 -20
  594. package/dist/packages/crucible/seeds/rust/seed-034.rs +20 -20
  595. package/dist/packages/crucible/seeds/rust/seed-035 2.rs +28 -28
  596. package/dist/packages/crucible/seeds/rust/seed-035.rs +28 -28
  597. package/dist/packages/crucible/seeds/rust/seed-036 2.rs +26 -26
  598. package/dist/packages/crucible/seeds/rust/seed-036.rs +26 -26
  599. package/dist/packages/crucible/seeds/rust/seed-037 2.rs +31 -31
  600. package/dist/packages/crucible/seeds/rust/seed-037.rs +31 -31
  601. package/dist/packages/crucible/seeds/rust/seed-038 2.rs +25 -25
  602. package/dist/packages/crucible/seeds/rust/seed-038.rs +25 -25
  603. package/dist/packages/crucible/seeds/rust/seed-039 2.rs +28 -28
  604. package/dist/packages/crucible/seeds/rust/seed-039.rs +28 -28
  605. package/dist/packages/crucible/seeds/rust/seed-040 2.rs +27 -27
  606. package/dist/packages/crucible/seeds/rust/seed-040.rs +27 -27
  607. package/dist/packages/crucible/seeds/rust/seed-041 2.rs +32 -32
  608. package/dist/packages/crucible/seeds/rust/seed-041.rs +32 -32
  609. package/dist/packages/crucible/seeds/rust/seed-042 2.rs +27 -27
  610. package/dist/packages/crucible/seeds/rust/seed-042.rs +27 -27
  611. package/dist/packages/crucible/seeds/rust/seed-043 2.rs +29 -29
  612. package/dist/packages/crucible/seeds/rust/seed-043.rs +29 -29
  613. package/dist/packages/crucible/seeds/rust/seed-044 2.rs +25 -25
  614. package/dist/packages/crucible/seeds/rust/seed-044.rs +25 -25
  615. package/dist/packages/crucible/seeds/rust/seed-045 2.rs +28 -28
  616. package/dist/packages/crucible/seeds/rust/seed-045.rs +28 -28
  617. package/dist/packages/crucible/seeds/rust/seed-046 2.rs +25 -25
  618. package/dist/packages/crucible/seeds/rust/seed-046.rs +25 -25
  619. package/dist/packages/crucible/seeds/rust/seed-047 2.rs +34 -34
  620. package/dist/packages/crucible/seeds/rust/seed-047.rs +34 -34
  621. package/dist/packages/crucible/seeds/rust/seed-048 2.rs +21 -21
  622. package/dist/packages/crucible/seeds/rust/seed-048.rs +21 -21
  623. package/dist/packages/crucible/seeds/rust/seed-049 2.rs +26 -26
  624. package/dist/packages/crucible/seeds/rust/seed-049.rs +26 -26
  625. package/dist/packages/crucible/seeds/rust/seed-050 2.rs +23 -23
  626. package/dist/packages/crucible/seeds/rust/seed-050.rs +23 -23
  627. package/dist/packages/crucible/seeds/ts/seed-001 2.ts +32 -32
  628. package/dist/packages/crucible/seeds/ts/seed-001.ts +32 -32
  629. package/dist/packages/crucible/seeds/ts/seed-002 2.ts +34 -34
  630. package/dist/packages/crucible/seeds/ts/seed-002.ts +34 -34
  631. package/dist/packages/crucible/seeds/ts/seed-003 2.ts +28 -28
  632. package/dist/packages/crucible/seeds/ts/seed-003.ts +28 -28
  633. package/dist/packages/crucible/seeds/ts/seed-004 2.ts +34 -34
  634. package/dist/packages/crucible/seeds/ts/seed-004.ts +34 -34
  635. package/dist/packages/crucible/seeds/ts/seed-005 2.ts +32 -32
  636. package/dist/packages/crucible/seeds/ts/seed-005.ts +32 -32
  637. package/dist/packages/crucible/seeds/ts/seed-006 2.ts +31 -31
  638. package/dist/packages/crucible/seeds/ts/seed-006.ts +31 -31
  639. package/dist/packages/crucible/seeds/ts/seed-007 2.ts +28 -28
  640. package/dist/packages/crucible/seeds/ts/seed-007.ts +28 -28
  641. package/dist/packages/crucible/seeds/ts/seed-008 2.ts +40 -40
  642. package/dist/packages/crucible/seeds/ts/seed-008.ts +40 -40
  643. package/dist/packages/crucible/seeds/ts/seed-009 2.ts +31 -31
  644. package/dist/packages/crucible/seeds/ts/seed-009.ts +31 -31
  645. package/dist/packages/crucible/seeds/ts/seed-010 2.ts +33 -33
  646. package/dist/packages/crucible/seeds/ts/seed-010.ts +33 -33
  647. package/dist/packages/crucible/seeds/ts/seed-011 2.ts +29 -29
  648. package/dist/packages/crucible/seeds/ts/seed-011.ts +29 -29
  649. package/dist/packages/crucible/seeds/ts/seed-012 2.ts +34 -34
  650. package/dist/packages/crucible/seeds/ts/seed-012.ts +34 -34
  651. package/dist/packages/crucible/seeds/ts/seed-013 2.ts +31 -31
  652. package/dist/packages/crucible/seeds/ts/seed-013.ts +31 -31
  653. package/dist/packages/crucible/seeds/ts/seed-014 2.ts +36 -36
  654. package/dist/packages/crucible/seeds/ts/seed-014.ts +36 -36
  655. package/dist/packages/crucible/seeds/ts/seed-015 2.ts +31 -31
  656. package/dist/packages/crucible/seeds/ts/seed-015.ts +31 -31
  657. package/dist/packages/crucible/seeds/ts/seed-016 2.ts +37 -37
  658. package/dist/packages/crucible/seeds/ts/seed-016.ts +37 -37
  659. package/dist/packages/crucible/seeds/ts/seed-017 2.ts +44 -44
  660. package/dist/packages/crucible/seeds/ts/seed-017.ts +44 -44
  661. package/dist/packages/crucible/seeds/ts/seed-018 2.ts +33 -33
  662. package/dist/packages/crucible/seeds/ts/seed-018.ts +33 -33
  663. package/dist/packages/crucible/seeds/ts/seed-019 2.ts +32 -32
  664. package/dist/packages/crucible/seeds/ts/seed-019.ts +32 -32
  665. package/dist/packages/crucible/seeds/ts/seed-020 2.ts +33 -33
  666. package/dist/packages/crucible/seeds/ts/seed-020.ts +33 -33
  667. package/dist/packages/crucible/seeds/ts/seed-021 2.ts +33 -33
  668. package/dist/packages/crucible/seeds/ts/seed-021.ts +33 -33
  669. package/dist/packages/crucible/seeds/ts/seed-022 2.ts +34 -34
  670. package/dist/packages/crucible/seeds/ts/seed-022.ts +34 -34
  671. package/dist/packages/crucible/seeds/ts/seed-023 2.ts +33 -33
  672. package/dist/packages/crucible/seeds/ts/seed-023.ts +33 -33
  673. package/dist/packages/crucible/seeds/ts/seed-024 2.ts +35 -35
  674. package/dist/packages/crucible/seeds/ts/seed-024.ts +35 -35
  675. package/dist/packages/crucible/seeds/ts/seed-025 2.ts +29 -29
  676. package/dist/packages/crucible/seeds/ts/seed-025.ts +29 -29
  677. package/dist/packages/crucible/seeds/ts/seed-026 2.ts +36 -36
  678. package/dist/packages/crucible/seeds/ts/seed-026.ts +36 -36
  679. package/dist/packages/crucible/seeds/ts/seed-027 2.ts +30 -30
  680. package/dist/packages/crucible/seeds/ts/seed-027.ts +30 -30
  681. package/dist/packages/crucible/seeds/ts/seed-028 2.ts +32 -32
  682. package/dist/packages/crucible/seeds/ts/seed-028.ts +32 -32
  683. package/dist/packages/crucible/seeds/ts/seed-029 2.ts +34 -34
  684. package/dist/packages/crucible/seeds/ts/seed-029.ts +34 -34
  685. package/dist/packages/crucible/seeds/ts/seed-030 2.ts +37 -37
  686. package/dist/packages/crucible/seeds/ts/seed-030.ts +37 -37
  687. package/dist/packages/crucible/seeds/ts/seed-031 2.ts +31 -31
  688. package/dist/packages/crucible/seeds/ts/seed-031.ts +31 -31
  689. package/dist/packages/crucible/seeds/ts/seed-032 2.ts +32 -32
  690. package/dist/packages/crucible/seeds/ts/seed-032.ts +32 -32
  691. package/dist/packages/crucible/seeds/ts/seed-033 2.ts +32 -32
  692. package/dist/packages/crucible/seeds/ts/seed-033.ts +32 -32
  693. package/dist/packages/crucible/seeds/ts/seed-034 2.ts +34 -34
  694. package/dist/packages/crucible/seeds/ts/seed-034.ts +34 -34
  695. package/dist/packages/crucible/seeds/ts/seed-035 2.ts +29 -29
  696. package/dist/packages/crucible/seeds/ts/seed-035.ts +29 -29
  697. package/dist/packages/crucible/seeds/ts/seed-036 2.ts +34 -34
  698. package/dist/packages/crucible/seeds/ts/seed-036.ts +34 -34
  699. package/dist/packages/crucible/seeds/ts/seed-037 2.ts +33 -33
  700. package/dist/packages/crucible/seeds/ts/seed-037.ts +33 -33
  701. package/dist/packages/crucible/seeds/ts/seed-038 2.ts +29 -29
  702. package/dist/packages/crucible/seeds/ts/seed-038.ts +29 -29
  703. package/dist/packages/crucible/seeds/ts/seed-039 2.ts +35 -35
  704. package/dist/packages/crucible/seeds/ts/seed-039.ts +35 -35
  705. package/dist/packages/crucible/seeds/ts/seed-040 2.ts +31 -31
  706. package/dist/packages/crucible/seeds/ts/seed-040.ts +31 -31
  707. package/dist/packages/crucible/seeds/ts/seed-041 2.ts +29 -29
  708. package/dist/packages/crucible/seeds/ts/seed-041.ts +29 -29
  709. package/dist/packages/crucible/seeds/ts/seed-042 2.ts +33 -33
  710. package/dist/packages/crucible/seeds/ts/seed-042.ts +33 -33
  711. package/dist/packages/crucible/seeds/ts/seed-043 2.ts +32 -32
  712. package/dist/packages/crucible/seeds/ts/seed-043.ts +32 -32
  713. package/dist/packages/crucible/seeds/ts/seed-044 2.ts +36 -36
  714. package/dist/packages/crucible/seeds/ts/seed-044.ts +36 -36
  715. package/dist/packages/crucible/seeds/ts/seed-045 2.ts +39 -39
  716. package/dist/packages/crucible/seeds/ts/seed-045.ts +39 -39
  717. package/dist/packages/crucible/seeds/ts/seed-046 2.ts +41 -41
  718. package/dist/packages/crucible/seeds/ts/seed-046.ts +41 -41
  719. package/dist/packages/crucible/seeds/ts/seed-047 2.ts +33 -33
  720. package/dist/packages/crucible/seeds/ts/seed-047.ts +33 -33
  721. package/dist/packages/crucible/seeds/ts/seed-048 2.ts +33 -33
  722. package/dist/packages/crucible/seeds/ts/seed-048.ts +33 -33
  723. package/dist/packages/crucible/seeds/ts/seed-049 2.ts +34 -34
  724. package/dist/packages/crucible/seeds/ts/seed-049.ts +34 -34
  725. package/dist/packages/crucible/seeds/ts/seed-050 2.ts +41 -41
  726. package/dist/packages/crucible/seeds/ts/seed-050.ts +41 -41
  727. package/dist/packages/crucible/seeds/ts/seed-051 2.ts +34 -34
  728. package/dist/packages/crucible/seeds/ts/seed-051.ts +34 -34
  729. package/dist/packages/crucible/seeds/ts/seed-052 2.ts +36 -36
  730. package/dist/packages/crucible/seeds/ts/seed-052.ts +36 -36
  731. package/dist/packages/scanner/ai-confidence-scorer.js +1 -1
  732. package/dist/packages/scanner/baseline-manager.js +1 -1
  733. package/dist/packages/scanner/code-scanner.js +1 -1
  734. package/dist/packages/scanner/codebase-passport.js +1 -1
  735. package/dist/packages/scanner/copy-paste-detector.js +1 -1
  736. package/dist/packages/scanner/dependency-graph.js +1 -1
  737. package/dist/packages/scanner/drift-detector.js +1 -1
  738. package/dist/packages/scanner/export-verifier.js +1 -1
  739. package/dist/packages/scanner/file-collector.js +1 -1
  740. package/dist/packages/scanner/ghost-code-detector.js +1 -1
  741. package/dist/packages/scanner/hallucination-detector.js +1 -1
  742. package/dist/packages/scanner/license-manager.js +1 -1
  743. package/dist/packages/scanner/pre-commit-gate.js +1 -1
  744. package/dist/packages/scanner/read-file.js +1 -0
  745. package/dist/packages/scanner/taint-tracker.js +1 -1
  746. package/dist/packages/scanner/tech-debt-analyzer.js +1 -1
  747. package/package.json +64 -63
@@ -1,34 +1,34 @@
1
- // [CRUCIBLE-SEED]
2
- // category: deserialization
3
- // severity: critical
4
- // language: typescript
5
- // expected: unsafe deserialization via class-transformer plainToClass with user-controlled type
6
-
7
- import { plainToClass } from 'class-transformer';
8
- import { IsString, IsNumber } from 'class-validator';
9
- import { Request, Response, Router } from 'express';
10
-
11
- class UserDto {
12
- @IsString() username!: string;
13
- @IsString() email!: string;
14
- @IsNumber() role!: number;
15
- }
16
-
17
- class AdminDto extends UserDto {
18
- @IsString() adminToken!: string;
19
- canDeleteUsers: boolean = true;
20
- }
21
-
22
- const router = Router();
23
-
24
- router.post('/users/import', (req: Request, res: Response) => {
25
- const { type, data } = req.body;
26
-
27
- // User controls which class to deserialize into — can escalate to AdminDto
28
- const DtoClass = type === 'admin' ? AdminDto : UserDto;
29
- const instance = plainToClass(DtoClass, data);
30
- // No validation step — properties set without checking constraints
31
- res.json({ created: instance });
32
- });
33
-
34
- export default router;
1
+ // [CRUCIBLE-SEED]
2
+ // category: deserialization
3
+ // severity: critical
4
+ // language: typescript
5
+ // expected: unsafe deserialization via class-transformer plainToClass with user-controlled type
6
+
7
+ import { plainToClass } from 'class-transformer';
8
+ import { IsString, IsNumber } from 'class-validator';
9
+ import { Request, Response, Router } from 'express';
10
+
11
+ class UserDto {
12
+ @IsString() username!: string;
13
+ @IsString() email!: string;
14
+ @IsNumber() role!: number;
15
+ }
16
+
17
+ class AdminDto extends UserDto {
18
+ @IsString() adminToken!: string;
19
+ canDeleteUsers: boolean = true;
20
+ }
21
+
22
+ const router = Router();
23
+
24
+ router.post('/users/import', (req: Request, res: Response) => {
25
+ const { type, data } = req.body;
26
+
27
+ // User controls which class to deserialize into — can escalate to AdminDto
28
+ const DtoClass = type === 'admin' ? AdminDto : UserDto;
29
+ const instance = plainToClass(DtoClass, data);
30
+ // No validation step — properties set without checking constraints
31
+ res.json({ created: instance });
32
+ });
33
+
34
+ export default router;
@@ -1,34 +1,34 @@
1
- // [CRUCIBLE-SEED]
2
- // category: deserialization
3
- // severity: critical
4
- // language: typescript
5
- // expected: unsafe deserialization via class-transformer plainToClass with user-controlled type
6
-
7
- import { plainToClass } from 'class-transformer';
8
- import { IsString, IsNumber } from 'class-validator';
9
- import { Request, Response, Router } from 'express';
10
-
11
- class UserDto {
12
- @IsString() username!: string;
13
- @IsString() email!: string;
14
- @IsNumber() role!: number;
15
- }
16
-
17
- class AdminDto extends UserDto {
18
- @IsString() adminToken!: string;
19
- canDeleteUsers: boolean = true;
20
- }
21
-
22
- const router = Router();
23
-
24
- router.post('/users/import', (req: Request, res: Response) => {
25
- const { type, data } = req.body;
26
-
27
- // User controls which class to deserialize into — can escalate to AdminDto
28
- const DtoClass = type === 'admin' ? AdminDto : UserDto;
29
- const instance = plainToClass(DtoClass, data);
30
- // No validation step — properties set without checking constraints
31
- res.json({ created: instance });
32
- });
33
-
34
- export default router;
1
+ // [CRUCIBLE-SEED]
2
+ // category: deserialization
3
+ // severity: critical
4
+ // language: typescript
5
+ // expected: unsafe deserialization via class-transformer plainToClass with user-controlled type
6
+
7
+ import { plainToClass } from 'class-transformer';
8
+ import { IsString, IsNumber } from 'class-validator';
9
+ import { Request, Response, Router } from 'express';
10
+
11
+ class UserDto {
12
+ @IsString() username!: string;
13
+ @IsString() email!: string;
14
+ @IsNumber() role!: number;
15
+ }
16
+
17
+ class AdminDto extends UserDto {
18
+ @IsString() adminToken!: string;
19
+ canDeleteUsers: boolean = true;
20
+ }
21
+
22
+ const router = Router();
23
+
24
+ router.post('/users/import', (req: Request, res: Response) => {
25
+ const { type, data } = req.body;
26
+
27
+ // User controls which class to deserialize into — can escalate to AdminDto
28
+ const DtoClass = type === 'admin' ? AdminDto : UserDto;
29
+ const instance = plainToClass(DtoClass, data);
30
+ // No validation step — properties set without checking constraints
31
+ res.json({ created: instance });
32
+ });
33
+
34
+ export default router;
@@ -1,36 +1,36 @@
1
- // [CRUCIBLE-SEED]
2
- // category: deserialization
3
- // severity: high
4
- // language: typescript
5
- // expected: unsafe deserialization via JSON.parse used to dynamically dispatch method calls
6
-
7
- import { Request, Response, Router } from 'express';
8
-
9
- interface TaskDescriptor {
10
- module: string;
11
- method: string;
12
- args: unknown[];
13
- }
14
-
15
- const allowedModules: Record<string, Record<string, (...args: unknown[]) => unknown>> = {
16
- reports: require('./reports'),
17
- notifications: require('./notifications'),
18
- };
19
-
20
- router.post('/task/run', (req: Request, res: Response) => {
21
- // task is JSON-parsed from user body without schema validation
22
- const task = JSON.parse(req.body.taskJson as string) as TaskDescriptor;
23
-
24
- const mod = allowedModules[task.module];
25
- if (!mod) return res.status(400).json({ error: 'Unknown module' });
26
-
27
- const fn = mod[task.method];
28
- if (typeof fn !== 'function') return res.status(400).json({ error: 'Unknown method' });
29
-
30
- // task.args passed directly — no sanitization or type validation
31
- const result = fn(...task.args);
32
- res.json({ result });
33
- });
34
-
35
- const router2 = Router();
36
- export default router2;
1
+ // [CRUCIBLE-SEED]
2
+ // category: deserialization
3
+ // severity: high
4
+ // language: typescript
5
+ // expected: unsafe deserialization via JSON.parse used to dynamically dispatch method calls
6
+
7
+ import { Request, Response, Router } from 'express';
8
+
9
+ interface TaskDescriptor {
10
+ module: string;
11
+ method: string;
12
+ args: unknown[];
13
+ }
14
+
15
+ const allowedModules: Record<string, Record<string, (...args: unknown[]) => unknown>> = {
16
+ reports: require('./reports'),
17
+ notifications: require('./notifications'),
18
+ };
19
+
20
+ router.post('/task/run', (req: Request, res: Response) => {
21
+ // task is JSON-parsed from user body without schema validation
22
+ const task = JSON.parse(req.body.taskJson as string) as TaskDescriptor;
23
+
24
+ const mod = allowedModules[task.module];
25
+ if (!mod) return res.status(400).json({ error: 'Unknown module' });
26
+
27
+ const fn = mod[task.method];
28
+ if (typeof fn !== 'function') return res.status(400).json({ error: 'Unknown method' });
29
+
30
+ // task.args passed directly — no sanitization or type validation
31
+ const result = fn(...task.args);
32
+ res.json({ result });
33
+ });
34
+
35
+ const router2 = Router();
36
+ export default router2;
@@ -1,36 +1,36 @@
1
- // [CRUCIBLE-SEED]
2
- // category: deserialization
3
- // severity: high
4
- // language: typescript
5
- // expected: unsafe deserialization via JSON.parse used to dynamically dispatch method calls
6
-
7
- import { Request, Response, Router } from 'express';
8
-
9
- interface TaskDescriptor {
10
- module: string;
11
- method: string;
12
- args: unknown[];
13
- }
14
-
15
- const allowedModules: Record<string, Record<string, (...args: unknown[]) => unknown>> = {
16
- reports: require('./reports'),
17
- notifications: require('./notifications'),
18
- };
19
-
20
- router.post('/task/run', (req: Request, res: Response) => {
21
- // task is JSON-parsed from user body without schema validation
22
- const task = JSON.parse(req.body.taskJson as string) as TaskDescriptor;
23
-
24
- const mod = allowedModules[task.module];
25
- if (!mod) return res.status(400).json({ error: 'Unknown module' });
26
-
27
- const fn = mod[task.method];
28
- if (typeof fn !== 'function') return res.status(400).json({ error: 'Unknown method' });
29
-
30
- // task.args passed directly — no sanitization or type validation
31
- const result = fn(...task.args);
32
- res.json({ result });
33
- });
34
-
35
- const router2 = Router();
36
- export default router2;
1
+ // [CRUCIBLE-SEED]
2
+ // category: deserialization
3
+ // severity: high
4
+ // language: typescript
5
+ // expected: unsafe deserialization via JSON.parse used to dynamically dispatch method calls
6
+
7
+ import { Request, Response, Router } from 'express';
8
+
9
+ interface TaskDescriptor {
10
+ module: string;
11
+ method: string;
12
+ args: unknown[];
13
+ }
14
+
15
+ const allowedModules: Record<string, Record<string, (...args: unknown[]) => unknown>> = {
16
+ reports: require('./reports'),
17
+ notifications: require('./notifications'),
18
+ };
19
+
20
+ router.post('/task/run', (req: Request, res: Response) => {
21
+ // task is JSON-parsed from user body without schema validation
22
+ const task = JSON.parse(req.body.taskJson as string) as TaskDescriptor;
23
+
24
+ const mod = allowedModules[task.module];
25
+ if (!mod) return res.status(400).json({ error: 'Unknown module' });
26
+
27
+ const fn = mod[task.method];
28
+ if (typeof fn !== 'function') return res.status(400).json({ error: 'Unknown method' });
29
+
30
+ // task.args passed directly — no sanitization or type validation
31
+ const result = fn(...task.args);
32
+ res.json({ result });
33
+ });
34
+
35
+ const router2 = Router();
36
+ export default router2;
@@ -1 +1 @@
1
- const fs=require("fs"),path=require("path"),HallucinationDetector=require("./hallucination-detector.js"),RISK_SIGNALS={evalUsage:/\beval\s*\(/,hardcodedSecret:/['"](?:sk-|pk-|xox[pboa]-|AKIA|ghp_|gho_|github_pat_)[a-zA-Z0-9_-]{10,}['"]|['"][a-zA-Z0-9]{32,}['"]|password\s*[:=]\s*['"][^'"]+['"]|["']password["']\s*:\s*["'][^"']+["']/i,commandInjection:/\b(?:child_process\.)?(?:exec|execSync|spawn|spawnSync|execFile|execFileSync)\s*\(\s*(?:`[^`]*\$\{|[^,)]*\+\s*\w|[^,)]*\+\s*['"`])/,sqlInjection:/\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|WHERE)\b.*(\$\{|\+\s*['"]|\+\s*\w+|f['"].*\{|%s|%d)/i},AI_SIGNALS={genericNames:["data","result","response","output","input","temp","item","element","val","obj","arr","str","num","flag","status"],aiCommentPatterns:[/\/\/\s*TODO:?\s*implement/i,/\/\/\s*handle\s+error/i,/\/\/\s*add\s+error\s+handling/i,/\/\/\s*you\s+(can|may|might|should)/i,/\/\/\s*replace\s+(this|with)/i,/\/\/\s*this\s+(function|method|class)\s+(takes|accepts|returns|handles)/i,/\/\/\s*example\s+usage/i,/\/\/\s*note:?\s*this/i,/\/\*\*[\s\S]*?@description\s/,/\/\/\s*import.*if\s+needed/i],aiStructurePatterns:[/try\s*\{[\s\S]*?\}\s*catch\s*\(\s*(?:error|err|e)\s*\)\s*\{[\s\S]*?console\.(log|error)\s*\(\s*(?:error|err|e)\s*\)/,/if\s*\(!.*\)\s*\{?\s*(?:return|throw)\s/]};class AIConfidenceScorer{constructor(e={}){this.rootPath=e.rootPath||process.cwd(),this.hallDetector=e.hallucinationDetector||new HallucinationDetector({rootPath:this.rootPath})}async scan(e){const t=[];let s=new Map;try{const t=await this.hallDetector.scan(e),n=[...t.phantomAPIs||[],...t.deprecatedAPIs||[]];for(const e of n)s.has(e.file)||s.set(e.file,[]),s.get(e.file).push(e)}catch(e){}for(const n of e){let e;try{if(fs.statSync(n).size>524288)continue;e=fs.readFileSync(n,"utf-8")}catch(e){continue}const i=path.relative(this.rootPath,n);if(this._isSkippable(i))continue;const r=s.get(i)||[],c=this._extractFunctions(e,i,r);t.push(...c)}t.sort((e,t)=>t.confidence-e.confidence);const n=t.filter(e=>e.confidence>=70),i=t.filter(e=>e.confidence>=40&&e.confidence<70),r=t.filter(e=>e.riskScore>=70),c=t.filter(e=>e.riskScore>=40&&e.riskScore<70);return{functions:t,highConfidence:n,mediumConfidence:i,totalFunctions:t.length,aiLikelyCount:n.length,aiPossibleCount:i.length,aiPercentage:t.length>0?Math.round(n.length/t.length*100):0,highRisk:r,mediumRisk:c,highRiskCount:r.length,mediumRiskCount:c.length}}_extractFunctions(e,t,s=[]){const n=e.split("\n"),i=[];for(let e=0;e<n.length;e++){const r=this._detectFunction(n,e);if(!r)continue;const c=this._findFunctionEnd(n,e),o=n.slice(e,c+1).join("\n"),a=n.slice(e,c+1),l=e+1,h=c+1,u=this._scoreFunction(r.name,o,a,n,e),m=this._scoreRisk(o,a,s,l,h);i.push({file:t,name:r.name,line:e+1,lineCount:c-e+1,confidence:u.score,signals:u.reasons,riskScore:m.score,riskSignals:m.reasons,riskSeverity:m.score>=70?"critical":m.score>=40?"high":m.score>=15?"medium":"low",testCoverage:this._hasTests(r.name,t)?"found":"none",category:"ai_confidence",severity:u.score>=80?"high":u.score>=50?"medium":"low"}),e=c}return i}_scoreRisk(e,t,s,n,i){let r=0;const c=[];RISK_SIGNALS.evalUsage.test(e)&&(r+=40,c.push("eval() usage — arbitrary code execution risk"));const o=s.filter(e=>e.line>=n&&e.line<=i);o.length>0&&(r+=Math.min(30,15*o.length),c.push(`${o.length} hallucinated/deprecated API usage(s): ${o.map(e=>e.name).slice(0,3).join(", ")}`));const a=this._maxBraceDepth(t);a>3&&(r+=Math.min(20,8*(a-3)),c.push(`Deep nesting — ${a} levels (>3 is risky)`));const l=this._cyclomaticComplexity(e);return l>10&&(r+=Math.min(15,Math.round(1.5*(l-10))),c.push(`High cyclomatic complexity (${l})`)),RISK_SIGNALS.hardcodedSecret.test(e)&&(r+=25,c.push("Hardcoded secret/credential detected")),RISK_SIGNALS.commandInjection.test(e)&&(r+=25,c.push("Command injection risk — exec/spawn with unsanitized string concatenation")),RISK_SIGNALS.sqlInjection.test(e)&&(r+=25,c.push("SQL injection risk — query built via string concatenation/interpolation")),r=Math.min(100,r),{score:r,reasons:c}}_maxBraceDepth(e){let t=0,s=0;for(const n of e)for(const e of n)"{"===e?(t++,t>s&&(s=t)):"}"===e&&(t=Math.max(0,t-1));return Math.max(0,s-1)}_cyclomaticComplexity(e){return 1+(e.match(/\b(if|else if|for|while|case|catch)\b|\?\s*[^:]+:|&&|\|\|/g)||[]).length}_scoreFunction(e,t,s,n,i){let r=0;const c=[],o=AI_SIGNALS.genericNames.filter(e=>{const s=new RegExp(`\\b${e}\\b`,"g");return(t.match(s)||[]).length>=2}).length;o>=3?(r+=15,c.push(`${o} generic variable names (data, result, response...)`)):o>=1&&(r+=8,c.push(`${o} generic variable name(s)`));const a=AI_SIGNALS.aiCommentPatterns.filter(e=>e.test(t)).length;if(a>=2?(r+=20,c.push(`${a} AI-style comments (TODO: implement, handle error, etc.)`)):a>=1&&(r+=10,c.push("AI-style comment detected")),i>0){const e=n.slice(Math.max(0,i-10),i).join("\n");/\/\*\*[\s\S]*@param[\s\S]*@returns?[\s\S]*@(?:throws|example)/s.test(e)&&(r+=10,c.push("Exhaustive JSDoc (params + returns + throws/example)"))}(t.match(/try\s*\{/g)||[]).length>=2&&(r+=10,c.push("Multiple try/catch blocks (AI over-handles errors)"));const l=s.length;return l>=50?(r+=15,c.push(`${l} lines — large self-contained function`)):l>=25&&(r+=8,c.push(`${l} lines`)),0===n.slice(0,i).concat(n.slice(i+l)).filter(t=>t.includes(e)).length&&l>10&&(r+=10,c.push("Isolated — no internal references")),/catch\s*\([^)]*\)\s*\{[^}]*console\.(log|error)\s*\(/.test(t)&&(r+=8,c.push("console.log/error in catch block (AI default pattern)")),/=>\s*\{[\s\n\r]*const\s*\{/.test(t)&&(r+=5,c.push("Immediate destructuring after arrow (common AI pattern)")),(t.match(/`[^`]*\$\{/g)||[]).length>=4&&(r+=5,c.push("Heavy template literal usage")),r=Math.min(100,r),{score:r,reasons:c}}_detectFunction(e,t){const s=e[t].trim();let n=s.match(/^(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);return n?{name:n[1]}:(n=s.match(/^(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:async\s+)?(?:function|\()/),n&&(s.includes("=>")||s.includes("function"))?{name:n[1]}:null)}_findFunctionEnd(e,t){let s=0,n=!1;for(let i=t;i<e.length&&i<t+300;i++){for(const t of e[i])"{"===t&&(s++,n=!0),"}"===t&&s--;if(n&&s<=0)return i}return Math.min(t+20,e.length-1)}_hasTests(e,t){return[t.replace(/\.(js|ts)$/,".test.$1"),t.replace(/\.(js|ts)$/,".spec.$1"),t.replace(/src\//,"test/"),t.replace(/src\//,"__tests__/")].some(e=>{try{return fs.existsSync(path.resolve(this.rootPath,e))}catch(e){return!1}})}_isSkippable(e){return[/\.test\./i,/\.spec\./i,/test[\/\\]/i,/spec[\/\\]/i,/__test__/i,/__mocks__/i,/\.config\./i,/\.d\.ts$/,/node_modules/i,/\.min\./i].some(t=>t.test(e))}}module.exports=AIConfidenceScorer;
1
+ const fs=require("fs"),path=require("path"),HallucinationDetector=require("./hallucination-detector.js"),RISK_SIGNALS={evalUsage:/\beval\s*\(/,hardcodedSecret:/['"](?:sk-|pk-|xox[pboa]-|AKIA|ghp_|gho_|github_pat_)[a-zA-Z0-9_-]{10,}['"]|['"][a-zA-Z0-9]{32,}['"]|password\s*[:=]\s*['"][^'"]+['"]|["']password["']\s*:\s*["'][^"']+["']/i,commandInjection:/\b(?:child_process\.)?(?:exec|execSync|spawn|spawnSync|execFile|execFileSync)\s*\(\s*(?:`[^`]*\$\{|[^,)]*\+\s*\w|[^,)]*\+\s*['"`])/,sqlInjection:/\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|WHERE)\b.*(\$\{|\+\s*['"]|\+\s*\w+|f['"].*\{|%s|%d)/i},AI_SIGNALS={genericNames:["data","result","response","output","input","temp","item","element","val","obj","arr","str","num","flag","status"],aiCommentPatterns:[/\/\/\s*TODO:?\s*implement/i,/\/\/\s*handle\s+error/i,/\/\/\s*add\s+error\s+handling/i,/\/\/\s*you\s+(can|may|might|should)/i,/\/\/\s*replace\s+(this|with)/i,/\/\/\s*this\s+(function|method|class)\s+(takes|accepts|returns|handles)/i,/\/\/\s*example\s+usage/i,/\/\/\s*note:?\s*this/i,/\/\*\*[\s\S]*?@description\s/,/\/\/\s*import.*if\s+needed/i],aiStructurePatterns:[/try\s*\{[\s\S]*?\}\s*catch\s*\(\s*(?:error|err|e)\s*\)\s*\{[\s\S]*?console\.(log|error)\s*\(\s*(?:error|err|e)\s*\)/,/if\s*\(!.*\)\s*\{?\s*(?:return|throw)\s/]};class AIConfidenceScorer{constructor(e={}){this.rootPath=e.rootPath||process.cwd(),this.hallDetector=e.hallucinationDetector||new HallucinationDetector({rootPath:this.rootPath})}async scan(e){const t=[];let s=new Map;try{const t=await this.hallDetector.scan(e),n=[...t.phantomAPIs||[],...t.deprecatedAPIs||[]];for(const e of n)s.has(e.file)||s.set(e.file,[]),s.get(e.file).push(e)}catch(e){}for(const n of e){let e;try{if(fs.statSync(n).size>524288)continue;e=fs.readFileSync(n,"utf-8").replace(/\r\n/g,"\n")}catch(e){continue}const i=path.relative(this.rootPath,n);if(this._isSkippable(i))continue;const c=s.get(i)||[],r=this._extractFunctions(e,i,c);t.push(...r)}t.sort((e,t)=>t.confidence-e.confidence);const n=t.filter(e=>e.confidence>=70),i=t.filter(e=>e.confidence>=40&&e.confidence<70),c=t.filter(e=>e.riskScore>=70),r=t.filter(e=>e.riskScore>=40&&e.riskScore<70);return{functions:t,highConfidence:n,mediumConfidence:i,totalFunctions:t.length,aiLikelyCount:n.length,aiPossibleCount:i.length,aiPercentage:t.length>0?Math.round(n.length/t.length*100):0,highRisk:c,mediumRisk:r,highRiskCount:c.length,mediumRiskCount:r.length}}_extractFunctions(e,t,s=[]){const n=e.split("\n"),i=[];for(let e=0;e<n.length;e++){const c=this._detectFunction(n,e);if(!c)continue;const r=this._findFunctionEnd(n,e),o=n.slice(e,r+1).join("\n"),a=n.slice(e,r+1),l=e+1,h=r+1,u=this._scoreFunction(c.name,o,a,n,e),m=this._scoreRisk(o,a,s,l,h);i.push({file:t,name:c.name,line:e+1,lineCount:r-e+1,confidence:u.score,signals:u.reasons,riskScore:m.score,riskSignals:m.reasons,riskSeverity:m.score>=70?"critical":m.score>=40?"high":m.score>=15?"medium":"low",testCoverage:this._hasTests(c.name,t)?"found":"none",category:"ai_confidence",severity:u.score>=80?"high":u.score>=50?"medium":"low"}),e=r}return i}_scoreRisk(e,t,s,n,i){let c=0;const r=[];RISK_SIGNALS.evalUsage.test(e)&&(c+=40,r.push("eval() usage — arbitrary code execution risk"));const o=s.filter(e=>e.line>=n&&e.line<=i);o.length>0&&(c+=Math.min(30,15*o.length),r.push(`${o.length} hallucinated/deprecated API usage(s): ${o.map(e=>e.name).slice(0,3).join(", ")}`));const a=this._maxBraceDepth(t);a>3&&(c+=Math.min(20,8*(a-3)),r.push(`Deep nesting — ${a} levels (>3 is risky)`));const l=this._cyclomaticComplexity(e);return l>10&&(c+=Math.min(15,Math.round(1.5*(l-10))),r.push(`High cyclomatic complexity (${l})`)),RISK_SIGNALS.hardcodedSecret.test(e)&&(c+=25,r.push("Hardcoded secret/credential detected")),RISK_SIGNALS.commandInjection.test(e)&&(c+=25,r.push("Command injection risk — exec/spawn with unsanitized string concatenation")),RISK_SIGNALS.sqlInjection.test(e)&&(c+=25,r.push("SQL injection risk — query built via string concatenation/interpolation")),c=Math.min(100,c),{score:c,reasons:r}}_maxBraceDepth(e){let t=0,s=0;for(const n of e)for(const e of n)"{"===e?(t++,t>s&&(s=t)):"}"===e&&(t=Math.max(0,t-1));return Math.max(0,s-1)}_cyclomaticComplexity(e){return 1+(e.match(/\b(if|else if|for|while|case|catch)\b|\?\s*[^:]+:|&&|\|\|/g)||[]).length}_scoreFunction(e,t,s,n,i){let c=0;const r=[],o=AI_SIGNALS.genericNames.filter(e=>{const s=new RegExp(`\\b${e}\\b`,"g");return(t.match(s)||[]).length>=2}).length;o>=3?(c+=15,r.push(`${o} generic variable names (data, result, response...)`)):o>=1&&(c+=8,r.push(`${o} generic variable name(s)`));const a=AI_SIGNALS.aiCommentPatterns.filter(e=>e.test(t)).length;if(a>=2?(c+=20,r.push(`${a} AI-style comments (TODO: implement, handle error, etc.)`)):a>=1&&(c+=10,r.push("AI-style comment detected")),i>0){const e=n.slice(Math.max(0,i-10),i).join("\n");/\/\*\*[\s\S]*@param[\s\S]*@returns?[\s\S]*@(?:throws|example)/s.test(e)&&(c+=10,r.push("Exhaustive JSDoc (params + returns + throws/example)"))}(t.match(/try\s*\{/g)||[]).length>=2&&(c+=10,r.push("Multiple try/catch blocks (AI over-handles errors)"));const l=s.length;return l>=50?(c+=15,r.push(`${l} lines — large self-contained function`)):l>=25&&(c+=8,r.push(`${l} lines`)),0===n.slice(0,i).concat(n.slice(i+l)).filter(t=>t.includes(e)).length&&l>10&&(c+=10,r.push("Isolated — no internal references")),/catch\s*\([^)]*\)\s*\{[^}]*console\.(log|error)\s*\(/.test(t)&&(c+=8,r.push("console.log/error in catch block (AI default pattern)")),/=>\s*\{[\s\n\r]*const\s*\{/.test(t)&&(c+=5,r.push("Immediate destructuring after arrow (common AI pattern)")),(t.match(/`[^`]*\$\{/g)||[]).length>=4&&(c+=5,r.push("Heavy template literal usage")),c=Math.min(100,c),{score:c,reasons:r}}_detectFunction(e,t){const s=e[t].trim();let n=s.match(/^(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);return n?{name:n[1]}:(n=s.match(/^(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:async\s+)?(?:function|\()/),n&&(s.includes("=>")||s.includes("function"))?{name:n[1]}:(n=s.match(/^func\s+(?:\([^)]*\)\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/),n?{name:n[1]}:(n=s.match(/^(?:private\s+|public\s+|internal\s+|protected\s+)?(?:suspend\s+)?fun\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*[(<]/),n?{name:n[1]}:(n=s.match(/^(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*[(<]/),n?{name:n[1]}:(n=s.match(/^(?:(?:public|private|protected|static)\s+)*function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/),n?{name:n[1]}:(n=s.match(/^def\s+(?:self\.)?([a-zA-Z_][a-zA-Z0-9_?!]*)\s*[(\s]?/),n?{name:n[1]}:null))))))}_findFunctionEnd(e,t){const s=(e[t]||"").trim();if(/^def\s/.test(s)){let s=1;for(let n=t+1;n<e.length&&n<t+300;n++){const t=e[n].trim();if(/^(?:def|class|module|begin|case)\b/.test(t)&&s++,/^(?:if|unless|while|until|for)\b/.test(t)&&!/\bthen\b/.test(t)&&s++,/\bdo\s*(\|[^|]*\|)?\s*$/.test(t)&&s++,/^end\b/.test(t)&&s--,s<=0)return n}return Math.min(t+20,e.length-1)}let n=0,i=!1;for(let s=t;s<e.length&&s<t+300;s++){for(const t of e[s])"{"===t&&(n++,i=!0),"}"===t&&n--;if(i&&n<=0)return s}return Math.min(t+20,e.length-1)}_hasTests(e,t){return[t.replace(/\.(js|ts)$/,".test.$1"),t.replace(/\.(js|ts)$/,".spec.$1"),t.replace(/src\//,"test/"),t.replace(/src\//,"__tests__/")].some(e=>{try{return fs.existsSync(path.resolve(this.rootPath,e))}catch(e){return!1}})}_isSkippable(e){return[/\.test\./i,/\.spec\./i,/test[\/\\]/i,/spec[\/\\]/i,/__test__/i,/__mocks__/i,/\.config\./i,/\.d\.ts$/,/node_modules/i,/\.min\./i].some(t=>t.test(e))}}module.exports=AIConfidenceScorer;
@@ -1 +1 @@
1
- const fs=require("fs"),path=require("path"),crypto=require("crypto"),BASELINE_FILE=".thuban-baseline.json";class BaselineManager{constructor(e){this.rootPath=path.resolve(e),this.baselinePath=path.join(this.rootPath,BASELINE_FILE);const n=path.resolve(this.baselinePath);if(!n.startsWith(this.rootPath+path.sep)&&n!==this.rootPath)throw new Error(`[BaselineManager] Baseline path escapes root directory: ${n}`)}_atomicWriteSync(e,n){const s=e+".tmp."+process.pid+"."+Date.now();fs.writeFileSync(s,n,"utf-8"),fs.renameSync(s,e)}create(e){const n={version:1,created:(new Date).toISOString(),generator:"thuban",issueCount:e.length,issues:e.map(e=>({fingerprint:this._fingerprint(e),file:e.file,category:e.category||"unknown",severity:e.severity||"unknown",id:e.id||null,message:(e.message||"").substring(0,200)}))};return n._hmac=this._computeHmac(n),this._atomicWriteSync(this.baselinePath,JSON.stringify(n,null,2)),{path:this.baselinePath,count:e.length}}load(){try{const e=fs.readFileSync(this.baselinePath,"utf-8");if(e.length>10485760)return console.warn("[BASELINE] WARNING: Baseline file exceeds 10MB — ignoring"),null;const n=JSON.parse(e);if(null==n||"object"!=typeof n||Array.isArray(n))return console.warn("[BASELINE] WARNING: Baseline file has invalid root type — ignoring"),null;if("__proto__"in n||"constructor"in n||"prototype"in n)return console.warn("[BASELINE] WARNING: Baseline file contains prototype pollution keys — ignoring"),null;if(1!==n.version||"thuban"!==n.generator)return console.warn("[BASELINE] WARNING: Baseline file has invalid version/generator — ignoring"),null;if(!Array.isArray(n.issues))return console.warn("[BASELINE] WARNING: Baseline file has non-array issues field — ignoring"),null;if("number"!=typeof n.issueCount||n.issueCount!==n.issues.length)return console.warn("[BASELINE] WARNING: Baseline issueCount mismatch — ignoring"),null;for(const e of n.issues){if(null==e||"object"!=typeof e||Array.isArray(e))return console.warn("[BASELINE] WARNING: Baseline contains invalid issue entry — ignoring"),null;if("__proto__"in e||"constructor"in e)return console.warn("[BASELINE] WARNING: Baseline issue contains prototype pollution keys — ignoring"),null;if("string"!=typeof e.fingerprint||!/^[a-f0-9]{16}$/.test(e.fingerprint))return console.warn("[BASELINE] WARNING: Baseline issue has invalid fingerprint — ignoring"),null}if(n._hmac){const e=this._computeHmac(n);if(n._hmac!==e)return console.warn("[BASELINE] WARNING: Baseline HMAC mismatch — file may have been tampered with — ignoring"),null}else console.warn("[BASELINE] NOTE: Baseline file has no integrity tag — re-run `thuban baseline` to add one");return n}catch(e){return null}}filterNew(e){const n=this.load();if(!n||!n.issues)return{newIssues:e,baselinedIssues:[],baselineExists:!1};const s=new Set(n.issues.map(e=>e.fingerprint)),t=[],i=[];for(const n of e){const e=this._fingerprint(n);s.has(e)?i.push(n):t.push(n)}return{newIssues:t,baselinedIssues:i,baselineExists:!0,baselineDate:n.created,baselineCount:n.issueCount}}exists(){return fs.existsSync(this.baselinePath)}_fingerprint(e){const n=[e.file||"",e.category||"",e.id||"",(e.message||"").substring(0,100)].join("|");return crypto.createHash("sha256").update(n).digest("hex").substring(0,16)}_computeHmac(e){const n=JSON.stringify({version:e.version,created:e.created,generator:e.generator,issueCount:e.issueCount,issues:e.issues});return crypto.createHmac("sha256",this.baselinePath).update(n).digest("hex")}}module.exports=BaselineManager;
1
+ const fs=require("fs"),path=require("path"),crypto=require("crypto"),BASELINE_FILE=".thuban-baseline.json";class BaselineManager{constructor(e){this.rootPath=path.resolve(e),this.baselinePath=path.join(this.rootPath,BASELINE_FILE);const n=path.resolve(this.baselinePath);if(!n.startsWith(this.rootPath+path.sep)&&n!==this.rootPath)throw new Error(`[BaselineManager] Baseline path escapes root directory: ${n}`)}_atomicWriteSync(e,n){const s=e+".tmp."+process.pid+"."+Date.now();fs.writeFileSync(s,n,"utf-8"),fs.renameSync(s,e)}create(e){const n={version:1,created:(new Date).toISOString(),generator:"thuban",issueCount:e.length,issues:e.map(e=>({fingerprint:this._fingerprint(e),file:e.file,category:e.category||"unknown",severity:e.severity||"unknown",id:e.id||null,message:(e.message||"").substring(0,200)}))};return n._hmac=this._computeHmac(n),this._atomicWriteSync(this.baselinePath,JSON.stringify(n,null,2)),{path:this.baselinePath,count:e.length}}load(){try{const e=fs.readFileSync(this.baselinePath,"utf-8");if(e.length>10485760)return console.warn("[BASELINE] WARNING: Baseline file exceeds 10MB — ignoring"),null;const n=JSON.parse(e);if(null==n||"object"!=typeof n||Array.isArray(n))return console.warn("[BASELINE] WARNING: Baseline file has invalid root type — ignoring"),null;const s=(e,n)=>Object.prototype.hasOwnProperty.call(e,n);if(s(n,"__proto__")||s(n,"constructor")||s(n,"prototype"))return console.warn("[BASELINE] WARNING: Baseline file contains prototype pollution keys — ignoring"),null;if(1!==n.version||"thuban"!==n.generator)return console.warn("[BASELINE] WARNING: Baseline file has invalid version/generator — ignoring"),null;if(!Array.isArray(n.issues))return console.warn("[BASELINE] WARNING: Baseline file has non-array issues field — ignoring"),null;if("number"!=typeof n.issueCount||n.issueCount!==n.issues.length)return console.warn("[BASELINE] WARNING: Baseline issueCount mismatch — ignoring"),null;for(const e of n.issues){if(null==e||"object"!=typeof e||Array.isArray(e))return console.warn("[BASELINE] WARNING: Baseline contains invalid issue entry — ignoring"),null;if(s(e,"__proto__")||s(e,"constructor"))return console.warn("[BASELINE] WARNING: Baseline issue contains prototype pollution keys — ignoring"),null;if("string"!=typeof e.fingerprint||!/^[a-f0-9]{16}$/.test(e.fingerprint))return console.warn("[BASELINE] WARNING: Baseline issue has invalid fingerprint — ignoring"),null}if(n._hmac){const e=this._computeHmac(n);if(n._hmac!==e)return console.warn("[BASELINE] WARNING: Baseline HMAC mismatch — file may have been tampered with — ignoring"),null}else console.warn("[BASELINE] NOTE: Baseline file has no integrity tag — re-run `thuban baseline` to add one");return n}catch(e){return null}}filterNew(e){const n=this.load();if(!n||!n.issues)return{newIssues:e,baselinedIssues:[],baselineExists:!1};const s=new Set(n.issues.map(e=>e.fingerprint)),t=[],i=[];for(const n of e){const e=this._fingerprint(n);s.has(e)?i.push(n):t.push(n)}return{newIssues:t,baselinedIssues:i,baselineExists:!0,baselineDate:n.created,baselineCount:n.issueCount}}exists(){return fs.existsSync(this.baselinePath)}_fingerprint(e){const n=[e.file||"",e.category||"",e.id||"",(e.message||"").substring(0,100)].join("|");return crypto.createHash("sha256").update(n).digest("hex").substring(0,16)}_computeHmac(e){const n=JSON.stringify({version:e.version,created:e.created,generator:e.generator,issueCount:e.issueCount,issues:e.issues});return crypto.createHmac("sha256",this.baselinePath).update(n).digest("hex")}}module.exports=BaselineManager;
@@ -1 +1 @@
1
- const fs=require("fs"),path=require("path"),EventEmitter=require("events"),astAnalyzer=require("./ast-analyzer"),taintTracker=require("./taint-tracker"),cfgAnalyzer=require("./cfg-analyzer"),{getCodeScannerPatterns:getCodeScannerPatterns}=require("../crucible/rule-loader.js");class CodeScanner extends EventEmitter{constructor(e={}){super();const t=new Set(["rootPath","ignorePatterns","maxFileSize","totalScanTimeout","perFileTimeout","complexityThreshold","nestingThreshold"]),s={};for(const n of Object.keys(e))t.has(n)&&(s[n]=e[n]);this.config={rootPath:s.rootPath||process.cwd(),ignorePatterns:s.ignorePatterns||["node_modules/**",".git/**","dist/**",".orion/snapshots/**"],maxFileSize:s.maxFileSize||5242880,totalScanTimeout:s.totalScanTimeout||3e5,perFileTimeout:s.perFileTimeout||1e4,complexityThreshold:s.complexityThreshold||15,nestingThreshold:s.nestingThreshold||4},this._scanStartTime=null,this.safePatterns=[/keyEnv\s*[:=]/i,/envVar\s*[:=]/i,/process\.env\[/,/process\.env\./,/model\s*[:=]\s*['"][^'"]+['"]/i,/version\s*[:=]\s*['"][^'"]+['"]/i,/claude-|gpt-|gemini-|llama-|mistral-|deepseek-|grok-|sonar-/i,/endpoint\s*[:=]/i,/https?:\/\//,/_API_KEY['"]/,/API_KEY['"]\s*\)/,/errors?\.\w+\s*=/i,/Error\s*[:=]/i,/required|invalid|must be|cannot be/i],this.testFilePatterns=[/\.test\.(js|ts|jsx|tsx)$/,/\.spec\.(js|ts|jsx|tsx)$/,/__tests__\//,/\.mock\.(js|ts)$/,/test-.*\.js$/],this.securityPatterns=getCodeScannerPatterns(),this.qualityPatterns=[{id:"QUAL001",name:"Console.log in Production",pattern:/console\.(log|debug|info)\(/,severity:"info",message:"console.log found - consider using proper logging",skipInTests:!0,skipInDirs:["test","scripts","tools","dev"]},{id:"QUAL002",name:"TODO Comment",pattern:/\/\/\s*TODO|\/\*\s*TODO|\*\s*TODO|#\s*TODO/i,severity:"info",message:"TODO comment found - track in issue system"},{id:"QUAL003",name:"FIXME Comment",pattern:/\/\/\s*FIXME|\/\*\s*FIXME|\*\s*FIXME|#\s*FIXME/i,severity:"warning",message:"FIXME comment found - needs attention"},{id:"QUAL004",name:"Empty Catch Block",pattern:/catch\s*\([^)]*\)\s*\{\s*\}/,severity:"warning",message:"Empty catch block swallows errors"},{id:"QUAL005",name:"Magic Number",pattern:/(?<![0-9a-zA-Z_])(?!(?:1000|2000|3000|3001|4000|5000|8000|8080|9000|80|443|24|60|1024|2048|4096)\b)\d{5,}(?![0-9])/,severity:"info",message:"Magic number detected - consider using named constant",skipInTests:!0},{id:"QUAL006",name:"Callback Hell",pattern:/\)\s*=>\s*\{[^}]*\)\s*=>\s*\{[^}]*\)\s*=>\s*\{/,severity:"warning",message:"Deeply nested callbacks - consider async/await"}],this.performancePatterns=[{id:"PERF001",name:"Sync FS in Async",pattern:/fs\.(readFileSync|writeFileSync|readdirSync|statSync)/,severity:"warning",message:"Synchronous file operation - may block event loop"},{id:"PERF002",name:"Missing Await",pattern:/async\s+function[^{]*\{[^}]*(?<!await\s)new\s+Promise/,severity:"warning",message:"Promise created in async function without await"},{id:"PERF003",name:"Loop Database Query",pattern:/for\s*\([^)]*\)\s*\{[^}]*(query|find|select|update)/i,severity:"error",message:"Possible N+1 query pattern - consider batch operation"},{id:"PERF004",name:"Large Array in Memory",pattern:/new\s+Array\s*\(\s*\d{6,}\s*\)/,severity:"warning",message:"Large array allocation - consider streaming"}],this.aiPatterns=[{id:"AI001",name:"Potentially Hallucinated Import",pattern:/require\(['"](?!\.)[^'"]+['"]\)/,severity:"info",message:"External dependency - verify it exists in package.json",check:(e,t,s)=>this._checkDependencyExists(e,s)},{id:"AI002",name:"Unused Import",pattern:/const\s+(\w+)\s*=\s*require\(['"][^'"]+['"]\)/,severity:"info",message:"Import may be unused - verify usage",check:(e,t)=>this._checkImportUsage(e,t)},{id:"AI003",name:"Over-Engineered Pattern",pattern:/class\s+\w+Factory\s*\{|AbstractFactory|Singleton\.getInstance/,severity:"info",message:"Complex pattern detected - ensure justified by requirements"}]}async scanAll(){console.log("[CODE-SCANNER] Starting full codebase scan...");const e=Date.now();this._scanStartTime=e;const t={filesScanned:0,filesSkipped:0,issues:[],summary:{},duration:0};try{const s=await this._discoverFiles(this.config.rootPath);console.log(`[CODE-SCANNER] Found ${s.length} files to scan`),this._exportTaintMap=this._buildExportTaintMap(s);for(const n of s){if(Date.now()-e>this.config.totalScanTimeout){console.warn(`[CODE-SCANNER] Total scan timeout (${Math.round(this.config.totalScanTimeout/1e3)}s) reached — stopping scan`),t.issues.push({file:"scan",category:"info",severity:"warning",id:"TIMEOUT001",message:`Scan stopped after ${Math.round(this.config.totalScanTimeout/1e3)}s timeout — ${s.length-t.filesScanned} files not scanned`});break}const i=Date.now(),r=await this.scanFile(n),a=Date.now()-i;a>this.config.perFileTimeout&&console.warn(`[CODE-SCANNER] File ${n} took ${a}ms (>${this.config.perFileTimeout}ms) — results included but flagged`),t.filesScanned++,t.issues.push(...r)}return this._exportTaintMap=null,t.duration=Date.now()-e,t.summary=this._summarizeIssues(t.issues),console.log(`[CODE-SCANNER] Scan complete: ${t.filesScanned} files, ${t.issues.length} issues`),t}catch(e){return console.error("[CODE-SCANNER] Scan failed"),t.error="Scan failed",t}}async scanFiles(e){const t=Date.now();this._scanStartTime=t;const s={filesScanned:0,filesSkipped:0,issues:[],summary:{}};this._exportTaintMap=this._buildExportTaintMap(e);for(const n of e){if(Date.now()-t>this.config.totalScanTimeout){console.warn(`[CODE-SCANNER] Total scan timeout (${Math.round(this.config.totalScanTimeout/1e3)}s) reached — stopping scan`),s.issues.push({file:"scan",category:"info",severity:"warning",id:"TIMEOUT001",message:`Scan stopped after ${Math.round(this.config.totalScanTimeout/1e3)}s timeout — ${e.length-s.filesScanned} files not scanned`});break}const i=Date.now(),r=await this.scanFile(n),a=Date.now()-i;a>this.config.perFileTimeout&&(console.warn(`[CODE-SCANNER] WARNING: ${n} took ${a}ms (>${this.config.perFileTimeout}ms) — skipping slow file`),s.filesSkipped++,s.issues.push({file:n,category:"performance",severity:"warning",id:"TIMEOUT002",message:`File scan took ${a}ms — exceeded ${this.config.perFileTimeout}ms per-file limit`})),s.filesScanned++,s.issues.push(...r)}return this._exportTaintMap=null,s.summary=this._summarizeIssues(s.issues),s}async scanFile(e){const t=[];try{const s=fs.statSync(e);if(s.isSymbolicLink&&s.isSymbolicLink())return[];if(0===s.size)return[];if(s.size>this.config.maxFileSize)return[{file:e,category:"quality",severity:"info",id:"FILE001",message:`File too large (${(s.size/1048576).toFixed(1)}MB > ${(this.config.maxFileSize/1048576).toFixed(0)}MB limit) - skipped`}];let n;try{n=fs.readFileSync(e)}catch(e){return[]}const i=Math.min(n.length,512);for(let e=0;e<i;e++)if(0===n[e])return[];const r=n.toString("utf8"),a=r.split("\n"),o=path.extname(e).toLowerCase();[".js",".ts",".jsx",".tsx",".mjs",".cjs",".py",".pyw",".go",".rs",".java",".kt",".cs",".php",".rb"].includes(o)&&(t.push(...this._runPatternScan(e,r,a,this.securityPatterns,"security")),t.push(...this._runPatternScan(e,r,a,this.qualityPatterns,"quality")),t.push(...this._runPatternScan(e,r,a,this.performancePatterns,"performance")),t.push(...this._runPatternScan(e,r,a,this.aiPatterns,"ai")),t.push(...this._analyzeComplexity(e,r,a)),t.push(...this._analyzeNesting(e,r,a)),t.push(...this._runAstScan(e,r,t)),t.push(...this._runTaintScan(e,r,t)),t.push(...this._runCfgScan(e,r,t))),".json"===o&&t.push(...this._scanJson(e,r));const c=this._applyThubanIgnore(a,t);for(const e of c)this.emit("issueFound",e);return c}catch(t){return[{file:e,category:"error",severity:"error",id:"SCAN001",message:"Failed to scan file: "+("ENOENT"===t.code?"File not found":"EACCES"===t.code?"Permission denied":"EISDIR"===t.code?"Path is a directory":"Scan error")}]}}_applyThubanIgnore(e,t){if(!t.length)return t;const s=new Map;for(let t=0;t<e.length;t++){const n=e[t].match(/thuban-ignore(?:\s+([A-Za-z0-9_,\s]+))?/);if(!n)continue;const i=n[1],r=[t+1,t+2];for(const e of r){if(!i||!i.trim()){s.set(e,!0);continue}const t=i.split(",").map(e=>e.trim()).filter(Boolean),n=s.get(e);if(!0===n)continue;const r=n instanceof Set?n:new Set;for(const e of t)r.add(e);s.set(e,r)}}return 0===s.size?t:t.filter(e=>{if(null==e.line)return!0;const t=s.get(e.line);return!t||!0!==t&&!t.has(e.id)})}getRules(){return{security:this.securityPatterns.map(e=>({id:e.id,name:e.name,severity:e.severity})),quality:this.qualityPatterns.map(e=>({id:e.id,name:e.name,severity:e.severity})),performance:this.performancePatterns.map(e=>({id:e.id,name:e.name,severity:e.severity})),ai:this.aiPatterns.map(e=>({id:e.id,name:e.name,severity:e.severity}))}}async _discoverFiles(e,t=[],s=0){if(s>100)return t;try{const n=fs.readdirSync(e,{withFileTypes:!0});for(const i of n){const n=path.join(e,i.name),r=path.relative(this.config.rootPath,n);if(!this._shouldIgnore(r))if(i.isDirectory())await this._discoverFiles(n,t,s+1);else if(i.isFile()){const e=path.extname(i.name).toLowerCase();[".js",".ts",".jsx",".tsx",".json",".py",".pyw",".go",".rs",".java",".kt",".cs",".php",".rb"].includes(e)&&t.push(n)}}}catch(e){}return t}_shouldIgnore(e){const t=e.replace(/\\/g,"/"),s=["node_modules",".git","dist",".vite"];for(const e of s)if(t.includes("/"+e+"/")||t.startsWith(e+"/")||t===e||t.endsWith("/"+e))return!0;for(const e of this.config.ignorePatterns){const s=e.replace(/\\/g,"/"),n=s.split("/")[0].replace(/\*/g,"");if(n&&(t===n||t.startsWith(n+"/")))return!0;let i=s.replace(/\./g,"\\.").replace(/\*\*/g,".*").replace(/\*/g,"[^/]*");if(new RegExp("^"+i).test(t))return!0}return!1}_runPatternScan(e,t,s,n,i){const r=[],a=path.basename(e),o=this.testFilePatterns.some(e=>e.test(a))||this.testFilePatterns.some(t=>t.test(e)&&!t.source.includes("\\.js$")),c=path.relative(this.config.rootPath,e).replace(/\\/g,"/");for(const a of n){if(a.skipInTests&&o)continue;if(a.skipInDirs&&a.skipInDirs.some(e=>c.startsWith(e+"/")||c.includes("/"+e+"/")))continue;if(a.context&&!a.lineContext&&!a.context.test(t))continue;let n=0;for(const o of s){n++;const s=o.match(a.pattern);if(s){if(a.skipIfSafe&&this.safePatterns.some(e=>e.test(o)))continue;if(a.check&&!a.check(s,t,e))continue;r.push({file:e,line:n,column:s.index,category:i,type:a.type||i,severity:a.severity,id:a.id,name:a.name,message:a.message,code:o.trim().substring(0,100)})}}}return r}_runAstScan(e,t,s){let n;try{n=astAnalyzer.analyze(e,t)}catch(e){return[]}if(!n||0===n.length)return[];const i=new Set(s.map(e=>`${e.line}:${e.type}`));return n.filter(e=>!i.has(`${e.line}:${e.type}`))}_buildExportTaintMap(e){try{return taintTracker.buildExportTaintMap(e)}catch(e){return new Map}}_runTaintScan(e,t,s){let n;try{const s=path.extname(e).toLowerCase();if(new Set([".js",".jsx",".ts",".tsx",".mjs",".cjs"]).has(s)){let s;if(this._exportTaintMap){const n=taintTracker._internal.parseJS(t);n&&(s=taintTracker.buildImportSeeds(e,n,this._exportTaintMap,fs.existsSync))}n=taintTracker.analyzeJSTaint(e,t,s&&s.size?s:void 0)}else n=taintTracker.analyze(e,t)}catch(e){return[]}return n&&0!==n.length?n:[]}_runCfgScan(e,t,s){let n;try{n=cfgAnalyzer.analyze(e,t)}catch(e){return[]}if(!n||0===n.length)return[];const i=new Set(s.map(e=>`${e.line}:${e.type}`));return n.filter(e=>!i.has(`${e.line}:${e.type}`))}_analyzeComplexity(e,t,s){const n=[],i=t.match(/function\s+\w+|=>\s*\{|async\s+\w+/g)||[],r=t.match(/\b(if|else|for|while|switch|case|catch|&&|\|\||\?)\b/g)||[],a=i.length>0?r.length/i.length:0;return a>this.config.complexityThreshold&&n.push({file:e,category:"quality",severity:"warning",id:"CMPLX001",name:"High Complexity",message:`Average cyclomatic complexity is ${a.toFixed(1)} (threshold: ${this.config.complexityThreshold})`}),s.length>500&&n.push({file:e,category:"quality",severity:"info",id:"CMPLX002",name:"Long File",message:`File has ${s.length} lines - consider splitting`}),n}_analyzeNesting(e,t,s){const n=[];let i=0,r=0,a=0,o=0;for(const e of s)o++,r+=(e.match(/\{/g)||[]).length-(e.match(/\}/g)||[]).length,r>i&&(i=r,a=o);return i>this.config.nestingThreshold&&n.push({file:e,line:a,category:"quality",severity:"warning",id:"NEST001",name:"Deep Nesting",message:`Max nesting depth is ${i} at line ${a} (threshold: ${this.config.nestingThreshold})`}),n}_scanJson(e,t){const s=[];try{JSON.parse(t)}catch(t){s.push({file:e,category:"quality",severity:"error",id:"JSON001",name:"Invalid JSON",message:`JSON parse error: ${t.message}`})}return/(password|secret|api[_-]?key|token)/i.test(t)&&s.push({file:e,category:"security",severity:"warning",id:"JSON002",name:"Sensitive Data in JSON",message:"Possible sensitive data in JSON file - verify not committed"}),s}_checkDependencyExists(e,t){const s=e[0].match(/require\(['"]([^'"]+)['"]\)/);if(!s)return!0;const n=s[1];if(n.startsWith("."))return!1;if(["fs","path","os","crypto","http","https","events","util","stream","child_process","url","querystring","buffer"].includes(n.split("/")[0]))return!1;try{let e=path.join(this.config.rootPath,"package.json");if(t.includes("server")){const t=path.join(this.config.rootPath,"server","package.json");fs.existsSync(t)&&(e=t)}const s=JSON.parse(fs.readFileSync(e,"utf8")),i={...s.dependencies||{},...s.devDependencies||{}};return!i[n.split("/")[0]]&&!i[n]}catch(e){return!1}}_checkImportUsage(e,t){const s=e[1];if(!s)return!1;const n=new RegExp("\\b"+s+"\\b","g");return(t.match(n)||[]).length<=1}_summarizeIssues(e){return{total:e.length,bySeverity:{critical:e.filter(e=>"critical"===e.severity).length,error:e.filter(e=>"error"===e.severity).length,warning:e.filter(e=>"warning"===e.severity).length,info:e.filter(e=>"info"===e.severity).length},byCategory:e.reduce((e,t)=>(e[t.category]=(e[t.category]||0)+1,e),{}),topFiles:this._getTopFiles(e,10)}}_getTopFiles(e,t){const s=e.reduce((e,t)=>(e[t.file]=(e[t.file]||0)+1,e),{});return Object.entries(s).sort((e,t)=>t[1]-e[1]).slice(0,t).map(([e,t])=>({file:path.relative(this.config.rootPath,e),count:t}))}}module.exports=CodeScanner;
1
+ const fs=require("fs"),path=require("path"),EventEmitter=require("events"),astAnalyzer=require("./ast-analyzer"),taintTracker=require("./taint-tracker"),cfgAnalyzer=require("./cfg-analyzer"),{getCodeScannerPatterns:getCodeScannerPatterns}=require("../crucible/rule-loader.js");class CodeScanner extends EventEmitter{constructor(e={}){super();const t=new Set(["rootPath","ignorePatterns","maxFileSize","totalScanTimeout","perFileTimeout","complexityThreshold","nestingThreshold"]),s={};for(const n of Object.keys(e))t.has(n)&&(s[n]=e[n]);this.config={rootPath:s.rootPath||process.cwd(),ignorePatterns:s.ignorePatterns||["node_modules/**",".git/**","dist/**",".orion/snapshots/**"],maxFileSize:s.maxFileSize||5242880,totalScanTimeout:s.totalScanTimeout||3e5,perFileTimeout:s.perFileTimeout||1e4,complexityThreshold:s.complexityThreshold||15,nestingThreshold:s.nestingThreshold||4},this._scanStartTime=null,this.safePatterns=[/keyEnv\s*[:=]/i,/envVar\s*[:=]/i,/process\.env\[/,/process\.env\./,/model\s*[:=]\s*['"][^'"]+['"]/i,/version\s*[:=]\s*['"][^'"]+['"]/i,/claude-|gpt-|gemini-|llama-|mistral-|deepseek-|grok-|sonar-/i,/endpoint\s*[:=]/i,/https?:\/\//,/_API_KEY['"]/,/API_KEY['"]\s*\)/,/errors?\.\w+\s*=/i,/Error\s*[:=]/i,/required|invalid|must be|cannot be/i],this.testFilePatterns=[/\.test\.(js|ts|jsx|tsx)$/,/\.spec\.(js|ts|jsx|tsx)$/,/__tests__\//,/\.mock\.(js|ts)$/,/test-.*\.js$/],this.securityPatterns=getCodeScannerPatterns(),this.qualityPatterns=[{id:"QUAL001",name:"Console.log in Production",pattern:/console\.(log|debug|info)\(/,severity:"info",message:"console.log found - consider using proper logging",skipInTests:!0,skipInDirs:["test","scripts","tools","dev"]},{id:"QUAL002",name:"TODO Comment",pattern:/\/\/\s*TODO|\/\*\s*TODO|\*\s*TODO|#\s*TODO/i,severity:"info",message:"TODO comment found - track in issue system"},{id:"QUAL003",name:"FIXME Comment",pattern:/\/\/\s*FIXME|\/\*\s*FIXME|\*\s*FIXME|#\s*FIXME/i,severity:"warning",message:"FIXME comment found - needs attention"},{id:"QUAL004",name:"Empty Catch Block",pattern:/catch\s*\([^)]*\)\s*\{\s*\}/,severity:"warning",message:"Empty catch block swallows errors"},{id:"QUAL005",name:"Magic Number",pattern:/(?<![0-9a-zA-Z_])(?!(?:1000|2000|3000|3001|4000|5000|8000|8080|9000|80|443|24|60|1024|2048|4096)\b)\d{5,}(?![0-9])/,severity:"info",message:"Magic number detected - consider using named constant",skipInTests:!0},{id:"QUAL006",name:"Callback Hell",pattern:/\)\s*=>\s*\{[^}]*\)\s*=>\s*\{[^}]*\)\s*=>\s*\{/,severity:"warning",message:"Deeply nested callbacks - consider async/await"}],this.performancePatterns=[{id:"PERF001",name:"Sync FS in Async",pattern:/fs\.(readFileSync|writeFileSync|readdirSync|statSync)/,severity:"warning",message:"Synchronous file operation - may block event loop"},{id:"PERF002",name:"Missing Await",pattern:/async\s+function[^{]*\{[^}]*(?<!await\s)new\s+Promise/,severity:"warning",message:"Promise created in async function without await"},{id:"PERF003",name:"Loop Database Query",pattern:/for\s*\([^)]*\)\s*\{[^}]*(query|find|select|update)/i,severity:"error",message:"Possible N+1 query pattern - consider batch operation"},{id:"PERF004",name:"Large Array in Memory",pattern:/new\s+Array\s*\(\s*\d{6,}\s*\)/,severity:"warning",message:"Large array allocation - consider streaming"}],this.aiPatterns=[{id:"AI001",name:"Potentially Hallucinated Import",pattern:/require\(['"](?!\.)[^'"]+['"]\)/,severity:"info",message:"External dependency - verify it exists in package.json",check:(e,t,s)=>this._checkDependencyExists(e,s)},{id:"AI002",name:"Unused Import",pattern:/const\s+(\w+)\s*=\s*require\(['"][^'"]+['"]\)/,severity:"info",message:"Import may be unused - verify usage",check:(e,t)=>this._checkImportUsage(e,t)},{id:"AI003",name:"Over-Engineered Pattern",pattern:/class\s+\w+Factory\s*\{|AbstractFactory|Singleton\.getInstance/,severity:"info",message:"Complex pattern detected - ensure justified by requirements"}]}async scanAll(){console.log("[CODE-SCANNER] Starting full codebase scan...");const e=Date.now();this._scanStartTime=e;const t={filesScanned:0,filesSkipped:0,issues:[],summary:{},duration:0};try{const s=await this._discoverFiles(this.config.rootPath);console.log(`[CODE-SCANNER] Found ${s.length} files to scan`),this._exportTaintMap=this._buildExportTaintMap(s);for(const n of s){if(Date.now()-e>this.config.totalScanTimeout){console.warn(`[CODE-SCANNER] Total scan timeout (${Math.round(this.config.totalScanTimeout/1e3)}s) reached — stopping scan`),t.issues.push({file:"scan",category:"info",severity:"warning",id:"TIMEOUT001",message:`Scan stopped after ${Math.round(this.config.totalScanTimeout/1e3)}s timeout — ${s.length-t.filesScanned} files not scanned`});break}const i=Date.now(),r=await this.scanFile(n),a=Date.now()-i;a>this.config.perFileTimeout&&console.warn(`[CODE-SCANNER] File ${n} took ${a}ms (>${this.config.perFileTimeout}ms) — results included but flagged`),t.filesScanned++,t.issues.push(...r)}return this._exportTaintMap=null,t.duration=Date.now()-e,t.summary=this._summarizeIssues(t.issues),console.log(`[CODE-SCANNER] Scan complete: ${t.filesScanned} files, ${t.issues.length} issues`),t}catch(e){return console.error("[CODE-SCANNER] Scan failed"),t.error="Scan failed",t}}async scanFiles(e){const t=Date.now();this._scanStartTime=t;const s={filesScanned:0,filesSkipped:0,issues:[],summary:{}};this._exportTaintMap=this._buildExportTaintMap(e);for(const n of e){if(Date.now()-t>this.config.totalScanTimeout){console.warn(`[CODE-SCANNER] Total scan timeout (${Math.round(this.config.totalScanTimeout/1e3)}s) reached — stopping scan`),s.issues.push({file:"scan",category:"info",severity:"warning",id:"TIMEOUT001",message:`Scan stopped after ${Math.round(this.config.totalScanTimeout/1e3)}s timeout — ${e.length-s.filesScanned} files not scanned`});break}const i=Date.now(),r=await this.scanFile(n),a=Date.now()-i;a>this.config.perFileTimeout&&(console.warn(`[CODE-SCANNER] WARNING: ${n} took ${a}ms (>${this.config.perFileTimeout}ms) — skipping slow file`),s.filesSkipped++,s.issues.push({file:n,category:"performance",severity:"warning",id:"TIMEOUT002",message:`File scan took ${a}ms — exceeded ${this.config.perFileTimeout}ms per-file limit`})),s.filesScanned++,s.issues.push(...r)}return this._exportTaintMap=null,s.summary=this._summarizeIssues(s.issues),s}async scanFile(e){const t=[];try{const s=fs.statSync(e);if(s.isSymbolicLink&&s.isSymbolicLink())return[];if(0===s.size)return[];if(s.size>this.config.maxFileSize)return[{file:e,category:"quality",severity:"info",id:"FILE001",message:`File too large (${(s.size/1048576).toFixed(1)}MB > ${(this.config.maxFileSize/1048576).toFixed(0)}MB limit) - skipped`}];let n;try{n=fs.readFileSync(e)}catch(e){return[]}const i=Math.min(n.length,512);for(let e=0;e<i;e++)if(0===n[e])return[];const r=n.toString("utf8").replace(/\r\n/g,"\n"),a=r.split("\n"),o=path.extname(e).toLowerCase();[".js",".ts",".jsx",".tsx",".mjs",".cjs",".vue",".svelte",".astro",".py",".pyw",".pyi",".pyx",".go",".rs",".java",".jsp",".kt",".kts",".cs",".cshtml",".razor",".vb",".fs",".fsx",".fsi",".php",".phtml",".blade.php",".rb",".erb",".rake",".gemspec",".swift",".m",".mm",".c",".h",".cpp",".cc",".cxx",".hpp",".hxx",".hh",".ino",".scala",".sc",".dart",".lua",".pl",".pm",".t",".r",".R",".Rmd",".groovy",".gvy",".gradle",".ex",".exs",".heex",".erl",".hrl",".hs",".lhs",".clj",".cljs",".cljc",".edn",".ml",".mli",".re",".rei",".nim",".nims",".zig",".cr",".jl",".d",".adb",".ads",".f90",".f95",".f03",".f08",".f",".for",".cob",".cbl",".cpy",".pas",".pp",".dpr",".cls",".trigger",".abap",".ps1",".psm1",".psd1",".sh",".bash",".zsh",".fish",".bat",".cmd",".sol",".move",".vy",".sql",".psql",".plsql",".graphql",".gql",".proto",".thrift",".html",".htm",".xhtml",".ejs",".hbs",".handlebars",".mustache",".pug",".jade",".haml",".slim",".twig",".njk",".liquid",".jinja",".jinja2",".css",".scss",".sass",".less",".styl",".xml",".xsl",".xslt",".xsd",".wsdl",".svg",".plist",".csproj",".sln",".props",".json",".jsonc",".json5",".yaml",".yml",".toml",".ini",".cfg",".conf",".env",".properties",".tf",".hcl",".dockerfile",".bicep",".pp",".md",".mdx",".rst",".adoc",".tex",".ipynb",".cmake",".as",".coffee",".ls",".elm",".purs",".v",".vv",".mojo",".tcl",".scm",".ss",".rkt",".lisp",".lsp",".cl",".pro",".P",".st",".vhd",".vhdl",".sv",".svh"].includes(o)&&(t.push(...this._runPatternScan(e,r,a,this.securityPatterns,"security")),t.push(...this._runPatternScan(e,r,a,this.qualityPatterns,"quality")),t.push(...this._runPatternScan(e,r,a,this.performancePatterns,"performance")),t.push(...this._runPatternScan(e,r,a,this.aiPatterns,"ai")),t.push(...this._analyzeComplexity(e,r,a)),t.push(...this._analyzeNesting(e,r,a)),t.push(...this._runAstScan(e,r,t)),t.push(...this._runTaintScan(e,r,t)),t.push(...this._runCfgScan(e,r,t))),".json"===o&&t.push(...this._scanJson(e,r));const c=this._applyThubanIgnore(a,t);for(const e of c)this.emit("issueFound",e);return c}catch(t){return[{file:e,category:"error",severity:"error",id:"SCAN001",message:"Failed to scan file: "+("ENOENT"===t.code?"File not found":"EACCES"===t.code?"Permission denied":"EISDIR"===t.code?"Path is a directory":"Scan error")}]}}_applyThubanIgnore(e,t){if(!t.length)return t;const s=new Map;for(let t=0;t<e.length;t++){const n=e[t].match(/thuban-ignore(?:\s+([A-Za-z0-9_,\s]+))?/);if(!n)continue;const i=n[1],r=[t+1,t+2];for(const e of r){if(!i||!i.trim()){s.set(e,!0);continue}const t=i.split(",").map(e=>e.trim()).filter(Boolean),n=s.get(e);if(!0===n)continue;const r=n instanceof Set?n:new Set;for(const e of t)r.add(e);s.set(e,r)}}return 0===s.size?t:t.filter(e=>{if(null==e.line)return!0;const t=s.get(e.line);return!t||!0!==t&&!t.has(e.id)})}getRules(){return{security:this.securityPatterns.map(e=>({id:e.id,name:e.name,severity:e.severity})),quality:this.qualityPatterns.map(e=>({id:e.id,name:e.name,severity:e.severity})),performance:this.performancePatterns.map(e=>({id:e.id,name:e.name,severity:e.severity})),ai:this.aiPatterns.map(e=>({id:e.id,name:e.name,severity:e.severity}))}}async _discoverFiles(e,t=[],s=0){if(s>100)return t;try{const n=fs.readdirSync(e,{withFileTypes:!0});for(const i of n){const n=path.join(e,i.name),r=path.relative(this.config.rootPath,n);if(!this._shouldIgnore(r))if(i.isDirectory())await this._discoverFiles(n,t,s+1);else if(i.isFile()){const e=path.extname(i.name).toLowerCase();[".js",".ts",".jsx",".tsx",".mjs",".cjs",".vue",".svelte",".astro",".json",".jsonc",".json5",".py",".pyw",".pyi",".pyx",".go",".rs",".java",".jsp",".kt",".kts",".cs",".cshtml",".razor",".vb",".fs",".fsx",".fsi",".php",".phtml",".rb",".erb",".rake",".gemspec",".swift",".m",".mm",".c",".h",".cpp",".cc",".cxx",".hpp",".hxx",".hh",".ino",".scala",".sc",".dart",".lua",".pl",".pm",".t",".r",".R",".Rmd",".groovy",".gvy",".gradle",".ex",".exs",".heex",".erl",".hrl",".hs",".lhs",".clj",".cljs",".cljc",".edn",".ml",".mli",".re",".rei",".nim",".nims",".zig",".cr",".jl",".d",".adb",".ads",".f90",".f95",".f03",".f08",".f",".for",".cob",".cbl",".cpy",".pas",".pp",".dpr",".cls",".trigger",".abap",".ps1",".psm1",".psd1",".sh",".bash",".zsh",".fish",".bat",".cmd",".sol",".move",".vy",".sql",".psql",".plsql",".graphql",".gql",".proto",".thrift",".html",".htm",".xhtml",".ejs",".hbs",".handlebars",".mustache",".pug",".jade",".haml",".slim",".twig",".njk",".liquid",".jinja",".jinja2",".css",".scss",".sass",".less",".styl",".xml",".xsl",".xslt",".xsd",".wsdl",".svg",".plist",".csproj",".sln",".props",".yaml",".yml",".toml",".ini",".cfg",".conf",".env",".properties",".tf",".hcl",".dockerfile",".bicep",".md",".mdx",".rst",".adoc",".tex",".ipynb",".cmake",".as",".coffee",".ls",".elm",".purs",".v",".vv",".mojo",".tcl",".scm",".ss",".rkt",".lisp",".lsp",".cl",".pro",".P",".st",".vhd",".vhdl",".sv",".svh"].includes(e)&&t.push(n)}}}catch(e){}return t}_shouldIgnore(e){const t=e.replace(/\\/g,"/"),s=["node_modules",".git","dist",".vite"];for(const e of s)if(t.includes("/"+e+"/")||t.startsWith(e+"/")||t===e||t.endsWith("/"+e))return!0;for(const e of this.config.ignorePatterns){const s=e.replace(/\\/g,"/"),n=s.split("/")[0].replace(/\*/g,"");if(n&&(t===n||t.startsWith(n+"/")))return!0;let i=s.replace(/\./g,"\\.").replace(/\*\*/g,".*").replace(/\*/g,"[^/]*");if(new RegExp("^"+i).test(t))return!0}return!1}_runPatternScan(e,t,s,n,i){const r=[],a=path.basename(e),o=this.testFilePatterns.some(e=>e.test(a))||this.testFilePatterns.some(t=>t.test(e)&&!t.source.includes("\\.js$")),c=path.relative(this.config.rootPath,e).replace(/\\/g,"/");for(const a of n){if(a.skipInTests&&o)continue;if(a.skipInDirs&&a.skipInDirs.some(e=>c.startsWith(e+"/")||c.includes("/"+e+"/")))continue;if(a.excludeExtensions){const t=path.extname(e).toLowerCase();if(a.excludeExtensions.includes(t))continue}if(a.context&&!a.lineContext&&!a.context.test(t))continue;let n=0;for(const o of s){n++;const s=o.match(a.pattern);if(s){if(a.skipIfSafe&&this.safePatterns.some(e=>e.test(o)))continue;if(a.check&&!a.check(s,t,e))continue;if(("sql_injection"===a.type||"xss"===a.type)&&/\b(?:parseInt|parseFloat|Number|int|float|str|strconv\.(?:Atoi|Itoa|ParseInt|ParseFloat|ParseBool|FormatInt)|\.to_i|\.to_f|\.to_s|CGI\.escapeHTML|ERB::Util\.html_escape|html\.EscapeString|url\.QueryEscape|encodeURIComponent|encodeURI|htmlspecialchars|htmlentities)\b/.test(o))continue;r.push({file:e,line:n,column:s.index,category:i,type:a.type||i,severity:a.severity,id:a.id,name:a.name,message:a.message,code:o.trim().substring(0,100),...a.owasp&&{owasp:a.owasp},...a.cwe&&{cwe:a.cwe}})}}}return r}_runAstScan(e,t,s){let n;try{n=astAnalyzer.analyze(e,t)}catch(e){return[]}if(!n||0===n.length)return[];const i=new Set(s.map(e=>`${e.line}:${e.type}`));return n.filter(e=>!i.has(`${e.line}:${e.type}`))}_buildExportTaintMap(e){try{return taintTracker.buildExportTaintMap(e)}catch(e){return new Map}}_runTaintScan(e,t,s){let n;try{const s=path.extname(e).toLowerCase();if(new Set([".js",".jsx",".ts",".tsx",".mjs",".cjs"]).has(s)){let s;if(this._exportTaintMap){const n=taintTracker._internal.parseJS(t);n&&(s=taintTracker.buildImportSeeds(e,n,this._exportTaintMap,fs.existsSync))}n=taintTracker.analyzeJSTaint(e,t,s&&s.size?s:void 0)}else n=taintTracker.analyze(e,t)}catch(e){return[]}return n&&0!==n.length?n:[]}_runCfgScan(e,t,s){let n;try{n=cfgAnalyzer.analyze(e,t)}catch(e){return[]}if(!n||0===n.length)return[];const i=new Set(s.map(e=>`${e.line}:${e.type}`));return n.filter(e=>!i.has(`${e.line}:${e.type}`))}_analyzeComplexity(e,t,s){const n=[],i=t.match(/(?:^|\n)\s*(?:export\s+)?(?:async\s+)?function\s+\w+|=>\s*\{|(?:^|\n)\s*func\s+(?:\([^)]*\)\s+)?\w+\s*\(|(?:^|\n)\s*(?:pub\s+)?(?:async\s+)?fn\s+\w+|(?:^|\n)\s*def\s+(?:self\.)?\w+|(?:^|\n)\s*(?:public|private|protected|internal)?\s*(?:fun|suspend\s+fun)\s+\w+/gm)||[],r=t.match(/\b(if|else|for|while|switch|case|catch|&&|\|\||\?)\b/g)||[],a=i.length>0?r.length/i.length:0;return a>this.config.complexityThreshold&&n.push({file:e,category:"quality",severity:"warning",id:"CMPLX001",name:"High Complexity",message:`Average cyclomatic complexity is ${a.toFixed(1)} (threshold: ${this.config.complexityThreshold})`}),s.length>500&&n.push({file:e,category:"quality",severity:"info",id:"CMPLX002",name:"Long File",message:`File has ${s.length} lines - consider splitting`}),n}_analyzeNesting(e,t,s){const n=[],i=path.extname(e).toLowerCase();let r=0,a=0,o=0;if(".rb"===i){let e=0;for(const t of s){e++;const s=t.replace(/#.*$/,"").trim();if(!s)continue;const n=(s.match(/\b(?:def|do|if|unless|while|until|for|begin|case|class|module)\b/g)||[]).length;a+=n-(/\b(?:if|unless|while|until)\b/.test(s)&&!s.match(/^\s*(?:if|unless|while|until|elsif)\b/)&&1===n?1:0)-(s.match(/\bend\b/g)||[]).length,a<0&&(a=0),a>r&&(r=a,o=e)}}else if(".py"===i||".pyw"===i){let e=0;for(const n of s){if(e++,!n.trim()||n.trim().startsWith("#"))continue;const s=n.match(/^(\s*)/)[1].length,i=Math.floor(s/(t.includes("\t")?1:4));i>r&&(r=i,o=e)}}else{let e=0;for(const t of s)e++,a+=(t.match(/\{/g)||[]).length-(t.match(/\}/g)||[]).length,a>r&&(r=a,o=e)}return r>this.config.nestingThreshold&&n.push({file:e,line:o,category:"quality",severity:"warning",id:"NEST001",name:"Deep Nesting",message:`Max nesting depth is ${r} at line ${o} (threshold: ${this.config.nestingThreshold})`}),n}_scanJson(e,t){const s=[];try{JSON.parse(t)}catch(t){s.push({file:e,category:"quality",severity:"error",id:"JSON001",name:"Invalid JSON",message:`JSON parse error: ${t.message}`})}return/(password|secret|api[_-]?key|token)/i.test(t)&&s.push({file:e,category:"security",severity:"warning",id:"JSON002",name:"Sensitive Data in JSON",message:"Possible sensitive data in JSON file - verify not committed"}),s}_checkDependencyExists(e,t){const s=e[0].match(/require\(['"]([^'"]+)['"]\)/);if(!s)return!0;const n=s[1];if(n.startsWith("."))return!1;const i=n.startsWith("node:")?n.slice(5).split("/")[0]:n.split("/")[0];if(["assert","async_hooks","buffer","child_process","cluster","console","constants","crypto","dgram","diagnostics_channel","dns","domain","events","fs","http","http2","https","inspector","module","net","os","path","perf_hooks","process","punycode","querystring","readline","repl","stream","string_decoder","sys","timers","tls","trace_events","tty","url","util","v8","vm","wasi","worker_threads","zlib"].includes(i))return!1;const r=n.startsWith("@")?n.split("/").slice(0,2).join("/"):i;let a=path.dirname(t);const o=this.config.rootPath;for(;a.length>=o.length;){try{const e=path.join(a,"package.json");if(fs.existsSync(e)){const t=JSON.parse(fs.readFileSync(e,"utf8")),s={...t.dependencies||{},...t.devDependencies||{},...t.peerDependencies||{}};if(s[r]||s[n])return!1}}catch(e){}const e=path.dirname(a);if(e===a)break;a=e}return!0}_checkImportUsage(e,t){const s=e[1];if(!s)return!1;const n=new RegExp("\\b"+s+"\\b","g");return(t.match(n)||[]).length<=1}_summarizeIssues(e){return{total:e.length,bySeverity:{critical:e.filter(e=>"critical"===e.severity).length,error:e.filter(e=>"error"===e.severity).length,warning:e.filter(e=>"warning"===e.severity).length,info:e.filter(e=>"info"===e.severity).length},byCategory:e.reduce((e,t)=>(e[t.category]=(e[t.category]||0)+1,e),{}),topFiles:this._getTopFiles(e,10)}}_getTopFiles(e,t){const s=e.reduce((e,t)=>(e[t.file]=(e[t.file]||0)+1,e),{});return Object.entries(s).sort((e,t)=>t[1]-e[1]).slice(0,t).map(([e,t])=>({file:path.relative(this.config.rootPath,e),count:t}))}}module.exports=CodeScanner;
@@ -1 +1 @@
1
- const fs=require("fs"),path=require("path"),crypto=require("crypto");class CodebasePassport{constructor(e={}){this.rootPath=e.rootPath||process.cwd()}generate(e,t={}){const s=Date.now(),r=[],i={},n={},o=[],c=[];let a=0;const l={},u=new Set;for(const t of e){let s;try{if(fs.statSync(t).size>1048576)continue;s=fs.readFileSync(t,"utf-8")}catch(e){continue}const d=path.relative(this.rootPath,t),p=s.split("\n");a+=p.length;const h=path.extname(t);l[h]=(l[h]||0)+p.length,this._detectFrameworks(s,u);const m=this._extractDependencies(s,d),f=this._extractExports(s,d);i[d]=m,f.length>0&&(n[d]=f),this._isEntryPoint(d,s)&&o.push(d);const g=e.filter(e=>{try{const t=fs.readFileSync(e,"utf-8");return t.includes(d.replace(/\\/g,"/"))||t.includes(d.replace(/\//g,"\\"))}catch(e){return!1}}).length;g>5&&c.push({file:d,importedBy:g,risk:g>10?"critical":"high",note:`Changing this file affects ${g} other files. Proceed with caution.`});const y=this._detectPurpose(d,s);r.push({path:d,lines:p.length,purpose:y,imports:m.length,exports:f.length})}const d=this._findCriticalPaths(i),p=this._detectArchitecture(r);return{_version:"1.0.0",_generated:(new Date).toISOString(),_generator:"thuban",project:{name:path.basename(this.rootPath),rootPath:this.rootPath,totalFiles:e.length,totalLines:a,languages:Object.entries(l).sort((e,t)=>t[1]-e[1]).map(([e,t])=>({extension:e,lines:t,percentage:Math.round(t/a*100)})),frameworks:[...u],architecture:p,entryPoints:o},files:r.sort((e,t)=>t.imports-e.imports).slice(0,200),sacred:c.sort((e,t)=>t.importedBy-e.importedBy),dependencies:{totalChains:Object.keys(i).length,criticalPaths:d.slice(0,20),circularRisks:this._findCircularRisks(i)},onboarding:{estimatedReadTimeMinutes:Math.ceil(a/200),startHere:o.slice(0,5),keyFiles:c.slice(0,10).map(e=>e.file),doNotTouch:c.filter(e=>"critical"===e.risk).map(e=>e.file),quickSummary:this._generateQuickSummary(r,u,p)},generatedIn:Date.now()-s+"ms"}}save(e,t){const s=t||path.join(this.rootPath,".thuban-passport.json");return fs.writeFileSync(s,JSON.stringify(e,null,2),"utf-8"),s}_extractDependencies(e,t){const s=[],r=[/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,/import\s+.*?from\s+['"]([^'"]+)['"]/g,/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g];for(const t of r){let r;for(;null!==(r=t.exec(e));)s.push(r[1])}return[...new Set(s)]}_extractExports(e,t){const s=[];e.match(/module\.exports\s*=\s*(\{[\s\S]*?\}|[a-zA-Z_$]\w*)/)&&s.push("module.exports");const r=e.match(/exports\.([a-zA-Z_$]\w*)\s*=/g);r&&r.forEach(e=>s.push(e.replace(/\s*=\s*$/,"")));const i=e.match(/export\s+(?:default\s+)?(?:const|let|var|function|class)\s+([a-zA-Z_$]\w*)/g);return i&&i.forEach(e=>s.push(e)),s}_isEntryPoint(e,t){return!!["index.","main.","app.","server.","cli.","bin/"].some(t=>e.includes(t))||!!t.includes("#!/usr/bin/env")||!(!t.includes("createServer")&&!t.includes(".listen("))}_detectPurpose(e,t){const s=e.toLowerCase();return/route/i.test(s)?"routing":/middleware/i.test(s)?"middleware":/model/i.test(s)?"data model":/controller/i.test(s)?"controller":/service/i.test(s)?"service":/util|helper|lib/i.test(s)?"utility":/config/i.test(s)?"configuration":/test|spec/i.test(s)?"test":/component/i.test(s)?"UI component":/hook/i.test(s)?"React hook":/store|redux|state/i.test(s)?"state management":/api/i.test(s)?"API layer":/db|database|migration/i.test(s)?"database":/auth/i.test(s)?"authentication":/cli|command/i.test(s)?"CLI":t.includes("app.get(")||t.includes("router.")?"routing":t.includes("mongoose.model")||t.includes("Schema")?"data model":t.includes("React")||t.includes("jsx")?"React component":"application logic"}_detectFrameworks(e,t){(e.includes("require('express'")||e.includes("from 'express'"))&&t.add("Express"),(e.includes("require('react'")||e.includes("from 'react'"))&&t.add("React"),(e.includes("require('next'")||e.includes("from 'next'"))&&t.add("Next.js"),(e.includes("require('mongoose'")||e.includes("from 'mongoose'"))&&t.add("Mongoose"),(e.includes("require('fastify'")||e.includes("from 'fastify'"))&&t.add("Fastify"),(e.includes("require('vue'")||e.includes("from 'vue'"))&&t.add("Vue"),(e.includes("require('stripe'")||e.includes("from 'stripe'"))&&t.add("Stripe"),e.includes("@angular")&&t.add("Angular")}_detectArchitecture(e){const t=e.map(e=>e.path.toLowerCase());return t.some(e=>e.includes("controller"))&&t.some(e=>e.includes("model"))?"MVC":t.some(e=>e.includes("service"))&&t.some(e=>e.includes("repository"))?"Service-Repository":t.some(e=>e.includes("component"))&&t.some(e=>e.includes("store"))?"Component-Store (SPA)":t.some(e=>e.includes("pages/"))&&t.some(e=>e.includes("api/"))?"Next.js (Pages Router)":t.some(e=>e.includes("app/"))&&t.some(e=>e.includes("route"))?"App Router":t.some(e=>e.includes("packages/"))?"Monorepo":"Flat"}_findCriticalPaths(e){return Object.entries(e).filter(([,e])=>e.length>3).map(([e,t])=>({file:e,dependsOn:t.length,topDeps:t.slice(0,5)})).sort((e,t)=>t.dependsOn-e.dependsOn).slice(0,20)}_findCircularRisks(e){const t=[];for(const[s,r]of Object.entries(e))for(const i of r){const r=this._resolveRelative(s,i);r&&e[r]&&e[r].some(e=>this._resolveRelative(r,e)===s)&&t.push({fileA:s,fileB:r,risk:"circular dependency"})}return t.slice(0,10)}_resolveRelative(e,t){if(t.startsWith(".")){const s=path.dirname(e);let r=path.join(s,t).replace(/\\/g,"/");return r.match(/\.\w+$/)||(r+=".js"),r}return null}_generateQuickSummary(e,t,s){return`${e.length}-file ${s} project using ${[...t].join(", ")||"vanilla"}. Start with entry points listed above.`}}module.exports=CodebasePassport;
1
+ const fs=require("fs"),path=require("path"),crypto=require("crypto");class CodebasePassport{constructor(e={}){this.rootPath=e.rootPath||process.cwd()}generate(e,t={}){const s=Date.now(),r=[],i={},n={},o=[],c=[];let a=0;const l={},u=new Set;for(const t of e){let s;try{if(fs.statSync(t).size>1048576)continue;s=fs.readFileSync(t,"utf-8").replace(/\r\n/g,"\n")}catch(e){continue}const d=path.relative(this.rootPath,t),p=s.split("\n");a+=p.length;const h=path.extname(t);l[h]=(l[h]||0)+p.length,this._detectFrameworks(s,u);const m=this._extractDependencies(s,d),f=this._extractExports(s,d);i[d]=m,f.length>0&&(n[d]=f),this._isEntryPoint(d,s)&&o.push(d);const g=e.filter(e=>{try{const t=fs.readFileSync(e,"utf-8").replace(/\r\n/g,"\n");return t.includes(d.replace(/\\/g,"/"))||t.includes(d.replace(/\//g,"\\"))}catch(e){return!1}}).length;g>5&&c.push({file:d,importedBy:g,risk:g>10?"critical":"high",note:`Changing this file affects ${g} other files. Proceed with caution.`});const y=this._detectPurpose(d,s);r.push({path:d,lines:p.length,purpose:y,imports:m.length,exports:f.length})}const d=this._findCriticalPaths(i),p=this._detectArchitecture(r);return{_version:"1.0.0",_generated:(new Date).toISOString(),_generator:"thuban",project:{name:path.basename(this.rootPath),rootPath:this.rootPath,totalFiles:e.length,totalLines:a,languages:Object.entries(l).sort((e,t)=>t[1]-e[1]).map(([e,t])=>({extension:e,lines:t,percentage:Math.round(t/a*100)})),frameworks:[...u],architecture:p,entryPoints:o},files:r.sort((e,t)=>t.imports-e.imports).slice(0,200),sacred:c.sort((e,t)=>t.importedBy-e.importedBy),dependencies:{totalChains:Object.keys(i).length,criticalPaths:d.slice(0,20),circularRisks:this._findCircularRisks(i)},onboarding:{estimatedReadTimeMinutes:Math.ceil(a/200),startHere:o.slice(0,5),keyFiles:c.slice(0,10).map(e=>e.file),doNotTouch:c.filter(e=>"critical"===e.risk).map(e=>e.file),quickSummary:this._generateQuickSummary(r,u,p)},generatedIn:Date.now()-s+"ms"}}save(e,t){const s=t||path.join(this.rootPath,".thuban-passport.json");return fs.writeFileSync(s,JSON.stringify(e,null,2),"utf-8"),s}_extractDependencies(e,t){const s=[],r=[/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,/import\s+.*?from\s+['"]([^'"]+)['"]/g,/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g];for(const t of r){let r;for(;null!==(r=t.exec(e));)s.push(r[1])}return[...new Set(s)]}_extractExports(e,t){const s=[];e.match(/module\.exports\s*=\s*(\{[\s\S]*?\}|[a-zA-Z_$]\w*)/)&&s.push("module.exports");const r=e.match(/exports\.([a-zA-Z_$]\w*)\s*=/g);r&&r.forEach(e=>s.push(e.replace(/\s*=\s*$/,"")));const i=e.match(/export\s+(?:default\s+)?(?:const|let|var|function|class)\s+([a-zA-Z_$]\w*)/g);return i&&i.forEach(e=>s.push(e)),s}_isEntryPoint(e,t){return!!["index.","main.","app.","server.","cli.","bin/"].some(t=>e.includes(t))||!!t.includes("#!/usr/bin/env")||!(!t.includes("createServer")&&!t.includes(".listen("))}_detectPurpose(e,t){const s=e.toLowerCase();return/route/i.test(s)?"routing":/middleware/i.test(s)?"middleware":/model/i.test(s)?"data model":/controller/i.test(s)?"controller":/service/i.test(s)?"service":/util|helper|lib/i.test(s)?"utility":/config/i.test(s)?"configuration":/test|spec/i.test(s)?"test":/component/i.test(s)?"UI component":/hook/i.test(s)?"React hook":/store|redux|state/i.test(s)?"state management":/api/i.test(s)?"API layer":/db|database|migration/i.test(s)?"database":/auth/i.test(s)?"authentication":/cli|command/i.test(s)?"CLI":t.includes("app.get(")||t.includes("router.")?"routing":t.includes("mongoose.model")||t.includes("Schema")?"data model":t.includes("React")||t.includes("jsx")?"React component":"application logic"}_detectFrameworks(e,t){(e.includes("require('express'")||e.includes("from 'express'"))&&t.add("Express"),(e.includes("require('react'")||e.includes("from 'react'"))&&t.add("React"),(e.includes("require('next'")||e.includes("from 'next'"))&&t.add("Next.js"),(e.includes("require('mongoose'")||e.includes("from 'mongoose'"))&&t.add("Mongoose"),(e.includes("require('fastify'")||e.includes("from 'fastify'"))&&t.add("Fastify"),(e.includes("require('vue'")||e.includes("from 'vue'"))&&t.add("Vue"),(e.includes("require('stripe'")||e.includes("from 'stripe'"))&&t.add("Stripe"),e.includes("@angular")&&t.add("Angular")}_detectArchitecture(e){const t=e.map(e=>e.path.toLowerCase());return t.some(e=>e.includes("controller"))&&t.some(e=>e.includes("model"))?"MVC":t.some(e=>e.includes("service"))&&t.some(e=>e.includes("repository"))?"Service-Repository":t.some(e=>e.includes("component"))&&t.some(e=>e.includes("store"))?"Component-Store (SPA)":t.some(e=>e.includes("pages/"))&&t.some(e=>e.includes("api/"))?"Next.js (Pages Router)":t.some(e=>e.includes("app/"))&&t.some(e=>e.includes("route"))?"App Router":t.some(e=>e.includes("packages/"))?"Monorepo":"Flat"}_findCriticalPaths(e){return Object.entries(e).filter(([,e])=>e.length>3).map(([e,t])=>({file:e,dependsOn:t.length,topDeps:t.slice(0,5)})).sort((e,t)=>t.dependsOn-e.dependsOn).slice(0,20)}_findCircularRisks(e){const t=[];for(const[s,r]of Object.entries(e))for(const i of r){const r=this._resolveRelative(s,i);r&&e[r]&&e[r].some(e=>this._resolveRelative(r,e)===s)&&t.push({fileA:s,fileB:r,risk:"circular dependency"})}return t.slice(0,10)}_resolveRelative(e,t){if(t.startsWith(".")){const s=path.dirname(e);let r=path.join(s,t).replace(/\\/g,"/");return r.match(/\.\w+$/)||(r+=".js"),r}return null}_generateQuickSummary(e,t,s){return`${e.length}-file ${s} project using ${[...t].join(", ")||"vanilla"}. Start with entry points listed above.`}}module.exports=CodebasePassport;
@@ -1 +1 @@
1
- const fs=require("fs"),path=require("path"),crypto=require("crypto");class CopyPasteDriftDetector{constructor(t={}){this.rootPath=t.rootPath||process.cwd(),this.minLines=t.minLines||5,this.similarityThreshold=t.similarity||.7}scan(t){const e=[];for(const n of t){let t;try{if(fs.statSync(n).size>524288)continue;t=fs.readFileSync(n,"utf-8")}catch(t){continue}const s=path.relative(this.rootPath,n);if(this._isSkippable(s))continue;const i=this._extractFunctions(t,s);e.push(...i)}const n=this._findClusters(e),s=n.reduce((t,e)=>t+e.members.reduce((t,e)=>t+e.lineCount,0)-e.members[0].lineCount,0);return{clusters:n,totalClusters:n.length,totalDuplicateInstances:n.reduce((t,e)=>t+e.members.length,0),totalDuplicateLines:s,recommendation:n.length>0?`Extract ${n.length} shared utilities to eliminate ${s} duplicate lines.`:"No copy-paste drift detected."}}_extractFunctions(t,e){const n=t.split("\n"),s=[];for(let t=0;t<n.length;t++){const i=this._detectFunction(n[t]);if(!i)continue;const r=this._findFunctionEnd(n,t),a=n.slice(t,r+1).join("\n"),o=r-t+1;if(o<this.minLines)continue;const l=this._normalize(a),c=crypto.createHash("md5").update(l).digest("hex"),h=this._tokenize(l);s.push({file:e,name:i.name,line:t+1,lineCount:o,hash:c,tokens:h,normalized:l}),t=r}return s}_findClusters(t){const e=new Set,n=[];for(let s=0;s<t.length;s++){if(e.has(s))continue;const i=[t[s]];e.add(s);for(let n=s+1;n<t.length;n++){if(e.has(n))continue;if(t[s].hash===t[n].hash){i.push(t[n]),e.add(n);continue}const r=this._tokenSimilarity(t[s].tokens,t[n].tokens);r>=this.similarityThreshold&&(i.push({...t[n],similarity:Math.round(100*r)}),e.add(n))}if(i.length>=2){const t=this._findDiffs(i);n.push({pattern:i[0].name,members:i.map(t=>({file:t.file,name:t.name,line:t.line,lineCount:t.lineCount,similarity:t.similarity||100})),totalInstances:i.length,differences:t,severity:i.length>=4?"high":i.length>=3?"medium":"low",recommendation:`Extract to shared utility. ${i.length} functions → 1.`})}}return n.sort((t,e)=>e.totalInstances-t.totalInstances)}_normalize(t){const e=[],n=/(?:const|let|var|function)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;let s;for(;null!==(s=n.exec(t));)e.includes(s[1])||e.push(s[1]);let i=t.replace(/\/\/.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").replace(/'[^']*'/g,"STR").replace(/"[^"]*"/g,"STR").replace(/`[^`]*`/g,"STR").replace(/\b\d+\.?\d*\b/g,"NUM");return e.forEach((t,e)=>{const n=new RegExp(`\\b${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\b`,"g");i=i.replace(n,`VAR_${e}`)}),i.replace(/\s+/g," ").trim()}_tokenize(t){return t.split(/[\s{}()\[\];,=+\-*/<>!&|?:.]+/).filter(t=>t.length>0)}_tokenSimilarity(t,e){if(0===t.length&&0===e.length)return 1;if(0===t.length||0===e.length)return 0;const n=new Set(t),s=new Set(e),i=[...n].filter(t=>s.has(t)).length,r=new Set([...t,...e]).size;return.7*(r>0?i/r:0)+Math.min(t.length,e.length)/Math.max(t.length,e.length)*.3}_findDiffs(t){if(t.length<2)return[];const e=[],n=t[0].normalized;for(let s=1;s<t.length;s++){const i=t[s].normalized;if(n!==i){const r=new Set(this._tokenize(n)),a=new Set(this._tokenize(i)),o=[...a].filter(t=>!r.has(t)),l=[...r].filter(t=>!a.has(t));(o.length>0||l.length>0)&&e.push({file:t[s].file,addedTokens:o.slice(0,5),removedTokens:l.slice(0,5)})}}return e}_detectFunction(t){const e=t.trim();let n=e.match(/^(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);return n?{name:n[1]}:(n=e.match(/^(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:async\s+)?(?:function|\()/),n?{name:n[1]}:(n=e.match(/^(?:private\s+|public\s+|internal\s+|protected\s+)?(?:suspend\s+)?fun\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*[(<]/),n?{name:n[1]}:(n=e.match(/^(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*[(<]/),n?{name:n[1]}:(n=e.match(/^(?:(?:public|private|protected|static)\s+)*function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/),n?{name:n[1]}:(n=e.match(/^def\s+(?:self\.)?([a-zA-Z_][a-zA-Z0-9_?!]*)\s*[(\s]?/),n?{name:n[1]}:null)))))}_findFunctionEnd(t,e){let n=0,s=!1;for(let i=e;i<t.length&&i<e+200;i++){for(const e of t[i])"{"===e&&(n++,s=!0),"}"===e&&n--;if(s&&n<=0)return i}return Math.min(e+20,t.length-1)}_isSkippable(t){return[/\.test\./i,/\.spec\./i,/test[\/\\]/i,/node_modules/i,/\.min\./i,/\.d\.ts$/,/\.config\./i].some(e=>e.test(t))}}module.exports=CopyPasteDriftDetector;
1
+ const fs=require("fs"),path=require("path"),crypto=require("crypto");class CopyPasteDriftDetector{constructor(t={}){this.rootPath=t.rootPath||process.cwd(),this.minLines=t.minLines||5,this.similarityThreshold=t.similarity||.7}scan(t){const e=[];for(const n of t){let t;try{if(fs.statSync(n).size>524288)continue;t=fs.readFileSync(n,"utf-8").replace(/\r\n/g,"\n")}catch(t){continue}const s=path.relative(this.rootPath,n);if(this._isSkippable(s))continue;const i=this._extractFunctions(t,s);e.push(...i)}const n=this._findClusters(e),s=n.reduce((t,e)=>t+e.members.reduce((t,e)=>t+e.lineCount,0)-e.members[0].lineCount,0);return{clusters:n,totalClusters:n.length,totalDuplicateInstances:n.reduce((t,e)=>t+e.members.length,0),totalDuplicateLines:s,recommendation:n.length>0?`Extract ${n.length} shared utilities to eliminate ${s} duplicate lines.`:"No copy-paste drift detected."}}_extractFunctions(t,e){const n=t.split("\n"),s=[];for(let t=0;t<n.length;t++){const i=this._detectFunction(n[t]);if(!i)continue;const a=this._findFunctionEnd(n,t),r=n.slice(t,a+1).join("\n"),l=a-t+1;if(l<this.minLines)continue;const o=this._normalize(r),c=crypto.createHash("md5").update(o).digest("hex"),h=this._tokenize(o);s.push({file:e,name:i.name,line:t+1,lineCount:l,hash:c,tokens:h,normalized:o}),t=a}return s}_findClusters(t){const e=new Set,n=[];for(let s=0;s<t.length;s++){if(e.has(s))continue;const i=[t[s]];e.add(s);for(let n=s+1;n<t.length;n++){if(e.has(n))continue;if(t[s].hash===t[n].hash){i.push(t[n]),e.add(n);continue}const a=this._tokenSimilarity(t[s].tokens,t[n].tokens);a>=this.similarityThreshold&&(i.push({...t[n],similarity:Math.round(100*a)}),e.add(n))}if(i.length>=2){const t=this._findDiffs(i);n.push({pattern:i[0].name,members:i.map(t=>({file:t.file,name:t.name,line:t.line,lineCount:t.lineCount,similarity:t.similarity||100})),totalInstances:i.length,differences:t,severity:i.length>=4?"high":i.length>=3?"medium":"low",recommendation:`Extract to shared utility. ${i.length} functions → 1.`})}}return n.sort((t,e)=>e.totalInstances-t.totalInstances)}_normalize(t){const e=[],n=[/(?:const|let|var|function)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g,/(?:([a-zA-Z_][a-zA-Z0-9_]*)\s*:=|var\s+([a-zA-Z_][a-zA-Z0-9_]*)|\bfunc\s+(?:\([^)]*\)\s+)?([a-zA-Z_][a-zA-Z0-9_]*))/g,/(?:let\s+(?:mut\s+)?([a-zA-Z_][a-zA-Z0-9_]*)|\bfn\s+([a-zA-Z_][a-zA-Z0-9_]*))/g,/(?:^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*=(?!=)|\bdef\s+(?:self\.)?([a-zA-Z_][a-zA-Z0-9_?!]*))/gm,/(?:^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*=(?!=)|\bdef\s+([a-zA-Z_][a-zA-Z0-9_]*))/gm];let s;for(const i of n)for(;null!==(s=i.exec(t));){const t=s[1]||s[2]||s[3];t&&!e.includes(t)&&t.length>1&&e.push(t)}let i=t.replace(/\/\/.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").replace(/#.*$/gm,"").replace(/'[^']*'/g,"STR").replace(/"[^"]*"/g,"STR").replace(/`[^`]*`/g,"STR").replace(/\b\d+\.?\d*\b/g,"NUM");return e.forEach((t,e)=>{const n=new RegExp(`\\b${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\b`,"g");i=i.replace(n,`VAR_${e}`)}),i.replace(/\s+/g," ").trim()}_tokenize(t){return t.split(/[\s{}()\[\];,=+\-*/<>!&|?:.]+/).filter(t=>t.length>0)}_tokenSimilarity(t,e){if(0===t.length&&0===e.length)return 1;if(0===t.length||0===e.length)return 0;const n=new Set(t),s=new Set(e),i=[...n].filter(t=>s.has(t)).length,a=new Set([...t,...e]).size;return.7*(a>0?i/a:0)+Math.min(t.length,e.length)/Math.max(t.length,e.length)*.3}_findDiffs(t){if(t.length<2)return[];const e=[],n=t[0].normalized;for(let s=1;s<t.length;s++){const i=t[s].normalized;if(n!==i){const a=new Set(this._tokenize(n)),r=new Set(this._tokenize(i)),l=[...r].filter(t=>!a.has(t)),o=[...a].filter(t=>!r.has(t));(l.length>0||o.length>0)&&e.push({file:t[s].file,addedTokens:l.slice(0,5),removedTokens:o.slice(0,5)})}}return e}_detectFunction(t){const e=t.trim();let n=e.match(/^(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);return n?{name:n[1]}:(n=e.match(/^(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:async\s+)?(?:function|\()/),n?{name:n[1]}:(n=e.match(/^func\s+(?:\([^)]*\)\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/),n?{name:n[1]}:(n=e.match(/^(?:private\s+|public\s+|internal\s+|protected\s+)?(?:suspend\s+)?fun\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*[(<]/),n?{name:n[1]}:(n=e.match(/^(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*[(<]/),n?{name:n[1]}:(n=e.match(/^(?:(?:public|private|protected|static)\s+)*function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/),n?{name:n[1]}:(n=e.match(/^def\s+(?:self\.)?([a-zA-Z_][a-zA-Z0-9_?!]*)\s*[(\s]?/),n?{name:n[1]}:null))))))}_findFunctionEnd(t,e){const n=(t[e]||"").trim();if(/^def\s/.test(n)){let n=1;for(let s=e+1;s<t.length&&s<e+200;s++){const e=t[s].trim();if(/^(?:def|class|module|begin|case)\b/.test(e)&&n++,/^(?:if|unless|while|until|for)\b/.test(e)&&!/\bthen\b/.test(e)&&n++,/\bdo\s*(\|[^|]*\|)?\s*$/.test(e)&&n++,/^end\b/.test(e)&&n--,n<=0)return s}return Math.min(e+20,t.length-1)}let s=0,i=!1;for(let n=e;n<t.length&&n<e+200;n++){for(const e of t[n])"{"===e&&(s++,i=!0),"}"===e&&s--;if(i&&s<=0)return n}return Math.min(e+20,t.length-1)}_isSkippable(t){return[/\.test\./i,/\.spec\./i,/test[\/\\]/i,/node_modules/i,/\.min\./i,/\.d\.ts$/,/\.config\./i].some(e=>e.test(t))}}module.exports=CopyPasteDriftDetector;
@@ -1 +1 @@
1
- const fs=require("fs"),path=require("path"),EventEmitter=require("events");class DependencyGraph extends EventEmitter{constructor(e={}){super(),this.config={rootPath:e.rootPath||process.cwd(),ignorePatterns:e.ignorePatterns||["node_modules",".git","dist",".vite",".orion/snapshots"],extensions:e.extensions||[".js",".ts",".jsx",".tsx",".php",".rb"],...e},this.graph=new Map,this.resolveCache=new Map,this.stats={filesAnalyzed:0,totalImports:0,unresolvedImports:0,circularDependencies:[]}}get circularDeps(){return this.stats.circularDependencies}async build(){Date.now();this.graph.clear(),this.resolveCache.clear(),this.stats={filesAnalyzed:0,totalImports:0,unresolvedImports:0,circularDependencies:[]};const e=await this._discoverFiles(this.config.rootPath);for(const t of e)await this._analyzeFile(t);return this._buildReverseDependencies(),this._detectCircularDependencies(),Date.now(),this.getStats()}getDependencies(e){const t=this._normalizePath(e),s=this.graph.get(t);return s?s.imports:[]}getDependents(e){const t=this._normalizePath(e),s=this.graph.get(t);return s?s.importedBy:[]}getFullDependencyChain(e,t=new Set){const s=this._normalizePath(e);if(t.has(s))return[];t.add(s);const i=this.getDependencies(s),r=[...i];for(const e of i)e.resolved&&r.push(...this.getFullDependencyChain(e.resolved,t));return r}getImpactAnalysis(e){const t=this._normalizePath(e),s={file:t,relativePath:path.relative(this.config.rootPath,t),directDependents:[],indirectDependents:[],totalImpact:0,riskLevel:"low",affectedModules:new Set};s.directDependents=this.getDependents(t).map(e=>({file:e,relativePath:path.relative(this.config.rootPath,e)}));const i=new Set([t]),r=[...s.directDependents.map(e=>e.file)];for(;r.length>0;){const e=r.shift();if(i.has(e))continue;i.add(e);const t=this.getDependents(e);for(const o of t)i.has(o)||(s.indirectDependents.push({file:o,relativePath:path.relative(this.config.rootPath,o),via:path.relative(this.config.rootPath,e)}),r.push(o))}s.totalImpact=s.directDependents.length+s.indirectDependents.length,0===s.totalImpact?s.riskLevel="none":s.totalImpact<=3?s.riskLevel="low":s.totalImpact<=10?s.riskLevel="medium":s.totalImpact<=25?s.riskLevel="high":s.riskLevel="critical";for(const e of[...s.directDependents,...s.indirectDependents]){const t=e.relativePath.split(path.sep);t.length>0&&s.affectedModules.add(t[0])}return s.affectedModules=Array.from(s.affectedModules),s}getMostCriticalFiles(e=20){const t=[];for(const[e,s]of this.graph)t.push({file:e,relativePath:path.relative(this.config.rootPath,e),dependentCount:s.importedBy.length,dependencyCount:s.imports.length});return t.sort((e,t)=>t.dependentCount-e.dependentCount).slice(0,e)}getOrphanFiles(){const e=[];for(const[t,s]of this.graph)0===s.imports.length&&0===s.importedBy.length&&e.push({file:t,relativePath:path.relative(this.config.rootPath,t)});return e}getModuleSummary(){const e=new Map;for(const[t,s]of this.graph){const i=path.relative(this.config.rootPath,t),r=i.split(path.sep)[0];e.has(r)||e.set(r,{name:r,files:[],internalDeps:0,externalDeps:0,dependedOnBy:new Set});const o=e.get(r);o.files.push(i);for(const e of s.imports)e.resolved&&(path.relative(this.config.rootPath,e.resolved).split(path.sep)[0]===r?o.internalDeps++:o.externalDeps++);for(const e of s.importedBy){const t=path.relative(this.config.rootPath,e).split(path.sep)[0];t!==r&&o.dependedOnBy.add(t)}}const t=[];for(const[s,i]of e)t.push({name:s,fileCount:i.files.length,internalDeps:i.internalDeps,externalDeps:i.externalDeps,dependedOnByCount:i.dependedOnBy.size,dependedOnBy:Array.from(i.dependedOnBy)});return t.sort((e,t)=>t.fileCount-e.fileCount)}getStats(){return{...this.stats,totalFiles:this.graph.size,circularCount:this.stats.circularDependencies.length}}toJSON(){const e={generated:(new Date).toISOString(),rootPath:this.config.rootPath,stats:this.getStats(),files:{}};for(const[t,s]of this.graph){const i=path.relative(this.config.rootPath,t);e.files[i]={imports:s.imports.map(e=>({module:e.module,resolved:e.resolved?path.relative(this.config.rootPath,e.resolved):null,specifiers:e.specifiers})),importedBy:s.importedBy.map(e=>path.relative(this.config.rootPath,e))}}return e}async _discoverFiles(e,t=[],s=0){if(s>100)return t;try{const i=fs.readdirSync(e,{withFileTypes:!0});for(const r of i){const i=path.join(e,r.name);if(!this._shouldIgnore(r.name))if(r.isDirectory())await this._discoverFiles(i,t,s+1);else if(r.isFile()){const e=path.extname(r.name).toLowerCase();this.config.extensions.includes(e)&&t.push(i)}}}catch(e){}return t}_shouldIgnore(e){return this.config.ignorePatterns.some(t=>e===t||e.startsWith(t))}async _analyzeFile(e){try{const t=fs.readFileSync(e,"utf8"),s=this._extractImports(t,e);this.graph.set(e,{imports:s,importedBy:[]}),this.stats.filesAnalyzed++,this.stats.totalImports+=s.length,this.stats.unresolvedImports+=s.filter(e=>!e.resolved).length}catch(e){}}_extractImports(e,t){const s=[],i=path.dirname(t),r=/(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;let o;for(;null!==(o=r.exec(e));){const e=o[1]?o[1].split(",").map(e=>e.trim().split(":")[0].trim()).filter(Boolean):[o[2]],t=o[3];s.push({module:t,specifiers:e,resolved:this._resolveModule(t,i),type:"require"})}const n=/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;for(;null!==(o=n.exec(e));){const e=o[1];s.some(t=>t.module===e)||s.push({module:e,specifiers:[],resolved:this._resolveModule(e,i),type:"require"})}const a=/import\s+(?:(?:\{([^}]+)\}|(\w+)|\*\s+as\s+(\w+))(?:\s*,\s*)?)+\s+from\s+['"]([^'"]+)['"]/g;for(;null!==(o=a.exec(e));){const e=[];o[1]&&e.push(...o[1].split(",").map(e=>e.trim().split(" as ")[0].trim()).filter(Boolean)),o[2]&&e.push(o[2]),o[3]&&e.push(o[3]);const t=o[4];s.push({module:t,specifiers:e,resolved:this._resolveModule(t,i),type:"import"})}return s}_resolveModule(e,t){const s=`${t}:${e}`;if(this.resolveCache.has(s))return this.resolveCache.get(s);let i=null;if(!e.startsWith(".")&&!e.startsWith("/"))return this.resolveCache.set(s,null),null;const r=path.resolve(t,e),o=["",".js",".ts",".jsx",".tsx",".php",".rb","/index.js","/index.ts"];for(const e of o){const t=r+e;if(fs.existsSync(t)&&fs.statSync(t).isFile()){i=t;break}}return this.resolveCache.set(s,i),i}_buildReverseDependencies(){for(const[e,t]of this.graph)for(const s of t.imports)if(s.resolved&&this.graph.has(s.resolved)){const t=this.graph.get(s.resolved);t.importedBy.includes(e)||t.importedBy.push(e)}}_detectCircularDependencies(){const e=new Set,t=new Set,s=(i,r=[])=>{if(t.has(i)){const e=r.indexOf(i),t=r.slice(e).map(e=>path.relative(this.config.rootPath,e));t.push(path.relative(this.config.rootPath,i));const s=t.sort().join(" -> ");return void(this.stats.circularDependencies.some(e=>e.sort().join(" -> ")===s)||this.stats.circularDependencies.push(t))}if(e.has(i))return;e.add(i),t.add(i);const o=this.graph.get(i);if(o)for(const e of o.imports)e.resolved&&s(e.resolved,[...r,i]);t.delete(i)};for(const e of this.graph.keys())s(e)}_normalizePath(e){return path.isAbsolute(e)?e:path.resolve(this.config.rootPath,e)}}module.exports=DependencyGraph;
1
+ const fs=require("fs"),path=require("path"),EventEmitter=require("events");class DependencyGraph extends EventEmitter{constructor(e={}){super(),this.config={rootPath:e.rootPath||process.cwd(),ignorePatterns:e.ignorePatterns||["node_modules",".git","dist",".vite",".orion/snapshots"],extensions:e.extensions||[".js",".ts",".jsx",".tsx",".php",".rb"],...e},this.graph=new Map,this.resolveCache=new Map,this.stats={filesAnalyzed:0,totalImports:0,unresolvedImports:0,circularDependencies:[]}}get circularDeps(){return this.stats.circularDependencies}async build(){Date.now();this.graph.clear(),this.resolveCache.clear(),this.stats={filesAnalyzed:0,totalImports:0,unresolvedImports:0,circularDependencies:[]};const e=await this._discoverFiles(this.config.rootPath);for(const t of e)await this._analyzeFile(t);return this._buildReverseDependencies(),this._detectCircularDependencies(),Date.now(),this.getStats()}getDependencies(e){const t=this._normalizePath(e),s=this.graph.get(t);return s?s.imports:[]}getDependents(e){const t=this._normalizePath(e),s=this.graph.get(t);return s?s.importedBy:[]}getFullDependencyChain(e,t=new Set){const s=this._normalizePath(e);if(t.has(s))return[];t.add(s);const i=this.getDependencies(s),r=[...i];for(const e of i)e.resolved&&r.push(...this.getFullDependencyChain(e.resolved,t));return r}getImpactAnalysis(e){const t=this._normalizePath(e),s={file:t,relativePath:path.relative(this.config.rootPath,t),directDependents:[],indirectDependents:[],totalImpact:0,riskLevel:"low",affectedModules:new Set};s.directDependents=this.getDependents(t).map(e=>({file:e,relativePath:path.relative(this.config.rootPath,e)}));const i=new Set([t]),r=[...s.directDependents.map(e=>e.file)];for(;r.length>0;){const e=r.shift();if(i.has(e))continue;i.add(e);const t=this.getDependents(e);for(const o of t)i.has(o)||(s.indirectDependents.push({file:o,relativePath:path.relative(this.config.rootPath,o),via:path.relative(this.config.rootPath,e)}),r.push(o))}s.totalImpact=s.directDependents.length+s.indirectDependents.length,0===s.totalImpact?s.riskLevel="none":s.totalImpact<=3?s.riskLevel="low":s.totalImpact<=10?s.riskLevel="medium":s.totalImpact<=25?s.riskLevel="high":s.riskLevel="critical";for(const e of[...s.directDependents,...s.indirectDependents]){const t=e.relativePath.split(path.sep);t.length>0&&s.affectedModules.add(t[0])}return s.affectedModules=Array.from(s.affectedModules),s}getMostCriticalFiles(e=20){const t=[];for(const[e,s]of this.graph)t.push({file:e,relativePath:path.relative(this.config.rootPath,e),dependentCount:s.importedBy.length,dependencyCount:s.imports.length});return t.sort((e,t)=>t.dependentCount-e.dependentCount).slice(0,e)}getOrphanFiles(){const e=[];for(const[t,s]of this.graph)0===s.imports.length&&0===s.importedBy.length&&e.push({file:t,relativePath:path.relative(this.config.rootPath,t)});return e}getModuleSummary(){const e=new Map;for(const[t,s]of this.graph){const i=path.relative(this.config.rootPath,t),r=i.split(path.sep)[0];e.has(r)||e.set(r,{name:r,files:[],internalDeps:0,externalDeps:0,dependedOnBy:new Set});const o=e.get(r);o.files.push(i);for(const e of s.imports)e.resolved&&(path.relative(this.config.rootPath,e.resolved).split(path.sep)[0]===r?o.internalDeps++:o.externalDeps++);for(const e of s.importedBy){const t=path.relative(this.config.rootPath,e).split(path.sep)[0];t!==r&&o.dependedOnBy.add(t)}}const t=[];for(const[s,i]of e)t.push({name:s,fileCount:i.files.length,internalDeps:i.internalDeps,externalDeps:i.externalDeps,dependedOnByCount:i.dependedOnBy.size,dependedOnBy:Array.from(i.dependedOnBy)});return t.sort((e,t)=>t.fileCount-e.fileCount)}getStats(){return{...this.stats,totalFiles:this.graph.size,circularCount:this.stats.circularDependencies.length}}toJSON(){const e={generated:(new Date).toISOString(),rootPath:this.config.rootPath,stats:this.getStats(),files:{}};for(const[t,s]of this.graph){const i=path.relative(this.config.rootPath,t);e.files[i]={imports:s.imports.map(e=>({module:e.module,resolved:e.resolved?path.relative(this.config.rootPath,e.resolved):null,specifiers:e.specifiers})),importedBy:s.importedBy.map(e=>path.relative(this.config.rootPath,e))}}return e}async _discoverFiles(e,t=[],s=0){if(s>100)return t;try{const i=fs.readdirSync(e,{withFileTypes:!0});for(const r of i){const i=path.join(e,r.name);if(!this._shouldIgnore(r.name))if(r.isDirectory())await this._discoverFiles(i,t,s+1);else if(r.isFile()){const e=path.extname(r.name).toLowerCase();this.config.extensions.includes(e)&&t.push(i)}}}catch(e){}return t}_shouldIgnore(e){return this.config.ignorePatterns.some(t=>e===t||e.startsWith(t))}async _analyzeFile(e){try{const t=fs.readFileSync(e,"utf8"),s=this._extractImports(t,e);this.graph.set(e,{imports:s,importedBy:[]}),this.stats.filesAnalyzed++,this.stats.totalImports+=s.length,this.stats.unresolvedImports+=s.filter(e=>!e.resolved).length}catch(e){}}_extractImports(e,t){const s=[],i=path.dirname(t),r=/(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;let o;for(;null!==(o=r.exec(e));){const e=o[1]?o[1].split(",").map(e=>e.trim().split(":")[0].trim()).filter(Boolean):[o[2]],t=o[3];s.push({module:t,specifiers:e,resolved:this._resolveModule(t,i),type:"require"})}const n=/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;for(;null!==(o=n.exec(e));){const e=o[1];s.some(t=>t.module===e)||s.push({module:e,specifiers:[],resolved:this._resolveModule(e,i),type:"require"})}const a=/import\s+(?:(?:\{([^}]+)\}|(\w+)|\*\s+as\s+(\w+))(?:\s*,\s*)?)+\s+from\s+['"]([^'"]+)['"]/g;for(;null!==(o=a.exec(e));){const e=[];o[1]&&e.push(...o[1].split(",").map(e=>e.trim().split(" as ")[0].trim()).filter(Boolean)),o[2]&&e.push(o[2]),o[3]&&e.push(o[3]);const t=o[4];s.push({module:t,specifiers:e,resolved:this._resolveModule(t,i),type:"import"})}return s}_resolveModule(e,t){const s=`${t}:${e}`;if(this.resolveCache.has(s))return this.resolveCache.get(s);let i=null;if(!e.startsWith(".")&&!path.isAbsolute(e))return this.resolveCache.set(s,null),null;const r=path.resolve(t,e),o=["",".js",".ts",".jsx",".tsx",".php",".rb",path.sep+"index.js",path.sep+"index.ts"];for(const e of o){const t=r+e;if(fs.existsSync(t)&&fs.statSync(t).isFile()){i=t;break}}return this.resolveCache.set(s,i),i}_buildReverseDependencies(){for(const[e,t]of this.graph)for(const s of t.imports)if(s.resolved&&this.graph.has(s.resolved)){const t=this.graph.get(s.resolved);t.importedBy.includes(e)||t.importedBy.push(e)}}_detectCircularDependencies(){const e=new Set,t=new Set,s=(i,r=[])=>{if(t.has(i)){const e=r.indexOf(i),t=r.slice(e).map(e=>path.relative(this.config.rootPath,e));t.push(path.relative(this.config.rootPath,i));const s=t.sort().join(" -> ");return void(this.stats.circularDependencies.some(e=>e.sort().join(" -> ")===s)||this.stats.circularDependencies.push(t))}if(e.has(i))return;e.add(i),t.add(i);const o=this.graph.get(i);if(o)for(const e of o.imports)e.resolved&&s(e.resolved,[...r,i]);t.delete(i)};for(const e of this.graph.keys())s(e)}_normalizePath(e){return path.isAbsolute(e)?e:path.resolve(this.config.rootPath,e)}}module.exports=DependencyGraph;
@@ -1 +1 @@
1
- const fs=require("fs"),path=require("path"),EventEmitter=require("events");class DriftDetector extends EventEmitter{constructor(e={}){super(),this.config={rootPath:e.rootPath||process.cwd(),widgetPattern:e.widgetPattern||/\/\*\*[\s\S]*?@(purpose|module|layer|imports-from|exports-to|depends-on|critical-for)[\s\S]*?\*\//,ignorePatterns:e.ignorePatterns||["node_modules/**",".git/**","dist/**",".orion/snapshots/**"],...e},this.analysisCache=new Map}async detectAll(){const e=Date.now(),t={filesAnalyzed:0,filesWithWidgets:0,filesWithoutWidgets:0,issues:[],moduleMap:{},duration:0};try{const s=await this._discoverFiles(this.config.rootPath);for(const e of s){const s=await this.analyzeFile(e);t.filesAnalyzed++,s.hasWidget?t.filesWithWidgets++:(t.filesWithoutWidgets++,t.issues.push({file:e,category:"drift",severity:"info",id:"DRIFT001",name:"Missing Widget",message:"File has no widget declaration - add @purpose, @module, @layer etc."})),t.issues.push(...s.issues),s.module&&(t.moduleMap[s.module]||(t.moduleMap[s.module]=[]),t.moduleMap[s.module].push(e))}return t.issues.push(...this._analyzeCrossFile(t.moduleMap)),t.duration=Date.now()-e,t}catch(e){return console.error("[DRIFT-DETECTOR] Detection failed"),t.error="Detection failed",t}}async analyzeFile(e){const t={file:e,hasWidget:!1,widget:null,actualExports:[],actualImports:[],module:null,issues:[]};try{const s=fs.readFileSync(e,"utf8");return t.widget=this._parseWidget(s),t.hasWidget=!!t.widget,t.actualExports=this._extractExports(s),t.actualImports=this._extractImports(s),t.widget&&(t.module=t.widget.module,t.issues.push(...this._compareWidgetToCode(e,t.widget,{exports:t.actualExports,imports:t.actualImports}))),t.drifts=t.issues,this.analysisCache.set(e,t),t}catch(s){const i="ENOENT"===s.code?"File not found":"EACCES"===s.code?"Permission denied":"Analysis error";return t.issues.push({file:e,category:"drift",severity:"error",id:"DRIFT000",message:`Failed to analyze file: ${i}`}),t.drifts=t.issues,t}}async checkFileDrift(e){const t=await this.analyzeFile(e);return{hasDrift:t.issues.length>0,issues:t.issues,widget:t.widget}}async suggestWidget(e){try{const t=fs.readFileSync(e,"utf8"),s=this._extractExports(t),i=this._extractImports(t),o=path.relative(this.config.rootPath,e),r=o.split(path.sep)[0];let n="application";return o.includes("routes")||o.includes("api")?n="presentation":o.includes("core")||o.includes("engine")?n="infrastructure":(o.includes("model")||o.includes("domain"))&&(n="domain"),{purpose:s.length>0?`Provides ${s.join(", ")}`:"Unknown purpose - needs description",module:r,layer:n,importsFrom:i.map(e=>`${e.module} - ${e.specifiers.join(", ")}`),exportsTo:"To be determined",dataFlow:"To be documented",sideEffects:t.includes("fs.")?"File system operations":"None documented",criticalFor:"To be documented"}}catch(e){return{error:e.message}}}async _discoverFiles(e,t=[],s=0){if(s>100)return t;try{const i=fs.readdirSync(e,{withFileTypes:!0});for(const o of i){const i=path.join(e,o.name),r=path.relative(this.config.rootPath,i);this._shouldIgnore(r)||(o.isDirectory()?await this._discoverFiles(i,t,s+1):o.isFile()&&o.name.endsWith(".js")&&t.push(i))}}catch(e){}return t}_shouldIgnore(e){const t=e.replace(/\\/g,"/"),s=["node_modules",".git","dist",".vite"];for(const e of s)if(t.includes("/"+e+"/")||t.startsWith(e+"/")||t===e||t.endsWith("/"+e))return!0;for(const e of this.config.ignorePatterns){const s=e.replace(/\\/g,"/"),i=s.split("/")[0].replace(/\*/g,"");if(i&&(t===i||t.startsWith(i+"/")))return!0;let o=s.replace(/\./g,"\\.").replace(/\*\*/g,".*").replace(/\*/g,"[^/]*");if(new RegExp("^"+o).test(t))return!0}return!1}_parseWidget(e){const t=e.match(/\/\*\*[\s\S]*?\*\//);if(t){const e=t[0],s=this._extractWidgetFields(e,t=>{const s=e.match(new RegExp(`@${t}\\s+(.+?)(?=\\n\\s*\\*\\s*@|\\n\\s*\\*\\/|$)`,"s"));return s?s[1].trim().replace(/\n\s*\*\s*/g," ").trim():null});if(s)return s}const s=e.match(/"{3}[\s\S]*?@(?:purpose|module|layer)[\s\S]*?"{3}|'{3}[\s\S]*?@(?:purpose|module|layer)[\s\S]*?'{3}/);if(s){const e=s[0],t=this._extractWidgetFields(e,t=>{const s=e.match(new RegExp(`@${t}\\s+(.+?)(?=\\n\\s*@|$)`,"s"));return s?s[1].trim():null});if(t)return t}const i=e.match(/(?:^|\n)((?:\s*#[^\n]*\n)+)/);if(i){const e=i[0];if(/@(?:purpose|module|layer)/.test(e)){const t=this._extractWidgetFields(e,t=>{const s=e.match(new RegExp(`@${t}\\s+(.+?)(?=\\n\\s*#\\s*@|\\n[^#]|$)`));return s?s[1].trim():null});if(t)return t}}const o=e.match(/(?:^|\n)((?:\s*\/\/[^\n]*\n)+)/);if(o){const e=o[0];if(/@(?:purpose|module|layer)/.test(e)){const t=this._extractWidgetFields(e,t=>{const s=e.match(new RegExp(`@${t}\\s+(.+?)(?=\\n\\s*//\\s*@|\\n[^/]|$)`));return s?s[1].trim():null});if(t)return t}}return null}_extractWidgetFields(e,t){const s=["purpose","module","layer","imports-from","exports-to","data-flow","side-effects","depends-on-env","critical-for","known-issues","last-audit","last-modified"],i={};for(const e of s){const s=t(e);s&&(i[e.replace(/-/g,"_")]=s)}return Object.keys(i).length>0?i:null}_extractExports(e){const t=[],s=e.match(/module\.exports\s*=\s*\{([^}]+)\}/);if(s){const e=s[1].match(/\w+/g)||[];t.push(...e)}const i=e.match(/module\.exports\s*=\s*(class\s+)?(\w+)/);i&&i[2]&&t.push(i[2]);const o=e.match(/exports\.(\w+)\s*=/g)||[];for(const e of o){const s=e.match(/exports\.(\w+)/);s&&t.push(s[1])}const r=e.match(/^(?:def|class)\s+([a-zA-Z_]\w*)/gm)||[];for(const e of r){const s=e.match(/^(?:def|class)\s+([a-zA-Z_]\w*)/);s&&!s[1].startsWith("_")&&t.push(s[1])}const n=e.match(/^func\s+([A-Z][a-zA-Z0-9]*)/gm)||[];for(const e of n){const s=e.match(/^func\s+([A-Z][a-zA-Z0-9]*)/);s&&t.push(s[1])}const a=e.match(/(?:public\s+function|class)\s+([a-zA-Z_]\w*)/g)||[];for(const e of a){const s=e.match(/(?:public\s+function|class)\s+([a-zA-Z_]\w*)/);s&&t.push(s[1])}return[...new Set(t)]}_extractImports(e){const t=[],s=e.matchAll(/(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\(['"]([^'"]+)['"]\)/g);for(const e of s){const s=e[1]?e[1].split(",").map(e=>e.trim().split(":")[0].trim()):[e[2]];t.push({module:e[3],specifiers:s.filter(Boolean)})}const i=e.matchAll(/^(?:import\s+(\S+)|from\s+(\S+)\s+import)/gm);for(const e of i)t.push({module:e[1]||e[2],specifiers:[]});const o=e.matchAll(/"([^"]+)"/g);for(const e of o)!e[1].includes("/")&&e[1].includes(" ")||t.push({module:e[1],specifiers:[]});return t}_compareWidgetToCode(e,t,s){const i=[];if(t.exports_to){const o=t.exports_to.split(",").map(e=>e.trim().split(" ")[0]).filter(e=>!s.exports.some(t=>t.toLowerCase().includes(e.toLowerCase())));o.length>0&&i.push({file:e,category:"drift",severity:"warning",id:"DRIFT002",name:"Missing Declared Export",message:`Widget declares exports not found in code: ${o.join(", ")}`})}if(t.imports_from){const o=t.imports_from.split(",").map(e=>e.trim().split(" ")[0]),r=s.imports.map(e=>e.module),n=o.filter(e=>!r.some(t=>t.includes(e)||e.includes(t.split("/").pop())));n.length>0&&i.push({file:e,category:"drift",severity:"warning",id:"DRIFT003",name:"Missing Declared Import",message:`Widget declares imports not found in code: ${n.join(", ")}`})}return!/fs\.(write|append|unlink|mkdir|rmdir)/i.test(fs.readFileSync(e,"utf8"))||t.side_effects&&"None"!==t.side_effects||i.push({file:e,category:"drift",severity:"warning",id:"DRIFT004",name:"Undeclared Side Effects",message:"File has file system operations but widget declares no side effects"}),i}_analyzeCrossFile(e){const t=[];for(const[s,i]of Object.entries(e))1===i.length&&t.push({file:i[0],category:"drift",severity:"info",id:"DRIFT005",name:"Orphaned Module",message:`Module "${s}" only has one file - verify module organization`});return t}}module.exports=DriftDetector;
1
+ const fs=require("fs"),path=require("path"),EventEmitter=require("events");class DriftDetector extends EventEmitter{constructor(e={}){super(),this.config={rootPath:e.rootPath||process.cwd(),widgetPattern:e.widgetPattern||/\/\*\*[\s\S]*?@(purpose|module|layer|imports-from|exports-to|depends-on|critical-for)[\s\S]*?\*\//,ignorePatterns:e.ignorePatterns||["node_modules/**",".git/**","dist/**",".orion/snapshots/**"],...e},this.analysisCache=new Map}async detectAll(){const e=Date.now(),t={filesAnalyzed:0,filesWithWidgets:0,filesWithoutWidgets:0,issues:[],moduleMap:{},duration:0};try{const s=await this._discoverFiles(this.config.rootPath);for(const e of s){const s=await this.analyzeFile(e);t.filesAnalyzed++,s.hasWidget?t.filesWithWidgets++:(t.filesWithoutWidgets++,t.issues.push({file:e,category:"drift",severity:"info",id:"DRIFT001",name:"Missing Widget",message:"File has no widget declaration - add @purpose, @module, @layer etc."})),t.issues.push(...s.issues),s.module&&(t.moduleMap[s.module]||(t.moduleMap[s.module]=[]),t.moduleMap[s.module].push(e))}return t.issues.push(...this._analyzeCrossFile(t.moduleMap)),t.duration=Date.now()-e,t}catch(e){return console.error("[DRIFT-DETECTOR] Detection failed"),t.error="Detection failed",t}}async analyzeFile(e){const t={file:e,hasWidget:!1,widget:null,actualExports:[],actualImports:[],module:null,issues:[]};try{const s=fs.readFileSync(e,"utf8").replace(/\r\n/g,"\n");return t.widget=this._parseWidget(s),t.hasWidget=!!t.widget,t.actualExports=this._extractExports(s),t.actualImports=this._extractImports(s),t.widget&&(t.module=t.widget.module,t.issues.push(...this._compareWidgetToCode(e,t.widget,{exports:t.actualExports,imports:t.actualImports}))),t.drifts=t.issues,this.analysisCache.set(e,t),t}catch(s){const o="ENOENT"===s.code?"File not found":"EACCES"===s.code?"Permission denied":"Analysis error";return t.issues.push({file:e,category:"drift",severity:"error",id:"DRIFT000",message:`Failed to analyze file: ${o}`}),t.drifts=t.issues,t}}async checkFileDrift(e){const t=await this.analyzeFile(e);return{hasDrift:t.issues.length>0,issues:t.issues,widget:t.widget}}async suggestWidget(e){try{const t=fs.readFileSync(e,"utf8").replace(/\r\n/g,"\n"),s=this._extractExports(t),o=this._extractImports(t),r=path.relative(this.config.rootPath,e),i=r.split(path.sep)[0];let n="application";return r.includes("routes")||r.includes("api")?n="presentation":r.includes("core")||r.includes("engine")?n="infrastructure":(r.includes("model")||r.includes("domain"))&&(n="domain"),{purpose:s.length>0?`Provides ${s.join(", ")}`:"Unknown purpose - needs description",module:i,layer:n,importsFrom:o.map(e=>`${e.module} - ${e.specifiers.join(", ")}`),exportsTo:"To be determined",dataFlow:"To be documented",sideEffects:t.includes("fs.")?"File system operations":"None documented",criticalFor:"To be documented"}}catch(e){return{error:e.message}}}async _discoverFiles(e,t=[],s=0){if(s>100)return t;try{const o=fs.readdirSync(e,{withFileTypes:!0});for(const r of o){const o=path.join(e,r.name),i=path.relative(this.config.rootPath,o);this._shouldIgnore(i)||(r.isDirectory()?await this._discoverFiles(o,t,s+1):r.isFile()&&r.name.endsWith(".js")&&t.push(o))}}catch(e){}return t}_shouldIgnore(e){const t=e.replace(/\\/g,"/"),s=["node_modules",".git","dist",".vite"];for(const e of s)if(t.includes("/"+e+"/")||t.startsWith(e+"/")||t===e||t.endsWith("/"+e))return!0;for(const e of this.config.ignorePatterns){const s=e.replace(/\\/g,"/"),o=s.split("/")[0].replace(/\*/g,"");if(o&&(t===o||t.startsWith(o+"/")))return!0;let r=s.replace(/\./g,"\\.").replace(/\*\*/g,".*").replace(/\*/g,"[^/]*");if(new RegExp("^"+r).test(t))return!0}return!1}_parseWidget(e){const t=e.match(/\/\*\*[\s\S]*?\*\//);if(t){const e=t[0],s=this._extractWidgetFields(e,t=>{const s=e.match(new RegExp(`@${t}\\s+(.+?)(?=\\r?\\n\\s*\\*\\s*@|\\r?\\n\\s*\\*\\/|$)`,"s"));return s?s[1].trim().replace(/\n\s*\*\s*/g," ").trim():null});if(s)return s}const s=e.match(/"{3}[\s\S]*?@(?:purpose|module|layer)[\s\S]*?"{3}|'{3}[\s\S]*?@(?:purpose|module|layer)[\s\S]*?'{3}/);if(s){const e=s[0],t=this._extractWidgetFields(e,t=>{const s=e.match(new RegExp(`@${t}\\s+(.+?)(?=\\r?\\n\\s*@|$)`,"s"));return s?s[1].trim():null});if(t)return t}const o=e.match(/(?:^|\r?\n)((?:\s*#[^\r\n]*\r?\n)+)/);if(o){const e=o[0];if(/@(?:purpose|module|layer)/.test(e)){const t=this._extractWidgetFields(e,t=>{const s=e.match(new RegExp(`@${t}\\s+(.+?)(?=\\r?\\n\\s*#\\s*@|\\r?\\n[^#]|$)`));return s?s[1].trim():null});if(t)return t}}const r=e.match(/(?:^|\r?\n)((?:\s*\/\/[^\r\n]*\r?\n)+)/);if(r){const e=r[0];if(/@(?:purpose|module|layer)/.test(e)){const t=this._extractWidgetFields(e,t=>{const s=e.match(new RegExp(`@${t}\\s+(.+?)(?=\\r?\\n\\s*//\\s*@|\\r?\\n[^/]|$)`));return s?s[1].trim():null});if(t)return t}}return null}_extractWidgetFields(e,t){const s=["purpose","module","layer","imports-from","exports-to","data-flow","side-effects","depends-on-env","critical-for","known-issues","last-audit","last-modified"],o={};for(const e of s){const s=t(e);s&&(o[e.replace(/-/g,"_")]=s)}return Object.keys(o).length>0?o:null}_extractExports(e){const t=[],s=e.search(/module\.exports\s*=\s*\{/);if(s>=0){const o=e.indexOf("{",s);if(o>=0){let s=0,r=e.length;for(let t=o;t<e.length;t++)if("{"===e[t]&&s++,"}"===e[t]&&(s--,0===s)){r=t;break}const i=e.substring(o+1,r);let n=0,c="";for(const e of i)if("{"!==e&&"["!==e&&"("!==e||n++,"}"!==e&&"]"!==e&&")"!==e||n--,0!==n||","!==e&&"\n"!==e)c+=e;else{const e=c.trim().match(/^(\w+)/);e&&t.push(e[1]),c=""}if(c.trim()){const e=c.trim().match(/^(\w+)/);e&&t.push(e[1])}}}const o=e.match(/module\.exports\s*=\s*(class\s+)?(\w+)/);o&&o[2]&&t.push(o[2]);const r=e.match(/exports\.(\w+)\s*=/g)||[];for(const e of r){const s=e.match(/exports\.(\w+)/);s&&t.push(s[1])}const i=e.match(/^(?:def|class)\s+([a-zA-Z_]\w*)/gm)||[];for(const e of i){const s=e.match(/^(?:def|class)\s+([a-zA-Z_]\w*)/);s&&!s[1].startsWith("_")&&t.push(s[1])}const n=e.match(/^func\s+([A-Z][a-zA-Z0-9]*)/gm)||[];for(const e of n){const s=e.match(/^func\s+([A-Z][a-zA-Z0-9]*)/);s&&t.push(s[1])}const c=e.match(/(?:public\s+function|class)\s+([a-zA-Z_]\w*)/g)||[];for(const e of c){const s=e.match(/(?:public\s+function|class)\s+([a-zA-Z_]\w*)/);s&&t.push(s[1])}const a=e.match(/^export\s+(?:(?:async\s+)?function|class|const|let|var)\s+([a-zA-Z_$]\w*)/gm)||[];for(const e of a){const s=e.match(/^export\s+(?:(?:async\s+)?function|class|const|let|var)\s+([a-zA-Z_$]\w*)/);s&&t.push(s[1])}const l=e.match(/^export\s*\{([^}]+)\}/gm)||[];for(const e of l){const s=e.match(/^export\s*\{([^}]+)\}/);if(s){const e=s[1].split(",").map(e=>{const t=e.trim().split(/\s+as\s+/);return(t[1]||t[0]).trim()}).filter(Boolean);t.push(...e)}}/^export\s+default\b/m.test(e)&&t.push("default");const d=e.match(/^pub(?:\([^)]*\))?\s+(?:(?:async\s+)?fn|struct|enum|type|trait)\s+([a-zA-Z_]\w*)/gm)||[];for(const e of d){const s=e.match(/(?:fn|struct|enum|type|trait)\s+([a-zA-Z_]\w*)/);s&&t.push(s[1])}const u=e.match(/^(?:\s*)def\s+(?:self\.)?([a-zA-Z_]\w*[?!]?)/gm)||[];for(const e of u){const s=e.match(/def\s+(?:self\.)?([a-zA-Z_]\w*[?!]?)/);s&&!s[1].startsWith("_")&&t.push(s[1])}return[...new Set(t)]}_extractImports(e){const t=[],s=e.matchAll(/(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\(['"]([^'"]+)['"]\)/g);for(const e of s){const s=e[1]?e[1].split(",").map(e=>e.trim().split(":")[0].trim()):[e[2]];t.push({module:e[3],specifiers:s.filter(Boolean)})}const o=e.matchAll(/^(?:import\s+(\S+)|from\s+(\S+)\s+import)/gm);for(const e of o)t.push({module:e[1]||e[2],specifiers:[]});const r=e.matchAll(/"([^"]+)"/g);for(const e of r)!e[1].includes("/")&&e[1].includes(" ")||t.push({module:e[1],specifiers:[]});return t}_compareWidgetToCode(e,t,s){const o=[];if(t.exports_to){const r=t.exports_to.split(",").map(e=>e.trim().split(" ")[0]).filter(e=>!s.exports.some(t=>t.toLowerCase().includes(e.toLowerCase())));r.length>0&&o.push({file:e,category:"drift",severity:"warning",id:"DRIFT002",name:"Missing Declared Export",message:`Widget declares exports not found in code: ${r.join(", ")}`})}if(t.imports_from){const r=t.imports_from.split(",").map(e=>e.trim().split(" ")[0]),i=s.imports.map(e=>e.module),n=r.filter(e=>!i.some(t=>t.includes(e)||e.includes(t.split("/").pop())));n.length>0&&o.push({file:e,category:"drift",severity:"warning",id:"DRIFT003",name:"Missing Declared Import",message:`Widget declares imports not found in code: ${n.join(", ")}`})}return!/fs\.(write|append|unlink|mkdir|rmdir)/i.test(fs.readFileSync(e,"utf8").replace(/\r\n/g,"\n"))||t.side_effects&&"None"!==t.side_effects||o.push({file:e,category:"drift",severity:"warning",id:"DRIFT004",name:"Undeclared Side Effects",message:"File has file system operations but widget declares no side effects"}),o}_analyzeCrossFile(e){const t=[];for(const[s,o]of Object.entries(e))1===o.length&&t.push({file:o[0],category:"drift",severity:"info",id:"DRIFT005",name:"Orphaned Module",message:`Module "${s}" only has one file - verify module organization`});return t}}module.exports=DriftDetector;